@burdenoff/microfe-billing 2026.625.3 → 2026.625.5
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/lib/native-purchases.js +2 -44
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +192 -187
- package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonCreatePage.js +322 -317
- package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonDetailPage.js +190 -185
- package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonEditPage.js +297 -292
- package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +60 -75
- package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
- package/dist/billing/modules/addons/pages/AddonsListPage.js +86 -101
- package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +124 -121
- package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js +103 -111
- package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +196 -160
- package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +58 -73
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +65 -75
- package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +399 -399
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/TransactionsPage.js +49 -48
- package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/UserRefundsPage.js +144 -151
- package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +430 -425
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js +121 -93
- package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CouponsListPage.js +154 -152
- package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js +467 -462
- package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
- package/dist/billing/modules/coupons/pages/EditCouponPage.js +294 -286
- package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js +123 -117
- package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +43 -42
- package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +192 -207
- package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
- package/dist/billing/modules/credits/pages/CreditsPage.js +40 -56
- package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +128 -121
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +165 -180
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/EarningsPage.js +103 -100
- package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +175 -175
- package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
- package/dist/billing/modules/earnings/pages/PayoutsPage.js +96 -96
- package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +42 -40
- package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +45 -38
- package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanCreatePage.js +339 -334
- package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanDetailPage.js +80 -74
- package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlanEditPage.js +342 -337
- package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js +38 -53
- package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
- package/dist/billing/modules/plans/pages/PlansListPage.js +60 -75
- package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +242 -237
- package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js +29 -39
- package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js +1 -0
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +299 -294
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +191 -186
- package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +308 -303
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +57 -78
- package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/SettingsPage.js +79 -78
- package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +15 -13
- package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +338 -299
- package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +82 -103
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +59 -54
- package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +172 -215
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/package.json +2 -2
- package/dist/billing/lib/native-purchases.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddonCreatePage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonCreatePage.tsx"],"sourcesContent":["/**\n * Addons Module - Addon Create Page\n * Form to create a new addon\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useAddonMutations } from '../hooks';\nimport { useQuotas } from '../../plans/hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, CurrencyPriceOverridesEditor, PageHeader } from '../../../shared/components';\nimport {\n PlanDuration,\n type CreateAddonInput,\n type CurrencyPriceOverrideInput,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface QuotaAllocation {\n quotaId: string;\n quantity: number;\n}\n\nexport const AddonCreatePage: 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 navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { createAddon, isCreating } = useAddonMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [price, setPrice] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [duration, setDuration] = useState<PlanDuration>(PlanDuration.MONTHLY);\n const [productId, setProductId] = useState('');\n const [quotaAllocations, setQuotaAllocations] = useState<QuotaAllocation[]>([]);\n const [currencyPriceOverrides, setCurrencyPriceOverrides] = useState<\n CurrencyPriceOverrideInput[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Permission check\n if (!permissions.canManageAddons) {\n return (\n <AccessDenied\n message={tr(\n 'billing.addons.noCreatePermission',\n \"You don't have permission to create addons.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isQuotasLoading) {\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, 5].map((i) => (\n <div key={i} className=\"h-16 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n const handleAddQuota = () => {\n // Add new quota at the top with empty quotaId (placeholder)\n setQuotaAllocations([{ quotaId: '', quantity: 1 }, ...quotaAllocations]);\n };\n\n const handleRemoveQuota = (index: number) => {\n setQuotaAllocations(quotaAllocations.filter((_, i) => i !== index));\n };\n\n const handleQuotaChange = (\n index: number,\n field: 'quotaId' | 'quantity',\n value: string | number\n ) => {\n const updated = [...quotaAllocations];\n if (field === 'quotaId') {\n updated[index].quotaId = value as string;\n } else {\n updated[index].quantity = Number(value);\n }\n setQuotaAllocations(updated);\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('Addon name is required');\n return;\n }\n if (!price || isNaN(Number(price)) || Number(price) < 0) {\n setError('Valid price is required');\n return;\n }\n if (!productId.trim()) {\n setError('Product ID is required');\n return;\n }\n if (quotaAllocations.length === 0) {\n setError('At least one quota is required');\n return;\n }\n // Check for unselected quotas\n const unselectedQuotas = quotaAllocations.filter((a) => a.quotaId === '');\n if (unselectedQuotas.length > 0) {\n setError('Please select a quota for all allocations');\n return;\n }\n\n const input: CreateAddonInput = {\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n productId: productId.trim(),\n quotas: quotaAllocations.map((a) => ({\n quotaId: a.quotaId,\n quantity: a.quantity,\n })),\n ...(currencyPriceOverrides.length > 0 ? { currencyPriceOverrides } : {}),\n };\n\n try {\n await createAddon(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo('/addons'), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create addon');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.addons.createNewAddon', 'Create New Addon')}\n description={tr('billing.addons.createNewAddonDesc', 'Set up a new addon with quotas')}\n />\n\n <form onSubmit={handleSubmit} className=\"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 {tr('billing.addons.createSuccess', 'Addon created successfully! Redirecting...')}\n </p>\n </div>\n )}\n\n {/* Two-column layout on desktop */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Basic Info */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit\">\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.addons.basicInfo', 'Basic Information')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('billing.addons.addonNameLabel', 'Addon Name')} *\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)]\"\n placeholder={tr('billing.addons.addonNamePlaceholder', 'e.g., Extra Storage')}\n required\n />\n </div>\n\n {/* Product ID */}\n <div>\n <label\n htmlFor=\"productId\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.productId', 'Product ID')} *\n </label>\n <input\n id=\"productId\"\n type=\"text\"\n value={productId}\n onChange={(e) => setProductId(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={tr('billing.addons.productIdPlaceholder', 'e.g., prod_abc123')}\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr('billing.addons.productIdDesc', 'The product this addon belongs to')}\n </p>\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"price\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.priceLabel', 'Price')} *\n </label>\n <input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n value={price}\n onChange={(e) => setPrice(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=\"9.99\"\n required\n />\n </div>\n <div>\n <label\n htmlFor=\"currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.currencyLabel', 'Currency')} *\n </label>\n <select\n id=\"currency\"\n value={currency}\n onChange={(e) => setCurrency(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=\"USD\">USD ($)</option>\n <option value=\"EUR\">EUR</option>\n <option value=\"GBP\">GBP</option>\n <option value=\"INR\">INR</option>\n <option value=\"AUD\">AUD</option>\n <option value=\"CAD\">CAD</option>\n <option value=\"JPY\">JPY</option>\n <option value=\"SGD\">SGD</option>\n </select>\n </div>\n </div>\n\n {/* Duration */}\n <div>\n <label\n htmlFor=\"duration\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.billingPeriodLabel', 'Billing Period')} *\n </label>\n <select\n id=\"duration\"\n value={duration}\n onChange={(e) => setDuration(e.target.value as PlanDuration)}\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={PlanDuration.MONTHLY}>Monthly</option>\n <option value={PlanDuration.YEARLY}>Yearly</option>\n </select>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30 flex items-center justify-between\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.quotaAllocations', 'Quota Allocations')} *\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n disabled={quotas.length === 0}\n className=\"px-3 py-1 text-sm font-medium text-text-link hover:bg-[var(--color-accent-soft)] rounded-md transition-colors disabled:opacity-50\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.addons.noQuotasAvailable',\n 'No quotas available. Please create quotas first.'\n )}\n </p>\n ) : quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.addons.noQuotasAllocated',\n 'No quotas allocated. Click \"Add Quota\" to add one.'\n )}\n </p>\n ) : (\n quotaAllocations.map((allocation, index) => {\n const selectedQuota = quotas.find((q) => q.id === allocation.quotaId);\n const limits = selectedQuota?.limits as { type?: string; value?: number } | null;\n const limitValue = limits?.value ?? 0;\n const limitType = limits?.type ?? 'unknown';\n const totalValue = limitValue * allocation.quantity;\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border-subtle rounded-lg bg-bg-sunken/20 space-y-3\"\n >\n <div className=\"flex items-center gap-3\">\n <select\n value={allocation.quotaId}\n onChange={(e) => handleQuotaChange(index, 'quotaId', e.target.value)}\n className={`flex-1 px-3 py-2 border border-border-subtle rounded-input bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n allocation.quotaId === '' ? 'text-text-muted' : 'text-text-primary'\n }`}\n >\n <option value=\"\" disabled>\n {tr('billing.addons.selectQuota', 'Select a quota...')}\n </option>\n {quotas.map((quota) => (\n <option key={quota.id} value={quota.id}>\n {quota.name}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => handleRemoveQuota(index)}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Remove quota\"\n >\n <svg\n className=\"size-5\"\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=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n\n {/* Quota Info - only show when quota is selected */}\n {selectedQuota && (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"text-text-muted\">\n <span className=\"capitalize\">Type: {limitType}</span>\n <span className=\"mx-2\">•</span>\n <span>\n Value per unit:{' '}\n <span className=\"font-medium text-text-primary\">\n {limitValue.toLocaleString()}\n </span>\n </span>\n </div>\n </div>\n\n {/* Quantity & Total */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-muted\">\n {tr('billing.addons.quantity', 'Quantity')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) =>\n handleQuotaChange(index, 'quantity', e.target.value)\n }\n className=\"w-24 px-3 py-1.5 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] text-center\"\n />\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"text-text-muted\">=</span>\n <span className=\"font-semibold text-status-success-text\">\n {totalValue.toLocaleString()} total\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n <CurrencyPriceOverridesEditor\n overrides={currencyPriceOverrides}\n onChange={setCurrencyPriceOverrides}\n />\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isCreating || quotas.length === 0}\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 {isCreating\n ? tr('billing.addons.creating', 'Creating...')\n : tr('billing.addons.createAddonBtn', 'Create Addon')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\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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"}
|
|
1
|
+
{"version":3,"file":"AddonCreatePage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonCreatePage.tsx"],"sourcesContent":["/**\n * Addons Module - Addon Create Page\n * Form to create a new addon\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useAddonMutations } from '../hooks';\nimport { useQuotas } from '../../plans/hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, CurrencyPriceOverridesEditor, PageHeader } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n PlanDuration,\n type CreateAddonInput,\n type CurrencyPriceOverrideInput,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface QuotaAllocation {\n quotaId: string;\n quantity: number;\n}\n\nexport const AddonCreatePage: 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 navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { createAddon, isCreating } = useAddonMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [price, setPrice] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [duration, setDuration] = useState<PlanDuration>(PlanDuration.MONTHLY);\n const [productId, setProductId] = useState('');\n const [quotaAllocations, setQuotaAllocations] = useState<QuotaAllocation[]>([]);\n const [currencyPriceOverrides, setCurrencyPriceOverrides] = useState<\n CurrencyPriceOverrideInput[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Permission check\n if (!permissions.canManageAddons) {\n return (\n <AccessDenied\n message={tr(\n 'billing.addons.noCreatePermission',\n \"You don't have permission to create addons.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isQuotasLoading) {\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, 5].map((i) => (\n <div key={i} className=\"h-16 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n const handleAddQuota = () => {\n // Add new quota at the top with empty quotaId (placeholder)\n setQuotaAllocations([{ quotaId: '', quantity: 1 }, ...quotaAllocations]);\n };\n\n const handleRemoveQuota = (index: number) => {\n setQuotaAllocations(quotaAllocations.filter((_, i) => i !== index));\n };\n\n const handleQuotaChange = (\n index: number,\n field: 'quotaId' | 'quantity',\n value: string | number\n ) => {\n const updated = [...quotaAllocations];\n if (field === 'quotaId') {\n updated[index].quotaId = value as string;\n } else {\n updated[index].quantity = Number(value);\n }\n setQuotaAllocations(updated);\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('Addon name is required');\n return;\n }\n if (!price || isNaN(Number(price)) || Number(price) < 0) {\n setError('Valid price is required');\n return;\n }\n if (!productId.trim()) {\n setError('Product ID is required');\n return;\n }\n if (quotaAllocations.length === 0) {\n setError('At least one quota is required');\n return;\n }\n // Check for unselected quotas\n const unselectedQuotas = quotaAllocations.filter((a) => a.quotaId === '');\n if (unselectedQuotas.length > 0) {\n setError('Please select a quota for all allocations');\n return;\n }\n\n const input: CreateAddonInput = {\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n productId: productId.trim(),\n quotas: quotaAllocations.map((a) => ({\n quotaId: a.quotaId,\n quantity: a.quantity,\n })),\n ...(currencyPriceOverrides.length > 0 ? { currencyPriceOverrides } : {}),\n };\n\n try {\n await createAddon(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo('/addons'), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create addon');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.addons.createNewAddon', 'Create New Addon')}\n description={tr('billing.addons.createNewAddonDesc', 'Set up a new addon with quotas')}\n />\n\n <PagePurpose>\n {tr(\n 'billing.addons.createPurpose',\n 'Create an add-on that customers can buy on top of a subscription — extra seats, storage, usage credits and so on. Set its price (with optional per-currency overrides) and the quotas it grants, then make it available to purchase.'\n )}\n </PagePurpose>\n\n <form onSubmit={handleSubmit} className=\"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 {tr('billing.addons.createSuccess', 'Addon created successfully! Redirecting...')}\n </p>\n </div>\n )}\n\n {/* Two-column layout on desktop */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Basic Info */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit\">\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.addons.basicInfo', 'Basic Information')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('billing.addons.addonNameLabel', 'Addon Name')} *\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)]\"\n placeholder={tr('billing.addons.addonNamePlaceholder', 'e.g., Extra Storage')}\n required\n />\n </div>\n\n {/* Product ID */}\n <div>\n <label\n htmlFor=\"productId\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.productId', 'Product ID')} *\n </label>\n <input\n id=\"productId\"\n type=\"text\"\n value={productId}\n onChange={(e) => setProductId(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={tr('billing.addons.productIdPlaceholder', 'e.g., prod_abc123')}\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr('billing.addons.productIdDesc', 'The product this addon belongs to')}\n </p>\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"price\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.priceLabel', 'Price')} *\n </label>\n <input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n value={price}\n onChange={(e) => setPrice(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=\"9.99\"\n required\n />\n </div>\n <div>\n <label\n htmlFor=\"currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.currencyLabel', 'Currency')} *\n </label>\n <select\n id=\"currency\"\n value={currency}\n onChange={(e) => setCurrency(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=\"USD\">USD ($)</option>\n <option value=\"EUR\">EUR</option>\n <option value=\"GBP\">GBP</option>\n <option value=\"INR\">INR</option>\n <option value=\"AUD\">AUD</option>\n <option value=\"CAD\">CAD</option>\n <option value=\"JPY\">JPY</option>\n <option value=\"SGD\">SGD</option>\n </select>\n </div>\n </div>\n\n {/* Duration */}\n <div>\n <label\n htmlFor=\"duration\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.billingPeriodLabel', 'Billing Period')} *\n </label>\n <select\n id=\"duration\"\n value={duration}\n onChange={(e) => setDuration(e.target.value as PlanDuration)}\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={PlanDuration.MONTHLY}>Monthly</option>\n <option value={PlanDuration.YEARLY}>Yearly</option>\n </select>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30 flex items-center justify-between\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.quotaAllocations', 'Quota Allocations')} *\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n disabled={quotas.length === 0}\n className=\"px-3 py-1 text-sm font-medium text-text-link hover:bg-[var(--color-accent-soft)] rounded-md transition-colors disabled:opacity-50\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.addons.noQuotasAvailable',\n 'No quotas available. Please create quotas first.'\n )}\n </p>\n ) : quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.addons.noQuotasAllocated',\n 'No quotas allocated. Click \"Add Quota\" to add one.'\n )}\n </p>\n ) : (\n quotaAllocations.map((allocation, index) => {\n const selectedQuota = quotas.find((q) => q.id === allocation.quotaId);\n const limits = selectedQuota?.limits as { type?: string; value?: number } | null;\n const limitValue = limits?.value ?? 0;\n const limitType = limits?.type ?? 'unknown';\n const totalValue = limitValue * allocation.quantity;\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border-subtle rounded-lg bg-bg-sunken/20 space-y-3\"\n >\n <div className=\"flex items-center gap-3\">\n <select\n value={allocation.quotaId}\n onChange={(e) => handleQuotaChange(index, 'quotaId', e.target.value)}\n className={`flex-1 px-3 py-2 border border-border-subtle rounded-input bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n allocation.quotaId === '' ? 'text-text-muted' : 'text-text-primary'\n }`}\n >\n <option value=\"\" disabled>\n {tr('billing.addons.selectQuota', 'Select a quota...')}\n </option>\n {quotas.map((quota) => (\n <option key={quota.id} value={quota.id}>\n {quota.name}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => handleRemoveQuota(index)}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Remove quota\"\n >\n <svg\n className=\"size-5\"\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=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n\n {/* Quota Info - only show when quota is selected */}\n {selectedQuota && (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"text-text-muted\">\n <span className=\"capitalize\">Type: {limitType}</span>\n <span className=\"mx-2\">•</span>\n <span>\n Value per unit:{' '}\n <span className=\"font-medium text-text-primary\">\n {limitValue.toLocaleString()}\n </span>\n </span>\n </div>\n </div>\n\n {/* Quantity & Total */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-muted\">\n {tr('billing.addons.quantity', 'Quantity')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) =>\n handleQuotaChange(index, 'quantity', e.target.value)\n }\n className=\"w-24 px-3 py-1.5 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] text-center\"\n />\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"text-text-muted\">=</span>\n <span className=\"font-semibold text-status-success-text\">\n {totalValue.toLocaleString()} total\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n <CurrencyPriceOverridesEditor\n overrides={currencyPriceOverrides}\n onChange={setCurrencyPriceOverrides}\n />\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isCreating || quotas.length === 0}\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 {isCreating\n ? tr('billing.addons.creating', 'Creating...')\n : tr('billing.addons.createAddonBtn', 'Create Addon')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\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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"}
|
|
@@ -15,8 +15,9 @@ import { useMemo as m } from "react";
|
|
|
15
15
|
import { useParams as h } from "react-router-dom";
|
|
16
16
|
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
17
17
|
import { useI18n as v } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
18
|
+
import { EmphasisPanel as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
|
|
18
19
|
//#region src/billing/modules/addons/pages/AddonDetailPage.tsx
|
|
19
|
-
function
|
|
20
|
+
function x(e) {
|
|
20
21
|
let t = /* @__PURE__ */ new Map();
|
|
21
22
|
for (let n of e) {
|
|
22
23
|
if (!n.quota) continue;
|
|
@@ -30,12 +31,12 @@ function y(e) {
|
|
|
30
31
|
}
|
|
31
32
|
return Array.from(t.values());
|
|
32
33
|
}
|
|
33
|
-
var
|
|
34
|
-
let { t: s } = v(),
|
|
34
|
+
var S = () => {
|
|
35
|
+
let { t: s } = v(), S = (e, t) => {
|
|
35
36
|
let n = s(e);
|
|
36
37
|
return n === e ? t : n;
|
|
37
|
-
}, { addonId:
|
|
38
|
-
return
|
|
38
|
+
}, { addonId: C } = h(), w = l(), T = c(), E = p(), { addon: D, isLoading: O, error: k, refetch: A } = u(C), { toggleAddon: j, isToggling: M } = d(), N = m(() => x(D?.features || []), [D?.features]);
|
|
39
|
+
return T.canViewAddons ? O ? /* @__PURE__ */ _("div", {
|
|
39
40
|
className: "space-y-6 p-6",
|
|
40
41
|
children: [/* @__PURE__ */ g("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ _("div", {
|
|
41
42
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
@@ -48,199 +49,203 @@ var b = () => {
|
|
|
48
49
|
].map((e) => /* @__PURE__ */ g("div", { className: "h-24 bg-bg-sunken animate-pulse rounded" }, e))
|
|
49
50
|
}), /* @__PURE__ */ g("div", { className: "h-64 bg-bg-sunken animate-pulse rounded" })]
|
|
50
51
|
})]
|
|
51
|
-
}) :
|
|
52
|
+
}) : k && o(k) ? /* @__PURE__ */ g("div", {
|
|
52
53
|
className: "p-6",
|
|
53
54
|
children: /* @__PURE__ */ g(n, {
|
|
54
|
-
title:
|
|
55
|
-
message:
|
|
56
|
-
onRetry: () =>
|
|
55
|
+
title: S("billing.addons.serverUnavailable", "Server Unavailable"),
|
|
56
|
+
message: S("billing.addons.serverUnavailableMessage", "Unable to load addon details. The server might be down or experiencing issues."),
|
|
57
|
+
onRetry: () => A(),
|
|
57
58
|
showRetry: !0
|
|
58
59
|
})
|
|
59
|
-
}) :
|
|
60
|
+
}) : D ? /* @__PURE__ */ _("div", {
|
|
60
61
|
className: "space-y-6 p-6",
|
|
61
|
-
children: [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ g(t, {
|
|
64
|
+
title: D.name,
|
|
65
|
+
description: `${a(D.duration)} addon`
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ g(b, { children: S("billing.addons.detailPurpose", "A full breakdown of this add-on — its price and the extra quotas it grants on top of a subscription. Review it here to decide whether to add it, or (as an admin) to edit what it includes.") }),
|
|
68
|
+
/* @__PURE__ */ _("div", {
|
|
69
|
+
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
68
70
|
children: [/* @__PURE__ */ _("div", {
|
|
69
|
-
className: "
|
|
70
|
-
children: [/* @__PURE__ */
|
|
71
|
-
className: "
|
|
72
|
-
children: /* @__PURE__ */ g("h2", {
|
|
73
|
-
className: "font-semibold text-text-primary",
|
|
74
|
-
children: b("billing.addons.pricing", "Pricing")
|
|
75
|
-
})
|
|
76
|
-
}), /* @__PURE__ */ _("div", {
|
|
77
|
-
className: "p-6",
|
|
78
|
-
children: [/* @__PURE__ */ _("div", {
|
|
79
|
-
className: "flex items-baseline gap-2",
|
|
80
|
-
children: [/* @__PURE__ */ g("span", {
|
|
81
|
-
className: "text-4xl font-bold text-text-primary",
|
|
82
|
-
children: (() => {
|
|
83
|
-
let { price: e, currency: t } = f(T.price, T.currency, T.currencyPrices, w);
|
|
84
|
-
return r(e, t);
|
|
85
|
-
})()
|
|
86
|
-
}), /* @__PURE__ */ _("span", {
|
|
87
|
-
className: "text-lg text-text-muted",
|
|
88
|
-
children: ["/ ", i(T.duration)]
|
|
89
|
-
})]
|
|
90
|
-
}), /* @__PURE__ */ _("p", {
|
|
91
|
-
className: "text-sm text-text-muted mt-2",
|
|
92
|
-
children: [
|
|
93
|
-
b("billing.addons.billed", "Billed"),
|
|
94
|
-
" ",
|
|
95
|
-
T.duration === "monthly" ? b("billing.addons.everyMonth", "every month") : b("billing.addons.annually", "annually")
|
|
96
|
-
]
|
|
97
|
-
})]
|
|
98
|
-
})]
|
|
99
|
-
}), /* @__PURE__ */ _("div", {
|
|
100
|
-
className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
101
|
-
children: [/* @__PURE__ */ g("div", {
|
|
102
|
-
className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
|
|
103
|
-
children: /* @__PURE__ */ _("h2", {
|
|
104
|
-
className: "font-semibold text-text-primary",
|
|
105
|
-
children: [
|
|
106
|
-
b("billing.addons.includedQuotas", "Included Quotas"),
|
|
107
|
-
" (",
|
|
108
|
-
j.length,
|
|
109
|
-
")"
|
|
110
|
-
]
|
|
111
|
-
})
|
|
112
|
-
}), /* @__PURE__ */ g("div", {
|
|
113
|
-
className: "p-4",
|
|
114
|
-
children: j.length === 0 ? /* @__PURE__ */ g("p", {
|
|
115
|
-
className: "text-sm text-text-muted py-4 text-center",
|
|
116
|
-
children: b("billing.addons.noQuotas", "No quotas associated with this addon.")
|
|
117
|
-
}) : /* @__PURE__ */ g("div", {
|
|
118
|
-
className: "space-y-3",
|
|
119
|
-
children: j.map((e) => /* @__PURE__ */ _("div", {
|
|
120
|
-
className: "flex items-center justify-between p-3 border border-border-subtle rounded-lg bg-bg-sunken/20",
|
|
121
|
-
children: [/* @__PURE__ */ _("div", {
|
|
122
|
-
className: "flex items-center gap-3",
|
|
123
|
-
children: [/* @__PURE__ */ g("div", {
|
|
124
|
-
className: "size-8 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center",
|
|
125
|
-
children: /* @__PURE__ */ g("svg", {
|
|
126
|
-
className: "size-4 text-text-link",
|
|
127
|
-
fill: "none",
|
|
128
|
-
viewBox: "0 0 24 24",
|
|
129
|
-
stroke: "currentColor",
|
|
130
|
-
children: /* @__PURE__ */ g("path", {
|
|
131
|
-
strokeLinecap: "round",
|
|
132
|
-
strokeLinejoin: "round",
|
|
133
|
-
strokeWidth: 2,
|
|
134
|
-
d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
138
|
-
className: "font-medium text-text-primary",
|
|
139
|
-
children: e.name
|
|
140
|
-
}), /* @__PURE__ */ _("p", {
|
|
141
|
-
className: "text-xs text-text-muted capitalize",
|
|
142
|
-
children: ["Type: ", e.type]
|
|
143
|
-
})] })]
|
|
144
|
-
}), /* @__PURE__ */ _("div", {
|
|
145
|
-
className: "text-right",
|
|
146
|
-
children: [/* @__PURE__ */ g("span", {
|
|
147
|
-
className: "text-lg font-semibold text-status-success-text",
|
|
148
|
-
children: e.totalValue.toLocaleString()
|
|
149
|
-
}), /* @__PURE__ */ g("p", {
|
|
150
|
-
className: "text-xs text-text-muted",
|
|
151
|
-
children: "total"
|
|
152
|
-
})]
|
|
153
|
-
})]
|
|
154
|
-
}, e.quotaId))
|
|
155
|
-
})
|
|
156
|
-
})]
|
|
157
|
-
})]
|
|
158
|
-
}), /* @__PURE__ */ _("div", {
|
|
159
|
-
className: "space-y-6",
|
|
160
|
-
children: [
|
|
161
|
-
/* @__PURE__ */ _("div", {
|
|
162
|
-
className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
71
|
+
className: "lg:col-span-2 space-y-6",
|
|
72
|
+
children: [/* @__PURE__ */ _(y, {
|
|
73
|
+
className: "overflow-hidden p-0",
|
|
163
74
|
children: [/* @__PURE__ */ g("div", {
|
|
164
|
-
className: "p-4 border-b border-border-subtle
|
|
75
|
+
className: "p-4 border-b border-border-subtle",
|
|
165
76
|
children: /* @__PURE__ */ g("h2", {
|
|
166
77
|
className: "font-semibold text-text-primary",
|
|
167
|
-
children:
|
|
78
|
+
children: S("billing.addons.pricing", "Pricing")
|
|
168
79
|
})
|
|
169
80
|
}), /* @__PURE__ */ _("div", {
|
|
170
|
-
className: "p-
|
|
171
|
-
children: [
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}),
|
|
192
|
-
/* @__PURE__ */ _("div", {
|
|
193
|
-
className: "flex items-center justify-between",
|
|
194
|
-
children: [/* @__PURE__ */ g("span", {
|
|
195
|
-
className: "text-sm text-text-muted",
|
|
196
|
-
children: b("billing.addons.billingLabel", "Billing")
|
|
197
|
-
}), /* @__PURE__ */ g("span", {
|
|
198
|
-
className: "text-sm font-medium text-text-primary capitalize",
|
|
199
|
-
children: T.duration
|
|
200
|
-
})]
|
|
201
|
-
})
|
|
202
|
-
]
|
|
81
|
+
className: "p-6",
|
|
82
|
+
children: [/* @__PURE__ */ _("div", {
|
|
83
|
+
className: "flex items-baseline gap-2",
|
|
84
|
+
children: [/* @__PURE__ */ g("span", {
|
|
85
|
+
className: "text-4xl font-bold text-text-primary",
|
|
86
|
+
children: (() => {
|
|
87
|
+
let { price: e, currency: t } = f(D.price, D.currency, D.currencyPrices, E);
|
|
88
|
+
return r(e, t);
|
|
89
|
+
})()
|
|
90
|
+
}), /* @__PURE__ */ _("span", {
|
|
91
|
+
className: "text-lg text-text-muted",
|
|
92
|
+
children: ["/ ", i(D.duration)]
|
|
93
|
+
})]
|
|
94
|
+
}), /* @__PURE__ */ _("p", {
|
|
95
|
+
className: "text-sm text-text-muted mt-2",
|
|
96
|
+
children: [
|
|
97
|
+
S("billing.addons.billed", "Billed"),
|
|
98
|
+
" ",
|
|
99
|
+
D.duration === "monthly" ? S("billing.addons.everyMonth", "every month") : S("billing.addons.annually", "annually")
|
|
100
|
+
]
|
|
101
|
+
})]
|
|
203
102
|
})]
|
|
204
|
-
}),
|
|
205
|
-
C.canManageAddons && /* @__PURE__ */ _("div", {
|
|
103
|
+
}), /* @__PURE__ */ _("div", {
|
|
206
104
|
className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
207
105
|
children: [/* @__PURE__ */ g("div", {
|
|
208
106
|
className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
|
|
209
|
-
children: /* @__PURE__ */
|
|
107
|
+
children: /* @__PURE__ */ _("h2", {
|
|
210
108
|
className: "font-semibold text-text-primary",
|
|
211
|
-
children:
|
|
109
|
+
children: [
|
|
110
|
+
S("billing.addons.includedQuotas", "Included Quotas"),
|
|
111
|
+
" (",
|
|
112
|
+
N.length,
|
|
113
|
+
")"
|
|
114
|
+
]
|
|
115
|
+
})
|
|
116
|
+
}), /* @__PURE__ */ g("div", {
|
|
117
|
+
className: "p-4",
|
|
118
|
+
children: N.length === 0 ? /* @__PURE__ */ g("p", {
|
|
119
|
+
className: "text-sm text-text-muted py-4 text-center",
|
|
120
|
+
children: S("billing.addons.noQuotas", "No quotas associated with this addon.")
|
|
121
|
+
}) : /* @__PURE__ */ g("div", {
|
|
122
|
+
className: "space-y-3",
|
|
123
|
+
children: N.map((e) => /* @__PURE__ */ _("div", {
|
|
124
|
+
className: "flex items-center justify-between p-3 border border-border-subtle rounded-lg bg-bg-sunken/20",
|
|
125
|
+
children: [/* @__PURE__ */ _("div", {
|
|
126
|
+
className: "flex items-center gap-3",
|
|
127
|
+
children: [/* @__PURE__ */ g("div", {
|
|
128
|
+
className: "size-8 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center",
|
|
129
|
+
children: /* @__PURE__ */ g("svg", {
|
|
130
|
+
className: "size-4 text-text-link",
|
|
131
|
+
fill: "none",
|
|
132
|
+
viewBox: "0 0 24 24",
|
|
133
|
+
stroke: "currentColor",
|
|
134
|
+
children: /* @__PURE__ */ g("path", {
|
|
135
|
+
strokeLinecap: "round",
|
|
136
|
+
strokeLinejoin: "round",
|
|
137
|
+
strokeWidth: 2,
|
|
138
|
+
d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
142
|
+
className: "font-medium text-text-primary",
|
|
143
|
+
children: e.name
|
|
144
|
+
}), /* @__PURE__ */ _("p", {
|
|
145
|
+
className: "text-xs text-text-muted capitalize",
|
|
146
|
+
children: ["Type: ", e.type]
|
|
147
|
+
})] })]
|
|
148
|
+
}), /* @__PURE__ */ _("div", {
|
|
149
|
+
className: "text-right",
|
|
150
|
+
children: [/* @__PURE__ */ g("span", {
|
|
151
|
+
className: "text-lg font-semibold text-status-success-text",
|
|
152
|
+
children: e.totalValue.toLocaleString()
|
|
153
|
+
}), /* @__PURE__ */ g("p", {
|
|
154
|
+
className: "text-xs text-text-muted",
|
|
155
|
+
children: "total"
|
|
156
|
+
})]
|
|
157
|
+
})]
|
|
158
|
+
}, e.quotaId))
|
|
212
159
|
})
|
|
213
|
-
}), /* @__PURE__ */ _("div", {
|
|
214
|
-
className: "p-4 space-y-2",
|
|
215
|
-
children: [/* @__PURE__ */ g("button", {
|
|
216
|
-
type: "button",
|
|
217
|
-
onClick: () => S(`/addons/${T.id}/edit`),
|
|
218
|
-
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",
|
|
219
|
-
children: "Edit Addon"
|
|
220
|
-
}), /* @__PURE__ */ g("button", {
|
|
221
|
-
type: "button",
|
|
222
|
-
onClick: async () => {
|
|
223
|
-
try {
|
|
224
|
-
await k(T.id, !T.isActive), O();
|
|
225
|
-
} catch (e) {
|
|
226
|
-
console.error("Failed to toggle addon:", e);
|
|
227
|
-
}
|
|
228
|
-
},
|
|
229
|
-
disabled: A,
|
|
230
|
-
className: `w-full px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors disabled:opacity-50 ${T.isActive ? "text-status-error-text hover:bg-status-error-bg-subtle" : "text-status-success-text hover:bg-status-success-bg-subtle"}`,
|
|
231
|
-
children: A ? b("billing.addons.processing", "Processing...") : T.isActive ? b("billing.addons.deactivateAddon", "Deactivate Addon") : b("billing.addons.activateAddon", "Activate Addon")
|
|
232
|
-
})]
|
|
233
160
|
})]
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
161
|
+
})]
|
|
162
|
+
}), /* @__PURE__ */ _("div", {
|
|
163
|
+
className: "space-y-6",
|
|
164
|
+
children: [
|
|
165
|
+
/* @__PURE__ */ _("div", {
|
|
166
|
+
className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
167
|
+
children: [/* @__PURE__ */ g("div", {
|
|
168
|
+
className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
|
|
169
|
+
children: /* @__PURE__ */ g("h2", {
|
|
170
|
+
className: "font-semibold text-text-primary",
|
|
171
|
+
children: S("billing.addons.statusTitle", "Status")
|
|
172
|
+
})
|
|
173
|
+
}), /* @__PURE__ */ _("div", {
|
|
174
|
+
className: "p-4 space-y-4",
|
|
175
|
+
children: [
|
|
176
|
+
/* @__PURE__ */ _("div", {
|
|
177
|
+
className: "flex items-center justify-between",
|
|
178
|
+
children: [/* @__PURE__ */ g("span", {
|
|
179
|
+
className: "text-sm text-text-muted",
|
|
180
|
+
children: S("billing.addons.status", "Status")
|
|
181
|
+
}), /* @__PURE__ */ g("span", {
|
|
182
|
+
className: `px-2 py-1 text-xs font-medium rounded ${D.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
|
|
183
|
+
children: D.isActive ? "Active" : "Inactive"
|
|
184
|
+
})]
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ _("div", {
|
|
187
|
+
className: "flex items-center justify-between",
|
|
188
|
+
children: [/* @__PURE__ */ g("span", {
|
|
189
|
+
className: "text-sm text-text-muted",
|
|
190
|
+
children: S("billing.addons.currency", "Currency")
|
|
191
|
+
}), /* @__PURE__ */ g("span", {
|
|
192
|
+
className: "text-sm font-medium text-text-primary",
|
|
193
|
+
children: f(D.price, D.currency, D.currencyPrices, E).currency
|
|
194
|
+
})]
|
|
195
|
+
}),
|
|
196
|
+
/* @__PURE__ */ _("div", {
|
|
197
|
+
className: "flex items-center justify-between",
|
|
198
|
+
children: [/* @__PURE__ */ g("span", {
|
|
199
|
+
className: "text-sm text-text-muted",
|
|
200
|
+
children: S("billing.addons.billingLabel", "Billing")
|
|
201
|
+
}), /* @__PURE__ */ g("span", {
|
|
202
|
+
className: "text-sm font-medium text-text-primary capitalize",
|
|
203
|
+
children: D.duration
|
|
204
|
+
})]
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
})]
|
|
208
|
+
}),
|
|
209
|
+
T.canManageAddons && /* @__PURE__ */ _("div", {
|
|
210
|
+
className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
|
|
211
|
+
children: [/* @__PURE__ */ g("div", {
|
|
212
|
+
className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
|
|
213
|
+
children: /* @__PURE__ */ g("h2", {
|
|
214
|
+
className: "font-semibold text-text-primary",
|
|
215
|
+
children: S("billing.addons.actions", "Actions")
|
|
216
|
+
})
|
|
217
|
+
}), /* @__PURE__ */ _("div", {
|
|
218
|
+
className: "p-4 space-y-2",
|
|
219
|
+
children: [/* @__PURE__ */ g("button", {
|
|
220
|
+
type: "button",
|
|
221
|
+
onClick: () => w(`/addons/${D.id}/edit`),
|
|
222
|
+
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",
|
|
223
|
+
children: "Edit Addon"
|
|
224
|
+
}), /* @__PURE__ */ g("button", {
|
|
225
|
+
type: "button",
|
|
226
|
+
onClick: async () => {
|
|
227
|
+
try {
|
|
228
|
+
await j(D.id, !D.isActive), A();
|
|
229
|
+
} catch (e) {
|
|
230
|
+
console.error("Failed to toggle addon:", e);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
disabled: M,
|
|
234
|
+
className: `w-full px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors disabled:opacity-50 ${D.isActive ? "text-status-error-text hover:bg-status-error-bg-subtle" : "text-status-success-text hover:bg-status-success-bg-subtle"}`,
|
|
235
|
+
children: M ? S("billing.addons.processing", "Processing...") : D.isActive ? S("billing.addons.deactivateAddon", "Deactivate Addon") : S("billing.addons.activateAddon", "Activate Addon")
|
|
236
|
+
})]
|
|
237
|
+
})]
|
|
238
|
+
}),
|
|
239
|
+
/* @__PURE__ */ g("button", {
|
|
240
|
+
type: "button",
|
|
241
|
+
onClick: () => w("/addons"),
|
|
242
|
+
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",
|
|
243
|
+
children: "Back to Addons"
|
|
244
|
+
})
|
|
245
|
+
]
|
|
246
|
+
})]
|
|
247
|
+
})
|
|
248
|
+
]
|
|
244
249
|
}) : /* @__PURE__ */ g("div", {
|
|
245
250
|
className: "flex items-center justify-center h-full min-h-[400px]",
|
|
246
251
|
children: /* @__PURE__ */ _("div", {
|
|
@@ -263,23 +268,23 @@ var b = () => {
|
|
|
263
268
|
}),
|
|
264
269
|
/* @__PURE__ */ g("h2", {
|
|
265
270
|
className: "text-lg font-semibold text-text-primary",
|
|
266
|
-
children:
|
|
271
|
+
children: S("billing.addons.addonNotFound", "Addon not found")
|
|
267
272
|
}),
|
|
268
273
|
/* @__PURE__ */ g("p", {
|
|
269
274
|
className: "text-sm text-text-muted",
|
|
270
|
-
children:
|
|
275
|
+
children: S("billing.addons.addonNotFoundMessage", "The addon you're looking for doesn't exist or has been removed.")
|
|
271
276
|
}),
|
|
272
277
|
/* @__PURE__ */ g("button", {
|
|
273
278
|
type: "button",
|
|
274
|
-
onClick: () =>
|
|
279
|
+
onClick: () => w("/addons"),
|
|
275
280
|
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",
|
|
276
281
|
children: "Back to Addons"
|
|
277
282
|
})
|
|
278
283
|
]
|
|
279
284
|
})
|
|
280
|
-
}) : /* @__PURE__ */ g(e, { message:
|
|
285
|
+
}) : /* @__PURE__ */ g(e, { message: S("billing.addons.noViewPermission", "You don't have permission to view addon details.") });
|
|
281
286
|
};
|
|
282
287
|
//#endregion
|
|
283
|
-
export {
|
|
288
|
+
export { S as AddonDetailPage };
|
|
284
289
|
|
|
285
290
|
//# sourceMappingURL=AddonDetailPage.js.map
|