|
| 1 | +<template> |
| 2 | + <PageContainer> |
| 3 | + <div class="flex flex-row gap-2.5 items-center"> |
| 4 | + <SectionTitle title="Задачи" /> |
| 5 | + <CounterBadge :value="filteredTasks.all.length" /> |
| 6 | + </div> |
| 7 | + |
| 8 | + <div class="grid grid-cols-1 gap-2.5 items-center"> |
| 9 | + <USelect |
| 10 | + v-model="sortedBy" |
| 11 | + size="xl" |
| 12 | + trailing-icon="i-lucide-arrow-down-wide-narrow" |
| 13 | + :ui="{ |
| 14 | + base: 'rounded-lg text-lg/5 font-bold ring-0', |
| 15 | + }" |
| 16 | + :items="[ |
| 17 | + { label: 'По дате создания (убывание)', value: 'updatedAtDesc' }, |
| 18 | + { label: 'По дате создания (возрастание)', value: 'updatedAtAsc' }, |
| 19 | + ]" |
| 20 | + class="motion-preset-slide-down" |
| 21 | + /> |
| 22 | + |
| 23 | + <USelect |
| 24 | + v-model="filteredBy" |
| 25 | + size="xl" |
| 26 | + trailing-icon="i-lucide-funnel" |
| 27 | + :ui="{ |
| 28 | + base: 'rounded-lg text-lg/5 font-bold ring-0', |
| 29 | + }" |
| 30 | + :items="[ |
| 31 | + { label: 'Все задачи', value: 'all' }, |
| 32 | + { label: 'Только выполненные', value: 'completed' }, |
| 33 | + ]" |
| 34 | + class="motion-preset-slide-up" |
| 35 | + /> |
| 36 | + |
| 37 | + <USelectMenu |
| 38 | + v-model="selectedPerformer" |
| 39 | + :items="availablePerformers" |
| 40 | + size="xl" |
| 41 | + :ui="{ |
| 42 | + base: 'rounded-lg text-lg/5 font-bold ring-0', |
| 43 | + }" |
| 44 | + placeholder="Все исполнители" |
| 45 | + class="motion-preset-slide-up" |
| 46 | + > |
| 47 | + <template v-if="selectedPerformer?.avatar.src" #trailing> |
| 48 | + <UAvatar |
| 49 | + v-if="selectedPerformer?.avatar" |
| 50 | + :src="selectedPerformer.avatar.src" |
| 51 | + size="xs" |
| 52 | + /> |
| 53 | + </template> |
| 54 | + </USelectMenu> |
| 55 | + </div> |
| 56 | + |
| 57 | + <div class="flex flex-col gap-2.5"> |
| 58 | + <div class="flex flex-col gap-4"> |
| 59 | + <TaskInfoCard |
| 60 | + v-for="task in filteredTasks.show" |
| 61 | + :key="task.id" |
| 62 | + :task="task" |
| 63 | + /> |
| 64 | + |
| 65 | + <UButton |
| 66 | + v-if="filteredTasks.canShowMore" |
| 67 | + variant="solid" |
| 68 | + color="secondary" |
| 69 | + size="xl" |
| 70 | + class="mt-6 mx-auto px-8 w-fit items-center justify-center" |
| 71 | + icon="i-lucide-arrow-down" |
| 72 | + :label="$t('common.show-more')" |
| 73 | + @click="handleClickShowMore()" |
| 74 | + /> |
| 75 | + </div> |
| 76 | + </div> |
| 77 | + </PageContainer> |
| 78 | +</template> |
| 79 | + |
| 80 | +<script setup lang="ts"> |
| 81 | +import type { Task } from '@roll-stack/database' |
| 82 | +
|
| 83 | +const { vibrate } = useFeedback() |
| 84 | +const userStore = useUserStore() |
| 85 | +const taskStore = useTaskStore() |
| 86 | +
|
| 87 | +// On load show last 50 tasks. On button click = show more |
| 88 | +const shownTasks = ref(50) |
| 89 | +
|
| 90 | +function handleClickShowMore() { |
| 91 | + vibrate('success') |
| 92 | + shownTasks.value += 50 |
| 93 | +} |
| 94 | +
|
| 95 | +const sortedBy = ref<'updatedAtDesc' | 'updatedAtAsc'>('updatedAtDesc') |
| 96 | +
|
| 97 | +function sortByUpdatedAtDesc(a: Task, b: Task) { |
| 98 | + const aTime = a.updatedAt ? new Date(a.updatedAt).getTime() : 0 |
| 99 | + const bTime = b.updatedAt ? new Date(b.updatedAt).getTime() : 0 |
| 100 | + return bTime - aTime |
| 101 | +} |
| 102 | +
|
| 103 | +function sortByUpdatedAtAsc(a: Task, b: Task) { |
| 104 | + const aTime = a.updatedAt ? new Date(a.updatedAt).getTime() : 0 |
| 105 | + const bTime = b.updatedAt ? new Date(b.updatedAt).getTime() : 0 |
| 106 | + return aTime - bTime |
| 107 | +} |
| 108 | +
|
| 109 | +function chooseSortFunction() { |
| 110 | + switch (sortedBy.value) { |
| 111 | + case 'updatedAtDesc': |
| 112 | + return sortByUpdatedAtDesc |
| 113 | + case 'updatedAtAsc': |
| 114 | + return sortByUpdatedAtAsc |
| 115 | + } |
| 116 | +} |
| 117 | +
|
| 118 | +const filteredBy = ref<'all' | 'completed'>('all') |
| 119 | +
|
| 120 | +function filterByAll() { |
| 121 | + return true |
| 122 | +} |
| 123 | +
|
| 124 | +function filterByCompleted(task: Task) { |
| 125 | + return task.completedAt |
| 126 | +} |
| 127 | +
|
| 128 | +function chooseFilterFunction() { |
| 129 | + switch (filteredBy.value) { |
| 130 | + case 'all': |
| 131 | + return filterByAll |
| 132 | + case 'completed': |
| 133 | + return filterByCompleted |
| 134 | + } |
| 135 | +} |
| 136 | +
|
| 137 | +const availablePerformers = computed(() => [{ |
| 138 | + label: 'Все исполнители', |
| 139 | + value: '', |
| 140 | + avatar: { |
| 141 | + src: undefined, |
| 142 | + alt: '', |
| 143 | + }, |
| 144 | + onSelect: () => { |
| 145 | + selectedPerformer.value = undefined |
| 146 | + }, |
| 147 | +}, ...userStore.staff.map((staff) => ({ |
| 148 | + label: `${staff.name} ${staff.surname}`, |
| 149 | + value: staff.id, |
| 150 | + avatar: { |
| 151 | + src: staff.avatarUrl ?? undefined, |
| 152 | + alt: '', |
| 153 | + }, |
| 154 | +}))]) |
| 155 | +
|
| 156 | +const selectedPerformer = ref<{ label: string, value: string, avatar: { src: string | undefined, alt: string } } | undefined>() |
| 157 | +
|
| 158 | +function filterByPerformer(task: Task) { |
| 159 | + return selectedPerformer.value?.value ? task.performerId === selectedPerformer.value.value : true |
| 160 | +} |
| 161 | +
|
| 162 | +const filteredTasks = computed(() => { |
| 163 | + const sorted = taskStore.tasks.toSorted(chooseSortFunction()) |
| 164 | + const filtered = sorted.filter(chooseFilterFunction()).filter(filterByPerformer) |
| 165 | +
|
| 166 | + const show = filtered.slice(0, shownTasks.value) |
| 167 | +
|
| 168 | + return { |
| 169 | + show, |
| 170 | + all: filtered, |
| 171 | + canShowMore: filtered.length > show.length, |
| 172 | + } |
| 173 | +}) |
| 174 | +</script> |
0 commit comments