@reeboot/strapi-payment-plugin 0.0.1 → 0.0.3

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 (84) hide show
  1. package/README.md +378 -119
  2. package/dist/_chunks/Analytics-DSJqY9ng.mjs +355 -0
  3. package/dist/_chunks/Analytics-nBSdLT2v.js +355 -0
  4. package/dist/_chunks/App-B83DZ9NG.js +70 -0
  5. package/dist/_chunks/App-BUSTbkyy.mjs +68 -0
  6. package/dist/_chunks/Customers-BpFzfglV.js +273 -0
  7. package/dist/_chunks/Customers-C6FH7-zG.mjs +273 -0
  8. package/dist/_chunks/Dashboard-CNMTzSyc.js +180 -0
  9. package/dist/_chunks/Dashboard-Dbwl0ZBo.mjs +180 -0
  10. package/dist/_chunks/Orders-CBkT2YfP.mjs +308 -0
  11. package/dist/_chunks/Orders-OG-pwV-B.js +308 -0
  12. package/dist/_chunks/Payments-BLen1P9N.js +489 -0
  13. package/dist/_chunks/Payments-DSDJ-HWm.mjs +489 -0
  14. package/dist/_chunks/Settings-Dq1xy32B.js +357 -0
  15. package/dist/_chunks/Settings-jmGslDsB.mjs +357 -0
  16. package/dist/_chunks/en-BJocyOVu.mjs +240 -0
  17. package/dist/_chunks/en-BkVAf_R4.js +240 -0
  18. package/dist/_chunks/index-BqqrpI6D.js +66 -0
  19. package/dist/_chunks/index-DS_PYNkf.mjs +67 -0
  20. package/dist/admin/index.js +2 -63
  21. package/dist/admin/index.mjs +2 -63
  22. package/dist/admin/src/components/AnalyticsChart.d.ts +19 -0
  23. package/dist/admin/src/components/CustomerList.d.ts +21 -0
  24. package/dist/admin/src/components/OrderList.d.ts +27 -0
  25. package/dist/admin/src/components/PaymentCard.d.ts +39 -0
  26. package/dist/admin/src/components/PaymentList.d.ts +19 -0
  27. package/dist/admin/src/components/RefundModal.d.ts +15 -0
  28. package/dist/admin/src/pages/Analytics.d.ts +2 -0
  29. package/dist/admin/src/pages/Customers.d.ts +2 -0
  30. package/dist/admin/src/pages/Dashboard.d.ts +2 -0
  31. package/dist/admin/src/pages/HomePage.d.ts +1 -1
  32. package/dist/admin/src/pages/Orders.d.ts +2 -0
  33. package/dist/admin/src/pages/Payments.d.ts +2 -0
  34. package/dist/admin/src/pages/Settings.d.ts +2 -0
  35. package/dist/admin/src/pluginId.d.ts +1 -1
  36. package/dist/server/index.js +1770 -992
  37. package/dist/server/index.mjs +1773 -995
  38. package/dist/server/src/bootstrap.d.ts +5 -11
  39. package/dist/server/src/config/index.d.ts +0 -10
  40. package/dist/server/src/content-types/customer/index.d.ts +69 -0
  41. package/dist/server/src/content-types/index.d.ts +123 -39
  42. package/dist/server/src/content-types/{product.d.ts → order/index.d.ts} +26 -19
  43. package/dist/server/src/content-types/{subscription.d.ts → payment/index.d.ts} +30 -21
  44. package/dist/server/src/controllers/controller.d.ts +5 -12
  45. package/dist/server/src/controllers/index.d.ts +29 -34
  46. package/dist/server/src/controllers/stripe.d.ts +104 -0
  47. package/dist/server/src/index.d.ts +179 -139
  48. package/dist/server/src/middlewares/index.d.ts +19 -1
  49. package/dist/server/src/policies/index.d.ts +3 -1
  50. package/dist/server/src/routes/{admin-routes.d.ts → admin/index.d.ts} +4 -4
  51. package/dist/server/src/routes/content-api/index.d.ts +21 -0
  52. package/dist/server/src/routes/index.d.ts +11 -16
  53. package/dist/server/src/services/index.d.ts +2 -38
  54. package/dist/server/src/services/{stripeDriver.d.ts → stripe.d.ts} +52 -59
  55. package/dist/server/src/types/index.d.ts +179 -0
  56. package/package.json +20 -25
  57. package/dist/_chunks/App-DD7GyuRr.mjs +0 -1424
  58. package/dist/_chunks/App-KZVBFRwo.js +0 -1424
  59. package/dist/_chunks/en-B4KWt_jN.js +0 -4
  60. package/dist/_chunks/en-Byx4XI2L.mjs +0 -4
  61. package/dist/admin/src/components/Header.d.ts +0 -2
  62. package/dist/admin/src/components/NavigationMenu.d.ts +0 -2
  63. package/dist/admin/src/components/Sidebar.d.ts +0 -2
  64. package/dist/admin/src/components/TransactionDetailsModal.d.ts +0 -18
  65. package/dist/admin/src/components/TransactionList.d.ts +0 -18
  66. package/dist/admin/src/pages/ConfigurationPage.d.ts +0 -2
  67. package/dist/admin/src/pages/DashboardPage.d.ts +0 -2
  68. package/dist/admin/src/pages/ProductsPage.d.ts +0 -2
  69. package/dist/admin/src/pages/SubscriptionsPage.d.ts +0 -2
  70. package/dist/admin/src/pages/TransactionsPage.d.ts +0 -2
  71. package/dist/server/src/controllers/product.d.ts +0 -18
  72. package/dist/server/src/controllers/subscription.d.ts +0 -16
  73. package/dist/server/src/controllers/webhook.d.ts +0 -10
  74. package/dist/server/src/routes/content-api.d.ts +0 -12
  75. package/dist/server/src/routes/product.d.ts +0 -2
  76. package/dist/server/src/routes/refund-routes.d.ts +0 -13
  77. package/dist/server/src/routes/subscription.d.ts +0 -5
  78. package/dist/server/src/routes/webhook.d.ts +0 -15
  79. package/dist/server/src/services/paypalDriver.d.ts +0 -47
  80. package/dist/server/src/services/product.d.ts +0 -7
  81. package/dist/server/src/services/service.d.ts +0 -26
  82. package/dist/server/src/services/subscription.d.ts +0 -9
  83. package/dist/server/src/services/sync.d.ts +0 -13
  84. package/jest.config.js +0 -13
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const designSystem = require("@strapi/design-system");
6
+ const reactIntl = require("react-intl");
7
+ const admin = require("@strapi/strapi/admin");
8
+ const index = require("./index-BqqrpI6D.js");
9
+ const Customers = () => {
10
+ const { formatMessage } = reactIntl.useIntl();
11
+ const { get } = admin.useFetchClient();
12
+ const [customers, setCustomers] = React.useState([]);
13
+ const [filteredCustomers, setFilteredCustomers] = React.useState([]);
14
+ const [loading, setLoading] = React.useState(true);
15
+ const [error, setError] = React.useState(null);
16
+ const [currentPage, setCurrentPage] = React.useState(1);
17
+ const [pageSize, setPageSize] = React.useState(25);
18
+ const [totalCount, setTotalCount] = React.useState(0);
19
+ const [filters, setFilters] = React.useState({
20
+ search: "",
21
+ country: "",
22
+ isActive: "",
23
+ registrationDateRange: { from: null, to: null }
24
+ });
25
+ React.useEffect(() => {
26
+ fetchCustomers();
27
+ }, [currentPage, pageSize, filters]);
28
+ React.useEffect(() => {
29
+ applyFilters();
30
+ }, [customers, filters]);
31
+ const fetchCustomers = async () => {
32
+ try {
33
+ setLoading(true);
34
+ const queryParams = new URLSearchParams({
35
+ page: currentPage.toString(),
36
+ pageSize: pageSize.toString(),
37
+ ...filters.search && { email: filters.search }
38
+ });
39
+ const { data } = await get(`/${index.PLUGIN_ID}/admin/customers?${queryParams}`);
40
+ if (data.success && data.data) {
41
+ const formattedCustomers = data.data.map((c) => ({
42
+ id: c.documentId,
43
+ stripeCustomerId: c.stripe_customer_id,
44
+ email: c.email,
45
+ firstName: c.firstName,
46
+ lastName: c.lastName,
47
+ phone: c.phone,
48
+ totalSpent: c.totalSpent || 0,
49
+ currency: "usd",
50
+ paymentCount: c.paymentCount || 0,
51
+ registrationDate: c.createdAt,
52
+ isActive: true,
53
+ tags: []
54
+ }));
55
+ setCustomers(formattedCustomers);
56
+ setTotalCount(data.meta.pagination.total);
57
+ }
58
+ setLoading(false);
59
+ } catch (err) {
60
+ setError("Failed to fetch customers");
61
+ setLoading(false);
62
+ }
63
+ };
64
+ const applyFilters = () => {
65
+ let filtered = [...customers];
66
+ if (filters.search) {
67
+ const searchLower = filters.search.toLowerCase();
68
+ filtered = filtered.filter(
69
+ (customer) => customer.email.toLowerCase().includes(searchLower) || `${customer.firstName} ${customer.lastName}`.toLowerCase().includes(searchLower) || customer.stripeCustomerId.toLowerCase().includes(searchLower)
70
+ );
71
+ }
72
+ if (filters.isActive) {
73
+ const isActive = filters.isActive === "active";
74
+ filtered = filtered.filter((customer) => customer.isActive === isActive);
75
+ }
76
+ if (filters.country) {
77
+ filtered = filtered.filter(
78
+ (customer) => customer.address?.country === filters.country
79
+ );
80
+ }
81
+ if (filters.registrationDateRange.from) {
82
+ filtered = filtered.filter(
83
+ (customer) => new Date(customer.registrationDate) >= filters.registrationDateRange.from
84
+ );
85
+ }
86
+ if (filters.registrationDateRange.to) {
87
+ filtered = filtered.filter(
88
+ (customer) => new Date(customer.registrationDate) <= filters.registrationDateRange.to
89
+ );
90
+ }
91
+ setFilteredCustomers(filtered);
92
+ };
93
+ const formatCurrency = (amount, currency = "usd") => {
94
+ return new Intl.NumberFormat("en-US", {
95
+ style: "currency",
96
+ currency: currency.toUpperCase()
97
+ }).format(amount);
98
+ };
99
+ const formatDate = (dateString) => {
100
+ try {
101
+ return new Date(dateString).toLocaleDateString("en-US", {
102
+ year: "numeric",
103
+ month: "short",
104
+ day: "numeric"
105
+ });
106
+ } catch (e) {
107
+ return "Invalid Date";
108
+ }
109
+ };
110
+ const getCustomerStatusBadge = (isActive) => {
111
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { size: "S", backgroundColor: isActive ? "success" : "neutral", children: isActive ? "Active" : "Inactive" });
112
+ };
113
+ const handleFilterChange = (key, value) => {
114
+ setFilters((prev) => ({ ...prev, [key]: value }));
115
+ setCurrentPage(1);
116
+ };
117
+ const clearFilters = () => {
118
+ setFilters({
119
+ search: "",
120
+ country: "",
121
+ isActive: "",
122
+ registrationDateRange: { from: null, to: null }
123
+ });
124
+ setCurrentPage(1);
125
+ };
126
+ const exportCustomers = () => {
127
+ const csvContent = [
128
+ ["Customer ID", "Email", "First Name", "Last Name", "Total Spent", "Payment Count", "Last Payment", "Status", "Registration Date"],
129
+ ...filteredCustomers.map((customer) => [
130
+ customer.stripeCustomerId,
131
+ customer.email,
132
+ customer.firstName || "",
133
+ customer.lastName || "",
134
+ (customer.totalSpent / 100).toString(),
135
+ customer.paymentCount.toString(),
136
+ customer.lastPaymentDate ? formatDate(customer.lastPaymentDate) : "Never",
137
+ customer.isActive ? "Active" : "Inactive",
138
+ formatDate(customer.registrationDate)
139
+ ])
140
+ ].map((row) => row.join(",")).join("\n");
141
+ const blob = new Blob([csvContent], { type: "text/csv" });
142
+ const url = window.URL.createObjectURL(blob);
143
+ const a = document.createElement("a");
144
+ a.href = url;
145
+ a.download = `customers-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`;
146
+ a.click();
147
+ window.URL.revokeObjectURL(url);
148
+ };
149
+ const startIndex = (currentPage - 1) * pageSize;
150
+ const endIndex = startIndex + pageSize;
151
+ const paginatedCustomers = filteredCustomers.slice(startIndex, endIndex);
152
+ const totalPages = Math.ceil(filteredCustomers.length / pageSize);
153
+ if (loading && customers.length === 0) {
154
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingLeft: 10, paddingRight: 10, paddingTop: 4, paddingBottom: 10, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "400px" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) }) });
155
+ }
156
+ if (error) {
157
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingLeft: 10, paddingRight: 10, paddingTop: 4, paddingBottom: 10, children: /* @__PURE__ */ jsxRuntime.jsx(
158
+ designSystem.EmptyStateLayout,
159
+ {
160
+ title: "Error loading customers",
161
+ subtitle: error,
162
+ action: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: fetchCustomers, children: formatMessage({ id: "payment-plugin.customers.retry", defaultMessage: "Retry" }) })
163
+ }
164
+ ) });
165
+ }
166
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { paddingLeft: 10, paddingRight: 10, paddingTop: 4, paddingBottom: 10, children: [
167
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
168
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", tag: "h1", children: formatMessage({ id: "payment-plugin.customers.title", defaultMessage: "Customers" }) }),
170
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: formatMessage({
171
+ id: "payment-plugin.customers.subtitle",
172
+ defaultMessage: "Manage and view customer information"
173
+ }) })
174
+ ] }),
175
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
176
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "secondary", onClick: exportCustomers, children: formatMessage({ id: "payment-plugin.customers.export", defaultMessage: "Export" }) }),
177
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { onClick: () => window.location.reload(), children: formatMessage({ id: "payment-plugin.customers.refresh", defaultMessage: "Refresh" }) })
178
+ ] })
179
+ ] }) }),
180
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Card, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 4, children: [
181
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
182
+ designSystem.TextInput,
183
+ {
184
+ placeholder: formatMessage({
185
+ id: "payment-plugin.customers.search.placeholder",
186
+ defaultMessage: "Search customers..."
187
+ }),
188
+ value: filters.search,
189
+ onChange: (e) => handleFilterChange("search", e.target.value)
190
+ }
191
+ ) }),
192
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(
193
+ designSystem.SingleSelect,
194
+ {
195
+ placeholder: formatMessage({
196
+ id: "payment-plugin.customers.status.placeholder",
197
+ defaultMessage: "All Statuses"
198
+ }),
199
+ value: filters.isActive,
200
+ onChange: (value) => handleFilterChange("isActive", value),
201
+ children: [
202
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "", children: formatMessage({ id: "payment-plugin.customers.all", defaultMessage: "All" }) }),
203
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "active", children: formatMessage({ id: "payment-plugin.customers.active", defaultMessage: "Active" }) }),
204
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "inactive", children: formatMessage({ id: "payment-plugin.customers.inactive", defaultMessage: "Inactive" }) })
205
+ ]
206
+ }
207
+ ) }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(
209
+ designSystem.SingleSelect,
210
+ {
211
+ placeholder: formatMessage({
212
+ id: "payment-plugin.customers.country.placeholder",
213
+ defaultMessage: "All Countries"
214
+ }),
215
+ value: filters.country,
216
+ onChange: (value) => handleFilterChange("country", value),
217
+ children: [
218
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "", children: formatMessage({ id: "payment-plugin.customers.all", defaultMessage: "All" }) }),
219
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "US", children: "United States" }),
220
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "CA", children: "Canada" }),
221
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "GB", children: "United Kingdom" }),
222
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "DE", children: "Germany" }),
223
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "FR", children: "France" })
224
+ ]
225
+ }
226
+ ) }),
227
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: clearFilters, children: formatMessage({ id: "payment-plugin.customers.clearFilters", defaultMessage: "Clear" }) }) }) })
228
+ ] }) }) }),
229
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, marginBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", children: formatMessage(
230
+ { id: "payment-plugin.customers.results", defaultMessage: "Showing {start}-{end} of {total} customers" },
231
+ {
232
+ start: startIndex + 1,
233
+ end: Math.min(endIndex, filteredCustomers.length),
234
+ total: filteredCustomers.length
235
+ }
236
+ ) }) }),
237
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Card, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { children: [
238
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
239
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.customer", defaultMessage: "Customer" }) }) }),
240
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.contact", defaultMessage: "Contact" }) }) }),
241
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.totalSpent", defaultMessage: "Total Spent" }) }) }),
242
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.payments", defaultMessage: "Payments" }) }) }),
243
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.lastPayment", defaultMessage: "Last Payment" }) }) }),
244
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.status", defaultMessage: "Status" }) }) }),
245
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.registration", defaultMessage: "Registered" }) }) })
246
+ ] }) }),
247
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: paginatedCustomers.map((customer) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
248
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
249
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", fontWeight: "bold", children: customer.firstName && customer.lastName ? `${customer.firstName} ${customer.lastName}` : customer.email }),
250
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", variant: "epsilon", fontFamily: "monospace", children: customer.stripeCustomerId })
251
+ ] }) }),
252
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
253
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: customer.email }),
254
+ customer.phone && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", variant: "epsilon", children: customer.phone })
255
+ ] }) }),
256
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", fontWeight: "bold", children: formatCurrency(customer.totalSpent, customer.currency) }) }),
257
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: customer.paymentCount }) }),
258
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: customer.lastPaymentDate ? formatDate(customer.lastPaymentDate) : "Never" }) }),
259
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: getCustomerStatusBadge(customer.isActive) }),
260
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: formatDate(customer.registrationDate) }) })
261
+ ] }, customer.id)) })
262
+ ] }) }) }),
263
+ totalPages > 1 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 4, children: /* @__PURE__ */ jsxRuntime.jsx(
264
+ designSystem.Pagination,
265
+ {
266
+ currentPage,
267
+ pageCount: totalPages,
268
+ onPageChange: setCurrentPage
269
+ }
270
+ ) })
271
+ ] });
272
+ };
273
+ exports.Customers = Customers;
@@ -0,0 +1,273 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Box, Flex, Loader, EmptyStateLayout, Button, Typography, Card, Grid, TextInput, SingleSelect, SingleSelectOption, Table, Thead, Tr, Th, Tbody, Td, Pagination, Badge } from "@strapi/design-system";
4
+ import { useIntl } from "react-intl";
5
+ import { useFetchClient } from "@strapi/strapi/admin";
6
+ import { P as PLUGIN_ID } from "./index-DS_PYNkf.mjs";
7
+ const Customers = () => {
8
+ const { formatMessage } = useIntl();
9
+ const { get } = useFetchClient();
10
+ const [customers, setCustomers] = useState([]);
11
+ const [filteredCustomers, setFilteredCustomers] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const [currentPage, setCurrentPage] = useState(1);
15
+ const [pageSize, setPageSize] = useState(25);
16
+ const [totalCount, setTotalCount] = useState(0);
17
+ const [filters, setFilters] = useState({
18
+ search: "",
19
+ country: "",
20
+ isActive: "",
21
+ registrationDateRange: { from: null, to: null }
22
+ });
23
+ useEffect(() => {
24
+ fetchCustomers();
25
+ }, [currentPage, pageSize, filters]);
26
+ useEffect(() => {
27
+ applyFilters();
28
+ }, [customers, filters]);
29
+ const fetchCustomers = async () => {
30
+ try {
31
+ setLoading(true);
32
+ const queryParams = new URLSearchParams({
33
+ page: currentPage.toString(),
34
+ pageSize: pageSize.toString(),
35
+ ...filters.search && { email: filters.search }
36
+ });
37
+ const { data } = await get(`/${PLUGIN_ID}/admin/customers?${queryParams}`);
38
+ if (data.success && data.data) {
39
+ const formattedCustomers = data.data.map((c) => ({
40
+ id: c.documentId,
41
+ stripeCustomerId: c.stripe_customer_id,
42
+ email: c.email,
43
+ firstName: c.firstName,
44
+ lastName: c.lastName,
45
+ phone: c.phone,
46
+ totalSpent: c.totalSpent || 0,
47
+ currency: "usd",
48
+ paymentCount: c.paymentCount || 0,
49
+ registrationDate: c.createdAt,
50
+ isActive: true,
51
+ tags: []
52
+ }));
53
+ setCustomers(formattedCustomers);
54
+ setTotalCount(data.meta.pagination.total);
55
+ }
56
+ setLoading(false);
57
+ } catch (err) {
58
+ setError("Failed to fetch customers");
59
+ setLoading(false);
60
+ }
61
+ };
62
+ const applyFilters = () => {
63
+ let filtered = [...customers];
64
+ if (filters.search) {
65
+ const searchLower = filters.search.toLowerCase();
66
+ filtered = filtered.filter(
67
+ (customer) => customer.email.toLowerCase().includes(searchLower) || `${customer.firstName} ${customer.lastName}`.toLowerCase().includes(searchLower) || customer.stripeCustomerId.toLowerCase().includes(searchLower)
68
+ );
69
+ }
70
+ if (filters.isActive) {
71
+ const isActive = filters.isActive === "active";
72
+ filtered = filtered.filter((customer) => customer.isActive === isActive);
73
+ }
74
+ if (filters.country) {
75
+ filtered = filtered.filter(
76
+ (customer) => customer.address?.country === filters.country
77
+ );
78
+ }
79
+ if (filters.registrationDateRange.from) {
80
+ filtered = filtered.filter(
81
+ (customer) => new Date(customer.registrationDate) >= filters.registrationDateRange.from
82
+ );
83
+ }
84
+ if (filters.registrationDateRange.to) {
85
+ filtered = filtered.filter(
86
+ (customer) => new Date(customer.registrationDate) <= filters.registrationDateRange.to
87
+ );
88
+ }
89
+ setFilteredCustomers(filtered);
90
+ };
91
+ const formatCurrency = (amount, currency = "usd") => {
92
+ return new Intl.NumberFormat("en-US", {
93
+ style: "currency",
94
+ currency: currency.toUpperCase()
95
+ }).format(amount);
96
+ };
97
+ const formatDate = (dateString) => {
98
+ try {
99
+ return new Date(dateString).toLocaleDateString("en-US", {
100
+ year: "numeric",
101
+ month: "short",
102
+ day: "numeric"
103
+ });
104
+ } catch (e) {
105
+ return "Invalid Date";
106
+ }
107
+ };
108
+ const getCustomerStatusBadge = (isActive) => {
109
+ return /* @__PURE__ */ jsx(Badge, { size: "S", backgroundColor: isActive ? "success" : "neutral", children: isActive ? "Active" : "Inactive" });
110
+ };
111
+ const handleFilterChange = (key, value) => {
112
+ setFilters((prev) => ({ ...prev, [key]: value }));
113
+ setCurrentPage(1);
114
+ };
115
+ const clearFilters = () => {
116
+ setFilters({
117
+ search: "",
118
+ country: "",
119
+ isActive: "",
120
+ registrationDateRange: { from: null, to: null }
121
+ });
122
+ setCurrentPage(1);
123
+ };
124
+ const exportCustomers = () => {
125
+ const csvContent = [
126
+ ["Customer ID", "Email", "First Name", "Last Name", "Total Spent", "Payment Count", "Last Payment", "Status", "Registration Date"],
127
+ ...filteredCustomers.map((customer) => [
128
+ customer.stripeCustomerId,
129
+ customer.email,
130
+ customer.firstName || "",
131
+ customer.lastName || "",
132
+ (customer.totalSpent / 100).toString(),
133
+ customer.paymentCount.toString(),
134
+ customer.lastPaymentDate ? formatDate(customer.lastPaymentDate) : "Never",
135
+ customer.isActive ? "Active" : "Inactive",
136
+ formatDate(customer.registrationDate)
137
+ ])
138
+ ].map((row) => row.join(",")).join("\n");
139
+ const blob = new Blob([csvContent], { type: "text/csv" });
140
+ const url = window.URL.createObjectURL(blob);
141
+ const a = document.createElement("a");
142
+ a.href = url;
143
+ a.download = `customers-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.csv`;
144
+ a.click();
145
+ window.URL.revokeObjectURL(url);
146
+ };
147
+ const startIndex = (currentPage - 1) * pageSize;
148
+ const endIndex = startIndex + pageSize;
149
+ const paginatedCustomers = filteredCustomers.slice(startIndex, endIndex);
150
+ const totalPages = Math.ceil(filteredCustomers.length / pageSize);
151
+ if (loading && customers.length === 0) {
152
+ return /* @__PURE__ */ jsx(Box, { paddingLeft: 10, paddingRight: 10, paddingTop: 4, paddingBottom: 10, children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", alignItems: "center", style: { minHeight: "400px" }, children: /* @__PURE__ */ jsx(Loader, {}) }) });
153
+ }
154
+ if (error) {
155
+ return /* @__PURE__ */ jsx(Box, { paddingLeft: 10, paddingRight: 10, paddingTop: 4, paddingBottom: 10, children: /* @__PURE__ */ jsx(
156
+ EmptyStateLayout,
157
+ {
158
+ title: "Error loading customers",
159
+ subtitle: error,
160
+ action: /* @__PURE__ */ jsx(Button, { onClick: fetchCustomers, children: formatMessage({ id: "payment-plugin.customers.retry", defaultMessage: "Retry" }) })
161
+ }
162
+ ) });
163
+ }
164
+ return /* @__PURE__ */ jsxs(Box, { paddingLeft: 10, paddingRight: 10, paddingTop: 4, paddingBottom: 10, children: [
165
+ /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
166
+ /* @__PURE__ */ jsxs(Box, { children: [
167
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", tag: "h1", children: formatMessage({ id: "payment-plugin.customers.title", defaultMessage: "Customers" }) }),
168
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: formatMessage({
169
+ id: "payment-plugin.customers.subtitle",
170
+ defaultMessage: "Manage and view customer information"
171
+ }) })
172
+ ] }),
173
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
174
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: exportCustomers, children: formatMessage({ id: "payment-plugin.customers.export", defaultMessage: "Export" }) }),
175
+ /* @__PURE__ */ jsx(Button, { onClick: () => window.location.reload(), children: formatMessage({ id: "payment-plugin.customers.refresh", defaultMessage: "Refresh" }) })
176
+ ] })
177
+ ] }) }),
178
+ /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsxs(Grid.Root, { gap: 4, children: [
179
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, children: /* @__PURE__ */ jsx(
180
+ TextInput,
181
+ {
182
+ placeholder: formatMessage({
183
+ id: "payment-plugin.customers.search.placeholder",
184
+ defaultMessage: "Search customers..."
185
+ }),
186
+ value: filters.search,
187
+ onChange: (e) => handleFilterChange("search", e.target.value)
188
+ }
189
+ ) }),
190
+ /* @__PURE__ */ jsx(Grid.Item, { col: 2, children: /* @__PURE__ */ jsxs(
191
+ SingleSelect,
192
+ {
193
+ placeholder: formatMessage({
194
+ id: "payment-plugin.customers.status.placeholder",
195
+ defaultMessage: "All Statuses"
196
+ }),
197
+ value: filters.isActive,
198
+ onChange: (value) => handleFilterChange("isActive", value),
199
+ children: [
200
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "", children: formatMessage({ id: "payment-plugin.customers.all", defaultMessage: "All" }) }),
201
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "active", children: formatMessage({ id: "payment-plugin.customers.active", defaultMessage: "Active" }) }),
202
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "inactive", children: formatMessage({ id: "payment-plugin.customers.inactive", defaultMessage: "Inactive" }) })
203
+ ]
204
+ }
205
+ ) }),
206
+ /* @__PURE__ */ jsx(Grid.Item, { col: 2, children: /* @__PURE__ */ jsxs(
207
+ SingleSelect,
208
+ {
209
+ placeholder: formatMessage({
210
+ id: "payment-plugin.customers.country.placeholder",
211
+ defaultMessage: "All Countries"
212
+ }),
213
+ value: filters.country,
214
+ onChange: (value) => handleFilterChange("country", value),
215
+ children: [
216
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "", children: formatMessage({ id: "payment-plugin.customers.all", defaultMessage: "All" }) }),
217
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "US", children: "United States" }),
218
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "CA", children: "Canada" }),
219
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "GB", children: "United Kingdom" }),
220
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "DE", children: "Germany" }),
221
+ /* @__PURE__ */ jsx(SingleSelectOption, { value: "FR", children: "France" })
222
+ ]
223
+ }
224
+ ) }),
225
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, children: /* @__PURE__ */ jsx(Flex, { gap: 2, children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: clearFilters, children: formatMessage({ id: "payment-plugin.customers.clearFilters", defaultMessage: "Clear" }) }) }) })
226
+ ] }) }) }),
227
+ /* @__PURE__ */ jsx(Box, { marginTop: 4, marginBottom: 2, children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", children: formatMessage(
228
+ { id: "payment-plugin.customers.results", defaultMessage: "Showing {start}-{end} of {total} customers" },
229
+ {
230
+ start: startIndex + 1,
231
+ end: Math.min(endIndex, filteredCustomers.length),
232
+ total: filteredCustomers.length
233
+ }
234
+ ) }) }),
235
+ /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(Box, { padding: 4, children: /* @__PURE__ */ jsxs(Table, { children: [
236
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
237
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.customer", defaultMessage: "Customer" }) }) }),
238
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.contact", defaultMessage: "Contact" }) }) }),
239
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.totalSpent", defaultMessage: "Total Spent" }) }) }),
240
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.payments", defaultMessage: "Payments" }) }) }),
241
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.lastPayment", defaultMessage: "Last Payment" }) }) }),
242
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.status", defaultMessage: "Status" }) }) }),
243
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "epsilon", children: formatMessage({ id: "payment-plugin.customers.registration", defaultMessage: "Registered" }) }) })
244
+ ] }) }),
245
+ /* @__PURE__ */ jsx(Tbody, { children: paginatedCustomers.map((customer) => /* @__PURE__ */ jsxs(Tr, { children: [
246
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Box, { children: [
247
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", fontWeight: "bold", children: customer.firstName && customer.lastName ? `${customer.firstName} ${customer.lastName}` : customer.email }),
248
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", fontFamily: "monospace", children: customer.stripeCustomerId })
249
+ ] }) }),
250
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Box, { children: [
251
+ /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", children: customer.email }),
252
+ customer.phone && /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", variant: "epsilon", children: customer.phone })
253
+ ] }) }),
254
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral800", fontWeight: "bold", children: formatCurrency(customer.totalSpent, customer.currency) }) }),
255
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: customer.paymentCount }) }),
256
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: customer.lastPaymentDate ? formatDate(customer.lastPaymentDate) : "Never" }) }),
257
+ /* @__PURE__ */ jsx(Td, { children: getCustomerStatusBadge(customer.isActive) }),
258
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: formatDate(customer.registrationDate) }) })
259
+ ] }, customer.id)) })
260
+ ] }) }) }),
261
+ totalPages > 1 && /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsx(
262
+ Pagination,
263
+ {
264
+ currentPage,
265
+ pageCount: totalPages,
266
+ onPageChange: setCurrentPage
267
+ }
268
+ ) })
269
+ ] });
270
+ };
271
+ export {
272
+ Customers
273
+ };