@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":"CreateCouponPage.js","names":[],"sources":["../../../../../src/billing/modules/coupons/pages/CreateCouponPage.tsx"],"sourcesContent":["/**\n * Create Coupon Page\n *\n * Form for creating new coupon codes with validation\n */\n\nimport { type FC, useState, useRef, type KeyboardEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { ArrowLeft, Save, Tag, Percent, Package, X, Users } from 'lucide-react';\n\nconst CURRENCIES: { code: string; symbol: string; name: string }[] = [\n { code: 'USD', symbol: '$', name: 'US Dollar' },\n { code: 'EUR', symbol: '€', name: 'Euro' },\n { code: 'GBP', symbol: '£', name: 'British Pound' },\n { code: 'INR', symbol: '₹', name: 'Indian Rupee' },\n { code: 'AUD', symbol: 'A$', name: 'Australian Dollar' },\n { code: 'CAD', symbol: 'C$', name: 'Canadian Dollar' },\n { code: 'SGD', symbol: 'S$', name: 'Singapore Dollar' },\n { code: 'JPY', symbol: '¥', name: 'Japanese Yen' },\n { code: 'MXN', symbol: 'MX$', name: 'Mexican Peso' },\n { code: 'BRL', symbol: 'R$', name: 'Brazilian Real' },\n { code: 'CHF', symbol: 'Fr', name: 'Swiss Franc' },\n { code: 'HKD', symbol: 'HK$', name: 'Hong Kong Dollar' },\n { code: 'NZD', symbol: 'NZ$', name: 'New Zealand Dollar' },\n { code: 'SEK', symbol: 'kr', name: 'Swedish Krona' },\n { code: 'KRW', symbol: '₩', name: 'South Korean Won' },\n];\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { useCreateCouponMutation } from '../../../../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CouponFormData {\n code: string;\n name: string;\n description: string;\n scope: 'GENERIC' | 'USER_SPECIFIC' | 'REFERRAL' | 'CAMPAIGN';\n type: 'PERCENTAGE' | 'FIXED_AMOUNT' | 'FREE_SHIPPING';\n value: number;\n currency: string;\n minPurchaseAmount: number | null;\n maxDiscountAmount: number | null;\n maxTotalUses: number | null;\n maxUsesPerUser: number;\n validFrom: string;\n validUntil: string;\n restrictedToUserIds: string[];\n applicableProducts: string[];\n applicableServices: string[];\n campaignName: string;\n}\n\nexport const CreateCouponPage: 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\n const [userIdInput, setUserIdInput] = useState('');\n const userIdInputRef = useRef<HTMLInputElement>(null);\n\n const [formData, setFormData] = useState<CouponFormData>({\n code: '',\n name: '',\n description: '',\n scope: 'GENERIC',\n type: 'PERCENTAGE',\n value: 0,\n currency: 'USD',\n minPurchaseAmount: null,\n maxDiscountAmount: null,\n maxTotalUses: null,\n maxUsesPerUser: 1,\n validFrom: new Date().toISOString().split('T')[0],\n validUntil: '',\n restrictedToUserIds: [],\n applicableProducts: [],\n applicableServices: [],\n campaignName: '',\n });\n\n const [errors, setErrors] = useState<Partial<Record<keyof CouponFormData, string>>>({});\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const currencySymbol =\n CURRENCIES.find((c) => c.code === formData.currency)?.symbol ?? formData.currency;\n\n // GraphQL mutation\n const [createCouponMutation] = useCreateCouponMutation({ refetchQueries: ['ListCoupons'] });\n\n // Check permissions\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to create coupons.\" />;\n }\n\n // Handle input change\n const handleChange = (\n field: keyof CouponFormData,\n value: CouponFormData[keyof CouponFormData]\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n\n // Clear error for this field\n if (errors[field]) {\n setErrors((prev) => {\n const newErrors = { ...prev };\n delete newErrors[field];\n return newErrors;\n });\n }\n };\n\n // Validate form\n const validateForm = (): boolean => {\n const newErrors: Partial<Record<keyof CouponFormData, string>> = {};\n\n // Code validation\n if (!formData.code.trim()) {\n newErrors.code = 'Coupon code is required';\n } else if (!/^[A-Z0-9-_]+$/.test(formData.code)) {\n newErrors.code =\n 'Code must contain only uppercase letters, numbers, hyphens, and underscores';\n } else if (formData.code.length < 3) {\n newErrors.code = 'Code must be at least 3 characters';\n } else if (formData.code.length > 50) {\n newErrors.code = 'Code must be at most 50 characters';\n }\n\n // Name validation\n if (!formData.name.trim()) {\n newErrors.name = 'Coupon name is required';\n }\n\n // Value validation\n if (formData.value <= 0) {\n newErrors.value = 'Value must be greater than 0';\n }\n if (formData.type === 'PERCENTAGE' && formData.value > 100) {\n newErrors.value = 'Percentage cannot exceed 100%';\n }\n\n // User-specific validation\n if (formData.scope === 'USER_SPECIFIC' && formData.restrictedToUserIds.length === 0) {\n newErrors.restrictedToUserIds = 'Add at least one user email or ID';\n }\n\n // Date validation\n if (!formData.validFrom) {\n newErrors.validFrom = 'Start date is required';\n }\n if (formData.validUntil && formData.validUntil < formData.validFrom) {\n newErrors.validUntil = 'End date must be after start date';\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const addUserId = (value: string) => {\n const trimmed = value.trim();\n if (!trimmed) return;\n if (!formData.restrictedToUserIds.includes(trimmed)) {\n handleChange('restrictedToUserIds', [...formData.restrictedToUserIds, trimmed]);\n }\n setUserIdInput('');\n };\n\n const removeUserId = (id: string) => {\n handleChange(\n 'restrictedToUserIds',\n formData.restrictedToUserIds.filter((u) => u !== id)\n );\n };\n\n const handleUserIdKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' || e.key === ',') {\n e.preventDefault();\n addUserId(userIdInput);\n } else if (e.key === 'Backspace' && !userIdInput && formData.restrictedToUserIds.length > 0) {\n removeUserId(formData.restrictedToUserIds[formData.restrictedToUserIds.length - 1]);\n }\n };\n\n // Handle submit\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) {\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n // Prepare input data - convert dates to ISO format\n const input = {\n code: formData.code,\n name: formData.name,\n description: formData.description,\n scope: formData.scope,\n type: formData.type,\n value: formData.value,\n currency: formData.currency,\n minPurchaseAmount: formData.minPurchaseAmount,\n maxDiscountAmount: formData.maxDiscountAmount,\n maxTotalUses: formData.maxTotalUses,\n maxUsesPerUser: formData.maxUsesPerUser,\n validFrom: new Date(formData.validFrom).toISOString(),\n validUntil: formData.validUntil ? new Date(formData.validUntil).toISOString() : undefined,\n restrictedToUserIds: formData.restrictedToUserIds,\n applicableProducts: formData.applicableProducts,\n applicableServices: formData.applicableServices,\n excludedProducts: [],\n excludedServices: [],\n campaignName: formData.campaignName || undefined,\n };\n\n // Call GraphQL mutation\n const result = await createCouponMutation({\n variables: { input },\n });\n\n if (result.data?.createCoupon) {\n // Success - navigate to coupons list\n navigateTo('/coupons');\n } else {\n throw new Error('Failed to create coupon');\n }\n } catch (error) {\n console.error('Error creating coupon:', error);\n const errorMessage =\n error instanceof Error ? error.message : 'Failed to create coupon. Please try again.';\n setErrors({ code: errorMessage });\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <div className=\"max-w-4xl mx-auto p-4 sm:p-6 space-y-6\">\n {/* Header */}\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <PageHeader\n title={tr('billing.coupons.createTitle', 'Create Coupon')}\n description=\"Create a new discount code or promotional offer\"\n icon={<Tag className=\"size-6\" />}\n />\n </div>\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Basic Information */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">Basic Information</h3>\n\n {/* Code */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Coupon Code *</label>\n <input\n type=\"text\"\n value={formData.code}\n onChange={(e) => handleChange('code', e.target.value.toUpperCase())}\n placeholder=\"e.g., WELCOME20\"\n className={`w-full px-4 py-2 rounded-lg border ${\n errors.code ? 'border-destructive' : 'border-border'\n } bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20`}\n />\n {errors.code && <p className=\"text-sm text-destructive mt-1\">{errors.code}</p>}\n <p className=\"text-xs text-muted-foreground mt-1\">\n Use uppercase letters, numbers, hyphens, and underscores\n </p>\n </div>\n\n {/* Name */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Display Name *</label>\n <input\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleChange('name', e.target.value)}\n placeholder=\"e.g., Welcome 20% Off\"\n className={`w-full px-4 py-2 rounded-lg border ${\n errors.name ? 'border-destructive' : 'border-border'\n } bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20`}\n />\n {errors.name && <p className=\"text-sm text-destructive mt-1\">{errors.name}</p>}\n </div>\n\n {/* Description */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Description</label>\n <textarea\n value={formData.description}\n onChange={(e) => handleChange('description', e.target.value)}\n placeholder=\"Optional description for internal use\"\n rows={3}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n </div>\n </div>\n\n {/* Discount Configuration */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">Discount Configuration</h3>\n\n {/* Currency */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Coupon Currency *\n </label>\n <select\n value={formData.currency}\n onChange={(e) => handleChange('currency', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n >\n {CURRENCIES.map(({ code, symbol, name }) => (\n <option key={code} value={code}>\n {symbol} {code} — {name}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-muted-foreground mt-1\">\n Amounts below are in this currency. Users paying in a different currency (e.g. INR)\n will have the amounts automatically converted using live exchange rates at validation\n time.\n </p>\n </div>\n\n {/* Type */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-2\">\n Discount Type *\n </label>\n <div className=\"grid grid-cols-3 gap-3\">\n {[\n { value: 'PERCENTAGE', label: 'Percentage', icon: Percent },\n { value: 'FIXED_AMOUNT', label: `Fixed (${currencySymbol})`, icon: null },\n { value: 'FREE_SHIPPING', label: 'Free Shipping', icon: Package },\n ].map(({ value, label, icon: Icon }) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => handleChange('type', value)}\n className={`p-4 rounded-lg border-2 transition-colors ${\n formData.type === value\n ? 'border-primary bg-primary/10'\n : 'border-border hover:border-primary/50'\n }`}\n >\n {Icon ? (\n <Icon className=\"size-5 mx-auto mb-2\" />\n ) : (\n <span className=\"block text-lg font-bold mb-2\">{currencySymbol}</span>\n )}\n <p className=\"text-sm font-medium\">{label}</p>\n </button>\n ))}\n </div>\n </div>\n\n {/* Value */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Discount Value *\n </label>\n <div className=\"relative\">\n {formData.type === 'PERCENTAGE' ? (\n <>\n <Percent className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground\" />\n <input\n type=\"number\"\n value={formData.value}\n onChange={(e) => handleChange('value', parseFloat(e.target.value))}\n min=\"0\"\n max=\"100\"\n step=\"0.01\"\n className={`w-full pl-10 pr-4 py-2 rounded-lg border ${\n errors.value ? 'border-destructive' : 'border-border'\n } bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20`}\n />\n </>\n ) : (\n <>\n <span className=\"absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground font-medium\">\n {currencySymbol}\n </span>\n <input\n type=\"number\"\n value={formData.value}\n onChange={(e) => handleChange('value', parseFloat(e.target.value))}\n min=\"0\"\n step=\"0.01\"\n className={`w-full pl-10 pr-4 py-2 rounded-lg border ${\n errors.value ? 'border-destructive' : 'border-border'\n } bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20`}\n />\n </>\n )}\n </div>\n {errors.value && <p className=\"text-sm text-destructive mt-1\">{errors.value}</p>}\n </div>\n\n {/* Min Purchase Amount */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Minimum Purchase Amount ({formData.currency})\n </label>\n <div className=\"relative\">\n <span className=\"absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground font-medium\">\n {currencySymbol}\n </span>\n <input\n type=\"number\"\n value={formData.minPurchaseAmount || ''}\n onChange={(e) =>\n handleChange(\n 'minPurchaseAmount',\n e.target.value ? parseFloat(e.target.value) : null\n )\n }\n min=\"0\"\n step=\"0.01\"\n placeholder=\"No minimum\"\n className=\"w-full pl-10 pr-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n </div>\n <p className=\"text-xs text-muted-foreground mt-1\">\n Order must be at least this amount in {formData.currency} (auto-converted for other\n currencies)\n </p>\n </div>\n\n {/* Max Discount Amount */}\n {formData.type === 'PERCENTAGE' && (\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Maximum Discount Amount ({formData.currency})\n </label>\n <div className=\"relative\">\n <span className=\"absolute left-3 top-1/2 -translate-y-1/2 text-sm text-muted-foreground font-medium\">\n {currencySymbol}\n </span>\n <input\n type=\"number\"\n value={formData.maxDiscountAmount || ''}\n onChange={(e) =>\n handleChange(\n 'maxDiscountAmount',\n e.target.value ? parseFloat(e.target.value) : null\n )\n }\n min=\"0\"\n step=\"0.01\"\n placeholder=\"No maximum\"\n className=\"w-full pl-10 pr-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n </div>\n <p className=\"text-xs text-muted-foreground mt-1\">\n Cap the discount in {formData.currency} — auto-converted to the user's payment\n currency\n </p>\n </div>\n )}\n </div>\n\n {/* Usage Limits */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">Usage Limits</h3>\n\n {/* Max Total Uses */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Total Usage Limit\n </label>\n <input\n type=\"number\"\n value={formData.maxTotalUses || ''}\n onChange={(e) =>\n handleChange('maxTotalUses', e.target.value ? parseInt(e.target.value) : null)\n }\n min=\"1\"\n placeholder=\"Unlimited\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Maximum number of times this coupon can be used across all users\n </p>\n </div>\n\n {/* Max Uses Per User */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Per-User Limit *\n </label>\n <input\n type=\"number\"\n value={formData.maxUsesPerUser}\n onChange={(e) => handleChange('maxUsesPerUser', parseInt(e.target.value))}\n min=\"1\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Maximum times a single user can use this coupon\n </p>\n </div>\n </div>\n\n {/* Validity Period */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">Validity Period</h3>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {/* Valid From */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Start Date *</label>\n <input\n type=\"date\"\n value={formData.validFrom}\n onChange={(e) => handleChange('validFrom', e.target.value)}\n className={`w-full px-4 py-2 rounded-lg border ${\n errors.validFrom ? 'border-destructive' : 'border-border'\n } bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20`}\n />\n {errors.validFrom && (\n <p className=\"text-sm text-destructive mt-1\">{errors.validFrom}</p>\n )}\n </div>\n\n {/* Valid Until */}\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">End Date</label>\n <input\n type=\"date\"\n value={formData.validUntil}\n onChange={(e) => handleChange('validUntil', e.target.value)}\n className={`w-full px-4 py-2 rounded-lg border ${\n errors.validUntil ? 'border-destructive' : 'border-border'\n } bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20`}\n />\n {errors.validUntil && (\n <p className=\"text-sm text-destructive mt-1\">{errors.validUntil}</p>\n )}\n <p className=\"text-xs text-muted-foreground mt-1\">Leave empty for no expiry</p>\n </div>\n </div>\n </div>\n\n {/* Scope */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-foreground\">Coupon Scope</h3>\n\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-2\">\n Who can use this coupon? *\n </label>\n <select\n value={formData.scope}\n onChange={(e) => handleChange('scope', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n >\n <option value=\"GENERIC\">Anyone (Public)</option>\n <option value=\"USER_SPECIFIC\">Specific Users Only</option>\n <option value=\"REFERRAL\">Referral Code</option>\n <option value=\"CAMPAIGN\">Campaign-Specific</option>\n </select>\n </div>\n\n {/* Campaign Name (if scope is CAMPAIGN) */}\n {formData.scope === 'CAMPAIGN' && (\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Campaign Name\n </label>\n <input\n type=\"text\"\n value={formData.campaignName}\n onChange={(e) => handleChange('campaignName', e.target.value)}\n placeholder=\"e.g., Summer Sale 2024\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n </div>\n )}\n\n {/* Restricted Users (if scope is USER_SPECIFIC) */}\n {formData.scope === 'USER_SPECIFIC' && (\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n <span className=\"flex items-center gap-1.5\">\n <Users className=\"size-4\" />\n Restricted Users *\n </span>\n </label>\n {/* Tag input container */}\n <div\n className={`min-h-[42px] flex flex-wrap gap-2 px-3 py-2 rounded-lg border ${\n errors.restrictedToUserIds ? 'border-destructive' : 'border-border'\n } bg-background cursor-text`}\n onClick={() => userIdInputRef.current?.focus()}\n >\n {formData.restrictedToUserIds.map((id) => (\n <span\n key={id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-primary/10 text-primary text-sm font-medium\"\n >\n {id}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeUserId(id);\n }}\n className=\"hover:text-destructive\"\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n <input\n ref={userIdInputRef}\n type=\"text\"\n value={userIdInput}\n onChange={(e) => setUserIdInput(e.target.value)}\n onKeyDown={handleUserIdKeyDown}\n onBlur={() => addUserId(userIdInput)}\n placeholder={\n formData.restrictedToUserIds.length === 0\n ? 'Enter user email or ID, press Enter to add'\n : 'Add another…'\n }\n className=\"flex-1 min-w-[200px] bg-transparent text-foreground placeholder:text-muted-foreground text-sm focus:outline-none\"\n />\n </div>\n {errors.restrictedToUserIds && (\n <p className=\"text-sm text-destructive mt-1\">{errors.restrictedToUserIds}</p>\n )}\n <p className=\"text-xs text-muted-foreground mt-1\">\n Type an email or user ID and press Enter or comma to add. Only these users can\n redeem this coupon.\n </p>\n </div>\n )}\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-6 py-3 rounded-lg border border-border text-foreground hover:bg-muted\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting}\n className=\"px-6 py-3 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isSubmitting ? (\n <>\n <div className=\"size-4 border-2 border-primary-foreground border-t-transparent rounded-full animate-spin\" />\n Creating…\n </>\n ) : (\n <>\n <Save className=\"size-4\" />\n Create Coupon\n </>\n )}\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateCouponPage.js","names":[],"sources":["../../../../../src/billing/modules/coupons/pages/CreateCouponPage.tsx"],"sourcesContent":["/**\n * Create Coupon Page\n *\n * Form for creating new coupon codes with validation\n */\n\nimport { type FC, useState, useRef, type KeyboardEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { ArrowLeft, Save, Tag, Percent, Package, X, Users } from 'lucide-react';\n\nconst CURRENCIES: { code: string; symbol: string; name: string }[] = [\n { code: 'USD', symbol: '$', name: 'US Dollar' },\n { code: 'EUR', symbol: '€', name: 'Euro' },\n { code: 'GBP', symbol: '£', name: 'British Pound' },\n { code: 'INR', symbol: '₹', name: 'Indian Rupee' },\n { code: 'AUD', symbol: 'A$', name: 'Australian Dollar' },\n { code: 'CAD', symbol: 'C$', name: 'Canadian Dollar' },\n { code: 'SGD', symbol: 'S$', name: 'Singapore Dollar' },\n { code: 'JPY', symbol: '¥', name: 'Japanese Yen' },\n { code: 'MXN', symbol: 'MX$', name: 'Mexican Peso' },\n { code: 'BRL', symbol: 'R$', name: 'Brazilian Real' },\n { code: 'CHF', symbol: 'Fr', name: 'Swiss Franc' },\n { code: 'HKD', symbol: 'HK$', name: 'Hong Kong Dollar' },\n { code: 'NZD', symbol: 'NZ$', name: 'New Zealand Dollar' },\n { code: 'SEK', symbol: 'kr', name: 'Swedish Krona' },\n { code: 'KRW', symbol: '₩', name: 'South Korean Won' },\n];\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { useCreateCouponMutation } from '../../../../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CouponFormData {\n code: string;\n name: string;\n description: string;\n scope: 'GENERIC' | 'USER_SPECIFIC' | 'REFERRAL' | 'CAMPAIGN';\n type: 'PERCENTAGE' | 'FIXED_AMOUNT' | 'FREE_SHIPPING';\n value: number;\n currency: string;\n minPurchaseAmount: number | null;\n maxDiscountAmount: number | null;\n maxTotalUses: number | null;\n maxUsesPerUser: number;\n validFrom: string;\n validUntil: string;\n restrictedToUserIds: string[];\n applicableProducts: string[];\n applicableServices: string[];\n campaignName: string;\n}\n\nexport const CreateCouponPage: 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\n const [userIdInput, setUserIdInput] = useState('');\n const userIdInputRef = useRef<HTMLInputElement>(null);\n\n const [formData, setFormData] = useState<CouponFormData>({\n code: '',\n name: '',\n description: '',\n scope: 'GENERIC',\n type: 'PERCENTAGE',\n value: 0,\n currency: 'USD',\n minPurchaseAmount: null,\n maxDiscountAmount: null,\n maxTotalUses: null,\n maxUsesPerUser: 1,\n validFrom: new Date().toISOString().split('T')[0],\n validUntil: '',\n restrictedToUserIds: [],\n applicableProducts: [],\n applicableServices: [],\n campaignName: '',\n });\n\n const [errors, setErrors] = useState<Partial<Record<keyof CouponFormData, string>>>({});\n const [isSubmitting, setIsSubmitting] = useState(false);\n\n const currencySymbol =\n CURRENCIES.find((c) => c.code === formData.currency)?.symbol ?? formData.currency;\n\n // GraphQL mutation\n const [createCouponMutation] = useCreateCouponMutation({ refetchQueries: ['ListCoupons'] });\n\n // Check permissions\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to create coupons.\" />;\n }\n\n // Handle input change\n const handleChange = (\n field: keyof CouponFormData,\n value: CouponFormData[keyof CouponFormData]\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n\n // Clear error for this field\n if (errors[field]) {\n setErrors((prev) => {\n const newErrors = { ...prev };\n delete newErrors[field];\n return newErrors;\n });\n }\n };\n\n // Validate form\n const validateForm = (): boolean => {\n const newErrors: Partial<Record<keyof CouponFormData, string>> = {};\n\n // Code validation\n if (!formData.code.trim()) {\n newErrors.code = 'Coupon code is required';\n } else if (!/^[A-Z0-9-_]+$/.test(formData.code)) {\n newErrors.code =\n 'Code must contain only uppercase letters, numbers, hyphens, and underscores';\n } else if (formData.code.length < 3) {\n newErrors.code = 'Code must be at least 3 characters';\n } else if (formData.code.length > 50) {\n newErrors.code = 'Code must be at most 50 characters';\n }\n\n // Name validation\n if (!formData.name.trim()) {\n newErrors.name = 'Coupon name is required';\n }\n\n // Value validation\n if (formData.value <= 0) {\n newErrors.value = 'Value must be greater than 0';\n }\n if (formData.type === 'PERCENTAGE' && formData.value > 100) {\n newErrors.value = 'Percentage cannot exceed 100%';\n }\n\n // User-specific validation\n if (formData.scope === 'USER_SPECIFIC' && formData.restrictedToUserIds.length === 0) {\n newErrors.restrictedToUserIds = 'Add at least one user email or ID';\n }\n\n // Date validation\n if (!formData.validFrom) {\n newErrors.validFrom = 'Start date is required';\n }\n if (formData.validUntil && formData.validUntil < formData.validFrom) {\n newErrors.validUntil = 'End date must be after start date';\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const addUserId = (value: string) => {\n const trimmed = value.trim();\n if (!trimmed) return;\n if (!formData.restrictedToUserIds.includes(trimmed)) {\n handleChange('restrictedToUserIds', [...formData.restrictedToUserIds, trimmed]);\n }\n setUserIdInput('');\n };\n\n const removeUserId = (id: string) => {\n handleChange(\n 'restrictedToUserIds',\n formData.restrictedToUserIds.filter((u) => u !== id)\n );\n };\n\n const handleUserIdKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' || e.key === ',') {\n e.preventDefault();\n addUserId(userIdInput);\n } else if (e.key === 'Backspace' && !userIdInput && formData.restrictedToUserIds.length > 0) {\n removeUserId(formData.restrictedToUserIds[formData.restrictedToUserIds.length - 1]);\n }\n };\n\n // Handle submit\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) {\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n // Prepare input data - convert dates to ISO format\n const input = {\n code: formData.code,\n name: formData.name,\n description: formData.description,\n scope: formData.scope,\n type: formData.type,\n value: formData.value,\n currency: formData.currency,\n minPurchaseAmount: formData.minPurchaseAmount,\n maxDiscountAmount: formData.maxDiscountAmount,\n maxTotalUses: formData.maxTotalUses,\n maxUsesPerUser: formData.maxUsesPerUser,\n validFrom: new Date(formData.validFrom).toISOString(),\n validUntil: formData.validUntil ? new Date(formData.validUntil).toISOString() : undefined,\n restrictedToUserIds: formData.restrictedToUserIds,\n applicableProducts: formData.applicableProducts,\n applicableServices: formData.applicableServices,\n excludedProducts: [],\n excludedServices: [],\n campaignName: formData.campaignName || undefined,\n };\n\n // Call GraphQL mutation\n const result = await createCouponMutation({\n variables: { input },\n });\n\n if (result.data?.createCoupon) {\n // Success - navigate to coupons list\n navigateTo('/coupons');\n } else {\n throw new Error('Failed to create coupon');\n }\n } catch (error) {\n console.error('Error creating coupon:', error);\n const errorMessage =\n error instanceof Error ? error.message : 'Failed to create coupon. Please try again.';\n setErrors({ code: errorMessage });\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <div className=\"max-w-4xl mx-auto p-4 sm:p-6 space-y-6\">\n {/* Header */}\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"p-2 rounded-lg hover:bg-bg-sunken text-text-muted hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <PageHeader\n title={tr('billing.coupons.createTitle', 'Create Coupon')}\n description=\"Create a new discount code or promotional offer\"\n icon={<Tag className=\"size-6\" />}\n />\n </div>\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Basic Information */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Basic Information</h3>\n\n {/* Code */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Coupon Code *\n </label>\n <input\n type=\"text\"\n value={formData.code}\n onChange={(e) => handleChange('code', e.target.value.toUpperCase())}\n placeholder=\"e.g., WELCOME20\"\n className={`w-full px-4 py-2 rounded-input border ${\n errors.code ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]`}\n />\n {errors.code && <p className=\"text-sm text-status-error-text mt-1\">{errors.code}</p>}\n <p className=\"text-xs text-text-muted mt-1\">\n Use uppercase letters, numbers, hyphens, and underscores\n </p>\n </div>\n\n {/* Name */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Display Name *\n </label>\n <input\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleChange('name', e.target.value)}\n placeholder=\"e.g., Welcome 20% Off\"\n className={`w-full px-4 py-2 rounded-input border ${\n errors.name ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]`}\n />\n {errors.name && <p className=\"text-sm text-status-error-text mt-1\">{errors.name}</p>}\n </div>\n\n {/* Description */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Description</label>\n <textarea\n value={formData.description}\n onChange={(e) => handleChange('description', e.target.value)}\n placeholder=\"Optional description for internal use\"\n rows={3}\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n </div>\n\n {/* Discount Configuration */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Discount Configuration</h3>\n\n {/* Currency */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Coupon Currency *\n </label>\n <select\n value={formData.currency}\n onChange={(e) => handleChange('currency', e.target.value)}\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {CURRENCIES.map(({ code, symbol, name }) => (\n <option key={code} value={code}>\n {symbol} {code} — {name}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-text-muted mt-1\">\n Amounts below are in this currency. Users paying in a different currency (e.g. INR)\n will have the amounts automatically converted using live exchange rates at validation\n time.\n </p>\n </div>\n\n {/* Type */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Discount Type *\n </label>\n <div className=\"grid grid-cols-3 gap-3\">\n {[\n { value: 'PERCENTAGE', label: 'Percentage', icon: Percent },\n { value: 'FIXED_AMOUNT', label: `Fixed (${currencySymbol})`, icon: null },\n { value: 'FREE_SHIPPING', label: 'Free Shipping', icon: Package },\n ].map(({ value, label, icon: Icon }) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => handleChange('type', value)}\n className={`p-4 rounded-lg border-2 transition-colors ${\n formData.type === value\n ? 'border-action-primary-bg bg-[var(--color-accent-soft)]'\n : 'border-border-subtle hover:border-action-primary-bg/50'\n }`}\n >\n {Icon ? (\n <Icon className=\"size-5 mx-auto mb-2\" />\n ) : (\n <span className=\"block text-lg font-bold mb-2\">{currencySymbol}</span>\n )}\n <p className=\"text-sm font-medium\">{label}</p>\n </button>\n ))}\n </div>\n </div>\n\n {/* Value */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Discount Value *\n </label>\n <div className=\"relative\">\n {formData.type === 'PERCENTAGE' ? (\n <>\n <Percent className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-muted\" />\n <input\n type=\"number\"\n value={formData.value}\n onChange={(e) => handleChange('value', parseFloat(e.target.value))}\n min=\"0\"\n max=\"100\"\n step=\"0.01\"\n className={`w-full pl-10 pr-4 py-2 rounded-input border ${\n errors.value ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]`}\n />\n </>\n ) : (\n <>\n <span className=\"absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-muted font-medium\">\n {currencySymbol}\n </span>\n <input\n type=\"number\"\n value={formData.value}\n onChange={(e) => handleChange('value', parseFloat(e.target.value))}\n min=\"0\"\n step=\"0.01\"\n className={`w-full pl-10 pr-4 py-2 rounded-input border ${\n errors.value ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]`}\n />\n </>\n )}\n </div>\n {errors.value && <p className=\"text-sm text-status-error-text mt-1\">{errors.value}</p>}\n </div>\n\n {/* Min Purchase Amount */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Minimum Purchase Amount ({formData.currency})\n </label>\n <div className=\"relative\">\n <span className=\"absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-muted font-medium\">\n {currencySymbol}\n </span>\n <input\n type=\"number\"\n value={formData.minPurchaseAmount || ''}\n onChange={(e) =>\n handleChange(\n 'minPurchaseAmount',\n e.target.value ? parseFloat(e.target.value) : null\n )\n }\n min=\"0\"\n step=\"0.01\"\n placeholder=\"No minimum\"\n className=\"w-full pl-10 pr-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <p className=\"text-xs text-text-muted mt-1\">\n Order must be at least this amount in {formData.currency} (auto-converted for other\n currencies)\n </p>\n </div>\n\n {/* Max Discount Amount */}\n {formData.type === 'PERCENTAGE' && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Maximum Discount Amount ({formData.currency})\n </label>\n <div className=\"relative\">\n <span className=\"absolute left-3 top-1/2 -translate-y-1/2 text-sm text-text-muted font-medium\">\n {currencySymbol}\n </span>\n <input\n type=\"number\"\n value={formData.maxDiscountAmount || ''}\n onChange={(e) =>\n handleChange(\n 'maxDiscountAmount',\n e.target.value ? parseFloat(e.target.value) : null\n )\n }\n min=\"0\"\n step=\"0.01\"\n placeholder=\"No maximum\"\n className=\"w-full pl-10 pr-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <p className=\"text-xs text-text-muted mt-1\">\n Cap the discount in {formData.currency} — auto-converted to the user's payment\n currency\n </p>\n </div>\n )}\n </div>\n\n {/* Usage Limits */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Usage Limits</h3>\n\n {/* Max Total Uses */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Total Usage Limit\n </label>\n <input\n type=\"number\"\n value={formData.maxTotalUses || ''}\n onChange={(e) =>\n handleChange('maxTotalUses', e.target.value ? parseInt(e.target.value) : null)\n }\n min=\"1\"\n placeholder=\"Unlimited\"\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Maximum number of times this coupon can be used across all users\n </p>\n </div>\n\n {/* Max Uses Per User */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Per-User Limit *\n </label>\n <input\n type=\"number\"\n value={formData.maxUsesPerUser}\n onChange={(e) => handleChange('maxUsesPerUser', parseInt(e.target.value))}\n min=\"1\"\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Maximum times a single user can use this coupon\n </p>\n </div>\n </div>\n\n {/* Validity Period */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Validity Period</h3>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {/* Valid From */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Start Date *\n </label>\n <input\n type=\"date\"\n value={formData.validFrom}\n onChange={(e) => handleChange('validFrom', e.target.value)}\n className={`w-full px-4 py-2 rounded-input border ${\n errors.validFrom ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]`}\n />\n {errors.validFrom && (\n <p className=\"text-sm text-status-error-text mt-1\">{errors.validFrom}</p>\n )}\n </div>\n\n {/* Valid Until */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">End Date</label>\n <input\n type=\"date\"\n value={formData.validUntil}\n onChange={(e) => handleChange('validUntil', e.target.value)}\n className={`w-full px-4 py-2 rounded-input border ${\n errors.validUntil ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]`}\n />\n {errors.validUntil && (\n <p className=\"text-sm text-status-error-text mt-1\">{errors.validUntil}</p>\n )}\n <p className=\"text-xs text-text-muted mt-1\">Leave empty for no expiry</p>\n </div>\n </div>\n </div>\n\n {/* Scope */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-4\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Coupon Scope</h3>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Who can use this coupon? *\n </label>\n <select\n value={formData.scope}\n onChange={(e) => handleChange('scope', e.target.value)}\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"GENERIC\">Anyone (Public)</option>\n <option value=\"USER_SPECIFIC\">Specific Users Only</option>\n <option value=\"REFERRAL\">Referral Code</option>\n <option value=\"CAMPAIGN\">Campaign-Specific</option>\n </select>\n </div>\n\n {/* Campaign Name (if scope is CAMPAIGN) */}\n {formData.scope === 'CAMPAIGN' && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n Campaign Name\n </label>\n <input\n type=\"text\"\n value={formData.campaignName}\n onChange={(e) => handleChange('campaignName', e.target.value)}\n placeholder=\"e.g., Summer Sale 2024\"\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n )}\n\n {/* Restricted Users (if scope is USER_SPECIFIC) */}\n {formData.scope === 'USER_SPECIFIC' && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n <span className=\"flex items-center gap-1.5\">\n <Users className=\"size-4\" />\n Restricted Users *\n </span>\n </label>\n {/* Tag input container */}\n <div\n className={`min-h-[42px] flex flex-wrap gap-2 px-3 py-2 rounded-lg border ${\n errors.restrictedToUserIds ? 'border-destructive' : 'border-border-subtle'\n } bg-bg-surface cursor-text`}\n onClick={() => userIdInputRef.current?.focus()}\n >\n {formData.restrictedToUserIds.map((id) => (\n <span\n key={id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-md bg-[var(--color-accent-soft)] text-text-link text-sm font-medium\"\n >\n {id}\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n removeUserId(id);\n }}\n className=\"hover:text-status-error-text\"\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n <input\n ref={userIdInputRef}\n type=\"text\"\n value={userIdInput}\n onChange={(e) => setUserIdInput(e.target.value)}\n onKeyDown={handleUserIdKeyDown}\n onBlur={() => addUserId(userIdInput)}\n placeholder={\n formData.restrictedToUserIds.length === 0\n ? 'Enter user email or ID, press Enter to add'\n : 'Add another…'\n }\n className=\"flex-1 min-w-[200px] bg-transparent text-text-primary placeholder:text-text-muted text-sm focus:outline-none\"\n />\n </div>\n {errors.restrictedToUserIds && (\n <p className=\"text-sm text-status-error-text mt-1\">{errors.restrictedToUserIds}</p>\n )}\n <p className=\"text-xs text-text-muted mt-1\">\n Type an email or user ID and press Enter or comma to add. Only these users can\n redeem this coupon.\n </p>\n </div>\n )}\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-6 py-3 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting}\n className=\"px-6 py-3 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isSubmitting ? (\n <>\n <div className=\"size-4 border-2 border-action-primary-text border-t-transparent rounded-full animate-spin\" />\n Creating…\n </>\n ) : (\n <>\n <Save className=\"size-4\" />\n Create Coupon\n </>\n )}\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -75,13 +75,13 @@ var g = () => {
|
|
|
75
75
|
actions: /* @__PURE__ */ m("button", {
|
|
76
76
|
type: "button",
|
|
77
77
|
onClick: () => v("/coupons"),
|
|
78
|
-
className: "px-4 py-2 rounded-lg border border-border text-
|
|
78
|
+
className: "px-4 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2",
|
|
79
79
|
children: [/* @__PURE__ */ p(u, { className: "size-4" }), "Back"]
|
|
80
80
|
})
|
|
81
81
|
}), /* @__PURE__ */ p("div", {
|
|
82
82
|
className: "flex items-center justify-center h-64",
|
|
83
83
|
children: /* @__PURE__ */ p("div", {
|
|
84
|
-
className: "text-muted
|
|
84
|
+
className: "text-text-muted",
|
|
85
85
|
children: "Loading coupon…"
|
|
86
86
|
})
|
|
87
87
|
})]
|
|
@@ -94,13 +94,13 @@ var g = () => {
|
|
|
94
94
|
actions: /* @__PURE__ */ m("button", {
|
|
95
95
|
type: "button",
|
|
96
96
|
onClick: () => v("/coupons"),
|
|
97
|
-
className: "px-4 py-2 rounded-lg border border-border text-
|
|
97
|
+
className: "px-4 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2",
|
|
98
98
|
children: [/* @__PURE__ */ p(u, { className: "size-4" }), "Back"]
|
|
99
99
|
})
|
|
100
100
|
}), /* @__PURE__ */ p("div", {
|
|
101
101
|
className: "flex items-center justify-center h-64",
|
|
102
102
|
children: /* @__PURE__ */ p("div", {
|
|
103
|
-
className: "text-
|
|
103
|
+
className: "text-status-error-text",
|
|
104
104
|
children: w ? `Error: ${w.message}` : "Coupon not found"
|
|
105
105
|
})
|
|
106
106
|
})]
|
|
@@ -114,7 +114,7 @@ var g = () => {
|
|
|
114
114
|
actions: /* @__PURE__ */ m("button", {
|
|
115
115
|
type: "button",
|
|
116
116
|
onClick: () => v("/coupons"),
|
|
117
|
-
className: "px-4 py-2 rounded-lg border border-border text-
|
|
117
|
+
className: "px-4 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2",
|
|
118
118
|
children: [/* @__PURE__ */ p(u, { className: "size-4" }), "Back to Coupons"]
|
|
119
119
|
})
|
|
120
120
|
}), /* @__PURE__ */ p("div", {
|
|
@@ -124,63 +124,63 @@ var g = () => {
|
|
|
124
124
|
className: "space-y-6",
|
|
125
125
|
children: [
|
|
126
126
|
/* @__PURE__ */ m("div", {
|
|
127
|
-
className: "bg-
|
|
127
|
+
className: "bg-bg-sunken/30 border border-border-subtle rounded-lg p-6",
|
|
128
128
|
children: [/* @__PURE__ */ p("h3", {
|
|
129
|
-
className: "text-lg font-semibold text-
|
|
129
|
+
className: "text-lg font-semibold text-text-primary mb-4",
|
|
130
130
|
children: "Coupon Details (Read-Only)"
|
|
131
131
|
}), /* @__PURE__ */ m("div", {
|
|
132
132
|
className: "grid grid-cols-2 gap-4 text-sm",
|
|
133
133
|
children: [
|
|
134
134
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("span", {
|
|
135
|
-
className: "text-muted
|
|
135
|
+
className: "text-text-muted",
|
|
136
136
|
children: "Code:"
|
|
137
137
|
}), /* @__PURE__ */ p("span", {
|
|
138
|
-
className: "ml-2 font-mono font-medium text-
|
|
138
|
+
className: "ml-2 font-mono font-medium text-text-primary",
|
|
139
139
|
children: R.code
|
|
140
140
|
})] }),
|
|
141
141
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("span", {
|
|
142
|
-
className: "text-muted
|
|
142
|
+
className: "text-text-muted",
|
|
143
143
|
children: "Type:"
|
|
144
144
|
}), /* @__PURE__ */ p("span", {
|
|
145
|
-
className: "ml-2 text-
|
|
145
|
+
className: "ml-2 text-text-primary capitalize",
|
|
146
146
|
children: R.type.replace("_", " ").toLowerCase()
|
|
147
147
|
})] }),
|
|
148
148
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("span", {
|
|
149
|
-
className: "text-muted
|
|
149
|
+
className: "text-text-muted",
|
|
150
150
|
children: "Value:"
|
|
151
151
|
}), /* @__PURE__ */ p("span", {
|
|
152
|
-
className: "ml-2 text-
|
|
152
|
+
className: "ml-2 text-text-primary",
|
|
153
153
|
children: R.type === "PERCENTAGE" ? `${R.value}%` : `$${R.value}`
|
|
154
154
|
})] }),
|
|
155
155
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("span", {
|
|
156
|
-
className: "text-muted
|
|
156
|
+
className: "text-text-muted",
|
|
157
157
|
children: "Scope:"
|
|
158
158
|
}), /* @__PURE__ */ p("span", {
|
|
159
|
-
className: "ml-2 text-
|
|
159
|
+
className: "ml-2 text-text-primary",
|
|
160
160
|
children: R.scope
|
|
161
161
|
})] }),
|
|
162
162
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("span", {
|
|
163
|
-
className: "text-muted
|
|
163
|
+
className: "text-text-muted",
|
|
164
164
|
children: "Current Uses:"
|
|
165
165
|
}), /* @__PURE__ */ m("span", {
|
|
166
|
-
className: "ml-2 text-
|
|
166
|
+
className: "ml-2 text-text-primary",
|
|
167
167
|
children: [R.currentUses, R.maxTotalUses ? ` / ${R.maxTotalUses}` : ""]
|
|
168
168
|
})] }),
|
|
169
169
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("span", {
|
|
170
|
-
className: "text-muted
|
|
170
|
+
className: "text-text-muted",
|
|
171
171
|
children: "Created:"
|
|
172
172
|
}), /* @__PURE__ */ p("span", {
|
|
173
|
-
className: "ml-2 text-
|
|
173
|
+
className: "ml-2 text-text-primary",
|
|
174
174
|
children: new Date(R.createdAt).toLocaleDateString()
|
|
175
175
|
})] })
|
|
176
176
|
]
|
|
177
177
|
})]
|
|
178
178
|
}),
|
|
179
179
|
/* @__PURE__ */ m("div", {
|
|
180
|
-
className: "bg-
|
|
180
|
+
className: "bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-6",
|
|
181
181
|
children: [
|
|
182
182
|
/* @__PURE__ */ p("h3", {
|
|
183
|
-
className: "text-lg font-semibold text-
|
|
183
|
+
className: "text-lg font-semibold text-text-primary",
|
|
184
184
|
children: "Editable Fields"
|
|
185
185
|
}),
|
|
186
186
|
/* @__PURE__ */ m("div", {
|
|
@@ -188,9 +188,9 @@ var g = () => {
|
|
|
188
188
|
children: [
|
|
189
189
|
/* @__PURE__ */ m("label", {
|
|
190
190
|
htmlFor: "name",
|
|
191
|
-
className: "block text-sm font-medium text-
|
|
191
|
+
className: "block text-sm font-medium text-text-primary",
|
|
192
192
|
children: ["Name ", /* @__PURE__ */ p("span", {
|
|
193
|
-
className: "text-
|
|
193
|
+
className: "text-status-error-text",
|
|
194
194
|
children: "*"
|
|
195
195
|
})]
|
|
196
196
|
}),
|
|
@@ -200,10 +200,10 @@ var g = () => {
|
|
|
200
200
|
value: D.name,
|
|
201
201
|
onChange: (e) => F("name", e.target.value),
|
|
202
202
|
placeholder: "e.g., Welcome Discount",
|
|
203
|
-
className: "w-full px-4 py-2 rounded-
|
|
203
|
+
className: "w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
204
204
|
}),
|
|
205
205
|
k.name && /* @__PURE__ */ p("p", {
|
|
206
|
-
className: "text-sm text-
|
|
206
|
+
className: "text-sm text-status-error-text",
|
|
207
207
|
children: k.name
|
|
208
208
|
})
|
|
209
209
|
]
|
|
@@ -212,7 +212,7 @@ var g = () => {
|
|
|
212
212
|
className: "space-y-2",
|
|
213
213
|
children: [/* @__PURE__ */ p("label", {
|
|
214
214
|
htmlFor: "description",
|
|
215
|
-
className: "block text-sm font-medium text-
|
|
215
|
+
className: "block text-sm font-medium text-text-primary",
|
|
216
216
|
children: "Description"
|
|
217
217
|
}), /* @__PURE__ */ p("textarea", {
|
|
218
218
|
id: "description",
|
|
@@ -220,20 +220,20 @@ var g = () => {
|
|
|
220
220
|
onChange: (e) => F("description", e.target.value),
|
|
221
221
|
placeholder: "Optional description",
|
|
222
222
|
rows: 3,
|
|
223
|
-
className: "w-full px-4 py-2 rounded-
|
|
223
|
+
className: "w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none"
|
|
224
224
|
})]
|
|
225
225
|
}),
|
|
226
226
|
/* @__PURE__ */ m("div", {
|
|
227
227
|
className: "space-y-2",
|
|
228
228
|
children: [/* @__PURE__ */ p("label", {
|
|
229
229
|
htmlFor: "status",
|
|
230
|
-
className: "block text-sm font-medium text-
|
|
230
|
+
className: "block text-sm font-medium text-text-primary",
|
|
231
231
|
children: "Status"
|
|
232
232
|
}), /* @__PURE__ */ m("select", {
|
|
233
233
|
id: "status",
|
|
234
234
|
value: D.status,
|
|
235
235
|
onChange: (e) => F("status", e.target.value),
|
|
236
|
-
className: "w-full px-4 py-2 rounded-
|
|
236
|
+
className: "w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
237
237
|
children: [
|
|
238
238
|
/* @__PURE__ */ p("option", {
|
|
239
239
|
value: "ACTIVE",
|
|
@@ -259,7 +259,7 @@ var g = () => {
|
|
|
259
259
|
children: [
|
|
260
260
|
/* @__PURE__ */ p("label", {
|
|
261
261
|
htmlFor: "maxTotalUses",
|
|
262
|
-
className: "block text-sm font-medium text-
|
|
262
|
+
className: "block text-sm font-medium text-text-primary",
|
|
263
263
|
children: "Maximum Total Uses"
|
|
264
264
|
}),
|
|
265
265
|
/* @__PURE__ */ p("input", {
|
|
@@ -269,14 +269,14 @@ var g = () => {
|
|
|
269
269
|
value: D.maxTotalUses,
|
|
270
270
|
onChange: (e) => F("maxTotalUses", e.target.value),
|
|
271
271
|
placeholder: "Leave empty for unlimited",
|
|
272
|
-
className: "w-full px-4 py-2 rounded-
|
|
272
|
+
className: "w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
273
273
|
}),
|
|
274
274
|
k.maxTotalUses && /* @__PURE__ */ p("p", {
|
|
275
|
-
className: "text-sm text-
|
|
275
|
+
className: "text-sm text-status-error-text",
|
|
276
276
|
children: k.maxTotalUses
|
|
277
277
|
}),
|
|
278
278
|
/* @__PURE__ */ m("p", {
|
|
279
|
-
className: "text-sm text-muted
|
|
279
|
+
className: "text-sm text-text-muted",
|
|
280
280
|
children: [
|
|
281
281
|
"Current uses: ",
|
|
282
282
|
R.currentUses,
|
|
@@ -290,7 +290,7 @@ var g = () => {
|
|
|
290
290
|
children: [
|
|
291
291
|
/* @__PURE__ */ p("label", {
|
|
292
292
|
htmlFor: "maxUsesPerUser",
|
|
293
|
-
className: "block text-sm font-medium text-
|
|
293
|
+
className: "block text-sm font-medium text-text-primary",
|
|
294
294
|
children: "Maximum Uses Per User"
|
|
295
295
|
}),
|
|
296
296
|
/* @__PURE__ */ p("input", {
|
|
@@ -300,10 +300,10 @@ var g = () => {
|
|
|
300
300
|
value: D.maxUsesPerUser,
|
|
301
301
|
onChange: (e) => F("maxUsesPerUser", e.target.value),
|
|
302
302
|
placeholder: "e.g., 1",
|
|
303
|
-
className: "w-full px-4 py-2 rounded-
|
|
303
|
+
className: "w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
304
304
|
}),
|
|
305
305
|
k.maxUsesPerUser && /* @__PURE__ */ p("p", {
|
|
306
|
-
className: "text-sm text-
|
|
306
|
+
className: "text-sm text-status-error-text",
|
|
307
307
|
children: k.maxUsesPerUser
|
|
308
308
|
})
|
|
309
309
|
]
|
|
@@ -313,7 +313,7 @@ var g = () => {
|
|
|
313
313
|
children: [
|
|
314
314
|
/* @__PURE__ */ p("label", {
|
|
315
315
|
htmlFor: "validUntil",
|
|
316
|
-
className: "block text-sm font-medium text-
|
|
316
|
+
className: "block text-sm font-medium text-text-primary",
|
|
317
317
|
children: "Valid Until"
|
|
318
318
|
}),
|
|
319
319
|
/* @__PURE__ */ p("input", {
|
|
@@ -321,10 +321,10 @@ var g = () => {
|
|
|
321
321
|
type: "date",
|
|
322
322
|
value: D.validUntil,
|
|
323
323
|
onChange: (e) => F("validUntil", e.target.value),
|
|
324
|
-
className: "w-full px-4 py-2 rounded-
|
|
324
|
+
className: "w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
|
|
325
325
|
}),
|
|
326
326
|
/* @__PURE__ */ p("p", {
|
|
327
|
-
className: "text-sm text-muted
|
|
327
|
+
className: "text-sm text-text-muted",
|
|
328
328
|
children: "Leave empty for no expiration"
|
|
329
329
|
})
|
|
330
330
|
]
|
|
@@ -332,14 +332,14 @@ var g = () => {
|
|
|
332
332
|
]
|
|
333
333
|
}),
|
|
334
334
|
j && /* @__PURE__ */ m("div", {
|
|
335
|
-
className: "flex items-start gap-3 p-4 rounded-lg bg-
|
|
336
|
-
children: [/* @__PURE__ */ p(l, { className: "size-5 text-
|
|
335
|
+
className: "flex items-start gap-3 p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border",
|
|
336
|
+
children: [/* @__PURE__ */ p(l, { className: "size-5 text-status-error-text mt-0.5" }), /* @__PURE__ */ m("div", {
|
|
337
337
|
className: "flex-1",
|
|
338
338
|
children: [/* @__PURE__ */ p("p", {
|
|
339
|
-
className: "text-sm font-medium text-
|
|
339
|
+
className: "text-sm font-medium text-status-error-text",
|
|
340
340
|
children: "Error"
|
|
341
341
|
}), /* @__PURE__ */ p("p", {
|
|
342
|
-
className: "text-sm text-
|
|
342
|
+
className: "text-sm text-status-error-text/90 mt-1",
|
|
343
343
|
children: j
|
|
344
344
|
})]
|
|
345
345
|
})]
|
|
@@ -358,16 +358,16 @@ var g = () => {
|
|
|
358
358
|
})]
|
|
359
359
|
}),
|
|
360
360
|
/* @__PURE__ */ m("div", {
|
|
361
|
-
className: "flex gap-4 pt-4 border-t border-border",
|
|
361
|
+
className: "flex gap-4 pt-4 border-t border-border-subtle",
|
|
362
362
|
children: [/* @__PURE__ */ m("button", {
|
|
363
363
|
type: "submit",
|
|
364
364
|
disabled: E || N,
|
|
365
|
-
className: "px-6 py-2 rounded-lg bg-primary text-primary-
|
|
365
|
+
className: "px-6 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",
|
|
366
366
|
children: [/* @__PURE__ */ p(f, { className: "size-4" }), E ? "Updating..." : "Update Coupon"]
|
|
367
367
|
}), /* @__PURE__ */ p("button", {
|
|
368
368
|
type: "button",
|
|
369
369
|
onClick: () => v("/coupons"),
|
|
370
|
-
className: "px-6 py-2 rounded-lg border border-border text-
|
|
370
|
+
className: "px-6 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken",
|
|
371
371
|
children: "Cancel"
|
|
372
372
|
})]
|
|
373
373
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditCouponPage.js","names":[],"sources":["../../../../../src/billing/modules/coupons/pages/EditCouponPage.tsx"],"sourcesContent":["/**\n * Edit Coupon Page\n *\n * Form for editing existing coupon codes\n * Note: Only certain fields can be edited (name, description, status, usage limits, expiration)\n * Code, type, value, and scope cannot be changed after creation\n */\n\nimport { type FC, useState, useEffect } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { ArrowLeft, Save, AlertCircle, CheckCircle } from 'lucide-react';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport {\n useGetCouponLazyQuery,\n useUpdateCouponMutation,\n} from '../../../../generated/global-operations';\nimport { Coupon, CouponStatus } from '../../../../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface EditFormData {\n name: string;\n description: string;\n status: CouponStatus;\n maxTotalUses: string;\n maxUsesPerUser: string;\n validUntil: string;\n}\n\nexport const EditCouponPage: 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\n // Get coupon ID from URL\n const couponId = window.location.pathname.split('/').slice(-2)[0];\n\n const [getCoupon, { data, loading: loadingCoupon, error: loadError }] = useGetCouponLazyQuery();\n const [updateCoupon, { loading: updating }] = useUpdateCouponMutation();\n\n const [formData, setFormData] = useState<EditFormData>({\n name: '',\n description: '',\n status: CouponStatus.Active,\n maxTotalUses: '',\n maxUsesPerUser: '',\n validUntil: '',\n });\n\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [submitError, setSubmitError] = useState('');\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n // Load coupon data\n useEffect(() => {\n if (couponId) {\n getCoupon({ variables: { id: couponId } });\n }\n }, [couponId, getCoupon]);\n\n // Populate form when data loads\n useEffect(() => {\n if (data?.getCoupon) {\n const coupon = data.getCoupon as Coupon;\n setFormData({\n name: coupon.name,\n description: coupon.description || '',\n status: coupon.status,\n maxTotalUses: coupon.maxTotalUses?.toString() || '',\n maxUsesPerUser: coupon.maxUsesPerUser.toString(),\n validUntil: coupon.validUntil\n ? new Date(coupon.validUntil).toISOString().split('T')[0]\n : '',\n });\n }\n }, [data]);\n\n // Check permissions\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to manage coupons.\" />;\n }\n\n const handleInputChange = (field: keyof EditFormData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: '' }));\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.name.trim()) {\n newErrors.name = 'Name is required';\n }\n\n if (formData.maxTotalUses && parseInt(formData.maxTotalUses) < 1) {\n newErrors.maxTotalUses = 'Must be at least 1';\n }\n\n if (formData.maxUsesPerUser && parseInt(formData.maxUsesPerUser) < 1) {\n newErrors.maxUsesPerUser = 'Must be at least 1';\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError('');\n setSubmitSuccess(false);\n\n if (!validateForm()) {\n return;\n }\n\n if (!couponId) {\n setSubmitError('Coupon ID is missing');\n return;\n }\n\n try {\n const input: {\n name?: string;\n description?: string;\n status?: CouponStatus;\n maxTotalUses?: number;\n maxUsesPerUser?: number;\n validUntil?: string;\n } = {};\n\n if (formData.name.trim()) input.name = formData.name.trim();\n if (formData.description.trim()) input.description = formData.description.trim();\n if (formData.status) input.status = formData.status;\n if (formData.maxTotalUses) input.maxTotalUses = parseInt(formData.maxTotalUses);\n if (formData.maxUsesPerUser) input.maxUsesPerUser = parseInt(formData.maxUsesPerUser);\n if (formData.validUntil) input.validUntil = new Date(formData.validUntil).toISOString();\n\n await updateCoupon({\n variables: {\n id: couponId,\n input,\n },\n });\n\n setSubmitSuccess(true);\n setTimeout(() => {\n navigateTo('/coupons');\n }, 1500);\n } catch (error) {\n console.error('Failed to update coupon:', error);\n setSubmitError(error instanceof Error ? error.message : 'Failed to update coupon');\n }\n };\n\n if (loadingCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title={tr('billing.coupons.editTitle', 'Edit Coupon')}\n description=\"Loading coupon data...\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-muted-foreground\">Loading coupon…</div>\n </div>\n </div>\n );\n }\n\n if (loadError || !data?.getCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description=\"Coupon not found\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-destructive\">\n {loadError ? `Error: ${loadError.message}` : 'Coupon not found'}\n </div>\n </div>\n </div>\n );\n }\n\n const coupon = data.getCoupon as Coupon;\n\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description={`Editing: ${coupon.code}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to Coupons\n </button>\n }\n />\n\n <div className=\"max-w-3xl mx-auto mt-8\">\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Read-only Information Card */}\n <div className=\"bg-muted/30 border border-border rounded-lg p-6\">\n <h3 className=\"text-lg font-semibold text-foreground mb-4\">\n Coupon Details (Read-Only)\n </h3>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">Code:</span>\n <span className=\"ml-2 font-mono font-medium text-foreground\">{coupon.code}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Type:</span>\n <span className=\"ml-2 text-foreground capitalize\">\n {coupon.type.replace('_', ' ').toLowerCase()}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Value:</span>\n <span className=\"ml-2 text-foreground\">\n {coupon.type === 'PERCENTAGE' ? `${coupon.value}%` : `$${coupon.value}`}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Scope:</span>\n <span className=\"ml-2 text-foreground\">{coupon.scope}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Current Uses:</span>\n <span className=\"ml-2 text-foreground\">\n {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"ml-2 text-foreground\">\n {new Date(coupon.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Editable Fields Card */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-6\">\n <h3 className=\"text-lg font-semibold text-foreground\">Editable Fields</h3>\n\n {/* Name */}\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-foreground\">\n Name <span className=\"text-destructive\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleInputChange('name', e.target.value)}\n placeholder=\"e.g., Welcome Discount\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.name && <p className=\"text-sm text-destructive\">{errors.name}</p>}\n </div>\n\n {/* Description */}\n <div className=\"space-y-2\">\n <label htmlFor=\"description\" className=\"block text-sm font-medium text-foreground\">\n Description\n </label>\n <textarea\n id=\"description\"\n value={formData.description}\n onChange={(e) => handleInputChange('description', e.target.value)}\n placeholder=\"Optional description\"\n rows={3}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 resize-none\"\n />\n </div>\n\n {/* Status */}\n <div className=\"space-y-2\">\n <label htmlFor=\"status\" className=\"block text-sm font-medium text-foreground\">\n Status\n </label>\n <select\n id=\"status\"\n value={formData.status}\n onChange={(e) => handleInputChange('status', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n >\n <option value=\"ACTIVE\">Active</option>\n <option value=\"INACTIVE\">Inactive</option>\n <option value=\"EXPIRED\">Expired</option>\n <option value=\"DEPLETED\">Depleted</option>\n </select>\n </div>\n\n {/* Max Total Uses */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxTotalUses\" className=\"block text-sm font-medium text-foreground\">\n Maximum Total Uses\n </label>\n <input\n id=\"maxTotalUses\"\n type=\"number\"\n min=\"1\"\n value={formData.maxTotalUses}\n onChange={(e) => handleInputChange('maxTotalUses', e.target.value)}\n placeholder=\"Leave empty for unlimited\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.maxTotalUses && (\n <p className=\"text-sm text-destructive\">{errors.maxTotalUses}</p>\n )}\n <p className=\"text-sm text-muted-foreground\">\n Current uses: {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </p>\n </div>\n\n {/* Max Uses Per User */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxUsesPerUser\" className=\"block text-sm font-medium text-foreground\">\n Maximum Uses Per User\n </label>\n <input\n id=\"maxUsesPerUser\"\n type=\"number\"\n min=\"1\"\n value={formData.maxUsesPerUser}\n onChange={(e) => handleInputChange('maxUsesPerUser', e.target.value)}\n placeholder=\"e.g., 1\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.maxUsesPerUser && (\n <p className=\"text-sm text-destructive\">{errors.maxUsesPerUser}</p>\n )}\n </div>\n\n {/* Valid Until */}\n <div className=\"space-y-2\">\n <label htmlFor=\"validUntil\" className=\"block text-sm font-medium text-foreground\">\n Valid Until\n </label>\n <input\n id=\"validUntil\"\n type=\"date\"\n value={formData.validUntil}\n onChange={(e) => handleInputChange('validUntil', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n <p className=\"text-sm text-muted-foreground\">Leave empty for no expiration</p>\n </div>\n </div>\n\n {/* Error/Success Messages */}\n {submitError && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-destructive/10 border border-destructive/20\">\n <AlertCircle className=\"size-5 text-destructive mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-destructive\">Error</p>\n <p className=\"text-sm text-destructive/90 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n {submitSuccess && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-bg-subtle\">\n <CheckCircle className=\"size-5 text-status-success-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-success-text\">Success</p>\n <p className=\"text-sm text-status-success-text/90 mt-1\">\n Coupon updated successfully! Redirecting…\n </p>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex gap-4 pt-4 border-t border-border\">\n <button\n type=\"submit\"\n disabled={updating || submitSuccess}\n className=\"px-6 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n <Save className=\"size-4\" />\n {updating ? 'Updating...' : 'Update Coupon'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-6 py-2 rounded-lg border border-border text-foreground hover:bg-muted\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EditCouponPage.js","names":[],"sources":["../../../../../src/billing/modules/coupons/pages/EditCouponPage.tsx"],"sourcesContent":["/**\n * Edit Coupon Page\n *\n * Form for editing existing coupon codes\n * Note: Only certain fields can be edited (name, description, status, usage limits, expiration)\n * Code, type, value, and scope cannot be changed after creation\n */\n\nimport { type FC, useState, useEffect } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { ArrowLeft, Save, AlertCircle, CheckCircle } from 'lucide-react';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport {\n useGetCouponLazyQuery,\n useUpdateCouponMutation,\n} from '../../../../generated/global-operations';\nimport { Coupon, CouponStatus } from '../../../../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface EditFormData {\n name: string;\n description: string;\n status: CouponStatus;\n maxTotalUses: string;\n maxUsesPerUser: string;\n validUntil: string;\n}\n\nexport const EditCouponPage: 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\n // Get coupon ID from URL\n const couponId = window.location.pathname.split('/').slice(-2)[0];\n\n const [getCoupon, { data, loading: loadingCoupon, error: loadError }] = useGetCouponLazyQuery();\n const [updateCoupon, { loading: updating }] = useUpdateCouponMutation();\n\n const [formData, setFormData] = useState<EditFormData>({\n name: '',\n description: '',\n status: CouponStatus.Active,\n maxTotalUses: '',\n maxUsesPerUser: '',\n validUntil: '',\n });\n\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [submitError, setSubmitError] = useState('');\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n // Load coupon data\n useEffect(() => {\n if (couponId) {\n getCoupon({ variables: { id: couponId } });\n }\n }, [couponId, getCoupon]);\n\n // Populate form when data loads\n useEffect(() => {\n if (data?.getCoupon) {\n const coupon = data.getCoupon as Coupon;\n setFormData({\n name: coupon.name,\n description: coupon.description || '',\n status: coupon.status,\n maxTotalUses: coupon.maxTotalUses?.toString() || '',\n maxUsesPerUser: coupon.maxUsesPerUser.toString(),\n validUntil: coupon.validUntil\n ? new Date(coupon.validUntil).toISOString().split('T')[0]\n : '',\n });\n }\n }, [data]);\n\n // Check permissions\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to manage coupons.\" />;\n }\n\n const handleInputChange = (field: keyof EditFormData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: '' }));\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.name.trim()) {\n newErrors.name = 'Name is required';\n }\n\n if (formData.maxTotalUses && parseInt(formData.maxTotalUses) < 1) {\n newErrors.maxTotalUses = 'Must be at least 1';\n }\n\n if (formData.maxUsesPerUser && parseInt(formData.maxUsesPerUser) < 1) {\n newErrors.maxUsesPerUser = 'Must be at least 1';\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError('');\n setSubmitSuccess(false);\n\n if (!validateForm()) {\n return;\n }\n\n if (!couponId) {\n setSubmitError('Coupon ID is missing');\n return;\n }\n\n try {\n const input: {\n name?: string;\n description?: string;\n status?: CouponStatus;\n maxTotalUses?: number;\n maxUsesPerUser?: number;\n validUntil?: string;\n } = {};\n\n if (formData.name.trim()) input.name = formData.name.trim();\n if (formData.description.trim()) input.description = formData.description.trim();\n if (formData.status) input.status = formData.status;\n if (formData.maxTotalUses) input.maxTotalUses = parseInt(formData.maxTotalUses);\n if (formData.maxUsesPerUser) input.maxUsesPerUser = parseInt(formData.maxUsesPerUser);\n if (formData.validUntil) input.validUntil = new Date(formData.validUntil).toISOString();\n\n await updateCoupon({\n variables: {\n id: couponId,\n input,\n },\n });\n\n setSubmitSuccess(true);\n setTimeout(() => {\n navigateTo('/coupons');\n }, 1500);\n } catch (error) {\n console.error('Failed to update coupon:', error);\n setSubmitError(error instanceof Error ? error.message : 'Failed to update coupon');\n }\n };\n\n if (loadingCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title={tr('billing.coupons.editTitle', 'Edit Coupon')}\n description=\"Loading coupon data...\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-text-muted\">Loading coupon…</div>\n </div>\n </div>\n );\n }\n\n if (loadError || !data?.getCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description=\"Coupon not found\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-status-error-text\">\n {loadError ? `Error: ${loadError.message}` : 'Coupon not found'}\n </div>\n </div>\n </div>\n );\n }\n\n const coupon = data.getCoupon as Coupon;\n\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description={`Editing: ${coupon.code}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to Coupons\n </button>\n }\n />\n\n <div className=\"max-w-3xl mx-auto mt-8\">\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Read-only Information Card */}\n <div className=\"bg-bg-sunken/30 border border-border-subtle rounded-lg p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Coupon Details (Read-Only)\n </h3>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-text-muted\">Code:</span>\n <span className=\"ml-2 font-mono font-medium text-text-primary\">{coupon.code}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">Type:</span>\n <span className=\"ml-2 text-text-primary capitalize\">\n {coupon.type.replace('_', ' ').toLowerCase()}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Value:</span>\n <span className=\"ml-2 text-text-primary\">\n {coupon.type === 'PERCENTAGE' ? `${coupon.value}%` : `$${coupon.value}`}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Scope:</span>\n <span className=\"ml-2 text-text-primary\">{coupon.scope}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">Current Uses:</span>\n <span className=\"ml-2 text-text-primary\">\n {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Created:</span>\n <span className=\"ml-2 text-text-primary\">\n {new Date(coupon.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Editable Fields Card */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-6\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Editable Fields</h3>\n\n {/* Name */}\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary\">\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleInputChange('name', e.target.value)}\n placeholder=\"e.g., Welcome Discount\"\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.name && <p className=\"text-sm text-status-error-text\">{errors.name}</p>}\n </div>\n\n {/* Description */}\n <div className=\"space-y-2\">\n <label htmlFor=\"description\" className=\"block text-sm font-medium text-text-primary\">\n Description\n </label>\n <textarea\n id=\"description\"\n value={formData.description}\n onChange={(e) => handleInputChange('description', e.target.value)}\n placeholder=\"Optional description\"\n rows={3}\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n {/* Status */}\n <div className=\"space-y-2\">\n <label htmlFor=\"status\" className=\"block text-sm font-medium text-text-primary\">\n Status\n </label>\n <select\n id=\"status\"\n value={formData.status}\n onChange={(e) => handleInputChange('status', e.target.value)}\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"ACTIVE\">Active</option>\n <option value=\"INACTIVE\">Inactive</option>\n <option value=\"EXPIRED\">Expired</option>\n <option value=\"DEPLETED\">Depleted</option>\n </select>\n </div>\n\n {/* Max Total Uses */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxTotalUses\" className=\"block text-sm font-medium text-text-primary\">\n Maximum Total Uses\n </label>\n <input\n id=\"maxTotalUses\"\n type=\"number\"\n min=\"1\"\n value={formData.maxTotalUses}\n onChange={(e) => handleInputChange('maxTotalUses', e.target.value)}\n placeholder=\"Leave empty for unlimited\"\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.maxTotalUses && (\n <p className=\"text-sm text-status-error-text\">{errors.maxTotalUses}</p>\n )}\n <p className=\"text-sm text-text-muted\">\n Current uses: {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </p>\n </div>\n\n {/* Max Uses Per User */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"maxUsesPerUser\"\n className=\"block text-sm font-medium text-text-primary\"\n >\n Maximum Uses Per User\n </label>\n <input\n id=\"maxUsesPerUser\"\n type=\"number\"\n min=\"1\"\n value={formData.maxUsesPerUser}\n onChange={(e) => handleInputChange('maxUsesPerUser', e.target.value)}\n placeholder=\"e.g., 1\"\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.maxUsesPerUser && (\n <p className=\"text-sm text-status-error-text\">{errors.maxUsesPerUser}</p>\n )}\n </div>\n\n {/* Valid Until */}\n <div className=\"space-y-2\">\n <label htmlFor=\"validUntil\" className=\"block text-sm font-medium text-text-primary\">\n Valid Until\n </label>\n <input\n id=\"validUntil\"\n type=\"date\"\n value={formData.validUntil}\n onChange={(e) => handleInputChange('validUntil', e.target.value)}\n className=\"w-full px-4 py-2 rounded-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-sm text-text-muted\">Leave empty for no expiration</p>\n </div>\n </div>\n\n {/* Error/Success Messages */}\n {submitError && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-status-error-bg-subtle border border-status-error-border\">\n <AlertCircle className=\"size-5 text-status-error-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-error-text\">Error</p>\n <p className=\"text-sm text-status-error-text/90 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n {submitSuccess && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-bg-subtle\">\n <CheckCircle className=\"size-5 text-status-success-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-success-text\">Success</p>\n <p className=\"text-sm text-status-success-text/90 mt-1\">\n Coupon updated successfully! Redirecting…\n </p>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex gap-4 pt-4 border-t border-border-subtle\">\n <button\n type=\"submit\"\n disabled={updating || submitSuccess}\n className=\"px-6 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n <Save className=\"size-4\" />\n {updating ? 'Updating...' : 'Update Coupon'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-6 py-2 rounded-lg border border-border-subtle text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;AA6BA,IAAa,UAA2B;CACtC,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,IAAa,GAAoB,EACjC,IAAc,GAAuB,EAGrC,IAAW,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,MAAM,GAAG,CAAC,IAEzD,CAAC,GAAW,EAAE,SAAM,SAAS,GAAe,OAAO,OAAe,GAAuB,EACzF,CAAC,GAAc,EAAE,SAAS,OAAc,GAAyB,EAEjE,CAAC,GAAU,KAAe,EAAuB;EACrD,MAAM;EACN,aAAa;EACb,QAAQ,EAAa;EACrB,cAAc;EACd,gBAAgB;EAChB,YAAY;EACb,CAAC,EAEI,CAAC,GAAQ,KAAa,EAAiC,EAAE,CAAC,EAC1D,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAe,KAAoB,EAAS,GAAM;AA2BzD,KAxBA,QAAgB;AACd,EAAI,KACF,EAAU,EAAE,WAAW,EAAE,IAAI,GAAU,EAAE,CAAC;IAE3C,CAAC,GAAU,EAAU,CAAC,EAGzB,QAAgB;AACd,MAAI,GAAM,WAAW;GACnB,IAAM,IAAS,EAAK;AACpB,KAAY;IACV,MAAM,EAAO;IACb,aAAa,EAAO,eAAe;IACnC,QAAQ,EAAO;IACf,cAAc,EAAO,cAAc,UAAU,IAAI;IACjD,gBAAgB,EAAO,eAAe,UAAU;IAChD,YAAY,EAAO,aACf,IAAI,KAAK,EAAO,WAAW,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,KACrD;IACL,CAAC;;IAEH,CAAC,EAAK,CAAC,EAGN,CAAC,EAAY,mBACf,QAAO,kBAAC,GAAD,EAAc,SAAQ,gDAAiD,CAAA;CAGhF,IAAM,KAAqB,GAA2B,MAAkB;AAEtE,EADA,GAAa,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAO,EAAE,EAChD,EAAO,MACT,GAAW,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAI,EAAE;IAI7C,UAA8B;EAClC,IAAM,IAAoC,EAAE;AAe5C,SAbK,EAAS,KAAK,MAAM,KACvB,EAAU,OAAO,qBAGf,EAAS,gBAAgB,SAAS,EAAS,aAAa,GAAG,MAC7D,EAAU,eAAe,uBAGvB,EAAS,kBAAkB,SAAS,EAAS,eAAe,GAAG,MACjE,EAAU,iBAAiB,uBAG7B,EAAU,EAAU,EACb,OAAO,KAAK,EAAU,CAAC,WAAW;IAGrC,IAAe,OAAO,MAAuB;AACjD,QAAE,gBAAgB,EAClB,EAAe,GAAG,EAClB,EAAiB,GAAM,EAElB,GAAc,EAInB;OAAI,CAAC,GAAU;AACb,MAAe,uBAAuB;AACtC;;AAGF,OAAI;IACF,IAAM,IAOF,EAAE;AAiBN,IAfI,EAAS,KAAK,MAAM,KAAE,EAAM,OAAO,EAAS,KAAK,MAAM,GACvD,EAAS,YAAY,MAAM,KAAE,EAAM,cAAc,EAAS,YAAY,MAAM,GAC5E,EAAS,WAAQ,EAAM,SAAS,EAAS,SACzC,EAAS,iBAAc,EAAM,eAAe,SAAS,EAAS,aAAa,GAC3E,EAAS,mBAAgB,EAAM,iBAAiB,SAAS,EAAS,eAAe,GACjF,EAAS,eAAY,EAAM,aAAa,IAAI,KAAK,EAAS,WAAW,CAAC,aAAa,GAEvF,MAAM,EAAa,EACjB,WAAW;KACT,IAAI;KACJ;KACD,EACF,CAAC,EAEF,EAAiB,GAAK,EACtB,iBAAiB;AACf,OAAW,WAAW;OACrB,KAAK;YACD,GAAO;AAEd,IADA,QAAQ,MAAM,4BAA4B,EAAM,EAChD,EAAe,aAAiB,QAAQ,EAAM,UAAU,0BAA0B;;;;AAItF,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAO,EAAG,6BAA6B,cAAc;GACrD,aAAY;GACZ,SACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,WAAW;IACrC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,OAEzB;;GAEX,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAkB;IAAqB,CAAA;GAClD,CAAA,CACF;;AAIV,KAAI,KAAa,CAAC,GAAM,UACtB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAM;GACN,aAAY;GACZ,SACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,WAAW;IACrC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,OAEzB;;GAEX,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,IAAY,UAAU,EAAU,YAAY;IACzC,CAAA;GACF,CAAA,CACF;;CAIV,IAAM,IAAS,EAAK;AAEpB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAM;GACN,aAAa,YAAY,EAAO;GAChC,SACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,WAAW;IACrC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,kBAEzB;;GAEX,CAAA,EAEF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,QAAD;IAAM,UAAU;IAAc,WAAU;cAAxC;KAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAExD,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkB;SAAY,CAAA,EAC9C,kBAAC,QAAD;SAAM,WAAU;mBAAgD,EAAO;SAAY,CAAA,CAC/E,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkB;SAAY,CAAA,EAC9C,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAO,KAAK,QAAQ,KAAK,IAAI,CAAC,aAAa;SACvC,CAAA,CACH,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkB;SAAa,CAAA,EAC/C,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAO,SAAS,eAAe,GAAG,EAAO,MAAM,KAAK,IAAI,EAAO;SAC3D,CAAA,CACH,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkB;SAAa,CAAA,EAC/C,kBAAC,QAAD;SAAM,WAAU;mBAA0B,EAAO;SAAa,CAAA,CAC1D,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkB;SAAoB,CAAA,EACtD,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAO,aACP,EAAO,eAAe,MAAM,EAAO,iBAAiB,GAChD;WACH,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAkB;SAAe,CAAA,EACjD,kBAAC,QAAD;SAAM,WAAU;mBACb,IAAI,KAAK,EAAO,UAAU,CAAC,oBAAoB;SAC3C,CAAA,CACH,EAAA,CAAA;QACF;SACF;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBAA0C;QAAoB,CAAA;OAG5E,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAO,WAAU;oBAAhC,CAA8E,SACvE,kBAAC,QAAD;WAAM,WAAU;qBAAyB;WAAQ,CAAA,CAChD;;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,QAAQ,EAAE,OAAO,MAAM;UAC1D,aAAY;UACZ,WAAU;UACV,CAAA;SACD,EAAO,QAAQ,kBAAC,KAAD;UAAG,WAAU;oBAAkC,EAAO;UAAS,CAAA;SAC3E;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SAAO,SAAQ;SAAc,WAAU;mBAA8C;SAE7E,CAAA,EACR,kBAAC,YAAD;SACE,IAAG;SACH,OAAO,EAAS;SAChB,WAAW,MAAM,EAAkB,eAAe,EAAE,OAAO,MAAM;SACjE,aAAY;SACZ,MAAM;SACN,WAAU;SACV,CAAA,CACE;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SAAO,SAAQ;SAAS,WAAU;mBAA8C;SAExE,CAAA,EACR,kBAAC,UAAD;SACE,IAAG;SACH,OAAO,EAAS;SAChB,WAAW,MAAM,EAAkB,UAAU,EAAE,OAAO,MAAM;SAC5D,WAAU;mBAJZ;UAME,kBAAC,UAAD;WAAQ,OAAM;qBAAS;WAAe,CAAA;UACtC,kBAAC,UAAD;WAAQ,OAAM;qBAAW;WAAiB,CAAA;UAC1C,kBAAC,UAAD;WAAQ,OAAM;qBAAU;WAAgB,CAAA;UACxC,kBAAC,UAAD;WAAQ,OAAM;qBAAW;WAAiB,CAAA;UACnC;WACL;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAe,WAAU;oBAA8C;UAE9E,CAAA;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,KAAI;UACJ,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,gBAAgB,EAAE,OAAO,MAAM;UAClE,aAAY;UACZ,WAAU;UACV,CAAA;SACD,EAAO,gBACN,kBAAC,KAAD;UAAG,WAAU;oBAAkC,EAAO;UAAiB,CAAA;SAEzE,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAuC;WACtB,EAAO;WACrB,EAAO,eAAe,MAAM,EAAO,iBAAiB;WACnD;;SACA;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UACE,SAAQ;UACR,WAAU;oBACX;UAEO,CAAA;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,KAAI;UACJ,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,kBAAkB,EAAE,OAAO,MAAM;UACpE,aAAY;UACZ,WAAU;UACV,CAAA;SACD,EAAO,kBACN,kBAAC,KAAD;UAAG,WAAU;oBAAkC,EAAO;UAAmB,CAAA;SAEvE;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAa,WAAU;oBAA8C;UAE5E,CAAA;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,cAAc,EAAE,OAAO,MAAM;UAChE,WAAU;UACV,CAAA;SACF,kBAAC,KAAD;UAAG,WAAU;oBAA0B;UAAiC,CAAA;SACpE;;OACF;;KAGL,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,wCAAyC,CAAA,EAChE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAS,CAAA,EACnE,kBAAC,KAAD;QAAG,WAAU;kBAA0C;QAAgB,CAAA,CACnE;SACF;;KAGP,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,0CAA2C,CAAA,EAClE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA+C;QAAW,CAAA,EACvE,kBAAC,KAAD;QAAG,WAAU;kBAA2C;QAEpD,CAAA,CACA;SACF;;KAIR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,KAAY;OACtB,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,IAAW,gBAAgB,gBACrB;UACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,WAAW;OACrC,WAAU;iBACX;OAEQ,CAAA,CACL;;KACD;;GACH,CAAA,CACF"}
|