Skip to content

Commit 2719478

Browse files
authored
feat: ticket message component (#172)
1 parent b928c69 commit 2719478

7 files changed

Lines changed: 148 additions & 59 deletions

File tree

apps/atrium-telegram/app/components/flow/ItemCard.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
<div class="flex justify-between items-center">
2626
<div class="flex flex-row gap-4">
27-
<div class="flex flex-row gap-1.5 items-center text-muted">
27+
<div class="flex flex-row gap-1.5 items-center text-sm text-muted">
2828
<UIcon name="i-lucide-message-circle" class="size-5" />
2929
<p>{{ item?.comments.length }}</p>
3030
</div>

apps/atrium-telegram/app/components/TicketCard.vue renamed to apps/atrium-telegram/app/components/ticket/Card.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
{{ ticket.description }}
1111
</div>
1212

13-
<div class="flex justify-between items-center">
13+
<div class="mt-6 flex justify-between items-center">
1414
<div class="flex flex-row gap-4">
15-
<div class="flex flex-row gap-1.5 items-center text-muted">
15+
<div class="flex flex-row gap-1.5 items-center text-sm text-muted">
1616
<UIcon name="i-lucide-message-circle" class="size-5" />
1717
<p>{{ ticket?.messages.length }}</p>
1818
</div>

apps/atrium-telegram/app/components/TicketMessage.vue renamed to apps/atrium-telegram/app/components/ticket/Message.vue

Lines changed: 29 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div class="mt-2.5">
44
<UAvatar :src="user?.avatarUrl ?? undefined" />
55
</div>
6-
<div class="w-full flex flex-col gap-1.5">
6+
<div class="relative w-full flex flex-col gap-1.5">
77
<UDropdownMenu
88
:items="items"
99
:ui="{
@@ -14,38 +14,18 @@
1414
sideOffset: -32,
1515
}"
1616
>
17-
<ActiveCard>
18-
<div class="w-full relative flex flex-col justify-between gap-2">
19-
<div class="flex flex-col gap-1">
20-
<div class="text-base/5 whitespace-break-spaces text-default font-medium">
21-
{{ message?.text }}
22-
</div>
23-
24-
<div v-if="message?.fileUrl && message.fileType !== 'image'">
25-
<UButton
26-
variant="solid"
27-
color="secondary"
28-
:icon="getFileIcon(message.fileType)"
29-
@click="handleFileClick(message.fileUrl)"
30-
>
31-
Прикрепленный файл
32-
</UButton>
33-
</div>
34-
<div v-else-if="message?.fileUrl && message.fileType === 'image'">
35-
<img
36-
:src="message.fileUrl"
37-
alt=""
38-
class="w-full h-full object-contain rounded-lg"
39-
@click="handleFileClick(message.fileUrl)"
40-
>
41-
</div>
42-
43-
<div v-if="message?.createdAt" class="mt-1 flex justify-end text-xs text-muted">
44-
{{ format(new Date(message.createdAt), 'dd MMMM в HH:mm', { locale: ru }) }}
45-
</div>
46-
</div>
47-
</div>
48-
</ActiveCard>
17+
<TicketMessageText
18+
v-if="isMessageWithText && message"
19+
:message="message"
20+
/>
21+
<TicketMessageImage
22+
v-else-if="isMessageWithImage && message"
23+
:message="message"
24+
/>
25+
<TicketMessageFile
26+
v-else-if="isMessageWithFile && message"
27+
:message="message"
28+
/>
4929
</UDropdownMenu>
5030

5131
<!-- <div v-if="message?.notifications?.length" class="-mt-4 ml-4 flex flex-row flex-wrap gap-1">
@@ -61,9 +41,6 @@
6141

6242
<script setup lang="ts">
6343
import type { DropdownMenuItem } from '@nuxt/ui'
64-
import type { TicketMessage } from '@roll-stack/database'
65-
import { format } from 'date-fns'
66-
import { ru } from 'date-fns/locale/ru'
6744
6845
const { ticketId, messageId } = defineProps<{
6946
ticketId: string
@@ -80,15 +57,27 @@ const ticket = computed(() => ticketStore.tickets.find((t) => t.id === ticketId)
8057
const message = computed(() => ticket.value?.messages.find((m) => m.id === messageId))
8158
const user = computed(() => userStore.find(message.value?.userId ?? ''))
8259
60+
const isMessageWithText = computed(() => !message.value?.fileUrl)
61+
const isMessageWithImage = computed(() => message.value?.fileUrl && message.value?.fileType === 'image')
62+
const isMessageWithFile = computed(() => message.value?.fileUrl && message.value?.fileType !== 'image')
63+
8364
const items = computed<DropdownMenuItem[]>(() => {
8465
const menuItems: DropdownMenuItem[] = [
66+
{
67+
label: 'Открыть',
68+
icon: 'i-lucide-external-link',
69+
color: 'neutral',
70+
disabled: false,
71+
onSelect: () => handleFileClick(message.value?.fileUrl),
72+
condition: message.value?.fileUrl,
73+
},
8574
{
8675
label: 'Скопировать сообщение',
8776
icon: 'i-lucide-copy',
8877
color: 'neutral',
8978
disabled: false,
9079
onSelect: () => navigator.clipboard.writeText(message.value?.text ?? ''),
91-
condition: true,
80+
condition: isMessageWithText.value,
9281
},
9382
// {
9483
// label: 'Маякнуть (будет позже)',
@@ -125,20 +114,10 @@ const items = computed<DropdownMenuItem[]>(() => {
125114
return menuItems.filter((item) => item.condition)
126115
})
127116
128-
function getFileIcon(type: TicketMessage['fileType']) {
129-
switch (type) {
130-
case 'image':
131-
return 'i-lucide-image'
132-
case 'video':
133-
return 'i-lucide-video'
134-
case 'document':
135-
return 'i-lucide-file'
136-
default:
137-
return 'i-lucide-file'
117+
function handleFileClick(fileUrl: string | null | undefined) {
118+
if (!fileUrl) {
119+
return
138120
}
139-
}
140-
141-
function handleFileClick(fileUrl: string) {
142121
window.open(fileUrl, '_blank')
143122
}
144123
</script>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<template>
2+
<div class="w-2/3 relative flex flex-col justify-between gap-1.5">
3+
<div class="py-4 flex flex-col gap-2 items-center justify-center border border-default rounded-lg">
4+
<UIcon :name="getFileData(message.fileType).icon" class="size-10 text-primary" />
5+
6+
<UButton
7+
variant="solid"
8+
color="secondary"
9+
:label="getFileData(message.fileType).label"
10+
/>
11+
</div>
12+
<div v-if="message?.createdAt" class="flex justify-end text-xs text-muted">
13+
{{ format(new Date(message.createdAt), 'dd MMMM в HH:mm', { locale: ru }) }}
14+
</div>
15+
</div>
16+
</template>
17+
18+
<script setup lang="ts">
19+
import type { TicketMessage } from '@roll-stack/database'
20+
import { format } from 'date-fns'
21+
import { ru } from 'date-fns/locale/ru'
22+
23+
const { message } = defineProps<{
24+
message: TicketMessage
25+
}>()
26+
27+
function getFileData(type: TicketMessage['fileType']) {
28+
switch (type) {
29+
case 'image':
30+
return {
31+
icon: 'i-lucide-image',
32+
label: 'Прикреплено фото',
33+
}
34+
case 'video':
35+
return {
36+
icon: 'i-lucide-video',
37+
label: 'Прикреплено видео',
38+
}
39+
case 'document':
40+
return {
41+
icon: 'i-lucide-file',
42+
label: 'Прикреплен документ',
43+
}
44+
default:
45+
return {
46+
icon: 'i-lucide-file',
47+
label: 'Прикреплен файл',
48+
}
49+
}
50+
}
51+
</script>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<template>
2+
<div v-if="message.fileUrl" class="w-2/3 relative flex flex-col justify-between gap-1.5">
3+
<img
4+
:src="message.fileUrl"
5+
alt=""
6+
class="w-full h-full object-contain rounded-lg"
7+
>
8+
<div v-if="message?.createdAt" class="flex justify-end text-xs text-muted">
9+
{{ format(new Date(message.createdAt), 'dd MMMM в HH:mm', { locale: ru }) }}
10+
</div>
11+
</div>
12+
</template>
13+
14+
<script setup lang="ts">
15+
import type { TicketMessage } from '@roll-stack/database'
16+
import { format } from 'date-fns'
17+
import { ru } from 'date-fns/locale/ru'
18+
19+
const { message } = defineProps<{
20+
message: TicketMessage
21+
}>()
22+
</script>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<ActiveCard>
3+
<div class="w-full relative flex flex-col justify-between gap-2">
4+
<div class="flex flex-col gap-1">
5+
<div class="text-base/5 whitespace-break-spaces text-default font-medium">
6+
{{ message?.text }}
7+
</div>
8+
9+
<div v-if="message?.createdAt" class="mt-1 flex justify-end text-xs text-muted">
10+
{{ format(new Date(message.createdAt), 'dd MMMM в HH:mm', { locale: ru }) }}
11+
</div>
12+
</div>
13+
</div>
14+
</ActiveCard>
15+
</template>
16+
17+
<script setup lang="ts">
18+
import type { TicketMessage } from '@roll-stack/database'
19+
import { format } from 'date-fns'
20+
import { ru } from 'date-fns/locale/ru'
21+
22+
const { message } = defineProps<{
23+
message: TicketMessage
24+
}>()
25+
</script>

apps/atrium-telegram/app/pages/ticket/[ticketId]/index.vue

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@
1212
<div class="w-full text-base/5 whitespace-pre-wrap break-words">
1313
{{ ticket?.description }}
1414
</div>
15-
</Section>
1615

17-
<Section class="flex flex-row justify-between items-center">
18-
<div class="flex flex-row items-center gap-2">
19-
<UIcon name="i-lucide-message-circle" class="size-5" />
20-
{{ ticket?.messages.length }} {{ pluralizationRu(ticket?.messages.length ?? 0, ['сообщение', 'сообщения', 'сообщений']) }}
16+
<div class="mt-6 flex justify-between items-center">
17+
<div class="flex flex-row gap-4">
18+
<div class="flex flex-row gap-1.5 items-center text-muted text-sm">
19+
<UIcon name="i-lucide-message-circle" class="size-5" />
20+
<p>{{ ticket?.messages.length }} {{ pluralizationRu(ticket?.messages.length ?? 0, ['сообщение', 'сообщения', 'сообщений']) }}</p>
21+
</div>
22+
</div>
23+
24+
<time
25+
v-if="ticket?.createdAt"
26+
:datetime="ticket.createdAt"
27+
class="text-sm text-muted"
28+
v-text="format(new Date(ticket.createdAt), 'от d MMMM yyyy', { locale: ru })"
29+
/>
2130
</div>
2231
</Section>
2332

@@ -60,6 +69,9 @@
6069
</template>
6170

6271
<script setup lang="ts">
72+
import { format } from 'date-fns'
73+
import { ru } from 'date-fns/locale/ru'
74+
6375
definePageMeta({
6476
name: 'ticket-ticketId',
6577
canReturn: true,
@@ -79,7 +91,7 @@ const isShowMore = computed<boolean>(() => messages.value?.length && ticket.valu
7991
// const isDrawerOpened = ref(false)
8092
8193
function handleClickShowMore() {
82-
vibrate()
94+
vibrate('success')
8395
shownMessages.value += 10
8496
}
8597
</script>

0 commit comments

Comments
 (0)