feat: add realtime connections and messages metrics to usage reports#2934
feat: add realtime connections and messages metrics to usage reports#2934lohanidamodar wants to merge 11 commits intomainfrom
Conversation
Console (appwrite/console)Project ID: Tip Build commands execute in runtime containers during deployment |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughUpdates add realtime billing and usage features and dependency tweaks. package.json updates include swapping an Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
# Conflicts: # bun.lock # package.json
Greptile SummaryThis PR adds realtime connections, messages, and bandwidth metrics to both the organization-level and project-level usage reports, along with supporting billing plan summary and usage rate display. The implementation follows existing patterns for other metrics (bar charts, progress bars, project breakdowns, empty states). Key changes:
One concrete inconsistency: the early-return stub in Confidence Score: 4/5Safe to merge after adding the two missing The feature implementation is complete and consistent with existing patterns; the only concrete bug is the missing
Important Files Changed
Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile |
src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts
Show resolved
Hide resolved
src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/routes/(console)/organization-[organization]/usage/[[invoice]]/ProjectBreakdown.svelte (1)
97-109:⚠️ Potential issue | 🟠 MajorHandle
screenshotsGeneratedinformat().The organization usage page still passes
metric="screenshotsGenerated"into this component, but this switch never formats that case, so those breakdown rows end up with an empty Usage cell.Suggested fix
switch (metric) { case 'imageTransformations': + case 'screenshotsGenerated': case 'authPhoneTotal': return formatNumberWithCommas(value);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/ProjectBreakdown.svelte around lines 97 - 109, The switch in the format(...) function inside ProjectBreakdown.svelte is missing a case for 'screenshotsGenerated', causing empty usage cells; add a case 'screenshotsGenerated' (alongside 'imageTransformations'/'authPhoneTotal') and return formatNumberWithCommas(value) so screenshot counts render correctly. Ensure you update the same switch block in the format function so other metrics keep their existing formatting.
🧹 Nitpick comments (1)
src/lib/components/billing/usageRates.svelte (1)
84-95: Consider de-duplicating the usage row rendering.Lines 84-95 repeat most of the same row/rate markup from the
limit !== falsebranch. You can compute the limit label once and render a single row path.♻️ Suggested simplification
{`#each` Object.entries(org.billingPlanDetails.usage) as [key, usage]} {`@const` limit = getPlanLimit(key)} - {`#if` limit !== false} + {`@const` limitLabel = + limit !== false + ? `${abbreviateNumber(limit)}${usage.unit}` + : usage.price > 0 + ? 'Pay-as-you-go' + : null} + {`#if` limitLabel} <Table.Row.Base {root}> <Table.Cell column="resource" {root}>{usage.name}</Table.Cell> - <Table.Cell column="limit" {root}> - {abbreviateNumber(limit)}{usage.unit} - </Table.Cell> + <Table.Cell column="limit" {root}>{limitLabel}</Table.Cell> {`#if` !isFree} <Table.Cell column="rate" {root}> {formatCurrency(usage.price)}/{abbreviateNumber( usage.value )}{usage.unit} </Table.Cell> {/if} </Table.Row.Base> - {:else if usage.price > 0} - <Table.Row.Base {root}> - <Table.Cell column="resource" {root}>{usage.name}</Table.Cell> - <Table.Cell column="limit" {root}>Pay-as-you-go</Table.Cell> - {`#if` !isFree} - <Table.Cell column="rate" {root}> - {formatCurrency(usage.price)}/{abbreviateNumber( - usage.value - )}{usage.unit} - </Table.Cell> - {/if} - </Table.Row.Base> {/if} {/each}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/components/billing/usageRates.svelte` around lines 84 - 95, The two branches render almost identical Table.Row.Base markup; compute a single limit label (e.g., const limitLabel = usage.limit !== false ? usage.limit : 'Pay-as-you-go') before the template and render one Table.Row.Base path that uses limitLabel and conditionally renders the rate cell when !isFree (using existing formatCurrency, abbreviateNumber, usage.unit), replacing the duplicated row in both the `limit !== false` and `usage.price > 0` branches so only one row template remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@package.json`:
- Line 37: The "cron-parser" dependency line in package.json has extra leading
whitespace causing malformed JSON/formatting errors; edit the package.json
dependency block and normalize the indentation to match the other entries (align
"cron-parser": "^4.9.0" with the same 8-space indentation used by the
surrounding dependency entries) so the JSON is properly formatted and passes
prettier/JSON parsing.
- Line 94: The package override for the transitive dependency "flatted" pins an
insecure version (^3.4.0) vulnerable to CVE-2026-33228; update the override in
package.json to "^3.4.2" (minimum secure version) and re-run your package
manager (npm/yarn/pnpm install) and dependency resolution to ensure no conflicts
with other pinned versions, then run npm/yarn audit (or equivalent) and the test
suite to confirm the upgrade is safe.
In
`@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/+page.svelte:
- Around line 36-37: Replace the unsafe cast by giving orgUsage an explicit
inline type instead of "as any" in the +page.svelte file (e.g., declare $:
orgUsage: { projects: ..., usage: ..., realtimeBandwidth: number;
realtimeBandwidthTotal: number; /* include other used fields */ } =
data.organizationUsage) so template accesses remain typed, and update the
fallback object defined in +page.ts to include realtimeBandwidth and
realtimeBandwidthTotal (with sensible default numeric values) so the template's
realtime fields never guard-fail; ensure the property names exactly match
realtimeBandwidth and realtimeBandwidthTotal and that any other realtime fields
referenced in the template are present in the fallback object.
In `@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/+page.ts:
- Around line 38-42: The migration fallback object is missing realtime bandwidth
fields and should match the normal load shape; update the fallback that
currently sets screenshotsGeneratedTotal, realtimeConnections,
realtimeConnectionsTotal, realtimeMessages, and realtimeMessagesTotal to also
include realtimeBandwidth and realtimeBandwidthTotal (set both to null) so
organizationUsage and any code reading organizationUsage.realtimeBandwidth and
organizationUsage.realtimeBandwidthTotal will receive the same shape as the
regular load path.
---
Outside diff comments:
In
`@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/ProjectBreakdown.svelte:
- Around line 97-109: The switch in the format(...) function inside
ProjectBreakdown.svelte is missing a case for 'screenshotsGenerated', causing
empty usage cells; add a case 'screenshotsGenerated' (alongside
'imageTransformations'/'authPhoneTotal') and return
formatNumberWithCommas(value) so screenshot counts render correctly. Ensure you
update the same switch block in the format function so other metrics keep their
existing formatting.
---
Nitpick comments:
In `@src/lib/components/billing/usageRates.svelte`:
- Around line 84-95: The two branches render almost identical Table.Row.Base
markup; compute a single limit label (e.g., const limitLabel = usage.limit !==
false ? usage.limit : 'Pay-as-you-go') before the template and render one
Table.Row.Base path that uses limitLabel and conditionally renders the rate cell
when !isFree (using existing formatCurrency, abbreviateNumber, usage.unit),
replacing the duplicated row in both the `limit !== false` and `usage.price > 0`
branches so only one row template remains.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c90fe848-0ef2-4ac3-8cf9-270479c9a415
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
package.jsonsrc/lib/components/billing/usageRates.sveltesrc/lib/stores/billing.tssrc/routes/(console)/organization-[organization]/billing/planSummary.sveltesrc/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.sveltesrc/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.tssrc/routes/(console)/organization-[organization]/usage/[[invoice]]/ProjectBreakdown.sveltesrc/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte
src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte
Outdated
Show resolved
Hide resolved
src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/lib/components/billing/alerts/realtimePricing.svelte (1)
4-5: Combine imports from the same module.✨ Suggested improvement
- import { organization } from '$lib/stores/organization'; - import { currentPlan } from '$lib/stores/organization'; + import { organization, currentPlan } from '$lib/stores/organization';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/components/billing/alerts/realtimePricing.svelte` around lines 4 - 5, Two separate imports from the same module should be combined into a single import to clean up the import statements; replace the two lines importing organization and currentPlan from '$lib/stores/organization' with a single import that destructures both symbols (organization, currentPlan) in one statement in realtimePricing.svelte so both stores are imported together.src/routes/(console)/organization-[organization]/+layout.ts (1)
68-75: Consider extracting the cutoff date as a named constant.The hardcoded date string
'2026-04-22'is a magic value. Extracting it as a constant improves readability and makes future updates easier.✨ Suggested improvement
+const REALTIME_PRICING_CUTOFF = new Date('2026-04-22'); + // Inside load function: - if (isCloud && new Date() < new Date('2026-04-22')) { + if (isCloud && new Date() < REALTIME_PRICING_CUTOFF) { headerAlert.add({ show: true, component: RealtimePricing, id: 'realtimePricing', importance: 1 }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/`(console)/organization-[organization]/+layout.ts around lines 68 - 75, Extract the hardcoded cutoff date '2026-04-22' into a named constant (e.g., REALTIME_PRICING_CUTOFF or REALTIME_PRICING_CUTOFF_DATE) and use that constant in the conditional instead of the literal string; update the check within the +layout.ts block that references isCloud and new Date(...) so it compares new Date() to new Date(REALTIME_PRICING_CUTOFF) and keep the headerAlert.add call (component: RealtimePricing, id: 'realtimePricing') unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/components/billing/alerts/realtimePricing.svelte`:
- Line 10: The top-level access to localStorage is causing SSR crashes; change
the dismissed initialization in realtimePricing.svelte (the dismissed variable
that uses DISMISS_KEY) so it does not read localStorage on the server — e.g.,
initialize dismissed to a safe default (false) and then in Svelte's onMount
lifecycle (or guard with typeof window !== 'undefined' / the Svelte `browser`
flag) read localStorage.getItem(DISMISS_KEY) and update dismissed accordingly;
ensure any writes to localStorage (when dismissing) also only run in the
browser.
---
Nitpick comments:
In `@src/lib/components/billing/alerts/realtimePricing.svelte`:
- Around line 4-5: Two separate imports from the same module should be combined
into a single import to clean up the import statements; replace the two lines
importing organization and currentPlan from '$lib/stores/organization' with a
single import that destructures both symbols (organization, currentPlan) in one
statement in realtimePricing.svelte so both stores are imported together.
In `@src/routes/`(console)/organization-[organization]/+layout.ts:
- Around line 68-75: Extract the hardcoded cutoff date '2026-04-22' into a named
constant (e.g., REALTIME_PRICING_CUTOFF or REALTIME_PRICING_CUTOFF_DATE) and use
that constant in the conditional instead of the literal string; update the check
within the +layout.ts block that references isCloud and new Date(...) so it
compares new Date() to new Date(REALTIME_PRICING_CUTOFF) and keep the
headerAlert.add call (component: RealtimePricing, id: 'realtimePricing')
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e90e0d1a-149a-4b8b-bfdd-8d96575f1daa
📒 Files selected for processing (2)
src/lib/components/billing/alerts/realtimePricing.sveltesrc/routes/(console)/organization-[organization]/+layout.ts
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte (1)
523-523: Keep the realtime bandwidth card spacing consistent with the other usage cards.This
CardGridis the only card in this block that dropsgap="none", so it will render with different internal spacing than its siblings.♻️ Proposed fix
- <CardGrid> + <CardGrid gap="none">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/+page.svelte at line 523, The realtime bandwidth CardGrid is using gap="none" which makes its internal spacing differ from sibling usage cards; update the CardGrid instance inside the realtime bandwidth block (the CardGrid element) to use the same gap prop as the other usage cards (either remove gap="none" or set it to the same gap value the other CardGrid components use) so spacing is consistent across cards.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/+page.svelte:
- Line 523: The realtime bandwidth CardGrid is using gap="none" which makes its
internal spacing differ from sibling usage cards; update the CardGrid instance
inside the realtime bandwidth block (the CardGrid element) to use the same gap
prop as the other usage cards (either remove gap="none" or set it to the same
gap value the other CardGrid components use) so spacing is consistent across
cards.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6682a3c6-93f3-4187-bec9-f81a2bed6085
📒 Files selected for processing (4)
package.jsonsrc/lib/components/billing/alerts/realtimePricing.sveltesrc/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.sveltesrc/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts
✅ Files skipped from review due to trivial changes (1)
- src/lib/components/billing/alerts/realtimePricing.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
- src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.ts
- package.json
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte (1)
520-520: Minor inconsistency: missinggap="none"prop.All other
CardGridcomponents in this file usegap="none", but this one doesn't. Consider adding it for consistency.Suggested fix
- <CardGrid> + <CardGrid gap="none">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/+page.svelte at line 520, The CardGrid instance at the shown location is missing the gap="none" prop used elsewhere; update that CardGrid component (the <CardGrid> element in +page.svelte) to include gap="none" for visual consistency by changing it to include the gap="none" attribute on the CardGrid component.src/lib/components/billing/alerts/realtimePricing.svelte (1)
19-21: Consider optional chaining on$organizationfor defensive coding.While the template guards with
$organization?.$id, the reactivehrefstatement evaluates independently. If$currentPlanchanges before$organizationis loaded, accessing$organization.$idcould throw.Suggested fix
$: href = $currentPlan?.usagePerProject - ? `${base}/organization-${$organization.$id}/billing` - : `${base}/organization-${$organization.$id}/usage`; + ? `${base}/organization-${$organization?.$id}/billing` + : `${base}/organization-${$organization?.$id}/usage`;Based on learnings: "billingPlanDetails is cloud-only in the Appwrite Console. When accessing billing-related properties in any Svelte file, guard with isCloud or use defensive access (e.g., optional chaining or explicit null checks)."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/components/billing/alerts/realtimePricing.svelte` around lines 19 - 21, The reactive href assignment using $currentPlan and $organization may access $organization.$id before $organization is defined; update the reactive statement that assigns href (the "$: href = ..." line referencing $currentPlan, $organization and base) to defensively access $organization (e.g., use optional chaining or an explicit null/undefined check on $organization before using $organization.$id) so the expression safely falls back to the usage path or a safe default when $organization is not yet loaded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/lib/components/billing/alerts/realtimePricing.svelte`:
- Around line 19-21: The reactive href assignment using $currentPlan and
$organization may access $organization.$id before $organization is defined;
update the reactive statement that assigns href (the "$: href = ..." line
referencing $currentPlan, $organization and base) to defensively access
$organization (e.g., use optional chaining or an explicit null/undefined check
on $organization before using $organization.$id) so the expression safely falls
back to the usage path or a safe default when $organization is not yet loaded.
In
`@src/routes/`(console)/organization-[organization]/usage/[[invoice]]/+page.svelte:
- Line 520: The CardGrid instance at the shown location is missing the
gap="none" prop used elsewhere; update that CardGrid component (the <CardGrid>
element in +page.svelte) to include gap="none" for visual consistency by
changing it to include the gap="none" attribute on the CardGrid component.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8b6bfead-65cf-42ac-bd9f-d6aeab12a7ea
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (3)
package.jsonsrc/lib/components/billing/alerts/realtimePricing.sveltesrc/routes/(console)/organization-[organization]/usage/[[invoice]]/+page.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
- package.json
…/console into chore/usage-update # Conflicts: # bun.lock # package.json

What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)
Summary by CodeRabbit
New Features
Chores