Conversation
WalkthroughIntroduces a new "Awards" (Награды) feature across the hub-telegram app, including new UI components to display prospective points and account creation days, a new /points page, updated navigation routing, store state additions, database schema expansion, and a Russian pluralization utility function. Changes
Sequence DiagramsequenceDiagram
participant User
participant Page as pages/index.vue
participant Block as ProspectiveBlock
participant Store as userStore
participant Points as ProspectivePoints
participant Days as ProspectiveDays
User->>Page: View home page
Page->>Block: Mount ProspectiveBlock
Block->>Store: useUserStore()
Store-->>Block: {prospectivePoints, createdAt}
Block->>Points: Pass prospectivePoints
Points-->>Block: Render points display
Block->>Days: Pass createdAt
Days-->>Block: Render days display
Block-->>User: Display prospective info
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
apps/hub-telegram/app/pages/points/index.vue (1)
1-16: Placeholder page pending implementation.The rewards page currently displays "Будет чуть позже" (coming soon). This appears intentional for this PR.
Do you want me to help implement the rewards functionality, or will this be addressed in a follow-up PR?
apps/hub-telegram/app/components/ProspectiveDays.vue (1)
15-16: Consider caching the days calculation.The
dayscomputed usesDate.now(), which means it will recalculate on every access. If real-time updates aren't needed, consider computing it once:-const date = computed(() => new Date(createdAt)) -const days = computed(() => Math.floor((Date.now() - date.value.getTime()) / 86400000)) +const date = new Date(createdAt) +const days = Math.floor((Date.now() - date.getTime()) / 86400000)Alternatively, if you want the day count to update at midnight, use a ref with an interval.
apps/hub-telegram/shared/utils/helpers.ts (1)
3-17: Consider consolidating duplicate pluralizationRu implementations.The exact same
pluralizationRufunction exists inapps/atrium-telegram/shared/utils/helpers.ts. Consider moving it to a shared package that both apps can import to avoid duplication and ensure consistency.Possible locations:
- Create a shared
packages/i18n-utilspackage- Use a monorepo shared utilities package
This would make maintenance easier and prevent drift between implementations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
apps/hub-telegram/app/components/ProspectiveBlock.vue(1 hunks)apps/hub-telegram/app/components/ProspectiveDays.vue(1 hunks)apps/hub-telegram/app/components/ProspectivePoints.vue(1 hunks)apps/hub-telegram/app/composables/useNavigation.ts(1 hunks)apps/hub-telegram/app/pages/flow/[itemId]/index.vue(1 hunks)apps/hub-telegram/app/pages/index.vue(1 hunks)apps/hub-telegram/app/pages/points/index.vue(1 hunks)apps/hub-telegram/app/stores/user.ts(3 hunks)apps/hub-telegram/shared/utils/helpers.ts(1 hunks)packages/database/src/tables.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/hub-telegram/shared/utils/helpers.ts (1)
apps/atrium-telegram/shared/utils/helpers.ts (1)
pluralizationRu(5-19)
⏰ 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 (9)
apps/hub-telegram/app/pages/flow/[itemId]/index.vue (1)
30-35: LGTM! Clean UI simplification.The inline view counter is more compact than the previous avatars section. Note that this trades detailed viewer information for a cleaner interface.
apps/hub-telegram/app/pages/index.vue (2)
7-7: LGTM! Avatar styling enhanced.The increased size and primary border improve visual prominence.
12-12: LGTM! Dynamic user data integration.Replacing the hardcoded "Ты 1 уровня" text with the
ProspectiveBlockcomponent enables dynamic display of user points and days data from the store.apps/hub-telegram/app/composables/useNavigation.ts (1)
16-20: LGTM! Navigation correctly configured.The rewards navigation item is properly set up with the appropriate path, icon, and title.
apps/hub-telegram/app/components/ProspectivePoints.vue (1)
1-14: LGTM! Clean implementation.The component correctly uses Russian pluralization for the points label. Ensure
pluralizationRuis auto-imported (as noted in ProspectiveDays.vue review).packages/database/src/tables.ts (1)
29-29: I need to gather more information about the database setup and migrations structure.Now let me check the actual directory structure and configuration files:
Verify that the migration file for
prospectivePointshas been generated and included in this PR.Drizzle requires SQL migration files to be generated from schema changes using drizzle-kit generate, then applied to the database with drizzle-kit migrate. The search found no migration files containing
prospective_points—only the schema file where the column was defined. If you're using a code-first approach where your TypeScript schema is the source of truth, you'll need to apply that schema to the database either through Drizzle or external migration tools.Ensure you've run the migration generation step (e.g.,
drizzle-kit generate) and that the resulting migration files are committed to this PR.apps/hub-telegram/app/components/ProspectiveBlock.vue (1)
12-14: LGTM!The script setup is clean and correctly uses the composition API.
apps/hub-telegram/app/stores/user.ts (2)
19-20: LGTM!The new reactive properties are correctly initialized with appropriate types and default values.
106-107: LGTM!The new properties are correctly exposed in the store's public API, making them available for components to consume.
| <template> | ||
| <div class="grid grid-cols-2 gap-2"> | ||
| <NuxtLink to="/points"> | ||
| <ActiveCard> | ||
| <ProspectivePoints :points="userStore.prospectivePoints" /> | ||
| </ActiveCard> | ||
| </NuxtLink> | ||
| <ProspectiveDays :created-at="userStore.createdAt as string" /> | ||
| </div> | ||
| </template> |
There was a problem hiding this comment.
Handle null createdAt to prevent runtime errors.
Line 8 uses a type assertion as string on userStore.createdAt, which is typed as string | null in the store (line 19 of user.ts). If the user data hasn't loaded yet or the API response is missing this field, createdAt will be null, causing the ProspectiveDays component to receive null instead of a string. This could lead to runtime errors or unexpected behavior.
Consider one of these solutions:
Solution 1: Add a conditional render
<template>
- <div class="grid grid-cols-2 gap-2">
+ <div v-if="userStore.createdAt" class="grid grid-cols-2 gap-2">
<NuxtLink to="/points">
<ActiveCard>
<ProspectivePoints :points="userStore.prospectivePoints" />
</ActiveCard>
</NuxtLink>
- <ProspectiveDays :created-at="userStore.createdAt as string" />
+ <ProspectiveDays :created-at="userStore.createdAt" />
</div>
</template>Solution 2: Update ProspectiveDays to handle null
If ProspectiveDays should handle the null case internally, update its prop type to accept string | null and remove the type assertion:
- <ProspectiveDays :created-at="userStore.createdAt as string" />
+ <ProspectiveDays :created-at="userStore.createdAt" />📝 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.
| <template> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <NuxtLink to="/points"> | |
| <ActiveCard> | |
| <ProspectivePoints :points="userStore.prospectivePoints" /> | |
| </ActiveCard> | |
| </NuxtLink> | |
| <ProspectiveDays :created-at="userStore.createdAt as string" /> | |
| </div> | |
| </template> | |
| <template> | |
| <div v-if="userStore.createdAt" class="grid grid-cols-2 gap-2"> | |
| <NuxtLink to="/points"> | |
| <ActiveCard> | |
| <ProspectivePoints :points="userStore.prospectivePoints" /> | |
| </ActiveCard> | |
| </NuxtLink> | |
| <ProspectiveDays :created-at="userStore.createdAt" /> | |
| </div> | |
| </template> |
| <template> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <NuxtLink to="/points"> | |
| <ActiveCard> | |
| <ProspectivePoints :points="userStore.prospectivePoints" /> | |
| </ActiveCard> | |
| </NuxtLink> | |
| <ProspectiveDays :created-at="userStore.createdAt as string" /> | |
| </div> | |
| </template> | |
| <template> | |
| <div class="grid grid-cols-2 gap-2"> | |
| <NuxtLink to="/points"> | |
| <ActiveCard> | |
| <ProspectivePoints :points="userStore.prospectivePoints" /> | |
| </ActiveCard> | |
| </NuxtLink> | |
| <ProspectiveDays :created-at="userStore.createdAt" /> | |
| </div> | |
| </template> |
🤖 Prompt for AI Agents
In apps/hub-telegram/app/components/ProspectiveBlock.vue around lines 1-10, the
template force-casts userStore.createdAt to string even though the store types
it as string | null; update the component to avoid passing null: either (A) add
a conditional render or fallback in the template (e.g. only render
<ProspectiveDays> when userStore.createdAt is non-null or pass a safe fallback
like an empty string) or (B) change ProspectiveDays to accept string | null by
updating its prop type and internal logic to handle the null case; pick one
approach and implement the corresponding change so no forced type assertion
remains and null is safely handled.
| <template> | ||
| <Section class="motion-preset-slide-down motion-delay-400"> | ||
| <div class="flex flex-col gap-2"> | ||
| <UIcon name="i-lucide-calendar-check-2" class="size-8 text-primary" /> | ||
| <p class="text-base/5"> | ||
| Вы с нами уже <span class="font-bold">{{ days }} {{ pluralizationRu(days, ['день', 'дня', 'дней']) }}</span> | ||
| </p> | ||
| </div> | ||
| </Section> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| const { createdAt } = defineProps<{ createdAt: string }>() | ||
|
|
||
| const date = computed(() => new Date(createdAt)) | ||
| const days = computed(() => Math.floor((Date.now() - date.value.getTime()) / 86400000)) | ||
| </script> |
There was a problem hiding this comment.
🧩 Analysis chain
Verify pluralizationRu is auto-imported.
The pluralizationRu function is used but not explicitly imported. Ensure your build configuration auto-imports it, or add an explicit import.
🏁 Script executed:
#!/bin/bash
# Check auto-imports configuration
cat apps/hub-telegram/nuxt.config.ts | grep -A 10 "imports"Length of output: 0
I need to investigate further. The initial grep produced no output. Let me check if the file exists and explore how auto-imports are configured in this project.
🏁 Script executed:
#!/bin/bash
# Find nuxt config file and check its structure
echo "=== Looking for nuxt config files ==="
find . -name "nuxt.config.*" -type f 2>/dev/null | head -20
echo ""
echo "=== Full content of apps/hub-telegram/nuxt.config.ts ==="
if [ -f "apps/hub-telegram/nuxt.config.ts" ]; then
cat "apps/hub-telegram/nuxt.config.ts"
else
echo "File not found"
fi
echo ""
echo "=== Search for pluralizationRu definition ==="
rg -n "pluralizationRu" --type ts --type vue -C 2Length of output: 1868
The config extends '@roll-stack/ui', which could provide auto-imports. Let me search for the pluralizationRu function definition and check the shared UI package:
🏁 Script executed:
#!/bin/bash
# Search for pluralizationRu definition across all files
echo "=== Search for pluralizationRu definition ==="
rg -n "pluralizationRu" -C 2
echo ""
echo "=== Check @roll-stack/ui structure (packages/ui) ==="
find packages/ui -type f -name "*.ts" -o -name "*.js" | grep -E "(util|composable|index)" | head -20
echo ""
echo "=== Search for pluralizationRu in packages/ui ==="
rg -n "pluralizationRu" packages/ui/ -C 2
echo ""
echo "=== Check for auto-import configuration in packages/ui ==="
cat packages/ui/nuxt.config.ts 2>/dev/null || echo "Not found"Length of output: 14994
I found that pluralizationRu is defined in apps/hub-telegram/shared/utils/helpers.ts. Let me check how other components import it and verify the auto-import configuration:
🏁 Script executed:
#!/bin/bash
# Check how ProspectivePoints.vue (which also uses pluralizationRu) handles imports
echo "=== Full ProspectivePoints.vue file ==="
cat apps/hub-telegram/app/components/ProspectivePoints.vue
echo ""
echo "=== Check for app.config.ts ==="
cat apps/hub-telegram/app.config.ts 2>/dev/null || echo "Not found"
echo ""
echo "=== Look for plugins or auto-import setup ==="
find apps/hub-telegram -name "*.ts" -o -name "*.js" | xargs grep -l "auto.*import\|import.*auto" 2>/dev/null | head -10
echo ""
echo "=== Check for tsconfig/path aliases ==="
cat apps/hub-telegram/tsconfig.json 2>/dev/null | grep -A 10 "paths" || echo "No tsconfig found or no paths"Length of output: 658
Let me check if there are any explicit imports of pluralizationRu anywhere in the codebase:
🏁 Script executed:
#!/bin/bash
# Check if pluralizationRu is explicitly imported anywhere
echo "=== Files that import pluralizationRu ==="
rg -n "import.*pluralizationRu"
echo ""
echo "=== Check root nuxt.config.ts ==="
cat nuxt.config.ts 2>/dev/null || echo "Not found"
echo ""
echo "=== Check package.json for nuxt config ==="
cat package.json | grep -A 20 '"nuxt"'
echo ""
echo "=== Check for .nuxtrc or nuxt.config.* in root ==="
find . -maxdepth 1 -name "nuxt.config.*" -o -name ".nuxtrc"Length of output: 182
Add explicit import for pluralizationRu from shared utils.
The function is used on line 6 but not imported. It's defined in apps/hub-telegram/shared/utils/helpers.ts and located outside standard Nuxt auto-import directories. Add an explicit import statement at the top of the <script setup> block:
import { pluralizationRu } from '#shared/utils/helpers'Verify the correct import path alias for your shared utilities.
🤖 Prompt for AI Agents
In apps/hub-telegram/app/components/ProspectiveDays.vue around lines 1 to 17,
the template uses pluralizationRu on line 6 but the <script setup> block does
not import it; add an explicit import of pluralizationRu from the shared utils
before using it (e.g. import { pluralizationRu } from the correct shared
alias/path) and ensure the import path matches your project aliases (e.g.
'#shared/utils/helpers') so the function is available in the template.
| createdAt.value = data.createdAt | ||
| prospectivePoints.value = data.prospectivePoints |
There was a problem hiding this comment.
Validate API response fields to prevent type inconsistencies.
The code assigns data.createdAt and data.prospectivePoints without verifying these fields exist in the API response. If the API doesn't return these fields (e.g., during a deployment where the backend hasn't been updated yet, or due to an API error), the values will be set to undefined, violating the type contracts (string | null and number).
Apply this diff to add safe fallbacks:
focusedTaskId.value = data.focusedTaskId
- createdAt.value = data.createdAt
- prospectivePoints.value = data.prospectivePoints
+ createdAt.value = data.createdAt ?? null
+ prospectivePoints.value = data.prospectivePoints ?? 0📝 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.
| createdAt.value = data.createdAt | |
| prospectivePoints.value = data.prospectivePoints | |
| focusedTaskId.value = data.focusedTaskId | |
| createdAt.value = data.createdAt ?? null | |
| prospectivePoints.value = data.prospectivePoints ?? 0 |
🤖 Prompt for AI Agents
In apps/hub-telegram/app/stores/user.ts around lines 54-55, the code assigns
data.createdAt and data.prospectivePoints directly which can set undefined and
violate the expected types; update the assignments to validate types and provide
safe fallbacks: set createdAt.value to the string if typeof data.createdAt ===
'string' else null, and set prospectivePoints.value to the numeric value if
typeof data.prospectivePoints === 'number' else a safe default (e.g., 0).


Summary by CodeRabbit
New Features
User Interface Improvements