diff --git a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte index 2da4bf9f3..c363a89e4 100644 --- a/src/routes/(console)/organization-[organization]/billing/planSummary.svelte +++ b/src/routes/(console)/organization-[organization]/billing/planSummary.svelte @@ -54,45 +54,15 @@ return name.length > 12 ? `${name.slice(0, 12)}…` : name; } - function resourceEntry(p: any, id: string): any { - const res = p?.resources; - if (!res) return undefined; - if (Array.isArray(res)) { - return res.find((r: any) => r.resourceId === id); - } - return res[id]; - } - function valueOf(p: any, id: string): number { - const entry = resourceEntry(p, id); - return (entry?.value ?? 0) as number; - } - function amountOf(p: any, id: string): number { - const entry = resourceEntry(p, id); - return (entry?.amount ?? 0) as number; - } + // helper function to get resource amount from backend data + function getResourceAmount(projectBreakdown: any, resourceId: string): number { + if (!projectBreakdown?.resources) return 0; - function calculateResourcePrice(usage: number, planLimit: number, planPricing: any): number { - if (!planPricing || usage <= planLimit) { - return 0; - } + const resource = Array.isArray(projectBreakdown.resources) + ? projectBreakdown.resources.find((r: any) => r.resourceId === resourceId) + : projectBreakdown.resources[resourceId]; - const overage = usage - planLimit; - const unitsPerPrice = planPricing.value; - const pricePerUnit = planPricing.price; - - const overageUnits = Math.ceil(overage / unitsPerPrice); - - return overageUnits * pricePerUnit; - } - function storageTotal(p: any): number { - const ids = [ - 'filesStorage', - 'deploymentsStorage', - 'buildsStorage', - 'backupsStorage', - 'databasesStorage' - ]; - return ids.reduce((sum, rid) => sum + (valueOf(p, rid) || 0), 0); + return resource?.amount || 0; } function getProgressColor(_percentage: number): string { @@ -108,7 +78,6 @@ maxValue === undefined || (typeof maxValue === 'number' && maxValue <= 0) ) { - //just show usage without progress bar return []; } @@ -136,7 +105,7 @@ ): Array<{ size: number; color: string; tooltip?: { title: string; label: string } }> { if (maxGB <= 0) return []; - const maxBytes = maxGB * 1024 * 1024 * 1024; // Convert GB to bytes + const maxBytes = maxGB * 1024 * 1024 * 1024; const percentage = Math.min((currentBytes / maxBytes) * 100, 100); const progressColor = getProgressColor(percentage); @@ -156,19 +125,26 @@ $: projectsList = organizationUsage?.projects?.length ? organizationUsage.projects - : (currentAggregation?.projectBreakdown || []).map((p) => ({ - projectId: p.$id, - storage: storageTotal(p), - executions: valueOf(p, 'executions'), - executionsMBSeconds: valueOf(p, 'executionsMBSeconds'), - bandwidth: valueOf(p, 'bandwidth'), - databasesReads: valueOf(p, 'databasesReads'), - databasesWrites: valueOf(p, 'databasesWrites'), - users: valueOf(p, 'users'), - authPhoneTotal: valueOf(p, 'authPhone'), - authPhoneEstimate: amountOf(p, 'authPhone'), - breakdown: p - })); + : (currentAggregation?.projectBreakdown || []).map((p) => { + // Find the corresponding project data from organizationUsage + const projectData = organizationUsage?.projects?.find( + (proj: any) => proj.projectId === p.$id + ); + + return { + projectId: p.$id, + storage: projectData?.storage || 0, + executions: projectData?.executions || 0, + executionsMBSeconds: projectData?.executionsMBSeconds || 0, + bandwidth: projectData?.bandwidth || 0, + databasesReads: projectData?.databasesReads || 0, + databasesWrites: projectData?.databasesWrites || 0, + users: projectData?.users || 0, + authPhoneTotal: projectData?.authPhoneTotal || 0, + authPhoneEstimate: projectData?.authPhoneEstimate || 0, + breakdown: p + }; + }); $: billingData = [ { @@ -180,182 +156,151 @@ price: formatCurrency(currentPlan?.price || 0) } }, - ...(projectsList?.map((project) => ({ - id: `project-${project.projectId}`, - expandable: true, - cells: { - item: $isSmallViewport - ? truncateForSmall( - usageProjects[project.projectId]?.name || `Project ${project.projectId}` - ) - : usageProjects[project.projectId]?.name || `Project ${project.projectId}`, - usage: '', - price: formatCurrency(projectIdToBreakdown.get(project.projectId)?.amount || 0) - }, - children: [ - // Bandwidth - { - id: `project-${project.projectId}-bandwidth`, - cells: { - item: 'Bandwidth', - usage: `${formatBandwidthUsage(project.bandwidth || 0, currentPlan?.bandwidth)}`, - price: formatCurrency( - calculateResourcePrice( - (project.bandwidth || 0) / (1024 * 1024 * 1024), - currentPlan?.bandwidth || 0, - currentPlan?.usage?.bandwidth - ) - ) - }, - progressData: createStorageProgressData( - project.bandwidth || 0, - currentPlan?.bandwidth || 0 - ), - maxValue: currentPlan?.bandwidth - ? currentPlan.bandwidth * 1024 * 1024 * 1024 - : 0 - }, - // Users - { - id: `project-${project.projectId}-users`, - cells: { - item: 'Users', - usage: `${formatNum(project.users || 0)} / ${currentPlan?.users ? formatNum(currentPlan.users) : 'Unlimited'}`, - price: formatCurrency( - calculateResourcePrice( - project.users || 0, - currentPlan?.users || 0, - currentPlan?.usage?.users - ) - ) - }, - progressData: createProgressData(project.users || 0, currentPlan?.users), - maxValue: currentPlan?.users - }, - // Database reads - { - id: `project-${project.projectId}-reads`, - cells: { - item: 'Database reads', - usage: `${formatNum(project.databasesReads || 0)} / ${currentPlan?.databasesReads ? formatNum(currentPlan.databasesReads) : 'Unlimited'}`, - price: formatCurrency( - calculateResourcePrice( - project.databasesReads || 0, - currentPlan?.databasesReads || 0, - currentPlan?.usage?.databasesReads - ) - ) - }, - progressData: createProgressData( - project.databasesReads || 0, - currentPlan?.databasesReads - ), - maxValue: currentPlan?.databasesReads - }, - // Database writes - { - id: `project-${project.projectId}-writes`, - cells: { - item: 'Database writes', - usage: `${formatNum(project.databasesWrites || 0)} / ${currentPlan?.databasesWrites ? formatNum(currentPlan.databasesWrites) : 'Unlimited'}`, - price: formatCurrency( - calculateResourcePrice( - project.databasesWrites || 0, - currentPlan?.databasesWrites || 0, - currentPlan?.usage?.databasesWrites - ) - ) - }, - progressData: createProgressData( - project.databasesWrites || 0, - currentPlan?.databasesWrites - ), - maxValue: currentPlan?.databasesWrites - }, - // Executions - { - id: `project-${project.projectId}-executions`, - cells: { - item: 'Executions', - usage: `${formatNum(project.executions || 0)} / ${currentPlan?.executions ? formatNum(currentPlan.executions) : 'Unlimited'}`, - price: formatCurrency( - calculateResourcePrice( - project.executions || 0, - currentPlan?.executions || 0, - currentPlan?.usage?.executions - ) - ) - }, - progressData: createProgressData( - project.executions || 0, - currentPlan?.executions - ), - maxValue: currentPlan?.executions - }, - // Storage - { - id: `project-${project.projectId}-storage`, - cells: { - item: 'Storage', - usage: `${formatHumanSize(project.storage || 0)} / ${currentPlan?.storage?.toString() || '0'} GB`, - price: formatCurrency( - calculateResourcePrice( - (project.storage || 0) / (1024 * 1024 * 1024), - currentPlan?.storage || 0, - currentPlan?.usage?.storage - ) - ) - }, - progressData: createStorageProgressData( - project.storage || 0, - currentPlan?.storage || 0 - ), - maxValue: currentPlan?.storage ? currentPlan.storage * 1024 * 1024 * 1024 : 0 // Convert GB to bytes - }, - // GB-hours (executions time) - { - id: `project-${project.projectId}-gb-hours`, - cells: { - item: 'GB-hours', - usage: `${formatNum((project.executionsMBSeconds || 0) / 1000 / 3600 || 0)} / ${currentPlan?.executions ? formatNum((currentPlan.executions * 1000 * 3600) / 1000 / 3600) : 'Unlimited'}`, - price: formatCurrency( - calculateResourcePrice( - (project.executionsMBSeconds || 0) / 1000 / 3600, - (currentPlan?.executions * 1000 * 3600) / 1000 / 3600 || 0, - currentPlan?.usage?.GBHours - ) - ) - }, - progressData: currentPlan?.executions - ? createProgressData( - (project.executionsMBSeconds || 0) / 1000 / 3600, - (currentPlan.executions * 1000 * 3600) / 1000 / 3600 + ...(projectsList?.map((project) => { + const projectBreakdown = projectIdToBreakdown.get(project.projectId); + + return { + id: `project-${project.projectId}`, + expandable: true, + cells: { + item: $isSmallViewport + ? truncateForSmall( + usageProjects[project.projectId]?.name || + `Project ${project.projectId}` ) - : [], - maxValue: currentPlan?.executions - ? (currentPlan.executions * 1000 * 3600) / 1000 / 3600 - : null + : usageProjects[project.projectId]?.name || `Project ${project.projectId}`, + usage: '', + price: formatCurrency(projectBreakdown?.amount || 0) }, - // Phone OTP (no progress bar) - { - id: `project-${project.projectId}-sms`, - cells: { - item: 'Phone OTP', - usage: `${formatNum(project.authPhoneTotal || 0)} SMS messages`, - price: formatCurrency(project.authPhoneEstimate || 0) + children: [ + // Bandwidth + { + id: `project-${project.projectId}-bandwidth`, + cells: { + item: 'Bandwidth', + usage: `${formatBandwidthUsage(project.bandwidth || 0, currentPlan?.bandwidth)}`, + price: formatCurrency(getResourceAmount(projectBreakdown, 'bandwidth')) + }, + progressData: createStorageProgressData( + project.bandwidth || 0, + currentPlan?.bandwidth || 0 + ), + maxValue: currentPlan?.bandwidth + ? currentPlan.bandwidth * 1024 * 1024 * 1024 + : 0 + }, + // Users + { + id: `project-${project.projectId}-users`, + cells: { + item: 'Users', + usage: `${formatNum(project.users || 0)} / ${currentPlan?.users ? formatNum(currentPlan.users) : 'Unlimited'}`, + price: formatCurrency(getResourceAmount(projectBreakdown, 'users')) + }, + progressData: createProgressData(project.users || 0, currentPlan?.users), + maxValue: currentPlan?.users + }, + // Database reads + { + id: `project-${project.projectId}-reads`, + cells: { + item: 'Database reads', + usage: `${formatNum(project.databasesReads || 0)} / ${currentPlan?.databasesReads ? formatNum(currentPlan.databasesReads) : 'Unlimited'}`, + price: formatCurrency( + getResourceAmount(projectBreakdown, 'databasesReads') + ) + }, + progressData: createProgressData( + project.databasesReads || 0, + currentPlan?.databasesReads + ), + maxValue: currentPlan?.databasesReads + }, + // Database writes + { + id: `project-${project.projectId}-writes`, + cells: { + item: 'Database writes', + usage: `${formatNum(project.databasesWrites || 0)} / ${currentPlan?.databasesWrites ? formatNum(currentPlan.databasesWrites) : 'Unlimited'}`, + price: formatCurrency( + getResourceAmount(projectBreakdown, 'databasesWrites') + ) + }, + progressData: createProgressData( + project.databasesWrites || 0, + currentPlan?.databasesWrites + ), + maxValue: currentPlan?.databasesWrites + }, + // Executions + { + id: `project-${project.projectId}-executions`, + cells: { + item: 'Executions', + usage: `${formatNum(project.executions || 0)} / ${currentPlan?.executions ? formatNum(currentPlan.executions) : 'Unlimited'}`, + price: formatCurrency(getResourceAmount(projectBreakdown, 'executions')) + }, + progressData: createProgressData( + project.executions || 0, + currentPlan?.executions + ), + maxValue: currentPlan?.executions + }, + // Storage + { + id: `project-${project.projectId}-storage`, + cells: { + item: 'Storage', + usage: `${formatHumanSize(project.storage || 0)} / ${currentPlan?.storage?.toString() || '0'} GB`, + price: formatCurrency(getResourceAmount(projectBreakdown, 'storage')) + }, + progressData: createStorageProgressData( + project.storage || 0, + currentPlan?.storage || 0 + ), + maxValue: currentPlan?.storage + ? currentPlan.storage * 1024 * 1024 * 1024 + : 0 + }, + // GB-hours (executions time) + { + id: `project-${project.projectId}-gb-hours`, + cells: { + item: 'GB-hours', + usage: `${formatNum((project.executionsMBSeconds || 0) / 1000 / 3600 || 0)} / ${currentPlan?.executions ? formatNum((currentPlan.executions * 1000 * 3600) / 1000 / 3600) : 'Unlimited'}`, + price: formatCurrency(getResourceAmount(projectBreakdown, 'GBHours')) + }, + progressData: currentPlan?.executions + ? createProgressData( + (project.executionsMBSeconds || 0) / 1000 / 3600, + (currentPlan.executions * 1000 * 3600) / 1000 / 3600 + ) + : [], + maxValue: currentPlan?.executions + ? (currentPlan.executions * 1000 * 3600) / 1000 / 3600 + : null + }, + // Phone OTP (no progress bar) + { + id: `project-${project.projectId}-sms`, + cells: { + item: 'Phone OTP', + usage: `${formatNum(project.authPhoneTotal || 0)} SMS messages`, + price: formatCurrency(project.authPhoneEstimate || 0) + } + }, + // Usage details link + { + id: `project-${project.projectId}-usage-details`, + cells: { + item: `Usage details`, + usage: '', + price: '' + } } - }, - // Usage details link - { - id: `project-${project.projectId}-usage-details`, - cells: { - item: `Usage details`, - usage: '', - price: '' - } - } - ] - })) || []), + ] + }; + }) || []), // Show info if no projects found ...(projectsList && projectsList.length === 0 ? [ @@ -372,18 +317,7 @@ : []) ]; - $: totalAmount = billingData.reduce((sum, item) => { - let itemPrice = parseFloat(item.cells.price.replace(/[^0-9.-]+/g, '')); - if (isNaN(itemPrice)) itemPrice = 0; - - const childrenPrice = - item.children?.reduce((childSum, child) => { - let childPrice = parseFloat(child.cells.price.replace(/[^0-9.-]+/g, '')); - if (isNaN(childPrice)) childPrice = 0; - return childSum + childPrice; - }, 0) || 0; - return sum + itemPrice + childrenPrice; - }, 0); + $: totalAmount = currentInvoice?.amount || currentPlan?.price || 0; {#if $organization}