Conversation
|
Caution Review failedThe pull request is closed. WalkthroughAdds a full credits system: translations across locales, DB schema/migration/types for credit grants/ledger/alerts, Supabase functions and Stripe checkout helpers, frontend Credits page with routing/tab, service APIs/types, tests, seeds, and removes legacy FEATURE_CREDITS_V2 and dashboard credits panel. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Frontend as Web UI (Credits.vue)
participant API_Start as App Function (/start-top-up)
participant Stripe
participant API_Complete as App Function (/complete-top-up)
participant DB as Supabase DB / RPC
User->>Frontend: Click "Top up" (orgId, quantity)
Frontend->>API_Start: POST /start-top-up (orgId, qty)
API_Start->>API_Start: resolveOrgStripeContext (auth, customerId)
API_Start->>Stripe: createOneTimeCheckout(productId, qty, successUrl)
Stripe-->>API_Start: session.url
API_Start-->>Frontend: return session URL
Frontend->>Stripe: Redirect user to Checkout
Stripe-->>Frontend: Redirect to successUrl?session_id=...
Frontend->>API_Complete: POST /complete-top-up (orgId, sessionId)
API_Complete->>Stripe: verify session, fetch line items
API_Complete->>DB: RPC top_up_usage_credits(p_org_id, amount, session/payment refs)
DB-->>API_Complete: grant/transaction info
API_Complete-->>Frontend: return grant and new balances
Frontend->>User: Show success and refresh organization state
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (18)
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 |
|
Don't merge it until #1231 is merged |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
messages/hi.json (1)
2-2: $schema is not a valid schema URLValue includes a newline and a Hindi note, likely breaking schema tooling. Use the canonical URL string only.
-"$schema": "https://inlang.com/schema/inlang-message-format\n\nकृपया ध्यान दें, URL को हम अनुवादित नहीं कर सकते हैं.", +"$schema": "https://inlang.com/schema/inlang-message-format",
🧹 Nitpick comments (23)
src/components/dashboard/Usage.vue (1)
643-689: Clarify guard and improve progress bar accessibility
- If intended to show zero-balance state, v-if="creditAvailable" hides the panel; consider using hasCreditSummary or explicit > 0 check per product decision.
- Add aria roles/labels to the usage bar for screen readers.
- <div v-if="creditsV2Enabled && !isLoading && creditAvailable" class="mb-6"> + <div v-if="creditsV2Enabled && !isLoading && creditAvailable > 0" class="mb-6"> ... - <div class="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700 overflow-hidden"> - <div - class="h-full rounded-full bg-emerald-500 transition-all" - :style="{ width: `${creditUsagePercent}%` }" - /> - </div> + <div class="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700 overflow-hidden" role="progressbar" + :aria-valuenow="creditUsagePercent" aria-valuemin="0" :aria-valuemax="100" + :aria-label="t('credits-used-in-period')"> + <div class="h-full rounded-full bg-emerald-500 transition-all" + :style="{ width: `${creditUsagePercent}%` }" /> + </div>messages/it.json (1)
275-286: English placeholders in Italian locale for creditsThese keys remain English; consider at least short Italian copies before release or explicitly accept fallback.
Also applies to: 292-301, 305-306
messages/zh-cn.json (1)
275-285: English placeholders in Simplified Chinese locale for creditsTranslate before shipping or confirm fallback-to-English is acceptable for this release.
Also applies to: 292-298, 299-301, 305-306
messages/fr.json (1)
275-285: English placeholders in French locale for creditsPlease localize or confirm temporary English is acceptable.
Also applies to: 292-301, 305-306
messages/pt-br.json (1)
275-285: English placeholders in pt-BR locale for creditsLocalize or accept English fallback for this iteration.
Also applies to: 292-301, 305-306
messages/hi.json (1)
275-285: English placeholders in Hindi locale for creditsTranslate or confirm English fallback for now.
Also applies to: 292-301, 305-306
src/services/stripe.ts (1)
125-145: Surface user feedback on invalid/missing parametersReturn null silently hides UX issues; show a toast so callers don’t need to duplicate checks.
-export async function completeCreditTopUp(orgId: string, sessionId: string) { - if (!orgId || !sessionId) - return null +export async function completeCreditTopUp(orgId: string, sessionId: string) { + if (!orgId || !sessionId) { + toast.error('Missing required checkout parameters') + return null + }supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql (4)
62-69: Balance snapshot timing may be inconsistent with ledger writeYou read balances (Lines 62-69) and then write the ledger transaction (Lines 70-88). If usage_credit_balances derives from transactions, the returned total/available and the recorded balance_after can diverge. Prefer inserting the transaction first, then re-querying balances for a consistent “after” snapshot to both store and return.
Would you confirm whether usage_credit_balances is a view over grants/transactions or a materialized table updated by triggers? If it’s the former, re-ordering is essential for consistency. I can propose the exact diff once confirmed.
Also applies to: 70-88, 90-94
28-30: Redundant/fragile role check; rely on SQL privileges or use SQLSTATEWith SECURITY DEFINER and GRANT EXECUTE only to service_role, the explicit role gate is redundant and string-comparison on current_setting is brittle. If you keep it, raise with SQLSTATE 42501 for proper privilege semantics.
Example tweak:
- IF current_user <> 'postgres' AND COALESCE(v_request_role, '') <> 'service_role' THEN - RAISE EXCEPTION 'insufficient_privileges'; + IF current_user <> 'postgres' AND COALESCE(v_request_role, '') <> 'service_role' THEN + RAISE EXCEPTION SQLSTATE '42501' USING MESSAGE = 'insufficient_privileges'; END IF;Also applies to: 109-109
40-60: Stripe/webhook idempotency hook missingsource_ref is stored but there’s no uniqueness guard to prevent double-granting on webhook retries. Consider a unique index on a stable id within p_source_ref (e.g., (org_id, (source_ref->>'event_id'))) and handle conflict DO NOTHING/RETURN existing.
I can draft a safe migration to add the constraint and make the INSERT idempotent.
Also applies to: 70-88
1-2: Seed fixtures update reminderCredits/top‑up features usually require seed data for tests and environments. Please update supabase/seed.sql accordingly and keep prior migrations unchanged. Based on learnings
I can prepare minimal seed rows for usage_credit_grants/usage_credit_transactions to back new tests.
messages/pl.json (2)
275-286: Untranslated strings in Polish locale (credit-transaction-*)All added values are in English. Please localize before release or confirm intentional fallback. Example: “Amount” → “Kwota”, “Balance after” → “Saldo po”.
292-301: Untranslated credits UI copyNew credits keys are English (“Credits”, “Need more credits?”, etc.). Localize or mark for translation to keep UI consistent for pl users.
Also applies to: 305-305
messages/id.json (2)
275-286: Translate credit-transaction- to Indonesian*Values are English in id locale. Please provide Indonesian translations (“Amount” → “Jumlah”, “Balance after” → “Saldo setelah”, etc.).
292-301: Translate credits UI strings“Credits”, “Buy credits”, and related descriptions are English; localize for id.json or confirm fallback policy.
Also applies to: 305-305
messages/ko.json (2)
275-286: Korean locale contains English credit-transaction stringsLocalize these values (“Amount” → “금액”, “Balance after” → “이후 잔액”, etc.) to avoid mixed-language UI.
292-301: Credits UI keys need Korean translations“Credits”, “Need more credits?”, “Buy credits”, etc., are English. Please translate or track as TODO before GA.
Also applies to: 305-305
messages/vi.json (2)
275-286: Translate credit-transaction- to Vietnamese*Currently English. Suggested examples: “Amount” → “Số tiền”, “Balance after” → “Số dư sau”.
292-301: Translate credits UI strings to VietnameseLocalize “Credits”, “Buy credits”, and descriptions for vi.json, or confirm intentional fallback.
Also applies to: 305-305
messages/tr.json (2)
275-286: Turkish locale: credit-transaction- values are English*Please translate (“Amount” → “Tutar”, “Balance after” → “Sonraki bakiye”, etc.) to maintain consistency.
292-301: Translate credits UI copyNew credits strings are English; localize for tr.json or mark for translation before release.
Also applies to: 305-305
messages/es.json (2)
275-286: Spanish locale has English credit-transaction stringsPlease translate (e.g., “Amount” → “Importe”, “Balance after” → “Saldo después”, “Refund” → “Reembolso”).
292-301: Credits UI strings need Spanish translationsLocalize “Credits”, “Need more credits?”, “Buy credits”, etc., to avoid mixed-language UI.
Also applies to: 305-305
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (29)
messages/de.json(1 hunks)messages/en.json(1 hunks)messages/es.json(1 hunks)messages/fr.json(1 hunks)messages/hi.json(1 hunks)messages/id.json(1 hunks)messages/it.json(1 hunks)messages/ja.json(1 hunks)messages/ko.json(1 hunks)messages/pl.json(1 hunks)messages/pt-br.json(1 hunks)messages/ru.json(1 hunks)messages/tr.json(1 hunks)messages/vi.json(1 hunks)messages/zh-cn.json(1 hunks)src/components/dashboard/Usage.vue(2 hunks)src/layouts/settings.vue(3 hunks)src/pages/settings/Account.vue(1 hunks)src/pages/settings/ChangePassword.vue(1 hunks)src/pages/settings/organization/Credits.vue(1 hunks)src/pages/settings/organization/Members.vue(1 hunks)src/pages/settings/organization/index.vue(1 hunks)src/services/stripe.ts(2 hunks)src/types/supabase.types.ts(29 hunks)supabase/functions/_backend/private/credits.ts(3 hunks)supabase/functions/_backend/utils/hono_middleware_stripe.ts(0 hunks)supabase/functions/_backend/utils/stripe.ts(1 hunks)supabase/functions/_backend/utils/supabase.types.ts(29 hunks)supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql(1 hunks)
💤 Files with no reviewable changes (1)
- supabase/functions/_backend/utils/hono_middleware_stripe.ts
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{vue,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use Vue.js with Tailwind CSS, favoring utility classes and composition-friendly patterns over bespoke CSS
Files:
src/components/dashboard/Usage.vuesrc/layouts/settings.vuesrc/pages/settings/organization/index.vuesrc/pages/settings/organization/Credits.vuesrc/pages/settings/organization/Members.vuesrc/pages/settings/ChangePassword.vuesrc/pages/settings/Account.vue
src/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
Use DaisyUI (d- prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent
Files:
src/components/dashboard/Usage.vuesrc/layouts/settings.vuesrc/pages/settings/organization/index.vuesrc/pages/settings/organization/Credits.vuesrc/pages/settings/organization/Members.vuesrc/pages/settings/ChangePassword.vuesrc/pages/settings/Account.vue
src/**/*.{vue,ts,js,tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Konsta components are reserved for safe area helpers; avoid importing konsta anywhere else in the app
Files:
src/components/dashboard/Usage.vuesrc/services/stripe.tssrc/layouts/settings.vuesrc/pages/settings/organization/index.vuesrc/pages/settings/organization/Credits.vuesrc/pages/settings/organization/Members.vuesrc/pages/settings/ChangePassword.vuesrc/pages/settings/Account.vuesrc/types/supabase.types.ts
{src/**/*.vue,src/styles/style.css}
📄 CodeRabbit inference engine (AGENTS.md)
Mirror the Capgo palette (deep slate bases, Extract azure highlight, soft radii) from src/styles/style.css when introducing new UI
Files:
src/components/dashboard/Usage.vuesrc/layouts/settings.vuesrc/pages/settings/organization/index.vuesrc/pages/settings/organization/Credits.vuesrc/pages/settings/organization/Members.vuesrc/pages/settings/ChangePassword.vuesrc/pages/settings/Account.vue
supabase/migrations/**
📄 CodeRabbit inference engine (AGENTS.md)
supabase/migrations/**: Use the Supabase CLI for every migration and operational task whenever possible; avoid manual changes through the dashboard or direct SQL
When a feature requires schema changes, create a single migration file with the Supabase CLI and keep editing that file until the feature ships; never edit previously committed migrations
A migration that introduces a new table may include seed inserts for that table, but treat that seeding as part of the current feature and do not modify previously committed migrations
Files:
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
🧠 Learnings (2)
📚 Learning: 2025-10-22T17:04:10.729Z
Learnt from: CR
PR: Cap-go/capgo#0
File: AGENTS.md:0-0
Timestamp: 2025-10-22T17:04:10.729Z
Learning: Applies to src/**/*.{vue,css} : Use Vue.js with Tailwind CSS, favoring utility classes and composition-friendly patterns over bespoke CSS
Applied to files:
src/pages/settings/organization/Members.vue
📚 Learning: 2025-10-22T17:04:10.729Z
Learnt from: CR
PR: Cap-go/capgo#0
File: AGENTS.md:0-0
Timestamp: 2025-10-22T17:04:10.729Z
Learning: Applies to supabase/seed.sql : Update supabase/seed.sql to back new or evolved tests; keep fixtures focused on current behavior while leaving committed migrations unchanged
Applied to files:
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sqlsrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
🧬 Code graph analysis (4)
supabase/functions/_backend/private/credits.ts (5)
supabase/functions/_backend/utils/hono.ts (3)
middlewareAuth(107-115)parseBody(227-238)simpleError(223-225)supabase/functions/_backend/utils/supabase.ts (2)
supabaseAdmin(74-83)hasOrgRight(242-259)supabase/functions/_backend/utils/utils.ts (1)
getEnv(116-120)supabase/functions/_backend/utils/loggin.ts (1)
cloudlog(3-15)supabase/functions/_backend/utils/stripe.ts (2)
createOneTimeCheckout(310-351)getStripe(8-13)
supabase/functions/_backend/utils/stripe.ts (2)
supabase/functions/_backend/utils/utils.ts (1)
existInEnv(112-114)supabase/functions/_backend/utils/loggin.ts (1)
cloudlog(3-15)
src/types/supabase.types.ts (1)
supabase/functions/_backend/utils/supabase.types.ts (3)
Json(1-7)Enums(2551-2566)Constants(2585-2680)
supabase/functions/_backend/utils/supabase.types.ts (1)
src/types/supabase.types.ts (7)
Json(1-7)Database(9-2466)TablesInsert(2501-2524)TablesUpdate(2526-2549)Enums(2551-2566)CompositeTypes(2568-2583)Constants(2585-2680)
⏰ 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: Run tests
🔇 Additional comments (1)
src/pages/settings/Account.vue (1)
11-14: Icon source switch to Heroicons looks goodImports align with usage (component vs raw). Please confirm vite-icon resolver config covers these heroicons paths.
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
messages/hi.json (2)
2-2: $schema must be a single URL (tooling breaks with embedded note/newlines).Replace the mixed URL+note with the canonical schema URL.
- "$schema": "https://inlang.com/schema/inlang-message-format\n\nकृपया ध्यान दें, URL को हम अनुवादित नहीं कर सकते हैं.", + "$schema": "https://inlang.com/schema/inlang-message-format",
887-887: Mistranslation: “used-in-period” reads as “menstruation period.”Update to a neutral period phrasing.
- "used-in-period": "मासिक धर्म के समय उपयोग किया जाता है", + "used-in-period": "अवधि में उपयोग",messages/ru.json (1)
2-2: $schema inválido: contém quebras de linha e texto adicional.Defina apenas a URL do esquema para compatibilidade com ferramentas.
- "$schema": "https://inlang.com/schema/inlang-message-format\n\nTranslation: https://inlang.com/schema/format-soobshcheniya-inlang", + "$schema": "https://inlang.com/schema/inlang-message-format",messages/ja.json (1)
2-2: Do not localize $schema URL (breaks tooling).$schema must be the exact URL. Remove added JP text/newlines.
Apply this diff:
- "$schema": "https://inlang.com/schema/inlang-message-format\n\nを日本語に翻訳します:\n\nhttps://inlang.com/schema/inlang-メッセージ形式", + "$schema": "https://inlang.com/schema/inlang-message-format",messages/es.json (1)
2-2: Restore exact $schema URL.Do not translate or alter the path; validators rely on it.
Apply this diff:
- "$schema": "https://inlang.com/schema/formato-de-mensaje-inlang", + "$schema": "https://inlang.com/schema/inlang-message-format",messages/tr.json (1)
2-2: Remove commentary; keep $schema as exact URL.Extra text breaks schema detection.
Apply this diff:
- "$schema": "https://inlang.com/schema/inlang-message-format\n\nThis is a URL and it cannot be translated.", + "$schema": "https://inlang.com/schema/inlang-message-format",messages/vi.json (1)
2-2: Fix $schema URL (must not be localized).Use the canonical URL.
Apply this diff:
- "$schema": "https://inlang.com/schema/dinh-dang-tin-nhan-inlang", + "$schema": "https://inlang.com/schema/inlang-message-format",messages/ko.json (1)
2-2: Keep $schema exact; remove localized note.Only the URL should remain.
Apply this diff:
- "$schema": "https://inlang.com/schema/inlang-message-format\n\n위의 웹사이트 주소는 언어에 관계없이 동일하게 유지됩니다.", + "$schema": "https://inlang.com/schema/inlang-message-format",
♻️ Duplicate comments (4)
messages/ru.json (1)
275-285: RU credit strings localized now; previous issue resolved.The earlier concern about English leftovers appears fixed; translations look consistent.
Also applies to: 292-301, 304-362, 364-367
messages/ja.json (1)
275-367: JP credit strings look translated and consistent.The new credit keys read naturally; nice work.
src/layouts/settings.vue (1)
20-20: Fix the feature flag to parse as a real boolean.
import.meta.envalways returns strings, so'false'remains truthy and the Credits tab stays visible even when the flag is disabled. Coerce the value before using it.-const creditsV2Enabled = import.meta.env.VITE_FEATURE_CREDITS_V2 +const creditsV2Enabled = String(import.meta.env.VITE_FEATURE_CREDITS_V2 ?? '').toLowerCase() === 'true'(vite.dev)
src/pages/settings/organization/Credits.vue (1)
20-20: Parse the feature flag to a boolean before use.
import.meta.envexposes flag values as strings, so'false'still evaluates truthy and this page never redirects when the flag is off. Normalize the value before using it.-const creditsV2Enabled = import.meta.env.VITE_FEATURE_CREDITS_V2 +const creditsV2Enabled = String(import.meta.env.VITE_FEATURE_CREDITS_V2 ?? '').toLowerCase() === 'true'(vite.dev)
🧹 Nitpick comments (1)
src/layouts/settings.vue (1)
93-95: Drop the leftover debug logs.These console statements add noise in production and don’t provide end-user value. Please remove them now that the feature flag logic is in place.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
messages/de.json(1 hunks)messages/en.json(1 hunks)messages/es.json(1 hunks)messages/fr.json(1 hunks)messages/hi.json(1 hunks)messages/id.json(1 hunks)messages/it.json(1 hunks)messages/ja.json(1 hunks)messages/ko.json(1 hunks)messages/pl.json(1 hunks)messages/pt-br.json(1 hunks)messages/ru.json(1 hunks)messages/tr.json(1 hunks)messages/vi.json(1 hunks)messages/zh-cn.json(1 hunks)src/layouts/settings.vue(3 hunks)src/pages/settings/organization/Credits.vue(1 hunks)src/services/stripe.ts(2 hunks)src/types/supabase.types.ts(18 hunks)supabase/functions/_backend/private/credits.ts(3 hunks)supabase/functions/_backend/utils/stripe.ts(1 hunks)supabase/functions/_backend/utils/supabase.types.ts(18 hunks)supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/services/stripe.ts
- messages/it.json
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.{vue,css}
📄 CodeRabbit inference engine (AGENTS.md)
Use Vue.js with Tailwind CSS, favoring utility classes and composition-friendly patterns over bespoke CSS
Files:
src/layouts/settings.vuesrc/pages/settings/organization/Credits.vue
src/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
Use DaisyUI (d- prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent
Files:
src/layouts/settings.vuesrc/pages/settings/organization/Credits.vue
src/**/*.{vue,ts,js,tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Konsta components are reserved for safe area helpers; avoid importing konsta anywhere else in the app
Files:
src/layouts/settings.vuesrc/pages/settings/organization/Credits.vuesrc/types/supabase.types.ts
{src/**/*.vue,src/styles/style.css}
📄 CodeRabbit inference engine (AGENTS.md)
Mirror the Capgo palette (deep slate bases, Extract azure highlight, soft radii) from src/styles/style.css when introducing new UI
Files:
src/layouts/settings.vuesrc/pages/settings/organization/Credits.vue
supabase/migrations/**
📄 CodeRabbit inference engine (AGENTS.md)
supabase/migrations/**: Use the Supabase CLI for every migration and operational task whenever possible; avoid manual changes through the dashboard or direct SQL
When a feature requires schema changes, create a single migration file with the Supabase CLI and keep editing that file until the feature ships; never edit previously committed migrations
A migration that introduces a new table may include seed inserts for that table, but treat that seeding as part of the current feature and do not modify previously committed migrations
Files:
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
🧠 Learnings (2)
📚 Learning: 2025-10-30T14:58:37.007Z
Learnt from: Dalanir
Repo: Cap-go/capgo PR: 1238
File: src/layouts/settings.vue:7-13
Timestamp: 2025-10-30T14:58:37.007Z
Learning: In the Cap-go/capgo repository, heroicons are imported using the pattern `~icons/heroicons/icon-name` (e.g., `~icons/heroicons/user`, `~icons/heroicons/bell`) without requiring `/outline/` or `/solid/` path segments. Solid variants use a `-solid` suffix in the icon name itself (e.g., `~icons/heroicons/arrow-path-solid`).
Applied to files:
src/layouts/settings.vue
📚 Learning: 2025-10-22T17:04:10.729Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-22T17:04:10.729Z
Learning: Applies to supabase/seed.sql : Update supabase/seed.sql to back new or evolved tests; keep fixtures focused on current behavior while leaving committed migrations unchanged
Applied to files:
src/types/supabase.types.tssupabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
🧬 Code graph analysis (4)
src/types/supabase.types.ts (1)
supabase/functions/_backend/utils/supabase.types.ts (1)
Json(1-7)
supabase/functions/_backend/private/credits.ts (5)
supabase/functions/_backend/utils/hono.ts (4)
MiddlewareKeyVariables(26-40)simpleError(223-225)middlewareAuth(107-115)parseBody(227-238)supabase/functions/_backend/utils/supabase.ts (2)
supabaseAdmin(74-83)hasOrgRight(242-259)supabase/functions/_backend/utils/utils.ts (1)
getEnv(116-120)supabase/functions/_backend/utils/loggin.ts (1)
cloudlog(3-15)supabase/functions/_backend/utils/stripe.ts (2)
createOneTimeCheckout(307-353)getStripe(8-13)
supabase/functions/_backend/utils/stripe.ts (2)
supabase/functions/_backend/utils/utils.ts (1)
existInEnv(112-114)supabase/functions/_backend/utils/loggin.ts (1)
cloudlog(3-15)
supabase/functions/_backend/utils/supabase.types.ts (1)
src/types/supabase.types.ts (6)
Database(9-2474)Json(1-7)TablesInsert(2509-2532)TablesUpdate(2534-2557)Enums(2559-2574)CompositeTypes(2576-2591)
⏰ 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: Run tests
🔇 Additional comments (4)
messages/hi.json (1)
275-285: Credits strings look consistent; placeholders preserved.The added credit and pricing keys read well in Hindi; {amount} is intact. LGTM.
Also applies to: 292-301, 304-362, 364-367
messages/fr.json (1)
275-285: Crédits/prix: bon libellé et variables conservées.Traductions fluides; {amount} conservé; unités cohérentes (GiB/To). LGTM.
Also applies to: 292-301, 304-362, 364-367
messages/pt-br.json (1)
275-285: Bloco de créditos e preços OK.Chaves novas consistentes; placeholder {amount} mantido. LGTM.
Also applies to: 292-301, 304-362, 364-367
messages/en.json (1)
275-285: Credit keys verified across all 15 locales with {amount} placeholder preserved.All verification checks pass: 86 baseline credit-related keys are consistently present across all locales (de, en, es, fr, hi, id, it, ja, ko, pl, pt-br, ru, tr, vi, zh-cn), and the {amount} placeholder is preserved in credits-top-up-total-estimate across the full locale set.
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
Outdated
Show resolved
Hide resolved
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
supabase/migrations/**
📄 CodeRabbit inference engine (AGENTS.md)
supabase/migrations/**: Use the Supabase CLI for every migration and operational task whenever possible; avoid manual changes through the dashboard or direct SQL
When a feature requires schema changes, create a single migration file with the Supabase CLI and keep editing that file until the feature ships; never edit previously committed migrations
A migration that introduces a new table may include seed inserts for that table, but treat that seeding as part of the current feature and do not modify previously committed migrations
Files:
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
🧠 Learnings (1)
📚 Learning: 2025-10-22T17:04:10.729Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-10-22T17:04:10.729Z
Learning: Applies to supabase/seed.sql : Update supabase/seed.sql to back new or evolved tests; keep fixtures focused on current behavior while leaving committed migrations unchanged
Applied to files:
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
⏰ 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: Run tests
🔇 Additional comments (3)
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql (3)
96-97: Previous critical issue fixed correctly.The missing final
RETURN;statement that was flagged in prior review has been properly addressed. The sequenceRETURN NEXT;followed byRETURN;is now correct for a plpgsql set-returning function.
82-82: Previous transaction type issue fixed.The transaction ledger now correctly uses
'purchase'instead of'grant'for Stripe-funded top-ups, ensuring proper classification for reporting and UI consistency.
1-114: Migration structure and compatibility.The migration properly wraps the function creation in an explicit transaction and uses Supabase CLI conventions (single migration file for the feature). The function grants execute permissions to
service_roleand includes a descriptive COMMENT. The implementation follows SQL and plpgsql best practices for a SECURITY DEFINER function with explicitsearch_pathsetting.
supabase/migrations/20251024155313_add_top_up_usage_credits_rpc.sql
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (4)
messages/ru.json (1)
297-411: Localizations for credits look good; translate “plan” valueThe credits and pricing blocks are now properly localized (no English leftovers, placeholders intact, and no duplicate
credits-balance), which is good.However, the
"plan"value is still English:"plan": "Plan"For consistency with the rest of the Russian locale, translate it, for example:
- "plan": "Plan", + "plan": "План",(or another RU term you standardize on, such as
"Тариф").Also applies to: 748-748
messages/pt-br.json (1)
297-411: Credits strings are localized; update “plan” to pt‑BRThe new credits, pricing, and top‑up keys are well localized in pt‑BR, with placeholders preserved and duplicate keys removed.
The
"plan"entry, however, is still English:"plan": "Plan"To match the rest of the locale, update it to Portuguese:
- "plan": "Plan", + "plan": "Plano",Also applies to: 748-748
messages/ko.json (1)
748-748: Translate “plan” key to Korean.Use locale-consistent term.
- "plan": "Plan", + "plan": "플랜",messages/zh-cn.json (1)
327-327: Unify to Simplified Chinese "积分" (remove Traditional "積分").The file mixes Traditional and Simplified characters for "credit points." Lines 327, 397–398, 399, 401, and 411 contain Traditional "積分", while the rest of the file uses Simplified "积分". Replace all occurrences with Simplified "积分" for consistency within zh-CN locale:
- "credits-pricing-description": "積分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", + "credits-pricing-description": "积分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", - "credits-pricing-disclaimer": "積分用于覆盖超出套餐包含限制的用量。積分需预付并在 12 个月内有效。", + "credits-pricing-disclaimer": "积分用于覆盖超出套餐包含限制的用量。积分需预付并在 12 个月内有效。", - "no-credits-available": "还没有積分。购买積分包以扩展套餐用量。", + "no-credits-available": "还没有积分。购买积分包以扩展套餐用量。", - "credits-top-up-quantity-label": "购买的積分", + "credits-top-up-quantity-label": "购买的积分", - "credits-top-up-quantity-invalid": "继续之前请输入有效的積分数量。", + "credits-top-up-quantity-invalid": "继续之前请输入有效的积分数量。", - "buy-credits": "购买積分", + "buy-credits": "购买积分",
🧹 Nitpick comments (3)
messages/de.json (1)
323-327: Use consistent German terminology (“Guthaben”) instead of “Credit”.Align with “credits”: “Guthaben”.
- "credits-balance": "Credit-Guthaben", + "credits-balance": "Guthaben", - "credits-next-expiration": "Nächste Credit-Ablauffrist", + "credits-next-expiration": "Nächste Guthaben-Ablauffrist", - "credits-pricing-title": "Credit-Preise", + "credits-pricing-title": "Guthaben-Preise",messages/fr.json (1)
382-395: Refine build‑tier phrasing to idiomatic French.Use “X minutes suivantes” and “100 premières minutes” for natural wording.
- "credits-pricing-build-tier-first-100": "Premières 100 minutes", + "credits-pricing-build-tier-first-100": "100 premières minutes", - "credits-pricing-build-tier-next-400": "Prochaines 400 minutes", + "credits-pricing-build-tier-next-400": "400 minutes suivantes", - "credits-pricing-build-tier-next-500": "Prochaines 500 minutes", + "credits-pricing-build-tier-next-500": "500 minutes suivantes", - "credits-pricing-build-tier-next-4000": "Prochaines 4 000 minutes", + "credits-pricing-build-tier-next-4000": "4 000 minutes suivantes", - "credits-pricing-build-tier-next-5000": "Prochaines 5 000 minutes", + "credits-pricing-build-tier-next-5000": "5 000 minutes suivantes", - "credits-pricing-build-tier-over-10000": "Au-delà de 10 000 minutes", + "credits-pricing-build-tier-over-10000": "Au‑delà de 10 000 minutes",messages/ja.json (1)
297-307: Add locale validation checks to prevent regressions.The suggestion to add a sanity check script is sound. The checks for duplicate keys, {amount} placeholder in
credits-top-up-total-estimate, and {current}/{total} placeholders incredits-pagination-labelare all valid and currently pass across all 15 locale files. Running these checks as part of CI would catch issues early if keys or placeholders are accidentally modified or removed.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
messages/de.json(2 hunks)messages/es.json(2 hunks)messages/fr.json(3 hunks)messages/hi.json(2 hunks)messages/id.json(2 hunks)messages/it.json(2 hunks)messages/ja.json(2 hunks)messages/ko.json(2 hunks)messages/pl.json(2 hunks)messages/pt-br.json(2 hunks)messages/ru.json(2 hunks)messages/tr.json(2 hunks)messages/vi.json(2 hunks)messages/zh-cn.json(2 hunks)src/constants/organizationTabs.ts(2 hunks)src/pages/settings/organization/Plans.vue(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
{supabase/functions/_backend/**/*.ts,src/**/*.{ts,vue}}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Backend code must use
./utils/import paths for utility functions; frontend code must use~/alias forsrc/directory imports
Files:
src/constants/organizationTabs.tssrc/pages/settings/organization/Plans.vue
{src/**/*.{ts,js,vue},supabase/**/*.{ts,js}}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Run
bun lint:fixbefore committing code to auto-fix linting issues; never commit without running this command
Files:
src/constants/organizationTabs.tssrc/pages/settings/organization/Plans.vue
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use single quotes and no semicolons per @antfu/eslint-config
Files:
src/constants/organizationTabs.tssrc/pages/settings/organization/Plans.vue
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript strict mode with path aliases mapping
~/tosrc/
Files:
src/constants/organizationTabs.ts
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}
📄 CodeRabbit inference engine (CLAUDE.md)
Mobile apps should use Capacitor with app ID
ee.forgr.capacitor_gofor native mobile functionality
Files:
src/constants/organizationTabs.tssrc/pages/settings/organization/Plans.vue
src/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.vue: Use<script setup>syntax exclusively in Vue components; do not use Options API
Use Tailwind utility classes for layout and spacing; use DaisyUI components (d-btn,d-input,d-card) for interactive elements
Use Konsta components ONLY for safe area helpers (top/bottom insets); avoid using Konsta for general UI components
src/**/*.vue: Use Vue 3 with Composition API and<script setup>syntax for frontend components
Style components using TailwindCSS with DaisyUI components
src/**/*.vue: Use DaisyUI (d-prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent
Konsta components are reserved for the safe area helpers; avoid importingkonstaanywhere else in the app
Files:
src/pages/settings/organization/Plans.vue
src/**/*.{vue,css}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Avoid custom CSS; prefer Tailwind utility composition and DaisyUI theming from
src/styles/style.css
src/**/*.{vue,css}: The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS
Capgo's look centers on deep slate bases with the 'Extract' azure highlight (--color-azure-500: #119eff) and soft radii; mirror the palette fromsrc/styles/style.csswhen introducing new UI
Files:
src/pages/settings/organization/Plans.vue
src/pages/**/*.vue
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/pages/**/*.vue: Frontend pages auto-route viaunplugin-vue-routerbased on file structure insrc/pages/, useuseRoute()for params anduseRouter()for navigation
UseuseRoute()fromvue-routerto access route parameters in page componentsUse file-based routing with unplugin-vue-router for frontend pages
Files:
src/pages/settings/organization/Plans.vue
🧠 Learnings (1)
📚 Learning: 2025-10-30T14:58:37.007Z
Learnt from: Dalanir
Repo: Cap-go/capgo PR: 1238
File: src/layouts/settings.vue:7-13
Timestamp: 2025-10-30T14:58:37.007Z
Learning: In the Cap-go/capgo repository, heroicons are imported using the pattern `~icons/heroicons/icon-name` (e.g., `~icons/heroicons/user`, `~icons/heroicons/bell`) without requiring `/outline/` or `/solid/` path segments. Solid variants use a `-solid` suffix in the icon name itself (e.g., `~icons/heroicons/arrow-path-solid`).
Applied to files:
src/constants/organizationTabs.ts
⏰ 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: Analyze (java-kotlin)
🔇 Additional comments (9)
src/constants/organizationTabs.ts (1)
4-13: Credits tab wiring and icon import look correct
IconCreditsimport path and ordering are consistent with existing heroicons usage, and the newcreditstab key matches the/settings/organization/creditsroute used by the new page. No issues from a routing or style perspective. Based on learnings, the icon import pattern is correct.messages/pl.json (1)
297-411: Polish credits translations and placeholders look consistentThe new credit transaction, pricing, daily breakdown, and top‑up strings are properly localized, placeholders (
{amount},{metric},{count},{current},{total}) are preserved, andcredits-balanceis no longer duplicated. No further changes needed here.messages/tr.json (1)
297-411: Turkish credits translations and pricing block are consistentThe added credit transaction, pricing, top‑up, and daily usage strings are correctly localized, the build‑time pricing block is no longer in English, and placeholders are preserved. No remaining issues in this section.
messages/es.json (1)
314-395: LGTM: credits, pricing, and top-up block reads well in Spanish.Copy, units, and placeholders look consistent.
Also applies to: 398-411
messages/ja.json (1)
297-307: Credits block localization looks solid.Strings are natural, placeholders are preserved ({amount}, {current}, {total}), and prior duplicate-key issues appear resolved. Nice work.
Also applies to: 314-346, 346-399, 399-411
messages/hi.json (1)
314-346: Credits localization: good coverage.New credits and pricing strings are consistent and placeholders intact.
Also applies to: 346-399, 399-411
messages/id.json (1)
382-395: Build-time pricing localized correctly.Terminology and number formatting are consistent with the rest of the file.
messages/vi.json (1)
382-396: Resolved: build pricing, “plan”, and top-up copy.
- Build-time pricing is fully localized.
- “plan” correctly translated.
- “Nạp tín dụng thành công.” fixes mixed-language phrase.
Also applies to: 402-402, 748-748
messages/fr.json (1)
297-307: Credits localization looks consistent and complete.Good coverage of transactions, pricing, and top‑up; placeholders preserved.
Also applies to: 314-346, 346-399, 399-411
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
messages/ko.json (2)
613-616: Duplicate JSON key: "general" — remove the later occurrence."general" appears twice; the later one overrides the earlier and breaks linting.
Apply this diff to keep the first value and remove the duplicate here:
- "general": "일반적인",
672-672: Localize “last-x-hours”.Value is still English. Suggested:
- "last-x-hours": "Last {hours} hours", + "last-x-hours": "지난 {hours}시간",messages/ja.json (1)
670-670: Localize “last-x-hours”.Translate to natural Japanese.
- "last-x-hours": "Last {hours} hours", + "last-x-hours": "過去{hours}時間",
♻️ Duplicate comments (8)
messages/pt-br.json (1)
816-816: Translate “plan”.Use pt-BR term for consistency.
- "plan": "Plan", + "plan": "Plano",messages/ja.json (1)
816-816: Translate “plan”.Align with surrounding plan keys.
- "plan": "Plan", + "plan": "プラン",messages/ko.json (1)
818-818: Translate “plan”.Use the locale term to match nearby keys.
- "plan": "Plan", + "plan": "플랜",messages/zh-cn.json (2)
391-391: Fix Simplified Chinese consistency (“积分”, not “積分”)Several credit strings use Traditional “積分” within zh-CN. Replace with Simplified “积分”. This aligns with earlier feedback.
Apply this diff:
- "credits-pricing-description": "積分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", + "credits-pricing-description": "积分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", @@ - "credits-pricing-disclaimer": "積分用于覆盖超出套餐包含限制的用量。積分需预付并在 12 个月内有效。", - "no-credits-available": "还没有積分。购买積分包以扩展套餐用量。", - "credits-top-up-quantity-label": "购买的積分", + "credits-pricing-disclaimer": "积分用于覆盖超出套餐包含限制的用量。积分需预付并在 12 个月内有效。", + "no-credits-available": "还没有积分。购买积分包以扩展套餐用量。", + "credits-top-up-quantity-label": "购买的积分", @@ - "credits-top-up-quantity-invalid": "继续之前请输入有效的積分数量。", + "credits-top-up-quantity-invalid": "继续之前请输入有效的积分数量。", @@ - "buy-credits": "购买積分", + "buy-credits": "购买积分",Also applies to: 461-466, 475-475
816-816: Translate “plan” to Simplified ChineseUse “计划” for consistency with other entries.
- "plan": "Plan", + "plan": "计划",messages/it.json (1)
816-816: Translate “plan” to ItalianUse “Piano” to match locale.
- "plan": "Plan", + "plan": "Piano",messages/id.json (1)
815-815: Localize “plan”.Use Indonesian for consistency.
- "plan": "Plan", + "plan": "Rencana",messages/hi.json (1)
815-815: Localize “plan”.Use Hindi to match surrounding keys.
- "plan": "Plan", + "plan": "योजना",
🧹 Nitpick comments (2)
messages/vi.json (2)
383-385: Use consistent term “tín dụng”.Align with other credit strings.
- "credits-used-in-period": "Số điểm đã sử dụng trong kỳ", + "credits-used-in-period": "Tín dụng đã dùng trong kỳ",
445-446: Prefer “xây dựng” over “build”.Keep Vietnamese wording consistent.
- "credits-pricing-build-title": "Thời gian build (phút)", - "credits-pricing-build-subtitle": "Tính cho mỗi phút build vượt quá những gì gói của bạn bao gồm.", + "credits-pricing-build-title": "Thời gian xây dựng (phút)", + "credits-pricing-build-subtitle": "Tính cho mỗi phút xây dựng vượt quá những gì gói của bạn bao gồm.",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
messages/de.json(2 hunks)messages/en.json(2 hunks)messages/es.json(2 hunks)messages/fr.json(3 hunks)messages/hi.json(2 hunks)messages/id.json(2 hunks)messages/it.json(2 hunks)messages/ja.json(2 hunks)messages/ko.json(2 hunks)messages/pl.json(2 hunks)messages/pt-br.json(2 hunks)messages/ru.json(2 hunks)messages/tr.json(2 hunks)messages/vi.json(2 hunks)messages/zh-cn.json(2 hunks)src/services/supabase.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/services/supabase.ts
- messages/ru.json
🧰 Additional context used
🪛 Biome (2.1.2)
messages/ko.json
[error] 435-436: The key create-a-free-account was already declared.
This where a duplicated key was declared again.
If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.
(lint/suspicious/noDuplicateObjectKeys)
⏰ 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: Run tests
🔇 Additional comments (8)
messages/tr.json (1)
360-371: LGTM: Turkish credits and pricing translations look good.Placeholders retained; pricing tiers coherent.
Also applies to: 378-468
messages/ko.json (1)
360-372: Sanity check for duplicates/placeholders in this file — verified.All checks passed: JSON is valid with 1054 keys and no duplicates. Required placeholders are intact:
credits-top-up-total-estimatehas{amount}credits-daily-deduction-titlehas{metric}credits-pagination-labelhas{current}and{total}messages/es.json (1)
447-460: Build pricing block translation looks solidPhrasing, numerals, and “por minuto” formatting are correct.
messages/en.json (1)
467-467: Placeholder and duplicate key verification passedAll locale files (de, en, es, fr, hi, id, it, ja, ko, pl, pt-br, ru, tr, vi, zh-cn) correctly include the
{amount}placeholder in thecredits-top-up-total-estimatekey, and none contain duplicate JSON keys.messages/de.json (1)
360-475: LGTM for new credits/pricing translations.Keys, placeholders, and pricing phrasing look consistent.
Also applies to: 378-389, 390-461, 462-475, 816-837
messages/hi.json (1)
359-474: All i18n sanity checks pass for new credits keys.Verified:
- No duplicate keys in messages/hi.json or other locale files
- All 4 new placeholders ({amount}, {metric}, {count}, {current}, {total}) present and correctly formatted across locale samples (en, es, fr, de tested)
The credits keys implementation is ready.
messages/fr.json (2)
445-458: Previous critical issue resolved: Build-time pricing now fully translated to French.All build-time pricing keys (lines 445–458) are now properly translated. The section maintains consistency with other pricing tiers and uses appropriate French terminology throughout.
359-474: New credits system keys fully translated and properly integrated.All 116+ new translation keys for the credits system—covering transactions, balance tracking, pricing tiers (MAU, bandwidth, storage, build), top-up workflows, and daily activity breakdowns—are translated to French with consistent terminology and formatting. Previous duplicate-key issues have been resolved. No syntax or structural issues detected.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
messages/ko.json (2)
292-299: Fix duplicate key “create-a-free-account” (JSON override bug)The key appears twice; the latter overrides the former and trips Biome. Remove one occurrence to keep a single definition.
Apply one of these diffs (keep whichever translation you prefer):
Option A — keep earlier, remove later:
@@ - "create-a-free-accoun": "무료 계정을 생성하십시오", - "create-a-free-account": "무료 계정을 생성하십시오", + "create-a-free-accoun": "무료 계정을 생성하십시오",Option B — keep later, remove earlier:
@@ - "create-a-free-account": "무료 계정을 생성하십시오", + /* removed duplicate; later definition kept */Also applies to: 372-376
14-16: Remove duplicate key “general” (JSON override bug)“general” is defined twice with different values, causing a silent override. Keep only one.
Suggested fix — remove the later one:
- "general": "일반적인",Also applies to: 612-616
♻️ Duplicate comments (1)
messages/zh-cn.json (1)
391-391: Unify “积分” (Simplified) — replace remaining Traditional “積分”.These changed lines still use “積分”; standardize to Simplified “积分” for zh-CN.
Apply:
- "credits-pricing-description": "積分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", + "credits-pricing-description": "积分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", - "credits-pricing-disclaimer": "積分用于覆盖超出套餐包含限制的用量。積分需预付并在 12 个月内有效。", + "credits-pricing-disclaimer": "积分用于覆盖超出套餐包含限制的用量。积分需预付并在 12 个月内有效。", - "no-credits-available": "还没有積分。购买積分包以扩展套餐用量。", + "no-credits-available": "还没有积分。购买积分包以扩展套餐用量。", - "credits-top-up-quantity-label": "购买的積分", + "credits-top-up-quantity-label": "购买的积分", - "credits-top-up-quantity-invalid": "继续之前请输入有效的積分数量。", + "credits-top-up-quantity-invalid": "继续之前请输入有效的积分数量。", - "buy-credits": "购买積分", + "buy-credits": "购买积分",To catch any other leftovers:
#!/bin/bash rg -n "積分" messages/zh-cn.json || echo "✓ No remaining Traditional characters for 积分"Also applies to: 461-466, 475-475
🧹 Nitpick comments (3)
messages/ru.json (1)
392-409: Unify “MAU” rendering across the localeAt the top “MAU” is localized as “МАУ” (Cyrillic), while pricing uses Latin “MAU” (e.g., “$0.003 за MAU”). Pick one and use it consistently across the file to avoid mixed terms (prefer keeping Latin “MAU” or consistently use “МАУ”).
Also applies to: 395-409
messages/ko.json (1)
816-819: Terminology consistency for “plan”Consider using “플랜” (commonly used in this locale) instead of “계획” to align with other plan-related strings.
- "plan": "계획", + "plan": "플랜",messages/id.json (1)
390-391: Minor copy tweak: “mencakup” reads more natural than “menutup”.Optional, improves fluency without changing meaning.
- "credits-pricing-description": "Kredit menutup penggunaan yang melampaui batas paket Anda. Gunakan tingkatan ini untuk memperkirakan jumlah yang perlu dibeli.", + "credits-pricing-description": "Kredit mencakup penggunaan yang melampaui batas paket Anda. Gunakan tingkatan ini untuk memperkirakan jumlah yang perlu dibeli.",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
messages/hi.json(2 hunks)messages/id.json(2 hunks)messages/it.json(2 hunks)messages/ja.json(2 hunks)messages/ko.json(2 hunks)messages/pt-br.json(2 hunks)messages/ru.json(2 hunks)messages/zh-cn.json(2 hunks)supabase/tests/26_test_rls_policies.sql(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- supabase/tests/26_test_rls_policies.sql
🧰 Additional context used
🪛 Biome (2.1.2)
messages/ko.json
[error] 435-436: The key create-a-free-account was already declared.
This where a duplicated key was declared again.
If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.
(lint/suspicious/noDuplicateObjectKeys)
⏰ 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: Run tests
🔇 Additional comments (3)
messages/ja.json (1)
360-370: Credits/pricing block localization looks goodTranslations are natural, placeholders ({metric}, {count}, {current}, {total}) preserved, and pricing tiers are consistent.
Also applies to: 378-476
messages/pt-br.json (1)
360-370: pt-BR credits/pricing block looks solidNatural translations, placeholders retained, and tier phrasing consistent.
Also applies to: 378-476
messages/hi.json (1)
359-369: LGTM: translations and placeholders read well.Strings are consistent; variables like {amount}/{metric}/{count} are intact. No action needed.
Also applies to: 377-390, 391-475, 815-815
| "credit-transaction-amount": "Importo", | ||
| "credit-transaction-balance": "Nuovo saldo", | ||
| "credit-transaction-description": "Descrizione", | ||
| "credit-transaction-deduction": "Detrazione per utilizzo", | ||
| "credit-transaction-expiry": "Scadenza", | ||
| "credit-transaction-grant": "Assegnazione", | ||
| "credit-transaction-manual_grant": "Assegnazione manuale", | ||
| "credit-transaction-occurred-at": "Data", | ||
| "credit-transaction-purchase": "Acquisto", | ||
| "credit-transaction-refund": "Rimborso", | ||
| "credit-transaction-type": "Tipo", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find the messages directory and check if it.json exists
find . -name "it.json" -path "*/messages/*" -type f 2>/dev/null | head -5Repository: Cap-go/capgo
Length of output: 74
🏁 Script executed:
#!/bin/bash
# Get the structure of messages directory
ls -la messages/ 2>/dev/null || find . -type d -name "messages" | head -3Repository: Cap-go/capgo
Length of output: 1143
🏁 Script executed:
#!/bin/bash
# Read the specific lines from messages/it.json (lines 360-370)
if [ -f "messages/it.json" ]; then
wc -l messages/it.json
echo "=== Lines 360-370 ==="
sed -n '360,370p' messages/it.json
fiRepository: Cap-go/capgo
Length of output: 619
🏁 Script executed:
#!/bin/bash
# Check lines 378-390
echo "=== Lines 378-390 ==="
sed -n '378,390p' messages/it.json
echo ""
echo "=== Lines 391-475 (sampling first 30 and last 10) ==="
sed -n '391,420p' messages/it.json
echo "... (middle section) ..."
sed -n '466,475p' messages/it.jsonRepository: Cap-go/capgo
Length of output: 3586
🏁 Script executed:
#!/bin/bash
# Check line 816
echo "=== Line 816 ==="
sed -n '816p' messages/it.jsonRepository: Cap-go/capgo
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Search for placeholders like {amount}, {metric}, {count}, {current}/{total}
echo "=== Placeholders found in messages/it.json ==="
rg -n '\{(amount|metric|count|current|total)\}' messages/it.json | head -20Repository: Cap-go/capgo
Length of output: 438
🏁 Script executed:
#!/bin/bash
# Check for duplicate keys in it.json
python3 << 'PYSCRIPT'
import json
import sys
try:
with open('messages/it.json', 'r', encoding='utf-8') as f:
content = json.load(f)
print(f"✓ Valid JSON with {len(content)} keys")
# Check for duplicates by reading line by line with lower-level parsing
with open('messages/it.json', 'r', encoding='utf-8') as f:
lines = f.readlines()
keys_seen = {}
for i, line in enumerate(lines, 1):
# Simple regex to extract keys
if '":' in line:
key_part = line.split('":')[0].strip().strip('"')
if key_part and not key_part.startswith('{'): # Skip non-key lines
if key_part in keys_seen:
print(f"DUPLICATE: '{key_part}' at line {i} (first seen at line {keys_seen[key_part]})")
else:
keys_seen[key_part] = i
if len(keys_seen) == len(content):
print(f"✓ No duplicate keys detected ({len(keys_seen)} unique keys)")
except json.JSONDecodeError as e:
print(f"✗ Invalid JSON: {e}")
sys.exit(1)
PYSCRIPTRepository: Cap-go/capgo
Length of output: 195
🏁 Script executed:
#!/bin/bash
# Search for all occurrences of "filter-actions"
echo "=== Searching for 'filter-actions' ==="
rg -n '"filter-actions"' messages/it.jsonRepository: Cap-go/capgo
Length of output: 161
🏁 Script executed:
#!/bin/bash
# Test the verification script provided in the review comment
cd /tmp && cp /repo/messages/*.json . && cat > verify_translations.sh << 'SCRIPT'
#!/bin/bash
set -euo pipefail
# 1) Fail fast on duplicate keys across all locales
for f in *.json; do
echo "Checking $f for duplicates..."
python3 - <<'PY' "$f"
import json,sys
def hook(items):
seen=set()
for k,_ in items:
if k in seen: raise SystemExit(f"Duplicate key in {sys.argv[1]}: {k}")
seen.add(k)
return dict(items)
json.load(open(sys.argv[1],encoding='utf-8'), object_pairs_hook=hook)
PY
done
echo "✓ No duplicate keys"
# 2) Ensure {amount} placeholder exists wherever total-estimate appears
fail=0
for f in *.json; do
if rg -n '"credits-top-up-total-estimate"' "$f" >/dev/null 2>&1; then
if ! rg -nP '\{amount\}' "$f" >/dev/null 2>&1; then
echo "Missing {amount} in $f"
fail=1
fi
fi
done
test $fail -eq 0 && echo "✓ All total-estimate keys include {amount}"
SCRIPT
chmod +x verify_translations.sh
./verify_translations.sh 2>&1 | head -50Repository: Cap-go/capgo
Length of output: 284
🏁 Script executed:
#!/bin/bash
# Run the verification script directly from repo root
set -euo pipefail
# Test the duplicate detection part on it.json
python3 << 'PYSCRIPT'
import json
import sys
def hook(items):
seen = set()
for k, _ in items:
if k in seen:
print(f"ERROR: Duplicate key found: {k}")
sys.exit(1)
seen.add(k)
return dict(items)
try:
with open('messages/it.json', encoding='utf-8') as f:
json.load(f, object_pairs_hook=hook)
print("✓ No duplicate keys detected")
except SystemExit as e:
print(f"✗ Duplicate key detection failed")
sys.exit(1)
PYSCRIPTRepository: Cap-go/capgo
Length of output: 131
🏁 Script executed:
#!/bin/bash
# Verify the specific placeholder check for total-estimate
echo "=== Checking total-estimate and {amount} placeholder ==="
if grep -n '"credits-top-up-total-estimate"' messages/it.json | grep -q '{amount}'; then
echo "✓ credits-top-up-total-estimate contains {amount}"
else
echo "✗ credits-top-up-total-estimate missing {amount}"
fi
# Also check all instances to be thorough
grep -n '"credits-top-up-total-estimate"' messages/it.jsonRepository: Cap-go/capgo
Length of output: 245
🏁 Script executed:
#!/bin/bash
# Check if the duplicate key issue exists in other locale files too
echo "=== Checking for 'filter-actions' duplicates in all locale files ==="
for f in messages/*.json; do
count=$(grep -c '"filter-actions"' "$f" || true)
if [ "$count" -gt 1 ]; then
echo "$f: $count occurrences"
grep -n '"filter-actions"' "$f"
fi
doneRepository: Cap-go/capgo
Length of output: 909
Critical issue: Duplicate keys found across all locale files.
The review's claim of "No duplicate keys observed" is incorrect. The "filter-actions" key appears twice in messages/it.json (lines 11 and 601), and this pattern repeats across all 8 locale files (de, it, ja, ko, pl, ru, tr, zh-cn). The second occurrence overwrites the first in valid JSON parsing.
The specific lines reviewed (360-370, 378-390, 391-475, 816) are correct and contain consistent, well-formed placeholders ({amount}, {metric}, {count}, {current}/{total}). However, the approval should be conditional on resolving the duplicate key issue first.
The suggested verification script using object_pairs_hook is accurate and correctly detects this problem—it should be run and acted upon before merging.
🤖 Prompt for AI Agents
In messages/it.json around lines 360 to 370 (and other locales where duplicates
exist), there are duplicate JSON keys (e.g., "filter-actions" appears twice)
which cause the second entry to overwrite the first; locate all duplicated keys
across the 8 locale files (de, it, ja, ko, pl, ru, tr, zh-cn), remove or
consolidate the duplicate entries so each key is unique, merge any differing
values into the correct single key (or rename if both are required), update the
translations accordingly, and re-run the provided object_pairs_hook detection
script to verify no duplicates remain before merging.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
messages/zh-cn.json (1)
390-390: Unify to Simplified Chinese "积分" (replace remaining "積分").The file uses mixed character encodings; these lines contain Traditional Chinese "積分" which must be replaced with Simplified Chinese "积分" for consistency with zh-cn locale:
- "credits-pricing-description": "積分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", + "credits-pricing-description": "积分可覆盖超出套餐限制的用量。使用以下分级来估算需要购买的数量。", - "credits-pricing-disclaimer": "積分用于覆盖超出套餐包含限制的用量。積分需预付并在 12 个月内有效。", + "credits-pricing-disclaimer": "积分用于覆盖超出套餐包含限制的用量。积分需预付并在 12 个月内有效。", - "no-credits-available": "还没有積分。购买積分包以扩展套餐用量。", + "no-credits-available": "还没有积分。购买积分包以扩展套餐用量。", - "credits-top-up-quantity-label": "购买的積分", + "credits-top-up-quantity-label": "购买的积分", - "credits-top-up-quantity-invalid": "继续之前请输入有效的積分数量。", + "credits-top-up-quantity-invalid": "继续之前请输入有效的积分数量。", - "buy-credits": "购买積分", + "buy-credits": "购买积分",messages/ko.json (1)
814-815: Use locale‑consistent term for “plan”.In this locale, “플랜” is commonly used across UI; switch from “계획” for consistency. This was raised earlier.
- "plan": "계획", + "plan": "플랜",
🧹 Nitpick comments (9)
messages/ru.json (2)
470-470: Use ICU plurals for Russian counts.Avoid "{count} ..." to handle 1/2/5 correctly.
- "credits-daily-deduction-count": "{count} списаний", + "credits-daily-deduction-count": "{count, plural, one {# списание} few {# списания} many {# списаний} other {# списания}}", - "credits-daily-transaction-count": "{count} транзакций", + "credits-daily-transaction-count": "{count, plural, one {# транзакция} few {# транзакции} many {# транзакций} other {# транзакции}}",Also applies to: 473-473
453-453: Prevent line breaks in large numerals.Use a non‑breaking thin space for thousands to avoid UI wraps.
- "credits-pricing-build-tier-next-4000": "Следующие 4 000 минут", + "credits-pricing-build-tier-next-4000": "Следующие 4 000 минут", - "credits-pricing-build-tier-next-5000": "Следующие 5 000 минут", + "credits-pricing-build-tier-next-5000": "Следующие 5 000 минут", - "credits-pricing-build-tier-over-10000": "Свыше 10 000 минут", + "credits-pricing-build-tier-over-10000": "Свыше 10 000 минут",Also applies to: 455-455, 457-457
messages/tr.json (1)
445-446: Prefer “Derleme” over “Build” for consistency.Align with Turkish terminology across UI.
- "credits-pricing-build-title": "Build süresi (dakika)", - "credits-pricing-build-subtitle": "Planınıza dahil olanın ötesinde yapılan build süresi için dakika başına.", + "credits-pricing-build-title": "Derleme süresi (dakika)", + "credits-pricing-build-subtitle": "Planınıza dahil olanın ötesindeki derleme için dakika başına.",messages/ja.json (1)
469-469: Use Japanese-style parentheses instead of em dash.Improves naturalness: “控除({metric})”.
- "credits-daily-deduction-title": "控除 — {metric}", + "credits-daily-deduction-title": "控除({metric})",messages/pt-br.json (1)
469-469: Add ICU plurals for Portuguese counts.Handle singular vs plural.
- "credits-daily-deduction-count": "{count} deduções", + "credits-daily-deduction-count": "{count, plural, one {# dedução} other {# deduções}}", - "credits-daily-transaction-count": "{count} transações", + "credits-daily-transaction-count": "{count, plural, one {# transação} other {# transações}}",Also applies to: 473-473
messages/es.json (1)
471-475: Make counters plural‑safe (ICU).Use ICU plurals so “1 deducción / 1 transacción” render correctly.
- "credits-daily-deduction-count": "{count} deducciones", + "credits-daily-deduction-count": "{count, plural, one {# deducción} other {# deducciones}}", - "credits-daily-transaction-count": "{count} transacciones", + "credits-daily-transaction-count": "{count, plural, one {# transacción} other {# transacciones}}",Please confirm the i18n runtime supports ICU plural syntax (inlang message format).
messages/it.json (1)
470-474: Plural‑safe counters (ICU).Ensure correct singular/plural for Italian.
- "credits-daily-deduction-count": "{count} detrazioni", + "credits-daily-deduction-count": "{count, plural, one {# detrazione} other {# detrazioni}}", - "credits-daily-transaction-count": "{count} transazioni", + "credits-daily-transaction-count": "{count, plural, one {# transazione} other {# transazioni}}",Confirm ICU plural support in the app’s i18n pipeline (inlang message format).
messages/hi.json (1)
469-469: Plural‑safe Hindi for deduction count (ICU).Render “1 कटौती” vs “2 कटौतियाँ”.
- "credits-daily-deduction-count": "{count} कटौतियाँ", + "credits-daily-deduction-count": "{count, plural, one {# कटौती} other {# कटौतियाँ}}",Confirm ICU plural handling in the Hindi locale runtime.
messages/de.json (1)
377-466: Unify tone (Sie/Ihr) and avoid English–German hybrids in credits copy.Current strings mix formal “Sie/Ihr” elsewhere with “du/dein” here, and use “Credit‑Guthaben/Credit‑Ablauffrist”. Suggest consistent formal tone and German terms.
Apply:
- "credits-balance": "Credit-Guthaben", + "credits-balance": "Guthaben", - "credits-next-expiration": "Nächste Credit-Ablauffrist", + "credits-next-expiration": "Nächste Guthaben-Ablauffrist", - "credits-pricing-description": "Credits decken die Nutzung über die Grenzen deines Tarifs hinaus ab. Nutze diese Stufen, um abzuschätzen, wie viele du kaufen solltest.", + "credits-pricing-description": "Credits decken die Nutzung über die Grenzen Ihres Tarifs hinaus ab. Verwenden Sie diese Stufen, um abzuschätzen, wie viele Sie kaufen sollten.", - "credits-pricing-mau-subtitle": "Pro Gerät, das sich mindestens einmal im Monat meldet.", + "credits-pricing-mau-subtitle": "Pro Gerät, das sich mindestens einmal im Monat meldet.", - "credits-pricing-bandwidth-subtitle": "Pro GiB, das zusätzlich zu deinem Tarif ausgeliefert wird.", + "credits-pricing-bandwidth-subtitle": "Pro GiB, das zusätzlich zu Ihrem Tarif ausgeliefert wird.", - "credits-pricing-storage-subtitle": "Pro GiB, das Speicherplatz für deine Releases belegt.", + "credits-pricing-storage-subtitle": "Pro GiB, das Speicherplatz für Ihre Releases belegt.", - "credits-pricing-build-subtitle": "Pro Build-Minute, die über das in deinem Tarif enthaltene hinausgeht.", + "credits-pricing-build-subtitle": "Pro Build‑Minute, die über das in Ihrem Tarif enthaltene hinausgeht.", - "no-credits-available": "Noch keine Credits. Kaufe ein Paket, um dein Planlimit zu erweitern.", + "no-credits-available": "Noch keine Credits. Kaufen Sie ein Paket, um Ihr Tariflimit zu erweitern.",Please also scan the remaining credits block for any lingering “dein/deinem/deines” and switch to “Ihr/Ihrem/Ihres” for consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
messages/de.json(2 hunks)messages/en.json(2 hunks)messages/es.json(2 hunks)messages/fr.json(4 hunks)messages/hi.json(2 hunks)messages/id.json(2 hunks)messages/it.json(2 hunks)messages/ja.json(2 hunks)messages/ko.json(2 hunks)messages/pl.json(2 hunks)messages/pt-br.json(2 hunks)messages/ru.json(3 hunks)messages/tr.json(2 hunks)messages/vi.json(2 hunks)messages/zh-cn.json(2 hunks)supabase/migrations/20251125184322_add_top_up_credits_system.sql(1 hunks)supabase/tests/00-supabase_test_helpers.sql(1 hunks)supabase/tests/26_test_rls_policies.sql(2 hunks)supabase/tests/32_test_usage_credits.sql(7 hunks)supabase/tests/33_credit_usage_alerts.sql(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- supabase/tests/00-supabase_test_helpers.sql
🚧 Files skipped from review as they are similar to previous changes (1)
- supabase/tests/33_credit_usage_alerts.sql
🧰 Additional context used
📓 Path-based instructions (2)
{supabase/functions/_backend/utils/postgress_schema.ts,supabase/migrations/**/*.sql}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Database schema must be defined in
utils/postgress_schema.tsusing Drizzle ORM and mirrored in SQL migrations
Files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
supabase/migrations/**/*.sql
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
supabase/migrations/**/*.sql: Never edit previously committed migration files; create new migration files withsupabase migration new <feature_slug>for schema changes
Runbun typesafter schema changes in migrations to regenerate TypeScript types for Supabase
supabase/migrations/**/*.sql: When a feature requires schema changes, create a single migration file withsupabase migration new <feature_slug>and keep editing that file until the feature ships; never edit previously committed migrations
A migration that introduces a new table may include seed inserts for that table, treating that seeding as part of the current feature and not modifying previously committed migrations
Do not create new cron jobs; instead update theprocess_all_cron_tasksfunction in a new migration file to add your job if needed
Files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
🧠 Learnings (8)
📚 Learning: 2025-12-05T17:34:36.569Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-05T17:34:36.569Z
Learning: Applies to supabase/seed.sql : Update `supabase/seed.sql` to back new or evolved tests; keep fixtures focused on current behavior while leaving committed migrations unchanged
Applied to files:
supabase/tests/26_test_rls_policies.sqlsupabase/tests/32_test_usage_credits.sqlsupabase/migrations/20251125184322_add_top_up_credits_system.sql
📚 Learning: 2025-12-05T17:34:12.533Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T17:34:12.533Z
Learning: Applies to supabase/seed.sql : Update `supabase/seed.sql` with new or changed test fixtures when adding database schema changes
Applied to files:
supabase/tests/26_test_rls_policies.sqlsupabase/tests/32_test_usage_credits.sqlsupabase/migrations/20251125184322_add_top_up_credits_system.sql
📚 Learning: 2025-12-05T17:34:36.569Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-05T17:34:36.569Z
Learning: Always cover database changes with Postgres-level tests and complement them with end-to-end tests for affected user flows
Applied to files:
supabase/tests/32_test_usage_credits.sql
📚 Learning: 2025-12-05T17:34:36.569Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-05T17:34:36.569Z
Learning: Applies to supabase/migrations/**/*.sql : A migration that introduces a new table may include seed inserts for that table, treating that seeding as part of the current feature and not modifying previously committed migrations
Applied to files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
📚 Learning: 2025-12-05T17:34:36.569Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-05T17:34:36.569Z
Learning: Applies to supabase/migrations/**/*.sql : Do not create new cron jobs; instead update the `process_all_cron_tasks` function in a new migration file to add your job if needed
Applied to files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
📚 Learning: 2025-12-05T17:34:12.533Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T17:34:12.533Z
Learning: Applies to supabase/migrations/**/*.sql : Never edit previously committed migration files; create new migration files with `supabase migration new <feature_slug>` for schema changes
Applied to files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
📚 Learning: 2025-12-05T17:34:36.569Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-05T17:34:36.569Z
Learning: Applies to supabase/migrations/**/*.sql : When a feature requires schema changes, create a single migration file with `supabase migration new <feature_slug>` and keep editing that file until the feature ships; never edit previously committed migrations
Applied to files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
📚 Learning: 2025-12-05T17:34:12.533Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-05T17:34:12.533Z
Learning: Applies to supabase/functions/_backend/**/*.ts : Use `getRightKey()` from `scripts/utils.mjs` to access environment-specific configuration values; never hard-code URLs or environment-specific values
Applied to files:
supabase/migrations/20251125184322_add_top_up_credits_system.sql
⏰ 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). (2)
- GitHub Check: Run tests
- GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (20)
messages/pl.json (1)
1-1055: Duplicate key issue from previous review has been resolved — no action required.All locale files now contain exactly 1053 unique keys with no duplicates. The critical structural issue flagged in the previous review (duplicate "filter-actions" and "credits-balance" keys) has been successfully fixed across pl.json and all related locale files (de.json, it.json, ja.json, ko.json, ru.json, tr.json, zh-cn.json). JSON validation confirms valid structure and consistency.
supabase/tests/26_test_rls_policies.sql (2)
6-6: Plan count updated correctly for new tests.Updated from 35 to 37 to account for the two new assertions on
usage_credit_ledger. The increment is correct.
298-322: Robust RLS and security_invoker assertions for usage_credit_ledger.The two new test assertions are well-designed:
- Lines 300–305 verify that the
authenticatedrole has SELECT privilege onusage_credit_ledger.- Lines 307–322 verify the view is configured with
security_invokerby queryingpg_class.reloptionsusing a flexibleLIKE 'security_invoker%'pattern. This correctly handles PostgreSQL's storage of view options as"keyword=value"strings.The approach is sound and more robust than exact-match checks.
supabase/tests/32_test_usage_credits.sql (5)
33-42: Test verifies source_ref disambiguation in top_up_usage_credits.Validates that the function definition includes the qualified column reference
t.source_refto avoid ambiguity in the unique_violation exception handler. This is a good regression test that prevents future refactors from inadvertently breaking the function's idempotency logic.
44-153: Test setup pattern correctly uses temp tables and CTEs for data persistence.The reorganized setup now properly chains CTEs and materializes results into a temporary table (
test_credit_context). This avoids the CTE scope issue flagged in the previous review—CTEs are defined within their WITH block and cannot be referenced in subsequent top-level statements, so persisting to a temp table and querying from it later is the correct approach.The dependency chain (plan → user → stripe_info → org → grant → step) is sound, with each CTE providing columns needed by subsequent CTEs.
155-177: Constraint validation test correctly uses throws_ok.Tests that the
sourcecolumn check constraint onusage_credit_grantsproperly rejects invalid values (e.g.,'invalid_source'). The expected error message matches PostgreSQL's constraint violation format.
305-384: Comprehensive concurrency and idempotency tests for top_up_usage_credits.Three assertions verify the critical idempotency guarantees:
- Duplicate RPC calls with identical source_ref return the same transaction_id (lines 346–357).
- Duplicate calls return the same grant_id (lines 359–370).
- Only one purchase transaction row is created in the database (lines 372–384).
This is excellent coverage for a payment-related function. The test isolates and validates the function's deduplication behavior, which is essential for preventing double-charging.
554-717: Ledger view CTE setup correctly chains dependencies and uses temp table persistence.The nested CTE structure (setup → context_insert → grant_one/two → overage → consumptions → deductions) properly chains inserts and returns. The final
SELECT 1statement executes the entire CTE block, ensuring all side effects (inserts) are committed.Later assertions (lines 719–743) correctly reference the persisted
test_usage_ledger_contexttemp table to validate ledger aggregation behavior (collapsing multiple grant deductions per overage event into one ledger row, with correct aggregated amounts).supabase/migrations/20251125184322_add_top_up_credits_system.sql (7)
3-13: Simplified credit_id approach in plans table.Adds
credit_idcolumn directly toplansrather than using a separate configuration table. This is cleaner than the earliercapgo_credit_productstable approach and addresses the hardcoding concern from the prior review by centralizing the product ID on the plan itself.The default value is set to a test Stripe product ID (
prod_TJRd2hFHZsBIPK), which is safe for a development/test default. Ensure seed or configuration management handles production product IDs appropriately.
17-44: Defensive DO block safely adds source constraint.Validates and enforces the
sourcecheck constraint onusage_credit_grants, updating any invalid/null values to the fallback'manual'before adding the constraint. The approach:
- Detects if constraint already exists (avoids errors on re-runs)
- Coalesces invalid values to a safe default
- Uses
format()to safely construct the constraint definitionDefensive programming well-applied for a production migration.
46-190: top_up_usage_credits function correctly implements security and idempotency.Security posture is sound:
SECURITY DEFINERallows the function to execute with elevated privileges (required for writes to credit tables from frontend context).SET search_path = ''mitigates search_path injection.- Role validation (line 82) restricts execution to
postgresorservice_roleonly.- Input validation on org_id, amount (lines 86–92).
Idempotency via subtransaction (lines 97–179):
- Wraps grant/transaction inserts in a subtransaction to catch
unique_violation.- Exception handler looks up the existing transaction by
sessionIdorpaymentIntentId(lines 153–163).- If found, returns the existing grant and transaction instead of creating duplicates.
- If not found, re-raises the exception (could indicate a different constraint violation).
This is a solid pattern for handling Stripe checkout idempotency (same session/payment intent → same grant/transaction).
205-267: Unique indexes on source_ref fields prevent duplicate purchases.The DO block creates conditional unique indexes on
source_ref->>'sessionId'andsource_ref->>'paymentIntentId':
- Pre-checks for existing duplicates (lines 217–245) to avoid migration failure.
- Indexes are conditional:
WHERE transaction_type = 'purchase' AND source_ref->>'key' IS NOT NULL.- Allows nulls and non-purchase transaction types to avoid over-constraining.
This is an excellent design—it prevents future duplicate purchases via database constraints while remaining flexible for other transaction types and edge cases.
469-619: usage_credit_ledger view correctly aggregates deductions with security_invoker.The view is defined with
security_invoker = trueandsecurity_barrier = true, enforcing RLS policies on underlying tables. The complex CTE logic:
- overage_allocations: Groups consumptions by overage event and aggregates grant allocations.
- aggregated_deductions: Collapses multiple deductions for the same overage event into one ledger row, summing amounts and preserving grant allocation details.
- other_transactions: Passes through purchase, expiry, and other non-deduction transactions unmodified.
- UNION ALL: Combines both branches.
The view correctly implements the ledger requirement: multiple deductions against different grants for the same overage event are consolidated into a single aggregated row in the ledger, reducing clutter while preserving full audit detail via the
grant_allocationsJSONB field.
625-701: Credit usage alert trigger correctly enqueues at threshold crossings.The
enqueue_credit_usage_alert()function fires on INSERT and:
- Ignores non-negative amounts (only alerts on deductions) (line 642).
- Calculates the percent used before and after the transaction (lines 664–665).
- Enqueues alerts only when crossing predefined thresholds (50%, 75%, 90%, 100%) (lines 669–689).
- Uses
alert_cycle = year*100 + monthto reset alerts monthly (line 667).The logic correctly prevents alert spam (one alert per threshold per cycle) while capturing the critical usage milestones. Integration with
process_all_cron_tasks(line 727) ensures alerts are processed frequently (every 10 seconds).
704-890: process_all_cron_tasks orchestrates all scheduled jobs with proper error isolation.The function schedules tasks at multiple cadences (every 10 seconds, every minute, every 5 minutes, hourly, every 2 hours, every 6 hours, and daily). Key observations:
- Each task is wrapped in its own
BEGIN...EXCEPTION WHEN OTHERS THENblock, ensuring failures do not cascade.credit_usage_alertsis processed every 10 seconds in the high-frequency queue (line 727), consistent with the alert trigger (line 699).- Credit expiry (
expire_usage_credits) is processed daily at 3 AM (line 848).- Task scheduling uses UTC time components (
EXTRACT) to ensure consistency.The error isolation and frequent polling of credit alerts ensures timely delivery without blocking other jobs.
messages/en.json (2)
2-2: ✓ Schema URL correctly updated to inlang-message-format.The Inlang message format is a simple storage plugin that stores simple messages in a JSON file per language using key-value pairs. The schema reference is now correct.
362-472: ✓ Comprehensive credits system translations added with proper structure.All 111 new credits-related keys (transactions, pricing tiers, UI labels, top-up flow) are properly formatted:
credit-transaction-*keys (11 entries) for ledger/transaction displaycredits-pricing-*keys with MAU, bandwidth, storage, and build-time tier pricingcredits-*keys for UI states (empty, loading, CTA, balance display, pagination)credits-top-up-*andcredits-daily-*keys for checkout and activity trackingbuy-creditsandplan-*keys for primary CTA and plan informationAll variable placeholders (
{amount},{current},{total},{count},{metric},{hours}) are correctly formatted. Terminology aligns with Capgo's usage model (MAU = Monthly Active Users, GiB = storage, etc.).messages/fr.json (3)
2-2: ✓ Schema URL correctly updated to match en.json.The schema reference is now aligned with the English file and the current Inlang standard.
359-472: ✓ All 111 credits keys now properly translated to French (resolves past critical issue).Previous reviews flagged that the build-time pricing section (lines 382–395 in the old version) remained in English. This critical issue is now resolved:
- Build-time pricing (lines 444–457): Properly translated with French terminology
- "Temps de build (minutes)" for the title
- "$0.50 par minute" for pricing (correctly localized currency format)
- All 12 build-time tier entries translated
- MAU, bandwidth, and storage pricing sections verified as complete with French translations
- Top-up flow and transaction keys translated consistently
- Placeholder syntax preserved: e.g.,
{amount},{current},{count}remain intactFrench terminology is idiomatic and consistent with standard SaaS localization conventions. No untranslated English fragments remain.
460-460: No action needed — duplicate key issue is already resolved.Verification confirms no duplicate keys exist in either en.json or fr.json, and perfect parity is maintained across both files with 130 credits-related keys present in each. The concern flagged in past reviews has been addressed.
| "created-at": "Creado en", | ||
| "credits-available": "Créditos disponibles", | ||
| "credits": "Créditos", | ||
| "credits-cta-description": "Compra créditos de uso adicionales al instante y mantén a tus usuarios recibiendo actualizaciones sin interrupciones.", | ||
| "credits-cta-title": "¿Necesitas más créditos?", | ||
| "credits-empty-state": "Aún no hay transacciones de créditos.", | ||
| "credits-load-error": "No pudimos cargar tu historial de créditos. Inténtalo de nuevo.", | ||
| "credits-transactions": "Transacciones de créditos", | ||
| "credits-used-in-period": "Créditos utilizados en el período", | ||
| "credits-used-dollars": "Créditos utilizados (USD)", | ||
| "credits-used-dollars-description": "Valor estimado en dólares de los créditos consumidos durante el período de facturación actual.", | ||
| "credits-balance": "Saldo de créditos", | ||
| "credits-available": "Créditos disponibles", | ||
| "credits-next-expiration": "Próximo vencimiento de créditos", |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
# List files in messages directory to understand locale structure
git ls-files messages/ | head -20Repository: Cap-go/capgo
Length of output: 335
🏁 Script executed:
# Check if messages/es.json exists and verify its content around lines 377-389
if [ -f messages/es.json ]; then
wc -l messages/es.json
sed -n '377,389p' messages/es.json
fiRepository: Cap-go/capgo
Length of output: 953
🏁 Script executed:
# Search for the keys mentioned in the script to see if they exist
rg "credits-top-up-total-estimate|credits-daily-deduction-title|credits-daily-deduction-count|credits-pagination-label|credits-daily-transaction-count" messages/Repository: Cap-go/capgo
Length of output: 5943
🏁 Script executed:
# Check for actual duplicate keys in all locale JSON files
for f in messages/*.json; do
python3 - <<'PY' "$f"
import json, sys
try:
with open(sys.argv[1], 'r', encoding='utf-8') as file:
data = json.load(file)
# Check if any keys are duplicated by reading with object_pairs_hook
seen = set()
duplicates = []
with open(sys.argv[1], 'r', encoding='utf-8') as file:
content = file.read()
import re
keys = re.findall(r'"([^"]+)"\s*:', content)
for k in keys:
if k in seen:
duplicates.append(k)
seen.add(k)
if duplicates:
print(f"{sys.argv[1]}: Duplicates found: {duplicates}")
else:
print(f"{sys.argv[1]}: OK")
except Exception as e:
print(f"{sys.argv[1]}: Error - {e}")
PY
doneRepository: Cap-go/capgo
Length of output: 403
🏁 Script executed:
# Verify JSON validity and check if all locale files are valid JSON
for f in messages/*.json; do
python3 -m json.tool "$f" > /dev/null 2>&1 && echo "$f: Valid JSON" || echo "$f: Invalid JSON"
doneRepository: Cap-go/capgo
Length of output: 496
Add translation QA checks to catch duplicate keys and placeholder drift.
The locale files contain at least one duplicate key (general in messages/pt-br.json) that would be caught by the proposed checks. Implementing duplicate key detection and placeholder validation across all locales (messages/*.json) will prevent similar issues in future translations.
🤖 Prompt for AI Agents
In messages/es.json around lines 377 to 389, the review notes missing QA checks
that allow duplicate translation keys and placeholder drift across locale files;
add a validation step that scans all messages/*.json to (1) detect duplicate
keys within each file (fail CI on duplicates), (2) ensure every locale has the
same key set as the canonical locale (e.g., messages/en.json) and report
missing/extra keys, and (3) validate placeholder tokens (e.g., {name}, %s) match
exactly between each translation and the canonical string; implement this as a
small node script or use an existing linter, wire it into CI to fail on
violations, and run it once to identify and fix the existing
duplicate/placeholder issues (then correct the locale files accordingly).
| "credits-used-in-period": "Số điểm đã sử dụng trong kỳ", | ||
| "credits-used-dollars": "Tín dụng đã dùng (USD)", | ||
| "credits-used-dollars-description": "Giá trị đô la ước tính của số tín dụng đã sử dụng trong kỳ thanh toán hiện tại.", | ||
| "credits-balance": "Số dư tín dụng", |
There was a problem hiding this comment.
Use “tín dụng” consistently.
Avoid mixing “điểm” with “tín dụng” in the credits surface.
- "credits-used-in-period": "Số điểm đã sử dụng trong kỳ",
+ "credits-used-in-period": "Tín dụng đã dùng trong kỳ",📝 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.
| "credits-used-in-period": "Số điểm đã sử dụng trong kỳ", | |
| "credits-used-dollars": "Tín dụng đã dùng (USD)", | |
| "credits-used-dollars-description": "Giá trị đô la ước tính của số tín dụng đã sử dụng trong kỳ thanh toán hiện tại.", | |
| "credits-balance": "Số dư tín dụng", | |
| "credits-used-in-period": "Tín dụng đã dùng trong kỳ", | |
| "credits-used-dollars": "Tín dụng đã dùng (USD)", | |
| "credits-used-dollars-description": "Giá trị đô la ước tính của số tín dụng đã sử dụng trong kỳ thanh toán hiện tại.", | |
| "credits-balance": "Số dư tín dụng", |
🤖 Prompt for AI Agents
messages/vi.json around lines 382 to 385: the translation for
"credits-used-in-period" uses "Số điểm đã sử dụng trong kỳ" which mixes "điểm"
with "tín dụng"; change it to use "tín dụng" consistently (e.g., "Số tín dụng đã
sử dụng trong kỳ"), and review the surrounding keys to ensure all credit-related
strings use "tín dụng" instead of "điểm".
|
Events for bento alerts email: const EVENT_BY_THRESHOLD: Record<number, string> = { |
|



Summary
Test plan
Quiet complex
Screenshots
Checklist
bun run lint:backend && bun run lint.accordingly.
my tests
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.