@burdenoff/microfe-admin 2026.625.1 → 2026.626.1
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/admin/components/AccessDenied.js +2 -2
- package/dist/admin/components/AccessDenied.js.map +1 -1
- package/dist/admin/components/ActivityFeed.js +4 -4
- package/dist/admin/components/ActivityFeed.js.map +1 -1
- package/dist/admin/components/Charts.js.map +1 -1
- package/dist/admin/components/ConfirmDialog.js +1 -1
- package/dist/admin/components/ConfirmDialog.js.map +1 -1
- package/dist/admin/components/FormSheet.js +1 -1
- package/dist/admin/components/FormSheet.js.map +1 -1
- package/dist/admin/components/TenantSsoPanel.js +1 -1
- package/dist/admin/components/TenantSsoPanel.js.map +1 -1
- package/dist/admin/components/Toast.js +11 -11
- package/dist/admin/components/Toast.js.map +1 -1
- package/dist/admin/components/forms/SubscriptionForm.js +2 -2
- package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
- package/dist/admin/components/forms/TenantForm.js +1 -1
- package/dist/admin/components/forms/UserForm.js +1 -1
- package/dist/admin/components/ui.js +3 -3
- package/dist/admin/components/ui.js.map +1 -1
- package/dist/admin/constants/index.js +33 -24
- package/dist/admin/constants/index.js.map +1 -1
- package/dist/admin/pages/AuditLogsPage.js +92 -77
- package/dist/admin/pages/AuditLogsPage.js.map +1 -1
- package/dist/admin/pages/BillingPage.js +95 -51
- package/dist/admin/pages/BillingPage.js.map +1 -1
- package/dist/admin/pages/CommunicationsPage.js +97 -92
- package/dist/admin/pages/CommunicationsPage.js.map +1 -1
- package/dist/admin/pages/ContentPagesPage.js +274 -259
- package/dist/admin/pages/ContentPagesPage.js.map +1 -1
- package/dist/admin/pages/DashboardPage.js +113 -84
- package/dist/admin/pages/DashboardPage.js.map +1 -1
- package/dist/admin/pages/FeatureFlagsPage.js +93 -75
- package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
- package/dist/admin/pages/OrganizationsListPage.js +105 -87
- package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
- package/dist/admin/pages/ProductsPage.js +138 -130
- package/dist/admin/pages/ProductsPage.js.map +1 -1
- package/dist/admin/pages/ReferralsPage.js +70 -52
- package/dist/admin/pages/ReferralsPage.js.map +1 -1
- package/dist/admin/pages/RolesPermissionsPage.js +136 -118
- package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
- package/dist/admin/pages/SREPage.js +34 -32
- package/dist/admin/pages/SREPage.js.map +1 -1
- package/dist/admin/pages/SandboxSettingsPage.js +360 -322
- package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage.js +63 -60
- package/dist/admin/pages/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SyncFailuresPage.js +120 -118
- package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
- package/dist/admin/pages/TenantDetailPage.js +261 -218
- package/dist/admin/pages/TenantDetailPage.js.map +1 -1
- package/dist/admin/pages/TenantFormRoutePage.js +65 -61
- package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
- package/dist/admin/pages/TenantsListPage.js +86 -71
- package/dist/admin/pages/TenantsListPage.js.map +1 -1
- package/dist/admin/pages/UsersListPage.js +128 -113
- package/dist/admin/pages/UsersListPage.js.map +1 -1
- package/dist/admin/pages/WaitlistPage.js +78 -63
- package/dist/admin/pages/WaitlistPage.js.map +1 -1
- package/dist/admin/pages/WorkflowsPage.js +107 -102
- package/dist/admin/pages/WorkflowsPage.js.map +1 -1
- package/dist/admin/pages/WorkspacesListPage.js +61 -49
- package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
- package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
- package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
- package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
- package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
- package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
- package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,76 +1,100 @@
|
|
|
1
|
-
import { Badge as e } from "../components/ui.js";
|
|
2
|
-
import { PageLayout as
|
|
3
|
-
import { formatCurrency as
|
|
4
|
-
import { MetricCard as
|
|
5
|
-
import { DataTable as
|
|
1
|
+
import { Badge as e, EmptyState as t } from "../components/ui.js";
|
|
2
|
+
import { PageLayout as n } from "../components/PageLayout.js";
|
|
3
|
+
import { formatCurrency as r, formatDateTime as i } from "../utils/formatters.js";
|
|
4
|
+
import { MetricCard as a, MetricsGrid as o } from "../components/MetricCard.js";
|
|
5
|
+
import { DataTable as s } from "../components/DataTable.js";
|
|
6
6
|
import "../components/index.js";
|
|
7
|
-
import { useListAllPlansQuery as
|
|
8
|
-
import { ArrowUpRight as
|
|
9
|
-
import { jsx as
|
|
10
|
-
import { CTAOverflowMenu as
|
|
7
|
+
import { useListAllPlansQuery as c, useListAllQuotasQuery as l, useListRefundRecordsQuery as u } from "../../generated/global-operations.js";
|
|
8
|
+
import { ArrowUpRight as d, CreditCard as f, Download as p, Plus as m, Receipt as h, Shield as g } from "lucide-react";
|
|
9
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
10
|
+
import { CTAOverflowMenu as y, NextSteps as b, PagePurpose as x } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/admin/pages/BillingPage.tsx
|
|
12
|
-
function
|
|
13
|
-
let { data:
|
|
12
|
+
function S() {
|
|
13
|
+
let { data: S, loading: C } = c(), { data: w, loading: T } = l(), { data: E, loading: D } = u({ variables: {
|
|
14
14
|
page: 1,
|
|
15
15
|
pageSize: 10
|
|
16
|
-
} }),
|
|
17
|
-
return /* @__PURE__ */
|
|
16
|
+
} }), O = S?.getAllPlans || [], k = w?.getAllQuotas || [], A = E?.getRefundRecords?.records || [];
|
|
17
|
+
return /* @__PURE__ */ _(n, {
|
|
18
18
|
title: "Billing & Subscriptions",
|
|
19
19
|
gradientWord: "Subscriptions",
|
|
20
20
|
description: "Manage platform-wide plans, quotas, and refunds",
|
|
21
|
-
actions: /* @__PURE__ */
|
|
21
|
+
actions: /* @__PURE__ */ _(y, {
|
|
22
22
|
primary: {
|
|
23
23
|
label: "Create Plan",
|
|
24
|
-
icon: /* @__PURE__ */
|
|
24
|
+
icon: /* @__PURE__ */ _(m, { className: "mr-2 h-4 w-4" }),
|
|
25
25
|
onSelect: () => {}
|
|
26
26
|
},
|
|
27
27
|
actions: [{
|
|
28
28
|
label: "Export Audit",
|
|
29
|
-
icon: /* @__PURE__ */
|
|
29
|
+
icon: /* @__PURE__ */ _(p, { className: "h-4 w-4" }),
|
|
30
30
|
onSelect: () => {}
|
|
31
31
|
}]
|
|
32
32
|
}),
|
|
33
|
-
children: /* @__PURE__ */
|
|
33
|
+
children: /* @__PURE__ */ v("div", {
|
|
34
34
|
className: "space-y-8",
|
|
35
35
|
children: [
|
|
36
|
-
/* @__PURE__ */ _(
|
|
37
|
-
|
|
36
|
+
/* @__PURE__ */ _(x, { children: "The money side of the platform in one place. Define the plans customers can buy, set the resource quotas each plan grants, and review refunds — so you can launch a new pricing tier or resolve a billing dispute without leaving the console." }),
|
|
37
|
+
/* @__PURE__ */ _(b, {
|
|
38
|
+
storageKey: "admin-billing",
|
|
39
|
+
steps: [
|
|
40
|
+
{
|
|
41
|
+
id: "create-plan",
|
|
42
|
+
label: "Create a plan",
|
|
43
|
+
description: "Define a new pricing tier customers can subscribe to.",
|
|
44
|
+
onClick: () => {}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "review-refunds",
|
|
48
|
+
label: "Review pending refunds",
|
|
49
|
+
description: "Approve or investigate refunds awaiting a decision.",
|
|
50
|
+
href: "#refunds"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "check-quotas",
|
|
54
|
+
label: "Check resource quotas",
|
|
55
|
+
description: "Make sure each plan grants the limits you intend.",
|
|
56
|
+
href: "#quotas"
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}),
|
|
60
|
+
/* @__PURE__ */ v(o, { children: [
|
|
61
|
+
/* @__PURE__ */ _(a, {
|
|
38
62
|
title: "Total Revenue (30d)",
|
|
39
63
|
hero: !0,
|
|
40
64
|
value: "$124,500",
|
|
41
|
-
icon: /* @__PURE__ */
|
|
65
|
+
icon: /* @__PURE__ */ _(f, { className: "h-4 w-4" }),
|
|
42
66
|
trend: "up"
|
|
43
67
|
}),
|
|
44
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ _(a, {
|
|
45
69
|
title: "Active Subscriptions",
|
|
46
70
|
value: "1,248",
|
|
47
|
-
icon: /* @__PURE__ */ g
|
|
71
|
+
icon: /* @__PURE__ */ _(g, { className: "h-4 w-4" })
|
|
48
72
|
}),
|
|
49
|
-
/* @__PURE__ */
|
|
73
|
+
/* @__PURE__ */ _(a, {
|
|
50
74
|
title: "Pending Refunds",
|
|
51
|
-
value:
|
|
52
|
-
icon: /* @__PURE__ */
|
|
75
|
+
value: E?.getRefundRecords?.totalCount ?? 0,
|
|
76
|
+
icon: /* @__PURE__ */ _(d, { className: "h-4 w-4" }),
|
|
53
77
|
priority: "high"
|
|
54
78
|
}),
|
|
55
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ _(a, {
|
|
56
80
|
title: "Upcoming Invoices",
|
|
57
81
|
value: "42",
|
|
58
|
-
icon: /* @__PURE__ */
|
|
82
|
+
icon: /* @__PURE__ */ _(h, { className: "h-4 w-4" })
|
|
59
83
|
})
|
|
60
84
|
] }),
|
|
61
|
-
/* @__PURE__ */
|
|
85
|
+
/* @__PURE__ */ v("div", {
|
|
62
86
|
className: "grid gap-8 lg:grid-cols-2",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
87
|
+
children: [/* @__PURE__ */ v("div", {
|
|
64
88
|
className: "space-y-4",
|
|
65
|
-
children: [/* @__PURE__ */
|
|
89
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
66
90
|
className: "text-lg font-semibold",
|
|
67
91
|
children: "Platform Plans"
|
|
68
|
-
}), /* @__PURE__ */
|
|
92
|
+
}), /* @__PURE__ */ _(s, {
|
|
69
93
|
columns: [
|
|
70
94
|
{
|
|
71
95
|
key: "name",
|
|
72
96
|
header: "Plan Name",
|
|
73
|
-
render: (e) => /* @__PURE__ */
|
|
97
|
+
render: (e) => /* @__PURE__ */ _("span", {
|
|
74
98
|
className: "font-medium",
|
|
75
99
|
children: e.name
|
|
76
100
|
})
|
|
@@ -78,7 +102,7 @@ function y() {
|
|
|
78
102
|
{
|
|
79
103
|
key: "price",
|
|
80
104
|
header: "Price",
|
|
81
|
-
render: (e) =>
|
|
105
|
+
render: (e) => r(e.price, e.currency)
|
|
82
106
|
},
|
|
83
107
|
{
|
|
84
108
|
key: "duration",
|
|
@@ -87,22 +111,29 @@ function y() {
|
|
|
87
111
|
{
|
|
88
112
|
key: "isActive",
|
|
89
113
|
header: "Status",
|
|
90
|
-
render: (t) => /* @__PURE__ */
|
|
114
|
+
render: (t) => /* @__PURE__ */ _(e, {
|
|
91
115
|
variant: t.isActive ? "success" : "secondary",
|
|
92
116
|
children: t.isActive ? "Active" : "Inactive"
|
|
93
117
|
})
|
|
94
118
|
}
|
|
95
119
|
],
|
|
96
|
-
data:
|
|
120
|
+
data: O,
|
|
97
121
|
keyExtractor: (e) => e.id,
|
|
98
|
-
loading:
|
|
122
|
+
loading: C,
|
|
123
|
+
emptyState: /* @__PURE__ */ _(t, {
|
|
124
|
+
illustration: "empty-data",
|
|
125
|
+
icon: /* @__PURE__ */ _(f, { className: "h-8 w-8" }),
|
|
126
|
+
title: "No plans yet",
|
|
127
|
+
description: "Create your first pricing plan to let customers subscribe."
|
|
128
|
+
})
|
|
99
129
|
})]
|
|
100
|
-
}), /* @__PURE__ */
|
|
130
|
+
}), /* @__PURE__ */ v("div", {
|
|
131
|
+
id: "quotas",
|
|
101
132
|
className: "space-y-4",
|
|
102
|
-
children: [/* @__PURE__ */
|
|
133
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
103
134
|
className: "text-lg font-semibold",
|
|
104
135
|
children: "Resource Quotas"
|
|
105
|
-
}), /* @__PURE__ */
|
|
136
|
+
}), /* @__PURE__ */ _(s, {
|
|
106
137
|
columns: [
|
|
107
138
|
{
|
|
108
139
|
key: "name",
|
|
@@ -115,34 +146,41 @@ function y() {
|
|
|
115
146
|
{
|
|
116
147
|
key: "isActive",
|
|
117
148
|
header: "Status",
|
|
118
|
-
render: (t) => /* @__PURE__ */
|
|
149
|
+
render: (t) => /* @__PURE__ */ _(e, {
|
|
119
150
|
variant: t.isActive ? "success" : "secondary",
|
|
120
151
|
children: t.isActive ? "Active" : "Inactive"
|
|
121
152
|
})
|
|
122
153
|
}
|
|
123
154
|
],
|
|
124
|
-
data:
|
|
155
|
+
data: k,
|
|
125
156
|
keyExtractor: (e) => e.id,
|
|
126
|
-
loading:
|
|
157
|
+
loading: T,
|
|
158
|
+
emptyState: /* @__PURE__ */ _(t, {
|
|
159
|
+
illustration: "empty-data",
|
|
160
|
+
icon: /* @__PURE__ */ _(g, { className: "h-8 w-8" }),
|
|
161
|
+
title: "No quotas defined",
|
|
162
|
+
description: "Resource quotas you create will appear here."
|
|
163
|
+
})
|
|
127
164
|
})]
|
|
128
165
|
})]
|
|
129
166
|
}),
|
|
130
|
-
/* @__PURE__ */
|
|
167
|
+
/* @__PURE__ */ v("div", {
|
|
168
|
+
id: "refunds",
|
|
131
169
|
className: "space-y-4",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
133
171
|
className: "text-lg font-semibold",
|
|
134
172
|
children: "Recent Refund Records"
|
|
135
|
-
}), /* @__PURE__ */
|
|
173
|
+
}), /* @__PURE__ */ _(s, {
|
|
136
174
|
columns: [
|
|
137
175
|
{
|
|
138
176
|
key: "amount",
|
|
139
177
|
header: "Amount",
|
|
140
|
-
render: (e) =>
|
|
178
|
+
render: (e) => r(e.amount, e.currency)
|
|
141
179
|
},
|
|
142
180
|
{
|
|
143
181
|
key: "status",
|
|
144
182
|
header: "Status",
|
|
145
|
-
render: (t) => /* @__PURE__ */
|
|
183
|
+
render: (t) => /* @__PURE__ */ _(e, {
|
|
146
184
|
variant: t.status === "COMPLETED" ? "success" : "warning",
|
|
147
185
|
children: t.status
|
|
148
186
|
})
|
|
@@ -154,12 +192,18 @@ function y() {
|
|
|
154
192
|
{
|
|
155
193
|
key: "createdAt",
|
|
156
194
|
header: "Date",
|
|
157
|
-
render: (e) =>
|
|
195
|
+
render: (e) => i(e.createdAt)
|
|
158
196
|
}
|
|
159
197
|
],
|
|
160
|
-
data:
|
|
198
|
+
data: A,
|
|
161
199
|
keyExtractor: (e) => e.id,
|
|
162
|
-
loading:
|
|
200
|
+
loading: D,
|
|
201
|
+
emptyState: /* @__PURE__ */ _(t, {
|
|
202
|
+
illustration: "empty-generic",
|
|
203
|
+
icon: /* @__PURE__ */ _(h, { className: "h-8 w-8" }),
|
|
204
|
+
title: "No refunds recorded",
|
|
205
|
+
description: "Refund activity will show up here as it happens."
|
|
206
|
+
})
|
|
163
207
|
})]
|
|
164
208
|
})
|
|
165
209
|
]
|
|
@@ -167,6 +211,6 @@ function y() {
|
|
|
167
211
|
});
|
|
168
212
|
}
|
|
169
213
|
//#endregion
|
|
170
|
-
export {
|
|
214
|
+
export { S as BillingPage };
|
|
171
215
|
|
|
172
216
|
//# sourceMappingURL=BillingPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BillingPage.js","names":[],"sources":["../../../src/admin/pages/BillingPage.tsx"],"sourcesContent":["import { CreditCard, Plus, ArrowUpRight, Shield, Receipt, Download } from 'lucide-react';\nimport { CTAOverflowMenu } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, DataTable, Badge, MetricCard, MetricsGrid } from '../components';\nimport {\n useListAllPlansQuery,\n useListAllQuotasQuery,\n useListRefundRecordsQuery,\n} from '../../generated/global-operations';\nimport { formatCurrency, formatDateTime } from '../utils/formatters';\n\nexport function BillingPage() {\n const { data: plansData, loading: plansLoading } = useListAllPlansQuery();\n const { data: quotasData, loading: quotasLoading } = useListAllQuotasQuery();\n const { data: refundsData, loading: refundsLoading } = useListRefundRecordsQuery({\n variables: { page: 1, pageSize: 10 },\n });\n\n const plans = plansData?.getAllPlans || [];\n const quotas = quotasData?.getAllQuotas || [];\n const refunds = refundsData?.getRefundRecords?.records || [];\n\n const planColumns = [\n {\n key: 'name',\n header: 'Plan Name',\n render: (plan: any) => <span className=\"font-medium\">{plan.name}</span>,\n },\n {\n key: 'price',\n header: 'Price',\n render: (plan: any) => formatCurrency(plan.price, plan.currency),\n },\n {\n key: 'duration',\n header: 'Interval',\n },\n {\n key: 'isActive',\n header: 'Status',\n render: (plan: any) => (\n <Badge variant={plan.isActive ? 'success' : 'secondary'}>\n {plan.isActive ? 'Active' : 'Inactive'}\n </Badge>\n ),\n },\n ];\n\n const quotaColumns = [\n {\n key: 'name',\n header: 'Quota Name',\n },\n {\n key: 'quotaType',\n header: 'Metric Type',\n },\n {\n key: 'isActive',\n header: 'Status',\n render: (quota: any) => (\n <Badge variant={quota.isActive ? 'success' : 'secondary'}>\n {quota.isActive ? 'Active' : 'Inactive'}\n </Badge>\n ),\n },\n ];\n\n const refundColumns = [\n {\n key: 'amount',\n header: 'Amount',\n render: (refund: any) => formatCurrency(refund.amount, refund.currency),\n },\n {\n key: 'status',\n header: 'Status',\n render: (refund: any) => (\n <Badge variant={refund.status === 'COMPLETED' ? 'success' : 'warning'}>\n {refund.status}\n </Badge>\n ),\n },\n {\n key: 'reason',\n header: 'Reason',\n },\n {\n key: 'createdAt',\n header: 'Date',\n render: (refund: any) => formatDateTime(refund.createdAt),\n },\n ];\n\n return (\n <PageLayout\n title=\"Billing & Subscriptions\"\n gradientWord=\"Subscriptions\"\n description=\"Manage platform-wide plans, quotas, and refunds\"\n actions={\n <CTAOverflowMenu\n primary={{\n label: 'Create Plan',\n icon: <Plus className=\"mr-2 h-4 w-4\" />,\n onSelect: () => {},\n }}\n actions={[\n {\n label: 'Export Audit',\n icon: <Download className=\"h-4 w-4\" />,\n onSelect: () => {},\n },\n ]}\n />\n }\n >\n <div className=\"space-y-8\">\n <MetricsGrid>\n <MetricCard\n title=\"Total Revenue (30d)\"\n hero\n value=\"$124,500\"\n icon={<CreditCard className=\"h-4 w-4\" />}\n trend=\"up\"\n />\n <MetricCard\n title=\"Active Subscriptions\"\n value=\"1,248\"\n icon={<Shield className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Pending Refunds\"\n value={refundsData?.getRefundRecords?.totalCount ?? 0}\n icon={<ArrowUpRight className=\"h-4 w-4\" />}\n priority=\"high\"\n />\n <MetricCard title=\"Upcoming Invoices\" value=\"42\" icon={<Receipt className=\"h-4 w-4\" />} />\n </MetricsGrid>\n\n <div className=\"grid gap-8 lg:grid-cols-2\">\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Platform Plans</h2>\n <DataTable\n columns={planColumns}\n data={plans}\n keyExtractor={(p) => p.id}\n loading={plansLoading}\n />\n </div>\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Resource Quotas</h2>\n <DataTable\n columns={quotaColumns}\n data={quotas}\n keyExtractor={(q) => q.id}\n loading={quotasLoading}\n />\n </div>\n </div>\n\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Recent Refund Records</h2>\n <DataTable\n columns={refundColumns}\n data={refunds}\n keyExtractor={(r) => r.id}\n loading={refundsLoading}\n />\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BillingPage.js","names":[],"sources":["../../../src/admin/pages/BillingPage.tsx"],"sourcesContent":["import { CreditCard, Plus, ArrowUpRight, Shield, Receipt, Download } from 'lucide-react';\nimport { CTAOverflowMenu, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, DataTable, Badge, MetricCard, MetricsGrid, EmptyState } from '../components';\nimport {\n useListAllPlansQuery,\n useListAllQuotasQuery,\n useListRefundRecordsQuery,\n} from '../../generated/global-operations';\nimport { formatCurrency, formatDateTime } from '../utils/formatters';\n\nexport function BillingPage() {\n const { data: plansData, loading: plansLoading } = useListAllPlansQuery();\n const { data: quotasData, loading: quotasLoading } = useListAllQuotasQuery();\n const { data: refundsData, loading: refundsLoading } = useListRefundRecordsQuery({\n variables: { page: 1, pageSize: 10 },\n });\n\n const plans = plansData?.getAllPlans || [];\n const quotas = quotasData?.getAllQuotas || [];\n const refunds = refundsData?.getRefundRecords?.records || [];\n\n const planColumns = [\n {\n key: 'name',\n header: 'Plan Name',\n render: (plan: any) => <span className=\"font-medium\">{plan.name}</span>,\n },\n {\n key: 'price',\n header: 'Price',\n render: (plan: any) => formatCurrency(plan.price, plan.currency),\n },\n {\n key: 'duration',\n header: 'Interval',\n },\n {\n key: 'isActive',\n header: 'Status',\n render: (plan: any) => (\n <Badge variant={plan.isActive ? 'success' : 'secondary'}>\n {plan.isActive ? 'Active' : 'Inactive'}\n </Badge>\n ),\n },\n ];\n\n const quotaColumns = [\n {\n key: 'name',\n header: 'Quota Name',\n },\n {\n key: 'quotaType',\n header: 'Metric Type',\n },\n {\n key: 'isActive',\n header: 'Status',\n render: (quota: any) => (\n <Badge variant={quota.isActive ? 'success' : 'secondary'}>\n {quota.isActive ? 'Active' : 'Inactive'}\n </Badge>\n ),\n },\n ];\n\n const refundColumns = [\n {\n key: 'amount',\n header: 'Amount',\n render: (refund: any) => formatCurrency(refund.amount, refund.currency),\n },\n {\n key: 'status',\n header: 'Status',\n render: (refund: any) => (\n <Badge variant={refund.status === 'COMPLETED' ? 'success' : 'warning'}>\n {refund.status}\n </Badge>\n ),\n },\n {\n key: 'reason',\n header: 'Reason',\n },\n {\n key: 'createdAt',\n header: 'Date',\n render: (refund: any) => formatDateTime(refund.createdAt),\n },\n ];\n\n return (\n <PageLayout\n title=\"Billing & Subscriptions\"\n gradientWord=\"Subscriptions\"\n description=\"Manage platform-wide plans, quotas, and refunds\"\n actions={\n <CTAOverflowMenu\n primary={{\n label: 'Create Plan',\n icon: <Plus className=\"mr-2 h-4 w-4\" />,\n onSelect: () => {},\n }}\n actions={[\n {\n label: 'Export Audit',\n icon: <Download className=\"h-4 w-4\" />,\n onSelect: () => {},\n },\n ]}\n />\n }\n >\n <div className=\"space-y-8\">\n <PagePurpose>\n The money side of the platform in one place. Define the plans customers can buy, set the\n resource quotas each plan grants, and review refunds — so you can launch a new pricing\n tier or resolve a billing dispute without leaving the console.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"admin-billing\"\n steps={[\n {\n id: 'create-plan',\n label: 'Create a plan',\n description: 'Define a new pricing tier customers can subscribe to.',\n onClick: () => {},\n },\n {\n id: 'review-refunds',\n label: 'Review pending refunds',\n description: 'Approve or investigate refunds awaiting a decision.',\n href: '#refunds',\n },\n {\n id: 'check-quotas',\n label: 'Check resource quotas',\n description: 'Make sure each plan grants the limits you intend.',\n href: '#quotas',\n },\n ]}\n />\n\n <MetricsGrid>\n <MetricCard\n title=\"Total Revenue (30d)\"\n hero\n value=\"$124,500\"\n icon={<CreditCard className=\"h-4 w-4\" />}\n trend=\"up\"\n />\n <MetricCard\n title=\"Active Subscriptions\"\n value=\"1,248\"\n icon={<Shield className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Pending Refunds\"\n value={refundsData?.getRefundRecords?.totalCount ?? 0}\n icon={<ArrowUpRight className=\"h-4 w-4\" />}\n priority=\"high\"\n />\n <MetricCard title=\"Upcoming Invoices\" value=\"42\" icon={<Receipt className=\"h-4 w-4\" />} />\n </MetricsGrid>\n\n <div className=\"grid gap-8 lg:grid-cols-2\">\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Platform Plans</h2>\n <DataTable\n columns={planColumns}\n data={plans}\n keyExtractor={(p) => p.id}\n loading={plansLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-data\"\n icon={<CreditCard className=\"h-8 w-8\" />}\n title=\"No plans yet\"\n description=\"Create your first pricing plan to let customers subscribe.\"\n />\n }\n />\n </div>\n <div id=\"quotas\" className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Resource Quotas</h2>\n <DataTable\n columns={quotaColumns}\n data={quotas}\n keyExtractor={(q) => q.id}\n loading={quotasLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-data\"\n icon={<Shield className=\"h-8 w-8\" />}\n title=\"No quotas defined\"\n description=\"Resource quotas you create will appear here.\"\n />\n }\n />\n </div>\n </div>\n\n <div id=\"refunds\" className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Recent Refund Records</h2>\n <DataTable\n columns={refundColumns}\n data={refunds}\n keyExtractor={(r) => r.id}\n loading={refundsLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-generic\"\n icon={<Receipt className=\"h-8 w-8\" />}\n title=\"No refunds recorded\"\n description=\"Refund activity will show up here as it happens.\"\n />\n }\n />\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,11 +7,12 @@ import "../components/index.js";
|
|
|
7
7
|
import { useGetNewsletterStatsQuery as l, useListNewslettersQuery as u, useSendNewsletterMutation as d } from "../../generated/global-operations.js";
|
|
8
8
|
import { Eye as f, Mail as p, MousePointer2 as m, Plus as h, Send as g, Users as _ } from "lucide-react";
|
|
9
9
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
10
|
+
import { PagePurpose as b } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/admin/pages/CommunicationsPage.tsx
|
|
11
|
-
function
|
|
12
|
-
let { data:
|
|
12
|
+
function x() {
|
|
13
|
+
let { data: x, loading: S } = l(), { data: C, loading: w, refetch: T } = u({ variables: { limit: 50 } }), [E] = d(), D = x?.newsletterStats, O = C?.newsletters || [], k = async (e) => {
|
|
13
14
|
if (confirm("Are you sure you want to send this newsletter to all subscribers?")) try {
|
|
14
|
-
await
|
|
15
|
+
await E({ variables: { id: e } }), T();
|
|
15
16
|
} catch (e) {
|
|
16
17
|
console.error("Failed to send newsletter:", e);
|
|
17
18
|
}
|
|
@@ -23,99 +24,103 @@ function b() {
|
|
|
23
24
|
actions: /* @__PURE__ */ y(t, { children: [/* @__PURE__ */ v(h, { className: "mr-2 h-4 w-4" }), "Create Newsletter"] }),
|
|
24
25
|
children: /* @__PURE__ */ y("div", {
|
|
25
26
|
className: "space-y-8",
|
|
26
|
-
children: [
|
|
27
|
-
/* @__PURE__ */ v(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
className: "space-y-4",
|
|
54
|
-
children: [/* @__PURE__ */ v("h2", {
|
|
55
|
-
className: "text-lg font-semibold",
|
|
56
|
-
children: "Newsletters"
|
|
57
|
-
}), /* @__PURE__ */ v(c, {
|
|
58
|
-
columns: [
|
|
59
|
-
{
|
|
60
|
-
key: "title",
|
|
61
|
-
header: "Title",
|
|
62
|
-
render: (e) => /* @__PURE__ */ y("div", {
|
|
63
|
-
className: "flex flex-col",
|
|
64
|
-
children: [/* @__PURE__ */ v("span", {
|
|
65
|
-
className: "font-medium text-text-primary",
|
|
66
|
-
children: e.title
|
|
67
|
-
}), /* @__PURE__ */ v("span", {
|
|
68
|
-
className: "text-xs text-text-secondary",
|
|
69
|
-
children: e.subject
|
|
70
|
-
})]
|
|
71
|
-
})
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
key: "status",
|
|
75
|
-
header: "Status",
|
|
76
|
-
render: (t) => /* @__PURE__ */ v(e, {
|
|
77
|
-
variant: {
|
|
78
|
-
SENT: "success",
|
|
79
|
-
SCHEDULED: "warning",
|
|
80
|
-
DRAFT: "secondary"
|
|
81
|
-
}[t.status] || "outline",
|
|
82
|
-
children: t.status
|
|
83
|
-
})
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
key: "sentAt",
|
|
87
|
-
header: "Sent Date",
|
|
88
|
-
render: (e) => e.sentAt ? i(e.sentAt) : "-"
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
key: "actions",
|
|
92
|
-
header: "",
|
|
93
|
-
align: "right",
|
|
94
|
-
render: (e) => /* @__PURE__ */ v("div", {
|
|
95
|
-
className: "flex justify-end gap-2",
|
|
96
|
-
children: e.status === "DRAFT" && /* @__PURE__ */ y(t, {
|
|
97
|
-
size: "sm",
|
|
98
|
-
onClick: () => O(e.id),
|
|
99
|
-
children: [/* @__PURE__ */ v(g, { className: "mr-2 h-3 w-3" }), "Send Now"]
|
|
100
|
-
})
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
],
|
|
104
|
-
data: D,
|
|
105
|
-
keyExtractor: (e) => e.id,
|
|
106
|
-
loading: C,
|
|
107
|
-
emptyState: /* @__PURE__ */ v(n, {
|
|
108
|
-
illustration: "empty-notifications",
|
|
109
|
-
icon: /* @__PURE__ */ v(p, { className: "h-8 w-8" }),
|
|
110
|
-
title: "No newsletters found",
|
|
111
|
-
description: "Create your first newsletter to communicate with users"
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ v(b, { children: "Reach your users directly. Draft newsletters, send them to your subscriber base, and track how many people opened and clicked — use it to announce launches, share product updates, or run a re-engagement campaign." }),
|
|
29
|
+
/* @__PURE__ */ y(s, { children: [
|
|
30
|
+
/* @__PURE__ */ v(o, {
|
|
31
|
+
title: "Total Subscribers",
|
|
32
|
+
hero: !0,
|
|
33
|
+
value: D?.totalSubscribers ?? 0,
|
|
34
|
+
loading: S,
|
|
35
|
+
icon: /* @__PURE__ */ v(_, { className: "h-4 w-4" })
|
|
36
|
+
}),
|
|
37
|
+
/* @__PURE__ */ v(o, {
|
|
38
|
+
title: "Newsletters Sent",
|
|
39
|
+
value: D?.totalSent ?? 0,
|
|
40
|
+
loading: S,
|
|
41
|
+
icon: /* @__PURE__ */ v(g, { className: "h-4 w-4" })
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ v(o, {
|
|
44
|
+
title: "Avg. Open Rate",
|
|
45
|
+
value: a(D?.averageOpenRate ?? 0),
|
|
46
|
+
loading: S,
|
|
47
|
+
icon: /* @__PURE__ */ v(f, { className: "h-4 w-4" })
|
|
48
|
+
}),
|
|
49
|
+
/* @__PURE__ */ v(o, {
|
|
50
|
+
title: "Avg. Click Rate",
|
|
51
|
+
value: a(D?.averageClickRate ?? 0),
|
|
52
|
+
loading: S,
|
|
53
|
+
icon: /* @__PURE__ */ v(m, { className: "h-4 w-4" })
|
|
112
54
|
})
|
|
113
|
-
})
|
|
114
|
-
|
|
55
|
+
] }),
|
|
56
|
+
/* @__PURE__ */ y("div", {
|
|
57
|
+
className: "space-y-4",
|
|
58
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
59
|
+
className: "text-lg font-semibold",
|
|
60
|
+
children: "Newsletters"
|
|
61
|
+
}), /* @__PURE__ */ v(c, {
|
|
62
|
+
columns: [
|
|
63
|
+
{
|
|
64
|
+
key: "title",
|
|
65
|
+
header: "Title",
|
|
66
|
+
render: (e) => /* @__PURE__ */ y("div", {
|
|
67
|
+
className: "flex flex-col",
|
|
68
|
+
children: [/* @__PURE__ */ v("span", {
|
|
69
|
+
className: "font-medium text-text-primary",
|
|
70
|
+
children: e.title
|
|
71
|
+
}), /* @__PURE__ */ v("span", {
|
|
72
|
+
className: "text-xs text-text-secondary",
|
|
73
|
+
children: e.subject
|
|
74
|
+
})]
|
|
75
|
+
})
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
key: "status",
|
|
79
|
+
header: "Status",
|
|
80
|
+
render: (t) => /* @__PURE__ */ v(e, {
|
|
81
|
+
variant: {
|
|
82
|
+
SENT: "success",
|
|
83
|
+
SCHEDULED: "warning",
|
|
84
|
+
DRAFT: "secondary"
|
|
85
|
+
}[t.status] || "outline",
|
|
86
|
+
children: t.status
|
|
87
|
+
})
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
key: "sentAt",
|
|
91
|
+
header: "Sent Date",
|
|
92
|
+
render: (e) => e.sentAt ? i(e.sentAt) : "-"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
key: "actions",
|
|
96
|
+
header: "",
|
|
97
|
+
align: "right",
|
|
98
|
+
render: (e) => /* @__PURE__ */ v("div", {
|
|
99
|
+
className: "flex justify-end gap-2",
|
|
100
|
+
children: e.status === "DRAFT" && /* @__PURE__ */ y(t, {
|
|
101
|
+
size: "sm",
|
|
102
|
+
onClick: () => k(e.id),
|
|
103
|
+
children: [/* @__PURE__ */ v(g, { className: "mr-2 h-3 w-3" }), "Send Now"]
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
data: O,
|
|
109
|
+
keyExtractor: (e) => e.id,
|
|
110
|
+
loading: w,
|
|
111
|
+
emptyState: /* @__PURE__ */ v(n, {
|
|
112
|
+
illustration: "empty-notifications",
|
|
113
|
+
icon: /* @__PURE__ */ v(p, { className: "h-8 w-8" }),
|
|
114
|
+
title: "No newsletters found",
|
|
115
|
+
description: "Create your first newsletter to communicate with users"
|
|
116
|
+
})
|
|
117
|
+
})]
|
|
118
|
+
})
|
|
119
|
+
]
|
|
115
120
|
})
|
|
116
121
|
});
|
|
117
122
|
}
|
|
118
123
|
//#endregion
|
|
119
|
-
export {
|
|
124
|
+
export { x as CommunicationsPage };
|
|
120
125
|
|
|
121
126
|
//# sourceMappingURL=CommunicationsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommunicationsPage.js","names":[],"sources":["../../../src/admin/pages/CommunicationsPage.tsx"],"sourcesContent":["import { Mail, Plus, Send, Users, Eye, MousePointer2 } from 'lucide-react';\nimport {\n PageLayout,\n Button,\n DataTable,\n Badge,\n MetricCard,\n MetricsGrid,\n EmptyState,\n} from '../components';\nimport {\n useListNewslettersQuery,\n useGetNewsletterStatsQuery,\n useSendNewsletterMutation,\n} from '../../generated/global-operations';\nimport { formatDateTime, formatPercentage } from '../utils/formatters';\n\nexport function CommunicationsPage() {\n const { data: statsData, loading: statsLoading } = useGetNewsletterStatsQuery();\n const {\n data: listData,\n loading: listLoading,\n refetch,\n } = useListNewslettersQuery({\n variables: { limit: 50 },\n });\n\n const [sendNewsletter] = useSendNewsletterMutation();\n\n const stats = statsData?.newsletterStats;\n const newsletters = listData?.newsletters || [];\n\n const handleSend = async (id: string) => {\n if (confirm('Are you sure you want to send this newsletter to all subscribers?')) {\n try {\n await sendNewsletter({ variables: { id } });\n refetch();\n } catch (err) {\n console.error('Failed to send newsletter:', err);\n }\n }\n };\n\n const columns = [\n {\n key: 'title',\n header: 'Title',\n render: (news: any) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium text-text-primary\">{news.title}</span>\n <span className=\"text-xs text-text-secondary\">{news.subject}</span>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n render: (news: any) => {\n const variants: Record<\n string,\n 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline'\n > = {\n SENT: 'success',\n SCHEDULED: 'warning',\n DRAFT: 'secondary',\n };\n return <Badge variant={variants[news.status] || 'outline'}>{news.status}</Badge>;\n },\n },\n {\n key: 'sentAt',\n header: 'Sent Date',\n render: (news: any) => (news.sentAt ? formatDateTime(news.sentAt) : '-'),\n },\n {\n key: 'actions',\n header: '',\n align: 'right' as const,\n render: (news: any) => (\n <div className=\"flex justify-end gap-2\">\n {news.status === 'DRAFT' && (\n <Button size=\"sm\" onClick={() => handleSend(news.id)}>\n <Send className=\"mr-2 h-3 w-3\" />\n Send Now\n </Button>\n )}\n </div>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Communications\"\n gradientWord=\"Communications\"\n description=\"Manage platform newsletters and system notifications\"\n actions={\n <Button>\n <Plus className=\"mr-2 h-4 w-4\" />\n Create Newsletter\n </Button>\n }\n >\n <div className=\"space-y-8\">\n <MetricsGrid>\n <MetricCard\n title=\"Total Subscribers\"\n hero\n value={stats?.totalSubscribers ?? 0}\n loading={statsLoading}\n icon={<Users className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Newsletters Sent\"\n value={stats?.totalSent ?? 0}\n loading={statsLoading}\n icon={<Send className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Avg. Open Rate\"\n value={formatPercentage(stats?.averageOpenRate ?? 0)}\n loading={statsLoading}\n icon={<Eye className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Avg. Click Rate\"\n value={formatPercentage(stats?.averageClickRate ?? 0)}\n loading={statsLoading}\n icon={<MousePointer2 className=\"h-4 w-4\" />}\n />\n </MetricsGrid>\n\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Newsletters</h2>\n <DataTable\n columns={columns}\n data={newsletters}\n keyExtractor={(n) => n.id}\n loading={listLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-notifications\"\n icon={<Mail className=\"h-8 w-8\" />}\n title=\"No newsletters found\"\n description=\"Create your first newsletter to communicate with users\"\n />\n }\n />\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"CommunicationsPage.js","names":[],"sources":["../../../src/admin/pages/CommunicationsPage.tsx"],"sourcesContent":["import { Mail, Plus, Send, Users, Eye, MousePointer2 } from 'lucide-react';\nimport {\n PageLayout,\n Button,\n DataTable,\n Badge,\n MetricCard,\n MetricsGrid,\n EmptyState,\n} from '../components';\nimport {\n useListNewslettersQuery,\n useGetNewsletterStatsQuery,\n useSendNewsletterMutation,\n} from '../../generated/global-operations';\nimport { formatDateTime, formatPercentage } from '../utils/formatters';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\n\nexport function CommunicationsPage() {\n const { data: statsData, loading: statsLoading } = useGetNewsletterStatsQuery();\n const {\n data: listData,\n loading: listLoading,\n refetch,\n } = useListNewslettersQuery({\n variables: { limit: 50 },\n });\n\n const [sendNewsletter] = useSendNewsletterMutation();\n\n const stats = statsData?.newsletterStats;\n const newsletters = listData?.newsletters || [];\n\n const handleSend = async (id: string) => {\n if (confirm('Are you sure you want to send this newsletter to all subscribers?')) {\n try {\n await sendNewsletter({ variables: { id } });\n refetch();\n } catch (err) {\n console.error('Failed to send newsletter:', err);\n }\n }\n };\n\n const columns = [\n {\n key: 'title',\n header: 'Title',\n render: (news: any) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium text-text-primary\">{news.title}</span>\n <span className=\"text-xs text-text-secondary\">{news.subject}</span>\n </div>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n render: (news: any) => {\n const variants: Record<\n string,\n 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline'\n > = {\n SENT: 'success',\n SCHEDULED: 'warning',\n DRAFT: 'secondary',\n };\n return <Badge variant={variants[news.status] || 'outline'}>{news.status}</Badge>;\n },\n },\n {\n key: 'sentAt',\n header: 'Sent Date',\n render: (news: any) => (news.sentAt ? formatDateTime(news.sentAt) : '-'),\n },\n {\n key: 'actions',\n header: '',\n align: 'right' as const,\n render: (news: any) => (\n <div className=\"flex justify-end gap-2\">\n {news.status === 'DRAFT' && (\n <Button size=\"sm\" onClick={() => handleSend(news.id)}>\n <Send className=\"mr-2 h-3 w-3\" />\n Send Now\n </Button>\n )}\n </div>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Communications\"\n gradientWord=\"Communications\"\n description=\"Manage platform newsletters and system notifications\"\n actions={\n <Button>\n <Plus className=\"mr-2 h-4 w-4\" />\n Create Newsletter\n </Button>\n }\n >\n <div className=\"space-y-8\">\n <PagePurpose>\n Reach your users directly. Draft newsletters, send them to your subscriber base, and\n track how many people opened and clicked — use it to announce launches, share product\n updates, or run a re-engagement campaign.\n </PagePurpose>\n\n <MetricsGrid>\n <MetricCard\n title=\"Total Subscribers\"\n hero\n value={stats?.totalSubscribers ?? 0}\n loading={statsLoading}\n icon={<Users className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Newsletters Sent\"\n value={stats?.totalSent ?? 0}\n loading={statsLoading}\n icon={<Send className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Avg. Open Rate\"\n value={formatPercentage(stats?.averageOpenRate ?? 0)}\n loading={statsLoading}\n icon={<Eye className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Avg. Click Rate\"\n value={formatPercentage(stats?.averageClickRate ?? 0)}\n loading={statsLoading}\n icon={<MousePointer2 className=\"h-4 w-4\" />}\n />\n </MetricsGrid>\n\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">Newsletters</h2>\n <DataTable\n columns={columns}\n data={newsletters}\n keyExtractor={(n) => n.id}\n loading={listLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-notifications\"\n icon={<Mail className=\"h-8 w-8\" />}\n title=\"No newsletters found\"\n description=\"Create your first newsletter to communicate with users\"\n />\n }\n />\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAkBA,SAAgB,IAAqB;CACnC,IAAM,EAAE,MAAM,GAAW,SAAS,MAAiB,GAA4B,EACzE,EACJ,MAAM,GACN,SAAS,GACT,eACE,EAAwB,EAC1B,WAAW,EAAE,OAAO,IAAI,EACzB,CAAC,EAEI,CAAC,KAAkB,GAA2B,EAE9C,IAAQ,GAAW,iBACnB,IAAc,GAAU,eAAe,EAAE,EAEzC,IAAa,OAAO,MAAe;AACvC,MAAI,QAAQ,oEAAoE,CAC9E,KAAI;AAEF,GADA,MAAM,EAAe,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC,EAC3C,GAAS;WACF,GAAK;AACZ,WAAQ,MAAM,8BAA8B,EAAI;;;AAqDtD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,cAAa;EACb,aAAY;EACZ,SACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,oBAE1B,EAAA,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD,EAAA,UAAa,wNAIC,CAAA;IAEd,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD;MACE,OAAM;MACN,MAAA;MACA,OAAO,GAAO,oBAAoB;MAClC,SAAS;MACT,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAO,aAAa;MAC3B,SAAS;MACT,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;MAClC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAiB,GAAO,mBAAmB,EAAE;MACpD,SAAS;MACT,MAAM,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;MACjC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAiB,GAAO,oBAAoB,EAAE;MACrD,SAAS;MACT,MAAM,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;MAC3C,CAAA;KACU,EAAA,CAAA;IAEd,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAwB;MAAgB,CAAA,EACtD,kBAAC,GAAD;MACW,SAlGH;OACd;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MACP,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAiC,EAAK;UAAa,CAAA,EACnE,kBAAC,QAAD;UAAM,WAAU;oBAA+B,EAAK;UAAe,CAAA,CAC/D;;QAET;OACD;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MASA,kBAAC,GAAD;SAAO,SALV;UACF,MAAM;UACN,WAAW;UACX,OAAO;UACR,CAC+B,EAAK,WAAW;mBAAY,EAAK;SAAe,CAAA;QAEnF;OACD;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MAAe,EAAK,SAAS,EAAe,EAAK,OAAO,GAAG;QACrE;OACD;QACE,KAAK;QACL,QAAQ;QACR,OAAO;QACP,SAAS,MACP,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAK,WAAW,WACf,kBAAC,GAAD;UAAQ,MAAK;UAAK,eAAe,EAAW,EAAK,GAAG;oBAApD,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,WAE1B;;SAEP,CAAA;QAET;OACF;MAqDS,MAAM;MACN,eAAe,MAAM,EAAE;MACvB,SAAS;MACT,YACE,kBAAC,GAAD;OACE,cAAa;OACb,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;OAClC,OAAM;OACN,aAAY;OACZ,CAAA;MAEJ,CAAA,CACE;;IACF;;EACK,CAAA"}
|