@burdenoff/microfe-billing 2026.614.1 → 2026.623.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/dist/billing/BillingAdminRoutes.js +7 -6
- package/dist/billing/BillingAdminRoutes.js.map +1 -1
- package/dist/billing/BillingRoot.js +1 -1
- package/dist/billing/BillingRoot.js.map +1 -1
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js +22 -22
- package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +31 -31
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +39 -39
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +29 -29
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +91 -91
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +40 -40
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +115 -115
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +75 -72
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +126 -126
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +45 -45
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js +25 -25
- package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +31 -35
- package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js +9 -9
- package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js +31 -31
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/components/PromoCodeInput.js +8 -8
- package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
- package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +9 -9
- package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
- package/dist/billing/modules/checkout/components/StripePaymentForm.js +21 -21
- package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js +11 -11
- package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +31 -31
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +23 -23
- package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +91 -91
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +62 -62
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +72 -60
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +66 -66
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +46 -46
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +19 -19
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +37 -37
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +90 -90
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +41 -41
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +31 -31
- package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +35 -35
- package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js +24 -24
- package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +99 -99
- package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +23 -23
- package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +22 -22
- package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +58 -58
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +126 -126
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +83 -79
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +16 -16
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +40 -40
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +35 -35
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +57 -57
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +35 -35
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +57 -57
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +52 -52
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +40 -39
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +22 -19
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +34 -34
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +47 -47
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +39 -39
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +52 -52
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +23 -23
- package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -246
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +88 -85
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +51 -51
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +206 -196
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/components/ServerError.js +4 -4
- package/dist/billing/shared/components/ServerError.js.map +1 -1
- package/dist/billing/shared/components/StatCard.js +11 -33
- package/dist/billing/shared/components/StatCard.js.map +1 -1
- package/dist/billing/shared/ui/Card.js +15 -0
- package/dist/billing/shared/ui/Card.js.map +1 -0
- package/dist/billing/shared/ui/MetricCard.js +44 -0
- package/dist/billing/shared/ui/MetricCard.js.map +1 -0
- package/dist/billing/shared/ui/ProgressBar.js +30 -0
- package/dist/billing/shared/ui/ProgressBar.js.map +1 -0
- package/dist/billing/shared/ui/SectionHeader.js +2 -0
- package/dist/billing/shared/ui/StatusPill.js +28 -0
- package/dist/billing/shared/ui/StatusPill.js.map +1 -0
- package/dist/billing/shared/ui/index.js +5 -0
- package/dist/billing/shared/utils/tokens.js +13 -30
- package/dist/billing/shared/utils/tokens.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaTemplateDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplateDetailPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Template Detail Page\n * Displays detailed information about a specific quota template\n */\n\nimport { type FC } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplate, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { formatDate } from '../../../shared/utils/format';\nimport { isServerError } from '../../../shared/utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const QuotaTemplateDetailPage: 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 { quotaId } = useParams<{ quotaId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quota, isLoading, error, refetch } = useQuotaTemplate(quotaId);\n const { toggleQuota, isToggling } = useQuotaTemplateMutations();\n\n // Permission check\n if (!permissions.canViewQuotas) {\n return <AccessDenied message=\"You don't have permission to view quota details.\" />;\n }\n\n // Loading state\n if (isLoading) {\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 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n <div className=\"h-64 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load quota details. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n // Not found state\n if (!quota) {\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-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=\"M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.quota.quotaNotFound', 'Quota not found')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n The quota you're looking for doesn't exist or has been removed.\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\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 Back to Quotas\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n try {\n await toggleQuota(quota.id, !quota.isActive);\n refetch();\n } catch (err) {\n console.error('Failed to toggle quota:', err);\n }\n };\n\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const limitUnit = limits?.unit ?? '';\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={quota.name}\n description={`${quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'} quota template`}\n />\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Limits Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.quota.limitsConfig', 'Limits Configuration')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"grid grid-cols-3 gap-6\">\n <div>\n <p className=\"text-sm text-muted-foreground\">Limit Type</p>\n <p className=\"text-lg font-semibold text-foreground capitalize mt-1\">\n {limitType}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Limit Value</p>\n <p className=\"text-lg font-semibold text-foreground mt-1\">\n {limitValue.toLocaleString()}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">Unit</p>\n <p className=\"text-lg font-semibold text-foreground mt-1\">{limitUnit || '-'}</p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Properties Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.quota.properties', 'Properties')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between p-3 border border-border rounded-lg\">\n <div>\n <p className=\"font-medium text-foreground\">Quota Type</p>\n <p className=\"text-sm text-muted-foreground\">\n {quota.quotaType === 'POOLED'\n ? 'Shared across all products'\n : 'Specific to individual products'}\n </p>\n </div>\n <span\n className={`px-3 py-1 text-sm font-medium rounded ${\n quota.quotaType === 'POOLED'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-accent-purple-subtle text-accent-purple'\n }`}\n >\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n </span>\n </div>\n\n <div className=\"flex items-center justify-between p-3 border border-border rounded-lg\">\n <div>\n <p className=\"font-medium text-foreground\">Reusable</p>\n <p className=\"text-sm text-muted-foreground\">\n {quota.reusable\n ? 'Quota can be freed up and reused (e.g., seats, bots)'\n : 'Quota is consumed permanently (e.g., API calls, storage)'}\n </p>\n </div>\n <span\n className={`px-3 py-1 text-sm font-medium rounded ${\n quota.reusable\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-muted text-muted-foreground'\n }`}\n >\n {quota.reusable ? 'Yes' : 'No'}\n </span>\n </div>\n </div>\n </div>\n\n {/* Raw Limits JSON */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Limits (Raw JSON)</h2>\n </div>\n <div className=\"p-4\">\n <pre className=\"text-sm text-foreground bg-muted/50 p-4 rounded-lg overflow-auto\">\n {JSON.stringify(quota.limits, null, 2)}\n </pre>\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Status Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Status</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Status</span>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n quota.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-muted text-muted-foreground'\n }`}\n >\n {quota.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Created</span>\n <span className=\"text-sm font-medium text-foreground\">\n {formatDate(quota.createdAt, 'short')}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Updated</span>\n <span className=\"text-sm font-medium text-foreground\">\n {formatDate(quota.updatedAt, 'short')}\n </span>\n </div>\n </div>\n </div>\n\n {/* Actions Card */}\n {permissions.canManageQuotas && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Actions</h2>\n </div>\n <div className=\"p-4 space-y-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/quotas/${quota.id}/edit`)}\n className=\"w-full px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Edit Quota\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`w-full px-4 py-2 text-sm font-medium border border-border rounded-md transition-colors disabled:opacity-50 ${\n quota.isActive\n ? 'text-destructive hover:bg-destructive/10'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? 'Processing...'\n : quota.isActive\n ? 'Deactivate Quota'\n : 'Activate Quota'}\n </button>\n </div>\n </div>\n )}\n\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Back to Quotas\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"QuotaTemplateDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplateDetailPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Template Detail Page\n * Displays detailed information about a specific quota template\n */\n\nimport { type FC } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplate, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { formatDate } from '../../../shared/utils/format';\nimport { isServerError } from '../../../shared/utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const QuotaTemplateDetailPage: 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 { quotaId } = useParams<{ quotaId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quota, isLoading, error, refetch } = useQuotaTemplate(quotaId);\n const { toggleQuota, isToggling } = useQuotaTemplateMutations();\n\n // Permission check\n if (!permissions.canViewQuotas) {\n return <AccessDenied message=\"You don't have permission to view quota details.\" />;\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-24 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n <div className=\"h-64 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load quota details. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n // Not found state\n if (!quota) {\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-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-muted\"\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.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.quota.quotaNotFound', 'Quota not found')}\n </h2>\n <p className=\"text-sm text-text-muted\">\n The quota you're looking for doesn't exist or has been removed.\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Back to Quotas\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n try {\n await toggleQuota(quota.id, !quota.isActive);\n refetch();\n } catch (err) {\n console.error('Failed to toggle quota:', err);\n }\n };\n\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const limitUnit = limits?.unit ?? '';\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={quota.name}\n description={`${quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'} quota template`}\n />\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Limits Card */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.quota.limitsConfig', 'Limits Configuration')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"grid grid-cols-3 gap-6\">\n <div>\n <p className=\"text-sm text-text-muted\">Limit Type</p>\n <p className=\"text-lg font-semibold text-text-primary capitalize mt-1\">\n {limitType}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">Limit Value</p>\n <p className=\"text-lg font-semibold text-text-primary mt-1\">\n {limitValue.toLocaleString()}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">Unit</p>\n <p className=\"text-lg font-semibold text-text-primary mt-1\">{limitUnit || '-'}</p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Properties Card */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.quota.properties', 'Properties')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between p-3 border border-border-subtle rounded-lg\">\n <div>\n <p className=\"font-medium text-text-primary\">Quota Type</p>\n <p className=\"text-sm text-text-muted\">\n {quota.quotaType === 'POOLED'\n ? 'Shared across all products'\n : 'Specific to individual products'}\n </p>\n </div>\n <span\n className={`px-3 py-1 text-sm font-medium rounded ${\n quota.quotaType === 'POOLED'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-accent-purple-subtle text-text-primary'\n }`}\n >\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n </span>\n </div>\n\n <div className=\"flex items-center justify-between p-3 border border-border-subtle rounded-lg\">\n <div>\n <p className=\"font-medium text-text-primary\">Reusable</p>\n <p className=\"text-sm text-text-muted\">\n {quota.reusable\n ? 'Quota can be freed up and reused (e.g., seats, bots)'\n : 'Quota is consumed permanently (e.g., API calls, storage)'}\n </p>\n </div>\n <span\n className={`px-3 py-1 text-sm font-medium rounded ${\n quota.reusable\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {quota.reusable ? 'Yes' : 'No'}\n </span>\n </div>\n </div>\n </div>\n\n {/* Raw Limits JSON */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Limits (Raw JSON)</h2>\n </div>\n <div className=\"p-4\">\n <pre className=\"text-sm text-text-primary bg-bg-sunken/50 p-4 rounded-lg overflow-auto\">\n {JSON.stringify(quota.limits, null, 2)}\n </pre>\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Status Card */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Status</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-muted\">Status</span>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n quota.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {quota.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-muted\">Created</span>\n <span className=\"text-sm font-medium text-text-primary\">\n {formatDate(quota.createdAt, 'short')}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-muted\">Updated</span>\n <span className=\"text-sm font-medium text-text-primary\">\n {formatDate(quota.updatedAt, 'short')}\n </span>\n </div>\n </div>\n </div>\n\n {/* Actions Card */}\n {permissions.canManageQuotas && (\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Actions</h2>\n </div>\n <div className=\"p-4 space-y-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/quotas/${quota.id}/edit`)}\n className=\"w-full px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Edit Quota\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`w-full px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors disabled:opacity-50 ${\n quota.isActive\n ? 'text-status-error-text hover:bg-status-error-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? 'Processing...'\n : quota.isActive\n ? 'Deactivate Quota'\n : 'Activate Quota'}\n </button>\n </div>\n </div>\n )}\n\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Back to Quotas\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -29,14 +29,14 @@ var h = () => {
|
|
|
29
29
|
}
|
|
30
30
|
}, [b]), y.canManageQuotas ? x ? /* @__PURE__ */ p("div", {
|
|
31
31
|
className: "space-y-6 p-6",
|
|
32
|
-
children: [/* @__PURE__ */ f("div", { className: "h-8 w-48 bg-
|
|
32
|
+
children: [/* @__PURE__ */ f("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ f("div", {
|
|
33
33
|
className: "max-w-2xl space-y-4",
|
|
34
34
|
children: [
|
|
35
35
|
1,
|
|
36
36
|
2,
|
|
37
37
|
3,
|
|
38
38
|
4
|
|
39
|
-
].map((e) => /* @__PURE__ */ f("div", { className: "h-16 bg-
|
|
39
|
+
].map((e) => /* @__PURE__ */ f("div", { className: "h-16 bg-bg-sunken animate-pulse rounded" }, e))
|
|
40
40
|
})]
|
|
41
41
|
}) : S && i(S) ? /* @__PURE__ */ f("div", {
|
|
42
42
|
className: "p-6",
|
|
@@ -104,11 +104,11 @@ var h = () => {
|
|
|
104
104
|
})
|
|
105
105
|
}),
|
|
106
106
|
/* @__PURE__ */ p("div", {
|
|
107
|
-
className: "border border-border rounded-
|
|
107
|
+
className: "border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
108
108
|
children: [/* @__PURE__ */ f("div", {
|
|
109
|
-
className: "p-4 border-b border-border bg-
|
|
109
|
+
className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
|
|
110
110
|
children: /* @__PURE__ */ f("h2", {
|
|
111
|
-
className: "font-semibold text-
|
|
111
|
+
className: "font-semibold text-text-primary",
|
|
112
112
|
children: "Basic Information"
|
|
113
113
|
})
|
|
114
114
|
}), /* @__PURE__ */ p("div", {
|
|
@@ -117,7 +117,7 @@ var h = () => {
|
|
|
117
117
|
/* @__PURE__ */ p("div", { children: [
|
|
118
118
|
/* @__PURE__ */ f("label", {
|
|
119
119
|
htmlFor: "name",
|
|
120
|
-
className: "block text-sm font-medium text-
|
|
120
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
121
121
|
children: "Quota Key *"
|
|
122
122
|
}),
|
|
123
123
|
/* @__PURE__ */ f("input", {
|
|
@@ -125,12 +125,12 @@ var h = () => {
|
|
|
125
125
|
type: "text",
|
|
126
126
|
value: E,
|
|
127
127
|
onChange: (e) => D(e.target.value),
|
|
128
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
128
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] font-mono text-sm",
|
|
129
129
|
placeholder: "e.g., sessions-started, tunnels-created",
|
|
130
130
|
required: !0
|
|
131
131
|
}),
|
|
132
132
|
/* @__PURE__ */ p("p", {
|
|
133
|
-
className: "text-xs text-muted
|
|
133
|
+
className: "text-xs text-text-muted mt-1",
|
|
134
134
|
children: [
|
|
135
135
|
"Internal machine key, must match the ",
|
|
136
136
|
/* @__PURE__ */ f("code", { children: "quotaName" }),
|
|
@@ -144,7 +144,7 @@ var h = () => {
|
|
|
144
144
|
/* @__PURE__ */ p("div", { children: [
|
|
145
145
|
/* @__PURE__ */ f("label", {
|
|
146
146
|
htmlFor: "displayName",
|
|
147
|
-
className: "block text-sm font-medium text-
|
|
147
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
148
148
|
children: "Display Name *"
|
|
149
149
|
}),
|
|
150
150
|
/* @__PURE__ */ f("input", {
|
|
@@ -152,19 +152,19 @@ var h = () => {
|
|
|
152
152
|
type: "text",
|
|
153
153
|
value: O,
|
|
154
154
|
onChange: (e) => k(e.target.value),
|
|
155
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
155
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
156
156
|
placeholder: "e.g., Tunnels, Session Starts, Webhooks",
|
|
157
157
|
required: !0
|
|
158
158
|
}),
|
|
159
159
|
/* @__PURE__ */ f("p", {
|
|
160
|
-
className: "text-xs text-muted
|
|
160
|
+
className: "text-xs text-text-muted mt-1",
|
|
161
161
|
children: "Human-readable name shown to users in billing and usage UI."
|
|
162
162
|
})
|
|
163
163
|
] }),
|
|
164
164
|
/* @__PURE__ */ p("div", { children: [
|
|
165
165
|
/* @__PURE__ */ f("label", {
|
|
166
166
|
htmlFor: "description",
|
|
167
|
-
className: "block text-sm font-medium text-
|
|
167
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
168
168
|
children: "Description"
|
|
169
169
|
}),
|
|
170
170
|
/* @__PURE__ */ f("textarea", {
|
|
@@ -172,19 +172,19 @@ var h = () => {
|
|
|
172
172
|
value: A,
|
|
173
173
|
onChange: (e) => j(e.target.value),
|
|
174
174
|
rows: 2,
|
|
175
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
175
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none",
|
|
176
176
|
placeholder: "e.g., Active tunnels created through VibeControls (slot freed on delete)"
|
|
177
177
|
}),
|
|
178
178
|
/* @__PURE__ */ f("p", {
|
|
179
|
-
className: "text-xs text-muted
|
|
179
|
+
className: "text-xs text-text-muted mt-1",
|
|
180
180
|
children: "Short explanation shown in billing and usage detail views."
|
|
181
181
|
})
|
|
182
182
|
] }),
|
|
183
183
|
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
184
|
-
className: "block text-sm font-medium text-
|
|
184
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
185
185
|
children: "Quota Type"
|
|
186
186
|
}), /* @__PURE__ */ p("div", {
|
|
187
|
-
className: "px-3 py-2 border border-border rounded-md bg-
|
|
187
|
+
className: "px-3 py-2 border border-border-subtle rounded-md bg-bg-sunken/50 text-text-muted",
|
|
188
188
|
children: [b.quotaType === "POOLED" ? "Pooled" : "Product Specific", /* @__PURE__ */ f("span", {
|
|
189
189
|
className: "text-xs ml-2",
|
|
190
190
|
children: "(cannot be changed)"
|
|
@@ -197,27 +197,27 @@ var h = () => {
|
|
|
197
197
|
type: "checkbox",
|
|
198
198
|
checked: R,
|
|
199
199
|
onChange: (e) => z(e.target.checked),
|
|
200
|
-
className: "size-4 rounded border-border text-
|
|
200
|
+
className: "size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]"
|
|
201
201
|
}), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
202
202
|
htmlFor: "reusable",
|
|
203
|
-
className: "text-sm font-medium text-
|
|
203
|
+
className: "text-sm font-medium text-text-primary",
|
|
204
204
|
children: "Reusable Quota"
|
|
205
205
|
}), /* @__PURE__ */ f("p", {
|
|
206
|
-
className: "text-xs text-muted
|
|
206
|
+
className: "text-xs text-text-muted",
|
|
207
207
|
children: "Enable for quotas like seats or bots that can be freed up"
|
|
208
208
|
})] })]
|
|
209
209
|
}),
|
|
210
210
|
/* @__PURE__ */ p("div", { children: [
|
|
211
211
|
/* @__PURE__ */ f("label", {
|
|
212
212
|
htmlFor: "resetPeriod",
|
|
213
|
-
className: "block text-sm font-medium text-
|
|
213
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
214
214
|
children: "Reset Period"
|
|
215
215
|
}),
|
|
216
216
|
/* @__PURE__ */ p("select", {
|
|
217
217
|
id: "resetPeriod",
|
|
218
218
|
value: B,
|
|
219
219
|
onChange: (e) => V(e.target.value),
|
|
220
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
220
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
221
221
|
children: [
|
|
222
222
|
/* @__PURE__ */ f("option", {
|
|
223
223
|
value: r.NEVER,
|
|
@@ -234,7 +234,7 @@ var h = () => {
|
|
|
234
234
|
]
|
|
235
235
|
}),
|
|
236
236
|
/* @__PURE__ */ f("p", {
|
|
237
|
-
className: "text-xs text-muted
|
|
237
|
+
className: "text-xs text-text-muted mt-1",
|
|
238
238
|
children: "How often the quota usage counter is reset to zero"
|
|
239
239
|
})
|
|
240
240
|
] }),
|
|
@@ -245,10 +245,10 @@ var h = () => {
|
|
|
245
245
|
type: "checkbox",
|
|
246
246
|
checked: H,
|
|
247
247
|
onChange: (e) => U(e.target.checked),
|
|
248
|
-
className: "size-4 rounded border-border text-
|
|
248
|
+
className: "size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]"
|
|
249
249
|
}), /* @__PURE__ */ f("label", {
|
|
250
250
|
htmlFor: "isActive",
|
|
251
|
-
className: "text-sm font-medium text-
|
|
251
|
+
className: "text-sm font-medium text-text-primary",
|
|
252
252
|
children: "Quota is active"
|
|
253
253
|
})]
|
|
254
254
|
})
|
|
@@ -256,11 +256,11 @@ var h = () => {
|
|
|
256
256
|
})]
|
|
257
257
|
}),
|
|
258
258
|
/* @__PURE__ */ p("div", {
|
|
259
|
-
className: "border border-border rounded-
|
|
259
|
+
className: "border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
260
260
|
children: [/* @__PURE__ */ f("div", {
|
|
261
|
-
className: "p-4 border-b border-border bg-
|
|
261
|
+
className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
|
|
262
262
|
children: /* @__PURE__ */ f("h2", {
|
|
263
|
-
className: "font-semibold text-
|
|
263
|
+
className: "font-semibold text-text-primary",
|
|
264
264
|
children: "Limits Configuration"
|
|
265
265
|
})
|
|
266
266
|
}), /* @__PURE__ */ p("div", {
|
|
@@ -268,13 +268,13 @@ var h = () => {
|
|
|
268
268
|
children: [
|
|
269
269
|
/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
|
|
270
270
|
htmlFor: "limitType",
|
|
271
|
-
className: "block text-sm font-medium text-
|
|
271
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
272
272
|
children: "Limit Type *"
|
|
273
273
|
}), /* @__PURE__ */ p("select", {
|
|
274
274
|
id: "limitType",
|
|
275
275
|
value: M,
|
|
276
276
|
onChange: (e) => N(e.target.value),
|
|
277
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
277
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
278
278
|
children: [
|
|
279
279
|
/* @__PURE__ */ f("option", {
|
|
280
280
|
value: "numeric",
|
|
@@ -297,7 +297,7 @@ var h = () => {
|
|
|
297
297
|
/* @__PURE__ */ p("div", { children: [
|
|
298
298
|
/* @__PURE__ */ f("label", {
|
|
299
299
|
htmlFor: "limitValue",
|
|
300
|
-
className: "block text-sm font-medium text-
|
|
300
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
301
301
|
children: "Limit Value *"
|
|
302
302
|
}),
|
|
303
303
|
/* @__PURE__ */ f("input", {
|
|
@@ -306,19 +306,19 @@ var h = () => {
|
|
|
306
306
|
min: "0",
|
|
307
307
|
value: P,
|
|
308
308
|
onChange: (e) => F(e.target.value),
|
|
309
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
309
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
310
310
|
placeholder: "1000",
|
|
311
311
|
required: !0
|
|
312
312
|
}),
|
|
313
313
|
/* @__PURE__ */ f("p", {
|
|
314
|
-
className: "text-xs text-muted
|
|
314
|
+
className: "text-xs text-text-muted mt-1",
|
|
315
315
|
children: "The maximum limit value for this quota"
|
|
316
316
|
})
|
|
317
317
|
] }),
|
|
318
318
|
/* @__PURE__ */ p("div", { children: [
|
|
319
319
|
/* @__PURE__ */ f("label", {
|
|
320
320
|
htmlFor: "unit",
|
|
321
|
-
className: "block text-sm font-medium text-
|
|
321
|
+
className: "block text-sm font-medium text-text-primary mb-1",
|
|
322
322
|
children: "Unit *"
|
|
323
323
|
}),
|
|
324
324
|
/* @__PURE__ */ f("input", {
|
|
@@ -326,12 +326,12 @@ var h = () => {
|
|
|
326
326
|
type: "text",
|
|
327
327
|
value: I,
|
|
328
328
|
onChange: (e) => L(e.target.value),
|
|
329
|
-
className: "w-full px-3 py-2 border border-border rounded-
|
|
329
|
+
className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
330
330
|
placeholder: "e.g., bots, GB, MB, seats, workflow runs",
|
|
331
331
|
required: !0
|
|
332
332
|
}),
|
|
333
333
|
/* @__PURE__ */ f("p", {
|
|
334
|
-
className: "text-xs text-muted
|
|
334
|
+
className: "text-xs text-text-muted mt-1",
|
|
335
335
|
children: "The unit of measurement displayed with usage values (e.g., \"2/6 bots\")"
|
|
336
336
|
})
|
|
337
337
|
] })
|
|
@@ -343,12 +343,12 @@ var h = () => {
|
|
|
343
343
|
children: [/* @__PURE__ */ f("button", {
|
|
344
344
|
type: "submit",
|
|
345
345
|
disabled: T,
|
|
346
|
-
className: "px-6 py-2 text-sm font-medium bg-primary text-primary-
|
|
346
|
+
className: "px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50",
|
|
347
347
|
children: T ? g("billing.quota.saving", "Saving...") : g("billing.quota.saveChanges", "Save Changes")
|
|
348
348
|
}), /* @__PURE__ */ f("button", {
|
|
349
349
|
type: "button",
|
|
350
350
|
onClick: () => v(`/quotas/${_}`),
|
|
351
|
-
className: "px-6 py-2 text-sm font-medium border border-border text-
|
|
351
|
+
className: "px-6 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
|
|
352
352
|
children: "Cancel"
|
|
353
353
|
})]
|
|
354
354
|
})
|
|
@@ -359,12 +359,12 @@ var h = () => {
|
|
|
359
359
|
children: /* @__PURE__ */ p("div", {
|
|
360
360
|
className: "text-center space-y-4",
|
|
361
361
|
children: [/* @__PURE__ */ f("h2", {
|
|
362
|
-
className: "text-lg font-semibold text-
|
|
362
|
+
className: "text-lg font-semibold text-text-primary",
|
|
363
363
|
children: "Quota not found"
|
|
364
364
|
}), /* @__PURE__ */ f("button", {
|
|
365
365
|
type: "button",
|
|
366
366
|
onClick: () => v("/quotas"),
|
|
367
|
-
className: "px-4 py-2 text-sm font-medium bg-primary text-primary-
|
|
367
|
+
className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover",
|
|
368
368
|
children: "Back to Quotas"
|
|
369
369
|
})]
|
|
370
370
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaTemplateEditPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplateEditPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Template Edit Page\n * Form to edit an existing quota template\n */\n\nimport { useState, useEffect, type FC, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplate, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { isServerError } from '../../../shared/utils';\nimport { QuotaResetPeriod, type UpdateQuotaInput } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const QuotaTemplateEditPage: 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 { quotaId } = useParams<{ quotaId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const {\n quota,\n isLoading: isQuotaLoading,\n error: quotaError,\n refetch,\n } = useQuotaTemplate(quotaId);\n const { updateQuota, isUpdating } = useQuotaTemplateMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [displayName, setDisplayName] = useState('');\n const [description, setDescription] = useState('');\n const [limitType, setLimitType] = useState('numeric');\n const [limitValue, setLimitValue] = useState('');\n const [unit, setUnit] = useState('');\n const [reusable, setReusable] = useState(false);\n const [resetPeriod, setResetPeriod] = useState<QuotaResetPeriod>(QuotaResetPeriod.NEVER);\n const [isActive, setIsActive] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Populate form when quota data loads\n useEffect(() => {\n if (quota) {\n setName(quota.name ?? '');\n setReusable(quota.reusable ?? false);\n setResetPeriod(quota.resetPeriod ?? QuotaResetPeriod.NEVER);\n setIsActive(quota.isActive ?? true);\n\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n setLimitType(limits?.type ?? 'numeric');\n setLimitValue(String(limits?.value ?? ''));\n setUnit(limits?.unit ?? '');\n\n const ctx = quota.context as { displayName?: string; description?: string } | null;\n setDisplayName(ctx?.displayName ?? '');\n setDescription(ctx?.description ?? '');\n }\n }, [quota]);\n\n // Permission check\n if (!permissions.canManageQuotas) {\n return <AccessDenied message=\"You don't have permission to edit quotas.\" />;\n }\n\n // Loading state\n if (isQuotaLoading) {\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=\"max-w-2xl space-y-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (quotaError && isServerError(quotaError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load quota. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!quota) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">Quota not found</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90\"\n >\n Back to Quotas\n </button>\n </div>\n </div>\n );\n }\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n setSuccess(false);\n\n // Validation\n if (!name.trim()) {\n setError('Quota name is required');\n return;\n }\n if (!limitValue || isNaN(Number(limitValue)) || Number(limitValue) < 0) {\n setError('Valid limit value is required');\n return;\n }\n if (!unit.trim()) {\n setError('Unit is required (e.g., bots, GB, seats)');\n return;\n }\n\n const input: UpdateQuotaInput = {\n id: quotaId!,\n name: name.trim(),\n limits: {\n type: limitType,\n value: Number(limitValue),\n unit: unit.trim(),\n },\n reusable,\n isActive,\n resetPeriod,\n context: {\n displayName: displayName.trim() || name.trim(),\n description: description.trim(),\n },\n };\n\n try {\n await updateQuota(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo(`/quotas/${quotaId}`), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update quota');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={`Edit Quota: ${(quota.context as { displayName?: string } | null)?.displayName ?? quota.name}`}\n description=\"Update the quota template configuration\"\n />\n\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-6\">\n {/* Error/Success Messages */}\n {error && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n {success && (\n <div className=\"p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-success-text\">\n Quota updated successfully! Redirecting…\n </p>\n </div>\n )}\n\n {/* Basic Info */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Basic Information</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Machine Key (name) */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-foreground mb-1\">\n Quota Key *\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary font-mono text-sm\"\n placeholder=\"e.g., sessions-started, tunnels-created\"\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Internal machine key, must match the <code>quotaName</code> used in{' '}\n <code>@usage</code> directives. Never shown to users.\n </p>\n </div>\n\n {/* Display Name */}\n <div>\n <label\n htmlFor=\"displayName\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n Display Name *\n </label>\n <input\n id=\"displayName\"\n type=\"text\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"e.g., Tunnels, Session Starts, Webhooks\"\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Human-readable name shown to users in billing and usage UI.\n </p>\n </div>\n\n {/* Description */}\n <div>\n <label\n htmlFor=\"description\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n Description\n </label>\n <textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary resize-none\"\n placeholder=\"e.g., Active tunnels created through VibeControls (slot freed on delete)\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Short explanation shown in billing and usage detail views.\n </p>\n </div>\n\n {/* Quota Type (read-only) */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Quota Type</label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/50 text-muted-foreground\">\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n <span className=\"text-xs ml-2\">(cannot be changed)</span>\n </div>\n </div>\n\n {/* Reusable */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"reusable\"\n type=\"checkbox\"\n checked={reusable}\n onChange={(e) => setReusable(e.target.checked)}\n className=\"size-4 rounded border-border text-primary focus:ring-primary\"\n />\n <div>\n <label htmlFor=\"reusable\" className=\"text-sm font-medium text-foreground\">\n Reusable Quota\n </label>\n <p className=\"text-xs text-muted-foreground\">\n Enable for quotas like seats or bots that can be freed up\n </p>\n </div>\n </div>\n\n {/* Reset Period */}\n <div>\n <label\n htmlFor=\"resetPeriod\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n Reset Period\n </label>\n <select\n id=\"resetPeriod\"\n value={resetPeriod}\n onChange={(e) => setResetPeriod(e.target.value as QuotaResetPeriod)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={QuotaResetPeriod.NEVER}>Never (one-time limit)</option>\n <option value={QuotaResetPeriod.DAILY}>Daily (resets every day)</option>\n <option value={QuotaResetPeriod.MONTHLY}>Monthly (resets every month)</option>\n </select>\n <p className=\"text-xs text-muted-foreground mt-1\">\n How often the quota usage counter is reset to zero\n </p>\n </div>\n\n {/* Status */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"size-4 rounded border-border text-primary focus:ring-primary\"\n />\n <label htmlFor=\"isActive\" className=\"text-sm font-medium text-foreground\">\n Quota is active\n </label>\n </div>\n </div>\n </div>\n\n {/* Limits Configuration */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Limits Configuration</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Limit Type */}\n <div>\n <label htmlFor=\"limitType\" className=\"block text-sm font-medium text-foreground mb-1\">\n Limit Type *\n </label>\n <select\n id=\"limitType\"\n value={limitType}\n onChange={(e) => setLimitType(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"numeric\">Numeric (count)</option>\n <option value=\"storage\">Storage (bytes)</option>\n <option value=\"duration\">Duration (seconds)</option>\n <option value=\"percentage\">Percentage</option>\n </select>\n </div>\n\n {/* Limit Value */}\n <div>\n <label\n htmlFor=\"limitValue\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n Limit Value *\n </label>\n <input\n id=\"limitValue\"\n type=\"number\"\n min=\"0\"\n value={limitValue}\n onChange={(e) => setLimitValue(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"1000\"\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n The maximum limit value for this quota\n </p>\n </div>\n\n {/* Unit */}\n <div>\n <label htmlFor=\"unit\" className=\"block text-sm font-medium text-foreground mb-1\">\n Unit *\n </label>\n <input\n id=\"unit\"\n type=\"text\"\n value={unit}\n onChange={(e) => setUnit(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"e.g., bots, GB, MB, seats, workflow runs\"\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n The unit of measurement displayed with usage values (e.g., "2/6 bots")\n </p>\n </div>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isUpdating}\n className=\"px-6 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isUpdating\n ? tr('billing.quota.saving', 'Saving...')\n : tr('billing.quota.saveChanges', 'Save Changes')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/quotas/${quotaId}`)}\n className=\"px-6 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,IAAa,UAAkC;CAC7C,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,EAAE,eAAY,GAAgC,EAC9C,IAAa,GAAoB,EACjC,IAAc,GAAuB,EACrC,EACJ,UACA,WAAW,GACX,OAAO,GACP,eACE,EAAiB,EAAQ,EACvB,EAAE,gBAAa,kBAAe,GAA2B,EAGzD,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAW,KAAgB,EAAS,UAAU,EAC/C,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAa,KAAkB,EAA2B,EAAiB,MAAM,EAClF,CAAC,GAAU,KAAe,EAAS,GAAK,EACxC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAS,KAAc,EAAS,GAAM;AAqH7C,QAlHA,QAAgB;AACd,MAAI,GAAO;AAIT,GAHA,EAAQ,EAAM,QAAQ,GAAG,EACzB,EAAY,EAAM,YAAY,GAAM,EACpC,EAAe,EAAM,eAAe,EAAiB,MAAM,EAC3D,EAAY,EAAM,YAAY,GAAK;GAEnC,IAAM,IAAS,EAAM;AAGrB,GAFA,EAAa,GAAQ,QAAQ,UAAU,EACvC,EAAc,OAAO,GAAQ,SAAS,GAAG,CAAC,EAC1C,EAAQ,GAAQ,QAAQ,GAAG;GAE3B,IAAM,IAAM,EAAM;AAElB,GADA,EAAe,GAAK,eAAe,GAAG,EACtC,EAAe,GAAK,eAAe,GAAG;;IAEvC,CAAC,EAAM,CAAC,EAGN,EAAY,kBAKb,IAEA,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAK,WAAU,2CAA4C,CAAA,EAC3D,kBAAC,OAAD;GAAK,WAAU;aACZ;IAAC;IAAG;IAAG;IAAG;IAAE,CAAC,KAAK,MACjB,kBAAC,OAAD,EAAa,WAAU,uCAAwC,EAArD,EAAqD,CAC/D;GACE,CAAA,CACF;MAKN,KAAc,EAAc,EAAW,GAEvC,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,OAAM;GACN,SAAQ;GACR,eAAe,GAAS;GACxB,WAAA;GACA,CAAA;EACE,CAAA,GAIL,IAgEH,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAO,eAAgB,EAAM,SAA6C,eAAe,EAAM;GAC/F,aAAY;GACZ,CAAA,EAEF,kBAAC,QAAD;GAAM,UArDW,OAAO,MAAiB;AAM3C,QALA,EAAE,gBAAgB,EAClB,EAAS,KAAK,EACd,EAAW,GAAM,EAGb,CAAC,EAAK,MAAM,EAAE;AAChB,OAAS,yBAAyB;AAClC;;AAEF,QAAI,CAAC,KAAc,MAAM,OAAO,EAAW,CAAC,IAAI,OAAO,EAAW,GAAG,GAAG;AACtE,OAAS,gCAAgC;AACzC;;AAEF,QAAI,CAAC,EAAK,MAAM,EAAE;AAChB,OAAS,2CAA2C;AACpD;;IAGF,IAAM,IAA0B;KAC9B,IAAI;KACJ,MAAM,EAAK,MAAM;KACjB,QAAQ;MACN,MAAM;MACN,OAAO,OAAO,EAAW;MACzB,MAAM,EAAK,MAAM;MAClB;KACD;KACA;KACA;KACA,SAAS;MACP,aAAa,EAAY,MAAM,IAAI,EAAK,MAAM;MAC9C,aAAa,EAAY,MAAM;MAChC;KACF;AAED,QAAI;AAIF,KAHA,MAAM,EAAY,EAAM,EACxB,EAAW,GAAK,EAEhB,iBAAiB,EAAW,WAAW,IAAU,EAAE,KAAK;aACjD,GAAK;AACZ,OAAS,aAAe,QAAQ,EAAI,UAAU,yBAAyB;;;GAWzC,WAAU;aAAxC;IAEG,KACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,KAAD;MAAG,WAAU;gBAAkC;MAAU,CAAA;KACrD,CAAA;IAEP,KACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,KAAD;MAAG,WAAU;gBAAmC;MAE5C,CAAA;KACA,CAAA;IAIR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,MAAD;OAAI,WAAU;iBAAgC;OAAsB,CAAA;MAChE,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEE,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SAAO,SAAQ;SAAO,WAAU;mBAAiD;SAEzE,CAAA;QACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;SACxC,WAAU;SACV,aAAY;SACZ,UAAA;SACA,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAb;UAAkD;UACX,kBAAC,QAAD,EAAA,UAAM,aAAgB,CAAA;;UAAS;UACpE,kBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;;UACjB;;QACA,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA;QACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;SAC/C,WAAU;SACV,aAAY;SACZ,UAAA;SACA,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAE9C,CAAA;QACA,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA;QACR,kBAAC,YAAD;SACE,IAAG;SACH,OAAO;SACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;SAC/C,MAAM;SACN,WAAU;SACV,aAAY;SACZ,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAE9C,CAAA;QACA,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAiD;QAAkB,CAAA,EACpF,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,EAAM,cAAc,WAAW,WAAW,oBAC3C,kBAAC,QAAD;SAAM,WAAU;mBAAe;SAA0B,CAAA,CACrD;UACF,EAAA,CAAA;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,SAAS;SACT,WAAW,MAAM,EAAY,EAAE,OAAO,QAAQ;SAC9C,WAAU;SACV,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,SAAQ;SAAW,WAAU;mBAAsC;SAElE,CAAA,EACR,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAEzC,CAAA,CACA,EAAA,CAAA,CACF;;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA;QACR,kBAAC,UAAD;SACE,IAAG;SACH,OAAO;SACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAA0B;SACnE,WAAU;mBAJZ;UAME,kBAAC,UAAD;WAAQ,OAAO,EAAiB;qBAAO;WAA+B,CAAA;UACtE,kBAAC,UAAD;WAAQ,OAAO,EAAiB;qBAAO;WAAiC,CAAA;UACxE,kBAAC,UAAD;WAAQ,OAAO,EAAiB;qBAAS;WAAqC,CAAA;UACvE;;QACT,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAE9C,CAAA;QACA,EAAA,CAAA;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,SAAS;SACT,WAAW,MAAM,EAAY,EAAE,OAAO,QAAQ;SAC9C,WAAU;SACV,CAAA,EACF,kBAAC,SAAD;SAAO,SAAQ;SAAW,WAAU;mBAAsC;SAElE,CAAA,CACJ;;OACF;QACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,MAAD;OAAI,WAAU;iBAAgC;OAAyB,CAAA;MACnE,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAY,WAAU;kBAAiD;QAE9E,CAAA,EACR,kBAAC,UAAD;QACE,IAAG;QACH,OAAO;QACP,WAAW,MAAM,EAAa,EAAE,OAAO,MAAM;QAC7C,WAAU;kBAJZ;SAME,kBAAC,UAAD;UAAQ,OAAM;oBAAU;UAAwB,CAAA;SAChD,kBAAC,UAAD;UAAQ,OAAM;oBAAU;UAAwB,CAAA;SAChD,kBAAC,UAAD;UAAQ,OAAM;oBAAW;UAA2B,CAAA;SACpD,kBAAC,UAAD;UAAQ,OAAM;oBAAa;UAAmB,CAAA;SACvC;UACL,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA;QACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,KAAI;SACJ,OAAO;SACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;SAC9C,WAAU;SACV,aAAY;SACZ,UAAA;SACA,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAE9C,CAAA;QACA,EAAA,CAAA;OAGN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SAAO,SAAQ;SAAO,WAAU;mBAAiD;SAEzE,CAAA;QACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;SACxC,WAAU;SACV,aAAY;SACZ,UAAA;SACA,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAE9C,CAAA;QACA,EAAA,CAAA;OACF;QACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,WAAU;gBAET,IACG,EAAG,wBAAwB,YAAY,GACvC,EAAG,6BAA6B,eAAe;MAC5C,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,WAAW,IAAU;MAC/C,WAAU;gBACX;MAEQ,CAAA,CACL;;IACD;KACH;MAnTJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAwC;IAAoB,CAAA,EAC1E,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,UAAU;IACpC,WAAU;cACX;IAEQ,CAAA,CACL;;EACF,CAAA,GA5CD,kBAAC,GAAD,EAAc,SAAQ,6CAA8C,CAAA"}
|
|
1
|
+
{"version":3,"file":"QuotaTemplateEditPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplateEditPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Template Edit Page\n * Form to edit an existing quota template\n */\n\nimport { useState, useEffect, type FC, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplate, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { isServerError } from '../../../shared/utils';\nimport { QuotaResetPeriod, type UpdateQuotaInput } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const QuotaTemplateEditPage: 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 { quotaId } = useParams<{ quotaId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const {\n quota,\n isLoading: isQuotaLoading,\n error: quotaError,\n refetch,\n } = useQuotaTemplate(quotaId);\n const { updateQuota, isUpdating } = useQuotaTemplateMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [displayName, setDisplayName] = useState('');\n const [description, setDescription] = useState('');\n const [limitType, setLimitType] = useState('numeric');\n const [limitValue, setLimitValue] = useState('');\n const [unit, setUnit] = useState('');\n const [reusable, setReusable] = useState(false);\n const [resetPeriod, setResetPeriod] = useState<QuotaResetPeriod>(QuotaResetPeriod.NEVER);\n const [isActive, setIsActive] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Populate form when quota data loads\n useEffect(() => {\n if (quota) {\n setName(quota.name ?? '');\n setReusable(quota.reusable ?? false);\n setResetPeriod(quota.resetPeriod ?? QuotaResetPeriod.NEVER);\n setIsActive(quota.isActive ?? true);\n\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n setLimitType(limits?.type ?? 'numeric');\n setLimitValue(String(limits?.value ?? ''));\n setUnit(limits?.unit ?? '');\n\n const ctx = quota.context as { displayName?: string; description?: string } | null;\n setDisplayName(ctx?.displayName ?? '');\n setDescription(ctx?.description ?? '');\n }\n }, [quota]);\n\n // Permission check\n if (!permissions.canManageQuotas) {\n return <AccessDenied message=\"You don't have permission to edit quotas.\" />;\n }\n\n // Loading state\n if (isQuotaLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"max-w-2xl space-y-4\">\n {[1, 2, 3, 4].map((i) => (\n <div key={i} className=\"h-16 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (quotaError && isServerError(quotaError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load quota. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!quota) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Quota not found</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover\"\n >\n Back to Quotas\n </button>\n </div>\n </div>\n );\n }\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n setSuccess(false);\n\n // Validation\n if (!name.trim()) {\n setError('Quota name is required');\n return;\n }\n if (!limitValue || isNaN(Number(limitValue)) || Number(limitValue) < 0) {\n setError('Valid limit value is required');\n return;\n }\n if (!unit.trim()) {\n setError('Unit is required (e.g., bots, GB, seats)');\n return;\n }\n\n const input: UpdateQuotaInput = {\n id: quotaId!,\n name: name.trim(),\n limits: {\n type: limitType,\n value: Number(limitValue),\n unit: unit.trim(),\n },\n reusable,\n isActive,\n resetPeriod,\n context: {\n displayName: displayName.trim() || name.trim(),\n description: description.trim(),\n },\n };\n\n try {\n await updateQuota(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo(`/quotas/${quotaId}`), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update quota');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={`Edit Quota: ${(quota.context as { displayName?: string } | null)?.displayName ?? quota.name}`}\n description=\"Update the quota template configuration\"\n />\n\n <form onSubmit={handleSubmit} className=\"max-w-2xl space-y-6\">\n {/* Error/Success Messages */}\n {error && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n {success && (\n <div className=\"p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-success-text\">\n Quota updated successfully! Redirecting…\n </p>\n </div>\n )}\n\n {/* Basic Info */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Basic Information</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Machine Key (name) */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n Quota Key *\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] font-mono text-sm\"\n placeholder=\"e.g., sessions-started, tunnels-created\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Internal machine key, must match the <code>quotaName</code> used in{' '}\n <code>@usage</code> directives. Never shown to users.\n </p>\n </div>\n\n {/* Display Name */}\n <div>\n <label\n htmlFor=\"displayName\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Display Name *\n </label>\n <input\n id=\"displayName\"\n type=\"text\"\n value={displayName}\n onChange={(e) => setDisplayName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"e.g., Tunnels, Session Starts, Webhooks\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Human-readable name shown to users in billing and usage UI.\n </p>\n </div>\n\n {/* Description */}\n <div>\n <label\n htmlFor=\"description\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Description\n </label>\n <textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n placeholder=\"e.g., Active tunnels created through VibeControls (slot freed on delete)\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Short explanation shown in billing and usage detail views.\n </p>\n </div>\n\n {/* Quota Type (read-only) */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Quota Type</label>\n <div className=\"px-3 py-2 border border-border-subtle rounded-md bg-bg-sunken/50 text-text-muted\">\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n <span className=\"text-xs ml-2\">(cannot be changed)</span>\n </div>\n </div>\n\n {/* Reusable */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"reusable\"\n type=\"checkbox\"\n checked={reusable}\n onChange={(e) => setReusable(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <div>\n <label htmlFor=\"reusable\" className=\"text-sm font-medium text-text-primary\">\n Reusable Quota\n </label>\n <p className=\"text-xs text-text-muted\">\n Enable for quotas like seats or bots that can be freed up\n </p>\n </div>\n </div>\n\n {/* Reset Period */}\n <div>\n <label\n htmlFor=\"resetPeriod\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Reset Period\n </label>\n <select\n id=\"resetPeriod\"\n value={resetPeriod}\n onChange={(e) => setResetPeriod(e.target.value as QuotaResetPeriod)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value={QuotaResetPeriod.NEVER}>Never (one-time limit)</option>\n <option value={QuotaResetPeriod.DAILY}>Daily (resets every day)</option>\n <option value={QuotaResetPeriod.MONTHLY}>Monthly (resets every month)</option>\n </select>\n <p className=\"text-xs text-text-muted mt-1\">\n How often the quota usage counter is reset to zero\n </p>\n </div>\n\n {/* Status */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <label htmlFor=\"isActive\" className=\"text-sm font-medium text-text-primary\">\n Quota is active\n </label>\n </div>\n </div>\n </div>\n\n {/* Limits Configuration */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Limits Configuration</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Limit Type */}\n <div>\n <label\n htmlFor=\"limitType\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Limit Type *\n </label>\n <select\n id=\"limitType\"\n value={limitType}\n onChange={(e) => setLimitType(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"numeric\">Numeric (count)</option>\n <option value=\"storage\">Storage (bytes)</option>\n <option value=\"duration\">Duration (seconds)</option>\n <option value=\"percentage\">Percentage</option>\n </select>\n </div>\n\n {/* Limit Value */}\n <div>\n <label\n htmlFor=\"limitValue\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Limit Value *\n </label>\n <input\n id=\"limitValue\"\n type=\"number\"\n min=\"0\"\n value={limitValue}\n onChange={(e) => setLimitValue(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"1000\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">The maximum limit value for this quota</p>\n </div>\n\n {/* Unit */}\n <div>\n <label htmlFor=\"unit\" className=\"block text-sm font-medium text-text-primary mb-1\">\n Unit *\n </label>\n <input\n id=\"unit\"\n type=\"text\"\n value={unit}\n onChange={(e) => setUnit(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"e.g., bots, GB, MB, seats, workflow runs\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n The unit of measurement displayed with usage values (e.g., "2/6 bots")\n </p>\n </div>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isUpdating}\n className=\"px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isUpdating\n ? tr('billing.quota.saving', 'Saving...')\n : tr('billing.quota.saveChanges', 'Save Changes')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/quotas/${quotaId}`)}\n className=\"px-6 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}
|