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
4 changes: 2 additions & 2 deletions apps/atrium-telegram/app/components/ActiveCard.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<template>
<div class="relative active:scale-95 duration-200" @click="vibrate()">
<button class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add type="button" to prevent unintended form submissions

Without an explicit type, buttons default to submit inside forms.

Apply this diff:

-  <button class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
+  <button type="button" class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
<button type="button" class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/ActiveCard.vue around line 2, the button
lacks an explicit type so it will default to "submit" when placed inside a form;
update the button element to include type="button" to prevent unintended form
submissions while preserving existing classes and the @click handler.

🛠️ Refactor suggestion

Restore visible focus styles; replace outline-0 with a custom focus-visible ring (and smooth the scale animation)

outline-0 removes the default focus indicator; add an explicit focus style. Also add transition-transform so active:scale-95 animates.

Apply this diff:

-  <button type="button" class="relative w-full active:scale-95 duration-200 text-left cursor-pointer outline-0" @click="vibrate()">
+  <button type="button" class="relative w-full active:scale-95 transition-transform duration-200 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-indigo-500" @click="vibrate()">

If you have design tokens (e.g., tg- colors), swap indigo-500/offsets accordingly.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/ActiveCard.vue around line 2, the button
currently removes the browser focus indicator with outline-0 and lacks a
transform transition; restore an accessible visible focus style and smooth the
active scale by replacing outline-0 with a focus-visible ring class (e.g.,
focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-indigo-500
focus-visible:ring-offset-white or the equivalent tg- token colors) and add
transition-transform to the class list so active:scale-95 animates smoothly;
ensure ring-offset color matches the component background and adjust token names
to your design system if present.

<Section>
<slot />
</Section>
</div>
</button>
</template>

<script setup lang="ts">
Expand Down
2 changes: 1 addition & 1 deletion apps/atrium-telegram/app/components/Section.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="p-4 tg-bg-section group/section space-y-3.5 rounded-lg">
<div class="relative p-4 tg-bg-section group/section space-y-3.5 rounded-lg">
<slot />
</div>
</template>
98 changes: 50 additions & 48 deletions apps/atrium-telegram/app/components/form/CreateEpicComment.vue
Original file line number Diff line number Diff line change
@@ -1,71 +1,73 @@
<template>
<UCard
variant="subtle"
class="mt-auto bg-elevated/25"
<UForm
:validate="createValidator(createEpicCommentSchema)"
:state="state"
Comment on lines +2 to +4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Missing/implicit createValidator import — verify auto-import.

createValidator is used but not imported; unless auto-imported, this will throw at runtime.

Run to locate its definition and usages:

If not auto-imported, add the proper import path used elsewhere in the project.


🏁 Script executed:

#!/bin/bash
# Find a definition/export of createValidator
rg -nP 'export\s+(?:const|function)\s+createValidator\b|createValidator:\s*' -C2
# Check for a Nuxt auto-import declaration
rg -nP 'autoImports|imports:\s*\{[^}]*imports' -g '!**/node_modules/**' -C3
# Where else is it used (to infer import style)
rg -nP '\bcreateValidator\s*\(' -g '!**/node_modules/**' -C2

Length of output: 19492


Add missing createValidator import
In apps/atrium-telegram/app/components/form/CreateEpicComment.vue’s <script setup> block, add:

import { createValidator } from '~/utils/ui'

createValidator is exported from app/utils/ui.ts and must be imported here to avoid a runtime error.

🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/form/CreateEpicComment.vue around lines 2
to 4, the template uses createValidator(createEpicCommentSchema) but the
createValidator function is not imported; add an import statement in the <script
setup> block: import { createValidator } from '~/utils/ui' so the component can
call createValidator at runtime without error.

class="flex flex-col gap-3"
@submit="onSubmit"
>
<form
class="flex flex-col gap-4"
@submit.prevent="onCommentSubmit"
>
<UFormField label="Комментарий" name="text">
<UTextarea
v-model="text"
color="neutral"
variant="none"
size="xl"
required
autoresize
v-model="state.text"
placeholder="Напишите свою мысль..."
:rows="3"
:disabled="loading"
autoresize
size="xl"
class="w-full"
:ui="{ base: 'p-1' }"
/>
</UFormField>

<div class="flex items-center justify-end gap-2">
<UButton
type="submit"
color="secondary"
size="md"
:loading="loading"
:disabled="!text"
label="Добавить комментарий"
icon="i-lucide-send"
:ui="{
base: 'px-3 rounded-full',
label: 'font-medium',
}"
/>
</div>
</form>
</UCard>
<UButton
type="submit"
variant="solid"
color="secondary"
size="xl"
icon="i-lucide-send"
block
class="mt-3"
:disabled="!state.text"
:label="$t('common.send')"
/>
</UForm>
</template>

<script setup lang="ts">
import type { CreateEpicComment } from '#shared/services/epic'
import type { FormSubmitEvent } from '@nuxt/ui'
import { createEpicCommentSchema } from '#shared/services/epic'

const { epicId } = defineProps<{ epicId: string }>()

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

const { vibrate } = useFeedback()
const userStore = useUserStore()
const epicStore = useEpicStore()

const text = ref('')
const loading = ref(false)

async function onCommentSubmit() {
const trimmed = text.value.trim()
if (!trimmed) {
return
}
const state = ref<Partial<CreateEpicComment>>({
text: undefined,
})

loading.value = true
async function onSubmit(event: FormSubmitEvent<CreateEpicComment>) {
emit('submitted')

try {
await epicStore.addComment(epicId, trimmed)
text.value = ''
await $fetch(`/api/epic/id/${epicId}/comment`, {
method: 'POST',
headers: {
Authorization: `tma ${userStore.initDataRaw}`,
},
body: event.data,
})

await Promise.all([
epicStore.update(),
userStore.update(),
])

vibrate('success')
} catch (e) {
console.error(e)
emit('success')
} catch (error) {
console.error(error)
vibrate('error')
} finally {
loading.value = false
}
}
</script>
19 changes: 19 additions & 0 deletions apps/atrium-telegram/app/components/modal/CreateEpicComment.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<UModal :title="$t('app.create.epic-comment.title')">
<template #body>
<FormCreateEpicComment
:epic-id="epicId"
@submitted="overlay.closeAll"
@success="overlay.closeAll"
/>
Comment on lines +4 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t close the modal on submit start; close only on success.

Closing on @submitted hides errors and prevents retry UX.

Apply this diff:

       <FormCreateEpicComment
         :epic-id="epicId"
-        @submitted="overlay.closeAll"
         @success="overlay.closeAll"
       />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<FormCreateEpicComment
:epic-id="epicId"
@submitted="overlay.closeAll"
@success="overlay.closeAll"
/>
<FormCreateEpicComment
:epic-id="epicId"
@success="overlay.closeAll"
/>
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/modal/CreateEpicComment.vue around lines
4 to 8, the modal is being closed on the `@submitted` event which hides
validation/errors and blocks retry; remove the `@submitted="overlay.closeAll"`
binding and only call `overlay.closeAll` on the `@success` event so the modal
stays open while submission is in progress or failed and only closes after a
confirmed success.

</template>
</UModal>
</template>

<script setup lang="ts">
defineProps<{
epicId: string
}>()

const overlay = useOverlay()
</script>
10 changes: 8 additions & 2 deletions apps/atrium-telegram/app/pages/epic/[epicId]/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,17 @@
/>
</div>

<FormCreateEpicComment v-if="epic?.id" :epic-id="epic.id" />
<CreateCard
v-if="epic?.id"
:label="$t('app.create.epic-comment.button')"
icon="i-lucide-message-circle"
@click="modalCreateEpicComment.open({ epicId: epic.id })"
/>
</PageContainer>
</template>

<script setup lang="ts">
import { ModalUpdateEpic } from '#components'
import { ModalCreateEpicComment, ModalUpdateEpic } from '#components'

const { params } = useRoute('epic-epicId')

Expand All @@ -53,6 +58,7 @@ const epic = computed(() => epicStore.epics.find((e) => e.id === params.epicId))

const overlay = useOverlay()
const modalUpdateEpic = overlay.create(ModalUpdateEpic)
const modalCreateEpicComment = overlay.create(ModalCreateEpicComment)

function handleEditEpic() {
if (!epic.value?.id) {
Expand Down
26 changes: 0 additions & 26 deletions apps/atrium-telegram/app/stores/epic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,6 @@ export const useEpicStore = defineStore('epic', () => {
}
}

async function addComment(epicId: string, text: string) {
try {
await $fetch(`/api/epic/id/${epicId}/comment`, {
method: 'POST',
headers: {
Authorization: `tma ${initDataRaw.value}`,
},
body: {
text,
},
})

await update()
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('401')) {
// No
}
if (error.message.includes('404')) {
// Not found
}
}
}
}

async function removeComment(id: string) {
try {
await $fetch(`/api/epic/comment/id/${id}`, {
Expand All @@ -89,7 +64,6 @@ export const useEpicStore = defineStore('epic', () => {
epics,

update,
addComment,
removeComment,
}
})
4 changes: 4 additions & 0 deletions apps/atrium-telegram/i18n/locales/ru-RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
"button": "Создать эпик",
"title": "Создание эпика"
},
"epic-comment": {
"button": "Создать комментарий",
"title": "Создание комментария"
},
Comment on lines +43 to +46
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Cyrillic letter in “комментарий” (combining mark currently used).

The button label uses "комментарий" (и + combining breve). Replace with the correct single character "й".

Apply this diff:

       "epic-comment": {
-        "button": "Создать комментарий",
+        "button": "Создать комментарий",
         "title": "Создание комментария"
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"epic-comment": {
"button": "Создать комментарий",
"title": "Создание комментария"
},
"epic-comment": {
"button": "Создать комментарий",
"title": "Создание комментария"
},
🤖 Prompt for AI Agents
In apps/atrium-telegram/i18n/locales/ru-RU.json around lines 43-46, the button
label uses a Cyrillic "и" followed by a combining breve (resulting in
"комментарий"); replace that sequence with the single precomposed Cyrillic
character "й" so the string becomes "Создать комментарий". Ensure the file is
saved with UTF-8 encoding and no combining marks remain in the value.

"beacon": {
"button": "Создать маяк",
"title": "Создание маяка"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default defineEventHandler(async (event) => {
}

const comment = await repository.epic.createComment({
...data,
text: data.text.trim(),
userId: event.context.user.id,
epicId,
})
Comment on lines 21 to 25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Prevent whitespace-only comments (trim happens after validation).

If the schema allows a single character, strings like " " will pass validation and be saved as empty after trim. Trim first and reject empty.

Apply this diff:

-    const comment = await repository.epic.createComment({
-      text: data.text.trim(),
-      userId: event.context.user.id,
-      epicId,
-    })
+    const text = data.text.trim()
+    if (!text) {
+      throw createError({ statusCode: 400, message: 'Text is required' })
+    }
+    const comment = await repository.epic.createComment({
+      text,
+      userId: event.context.user.id,
+      epicId,
+    })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const comment = await repository.epic.createComment({
...data,
text: data.text.trim(),
userId: event.context.user.id,
epicId,
})
// Trim input and reject empty text before creation
const text = data.text.trim()
if (!text) {
throw createError({ statusCode: 400, message: 'Text is required' })
}
const comment = await repository.epic.createComment({
text,
userId: event.context.user.id,
epicId,
})
🤖 Prompt for AI Agents
In apps/atrium-telegram/server/api/epic/id/[epicId]/comment.post.ts around lines
21-25, the code trims the comment text only when creating the record which
allows whitespace-only strings to pass earlier validation; change the flow to
trim data.text immediately (e.g., const text = (data.text || "").trim()),
validate that text is non-empty and reject with a 4xx error if empty, then pass
the trimmed text into repository.epic.createComment so only non-empty, trimmed
comments are persisted.

Expand Down