Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/atrium-telegram/app/components/SectionTitle.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<h2 class="text-2xl/6 font-bold tracking-tight">
<h2 class="text-2xl/7 font-bold tracking-tight">
{{ title }}
</h2>
</template>
Expand Down
2 changes: 1 addition & 1 deletion apps/atrium-telegram/app/components/StaffBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<div
v-for="user in allUsers"
:key="user.id"
class="w-18 flex flex-col gap-1 justify-start items-center scroll-ml-6 snap-start motion-preset-slide-right"
class="w-18 flex flex-col gap-1 justify-start items-center scroll-ml-6 snap-start motion-preset-slide-up"
@click="handleClick(user.id)"
>
<div class="relative">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
<template>
<div class="w-full flex flex-row items-start gap-1">
<UIcon
v-if="isCompleted"
name="i-lucide-check"
class="shrink-0 mt-1.5 size-6 text-secondary"
/>
<UCheckbox
v-else-if="canComplete"
v-model="checkbox"
variant="list"
icon="i-lucide-check"
class="shrink-0 mt-1.5 duration-200 motion-preset-bounce"
:ui="{
base: 'size-6',
}"
@change="handleComplete"
/>
<UCheckbox
v-else
v-model="checkbox"
color="secondary"
variant="list"
icon="i-lucide-check"
:ui="{
base: 'size-6',
}"
class="shrink-0 mt-1.5 duration-200 motion-preset-bounce"
disabled
/>

<div>
<UDropdownMenu
:items="items"
:content="{
Expand All @@ -39,44 +10,7 @@
item: 'p-2 motion-preset-slide-left motion-duration-200',
}"
>
<UButton
color="secondary"
:variant="isFocused ? 'ghost' : 'ghost'"
:trailing-icon="isFocused ? 'i-lucide-goal' : undefined"
block
:ui="{
trailingIcon: [
'self-start mt-0.5 text-dimmed',
isFocused ? 'text-secondary' : undefined,
],
}"
class="group/task duration-200 motion-preset-bounce"
@click="vibrate()"
>
<div class="w-full flex flex-col gap-2 items-start">
<div class="flex flex-col gap-1 items-start text-left">
<h4 class="text-base/5 font-medium tg-text">
{{ task.name }}
</h4>
<p v-if="task.description" class="text-sm/4 text-muted font-normal">
{{ task.description }}
</p>
</div>

<div class="flex flex-row gap-y-1 gap-x-2 items-center">
<UBadge
v-if="task?.date"
size="md"
color="primary"
variant="soft"
icon="i-lucide-calendar"
class="shrink-0"
>
{{ df.format(parseDate(task.date).toDate(getLocalTimeZone())) }}
</UBadge>
</div>
</div>
</UButton>
<TaskInfoCard :task="task" />
</UDropdownMenu>
</div>
</template>
Expand All @@ -85,26 +19,22 @@
import type { DropdownMenuItem } from '@nuxt/ui'
import type { Task } from '@roll-stack/database'
import { ModalCompleteTask, ModalUpdateTask } from '#components'
import { DateFormatter, getLocalTimeZone, parseDate } from '@internationalized/date'

const { task } = defineProps<{
task: Task
}>()

const { vibrate } = useFeedback()
const taskStore = useTaskStore()

const userStore = useUserStore()
const taskStore = useTaskStore()

const list = computed(() => taskStore.lists.find((list) => list.id === task.listId))

const overlay = useOverlay()
const modalUpdateTask = overlay.create(ModalUpdateTask)
const modalCompleteTask = overlay.create(ModalCompleteTask)

const df = new DateFormatter('ru-RU', {
dateStyle: 'long',
})

const isCompleted = computed(() => !!task.completedAt)
const performer = computed(() => userStore.staff.find((staff) => staff.id === task.performerId))

Expand All @@ -113,8 +43,6 @@ const canComplete = computed(() => canEdit.value && !isCompleted.value && (task.
const canFocus = computed(() => task.performerId === userStore.id && !isCompleted.value)
const isFocused = computed(() => task.id === performer.value?.focusedTaskId)

const checkbox = ref(false)

const items = computed<DropdownMenuItem[]>(() => {
const menuItems: DropdownMenuItem[] = [
{
Expand Down Expand Up @@ -147,6 +75,12 @@ const items = computed<DropdownMenuItem[]>(() => {
},
condition: canEdit.value,
},
{
label: 'Задача закрыта',
icon: 'i-lucide-check',
disabled: true,
condition: isCompleted.value,
},
]

return menuItems.filter((item) => item.condition)
Expand Down Expand Up @@ -177,16 +111,4 @@ async function onUnfocus() {
vibrate('error')
}
}

function handleComplete() {
vibrate()

if (!checkbox.value) {
return
}

modalCompleteTask.open({ taskId: task.id })

checkbox.value = false
}
</script>
9 changes: 8 additions & 1 deletion apps/atrium-telegram/app/components/TaskInfoCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<ActiveCard>
<ActiveCard class="motion-preset-slide-left">
<Section>
<div class="flex flex-row gap-2 items-center">
<UAvatar :src="performer?.avatarUrl ?? undefined" class="size-8" />
Expand Down Expand Up @@ -38,6 +38,13 @@
{{ task.description }}
</div>

<div v-if="task?.date" class="flex flex-row gap-2 items-center w-full">
<UIcon name="i-lucide-calendar" class="shrink-0 size-5 text-primary" />
<p class="text-base/5 font-semibold whitespace-pre-wrap break-words">
{{ format(new Date(task.date), 'd MMMM yyyy', { locale: ru }) }}
</p>
</div>

<div v-if="task.report" class="flex flex-row gap-2 items-start w-full">
<UIcon name="i-lucide-clipboard-pen" class="shrink-0 size-5 text-primary" />
<p class="text-base/5 font-semibold whitespace-pre-wrap break-words line-clamp-12">
Expand Down
98 changes: 36 additions & 62 deletions apps/atrium-telegram/app/components/TaskList.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
<template>
<Section>
<div class="flex flex-row gap-2 items-center justify-between">
<div class="flex flex-row gap-2.5 items-center">
<UPopover
v-if="activeChatMembers?.length"
mode="hover"
:content="{
align: 'center',
side: 'bottom',
sideOffset: 8,
}"
>
<div class="flex flex-col gap-2.5">
<div class="flex flex-row gap-2.5 items-center justify-between">
<SectionTitle :title="list?.name ?? ''" />

<UButton
v-if="canEdit"
variant="solid"
color="secondary"
icon="i-lucide-plus"
label="Создать"
@click="handleCreateTask()"
/>
</div>

<Section class="py-2">
<div class="flex flex-row items-center justify-between">
<div class="h-8">
<UAvatarGroup
:max="2"
size="xs"
v-if="activeChatMembers?.length"
:max="3"
size="md"
:ui="{
base: '-me-1.5',
}"
Expand All @@ -25,69 +31,37 @@
alt=""
/>
</UAvatarGroup>
</div>

<template #content>
<div class="h-auto w-fit px-1.5 py-2 flex flex-col gap-2">
<UButtonGroup orientation="vertical">
<UButton
v-for="member in activeChatMembers"
:key="member.id"
:to="`/staff/${member.user.id}`"
:avatar="{ src: member.user.avatarUrl ?? undefined }"
:ui="{
leadingAvatarSize: 'sm',
}"
:label="`${member.user.name} ${member.user.surname}`"
block
color="primary"
variant="link"
class="text-sm justify-start"
/>
</UButtonGroup>
</div>
</template>
</UPopover>

<h3 class="text-lg/5 font-bold">
{{ list?.name }}
</h3>
<div v-if="canEdit" class="flex flex-row gap-2">
<UButton
variant="soft"
color="primary"
size="xl"
icon="i-lucide-pencil"
class="h-10"
@click="handleEditTaskList()"
/>
</div>
</div>

<div v-if="canEdit" class="flex flex-row gap-2">
<UButton
variant="soft"
color="primary"
size="md"
icon="i-lucide-pencil"
@click="handleEditTaskList"
/>

<UButton
variant="solid"
color="secondary"
size="md"
icon="i-lucide-plus"
@click="handleCreateTask"
/>
</div>
</div>
</Section>

<div
v-if="tasks.length"
class="w-full flex flex-col gap-3"
class="w-full flex flex-col gap-2.5"
>
<TaskCard
<TaskActiveCard
v-for="task in tasks"
:key="task.id"
:task="task"
/>
</div>
<template v-else>
<p class="text-base text-muted">
<p class="text-center text-base text-muted">
Активных задач нет
</p>
</template>
</Section>
</div>
</template>

<script setup lang="ts">
Expand Down
2 changes: 2 additions & 0 deletions apps/atrium-telegram/app/components/form/CompleteTask.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const { taskId } = defineProps<{

const emit = defineEmits(['success', 'submitted'])

const { pop } = useConfetti()
const { vibrate } = useFeedback()
const userStore = useUserStore()
const taskStore = useTaskStore()
Expand Down Expand Up @@ -77,6 +78,7 @@ async function onSubmit(event: FormSubmitEvent<CompleteTask>) {
userStore.update(),
])

pop()
vibrate('success')
emit('success')
} catch (error) {
Expand Down
14 changes: 14 additions & 0 deletions apps/atrium-telegram/app/composables/useConfetti.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function _useConfetti() {
const isShown = ref(false)

function pop() {
isShown.value = true
setTimeout(() => {
isShown.value = false
}, 5000)
}

return { isShown, pop }
}

export const useConfetti = createSharedComposable(_useConfetti)
15 changes: 15 additions & 0 deletions apps/atrium-telegram/app/layouts/default.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
<template>
<ClientOnly>
<div
v-if="isShown"
ref="confetti"
class="z-40 mx-auto h-dvh w-dvw fixed inset-0 overflow-y-hidden overscroll-y-none"
>
<div v-confetti="{ particleCount: 240, duration: 4500, stageHeight: confetti?.clientHeight, stageWidth: confetti?.clientWidth, force: 0.4 }" />
</div>
</ClientOnly>

<main class="tg-text tg-safe-area">
<slot />
</main>
Expand All @@ -7,5 +17,10 @@
</template>

<script setup lang="ts">
import { vConfetti } from '@neoconfetti/vue'

const confetti = ref<HTMLElement | null>(null)
const { isShown } = useConfetti()

const userStore = useUserStore()
</script>
4 changes: 2 additions & 2 deletions apps/atrium-telegram/app/pages/task/[taskId]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@
</p>
</div>

<div v-if="task?.date" class="flex flex-row gap-2 items-start w-full">
<div v-if="task?.date" class="flex flex-row gap-2 items-center w-full">
<UIcon name="i-lucide-calendar" class="shrink-0 size-6 text-primary" />
<p class="text-base/5 font-semibold whitespace-pre-wrap break-words">
{{ format(new Date(task.date), 'd MMMM yyyy', { locale: ru }) }}
</p>
</div>

<div v-if="taskList" class="flex flex-row gap-2 items-start w-full">
<div v-if="taskList" class="flex flex-row gap-2 items-center w-full">
<UIcon name="i-lucide-book-marked" class="shrink-0 size-6 text-primary" />
<p class="text-base/5 font-semibold whitespace-pre-wrap break-words">
{{ taskList.name }}
Expand Down
Loading