@burdenoff/microfe-billing 2026.623.3 → 2026.624.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.
Files changed (43) hide show
  1. package/dist/billing/BillingUserRoutes.js +3 -3
  2. package/dist/billing/BillingUserRoutes.js.map +1 -1
  3. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +34 -34
  4. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  5. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +94 -94
  6. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  7. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +162 -187
  8. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  9. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +146 -153
  10. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  11. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +10 -10
  12. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  13. package/dist/billing/modules/billing/pages/TransactionsPage.js +62 -62
  14. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  15. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +164 -179
  16. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  17. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +8 -8
  18. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  19. package/dist/billing/modules/earnings/pages/EarningsPage.js +243 -305
  20. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  21. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +73 -78
  22. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  23. package/dist/billing/modules/earnings/pages/PayoutsPage.js +173 -179
  24. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  25. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +4 -4
  26. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  27. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
  28. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  29. package/dist/billing/modules/settings/pages/SettingsPage.js +181 -173
  30. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  31. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +111 -99
  32. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  33. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +68 -83
  34. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  35. package/dist/billing/shared/components/AccessDenied.js +4 -4
  36. package/dist/billing/shared/components/AccessDenied.js.map +1 -1
  37. package/dist/billing/shared/components/ActorIdentity.js +8 -8
  38. package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
  39. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +11 -11
  40. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
  41. package/dist/billing/shared/ui/MetricCard.js +1 -1
  42. package/dist/billing/shared/ui/MetricCard.js.map +1 -1
  43. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AffiliateDashboardPage.js","names":[],"sources":["../../../../../src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport { Link2, Plus, RefreshCw, StickyNote, Ticket, TrendingUp, Users } from 'lucide-react';\n\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n AccessDenied,\n EmptyState,\n PageHeader,\n ServerError,\n StatCard,\n} from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport {\n addAffiliateCodeNote,\n createAffiliateCode,\n deactivateAffiliateCode,\n fetchMyAffiliateAnalytics,\n fetchMyAffiliateCodes,\n fetchMyAffiliateJourneys,\n updateAffiliateCode,\n type AffiliateCode,\n type AffiliateJourney,\n type AffiliateAnalyticsSummary,\n} from '../api';\n\nexport const AffiliateDashboardPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [codes, setCodes] = useState<AffiliateCode[]>([]);\n const [journeys, setJourneys] = useState<AffiliateJourney[]>([]);\n const [analytics, setAnalytics] = useState<AffiliateAnalyticsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [selectedCodeId, setSelectedCodeId] = useState<string | null>(null);\n const [form, setForm] = useState({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n\n const requestContext = useMemo(\n () => ({\n apiGatewayUrl,\n authToken,\n orgId,\n }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [loadedCodes, loadedAnalytics, loadedJourneys] = await Promise.all([\n fetchMyAffiliateCodes(requestContext),\n fetchMyAffiliateAnalytics(requestContext),\n fetchMyAffiliateJourneys(requestContext),\n ]);\n setCodes(loadedCodes);\n setAnalytics(loadedAnalytics);\n setJourneys(loadedJourneys);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError : new Error('Failed to load affiliate dashboard')\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view affiliate analytics.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Affiliate service unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const selectedCode = codes.find((item) => item.id === selectedCodeId) ?? null;\n\n const handleSubmit = async () => {\n if (!form.productSlug.trim()) {\n setError(new Error('Product slug is required'));\n return;\n }\n\n try {\n if (selectedCode) {\n await updateAffiliateCode(\n selectedCode.id,\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.updated', {\n route: '/billing/affiliate',\n entityId: selectedCode.id,\n source: 'affiliate-dashboard',\n });\n } else {\n const createdCode = await createAffiliateCode(\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n productSlug: form.productSlug,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.created', {\n route: '/billing/affiliate',\n entityId: createdCode.id,\n source: 'affiliate-dashboard',\n });\n }\n\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n await loadData();\n } catch (submitError) {\n setError(\n submitError instanceof Error ? submitError : new Error('Failed to save affiliate code')\n );\n }\n };\n\n const handleEdit = (code: AffiliateCode) => {\n setSelectedCodeId(code.id);\n setForm({\n label: code.label ?? '',\n description: code.description ?? '',\n notes: code.notes ?? '',\n productSlug: code.productSlug,\n destinationPath: code.destinationPath ?? '',\n destinationUrl: code.destinationUrl ?? '',\n });\n };\n\n const handleDeactivate = async (codeId: string) => {\n await deactivateAffiliateCode(codeId, requestContext);\n emit('billing.affiliate_code.deactivated', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const handleQuickNote = async (codeId: string) => {\n const note = window.prompt('Add an internal note for this affiliate code');\n if (!note) return;\n await addAffiliateCodeNote(codeId, note, requestContext);\n emit('billing.affiliate_code.note_added', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const metricCards = analytics\n ? [\n { title: 'Codes', value: analytics.totalCodes, icon: <Ticket className=\"size-5\" /> },\n { title: 'Visits', value: analytics.totalVisits, icon: <Users className=\"size-5\" /> },\n { title: 'Journeys', value: analytics.totalJourneys, icon: <Link2 className=\"size-5\" /> },\n {\n title: 'Revenue influenced',\n value: formatCurrency(analytics.totalRevenueAmount, 'USD'),\n icon: <TrendingUp className=\"size-5\" />,\n },\n ]\n : [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Affiliate codes\"\n description=\"Create referral codes, add internal notes, and monitor attributed journeys.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-2 text-sm font-medium text-foreground\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n }\n />\n\n {loading ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {[1, 2, 3, 4].map((item) => (\n <div\n key={item}\n className=\"h-28 animate-pulse rounded-lg border border-border bg-card\"\n />\n ))}\n </div>\n ) : analytics ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {metricCards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n ) : null}\n\n {error && !isServerError(error) ? (\n <div className=\"rounded-lg border border-border bg-card p-4 text-sm text-destructive\">\n {error.message}\n </div>\n ) : null}\n\n <div className=\"grid gap-6 xl:grid-cols-[1.1fr,1.9fr]\">\n <section className=\"rounded-lg border border-border bg-card p-4\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {selectedCode ? 'Update affiliate code' : 'Create affiliate code'}\n </h2>\n {!selectedCode ? <Plus className=\"size-4 text-muted-foreground\" /> : null}\n </div>\n\n <div className=\"space-y-3\">\n <input\n value={form.label}\n onChange={(event) =>\n setForm((current) => ({ ...current, label: event.target.value }))\n }\n placeholder=\"Label\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n <input\n value={form.productSlug}\n onChange={(event) =>\n setForm((current) => ({ ...current, productSlug: event.target.value }))\n }\n placeholder=\"Product slug (eg. bigconsole)\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n disabled={Boolean(selectedCode)}\n />\n <input\n value={form.destinationPath}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationPath: event.target.value }))\n }\n placeholder=\"Destination path (eg. /pricing)\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n <input\n value={form.destinationUrl}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationUrl: event.target.value }))\n }\n placeholder=\"Absolute destination URL (optional)\"\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n <textarea\n value={form.description}\n onChange={(event) =>\n setForm((current) => ({ ...current, description: event.target.value }))\n }\n placeholder=\"Description\"\n className=\"min-h-24 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n <textarea\n value={form.notes}\n onChange={(event) =>\n setForm((current) => ({ ...current, notes: event.target.value }))\n }\n placeholder=\"Internal notes\"\n className=\"min-h-24 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground\"\n >\n {selectedCode ? 'Save changes' : 'Create code'}\n </button>\n {selectedCode ? (\n <button\n type=\"button\"\n onClick={() => {\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n }}\n className=\"rounded-md border border-border bg-card px-4 py-2 text-sm font-medium text-foreground\"\n >\n Cancel\n </button>\n ) : null}\n </div>\n </section>\n\n <section className=\"space-y-4\">\n <div className=\"rounded-lg border border-border bg-card p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-foreground\">My codes</h2>\n {codes.length === 0 && !loading ? (\n <EmptyState\n icon={<Ticket className=\"size-8\" />}\n title=\"No affiliate codes yet\"\n description=\"Create your first code to start tracking attributable journeys.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {codes.map((code) => {\n const shareUrl =\n code.destinationUrl ||\n `https://${code.productSlug}.com${code.destinationPath || ''}?ref=${code.code}`;\n return (\n <div\n key={code.id}\n className=\"rounded-lg border border-border bg-background p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <p className=\"text-sm font-semibold text-foreground\">\n {code.label || code.code}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {code.code} · {code.productSlug}\n </p>\n <p className=\"mt-1 break-all text-xs text-muted-foreground\">{shareUrl}</p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => handleEdit(code)}\n className=\"rounded-md border border-border px-3 py-1.5 text-xs font-medium text-foreground\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => void handleQuickNote(code.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border px-3 py-1.5 text-xs font-medium text-foreground\"\n >\n <StickyNote className=\"size-3.5\" />\n Note\n </button>\n {code.status === 'ACTIVE' ? (\n <button\n type=\"button\"\n onClick={() => void handleDeactivate(code.id)}\n className=\"rounded-md border border-border px-3 py-1.5 text-xs font-medium text-destructive\"\n >\n Deactivate\n </button>\n ) : null}\n </div>\n </div>\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <div className=\"rounded-md border border-border bg-card p-3\">\n <p className=\"text-xs text-muted-foreground\">Visits</p>\n <p className=\"text-lg font-semibold text-foreground\">\n {code.totalVisits}\n </p>\n </div>\n <div className=\"rounded-md border border-border bg-card p-3\">\n <p className=\"text-xs text-muted-foreground\">Signups</p>\n <p className=\"text-lg font-semibold text-foreground\">\n {code.totalSignups}\n </p>\n </div>\n <div className=\"rounded-md border border-border bg-card p-3\">\n <p className=\"text-xs text-muted-foreground\">Paid conversions</p>\n <p className=\"text-lg font-semibold text-foreground\">\n {code.totalPaidConversions + code.totalStorePurchases}\n </p>\n </div>\n <div className=\"rounded-md border border-border bg-card p-3\">\n <p className=\"text-xs text-muted-foreground\">Rewards earned</p>\n <p className=\"text-lg font-semibold text-foreground\">\n {formatCurrency(code.totalRewardAmount, 'USD')}\n </p>\n </div>\n </div>\n {code.notes ? (\n <p className=\"mt-3 text-sm text-muted-foreground\">{code.notes}</p>\n ) : null}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <div className=\"rounded-lg border border-border bg-card p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-foreground\">\n Recent attributed journeys\n </h2>\n {journeys.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-8\" />}\n title=\"No journeys yet\"\n description=\"Journeys will appear as visitors land on your affiliate links.\"\n />\n ) : (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border text-left text-muted-foreground\">\n <th className=\"px-3 py-2\">Code</th>\n <th className=\"px-3 py-2\">Product</th>\n <th className=\"px-3 py-2\">Path</th>\n <th className=\"px-3 py-2\">Country</th>\n <th className=\"px-3 py-2\">Status</th>\n <th className=\"px-3 py-2\">Visited</th>\n </tr>\n </thead>\n <tbody>\n {journeys.map((journey) => (\n <tr key={journey.id} className=\"border-b border-border/70 text-foreground\">\n <td className=\"px-3 py-2 font-medium\">{journey.codeValue}</td>\n <td className=\"px-3 py-2\">{journey.productSlug}</td>\n <td className=\"px-3 py-2\">\n {journey.currentPath || journey.landingPath || '-'}\n </td>\n <td className=\"px-3 py-2\">{journey.geographyCountry || '-'}</td>\n <td className=\"px-3 py-2\">{journey.status}</td>\n <td className=\"px-3 py-2\">{formatDateTime(journey.lastVisitedAt)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n </section>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AffiliateDashboardPage.js","names":[],"sources":["../../../../../src/billing/modules/affiliate/pages/AffiliateDashboardPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport { Link2, Plus, RefreshCw, StickyNote, Ticket, TrendingUp, Users } from 'lucide-react';\nimport { ResponsiveTable, type ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\n\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n AccessDenied,\n EmptyState,\n PageHeader,\n ServerError,\n StatCard,\n} from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\n\nconst journeyColumns: ResponsiveTableColumn<AffiliateJourney>[] = [\n { key: 'code', header: 'Code', priority: 'primary', cell: (journey) => journey.codeValue },\n { key: 'product', header: 'Product', cell: (journey) => journey.productSlug },\n {\n key: 'path',\n header: 'Path',\n cell: (journey) => journey.currentPath || journey.landingPath || '-',\n },\n { key: 'country', header: 'Country', cell: (journey) => journey.geographyCountry || '-' },\n { key: 'status', header: 'Status', cell: (journey) => journey.status },\n {\n key: 'visited',\n header: 'Visited',\n cell: (journey) => formatDateTime(journey.lastVisitedAt),\n },\n];\nimport {\n addAffiliateCodeNote,\n createAffiliateCode,\n deactivateAffiliateCode,\n fetchMyAffiliateAnalytics,\n fetchMyAffiliateCodes,\n fetchMyAffiliateJourneys,\n updateAffiliateCode,\n type AffiliateCode,\n type AffiliateJourney,\n type AffiliateAnalyticsSummary,\n} from '../api';\n\nexport const AffiliateDashboardPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [codes, setCodes] = useState<AffiliateCode[]>([]);\n const [journeys, setJourneys] = useState<AffiliateJourney[]>([]);\n const [analytics, setAnalytics] = useState<AffiliateAnalyticsSummary | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [selectedCodeId, setSelectedCodeId] = useState<string | null>(null);\n const [form, setForm] = useState({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n\n const requestContext = useMemo(\n () => ({\n apiGatewayUrl,\n authToken,\n orgId,\n }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [loadedCodes, loadedAnalytics, loadedJourneys] = await Promise.all([\n fetchMyAffiliateCodes(requestContext),\n fetchMyAffiliateAnalytics(requestContext),\n fetchMyAffiliateJourneys(requestContext),\n ]);\n setCodes(loadedCodes);\n setAnalytics(loadedAnalytics);\n setJourneys(loadedJourneys);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError : new Error('Failed to load affiliate dashboard')\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view affiliate analytics.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Affiliate service unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const selectedCode = codes.find((item) => item.id === selectedCodeId) ?? null;\n\n const handleSubmit = async () => {\n if (!form.productSlug.trim()) {\n setError(new Error('Product slug is required'));\n return;\n }\n\n try {\n if (selectedCode) {\n await updateAffiliateCode(\n selectedCode.id,\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.updated', {\n route: '/billing/affiliate',\n entityId: selectedCode.id,\n source: 'affiliate-dashboard',\n });\n } else {\n const createdCode = await createAffiliateCode(\n {\n label: form.label,\n description: form.description,\n notes: form.notes,\n productSlug: form.productSlug,\n destinationPath: form.destinationPath,\n destinationUrl: form.destinationUrl,\n },\n requestContext\n );\n emit('billing.affiliate_code.created', {\n route: '/billing/affiliate',\n entityId: createdCode.id,\n source: 'affiliate-dashboard',\n });\n }\n\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n await loadData();\n } catch (submitError) {\n setError(\n submitError instanceof Error ? submitError : new Error('Failed to save affiliate code')\n );\n }\n };\n\n const handleEdit = (code: AffiliateCode) => {\n setSelectedCodeId(code.id);\n setForm({\n label: code.label ?? '',\n description: code.description ?? '',\n notes: code.notes ?? '',\n productSlug: code.productSlug,\n destinationPath: code.destinationPath ?? '',\n destinationUrl: code.destinationUrl ?? '',\n });\n };\n\n const handleDeactivate = async (codeId: string) => {\n await deactivateAffiliateCode(codeId, requestContext);\n emit('billing.affiliate_code.deactivated', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const handleQuickNote = async (codeId: string) => {\n const note = window.prompt('Add an internal note for this affiliate code');\n if (!note) return;\n await addAffiliateCodeNote(codeId, note, requestContext);\n emit('billing.affiliate_code.note_added', {\n route: '/billing/affiliate',\n entityId: codeId,\n source: 'affiliate-dashboard',\n });\n await loadData();\n };\n\n const metricCards = analytics\n ? [\n { title: 'Codes', value: analytics.totalCodes, icon: <Ticket className=\"size-5\" /> },\n { title: 'Visits', value: analytics.totalVisits, icon: <Users className=\"size-5\" /> },\n { title: 'Journeys', value: analytics.totalJourneys, icon: <Link2 className=\"size-5\" /> },\n {\n title: 'Revenue influenced',\n value: formatCurrency(analytics.totalRevenueAmount, 'USD'),\n icon: <TrendingUp className=\"size-5\" />,\n },\n ]\n : [];\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Affiliate codes\"\n description=\"Create referral codes, add internal notes, and monitor attributed journeys.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh\n </button>\n }\n />\n\n {loading ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {[1, 2, 3, 4].map((item) => (\n <div\n key={item}\n className=\"h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : analytics ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {metricCards.map((card) => (\n <StatCard key={card.title} label={card.title} value={card.value} icon={card.icon} />\n ))}\n </div>\n ) : null}\n\n {error && !isServerError(error) ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text\">\n {error.message}\n </div>\n ) : null}\n\n <div className=\"grid gap-6 xl:grid-cols-[1.1fr,1.9fr]\">\n <section className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {selectedCode ? 'Update affiliate code' : 'Create affiliate code'}\n </h2>\n {!selectedCode ? <Plus className=\"size-4 text-text-secondary\" /> : null}\n </div>\n\n <div className=\"space-y-3\">\n <input\n value={form.label}\n onChange={(event) =>\n setForm((current) => ({ ...current, label: event.target.value }))\n }\n placeholder=\"Label\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <input\n value={form.productSlug}\n onChange={(event) =>\n setForm((current) => ({ ...current, productSlug: event.target.value }))\n }\n placeholder=\"Product slug (eg. bigconsole)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n disabled={Boolean(selectedCode)}\n />\n <input\n value={form.destinationPath}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationPath: event.target.value }))\n }\n placeholder=\"Destination path (eg. /pricing)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <input\n value={form.destinationUrl}\n onChange={(event) =>\n setForm((current) => ({ ...current, destinationUrl: event.target.value }))\n }\n placeholder=\"Absolute destination URL (optional)\"\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <textarea\n value={form.description}\n onChange={(event) =>\n setForm((current) => ({ ...current, description: event.target.value }))\n }\n placeholder=\"Description\"\n className=\"min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n <textarea\n value={form.notes}\n onChange={(event) =>\n setForm((current) => ({ ...current, notes: event.target.value }))\n }\n placeholder=\"Internal notes\"\n className=\"min-h-24 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n />\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n {selectedCode ? 'Save changes' : 'Create code'}\n </button>\n {selectedCode ? (\n <button\n type=\"button\"\n onClick={() => {\n setSelectedCodeId(null);\n setForm({\n label: '',\n description: '',\n notes: '',\n productSlug: '',\n destinationPath: '',\n destinationUrl: '',\n });\n }}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary\"\n >\n Cancel\n </button>\n ) : null}\n </div>\n </section>\n\n <section className=\"space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">My codes</h2>\n {codes.length === 0 && !loading ? (\n <EmptyState\n icon={<Ticket className=\"size-8\" />}\n title=\"No affiliate codes yet\"\n description=\"Create your first code to start tracking attributable journeys.\"\n />\n ) : (\n <div className=\"space-y-4\">\n {codes.map((code) => {\n const shareUrl =\n code.destinationUrl ||\n `https://${code.productSlug}.com${code.destinationPath || ''}?ref=${code.code}`;\n return (\n <div\n key={code.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {code.label || code.code}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {code.code} · {code.productSlug}\n </p>\n <p className=\"mt-1 break-all text-xs text-text-secondary\">{shareUrl}</p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => handleEdit(code)}\n className=\"rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => void handleQuickNote(code.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary\"\n >\n <StickyNote className=\"size-3.5\" />\n Note\n </button>\n {code.status === 'ACTIVE' ? (\n <button\n type=\"button\"\n onClick={() => void handleDeactivate(code.id)}\n className=\"rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n Deactivate\n </button>\n ) : null}\n </div>\n </div>\n <div className=\"mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4\">\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Visits</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalVisits}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Signups</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalSignups}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Paid conversions</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {code.totalPaidConversions + code.totalStorePurchases}\n </p>\n </div>\n <div className=\"rounded-md border border-border-subtle bg-bg-surface p-3\">\n <p className=\"text-xs text-text-secondary\">Rewards earned</p>\n <p className=\"text-lg font-semibold text-text-primary\">\n {formatCurrency(code.totalRewardAmount, 'USD')}\n </p>\n </div>\n </div>\n {code.notes ? (\n <p className=\"mt-3 text-sm text-text-secondary\">{code.notes}</p>\n ) : null}\n </div>\n );\n })}\n </div>\n )}\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n Recent attributed journeys\n </h2>\n {journeys.length === 0 ? (\n <EmptyState\n icon={<Users className=\"size-8\" />}\n title=\"No journeys yet\"\n description=\"Journeys will appear as visitors land on your affiliate links.\"\n />\n ) : (\n <ResponsiveTable\n data={journeys}\n rowKey={(journey) => journey.id}\n columns={journeyColumns}\n />\n )}\n </div>\n </section>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}