-
Notifications
You must be signed in to change notification settings - Fork 0
feat: show tickets page #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| <template> | ||
| <ActiveCard> | ||
| <UIcon name="i-lucide-mail-question-mark" class="size-8 text-primary" /> | ||
|
|
||
| <h3 class="text-xl/5 font-bold"> | ||
| {{ ticket.title }} | ||
| </h3> | ||
|
|
||
| <div class="w-full text-base/5 font-normal whitespace-pre-wrap break-words line-clamp-5"> | ||
| {{ ticket.description }} | ||
| </div> | ||
|
|
||
| <div class="flex justify-between items-center"> | ||
| <div class="flex flex-row gap-4"> | ||
| <div class="flex flex-row gap-1.5 items-center text-muted"> | ||
| <UIcon name="i-lucide-message-circle" class="size-5" /> | ||
| <p>{{ ticket?.messages.length }}</p> | ||
| </div> | ||
| </div> | ||
|
|
||
| <time | ||
| :datetime="ticket.updatedAt" | ||
| class="text-sm text-muted" | ||
| v-text="format(new Date(ticket.updatedAt), 'обновлен d MMMM yyyy', { locale: ru })" | ||
| /> | ||
| </div> | ||
| </ActiveCard> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import type { TicketWithData } from '~/stores/ticket' | ||
| import { format } from 'date-fns' | ||
| import { ru } from 'date-fns/locale/ru' | ||
|
|
||
| defineProps<{ | ||
| ticket: TicketWithData | ||
| }>() | ||
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| <template> | ||
| <div class="flex flex-row gap-2 items-start"> | ||
| <div class="mt-2.5"> | ||
| <UAvatar :src="user?.avatarUrl ?? undefined" /> | ||
| </div> | ||
| <div class="w-full flex flex-col gap-1.5"> | ||
| <UDropdownMenu | ||
| :items="items" | ||
| :ui="{ | ||
| content: 'w-56', | ||
| item: 'p-2 motion-preset-slide-left motion-duration-200', | ||
| }" | ||
| :content="{ | ||
| sideOffset: -32, | ||
| }" | ||
| > | ||
| <ActiveCard> | ||
| <div class="w-full relative flex flex-col justify-between gap-2"> | ||
| <div class="flex flex-col gap-1"> | ||
| <div class="text-base/5 whitespace-break-spaces text-default font-medium"> | ||
| {{ message?.text }} | ||
| </div> | ||
|
|
||
| <div v-if="message?.fileUrl && message.fileType !== 'image'"> | ||
| <UButton | ||
| variant="solid" | ||
| color="secondary" | ||
| :icon="getFileIcon(message.fileType)" | ||
| @click="handleFileClick(message.fileUrl)" | ||
| > | ||
| Прикрепленный файл | ||
| </UButton> | ||
| </div> | ||
| <div v-else-if="message?.fileUrl && message.fileType === 'image'"> | ||
| <img | ||
| :src="message.fileUrl" | ||
| alt="" | ||
| class="w-full h-full object-contain rounded-lg" | ||
| @click="handleFileClick(message.fileUrl)" | ||
| > | ||
| </div> | ||
|
|
||
| <div v-if="message?.createdAt" class="mt-1 flex justify-end text-xs text-muted"> | ||
| {{ format(new Date(message.createdAt), 'dd MMMM в HH:mm', { locale: ru }) }} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </ActiveCard> | ||
| </UDropdownMenu> | ||
|
|
||
| <!-- <div v-if="message?.notifications?.length" class="-mt-4 ml-4 flex flex-row flex-wrap gap-1"> | ||
| <UserBeacon | ||
| v-for="notification in comment.notifications" | ||
| :key="notification.id" | ||
| :notification="notification" | ||
| /> | ||
| </div> --> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import type { DropdownMenuItem } from '@nuxt/ui' | ||
| import type { TicketMessage } from '@roll-stack/database' | ||
| import { format } from 'date-fns' | ||
| import { ru } from 'date-fns/locale/ru' | ||
|
|
||
| const { ticketId, messageId } = defineProps<{ | ||
| ticketId: string | ||
| messageId: string | ||
| }>() | ||
|
|
||
| // const overlay = useOverlay() | ||
| // const modalCreateEpicCommentBeacon = overlay.create(ModalCreateEpicCommentBeacon) | ||
|
|
||
| const ticketStore = useTicketStore() | ||
| const userStore = useUserStore() | ||
|
|
||
| const ticket = computed(() => ticketStore.tickets.find((t) => t.id === ticketId)) | ||
| const message = computed(() => ticket.value?.messages.find((m) => m.id === messageId)) | ||
| const user = computed(() => userStore.find(message.value?.userId ?? '')) | ||
|
|
||
| const items = computed<DropdownMenuItem[]>(() => { | ||
| const menuItems: DropdownMenuItem[] = [ | ||
| { | ||
| label: 'Скопировать сообщение', | ||
| icon: 'i-lucide-copy', | ||
| color: 'neutral', | ||
| disabled: false, | ||
| onSelect: () => navigator.clipboard.writeText(message.value?.text ?? ''), | ||
| condition: true, | ||
| }, | ||
| // { | ||
| // label: 'Маякнуть (будет позже)', | ||
| // icon: 'i-lucide-users-round', | ||
| // color: 'neutral', | ||
| // disabled: true, | ||
| // onSelect: () => modalCreateEpicCommentBeacon.open({ messageId }), | ||
| // condition: true, | ||
| // }, | ||
| { | ||
| label: 'Лайкнуть (будет позже)', | ||
| icon: 'i-lucide-thumbs-up', | ||
| color: 'neutral', | ||
| disabled: true, | ||
| onSelect: () => {}, | ||
| condition: user.value?.id !== userStore.id, | ||
| }, | ||
| { | ||
| label: 'Редактировать', | ||
| icon: 'i-lucide-edit', | ||
| disabled: true, | ||
| onSelect: () => {}, | ||
| condition: user.value?.id === userStore.id, | ||
| }, | ||
| { | ||
| label: 'Удалить', | ||
| icon: 'i-lucide-trash-2', | ||
| disabled: true, | ||
| onSelect: () => {}, | ||
| condition: user.value?.id === userStore.id, | ||
| }, | ||
| ] | ||
|
|
||
| return menuItems.filter((item) => item.condition) | ||
| }) | ||
|
|
||
| function getFileIcon(type: TicketMessage['fileType']) { | ||
| switch (type) { | ||
| case 'image': | ||
| return 'i-lucide-image' | ||
| case 'video': | ||
| return 'i-lucide-video' | ||
| case 'document': | ||
| return 'i-lucide-file' | ||
| default: | ||
| return 'i-lucide-file' | ||
| } | ||
| } | ||
|
|
||
| function handleFileClick(fileUrl: string) { | ||
| window.open(fileUrl, '_blank') | ||
| } | ||
| </script> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
apps/atrium-telegram/app/pages/ticket/[ticketId]/index.vue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| <template> | ||
| <PageContainer> | ||
| <Section> | ||
| <div class="flex flex-row items-start justify-between gap-2.5"> | ||
| <UIcon name="i-lucide-mail-question-mark" class="size-10 text-primary" /> | ||
| </div> | ||
|
|
||
| <h1 class="text-2xl/6 font-bold"> | ||
| {{ ticket?.title }} | ||
| </h1> | ||
|
|
||
| <div class="w-full text-base/5 whitespace-pre-wrap break-words"> | ||
| {{ ticket?.description }} | ||
| </div> | ||
| </Section> | ||
|
|
||
| <Section class="flex flex-row justify-between items-center"> | ||
| <div class="flex flex-row items-center gap-2"> | ||
| <UIcon name="i-lucide-message-circle" class="size-5" /> | ||
| {{ ticket?.messages.length }} {{ pluralizationRu(ticket?.messages.length ?? 0, ['сообщение', 'сообщения', 'сообщений']) }} | ||
| </div> | ||
| </Section> | ||
|
|
||
| <div class="w-full flex flex-col gap-3.5 flex-1 last-of-type:mb-20"> | ||
| <TicketMessage | ||
| v-for="message in messages" | ||
| :key="message.id" | ||
| :ticket-id="message.ticketId" | ||
| :message-id="message.id" | ||
| /> | ||
|
|
||
| <UButton | ||
| v-if="isShowMore" | ||
| variant="solid" | ||
| color="secondary" | ||
| size="xl" | ||
| class="w-full items-center justify-center" | ||
| icon="i-lucide-message-circle" | ||
| :label="$t('common.show-more')" | ||
| @click="shownMessages += 10" | ||
| /> | ||
| </div> | ||
|
|
||
| <!-- <UDrawer v-model:open="isDrawerOpened"> | ||
| <CreateCard | ||
| v-if="epic?.id" | ||
| :label="$t('app.create.epic-comment.button')" | ||
| icon="i-lucide-message-circle" | ||
| /> | ||
|
|
||
| <template #body> | ||
| <FormCreateEpicComment | ||
| :epic-id="epic?.id ?? ''" | ||
| @submitted="isDrawerOpened = false" | ||
| @success="isDrawerOpened = false" | ||
| /> | ||
| </template> | ||
| </UDrawer> --> | ||
| </PageContainer> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| definePageMeta({ | ||
| name: 'ticket-ticketId', | ||
| canReturn: true, | ||
| }) | ||
|
|
||
| const { params } = useRoute('ticket-ticketId') | ||
|
|
||
| const ticketStore = useTicketStore() | ||
| const ticket = computed(() => ticketStore.tickets.find((e) => e.id === params.ticketId)) | ||
|
|
||
| // On load show last 10 messages. On button click = show more 10 messages | ||
| const shownMessages = ref(10) | ||
| const messages = computed(() => ticket.value?.messages.slice(0, shownMessages.value)) | ||
| const isShowMore = computed<boolean>(() => messages.value?.length && ticket.value?.messages.length ? messages.value.length < ticket.value.messages.length : false) | ||
|
|
||
| // const isDrawerOpened = ref(false) | ||
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <template> | ||
| <PageContainer> | ||
| <NuxtLink | ||
| v-for="ticket of ticketStore.tickets" | ||
| :key="ticket.id" | ||
| :to="`/ticket/${ticket.id}`" | ||
| class="motion-preset-slide-left" | ||
| > | ||
| <TicketCard :ticket="ticket"> | ||
| {{ ticket.title }} | ||
| </TicketCard> | ||
| </NuxtLink> | ||
|
|
||
| <div class="mt-16 flex flex-row justify-center"> | ||
| <UIcon name="i-lucide-route" class="size-8 text-dimmed/25" /> | ||
| </div> | ||
| </PageContainer> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| const ticketStore = useTicketStore() | ||
| </script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import type { Ticket, TicketMessage, User } from '@roll-stack/database' | ||
| import { initDataRaw as _initDataRaw, useSignal } from '@telegram-apps/sdk-vue' | ||
|
|
||
| export type TicketWithData = Ticket & { | ||
| messages: TicketMessage[] | ||
| lastMessage: TicketMessage | null | ||
| user: User | ||
| } | ||
|
|
||
| export const useTicketStore = defineStore('ticket', () => { | ||
| const tickets = ref<TicketWithData[]>([]) | ||
|
|
||
| const initDataRaw = useSignal(_initDataRaw) | ||
|
|
||
| async function update() { | ||
| try { | ||
| const data = await $fetch('/api/ticket/list', { | ||
| headers: { | ||
| Authorization: `tma ${initDataRaw.value}`, | ||
| }, | ||
| }) | ||
| if (!data) { | ||
| return | ||
| } | ||
|
|
||
| tickets.value = data | ||
| } catch (error) { | ||
| if (error instanceof Error) { | ||
| if (error.message.includes('401')) { | ||
| // No session | ||
| } | ||
| if (error.message.includes('404')) { | ||
| // Not found | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return { | ||
| tickets, | ||
|
|
||
| update, | ||
| } | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Ensure proper handling of undefined condition property
The TypeScript type
DropdownMenuItemmight not include aconditionproperty based on the Nuxt UI types. This could cause TypeScript errors or runtime issues.🌐 Web query:
💡 Result:
No. The official DropdownMenuItem type in @nuxt/ui does not include a "condition" property — the documented item properties (label, icon, avatar, kbds, type, color, checked, disabled, slot, onSelect, onUpdateChecked, children, class, ui, etc.) do not list "condition". [1][2]
Remove/guard unknown "condition" on DropdownMenuItem objects
The @nuxt/ui DropdownMenuItem type does not include a "condition" property; keeping it in an object literal will cause TypeScript errors.
condition: true(line 82) and compute/filter visibility before creating DropdownMenuItem objects.as DropdownMenuItem & { condition?: boolean }or module augmentation).🤖 Prompt for AI Agents