@burdenoff/microfe-billing 2026.601.1 → 2026.601.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-billing",
3
- "version": "2026.601.1",
3
+ "version": "2026.601.2",
4
4
  "description": "Billing microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [
@@ -1 +0,0 @@
1
- {"version":3,"file":"useQuota.js","names":[],"sources":["../../../../../src/billing/modules/quota/hooks/useQuota.ts"],"sourcesContent":["/**\n * Quota Module - useQuota Hook\n * Provides access to quota assignments and analytics\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback } from 'react';\nimport {\n useGetAllQuotasAssignmentQuery,\n useGetQuotaAssignmentsWithAnalyticsQuery,\n useGetQuotaAnalyticsQuery,\n useGetBillingAccountQuotasSummaryQuery,\n useCheckQuotaExhaustedQuery,\n useAddUsageMutation,\n useFreeUpUsageMutation,\n GetAllQuotasAssignmentDocument,\n GetBillingAccountQuotasSummaryDocument,\n} from '../../../../generated/global-operations';\nimport type {\n QuotaAssignment,\n QuotaAssignmentWithAnalytics,\n QuotaUsageAnalytics,\n QuotaStatus,\n AnalyticsPeriod,\n BillingAccountQuotasSummary,\n PaginatedQuotaAssignments,\n PaginatedQuotaAssignmentsWithAnalytics,\n} from '../../../shared/types';\n\ninterface UseQuotaAssignmentsOptions {\n billingAccountId: string;\n page?: number;\n pageSize?: number;\n searchQuery?: string;\n status?: QuotaStatus[];\n fromDate?: string;\n toDate?: string;\n sortBy?: string;\n sortOrder?: 'ASC' | 'DESC';\n}\n\ninterface UseQuotaAssignmentsResult {\n quotaAssignments: QuotaAssignment[];\n pagination: {\n page: number;\n pageSize: number;\n totalCount: number;\n totalPages: number;\n };\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseQuotaAssignmentsWithAnalyticsResult {\n quotaAssignments: QuotaAssignmentWithAnalytics[];\n pagination: {\n page: number;\n pageSize: number;\n totalCount: number;\n totalPages: number;\n };\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\n/**\n * Hook for fetching quota assignments\n */\nexport function useQuotaAssignments(\n options: UseQuotaAssignmentsOptions\n): UseQuotaAssignmentsResult {\n const {\n billingAccountId,\n page = 1,\n pageSize = 10,\n searchQuery,\n status,\n fromDate,\n toDate,\n sortBy,\n sortOrder,\n } = options;\n\n const { data, loading, error, refetch } = useGetAllQuotasAssignmentQuery({\n variables: {\n billingAccountId,\n page,\n pageSize,\n searchQuery,\n status,\n fromDate,\n toDate,\n sortBy,\n sortOrder,\n },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const result: PaginatedQuotaAssignments = data?.getAllQuotasAssignment || {\n quotaAssignments: [],\n page: 1,\n pageSize: 10,\n totalCount: 0,\n totalPages: 0,\n };\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n quotaAssignments: result.quotaAssignments,\n pagination: {\n page: result.page,\n pageSize: result.pageSize,\n totalCount: result.totalCount,\n totalPages: result.totalPages,\n },\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching quota assignments with analytics\n */\nexport function useQuotaAssignmentsWithAnalytics(\n options: UseQuotaAssignmentsOptions\n): UseQuotaAssignmentsWithAnalyticsResult {\n const {\n billingAccountId,\n page = 1,\n pageSize = 10,\n searchQuery,\n status,\n fromDate,\n toDate,\n sortBy,\n sortOrder,\n } = options;\n\n const { data, loading, error, refetch } = useGetQuotaAssignmentsWithAnalyticsQuery({\n variables: {\n billingAccountId,\n page,\n pageSize,\n searchQuery,\n status,\n fromDate,\n toDate,\n sortBy,\n sortOrder,\n },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const result: PaginatedQuotaAssignmentsWithAnalytics = data?.getQuotaAssignmentsWithAnalytics || {\n quotaAssignments: [],\n page: 1,\n pageSize: 10,\n totalCount: 0,\n totalPages: 0,\n };\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n quotaAssignments: result.quotaAssignments,\n pagination: {\n page: result.page,\n pageSize: result.pageSize,\n totalCount: result.totalCount,\n totalPages: result.totalPages,\n },\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching quota analytics\n */\nexport function useQuotaAnalytics(\n quotaAssignmentId: string | undefined,\n period: AnalyticsPeriod = 'LAST_30_DAYS' as AnalyticsPeriod\n) {\n const { data, loading, error, refetch } = useGetQuotaAnalyticsQuery({\n variables: { quotaAssignmentId: quotaAssignmentId!, period },\n skip: !quotaAssignmentId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n analytics: data?.getQuotaAnalytics as QuotaUsageAnalytics | null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching billing account quotas summary\n */\nexport function useBillingAccountQuotasSummary(\n billingAccountId: string | undefined,\n productId?: string\n) {\n const { data, loading, error, refetch } = useGetBillingAccountQuotasSummaryQuery({\n variables: { billingAccountId: billingAccountId!, productId },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n summary: data?.getBillingAccountQuotasSummary as BillingAccountQuotasSummary | null,\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for checking quota exhaustion\n */\nexport function useQuotaCheck(billingAccountId: string, quotaName: string, productId?: string) {\n const { data, loading, error } = useCheckQuotaExhaustedQuery({\n variables: { billingAccountId, quotaName, productId },\n skip: !billingAccountId || !quotaName,\n fetchPolicy: 'network-only',\n });\n\n return {\n limitLeft: data?.checkQuotaExhausted?.limitLeft ?? 0,\n noLimit: data?.checkQuotaExhausted?.noLimit ?? false,\n isExhausted: data?.checkQuotaExhausted?.limitLeft === 0 && !data?.checkQuotaExhausted?.noLimit,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for quota mutations\n */\nexport function useQuotaMutations() {\n const client = useApolloClient();\n\n const [addUsageMutation, { loading: isAdding }] = useAddUsageMutation();\n const [freeUpUsageMutation, { loading: isFreeing }] = useFreeUpUsageMutation();\n\n const addUsage = useCallback(\n async (params: {\n billingAccountId: string;\n quotaName: string;\n value: number;\n description: string;\n userId?: string;\n productId?: string;\n hash?: string;\n tags?: Record<string, unknown>;\n }): Promise<boolean> => {\n try {\n const { data } = await addUsageMutation({\n variables: params,\n });\n await client.refetchQueries({\n include: [GetAllQuotasAssignmentDocument, GetBillingAccountQuotasSummaryDocument],\n });\n return data!.addUsage;\n } catch (error) {\n console.error('Failed to add usage:', error);\n throw error;\n }\n },\n [addUsageMutation, client]\n );\n\n const freeUpUsage = useCallback(\n async (params: {\n billingAccountId: string;\n quotaName: string;\n value: number;\n description?: string;\n userId?: string;\n productId?: string;\n hash?: string;\n tags?: Record<string, unknown>;\n }): Promise<boolean> => {\n try {\n const { data } = await freeUpUsageMutation({\n variables: params,\n });\n await client.refetchQueries({\n include: [GetAllQuotasAssignmentDocument, GetBillingAccountQuotasSummaryDocument],\n });\n return data!.freeUpUsage;\n } catch (error) {\n console.error('Failed to free up usage:', error);\n throw error;\n }\n },\n [freeUpUsageMutation, client]\n );\n\n return {\n addUsage,\n freeUpUsage,\n isAdding,\n isFreeing,\n };\n}\n"],"mappings":";;;;AAkIA,SAAgB,EACd,GACwC;CACxC,IAAM,EACJ,qBACA,UAAO,GACP,cAAW,IACX,gBACA,WACA,aACA,WACA,WACA,iBACE,GAEE,EAAE,SAAM,YAAS,UAAO,eAAY,EAAyC;EACjF,WAAW;GACT;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;EACD,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,IAAiD,GAAM,oCAAoC;EAC/F,kBAAkB,EAAE;EACpB,MAAM;EACN,UAAU;EACV,YAAY;EACZ,YAAY;EACb,EAEK,IAAgB,EAAY,YAAY;AAC5C,QAAM,GAAS;IACd,CAAC,EAAQ,CAAC;AAEb,QAAO;EACL,kBAAkB,EAAO;EACzB,YAAY;GACV,MAAM,EAAO;GACb,UAAU,EAAO;GACjB,YAAY,EAAO;GACnB,YAAY,EAAO;GACpB;EACD,WAAW;EACJ;EACP,SAAS;EACV;;AA+BH,SAAgB,EACd,GACA,GACA;CACA,IAAM,EAAE,SAAM,YAAS,UAAO,eAAY,EAAuC;EAC/E,WAAW;GAAoB;GAAmB;GAAW;EAC7D,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,IAAgB,EAAY,YAAY;AAC5C,QAAM,GAAS;IACd,CAAC,EAAQ,CAAC;AAEb,QAAO;EACL,SAAS,GAAM;EACf,WAAW;EACJ;EACP,SAAS;EACV"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"QuotaPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Page\n * Displays quota assignments and usage summary\n */\n\nimport { useState, type FC } from 'react';\nimport {\n useBillingAccountQuotasSummary,\n useQuotaAssignmentsWithAnalytics,\n} from '../hooks/useQuota';\nimport { useDefaultBillingAccount } from '../../subscriptions/hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n formatPercentage,\n formatNumber,\n formatDate,\n formatQuotaStatus,\n} from '../../../shared/utils/format';\nimport {\n getQuotaStatusColor,\n getStatusBadgeClasses,\n getQuotaProgressColor,\n getQuotaProgressBgColor,\n} from '../../../shared/utils/status';\nimport type { QuotaAssignmentWithAnalytics, QuotaStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype FilterStatus = 'all' | QuotaStatus;\n\nexport const QuotaPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const permissions = useBillingPermissions();\n const { billingAccount, isLoading: isLoadingAccount } = useDefaultBillingAccount();\n\n const [filterStatus, setFilterStatus] = useState<FilterStatus>('all');\n const [searchQuery, setSearchQuery] = useState('');\n const [page, setPage] = useState(1);\n\n const { summary, isLoading: isLoadingSummary } = useBillingAccountQuotasSummary(\n billingAccount?.id\n );\n\n const {\n quotaAssignments,\n pagination,\n isLoading: isLoadingQuotas,\n error,\n refetch,\n } = useQuotaAssignmentsWithAnalytics({\n billingAccountId: billingAccount?.id || '',\n page,\n pageSize: 10,\n searchQuery: searchQuery || undefined,\n status: filterStatus === 'all' ? undefined : [filterStatus],\n });\n\n // Permission check\n if (!permissions.canViewUsage) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <svg\n className=\"size-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">Access Denied</h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n You don&apos;t have permission to view quotas.\n </p>\n </div>\n </div>\n );\n }\n\n const isLoading = isLoadingAccount || isLoadingSummary || isLoadingQuotas;\n\n // Loading state\n if (isLoading && !billingAccount) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 sm:grid-cols-4 gap-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"border border-border rounded-lg p-4\">\n <div className=\"h-4 w-20 bg-muted animate-pulse rounded mb-2\" />\n <div className=\"h-8 w-16 bg-muted animate-pulse rounded\" />\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center\">\n <svg\n className=\"size-6 text-destructive\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.quota.failedToLoad', 'Failed to load quotas')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n const allQuotas = [...(summary?.pooledQuotas || []), ...(summary?.subscriptionQuotas || [])];\n const healthyCount = allQuotas.filter((q) => q.usagePercentage < 75).length;\n const warningCount = allQuotas.filter(\n (q) => q.usagePercentage >= 75 && q.usagePercentage < 90\n ).length;\n const criticalCount = allQuotas.filter(\n (q) => q.usagePercentage >= 90 && q.usagePercentage < 100\n ).length;\n const exhaustedCount = allQuotas.filter((q) => q.usagePercentage >= 100).length;\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.quota.title', 'Quotas')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Monitor your quota allocations and usage\n </p>\n </div>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4\">\n <div className=\"border border-border rounded-lg bg-status-success-bg-subtle p-4\">\n <p className=\"text-sm text-status-success-text\">\n {tr('billing.quota.healthy', 'Healthy')}\n </p>\n <p className=\"text-2xl font-bold text-status-success-text mt-1\">{healthyCount}</p>\n </div>\n <div className=\"border border-border rounded-lg bg-status-warning-bg-subtle p-4\">\n <p className=\"text-sm text-status-warning-text\">Warning</p>\n <p className=\"text-2xl font-bold text-status-warning-text mt-1\">{warningCount}</p>\n </div>\n <div className=\"border border-border rounded-lg bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">Critical</p>\n <p className=\"text-2xl font-bold text-status-error-text mt-1\">{criticalCount}</p>\n </div>\n <div className=\"border border-border rounded-lg bg-bg-canvas p-4\">\n <p className=\"text-sm text-text-secondary\">Exhausted</p>\n <p className=\"text-2xl font-bold text-text-secondary mt-1\">{exhaustedCount}</p>\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border\">\n {/* Status Filter */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50 flex-wrap\">\n {(['all', 'HEALTHY', 'WARNING', 'CRITICAL', 'EXHAUSTED'] as FilterStatus[]).map(\n (status) => (\n <button\n type=\"button\"\n key={status}\n onClick={() => setFilterStatus(status)}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n filterStatus === status\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {status === 'all' ? 'All' : formatQuotaStatus(status as QuotaStatus)}\n </button>\n )\n )}\n </div>\n\n {/* Search */}\n <div className=\"relative flex-1 min-w-0 w-full sm:max-w-md\">\n <svg\n className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n <input\n type=\"text\"\n placeholder=\"Search quotas...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 text-sm border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n </div>\n\n {/* Quotas List */}\n {quotaAssignments.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <svg\n className=\"size-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">No quotas found</h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {filterStatus !== 'all' || searchQuery\n ? 'No quotas match your filters.'\n : 'No quota assignments available.'}\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {quotaAssignments.map((quota) => (\n <QuotaCard key={quota.id} quota={quota} />\n ))}\n\n {/* Pagination */}\n {pagination.totalPages > 1 && (\n <div className=\"flex items-center justify-center gap-2 pt-4\">\n <button\n type=\"button\"\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page === 1}\n className=\"px-3 py-1.5 text-sm font-medium border border-border rounded-md disabled:opacity-50\"\n >\n Previous\n </button>\n <span className=\"text-sm text-muted-foreground\">\n Page {page} of {pagination.totalPages}\n </span>\n <button\n type=\"button\"\n onClick={() => setPage((p) => Math.min(pagination.totalPages, p + 1))}\n disabled={page === pagination.totalPages}\n className=\"px-3 py-1.5 text-sm font-medium border border-border rounded-md disabled:opacity-50\"\n >\n Next\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Quota Card Component\n// ============================================================================\n\ninterface QuotaCardProps {\n quota: QuotaAssignmentWithAnalytics;\n}\n\nconst QuotaCard: FC<QuotaCardProps> = ({ quota }) => {\n const statusColor = getQuotaStatusColor(quota.status);\n const progressColor = getQuotaProgressColor(quota.usagePercentage);\n const progressBgColor = getQuotaProgressBgColor(quota.usagePercentage);\n const limits = quota.limits as { value?: number } | null;\n const limit = limits?.value || 0;\n\n return (\n <div className=\"border border-border rounded-lg bg-card p-6 hover:shadow-sm transition-shadow\">\n <div className=\"flex flex-col sm:flex-row sm:items-start gap-4\">\n {/* Info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"font-semibold text-foreground\">{quota.name}</h3>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatQuotaStatus(quota.status)}\n </span>\n <span className=\"px-2 py-0.5 text-xs font-medium bg-muted text-muted-foreground rounded\">\n {quota.quotaType}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Expires: {formatDate(quota.endtime, 'short')}\n </p>\n </div>\n\n {/* Usage Stats */}\n <div className=\"text-right\">\n <p className=\"text-2xl font-bold text-foreground\">\n {formatNumber(quota.currentUsageSum)} / {limit > 0 ? formatNumber(limit) : '∞'}\n </p>\n <p className=\"text-sm text-muted-foreground\">\n {formatPercentage(quota.usagePercentage)} used\n </p>\n </div>\n </div>\n\n {/* Progress Bar */}\n <div className=\"mt-4\">\n <div className={`h-2 rounded-full overflow-hidden ${progressBgColor}`}>\n <div\n className={`h-full transition-all duration-500 ${progressColor}`}\n style={{ width: `${Math.min(100, quota.usagePercentage)}%` }}\n />\n </div>\n <div className=\"flex items-center justify-between mt-2 text-xs text-muted-foreground\">\n <span>{formatNumber(quota.limitLeft)} remaining</span>\n <span>{quota.totalUsageCount} usage records</span>\n </div>\n </div>\n\n {/* Analytics Preview */}\n {quota.analytics?.trends && (\n <div className=\"mt-4 pt-4 border-t border-border flex flex-wrap gap-4 text-sm\">\n {quota.analytics.trends.averageDailyUsage !== undefined && (\n <div>\n <span className=\"text-muted-foreground\">Avg. Daily: </span>\n <span className=\"font-medium text-foreground\">\n {formatNumber(quota.analytics.trends.averageDailyUsage, 1)}\n </span>\n </div>\n )}\n {quota.analytics.trends.growthRate !== undefined && (\n <div>\n <span className=\"text-muted-foreground\">Growth: </span>\n <span\n className={`font-medium ${quota.analytics.trends.growthRate > 0 ? 'text-status-error-text' : 'text-status-success-text'}`}\n >\n {quota.analytics.trends.growthRate > 0 ? '+' : ''}\n {formatPercentage(quota.analytics.trends.growthRate)}\n </span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}