@burdenoff/microfe-store 2026.915.1 → 2026.915.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/StoreAdminRoutes.js +17 -7
- package/dist/StoreAdminRoutes.js.map +1 -1
- package/dist/pages/AdminFulfillmentLogsPage.d.ts +9 -0
- package/dist/pages/AdminFulfillmentLogsPage.js +278 -0
- package/dist/pages/AdminFulfillmentLogsPage.js.map +1 -0
- package/dist/pages/AdminOrdersPage.js +134 -124
- package/dist/pages/AdminOrdersPage.js.map +1 -1
- package/package.json +1 -1
package/dist/StoreAdminRoutes.js
CHANGED
|
@@ -5,8 +5,8 @@ import { Suspense as i, lazy as a } from "react";
|
|
|
5
5
|
import { Navigate as o, Route as s, Routes as c } from "react-router";
|
|
6
6
|
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
7
7
|
//#region src/StoreAdminRoutes.tsx
|
|
8
|
-
var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default: e.AdminDashboardPage }))), f = a(() => import("./pages/AdminSubmissionsQueuePage.js").then((e) => ({ default: e.AdminSubmissionsQueuePage }))), p = a(() => import("./pages/AdminStoresPage.js").then((e) => ({ default: e.AdminStoresPage }))), m = a(() => import("./pages/AdminReviewModerationPage.js").then((e) => ({ default: e.AdminReviewModerationPage }))), h = a(() => import("./pages/AdminOrdersPage.js").then((e) => ({ default: e.AdminOrdersPage }))), g = () => {
|
|
9
|
-
let { hasPermission: a } = t(),
|
|
8
|
+
var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default: e.AdminDashboardPage }))), f = a(() => import("./pages/AdminSubmissionsQueuePage.js").then((e) => ({ default: e.AdminSubmissionsQueuePage }))), p = a(() => import("./pages/AdminStoresPage.js").then((e) => ({ default: e.AdminStoresPage }))), m = a(() => import("./pages/AdminReviewModerationPage.js").then((e) => ({ default: e.AdminReviewModerationPage }))), h = a(() => import("./pages/AdminOrdersPage.js").then((e) => ({ default: e.AdminOrdersPage }))), g = a(() => import("./pages/AdminFulfillmentLogsPage.js").then((e) => ({ default: e.AdminFulfillmentLogsPage }))), _ = () => {
|
|
9
|
+
let { hasPermission: a } = t(), _ = a(e.REVIEW_ADMIN) || a(e.PRODUCT_ADMIN);
|
|
10
10
|
return /* @__PURE__ */ u(c, { children: [
|
|
11
11
|
/* @__PURE__ */ l(s, {
|
|
12
12
|
index: !0,
|
|
@@ -18,7 +18,7 @@ var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default:
|
|
|
18
18
|
/* @__PURE__ */ l(s, {
|
|
19
19
|
path: "dashboard",
|
|
20
20
|
element: /* @__PURE__ */ l(n, {
|
|
21
|
-
allowed:
|
|
21
|
+
allowed: _,
|
|
22
22
|
children: /* @__PURE__ */ l(i, {
|
|
23
23
|
fallback: /* @__PURE__ */ l(r, {}),
|
|
24
24
|
children: /* @__PURE__ */ l(d, {})
|
|
@@ -28,7 +28,7 @@ var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default:
|
|
|
28
28
|
/* @__PURE__ */ l(s, {
|
|
29
29
|
path: "submissions",
|
|
30
30
|
element: /* @__PURE__ */ l(n, {
|
|
31
|
-
allowed:
|
|
31
|
+
allowed: _,
|
|
32
32
|
children: /* @__PURE__ */ l(i, {
|
|
33
33
|
fallback: /* @__PURE__ */ l(r, {}),
|
|
34
34
|
children: /* @__PURE__ */ l(f, {})
|
|
@@ -38,7 +38,7 @@ var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default:
|
|
|
38
38
|
/* @__PURE__ */ l(s, {
|
|
39
39
|
path: "stores",
|
|
40
40
|
element: /* @__PURE__ */ l(n, {
|
|
41
|
-
allowed:
|
|
41
|
+
allowed: _,
|
|
42
42
|
children: /* @__PURE__ */ l(i, {
|
|
43
43
|
fallback: /* @__PURE__ */ l(r, {}),
|
|
44
44
|
children: /* @__PURE__ */ l(p, {})
|
|
@@ -48,13 +48,23 @@ var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default:
|
|
|
48
48
|
/* @__PURE__ */ l(s, {
|
|
49
49
|
path: "orders",
|
|
50
50
|
element: /* @__PURE__ */ l(n, {
|
|
51
|
-
allowed:
|
|
51
|
+
allowed: _,
|
|
52
52
|
children: /* @__PURE__ */ l(i, {
|
|
53
53
|
fallback: /* @__PURE__ */ l(r, {}),
|
|
54
54
|
children: /* @__PURE__ */ l(h, {})
|
|
55
55
|
})
|
|
56
56
|
})
|
|
57
57
|
}),
|
|
58
|
+
/* @__PURE__ */ l(s, {
|
|
59
|
+
path: "fulfillment-logs",
|
|
60
|
+
element: /* @__PURE__ */ l(n, {
|
|
61
|
+
allowed: _,
|
|
62
|
+
children: /* @__PURE__ */ l(i, {
|
|
63
|
+
fallback: /* @__PURE__ */ l(r, {}),
|
|
64
|
+
children: /* @__PURE__ */ l(g, {})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}),
|
|
58
68
|
/* @__PURE__ */ l(s, {
|
|
59
69
|
path: "reviews",
|
|
60
70
|
element: /* @__PURE__ */ l(n, {
|
|
@@ -75,6 +85,6 @@ var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default:
|
|
|
75
85
|
] });
|
|
76
86
|
};
|
|
77
87
|
//#endregion
|
|
78
|
-
export {
|
|
88
|
+
export { _ as StoreAdminRoutes };
|
|
79
89
|
|
|
80
90
|
//# sourceMappingURL=StoreAdminRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StoreAdminRoutes.js","names":[],"sources":["../src/StoreAdminRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, type FC } from 'react';\nimport { Routes, Route, Navigate } from 'react-router';\nimport { useStorePermissions, STORE_PERMISSIONS } from './hooks/useStorePermissions';\nimport { PageLoadingFallback, PermissionGuard } from './components/RouteGuards';\n\nconst AdminDashboardPage = lazy(() =>\n import('./pages/AdminDashboardPage').then((m) => ({\n default: m.AdminDashboardPage,\n }))\n);\n\nconst AdminSubmissionsQueuePage = lazy(() =>\n import('./pages/AdminSubmissionsQueuePage').then((m) => ({\n default: m.AdminSubmissionsQueuePage,\n }))\n);\n\nconst AdminStoresPage = lazy(() =>\n import('./pages/AdminStoresPage').then((m) => ({\n default: m.AdminStoresPage,\n }))\n);\n\nconst AdminReviewModerationPage = lazy(() =>\n import('./pages/AdminReviewModerationPage').then((m) => ({\n default: m.AdminReviewModerationPage,\n }))\n);\n\nconst AdminOrdersPage = lazy(() =>\n import('./pages/AdminOrdersPage').then((m) => ({\n default: m.AdminOrdersPage,\n }))\n);\n\n/**\n * StoreAdminRoutes component\n *\n * Admin-only routing for the Store microfrontend.\n * Mounted by the admin app shell via StoreAdminRoot.\n */\nexport const StoreAdminRoutes: FC = () => {\n const { hasPermission } = useStorePermissions();\n const isAdmin =\n hasPermission(STORE_PERMISSIONS.REVIEW_ADMIN) || hasPermission(STORE_PERMISSIONS.PRODUCT_ADMIN);\n\n return (\n <Routes>\n {/* Default redirect to dashboard */}\n <Route index element={<Navigate to=\"dashboard\" replace />} />\n\n <Route\n path=\"dashboard\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminDashboardPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"submissions\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminSubmissionsQueuePage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"stores\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminStoresPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"orders\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminOrdersPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"reviews\"\n element={\n <PermissionGuard allowed={hasPermission(STORE_PERMISSIONS.REVIEW_ADMIN)}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminReviewModerationPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n {/* Catch-all redirect */}\n <Route path=\"*\" element={<Navigate to=\"dashboard\" replace />} />\n </Routes>\n );\n};\n"],"mappings":";;;;;;;AAKA,IAAM,IAAqB,QACzB,OAAO,iCAA8B,MAAM,OAAO,EAChD,SAAS,EAAE,oBACZ,EAAE,CACJ,EAEK,IAA4B,QAChC,OAAO,wCAAqC,MAAM,OAAO,EACvD,SAAS,EAAE,2BACZ,EAAE,CACJ,EAEK,IAAkB,QACtB,OAAO,8BAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,iBACZ,EAAE,CACJ,EAEK,IAA4B,QAChC,OAAO,wCAAqC,MAAM,OAAO,EACvD,SAAS,EAAE,2BACZ,EAAE,CACJ,EAEK,IAAkB,QACtB,OAAO,8BAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,iBACZ,EAAE,CACJ,EAQY,UAA6B;CACxC,IAAM,EAAE,qBAAkB,GAAqB,EACzC,IACJ,EAAc,EAAkB,aAAa,IAAI,EAAc,EAAkB,cAAc;AAEjG,QACE,kBAAC,GAAD,EAAA,UAAA;EAEE,kBAAC,GAAD;GAAO,OAAA;GAAM,SAAS,kBAAC,GAAD;IAAU,IAAG;IAAY,SAAA;IAAU,CAAA;GAAI,CAAA;EAE7D,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAAsB,CAAA;KACb,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAA6B,CAAA;KACpB,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAAmB,CAAA;KACV,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAAmB,CAAA;KACV,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS,EAAc,EAAkB,aAAa;cACrE,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAA6B,CAAA;KACpB,CAAA;IACK,CAAA;GAEpB,CAAA;EAGF,kBAAC,GAAD;GAAO,MAAK;GAAI,SAAS,kBAAC,GAAD;IAAU,IAAG;IAAY,SAAA;IAAU,CAAA;GAAI,CAAA;EACzD,EAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"StoreAdminRoutes.js","names":[],"sources":["../src/StoreAdminRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, type FC } from 'react';\nimport { Routes, Route, Navigate } from 'react-router';\nimport { useStorePermissions, STORE_PERMISSIONS } from './hooks/useStorePermissions';\nimport { PageLoadingFallback, PermissionGuard } from './components/RouteGuards';\n\nconst AdminDashboardPage = lazy(() =>\n import('./pages/AdminDashboardPage').then((m) => ({\n default: m.AdminDashboardPage,\n }))\n);\n\nconst AdminSubmissionsQueuePage = lazy(() =>\n import('./pages/AdminSubmissionsQueuePage').then((m) => ({\n default: m.AdminSubmissionsQueuePage,\n }))\n);\n\nconst AdminStoresPage = lazy(() =>\n import('./pages/AdminStoresPage').then((m) => ({\n default: m.AdminStoresPage,\n }))\n);\n\nconst AdminReviewModerationPage = lazy(() =>\n import('./pages/AdminReviewModerationPage').then((m) => ({\n default: m.AdminReviewModerationPage,\n }))\n);\n\nconst AdminOrdersPage = lazy(() =>\n import('./pages/AdminOrdersPage').then((m) => ({\n default: m.AdminOrdersPage,\n }))\n);\n\nconst AdminFulfillmentLogsPage = lazy(() =>\n import('./pages/AdminFulfillmentLogsPage').then((m) => ({\n default: m.AdminFulfillmentLogsPage,\n }))\n);\n\n/**\n * StoreAdminRoutes component\n *\n * Admin-only routing for the Store microfrontend.\n * Mounted by the admin app shell via StoreAdminRoot.\n */\nexport const StoreAdminRoutes: FC = () => {\n const { hasPermission } = useStorePermissions();\n const isAdmin =\n hasPermission(STORE_PERMISSIONS.REVIEW_ADMIN) || hasPermission(STORE_PERMISSIONS.PRODUCT_ADMIN);\n\n return (\n <Routes>\n {/* Default redirect to dashboard */}\n <Route index element={<Navigate to=\"dashboard\" replace />} />\n\n <Route\n path=\"dashboard\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminDashboardPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"submissions\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminSubmissionsQueuePage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"stores\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminStoresPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"orders\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminOrdersPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"fulfillment-logs\"\n element={\n <PermissionGuard allowed={isAdmin}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminFulfillmentLogsPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n <Route\n path=\"reviews\"\n element={\n <PermissionGuard allowed={hasPermission(STORE_PERMISSIONS.REVIEW_ADMIN)}>\n <Suspense fallback={<PageLoadingFallback />}>\n <AdminReviewModerationPage />\n </Suspense>\n </PermissionGuard>\n }\n />\n\n {/* Catch-all redirect */}\n <Route path=\"*\" element={<Navigate to=\"dashboard\" replace />} />\n </Routes>\n );\n};\n"],"mappings":";;;;;;;AAKA,IAAM,IAAqB,QACzB,OAAO,iCAA8B,MAAM,OAAO,EAChD,SAAS,EAAE,oBACZ,EAAE,CACJ,EAEK,IAA4B,QAChC,OAAO,wCAAqC,MAAM,OAAO,EACvD,SAAS,EAAE,2BACZ,EAAE,CACJ,EAEK,IAAkB,QACtB,OAAO,8BAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,iBACZ,EAAE,CACJ,EAEK,IAA4B,QAChC,OAAO,wCAAqC,MAAM,OAAO,EACvD,SAAS,EAAE,2BACZ,EAAE,CACJ,EAEK,IAAkB,QACtB,OAAO,8BAA2B,MAAM,OAAO,EAC7C,SAAS,EAAE,iBACZ,EAAE,CACJ,EAEK,IAA2B,QAC/B,OAAO,uCAAoC,MAAM,OAAO,EACtD,SAAS,EAAE,0BACZ,EAAE,CACJ,EAQY,UAA6B;CACxC,IAAM,EAAE,qBAAkB,GAAqB,EACzC,IACJ,EAAc,EAAkB,aAAa,IAAI,EAAc,EAAkB,cAAc;AAEjG,QACE,kBAAC,GAAD,EAAA,UAAA;EAEE,kBAAC,GAAD;GAAO,OAAA;GAAM,SAAS,kBAAC,GAAD;IAAU,IAAG;IAAY,SAAA;IAAU,CAAA;GAAI,CAAA;EAE7D,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAAsB,CAAA;KACb,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAA6B,CAAA;KACpB,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAAmB,CAAA;KACV,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAAmB,CAAA;KACV,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS;cACxB,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAA4B,CAAA;KACnB,CAAA;IACK,CAAA;GAEpB,CAAA;EAEF,kBAAC,GAAD;GACE,MAAK;GACL,SACE,kBAAC,GAAD;IAAiB,SAAS,EAAc,EAAkB,aAAa;cACrE,kBAAC,GAAD;KAAU,UAAU,kBAAC,GAAD,EAAuB,CAAA;eACzC,kBAAC,GAAD,EAA6B,CAAA;KACpB,CAAA;IACK,CAAA;GAEpB,CAAA;EAGF,kBAAC,GAAD;GAAO,MAAK;GAAI,SAAS,kBAAC,GAAD;IAAU,IAAG;IAAY,SAAA;IAAU,CAAA;GAAI,CAAA;EACzD,EAAA,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Admin-only log of every carrier shipment-creation attempt (every Temporal
|
|
5
|
+
* retry, not just the last failure) — the "why did this order's shipment
|
|
6
|
+
* fail" debugging surface that OrderFulfillment.failureReason alone can't
|
|
7
|
+
* answer, since it only ever holds the last attempt's message.
|
|
8
|
+
*/
|
|
9
|
+
export declare const AdminFulfillmentLogsPage: FC;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { useStore as e } from "../providers/StoreProvider.js";
|
|
2
|
+
import { useCallback as t, useEffect as n, useState as r } from "react";
|
|
3
|
+
import { useNavigate as i, useSearchParams as a } from "react-router";
|
|
4
|
+
import { ArrowLeft as o, CheckCircle2 as s, RefreshCw as c, Truck as l, XCircle as u } from "lucide-react";
|
|
5
|
+
import { graphqlFetch as d } from "@burdenoff/fe-libs/shared/graphql";
|
|
6
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
7
|
+
import { IllustratedEmptyState as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/pages/AdminFulfillmentLogsPage.tsx
|
|
9
|
+
var _ = [
|
|
10
|
+
{
|
|
11
|
+
value: "",
|
|
12
|
+
label: "All results"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
value: "false",
|
|
16
|
+
label: "Failed only"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: "true",
|
|
20
|
+
label: "Succeeded only"
|
|
21
|
+
}
|
|
22
|
+
], v = "\n query AdminOrderFulfillmentAttempts(\n $limit: Int!\n $offset: Int!\n $orderId: ID\n $carrier: String\n $succeeded: Boolean\n ) {\n orderFulfillmentAttempts(\n limit: $limit\n offset: $offset\n orderId: $orderId\n carrier: $carrier\n succeeded: $succeeded\n ) {\n totalCount\n attempts {\n id\n attemptNumber\n carrier\n succeeded\n errorMessage\n errorStatusCode\n durationMs\n occurredAt\n }\n }\n }\n", y = 25;
|
|
23
|
+
function b(e) {
|
|
24
|
+
return new Date(e).toLocaleString("en-US", {
|
|
25
|
+
year: "numeric",
|
|
26
|
+
month: "short",
|
|
27
|
+
day: "numeric",
|
|
28
|
+
hour: "2-digit",
|
|
29
|
+
minute: "2-digit",
|
|
30
|
+
second: "2-digit"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
var x = () => {
|
|
34
|
+
let x = i(), { authToken: S, basePath: C } = e(), [w] = a(), [T, E] = r([]), [D, O] = r(0), [k, A] = r(!1), [j, M] = r(null), [N, P] = r(0), [F, I] = r(""), [L, R] = r(""), [z, B] = r(w.get("orderId") ?? ""), V = t(async () => {
|
|
35
|
+
A(!0), M(null);
|
|
36
|
+
try {
|
|
37
|
+
let e = await d({
|
|
38
|
+
gateway: "global",
|
|
39
|
+
query: v,
|
|
40
|
+
variables: {
|
|
41
|
+
limit: y,
|
|
42
|
+
offset: N * y,
|
|
43
|
+
orderId: z.trim() || void 0,
|
|
44
|
+
carrier: L.trim() || void 0,
|
|
45
|
+
succeeded: F === "" ? void 0 : F === "true"
|
|
46
|
+
},
|
|
47
|
+
authToken: S || void 0
|
|
48
|
+
});
|
|
49
|
+
if (e.errors?.length) throw Error(e.errors[0]?.message ?? "Failed to load fulfillment logs");
|
|
50
|
+
O(e.data?.orderFulfillmentAttempts.totalCount ?? 0), E(e.data?.orderFulfillmentAttempts.attempts ?? []);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
M(e instanceof Error ? e.message : "Failed to load fulfillment logs");
|
|
53
|
+
} finally {
|
|
54
|
+
A(!1);
|
|
55
|
+
}
|
|
56
|
+
}, [
|
|
57
|
+
S,
|
|
58
|
+
N,
|
|
59
|
+
z,
|
|
60
|
+
L,
|
|
61
|
+
F
|
|
62
|
+
]);
|
|
63
|
+
return n(() => {
|
|
64
|
+
V();
|
|
65
|
+
}, [V]), /* @__PURE__ */ m("div", {
|
|
66
|
+
className: "min-h-screen bg-surface-default",
|
|
67
|
+
children: [/* @__PURE__ */ p("header", {
|
|
68
|
+
className: "border-b border-border-seam bg-surface-raised",
|
|
69
|
+
children: /* @__PURE__ */ p("div", {
|
|
70
|
+
className: "mx-auto max-w-7xl px-6 py-4",
|
|
71
|
+
children: /* @__PURE__ */ m("div", {
|
|
72
|
+
className: "flex items-center justify-between",
|
|
73
|
+
children: [/* @__PURE__ */ m("div", {
|
|
74
|
+
className: "flex items-center gap-3",
|
|
75
|
+
children: [/* @__PURE__ */ m("button", {
|
|
76
|
+
type: "button",
|
|
77
|
+
onClick: () => x(`${C}/dashboard`),
|
|
78
|
+
className: "inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary",
|
|
79
|
+
children: [/* @__PURE__ */ p(o, { className: "size-4" }), "Back"]
|
|
80
|
+
}), /* @__PURE__ */ m("div", {
|
|
81
|
+
className: "flex items-center gap-2",
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ p(l, { className: "size-5 text-text-primary" }),
|
|
84
|
+
/* @__PURE__ */ p("h1", {
|
|
85
|
+
className: "text-xl font-semibold text-text-primary",
|
|
86
|
+
children: "Fulfillment logs"
|
|
87
|
+
}),
|
|
88
|
+
/* @__PURE__ */ p("span", {
|
|
89
|
+
className: "rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent",
|
|
90
|
+
children: D
|
|
91
|
+
})
|
|
92
|
+
]
|
|
93
|
+
})]
|
|
94
|
+
}), /* @__PURE__ */ m("button", {
|
|
95
|
+
type: "button",
|
|
96
|
+
onClick: () => void V(),
|
|
97
|
+
className: "inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover",
|
|
98
|
+
disabled: k,
|
|
99
|
+
children: [/* @__PURE__ */ p(c, { className: `size-4 ${k ? "animate-spin" : ""}` }), "Refresh"]
|
|
100
|
+
})]
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
}), /* @__PURE__ */ m("main", {
|
|
104
|
+
className: "mx-auto max-w-7xl p-6",
|
|
105
|
+
children: [
|
|
106
|
+
/* @__PURE__ */ p(g, {
|
|
107
|
+
className: "mb-4",
|
|
108
|
+
children: "Every carrier shipment-creation attempt for every physical order — including retries Temporal made after a failure — so you can see what happened, why, and whether it eventually succeeded. OrderFulfillment.failureReason only ever shows the last attempt; this is the full history."
|
|
109
|
+
}),
|
|
110
|
+
/* @__PURE__ */ m("div", {
|
|
111
|
+
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center",
|
|
112
|
+
children: [
|
|
113
|
+
/* @__PURE__ */ p("select", {
|
|
114
|
+
"aria-label": "Filter by result",
|
|
115
|
+
value: F,
|
|
116
|
+
onChange: (e) => {
|
|
117
|
+
I(e.target.value), P(0);
|
|
118
|
+
},
|
|
119
|
+
className: "rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary",
|
|
120
|
+
children: _.map((e) => /* @__PURE__ */ p("option", {
|
|
121
|
+
value: e.value,
|
|
122
|
+
children: e.label
|
|
123
|
+
}, e.value))
|
|
124
|
+
}),
|
|
125
|
+
/* @__PURE__ */ p("input", {
|
|
126
|
+
type: "text",
|
|
127
|
+
"aria-label": "Filter by carrier",
|
|
128
|
+
placeholder: "Carrier (e.g. shiprocket)…",
|
|
129
|
+
value: L,
|
|
130
|
+
onChange: (e) => {
|
|
131
|
+
R(e.target.value), P(0);
|
|
132
|
+
},
|
|
133
|
+
className: "rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary sm:w-48"
|
|
134
|
+
}),
|
|
135
|
+
/* @__PURE__ */ p("input", {
|
|
136
|
+
type: "search",
|
|
137
|
+
"aria-label": "Filter by order ID",
|
|
138
|
+
placeholder: "Order ID…",
|
|
139
|
+
value: z,
|
|
140
|
+
onChange: (e) => {
|
|
141
|
+
B(e.target.value), P(0);
|
|
142
|
+
},
|
|
143
|
+
className: "flex-1 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary"
|
|
144
|
+
})
|
|
145
|
+
]
|
|
146
|
+
}),
|
|
147
|
+
j && /* @__PURE__ */ p("div", {
|
|
148
|
+
role: "alert",
|
|
149
|
+
className: "mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger",
|
|
150
|
+
children: j
|
|
151
|
+
}),
|
|
152
|
+
k && T.length === 0 ? /* @__PURE__ */ p("output", {
|
|
153
|
+
className: "block py-16 text-center text-text-secondary",
|
|
154
|
+
children: "Loading fulfillment logs…"
|
|
155
|
+
}) : T.length === 0 ? /* @__PURE__ */ p(h, {
|
|
156
|
+
illustration: "empty-generic",
|
|
157
|
+
title: "No fulfillment attempts",
|
|
158
|
+
description: "Carrier shipment-creation attempts will show up here as physical orders are placed."
|
|
159
|
+
}) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("div", {
|
|
160
|
+
className: "overflow-hidden rounded-lg border border-border-seam",
|
|
161
|
+
children: /* @__PURE__ */ m("table", {
|
|
162
|
+
className: "w-full text-sm",
|
|
163
|
+
children: [/* @__PURE__ */ p("thead", {
|
|
164
|
+
className: "bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary",
|
|
165
|
+
children: /* @__PURE__ */ m("tr", { children: [
|
|
166
|
+
/* @__PURE__ */ p("th", {
|
|
167
|
+
className: "px-4 py-2",
|
|
168
|
+
children: "Attempt #"
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ p("th", {
|
|
171
|
+
className: "px-4 py-2",
|
|
172
|
+
children: "Carrier"
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ p("th", {
|
|
175
|
+
className: "px-4 py-2",
|
|
176
|
+
children: "Result"
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ p("th", {
|
|
179
|
+
className: "px-4 py-2",
|
|
180
|
+
children: "Error"
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ p("th", {
|
|
183
|
+
className: "px-4 py-2 text-right",
|
|
184
|
+
children: "Duration"
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ p("th", {
|
|
187
|
+
className: "px-4 py-2",
|
|
188
|
+
children: "Occurred"
|
|
189
|
+
})
|
|
190
|
+
] })
|
|
191
|
+
}), /* @__PURE__ */ p("tbody", {
|
|
192
|
+
className: "divide-y divide-border-seam bg-surface-raised",
|
|
193
|
+
children: T.map((e) => /* @__PURE__ */ m("tr", {
|
|
194
|
+
className: "text-text-primary align-top",
|
|
195
|
+
children: [
|
|
196
|
+
/* @__PURE__ */ p("td", {
|
|
197
|
+
className: "px-4 py-2 font-mono text-xs",
|
|
198
|
+
children: e.attemptNumber
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ p("td", {
|
|
201
|
+
className: "px-4 py-2 text-xs",
|
|
202
|
+
children: e.carrier
|
|
203
|
+
}),
|
|
204
|
+
/* @__PURE__ */ p("td", {
|
|
205
|
+
className: "px-4 py-2",
|
|
206
|
+
children: e.succeeded ? /* @__PURE__ */ m("span", {
|
|
207
|
+
className: "inline-flex items-center gap-1 rounded-full bg-surface-success-muted px-2 py-0.5 text-xs font-medium text-text-success",
|
|
208
|
+
children: [/* @__PURE__ */ p(s, { className: "size-3" }), "Succeeded"]
|
|
209
|
+
}) : /* @__PURE__ */ m("span", {
|
|
210
|
+
className: "inline-flex items-center gap-1 rounded-full bg-surface-danger-muted px-2 py-0.5 text-xs font-medium text-text-danger",
|
|
211
|
+
children: [
|
|
212
|
+
/* @__PURE__ */ p(u, { className: "size-3" }),
|
|
213
|
+
"Failed",
|
|
214
|
+
e.errorStatusCode ? ` (${e.errorStatusCode})` : ""
|
|
215
|
+
]
|
|
216
|
+
})
|
|
217
|
+
}),
|
|
218
|
+
/* @__PURE__ */ p("td", {
|
|
219
|
+
className: "max-w-md px-4 py-2 text-xs text-text-secondary",
|
|
220
|
+
children: e.errorMessage ? /* @__PURE__ */ p("span", {
|
|
221
|
+
title: e.errorMessage,
|
|
222
|
+
className: "line-clamp-2",
|
|
223
|
+
children: e.errorMessage
|
|
224
|
+
}) : /* @__PURE__ */ p("span", {
|
|
225
|
+
className: "text-text-muted",
|
|
226
|
+
children: "—"
|
|
227
|
+
})
|
|
228
|
+
}),
|
|
229
|
+
/* @__PURE__ */ p("td", {
|
|
230
|
+
className: "px-4 py-2 text-right tabular-nums text-xs text-text-secondary",
|
|
231
|
+
children: e.durationMs === null ? "—" : `${e.durationMs}ms`
|
|
232
|
+
}),
|
|
233
|
+
/* @__PURE__ */ p("td", {
|
|
234
|
+
className: "px-4 py-2 text-xs text-text-secondary whitespace-nowrap",
|
|
235
|
+
children: b(e.occurredAt)
|
|
236
|
+
})
|
|
237
|
+
]
|
|
238
|
+
}, e.id))
|
|
239
|
+
})]
|
|
240
|
+
})
|
|
241
|
+
}), D > y && /* @__PURE__ */ m("div", {
|
|
242
|
+
className: "mt-4 flex items-center justify-between text-sm",
|
|
243
|
+
children: [/* @__PURE__ */ m("span", {
|
|
244
|
+
className: "text-text-secondary",
|
|
245
|
+
children: [
|
|
246
|
+
"Showing ",
|
|
247
|
+
N * y + 1,
|
|
248
|
+
"–",
|
|
249
|
+
Math.min((N + 1) * y, D),
|
|
250
|
+
" of",
|
|
251
|
+
" ",
|
|
252
|
+
D
|
|
253
|
+
]
|
|
254
|
+
}), /* @__PURE__ */ m("div", {
|
|
255
|
+
className: "flex items-center gap-2",
|
|
256
|
+
children: [/* @__PURE__ */ p("button", {
|
|
257
|
+
type: "button",
|
|
258
|
+
disabled: N === 0,
|
|
259
|
+
onClick: () => P((e) => e - 1),
|
|
260
|
+
className: "rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40",
|
|
261
|
+
children: "Previous"
|
|
262
|
+
}), /* @__PURE__ */ p("button", {
|
|
263
|
+
type: "button",
|
|
264
|
+
disabled: (N + 1) * y >= D,
|
|
265
|
+
onClick: () => P((e) => e + 1),
|
|
266
|
+
className: "rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40",
|
|
267
|
+
children: "Next"
|
|
268
|
+
})]
|
|
269
|
+
})]
|
|
270
|
+
})] })
|
|
271
|
+
]
|
|
272
|
+
})]
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
//#endregion
|
|
276
|
+
export { x as AdminFulfillmentLogsPage };
|
|
277
|
+
|
|
278
|
+
//# sourceMappingURL=AdminFulfillmentLogsPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminFulfillmentLogsPage.js","names":[],"sources":["../../src/pages/AdminFulfillmentLogsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { ArrowLeft, RefreshCw, Truck, CheckCircle2, XCircle } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate, useSearchParams } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\n\ninterface FulfillmentAttempt {\n id: string;\n attemptNumber: number;\n carrier: string;\n succeeded: boolean;\n errorMessage: string | null;\n errorStatusCode: number | null;\n durationMs: number | null;\n occurredAt: string;\n}\n\nconst SUCCEEDED_OPTIONS: { value: '' | 'true' | 'false'; label: string }[] = [\n { value: '', label: 'All results' },\n { value: 'false', label: 'Failed only' },\n { value: 'true', label: 'Succeeded only' },\n];\n\nconst ORDER_FULFILLMENT_ATTEMPTS_QUERY = /* GraphQL */ `\n query AdminOrderFulfillmentAttempts(\n $limit: Int!\n $offset: Int!\n $orderId: ID\n $carrier: String\n $succeeded: Boolean\n ) {\n orderFulfillmentAttempts(\n limit: $limit\n offset: $offset\n orderId: $orderId\n carrier: $carrier\n succeeded: $succeeded\n ) {\n totalCount\n attempts {\n id\n attemptNumber\n carrier\n succeeded\n errorMessage\n errorStatusCode\n durationMs\n occurredAt\n }\n }\n }\n`;\n\nconst PAGE_SIZE = 25;\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n });\n}\n\n/**\n * Admin-only log of every carrier shipment-creation attempt (every Temporal\n * retry, not just the last failure) — the \"why did this order's shipment\n * fail\" debugging surface that OrderFulfillment.failureReason alone can't\n * answer, since it only ever holds the last attempt's message.\n */\nexport const AdminFulfillmentLogsPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [searchParams] = useSearchParams();\n\n const [attempts, setAttempts] = useState<FulfillmentAttempt[]>([]);\n const [total, setTotal] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [page, setPage] = useState(0);\n const [succeededFilter, setSucceededFilter] = useState<'' | 'true' | 'false'>('');\n const [carrierFilter, setCarrierFilter] = useState('');\n const [orderId, setOrderId] = useState(searchParams.get('orderId') ?? '');\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n orderFulfillmentAttempts: { totalCount: number; attempts: FulfillmentAttempt[] };\n }>({\n gateway: 'global',\n query: ORDER_FULFILLMENT_ATTEMPTS_QUERY,\n variables: {\n limit: PAGE_SIZE,\n offset: page * PAGE_SIZE,\n orderId: orderId.trim() || undefined,\n carrier: carrierFilter.trim() || undefined,\n succeeded: succeededFilter === '' ? undefined : succeededFilter === 'true',\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load fulfillment logs');\n }\n setTotal(result.data?.orderFulfillmentAttempts.totalCount ?? 0);\n setAttempts(result.data?.orderFulfillmentAttempts.attempts ?? []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load fulfillment logs');\n } finally {\n setLoading(false);\n }\n }, [authToken, page, orderId, carrierFilter, succeededFilter]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-seam bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <Truck className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Fulfillment logs</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {total}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n <PagePurpose className=\"mb-4\">\n Every carrier shipment-creation attempt for every physical order — including retries\n Temporal made after a failure — so you can see what happened, why, and whether it\n eventually succeeded. OrderFulfillment.failureReason only ever shows the last attempt;\n this is the full history.\n </PagePurpose>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center\">\n <select\n aria-label=\"Filter by result\"\n value={succeededFilter}\n onChange={(e) => {\n setSucceededFilter(e.target.value as '' | 'true' | 'false');\n setPage(0);\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n >\n {SUCCEEDED_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <input\n type=\"text\"\n aria-label=\"Filter by carrier\"\n placeholder=\"Carrier (e.g. shiprocket)…\"\n value={carrierFilter}\n onChange={(e) => {\n setCarrierFilter(e.target.value);\n setPage(0);\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary sm:w-48\"\n />\n <input\n type=\"search\"\n aria-label=\"Filter by order ID\"\n placeholder=\"Order ID…\"\n value={orderId}\n onChange={(e) => {\n setOrderId(e.target.value);\n setPage(0);\n }}\n className=\"flex-1 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </div>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading && attempts.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">\n Loading fulfillment logs…\n </output>\n ) : attempts.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No fulfillment attempts\"\n description=\"Carrier shipment-creation attempts will show up here as physical orders are placed.\"\n />\n ) : (\n <>\n <div className=\"overflow-hidden rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary\">\n <tr>\n <th className=\"px-4 py-2\">Attempt #</th>\n <th className=\"px-4 py-2\">Carrier</th>\n <th className=\"px-4 py-2\">Result</th>\n <th className=\"px-4 py-2\">Error</th>\n <th className=\"px-4 py-2 text-right\">Duration</th>\n <th className=\"px-4 py-2\">Occurred</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-surface-raised\">\n {attempts.map((attempt) => (\n <tr key={attempt.id} className=\"text-text-primary align-top\">\n <td className=\"px-4 py-2 font-mono text-xs\">{attempt.attemptNumber}</td>\n <td className=\"px-4 py-2 text-xs\">{attempt.carrier}</td>\n <td className=\"px-4 py-2\">\n {attempt.succeeded ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-surface-success-muted px-2 py-0.5 text-xs font-medium text-text-success\">\n <CheckCircle2 className=\"size-3\" />\n Succeeded\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-surface-danger-muted px-2 py-0.5 text-xs font-medium text-text-danger\">\n <XCircle className=\"size-3\" />\n Failed{attempt.errorStatusCode ? ` (${attempt.errorStatusCode})` : ''}\n </span>\n )}\n </td>\n <td className=\"max-w-md px-4 py-2 text-xs text-text-secondary\">\n {attempt.errorMessage ? (\n <span title={attempt.errorMessage} className=\"line-clamp-2\">\n {attempt.errorMessage}\n </span>\n ) : (\n <span className=\"text-text-muted\">—</span>\n )}\n </td>\n <td className=\"px-4 py-2 text-right tabular-nums text-xs text-text-secondary\">\n {attempt.durationMs !== null ? `${attempt.durationMs}ms` : '—'}\n </td>\n <td className=\"px-4 py-2 text-xs text-text-secondary whitespace-nowrap\">\n {formatDate(attempt.occurredAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {total > PAGE_SIZE && (\n <div className=\"mt-4 flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * PAGE_SIZE + 1}–{Math.min((page + 1) * PAGE_SIZE, total)} of{' '}\n {total}\n </span>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => p - 1)}\n className=\"rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={(page + 1) * PAGE_SIZE >= total}\n onClick={() => setPage((p) => p + 1)}\n className=\"rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40\"\n >\n Next\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -2,12 +2,12 @@ import { useStore as e } from "../providers/StoreProvider.js";
|
|
|
2
2
|
import { formatPrice as t } from "../utils/index.js";
|
|
3
3
|
import { useCallback as n, useEffect as r, useState as i } from "react";
|
|
4
4
|
import { useNavigate as a } from "react-router";
|
|
5
|
-
import { ArrowLeft as o, ExternalLink as s, Flag as c, Loader2 as l, Package as u, RefreshCw as d } from "lucide-react";
|
|
6
|
-
import { graphqlFetch as
|
|
7
|
-
import { Fragment as
|
|
8
|
-
import { IllustratedEmptyState as
|
|
5
|
+
import { ArrowLeft as o, ExternalLink as s, Flag as c, Loader2 as l, Package as u, RefreshCw as d, ScrollText as f } from "lucide-react";
|
|
6
|
+
import { graphqlFetch as p } from "@burdenoff/fe-libs/shared/graphql";
|
|
7
|
+
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
8
|
+
import { IllustratedEmptyState as _, PagePurpose as v } from "@burdenoff/fe-libs/ui";
|
|
9
9
|
//#region src/pages/AdminOrdersPage.tsx
|
|
10
|
-
var
|
|
10
|
+
var y = [
|
|
11
11
|
{
|
|
12
12
|
value: "",
|
|
13
13
|
label: "All statuses"
|
|
@@ -28,7 +28,7 @@ var v = [
|
|
|
28
28
|
value: "REFUNDED",
|
|
29
29
|
label: "Refunded"
|
|
30
30
|
}
|
|
31
|
-
],
|
|
31
|
+
], b = {
|
|
32
32
|
PENDING: "bg-surface-warning-muted text-text-warning",
|
|
33
33
|
PROCESSING: "bg-surface-warning-muted text-text-warning",
|
|
34
34
|
READY_TO_SHIP: "bg-surface-accent-muted text-text-accent",
|
|
@@ -39,13 +39,13 @@ var v = [
|
|
|
39
39
|
FAILED: "bg-surface-danger-muted text-text-danger",
|
|
40
40
|
RETURNED: "bg-surface-danger-muted text-text-danger",
|
|
41
41
|
CANCELLED: "bg-surface-muted text-text-secondary"
|
|
42
|
-
},
|
|
42
|
+
}, x = {
|
|
43
43
|
OUT_OF_STOCK: "Out of stock",
|
|
44
44
|
CANNOT_FULFILL: "Cannot fulfill",
|
|
45
45
|
WRONG_ADDRESS: "Wrong address",
|
|
46
46
|
OTHER: "Other"
|
|
47
|
-
},
|
|
48
|
-
function
|
|
47
|
+
}, S = "\n query AdminPhysicalOrders($page: Int!, $limit: Int!, $status: OrderStatus, $search: String) {\n adminPhysicalOrders(page: $page, limit: $limit, status: $status, search: $search) {\n total\n orders {\n id\n status\n userId\n total\n currency\n createdAt\n fulfillment {\n status\n carrier\n trackingNumber\n trackingUrl\n }\n issueFlags {\n id\n category\n note\n status\n raisedByOrgId\n createdAt\n }\n }\n }\n }\n", C = "\n mutation AdminResolveOrderIssue($id: ID!) {\n resolveOrderIssueFlag(id: $id) {\n id\n status\n }\n }\n", w = 25;
|
|
48
|
+
function T(e) {
|
|
49
49
|
return new Date(e).toLocaleDateString("en-US", {
|
|
50
50
|
year: "numeric",
|
|
51
51
|
month: "short",
|
|
@@ -54,276 +54,286 @@ function w(e) {
|
|
|
54
54
|
minute: "2-digit"
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
var
|
|
58
|
-
let
|
|
59
|
-
|
|
57
|
+
var E = () => {
|
|
58
|
+
let E = a(), { authToken: D, basePath: O } = e(), [k, A] = i([]), [j, M] = i(0), [N, P] = i(!1), [F, I] = i(null), [L, R] = i(0), [z, B] = i(""), [V, H] = i(""), [U, W] = i(null), G = n(async () => {
|
|
59
|
+
P(!0), I(null);
|
|
60
60
|
try {
|
|
61
|
-
let e = await
|
|
61
|
+
let e = await p({
|
|
62
62
|
gateway: "global",
|
|
63
|
-
query:
|
|
63
|
+
query: S,
|
|
64
64
|
variables: {
|
|
65
|
-
page:
|
|
66
|
-
limit:
|
|
67
|
-
status:
|
|
68
|
-
search:
|
|
65
|
+
page: L + 1,
|
|
66
|
+
limit: w,
|
|
67
|
+
status: z || void 0,
|
|
68
|
+
search: V.trim() || void 0
|
|
69
69
|
},
|
|
70
|
-
authToken:
|
|
70
|
+
authToken: D || void 0
|
|
71
71
|
});
|
|
72
72
|
if (e.errors?.length) throw Error(e.errors[0]?.message ?? "Failed to load orders");
|
|
73
|
-
|
|
73
|
+
M(e.data?.adminPhysicalOrders.total ?? 0), A(e.data?.adminPhysicalOrders.orders ?? []);
|
|
74
74
|
} catch (e) {
|
|
75
|
-
|
|
75
|
+
I(e instanceof Error ? e.message : "Failed to load orders");
|
|
76
76
|
} finally {
|
|
77
|
-
|
|
77
|
+
P(!1);
|
|
78
78
|
}
|
|
79
79
|
}, [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
D,
|
|
81
|
+
L,
|
|
82
|
+
z,
|
|
83
|
+
V
|
|
84
84
|
]);
|
|
85
85
|
r(() => {
|
|
86
|
-
|
|
87
|
-
}, [
|
|
88
|
-
let
|
|
89
|
-
|
|
86
|
+
G();
|
|
87
|
+
}, [G]);
|
|
88
|
+
let K = async (e) => {
|
|
89
|
+
W(e);
|
|
90
90
|
try {
|
|
91
|
-
await
|
|
91
|
+
await p({
|
|
92
92
|
gateway: "global",
|
|
93
|
-
query:
|
|
93
|
+
query: C,
|
|
94
94
|
variables: { id: e },
|
|
95
|
-
authToken:
|
|
96
|
-
}), await
|
|
95
|
+
authToken: D || void 0
|
|
96
|
+
}), await G();
|
|
97
97
|
} catch (e) {
|
|
98
|
-
|
|
98
|
+
I(e instanceof Error ? e.message : "Failed to resolve issue");
|
|
99
99
|
} finally {
|
|
100
|
-
|
|
100
|
+
W(null);
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
-
return /* @__PURE__ */
|
|
103
|
+
return /* @__PURE__ */ g("div", {
|
|
104
104
|
className: "min-h-screen bg-surface-default",
|
|
105
|
-
children: [/* @__PURE__ */
|
|
105
|
+
children: [/* @__PURE__ */ h("header", {
|
|
106
106
|
className: "border-b border-border-seam bg-surface-raised",
|
|
107
|
-
children: /* @__PURE__ */
|
|
107
|
+
children: /* @__PURE__ */ h("div", {
|
|
108
108
|
className: "mx-auto max-w-7xl px-6 py-4",
|
|
109
|
-
children: /* @__PURE__ */
|
|
109
|
+
children: /* @__PURE__ */ g("div", {
|
|
110
110
|
className: "flex items-center justify-between",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ g("div", {
|
|
112
112
|
className: "flex items-center gap-3",
|
|
113
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ g("button", {
|
|
114
114
|
type: "button",
|
|
115
|
-
onClick: () =>
|
|
115
|
+
onClick: () => E(`${O}/dashboard`),
|
|
116
116
|
className: "inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary",
|
|
117
|
-
children: [/* @__PURE__ */
|
|
118
|
-
}), /* @__PURE__ */
|
|
117
|
+
children: [/* @__PURE__ */ h(o, { className: "size-4" }), "Back"]
|
|
118
|
+
}), /* @__PURE__ */ g("div", {
|
|
119
119
|
className: "flex items-center gap-2",
|
|
120
120
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ h(u, { className: "size-5 text-text-primary" }),
|
|
122
|
+
/* @__PURE__ */ h("h1", {
|
|
123
123
|
className: "text-xl font-semibold text-text-primary",
|
|
124
124
|
children: "Physical orders"
|
|
125
125
|
}),
|
|
126
|
-
/* @__PURE__ */
|
|
126
|
+
/* @__PURE__ */ h("span", {
|
|
127
127
|
className: "rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent",
|
|
128
|
-
children:
|
|
128
|
+
children: j
|
|
129
129
|
})
|
|
130
130
|
]
|
|
131
131
|
})]
|
|
132
|
-
}), /* @__PURE__ */
|
|
132
|
+
}), /* @__PURE__ */ g("button", {
|
|
133
133
|
type: "button",
|
|
134
|
-
onClick: () => void
|
|
134
|
+
onClick: () => void G(),
|
|
135
135
|
className: "inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover",
|
|
136
|
-
disabled:
|
|
137
|
-
children: [/* @__PURE__ */
|
|
136
|
+
disabled: N,
|
|
137
|
+
children: [/* @__PURE__ */ h(d, { className: `size-4 ${N ? "animate-spin" : ""}` }), "Refresh"]
|
|
138
138
|
})]
|
|
139
139
|
})
|
|
140
140
|
})
|
|
141
|
-
}), /* @__PURE__ */
|
|
141
|
+
}), /* @__PURE__ */ g("main", {
|
|
142
142
|
className: "mx-auto max-w-7xl p-6",
|
|
143
143
|
children: [
|
|
144
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ h(v, {
|
|
145
145
|
className: "mb-4",
|
|
146
146
|
children: "Payment/order status and Shiprocket fulfillment status for every physical order, plus any issue a seller has flagged (out of stock, cannot fulfill, wrong address). Read-only — cancellation and refunds still happen manually via the Shiprocket dashboard for now."
|
|
147
147
|
}),
|
|
148
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ g("div", {
|
|
149
149
|
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
150
|
+
children: [/* @__PURE__ */ h("select", {
|
|
151
151
|
"aria-label": "Filter by status",
|
|
152
|
-
value:
|
|
152
|
+
value: z,
|
|
153
153
|
onChange: (e) => {
|
|
154
|
-
|
|
154
|
+
B(e.target.value), R(0);
|
|
155
155
|
},
|
|
156
156
|
className: "rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary",
|
|
157
|
-
children:
|
|
157
|
+
children: y.map((e) => /* @__PURE__ */ h("option", {
|
|
158
158
|
value: e.value,
|
|
159
159
|
children: e.label
|
|
160
160
|
}, e.value))
|
|
161
|
-
}), /* @__PURE__ */
|
|
161
|
+
}), /* @__PURE__ */ h("input", {
|
|
162
162
|
type: "search",
|
|
163
163
|
"aria-label": "Search by order ID, buyer, or tracking number",
|
|
164
164
|
placeholder: "Search order ID, buyer, or tracking number…",
|
|
165
|
-
value:
|
|
165
|
+
value: V,
|
|
166
166
|
onChange: (e) => {
|
|
167
|
-
|
|
167
|
+
H(e.target.value), R(0);
|
|
168
168
|
},
|
|
169
169
|
className: "flex-1 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary"
|
|
170
170
|
})]
|
|
171
171
|
}),
|
|
172
|
-
|
|
172
|
+
F && /* @__PURE__ */ h("div", {
|
|
173
173
|
role: "alert",
|
|
174
174
|
className: "mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger",
|
|
175
|
-
children:
|
|
175
|
+
children: F
|
|
176
176
|
}),
|
|
177
|
-
|
|
177
|
+
N && k.length === 0 ? /* @__PURE__ */ h("output", {
|
|
178
178
|
className: "block py-16 text-center text-text-secondary",
|
|
179
179
|
children: "Loading orders…"
|
|
180
|
-
}) :
|
|
180
|
+
}) : k.length === 0 ? /* @__PURE__ */ h(_, {
|
|
181
181
|
illustration: "empty-generic",
|
|
182
182
|
title: "No physical orders",
|
|
183
183
|
description: "Orders with a physical line item will show up here once customers make purchases."
|
|
184
|
-
}) : /* @__PURE__ */
|
|
184
|
+
}) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h("div", {
|
|
185
185
|
className: "overflow-hidden rounded-lg border border-border-seam",
|
|
186
|
-
children: /* @__PURE__ */
|
|
186
|
+
children: /* @__PURE__ */ g("table", {
|
|
187
187
|
className: "w-full text-sm",
|
|
188
|
-
children: [/* @__PURE__ */
|
|
188
|
+
children: [/* @__PURE__ */ h("thead", {
|
|
189
189
|
className: "bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary",
|
|
190
|
-
children: /* @__PURE__ */
|
|
191
|
-
/* @__PURE__ */
|
|
190
|
+
children: /* @__PURE__ */ g("tr", { children: [
|
|
191
|
+
/* @__PURE__ */ h("th", {
|
|
192
192
|
className: "px-4 py-2",
|
|
193
193
|
children: "Order"
|
|
194
194
|
}),
|
|
195
|
-
/* @__PURE__ */
|
|
195
|
+
/* @__PURE__ */ h("th", {
|
|
196
196
|
className: "px-4 py-2",
|
|
197
197
|
children: "Buyer"
|
|
198
198
|
}),
|
|
199
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ h("th", {
|
|
200
200
|
className: "px-4 py-2 text-right",
|
|
201
201
|
children: "Total"
|
|
202
202
|
}),
|
|
203
|
-
/* @__PURE__ */
|
|
203
|
+
/* @__PURE__ */ h("th", {
|
|
204
204
|
className: "px-4 py-2",
|
|
205
205
|
children: "Status"
|
|
206
206
|
}),
|
|
207
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ h("th", {
|
|
208
208
|
className: "px-4 py-2",
|
|
209
209
|
children: "Fulfillment"
|
|
210
210
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ h("th", {
|
|
212
212
|
className: "px-4 py-2",
|
|
213
213
|
children: "Tracking"
|
|
214
214
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ h("th", {
|
|
216
216
|
className: "px-4 py-2",
|
|
217
217
|
children: "Issue"
|
|
218
218
|
}),
|
|
219
|
-
/* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ h("th", {
|
|
220
220
|
className: "px-4 py-2",
|
|
221
221
|
children: "Placed"
|
|
222
222
|
})
|
|
223
223
|
] })
|
|
224
|
-
}), /* @__PURE__ */
|
|
224
|
+
}), /* @__PURE__ */ h("tbody", {
|
|
225
225
|
className: "divide-y divide-border-seam bg-surface-raised",
|
|
226
|
-
children:
|
|
226
|
+
children: k.map((e) => {
|
|
227
227
|
let n = e.issueFlags.find((e) => e.status === "OPEN");
|
|
228
|
-
return /* @__PURE__ */
|
|
228
|
+
return /* @__PURE__ */ g("tr", {
|
|
229
229
|
className: "text-text-primary align-top",
|
|
230
230
|
children: [
|
|
231
|
-
/* @__PURE__ */
|
|
231
|
+
/* @__PURE__ */ h("td", {
|
|
232
232
|
className: "px-4 py-2 font-mono text-xs",
|
|
233
233
|
children: e.id.slice(0, 8).toUpperCase()
|
|
234
234
|
}),
|
|
235
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ g("td", {
|
|
236
236
|
className: "px-4 py-2 font-mono text-xs text-text-secondary",
|
|
237
237
|
children: [e.userId.slice(0, 8), "…"]
|
|
238
238
|
}),
|
|
239
|
-
/* @__PURE__ */
|
|
239
|
+
/* @__PURE__ */ h("td", {
|
|
240
240
|
className: "px-4 py-2 text-right tabular-nums",
|
|
241
241
|
children: t(e.total, e.currency)
|
|
242
242
|
}),
|
|
243
|
-
/* @__PURE__ */
|
|
243
|
+
/* @__PURE__ */ h("td", {
|
|
244
244
|
className: "px-4 py-2",
|
|
245
|
-
children: /* @__PURE__ */
|
|
245
|
+
children: /* @__PURE__ */ h("span", {
|
|
246
246
|
className: "rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent",
|
|
247
247
|
children: e.status
|
|
248
248
|
})
|
|
249
249
|
}),
|
|
250
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ h("td", {
|
|
251
251
|
className: "px-4 py-2",
|
|
252
|
-
children:
|
|
253
|
-
className:
|
|
254
|
-
children: e.fulfillment
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
252
|
+
children: /* @__PURE__ */ g("div", {
|
|
253
|
+
className: "flex items-center gap-1.5",
|
|
254
|
+
children: [e.fulfillment ? /* @__PURE__ */ h("span", {
|
|
255
|
+
className: `rounded-full px-2 py-0.5 text-xs font-medium ${b[e.fulfillment.status]}`,
|
|
256
|
+
children: e.fulfillment.status.replace(/_/g, " ")
|
|
257
|
+
}) : /* @__PURE__ */ h("span", {
|
|
258
|
+
className: "rounded-full bg-surface-danger-muted px-2 py-0.5 text-xs font-medium text-text-danger",
|
|
259
|
+
title: "No fulfillment record — the post-payment fulfillment step never ran for this order",
|
|
260
|
+
children: "Not started"
|
|
261
|
+
}), /* @__PURE__ */ h("button", {
|
|
262
|
+
type: "button",
|
|
263
|
+
title: "View fulfillment logs for this order",
|
|
264
|
+
onClick: () => E(`${O}/fulfillment-logs?orderId=${e.id}`),
|
|
265
|
+
className: "text-text-muted hover:text-text-primary",
|
|
266
|
+
children: /* @__PURE__ */ h(f, { className: "size-3.5" })
|
|
267
|
+
})]
|
|
259
268
|
})
|
|
260
269
|
}),
|
|
261
|
-
/* @__PURE__ */
|
|
270
|
+
/* @__PURE__ */ h("td", {
|
|
262
271
|
className: "px-4 py-2 text-xs",
|
|
263
|
-
children: e.fulfillment?.trackingNumber ? /* @__PURE__ */
|
|
272
|
+
children: e.fulfillment?.trackingNumber ? /* @__PURE__ */ g("a", {
|
|
264
273
|
href: e.fulfillment.trackingUrl ?? "#",
|
|
265
274
|
target: "_blank",
|
|
266
275
|
rel: "noreferrer",
|
|
267
276
|
className: "inline-flex items-center gap-1 text-text-accent hover:underline",
|
|
268
|
-
children: [e.fulfillment.carrier ?? "Carrier", /* @__PURE__ */
|
|
269
|
-
}) : /* @__PURE__ */
|
|
277
|
+
children: [e.fulfillment.carrier ?? "Carrier", /* @__PURE__ */ h(s, { className: "size-3" })]
|
|
278
|
+
}) : /* @__PURE__ */ h("span", {
|
|
270
279
|
className: "text-text-muted",
|
|
271
280
|
children: "—"
|
|
272
281
|
})
|
|
273
282
|
}),
|
|
274
|
-
/* @__PURE__ */
|
|
283
|
+
/* @__PURE__ */ h("td", {
|
|
275
284
|
className: "px-4 py-2",
|
|
276
|
-
children: n ? /* @__PURE__ */
|
|
285
|
+
children: n ? /* @__PURE__ */ g("div", {
|
|
277
286
|
className: "flex flex-col items-start gap-1",
|
|
278
|
-
children: [/* @__PURE__ */
|
|
287
|
+
children: [/* @__PURE__ */ g("span", {
|
|
279
288
|
title: n.note ?? void 0,
|
|
280
289
|
className: "flex items-center gap-1 rounded-full bg-surface-warning-muted px-2 py-0.5 text-xs font-medium text-text-warning",
|
|
281
|
-
children: [/* @__PURE__ */
|
|
282
|
-
}), /* @__PURE__ */
|
|
290
|
+
children: [/* @__PURE__ */ h(c, { className: "size-3" }), x[n.category]]
|
|
291
|
+
}), /* @__PURE__ */ h("button", {
|
|
283
292
|
type: "button",
|
|
284
|
-
onClick: () => void
|
|
285
|
-
disabled:
|
|
293
|
+
onClick: () => void K(n.id),
|
|
294
|
+
disabled: U === n.id,
|
|
286
295
|
className: "text-xs font-medium text-text-secondary underline hover:text-text-primary disabled:opacity-50",
|
|
287
|
-
children:
|
|
296
|
+
children: U === n.id ? /* @__PURE__ */ h(l, { className: "size-3 animate-spin" }) : "Mark resolved"
|
|
288
297
|
})]
|
|
289
|
-
}) : /* @__PURE__ */
|
|
298
|
+
}) : /* @__PURE__ */ h("span", {
|
|
290
299
|
className: "text-text-muted",
|
|
291
300
|
children: "—"
|
|
292
301
|
})
|
|
293
302
|
}),
|
|
294
|
-
/* @__PURE__ */
|
|
303
|
+
/* @__PURE__ */ h("td", {
|
|
295
304
|
className: "px-4 py-2 text-xs text-text-secondary whitespace-nowrap",
|
|
296
|
-
children:
|
|
305
|
+
children: T(e.createdAt)
|
|
297
306
|
})
|
|
298
307
|
]
|
|
299
308
|
}, e.id);
|
|
300
309
|
})
|
|
301
310
|
})]
|
|
302
311
|
})
|
|
303
|
-
}),
|
|
312
|
+
}), j > w && /* @__PURE__ */ g("div", {
|
|
304
313
|
className: "mt-4 flex items-center justify-between text-sm",
|
|
305
|
-
children: [/* @__PURE__ */
|
|
314
|
+
children: [/* @__PURE__ */ g("span", {
|
|
306
315
|
className: "text-text-secondary",
|
|
307
316
|
children: [
|
|
308
317
|
"Showing ",
|
|
309
|
-
|
|
318
|
+
L * w + 1,
|
|
310
319
|
"–",
|
|
311
|
-
Math.min((
|
|
312
|
-
" of
|
|
313
|
-
|
|
320
|
+
Math.min((L + 1) * w, j),
|
|
321
|
+
" of",
|
|
322
|
+
" ",
|
|
323
|
+
j
|
|
314
324
|
]
|
|
315
|
-
}), /* @__PURE__ */
|
|
325
|
+
}), /* @__PURE__ */ g("div", {
|
|
316
326
|
className: "flex items-center gap-2",
|
|
317
|
-
children: [/* @__PURE__ */
|
|
327
|
+
children: [/* @__PURE__ */ h("button", {
|
|
318
328
|
type: "button",
|
|
319
|
-
disabled:
|
|
320
|
-
onClick: () =>
|
|
329
|
+
disabled: L === 0,
|
|
330
|
+
onClick: () => R((e) => e - 1),
|
|
321
331
|
className: "rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40",
|
|
322
332
|
children: "Previous"
|
|
323
|
-
}), /* @__PURE__ */
|
|
333
|
+
}), /* @__PURE__ */ h("button", {
|
|
324
334
|
type: "button",
|
|
325
|
-
disabled: (
|
|
326
|
-
onClick: () =>
|
|
335
|
+
disabled: (L + 1) * w >= j,
|
|
336
|
+
onClick: () => R((e) => e + 1),
|
|
327
337
|
className: "rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40",
|
|
328
338
|
children: "Next"
|
|
329
339
|
})]
|
|
@@ -334,6 +344,6 @@ var T = () => {
|
|
|
334
344
|
});
|
|
335
345
|
};
|
|
336
346
|
//#endregion
|
|
337
|
-
export {
|
|
347
|
+
export { E as AdminOrdersPage };
|
|
338
348
|
|
|
339
349
|
//# sourceMappingURL=AdminOrdersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOrdersPage.js","names":[],"sources":["../../src/pages/AdminOrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { ArrowLeft, RefreshCw, Package, ExternalLink, Flag, Loader2 } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { formatPrice } from '../utils';\n\ntype OrderStatus = 'PENDING' | 'PROCESSED' | 'FAILED' | 'REFUNDED';\ntype FulfillmentStatus =\n | 'PENDING'\n | 'PROCESSING'\n | 'READY_TO_SHIP'\n | 'SHIPPED'\n | 'IN_TRANSIT'\n | 'OUT_FOR_DELIVERY'\n | 'DELIVERED'\n | 'FAILED'\n | 'RETURNED'\n | 'CANCELLED';\ntype IssueCategory = 'OUT_OF_STOCK' | 'CANNOT_FULFILL' | 'WRONG_ADDRESS' | 'OTHER';\n\ninterface IssueFlag {\n id: string;\n category: IssueCategory;\n note: string | null;\n status: 'OPEN' | 'RESOLVED';\n raisedByOrgId: string;\n createdAt: string;\n}\n\ninterface Fulfillment {\n status: FulfillmentStatus;\n carrier: string | null;\n trackingNumber: string | null;\n trackingUrl: string | null;\n}\n\ninterface AdminOrder {\n id: string;\n status: OrderStatus;\n userId: string;\n total: number;\n currency: string;\n createdAt: string;\n fulfillment: Fulfillment | null;\n issueFlags: IssueFlag[];\n}\n\nconst ORDER_STATUS_OPTIONS: { value: OrderStatus | ''; label: string }[] = [\n { value: '', label: 'All statuses' },\n { value: 'PENDING', label: 'Pending' },\n { value: 'PROCESSED', label: 'Processed' },\n { value: 'FAILED', label: 'Failed' },\n { value: 'REFUNDED', label: 'Refunded' },\n];\n\nconst FULFILLMENT_STYLES: Record<FulfillmentStatus, string> = {\n PENDING: 'bg-surface-warning-muted text-text-warning',\n PROCESSING: 'bg-surface-warning-muted text-text-warning',\n READY_TO_SHIP: 'bg-surface-accent-muted text-text-accent',\n SHIPPED: 'bg-surface-accent-muted text-text-accent',\n IN_TRANSIT: 'bg-surface-accent-muted text-text-accent',\n OUT_FOR_DELIVERY: 'bg-surface-accent-muted text-text-accent',\n DELIVERED: 'bg-surface-success-muted text-text-success',\n FAILED: 'bg-surface-danger-muted text-text-danger',\n RETURNED: 'bg-surface-danger-muted text-text-danger',\n CANCELLED: 'bg-surface-muted text-text-secondary',\n};\n\nconst ISSUE_CATEGORY_LABELS: Record<IssueCategory, string> = {\n OUT_OF_STOCK: 'Out of stock',\n CANNOT_FULFILL: 'Cannot fulfill',\n WRONG_ADDRESS: 'Wrong address',\n OTHER: 'Other',\n};\n\nconst ADMIN_PHYSICAL_ORDERS_QUERY = /* GraphQL */ `\n query AdminPhysicalOrders($page: Int!, $limit: Int!, $status: OrderStatus, $search: String) {\n adminPhysicalOrders(page: $page, limit: $limit, status: $status, search: $search) {\n total\n orders {\n id\n status\n userId\n total\n currency\n createdAt\n fulfillment {\n status\n carrier\n trackingNumber\n trackingUrl\n }\n issueFlags {\n id\n category\n note\n status\n raisedByOrgId\n createdAt\n }\n }\n }\n }\n`;\n\nconst RESOLVE_ORDER_ISSUE_MUTATION = /* GraphQL */ `\n mutation AdminResolveOrderIssue($id: ID!) {\n resolveOrderIssueFlag(id: $id) {\n id\n status\n }\n }\n`;\n\nconst PAGE_SIZE = 25;\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/**\n * Admin-only visibility into physical (fulfillment-tracked) orders —\n * order/payment status, carrier/tracking, and any seller-reported issue\n * flags. Read-only for now: no cancel or refund action here (differs\n * between credits and Razorpay, waits on a proper store refund flow —\n * see BOFF-7192). Cancellation stays manual via the Shiprocket dashboard.\n */\nexport const AdminOrdersPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n\n const [orders, setOrders] = useState<AdminOrder[]>([]);\n const [total, setTotal] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [page, setPage] = useState(0);\n const [statusFilter, setStatusFilter] = useState<OrderStatus | ''>('');\n const [search, setSearch] = useState('');\n const [resolvingFlagId, setResolvingFlagId] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n adminPhysicalOrders: { total: number; orders: AdminOrder[] };\n }>({\n gateway: 'global',\n query: ADMIN_PHYSICAL_ORDERS_QUERY,\n variables: {\n page: page + 1,\n limit: PAGE_SIZE,\n status: statusFilter || undefined,\n search: search.trim() || undefined,\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load orders');\n }\n setTotal(result.data?.adminPhysicalOrders.total ?? 0);\n setOrders(result.data?.adminPhysicalOrders.orders ?? []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load orders');\n } finally {\n setLoading(false);\n }\n }, [authToken, page, statusFilter, search]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const resolveFlag = async (flagId: string) => {\n setResolvingFlagId(flagId);\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RESOLVE_ORDER_ISSUE_MUTATION,\n variables: { id: flagId },\n authToken: authToken || undefined,\n });\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to resolve issue');\n } finally {\n setResolvingFlagId(null);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-seam bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <Package className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Physical orders</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {total}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n <PagePurpose className=\"mb-4\">\n Payment/order status and Shiprocket fulfillment status for every physical order, plus any\n issue a seller has flagged (out of stock, cannot fulfill, wrong address). Read-only —\n cancellation and refunds still happen manually via the Shiprocket dashboard for now.\n </PagePurpose>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center\">\n <select\n aria-label=\"Filter by status\"\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as OrderStatus | '');\n setPage(0);\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n >\n {ORDER_STATUS_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <input\n type=\"search\"\n aria-label=\"Search by order ID, buyer, or tracking number\"\n placeholder=\"Search order ID, buyer, or tracking number…\"\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPage(0);\n }}\n className=\"flex-1 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </div>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading && orders.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">Loading orders…</output>\n ) : orders.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No physical orders\"\n description=\"Orders with a physical line item will show up here once customers make purchases.\"\n />\n ) : (\n <>\n <div className=\"overflow-hidden rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary\">\n <tr>\n <th className=\"px-4 py-2\">Order</th>\n <th className=\"px-4 py-2\">Buyer</th>\n <th className=\"px-4 py-2 text-right\">Total</th>\n <th className=\"px-4 py-2\">Status</th>\n <th className=\"px-4 py-2\">Fulfillment</th>\n <th className=\"px-4 py-2\">Tracking</th>\n <th className=\"px-4 py-2\">Issue</th>\n <th className=\"px-4 py-2\">Placed</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-surface-raised\">\n {orders.map((order) => {\n const openFlag = order.issueFlags.find((f) => f.status === 'OPEN');\n return (\n <tr key={order.id} className=\"text-text-primary align-top\">\n <td className=\"px-4 py-2 font-mono text-xs\">\n {order.id.slice(0, 8).toUpperCase()}\n </td>\n <td className=\"px-4 py-2 font-mono text-xs text-text-secondary\">\n {order.userId.slice(0, 8)}…\n </td>\n <td className=\"px-4 py-2 text-right tabular-nums\">\n {formatPrice(order.total, order.currency)}\n </td>\n <td className=\"px-4 py-2\">\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {order.status}\n </span>\n </td>\n <td className=\"px-4 py-2\">\n {order.fulfillment ? (\n <span\n className={`rounded-full px-2 py-0.5 text-xs font-medium ${FULFILLMENT_STYLES[order.fulfillment.status]}`}\n >\n {order.fulfillment.status.replace(/_/g, ' ')}\n </span>\n ) : (\n <span\n className=\"rounded-full bg-surface-danger-muted px-2 py-0.5 text-xs font-medium text-text-danger\"\n title=\"No fulfillment record — the post-payment fulfillment step never ran for this order\"\n >\n Not started\n </span>\n )}\n </td>\n <td className=\"px-4 py-2 text-xs\">\n {order.fulfillment?.trackingNumber ? (\n <a\n href={order.fulfillment.trackingUrl ?? '#'}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 text-text-accent hover:underline\"\n >\n {order.fulfillment.carrier ?? 'Carrier'}\n <ExternalLink className=\"size-3\" />\n </a>\n ) : (\n <span className=\"text-text-muted\">—</span>\n )}\n </td>\n <td className=\"px-4 py-2\">\n {openFlag ? (\n <div className=\"flex flex-col items-start gap-1\">\n <span\n title={openFlag.note ?? undefined}\n className=\"flex items-center gap-1 rounded-full bg-surface-warning-muted px-2 py-0.5 text-xs font-medium text-text-warning\"\n >\n <Flag className=\"size-3\" />\n {ISSUE_CATEGORY_LABELS[openFlag.category]}\n </span>\n <button\n type=\"button\"\n onClick={() => void resolveFlag(openFlag.id)}\n disabled={resolvingFlagId === openFlag.id}\n className=\"text-xs font-medium text-text-secondary underline hover:text-text-primary disabled:opacity-50\"\n >\n {resolvingFlagId === openFlag.id ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n 'Mark resolved'\n )}\n </button>\n </div>\n ) : (\n <span className=\"text-text-muted\">—</span>\n )}\n </td>\n <td className=\"px-4 py-2 text-xs text-text-secondary whitespace-nowrap\">\n {formatDate(order.createdAt)}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {total > PAGE_SIZE && (\n <div className=\"mt-4 flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * PAGE_SIZE + 1}–{Math.min((page + 1) * PAGE_SIZE, total)} of {total}\n </span>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => p - 1)}\n className=\"rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={(page + 1) * PAGE_SIZE >= total}\n onClick={() => setPage((p) => p + 1)}\n className=\"rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40\"\n >\n Next\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminOrdersPage.js","names":[],"sources":["../../src/pages/AdminOrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport {\n ArrowLeft,\n RefreshCw,\n Package,\n ExternalLink,\n Flag,\n Loader2,\n ScrollText,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { formatPrice } from '../utils';\n\ntype OrderStatus = 'PENDING' | 'PROCESSED' | 'FAILED' | 'REFUNDED';\ntype FulfillmentStatus =\n | 'PENDING'\n | 'PROCESSING'\n | 'READY_TO_SHIP'\n | 'SHIPPED'\n | 'IN_TRANSIT'\n | 'OUT_FOR_DELIVERY'\n | 'DELIVERED'\n | 'FAILED'\n | 'RETURNED'\n | 'CANCELLED';\ntype IssueCategory = 'OUT_OF_STOCK' | 'CANNOT_FULFILL' | 'WRONG_ADDRESS' | 'OTHER';\n\ninterface IssueFlag {\n id: string;\n category: IssueCategory;\n note: string | null;\n status: 'OPEN' | 'RESOLVED';\n raisedByOrgId: string;\n createdAt: string;\n}\n\ninterface Fulfillment {\n status: FulfillmentStatus;\n carrier: string | null;\n trackingNumber: string | null;\n trackingUrl: string | null;\n}\n\ninterface AdminOrder {\n id: string;\n status: OrderStatus;\n userId: string;\n total: number;\n currency: string;\n createdAt: string;\n fulfillment: Fulfillment | null;\n issueFlags: IssueFlag[];\n}\n\nconst ORDER_STATUS_OPTIONS: { value: OrderStatus | ''; label: string }[] = [\n { value: '', label: 'All statuses' },\n { value: 'PENDING', label: 'Pending' },\n { value: 'PROCESSED', label: 'Processed' },\n { value: 'FAILED', label: 'Failed' },\n { value: 'REFUNDED', label: 'Refunded' },\n];\n\nconst FULFILLMENT_STYLES: Record<FulfillmentStatus, string> = {\n PENDING: 'bg-surface-warning-muted text-text-warning',\n PROCESSING: 'bg-surface-warning-muted text-text-warning',\n READY_TO_SHIP: 'bg-surface-accent-muted text-text-accent',\n SHIPPED: 'bg-surface-accent-muted text-text-accent',\n IN_TRANSIT: 'bg-surface-accent-muted text-text-accent',\n OUT_FOR_DELIVERY: 'bg-surface-accent-muted text-text-accent',\n DELIVERED: 'bg-surface-success-muted text-text-success',\n FAILED: 'bg-surface-danger-muted text-text-danger',\n RETURNED: 'bg-surface-danger-muted text-text-danger',\n CANCELLED: 'bg-surface-muted text-text-secondary',\n};\n\nconst ISSUE_CATEGORY_LABELS: Record<IssueCategory, string> = {\n OUT_OF_STOCK: 'Out of stock',\n CANNOT_FULFILL: 'Cannot fulfill',\n WRONG_ADDRESS: 'Wrong address',\n OTHER: 'Other',\n};\n\nconst ADMIN_PHYSICAL_ORDERS_QUERY = /* GraphQL */ `\n query AdminPhysicalOrders($page: Int!, $limit: Int!, $status: OrderStatus, $search: String) {\n adminPhysicalOrders(page: $page, limit: $limit, status: $status, search: $search) {\n total\n orders {\n id\n status\n userId\n total\n currency\n createdAt\n fulfillment {\n status\n carrier\n trackingNumber\n trackingUrl\n }\n issueFlags {\n id\n category\n note\n status\n raisedByOrgId\n createdAt\n }\n }\n }\n }\n`;\n\nconst RESOLVE_ORDER_ISSUE_MUTATION = /* GraphQL */ `\n mutation AdminResolveOrderIssue($id: ID!) {\n resolveOrderIssueFlag(id: $id) {\n id\n status\n }\n }\n`;\n\nconst PAGE_SIZE = 25;\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n/**\n * Admin-only visibility into physical (fulfillment-tracked) orders —\n * order/payment status, carrier/tracking, and any seller-reported issue\n * flags. Read-only for now: no cancel or refund action here (differs\n * between credits and Razorpay, waits on a proper store refund flow —\n * see BOFF-7192). Cancellation stays manual via the Shiprocket dashboard.\n */\nexport const AdminOrdersPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n\n const [orders, setOrders] = useState<AdminOrder[]>([]);\n const [total, setTotal] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [page, setPage] = useState(0);\n const [statusFilter, setStatusFilter] = useState<OrderStatus | ''>('');\n const [search, setSearch] = useState('');\n const [resolvingFlagId, setResolvingFlagId] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n adminPhysicalOrders: { total: number; orders: AdminOrder[] };\n }>({\n gateway: 'global',\n query: ADMIN_PHYSICAL_ORDERS_QUERY,\n variables: {\n page: page + 1,\n limit: PAGE_SIZE,\n status: statusFilter || undefined,\n search: search.trim() || undefined,\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load orders');\n }\n setTotal(result.data?.adminPhysicalOrders.total ?? 0);\n setOrders(result.data?.adminPhysicalOrders.orders ?? []);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load orders');\n } finally {\n setLoading(false);\n }\n }, [authToken, page, statusFilter, search]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const resolveFlag = async (flagId: string) => {\n setResolvingFlagId(flagId);\n try {\n await graphqlFetch({\n gateway: 'global',\n query: RESOLVE_ORDER_ISSUE_MUTATION,\n variables: { id: flagId },\n authToken: authToken || undefined,\n });\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to resolve issue');\n } finally {\n setResolvingFlagId(null);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-seam bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <Package className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Physical orders</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {total}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n <PagePurpose className=\"mb-4\">\n Payment/order status and Shiprocket fulfillment status for every physical order, plus any\n issue a seller has flagged (out of stock, cannot fulfill, wrong address). Read-only —\n cancellation and refunds still happen manually via the Shiprocket dashboard for now.\n </PagePurpose>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center\">\n <select\n aria-label=\"Filter by status\"\n value={statusFilter}\n onChange={(e) => {\n setStatusFilter(e.target.value as OrderStatus | '');\n setPage(0);\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n >\n {ORDER_STATUS_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n <input\n type=\"search\"\n aria-label=\"Search by order ID, buyer, or tracking number\"\n placeholder=\"Search order ID, buyer, or tracking number…\"\n value={search}\n onChange={(e) => {\n setSearch(e.target.value);\n setPage(0);\n }}\n className=\"flex-1 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary\"\n />\n </div>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading && orders.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">Loading orders…</output>\n ) : orders.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No physical orders\"\n description=\"Orders with a physical line item will show up here once customers make purchases.\"\n />\n ) : (\n <>\n <div className=\"overflow-hidden rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary\">\n <tr>\n <th className=\"px-4 py-2\">Order</th>\n <th className=\"px-4 py-2\">Buyer</th>\n <th className=\"px-4 py-2 text-right\">Total</th>\n <th className=\"px-4 py-2\">Status</th>\n <th className=\"px-4 py-2\">Fulfillment</th>\n <th className=\"px-4 py-2\">Tracking</th>\n <th className=\"px-4 py-2\">Issue</th>\n <th className=\"px-4 py-2\">Placed</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam bg-surface-raised\">\n {orders.map((order) => {\n const openFlag = order.issueFlags.find((f) => f.status === 'OPEN');\n return (\n <tr key={order.id} className=\"text-text-primary align-top\">\n <td className=\"px-4 py-2 font-mono text-xs\">\n {order.id.slice(0, 8).toUpperCase()}\n </td>\n <td className=\"px-4 py-2 font-mono text-xs text-text-secondary\">\n {order.userId.slice(0, 8)}…\n </td>\n <td className=\"px-4 py-2 text-right tabular-nums\">\n {formatPrice(order.total, order.currency)}\n </td>\n <td className=\"px-4 py-2\">\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {order.status}\n </span>\n </td>\n <td className=\"px-4 py-2\">\n <div className=\"flex items-center gap-1.5\">\n {order.fulfillment ? (\n <span\n className={`rounded-full px-2 py-0.5 text-xs font-medium ${FULFILLMENT_STYLES[order.fulfillment.status]}`}\n >\n {order.fulfillment.status.replace(/_/g, ' ')}\n </span>\n ) : (\n <span\n className=\"rounded-full bg-surface-danger-muted px-2 py-0.5 text-xs font-medium text-text-danger\"\n title=\"No fulfillment record — the post-payment fulfillment step never ran for this order\"\n >\n Not started\n </span>\n )}\n <button\n type=\"button\"\n title=\"View fulfillment logs for this order\"\n onClick={() =>\n navigate(`${basePath}/fulfillment-logs?orderId=${order.id}`)\n }\n className=\"text-text-muted hover:text-text-primary\"\n >\n <ScrollText className=\"size-3.5\" />\n </button>\n </div>\n </td>\n <td className=\"px-4 py-2 text-xs\">\n {order.fulfillment?.trackingNumber ? (\n <a\n href={order.fulfillment.trackingUrl ?? '#'}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 text-text-accent hover:underline\"\n >\n {order.fulfillment.carrier ?? 'Carrier'}\n <ExternalLink className=\"size-3\" />\n </a>\n ) : (\n <span className=\"text-text-muted\">—</span>\n )}\n </td>\n <td className=\"px-4 py-2\">\n {openFlag ? (\n <div className=\"flex flex-col items-start gap-1\">\n <span\n title={openFlag.note ?? undefined}\n className=\"flex items-center gap-1 rounded-full bg-surface-warning-muted px-2 py-0.5 text-xs font-medium text-text-warning\"\n >\n <Flag className=\"size-3\" />\n {ISSUE_CATEGORY_LABELS[openFlag.category]}\n </span>\n <button\n type=\"button\"\n onClick={() => void resolveFlag(openFlag.id)}\n disabled={resolvingFlagId === openFlag.id}\n className=\"text-xs font-medium text-text-secondary underline hover:text-text-primary disabled:opacity-50\"\n >\n {resolvingFlagId === openFlag.id ? (\n <Loader2 className=\"size-3 animate-spin\" />\n ) : (\n 'Mark resolved'\n )}\n </button>\n </div>\n ) : (\n <span className=\"text-text-muted\">—</span>\n )}\n </td>\n <td className=\"px-4 py-2 text-xs text-text-secondary whitespace-nowrap\">\n {formatDate(order.createdAt)}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {total > PAGE_SIZE && (\n <div className=\"mt-4 flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">\n Showing {page * PAGE_SIZE + 1}–{Math.min((page + 1) * PAGE_SIZE, total)} of{' '}\n {total}\n </span>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={page === 0}\n onClick={() => setPage((p) => p - 1)}\n className=\"rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n disabled={(page + 1) * PAGE_SIZE >= total}\n onClick={() => setPage((p) => p + 1)}\n className=\"rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-surface-hover disabled:opacity-40\"\n >\n Next\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|