chore: visual rework#112
Conversation
WalkthroughUI adjustments in ActiveCard and EpicCard modify active scaling, wrapper structure, and timestamp layout. CreateEpicCommentBeacon switches recipient selection from multi-select to single-select, updating state, bindings, labels, and watcher logic while keeping submission behavior intact. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Form as CreateEpicCommentBeacon
participant API as Backend API
User->>Form: Open form
User->>Form: Select single recipient (selectedMember)
Note right of Form: state.usersId = [selectedMember.value]
User->>Form: Submit
Form->>API: POST /epic-comment/beacon { usersId: [id], ... }
API-->>Form: 200 OK / Error
alt Success
Form-->>User: Confirmation & UI update
else Error
Form-->>User: Error message
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
apps/atrium-telegram/app/components/EpicCard.vue (2)
3-11: Verify spacing after wrapper removalDropping the outer flex/column wrapper can collapse vertical rhythm between icon, title, and description. If spacing regressed, add a container with gap (e.g., flex-col gap-3.5) or use space-y utilities.
21-25: Reduce SSR hydration risk from timezone differencesFormatting with new Date(...) can render different days server vs client near midnight in differing timezones. Parse ISO and (optionally) pin timezone.
- v-text="format(new Date(epic.createdAt), 'от d MMMM yyyy', { locale: ru })" + v-text="format(parseISO(epic.createdAt), 'от d MMMM yyyy', { locale: ru })"Add import (outside this hunk):
import { format, parseISO } from 'date-fns'If you see hydration warnings, consider date-fns-tz’s formatInTimeZone to a fixed TZ (e.g., UTC).
apps/atrium-telegram/app/components/form/CreateEpicCommentBeacon.vue (3)
11-11: Localize static textUse i18n like surrounding strings for consistency.
- Отметьте, кому хотите отправить уведомление + {{ $t('beacon.selectRecipientHint') }}
84-85: Prefer explicit null initial stateClearer intent and simpler checks in templates.
-const selectedMember = ref<FormMember>() +const selectedMember = ref<FormMember | null>(null)
58-83: Avatar alt textEmpty alt is fine for decorative avatars; if used as identification, consider alt with member name.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
apps/atrium-telegram/app/components/ActiveCard.vue(1 hunks)apps/atrium-telegram/app/components/EpicCard.vue(1 hunks)apps/atrium-telegram/app/components/form/CreateEpicCommentBeacon.vue(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (4)
apps/atrium-telegram/app/components/form/CreateEpicCommentBeacon.vue (4)
41-41: Good disabled logic tied to usersId lengthSubmit gating aligns with schema and watch updates once the above fix is applied.
100-116: Network flow looks fine; consider user-facing error feedback laterConsole logging + haptics is acceptable for now in a chore PR.
1-45: No leftoverselectedMembersreferences in CreateEpicCommentBeacon.vue The onlyname="members"attribute remains to map into thestate.usersIdarray as designed.
20-30: Replace clearable suggestion with watcher reset
USelectMenu doesn’t support aclearableprop or@clearevent—update the existing watcher to clearusersIdwhenselectedMemberis falsy:watch(selectedMember, val => { state.value.usersId = val ? [val.value] : [] })Likely an incorrect or invalid review comment.
| @@ -1,5 +1,5 @@ | |||
| <template> | |||
| <div class="relative active:scale-90 duration-200" @click="vibrate()"> | |||
| <div class="relative active:scale-95 duration-200" @click="vibrate()"> | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Add keyboard a11y + proper transition + reduced-motion safeguards
Make the card operable via keyboard, and ensure the scale animation actually transitions (Tailwind needs a transition class). Also respect prefers-reduced-motion.
Apply:
- <div class="relative active:scale-95 duration-200" @click="vibrate()">
+ <div
+ class="relative active:scale-95 transition-transform duration-200 motion-reduce:transition-none motion-reduce:transform-none cursor-pointer"
+ role="button" tabindex="0"
+ @click="vibrate()"
+ @keydown.enter="vibrate()" @keydown.space.prevent="vibrate()"
+ >🏁 Script executed:
#!/bin/bash
# Verify existence of ActiveCard.vue and extract the relevant snippet
echo "Checking for ActiveCard.vue..."
if [ -f apps/atrium-telegram/app/components/ActiveCard.vue ]; then
echo "✅ Found ActiveCard.vue"
else
echo "❌ ActiveCard.vue not found"
exit 1
fi
echo
echo "Extracting lines around the clickable <div>..."
grep -n -C2 "active:scale-95" apps/atrium-telegram/app/components/ActiveCard.vueLength of output: 502
🏁 Script executed:
#!/bin/bash
# Show the start of the component to inspect script section
sed -n '1,200p' apps/atrium-telegram/app/components/ActiveCard.vue
echo
# Find where vibrate is defined or imported
grep -n "vibrate" apps/atrium-telegram/app/components/ActiveCard.vueLength of output: 474
Add keyboard accessibility, proper transition, and reduced-motion support to ActiveCard.vue
The clickable <div> needs a transition class for transforms, a visible focus target, cursor-pointer, and prefers-reduced-motion safeguards. Apply:
- <div class="relative active:scale-95 duration-200" @click="vibrate()">
+ <div
+ class="relative active:scale-95 transition-transform duration-200 motion-reduce:transition-none motion-reduce:transform-none cursor-pointer"
+ role="button" tabindex="0"
+ @click="vibrate()"
+ @keydown.enter="vibrate()" @keydown.space.prevent="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.
| <div class="relative active:scale-95 duration-200" @click="vibrate()"> | |
| <div | |
| class="relative active:scale-95 transition-transform duration-200 motion-reduce:transition-none motion-reduce:transform-none cursor-pointer" | |
| role="button" tabindex="0" | |
| @click="vibrate()" | |
| @keydown.enter="vibrate()" @keydown.space.prevent="vibrate()" | |
| > |
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/ActiveCard.vue around line 2, the
clickable <div> currently only has an active scale and click handler but lacks
keyboard accessibility, a proper transform transition, visible focus styling,
cursor affordance, and reduced-motion support; update the element to be
keyboard-focusable (add tabindex="0"), expose button semantics (role="button"),
handle Enter/Space keydown to invoke vibrate(), add a transition class that
targets transforms (e.g., transition-transform) and ensure cursor-pointer is
present, add a focus-visible style (ring or outline) so keyboard users see
focus, and respect prefers-reduced-motion by disabling or greatly reducing the
transform/transition when the user requests reduced motion (via a CSS class or
utility such as motion-reduce to remove the transform/transition).
| <UIcon name="i-lucide-message-circle" class="size-5" /> | ||
| <p>{{ epic?.comments.length }}</p> | ||
| </div> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Guard undefined comments; show 0 instead of blank
Prevents empty render when comments is missing.
- <p>{{ epic?.comments.length }}</p>
+ <p>{{ epic?.comments?.length ?? 0 }}</p>📝 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.
| <UIcon name="i-lucide-message-circle" class="size-5" /> | |
| <p>{{ epic?.comments.length }}</p> | |
| </div> | |
| <UIcon name="i-lucide-message-circle" class="size-5" /> | |
| <p>{{ epic?.comments?.length ?? 0 }}</p> | |
| </div> |
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/EpicCard.vue around lines 16 to 18, the
template directly renders epic?.comments.length which can be undefined and
results in a blank UI; update the binding to safely handle missing comments and
display 0 instead (for example, use a nullish/coalescing or conditional fallback
like epic?.comments?.length ?? 0 or compute a commentsCount that returns 0 when
epic or epic.comments is falsy) so the UI always shows a numeric value.
| <UFormField | ||
| label="Получатели" | ||
| label="Получатель" | ||
| name="members" | ||
| required | ||
| > |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Bind field name to schema key for proper error mapping
Schema validates usersId; UFormField name should match to surface errors under this field. Also localize label.
- <UFormField
- label="Получатель"
- name="members"
+ <UFormField
+ :label="$t('beacon.recipient')"
+ name="usersId"📝 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.
| <UFormField | |
| label="Получатели" | |
| label="Получатель" | |
| name="members" | |
| required | |
| > | |
| <UFormField | |
| :label="$t('beacon.recipient')" | |
| name="usersId" | |
| required | |
| > |
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/form/CreateEpicCommentBeacon.vue around
lines 15 to 19, the UFormField uses name="members" but the validation schema
expects usersId so errors won't map correctly; change the name prop to "usersId"
to match the schema and update the label to a localized string (use the existing
i18n key or wrap the label with $t('...')) so the field label is localized.
| watch(selectedMember, () => { | ||
| if (!selectedMember.value) { | ||
| return | ||
| } | ||
|
|
||
| state.value.usersId = selectedMembers.value?.map((member) => member?.value) | ||
| state.value.usersId = [selectedMember.value.value] | ||
| }) |
There was a problem hiding this comment.
Fix stale recipient on deselect; update immediately
Currently, clearing selection leaves previous usersId intact. Also initialize usersId from any pre-set selection.
-watch(selectedMember, () => {
- if (!selectedMember.value) {
- return
- }
-
- state.value.usersId = [selectedMember.value.value]
-})
+watch(selectedMember, (m) => {
+ state.value.usersId = m ? [m.value] : []
+}, { immediate: true })📝 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.
| watch(selectedMember, () => { | |
| if (!selectedMember.value) { | |
| return | |
| } | |
| state.value.usersId = selectedMembers.value?.map((member) => member?.value) | |
| state.value.usersId = [selectedMember.value.value] | |
| }) | |
| watch(selectedMember, (m) => { | |
| state.value.usersId = m ? [m.value] : [] | |
| }, { immediate: true }) |
🤖 Prompt for AI Agents
In apps/atrium-telegram/app/components/form/CreateEpicCommentBeacon.vue around
lines 86-92, the watcher for selectedMember must both clear usersId when
selection is removed and initialize usersId from any pre-set selection; update
the watcher so it sets state.value.usersId = selectedMember.value ?
[selectedMember.value.value] : [] and add the watcher option { immediate: true }
so the handler runs on setup to populate usersId from an existing selection.



Summary by CodeRabbit