Skip to content

Commit e52e382

Browse files
authored
feat: create message action (#173)
1 parent 2719478 commit e52e382

17 files changed

Lines changed: 233 additions & 35 deletions

File tree

apps/atrium-telegram/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Main database
22
DATABASE_URL=
33

4+
# Queue
5+
QUEUE_URL=
6+
47
# Main API
58
NUXT_PUBLIC_CORE_API_URL=
69

apps/atrium-telegram/app/components/PageContainer.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
<div class="z-30 overflow-hidden h-full min-h-dvh w-full tg-content-safe-area">
33
<div class="px-4 py-2 max-w-[28rem] mx-auto mb-20 flex flex-col gap-y-6" :class="className">
44
<slot />
5+
6+
<div class="mt-16 flex flex-row justify-center">
7+
<img
8+
src="/sushi-heart.svg"
9+
alt=""
10+
class="w-10 opacity-25 invert-50"
11+
>
12+
</div>
513
</div>
614
</div>
715
</template>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<template>
2+
<UForm
3+
:validate="createValidator(createTicketMessageSchema)"
4+
:state="state"
5+
class="flex flex-col gap-3"
6+
@submit="onSubmit"
7+
>
8+
<UFormField label="Ваше сообщение" name="text">
9+
<UTextarea
10+
v-model="state.text"
11+
placeholder="Не торопись, осмотрись..."
12+
autoresize
13+
size="xl"
14+
class="w-full"
15+
/>
16+
</UFormField>
17+
18+
<UButton
19+
type="submit"
20+
variant="solid"
21+
color="secondary"
22+
size="xl"
23+
icon="i-lucide-send"
24+
block
25+
class="mt-3"
26+
:disabled="!state.text"
27+
:label="$t('common.send')"
28+
/>
29+
</UForm>
30+
</template>
31+
32+
<script setup lang="ts">
33+
import type { CreateTicketMessage } from '#shared/services/ticket'
34+
import type { FormSubmitEvent } from '@nuxt/ui'
35+
import { createTicketMessageSchema } from '#shared/services/ticket'
36+
37+
const { ticketId } = defineProps<{ ticketId: string }>()
38+
39+
const emit = defineEmits(['success', 'submitted'])
40+
41+
const { vibrate } = useFeedback()
42+
const userStore = useUserStore()
43+
const ticketStore = useTicketStore()
44+
45+
const state = ref<Partial<CreateTicketMessage>>({
46+
text: undefined,
47+
})
48+
49+
async function onSubmit(event: FormSubmitEvent<CreateTicketMessage>) {
50+
emit('submitted')
51+
52+
try {
53+
await $fetch(`/api/ticket/id/${ticketId}/message`, {
54+
method: 'POST',
55+
headers: {
56+
Authorization: `tma ${userStore.initDataRaw}`,
57+
},
58+
body: event.data,
59+
})
60+
61+
await Promise.all([
62+
ticketStore.update(),
63+
userStore.update(),
64+
])
65+
66+
vibrate('success')
67+
emit('success')
68+
} catch (error) {
69+
console.error(error)
70+
vibrate('error')
71+
}
72+
}
73+
</script>

apps/atrium-telegram/app/components/ticket/Card.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
<template>
22
<ActiveCard>
3-
<UIcon name="i-lucide-mail-question-mark" class="size-8 text-primary" />
3+
<div class="flex flex-row gap-2 items-center">
4+
<UIcon name="i-lucide-mail-question-mark" class="size-8 text-primary" />
5+
6+
<div v-if="hasAnswerFromUser" class="flex flex-row items-center gap-1.5 text-error">
7+
<UIcon
8+
name="i-lucide-pointer"
9+
class="size-8 motion-translate-y-loop-25 motion-preset-seesaw motion-duration-2000"
10+
/>
11+
<p class="max-w-22 text-sm/4 font-bold">
12+
Есть ответ от партнера
13+
</p>
14+
</div>
15+
</div>
416

517
<h3 class="text-xl/5 font-bold">
618
{{ ticket.title }}
@@ -21,7 +33,7 @@
2133
<time
2234
:datetime="ticket.updatedAt"
2335
class="text-sm text-muted"
24-
v-text="format(new Date(ticket.updatedAt), 'обновлен d MMMM yyyy', { locale: ru })"
36+
v-text="format(new Date(ticket.updatedAt), 'd MMMM yyyy в HH:mm', { locale: ru })"
2537
/>
2638
</div>
2739
</ActiveCard>
@@ -32,7 +44,9 @@ import type { TicketWithData } from '~/stores/ticket'
3244
import { format } from 'date-fns'
3345
import { ru } from 'date-fns/locale/ru'
3446
35-
defineProps<{
47+
const { ticket } = defineProps<{
3648
ticket: TicketWithData
3749
}>()
50+
51+
const hasAnswerFromUser = computed(() => ticket.lastMessage?.userId === ticket.userId)
3852
</script>

apps/atrium-telegram/app/components/ticket/Message.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div class="flex flex-row gap-2 items-start">
33
<div class="mt-2.5">
4-
<UAvatar :src="user?.avatarUrl ?? undefined" />
4+
<UAvatar :src="user?.avatarUrl ?? undefined" size="lg" />
55
</div>
66
<div class="relative w-full flex flex-col gap-1.5">
77
<UDropdownMenu

apps/atrium-telegram/app/components/ticket/MessageFile.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<UIcon :name="getFileData(message.fileType).icon" class="size-10 text-primary" />
55

66
<UButton
7-
variant="solid"
7+
variant="soft"
88
color="secondary"
99
:label="getFileData(message.fileType).label"
1010
/>

apps/atrium-telegram/app/composables/useNavigation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ function _useNavigation() {
55

66
const taskStore = useTaskStore()
77
const flowStore = useFlowStore()
8+
const ticketStore = useTicketStore()
89

910
const mainRoutes = computed<NavigationRoute[]>(() => [
1011
{
@@ -20,6 +21,7 @@ function _useNavigation() {
2021
names: ['ticket', 'ticket-ticketId'],
2122
title: t('app.tickets'),
2223
icon: 'i-lucide-mail-question-mark',
24+
badge: ticketStore.ticketsWithoutAnswer.length.toString(),
2325
},
2426
{
2527
path: '/tasks',

apps/atrium-telegram/app/pages/flow/[itemId]/index.vue

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@
1212
<div class="w-full text-base/5 whitespace-pre-wrap break-words">
1313
{{ item?.description }}
1414
</div>
15+
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>0</p>
21+
</div>
22+
</div>
23+
24+
<time
25+
v-if="item?.createdAt"
26+
:datetime="item.createdAt"
27+
class="text-sm text-muted"
28+
v-text="format(new Date(item.createdAt), 'd MMMM yyyy в HH:mm', { locale: ru })"
29+
/>
30+
</div>
1531
</Section>
1632

1733
<Section class="flex flex-col">
@@ -24,13 +40,17 @@
2440
v-for="view in item?.views"
2541
:key="view.id"
2642
:src="userStore.getAvatarUrl(view.userId)"
43+
size="lg"
2744
/>
2845
</div>
2946
</Section>
3047
</PageContainer>
3148
</template>
3249

3350
<script setup lang="ts">
51+
import { format } from 'date-fns'
52+
import { ru } from 'date-fns/locale/ru'
53+
3454
definePageMeta({
3555
name: 'flow-itemId',
3656
canReturn: true,

apps/atrium-telegram/app/pages/index.vue

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@
3434
</NuxtLink>
3535
</div>
3636
</div>
37-
38-
<div class="mt-16 flex flex-row justify-center">
39-
<UIcon name="i-lucide-route" class="size-8 text-dimmed/25" />
40-
</div>
4137
</PageContainer>
4238
</template>
4339

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

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,23 @@
3030
</div>
3131
</Section>
3232

33-
<div class="w-full flex flex-col gap-3.5 flex-1 last-of-type:mb-20">
33+
<div class="w-full flex flex-col gap-3.5 flex-1">
34+
<UDrawer v-model:open="isDrawerOpened">
35+
<CreateCard
36+
label="Написать сообщение"
37+
icon="i-lucide-message-circle"
38+
class="mb-4"
39+
/>
40+
41+
<template #body>
42+
<FormCreateTicketMessage
43+
:ticket-id="ticket?.id ?? ''"
44+
@submitted="isDrawerOpened = false"
45+
@success="isDrawerOpened = false"
46+
/>
47+
</template>
48+
</UDrawer>
49+
3450
<TicketMessage
3551
v-for="message in messages"
3652
:key="message.id"
@@ -40,31 +56,15 @@
4056

4157
<UButton
4258
v-if="isShowMore"
43-
variant="solid"
44-
color="secondary"
59+
variant="soft"
60+
color="primary"
4561
size="xl"
46-
class="w-full items-center justify-center"
47-
icon="i-lucide-message-circle"
62+
class="mt-6 mx-auto w-fit items-center justify-center"
63+
icon="i-lucide-message-circle-more"
4864
:label="$t('common.show-more')"
4965
@click="handleClickShowMore()"
5066
/>
5167
</div>
52-
53-
<!-- <UDrawer v-model:open="isDrawerOpened">
54-
<CreateCard
55-
v-if="epic?.id"
56-
:label="$t('app.create.epic-comment.button')"
57-
icon="i-lucide-message-circle"
58-
/>
59-
60-
<template #body>
61-
<FormCreateEpicComment
62-
:epic-id="epic?.id ?? ''"
63-
@submitted="isDrawerOpened = false"
64-
@success="isDrawerOpened = false"
65-
/>
66-
</template>
67-
</UDrawer> -->
6868
</PageContainer>
6969
</template>
7070

@@ -88,7 +88,7 @@ const shownMessages = ref(10)
8888
const messages = computed(() => ticket.value?.messages.slice(0, shownMessages.value))
8989
const isShowMore = computed<boolean>(() => messages.value?.length && ticket.value?.messages.length ? messages.value.length < ticket.value.messages.length : false)
9090
91-
// const isDrawerOpened = ref(false)
91+
const isDrawerOpened = ref(false)
9292
9393
function handleClickShowMore() {
9494
vibrate('success')

0 commit comments

Comments
 (0)