@burdenoff/microfe-store 2026.912.4 → 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 +26 -6
- 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.d.ts +10 -0
- package/dist/pages/AdminOrdersPage.js +349 -0
- package/dist/pages/AdminOrdersPage.js.map +1 -0
- 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 = () => {
|
|
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,13 +38,33 @@ 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, {})
|
|
45
45
|
})
|
|
46
46
|
})
|
|
47
47
|
}),
|
|
48
|
+
/* @__PURE__ */ l(s, {
|
|
49
|
+
path: "orders",
|
|
50
|
+
element: /* @__PURE__ */ l(n, {
|
|
51
|
+
allowed: _,
|
|
52
|
+
children: /* @__PURE__ */ l(i, {
|
|
53
|
+
fallback: /* @__PURE__ */ l(r, {}),
|
|
54
|
+
children: /* @__PURE__ */ l(h, {})
|
|
55
|
+
})
|
|
56
|
+
})
|
|
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
|
+
}),
|
|
48
68
|
/* @__PURE__ */ l(s, {
|
|
49
69
|
path: "reviews",
|
|
50
70
|
element: /* @__PURE__ */ l(n, {
|
|
@@ -65,6 +85,6 @@ var d = a(() => import("./pages/AdminDashboardPage.js").then((e) => ({ default:
|
|
|
65
85
|
] });
|
|
66
86
|
};
|
|
67
87
|
//#endregion
|
|
68
|
-
export {
|
|
88
|
+
export { _ as StoreAdminRoutes };
|
|
69
89
|
|
|
70
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\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=\"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,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,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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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Admin-only visibility into physical (fulfillment-tracked) orders —
|
|
5
|
+
* order/payment status, carrier/tracking, and any seller-reported issue
|
|
6
|
+
* flags. Read-only for now: no cancel or refund action here (differs
|
|
7
|
+
* between credits and Razorpay, waits on a proper store refund flow —
|
|
8
|
+
* see BOFF-7192). Cancellation stays manual via the Shiprocket dashboard.
|
|
9
|
+
*/
|
|
10
|
+
export declare const AdminOrdersPage: FC;
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { useStore as e } from "../providers/StoreProvider.js";
|
|
2
|
+
import { formatPrice as t } from "../utils/index.js";
|
|
3
|
+
import { useCallback as n, useEffect as r, useState as i } from "react";
|
|
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, 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
|
+
//#region src/pages/AdminOrdersPage.tsx
|
|
10
|
+
var y = [
|
|
11
|
+
{
|
|
12
|
+
value: "",
|
|
13
|
+
label: "All statuses"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "PENDING",
|
|
17
|
+
label: "Pending"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: "PROCESSED",
|
|
21
|
+
label: "Processed"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "FAILED",
|
|
25
|
+
label: "Failed"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: "REFUNDED",
|
|
29
|
+
label: "Refunded"
|
|
30
|
+
}
|
|
31
|
+
], b = {
|
|
32
|
+
PENDING: "bg-surface-warning-muted text-text-warning",
|
|
33
|
+
PROCESSING: "bg-surface-warning-muted text-text-warning",
|
|
34
|
+
READY_TO_SHIP: "bg-surface-accent-muted text-text-accent",
|
|
35
|
+
SHIPPED: "bg-surface-accent-muted text-text-accent",
|
|
36
|
+
IN_TRANSIT: "bg-surface-accent-muted text-text-accent",
|
|
37
|
+
OUT_FOR_DELIVERY: "bg-surface-accent-muted text-text-accent",
|
|
38
|
+
DELIVERED: "bg-surface-success-muted text-text-success",
|
|
39
|
+
FAILED: "bg-surface-danger-muted text-text-danger",
|
|
40
|
+
RETURNED: "bg-surface-danger-muted text-text-danger",
|
|
41
|
+
CANCELLED: "bg-surface-muted text-text-secondary"
|
|
42
|
+
}, x = {
|
|
43
|
+
OUT_OF_STOCK: "Out of stock",
|
|
44
|
+
CANNOT_FULFILL: "Cannot fulfill",
|
|
45
|
+
WRONG_ADDRESS: "Wrong address",
|
|
46
|
+
OTHER: "Other"
|
|
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
|
+
return new Date(e).toLocaleDateString("en-US", {
|
|
50
|
+
year: "numeric",
|
|
51
|
+
month: "short",
|
|
52
|
+
day: "numeric",
|
|
53
|
+
hour: "2-digit",
|
|
54
|
+
minute: "2-digit"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
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
|
+
try {
|
|
61
|
+
let e = await p({
|
|
62
|
+
gateway: "global",
|
|
63
|
+
query: S,
|
|
64
|
+
variables: {
|
|
65
|
+
page: L + 1,
|
|
66
|
+
limit: w,
|
|
67
|
+
status: z || void 0,
|
|
68
|
+
search: V.trim() || void 0
|
|
69
|
+
},
|
|
70
|
+
authToken: D || void 0
|
|
71
|
+
});
|
|
72
|
+
if (e.errors?.length) throw Error(e.errors[0]?.message ?? "Failed to load orders");
|
|
73
|
+
M(e.data?.adminPhysicalOrders.total ?? 0), A(e.data?.adminPhysicalOrders.orders ?? []);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
I(e instanceof Error ? e.message : "Failed to load orders");
|
|
76
|
+
} finally {
|
|
77
|
+
P(!1);
|
|
78
|
+
}
|
|
79
|
+
}, [
|
|
80
|
+
D,
|
|
81
|
+
L,
|
|
82
|
+
z,
|
|
83
|
+
V
|
|
84
|
+
]);
|
|
85
|
+
r(() => {
|
|
86
|
+
G();
|
|
87
|
+
}, [G]);
|
|
88
|
+
let K = async (e) => {
|
|
89
|
+
W(e);
|
|
90
|
+
try {
|
|
91
|
+
await p({
|
|
92
|
+
gateway: "global",
|
|
93
|
+
query: C,
|
|
94
|
+
variables: { id: e },
|
|
95
|
+
authToken: D || void 0
|
|
96
|
+
}), await G();
|
|
97
|
+
} catch (e) {
|
|
98
|
+
I(e instanceof Error ? e.message : "Failed to resolve issue");
|
|
99
|
+
} finally {
|
|
100
|
+
W(null);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
return /* @__PURE__ */ g("div", {
|
|
104
|
+
className: "min-h-screen bg-surface-default",
|
|
105
|
+
children: [/* @__PURE__ */ h("header", {
|
|
106
|
+
className: "border-b border-border-seam bg-surface-raised",
|
|
107
|
+
children: /* @__PURE__ */ h("div", {
|
|
108
|
+
className: "mx-auto max-w-7xl px-6 py-4",
|
|
109
|
+
children: /* @__PURE__ */ g("div", {
|
|
110
|
+
className: "flex items-center justify-between",
|
|
111
|
+
children: [/* @__PURE__ */ g("div", {
|
|
112
|
+
className: "flex items-center gap-3",
|
|
113
|
+
children: [/* @__PURE__ */ g("button", {
|
|
114
|
+
type: "button",
|
|
115
|
+
onClick: () => E(`${O}/dashboard`),
|
|
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__ */ h(o, { className: "size-4" }), "Back"]
|
|
118
|
+
}), /* @__PURE__ */ g("div", {
|
|
119
|
+
className: "flex items-center gap-2",
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ h(u, { className: "size-5 text-text-primary" }),
|
|
122
|
+
/* @__PURE__ */ h("h1", {
|
|
123
|
+
className: "text-xl font-semibold text-text-primary",
|
|
124
|
+
children: "Physical orders"
|
|
125
|
+
}),
|
|
126
|
+
/* @__PURE__ */ h("span", {
|
|
127
|
+
className: "rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent",
|
|
128
|
+
children: j
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
})]
|
|
132
|
+
}), /* @__PURE__ */ g("button", {
|
|
133
|
+
type: "button",
|
|
134
|
+
onClick: () => void G(),
|
|
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: N,
|
|
137
|
+
children: [/* @__PURE__ */ h(d, { className: `size-4 ${N ? "animate-spin" : ""}` }), "Refresh"]
|
|
138
|
+
})]
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
}), /* @__PURE__ */ g("main", {
|
|
142
|
+
className: "mx-auto max-w-7xl p-6",
|
|
143
|
+
children: [
|
|
144
|
+
/* @__PURE__ */ h(v, {
|
|
145
|
+
className: "mb-4",
|
|
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
|
+
}),
|
|
148
|
+
/* @__PURE__ */ g("div", {
|
|
149
|
+
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center",
|
|
150
|
+
children: [/* @__PURE__ */ h("select", {
|
|
151
|
+
"aria-label": "Filter by status",
|
|
152
|
+
value: z,
|
|
153
|
+
onChange: (e) => {
|
|
154
|
+
B(e.target.value), R(0);
|
|
155
|
+
},
|
|
156
|
+
className: "rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary",
|
|
157
|
+
children: y.map((e) => /* @__PURE__ */ h("option", {
|
|
158
|
+
value: e.value,
|
|
159
|
+
children: e.label
|
|
160
|
+
}, e.value))
|
|
161
|
+
}), /* @__PURE__ */ h("input", {
|
|
162
|
+
type: "search",
|
|
163
|
+
"aria-label": "Search by order ID, buyer, or tracking number",
|
|
164
|
+
placeholder: "Search order ID, buyer, or tracking number…",
|
|
165
|
+
value: V,
|
|
166
|
+
onChange: (e) => {
|
|
167
|
+
H(e.target.value), R(0);
|
|
168
|
+
},
|
|
169
|
+
className: "flex-1 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary"
|
|
170
|
+
})]
|
|
171
|
+
}),
|
|
172
|
+
F && /* @__PURE__ */ h("div", {
|
|
173
|
+
role: "alert",
|
|
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: F
|
|
176
|
+
}),
|
|
177
|
+
N && k.length === 0 ? /* @__PURE__ */ h("output", {
|
|
178
|
+
className: "block py-16 text-center text-text-secondary",
|
|
179
|
+
children: "Loading orders…"
|
|
180
|
+
}) : k.length === 0 ? /* @__PURE__ */ h(_, {
|
|
181
|
+
illustration: "empty-generic",
|
|
182
|
+
title: "No physical orders",
|
|
183
|
+
description: "Orders with a physical line item will show up here once customers make purchases."
|
|
184
|
+
}) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h("div", {
|
|
185
|
+
className: "overflow-hidden rounded-lg border border-border-seam",
|
|
186
|
+
children: /* @__PURE__ */ g("table", {
|
|
187
|
+
className: "w-full text-sm",
|
|
188
|
+
children: [/* @__PURE__ */ h("thead", {
|
|
189
|
+
className: "bg-surface-muted text-left text-xs uppercase tracking-wide text-text-secondary",
|
|
190
|
+
children: /* @__PURE__ */ g("tr", { children: [
|
|
191
|
+
/* @__PURE__ */ h("th", {
|
|
192
|
+
className: "px-4 py-2",
|
|
193
|
+
children: "Order"
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ h("th", {
|
|
196
|
+
className: "px-4 py-2",
|
|
197
|
+
children: "Buyer"
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ h("th", {
|
|
200
|
+
className: "px-4 py-2 text-right",
|
|
201
|
+
children: "Total"
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ h("th", {
|
|
204
|
+
className: "px-4 py-2",
|
|
205
|
+
children: "Status"
|
|
206
|
+
}),
|
|
207
|
+
/* @__PURE__ */ h("th", {
|
|
208
|
+
className: "px-4 py-2",
|
|
209
|
+
children: "Fulfillment"
|
|
210
|
+
}),
|
|
211
|
+
/* @__PURE__ */ h("th", {
|
|
212
|
+
className: "px-4 py-2",
|
|
213
|
+
children: "Tracking"
|
|
214
|
+
}),
|
|
215
|
+
/* @__PURE__ */ h("th", {
|
|
216
|
+
className: "px-4 py-2",
|
|
217
|
+
children: "Issue"
|
|
218
|
+
}),
|
|
219
|
+
/* @__PURE__ */ h("th", {
|
|
220
|
+
className: "px-4 py-2",
|
|
221
|
+
children: "Placed"
|
|
222
|
+
})
|
|
223
|
+
] })
|
|
224
|
+
}), /* @__PURE__ */ h("tbody", {
|
|
225
|
+
className: "divide-y divide-border-seam bg-surface-raised",
|
|
226
|
+
children: k.map((e) => {
|
|
227
|
+
let n = e.issueFlags.find((e) => e.status === "OPEN");
|
|
228
|
+
return /* @__PURE__ */ g("tr", {
|
|
229
|
+
className: "text-text-primary align-top",
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ h("td", {
|
|
232
|
+
className: "px-4 py-2 font-mono text-xs",
|
|
233
|
+
children: e.id.slice(0, 8).toUpperCase()
|
|
234
|
+
}),
|
|
235
|
+
/* @__PURE__ */ g("td", {
|
|
236
|
+
className: "px-4 py-2 font-mono text-xs text-text-secondary",
|
|
237
|
+
children: [e.userId.slice(0, 8), "…"]
|
|
238
|
+
}),
|
|
239
|
+
/* @__PURE__ */ h("td", {
|
|
240
|
+
className: "px-4 py-2 text-right tabular-nums",
|
|
241
|
+
children: t(e.total, e.currency)
|
|
242
|
+
}),
|
|
243
|
+
/* @__PURE__ */ h("td", {
|
|
244
|
+
className: "px-4 py-2",
|
|
245
|
+
children: /* @__PURE__ */ h("span", {
|
|
246
|
+
className: "rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent",
|
|
247
|
+
children: e.status
|
|
248
|
+
})
|
|
249
|
+
}),
|
|
250
|
+
/* @__PURE__ */ h("td", {
|
|
251
|
+
className: "px-4 py-2",
|
|
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
|
+
})]
|
|
268
|
+
})
|
|
269
|
+
}),
|
|
270
|
+
/* @__PURE__ */ h("td", {
|
|
271
|
+
className: "px-4 py-2 text-xs",
|
|
272
|
+
children: e.fulfillment?.trackingNumber ? /* @__PURE__ */ g("a", {
|
|
273
|
+
href: e.fulfillment.trackingUrl ?? "#",
|
|
274
|
+
target: "_blank",
|
|
275
|
+
rel: "noreferrer",
|
|
276
|
+
className: "inline-flex items-center gap-1 text-text-accent hover:underline",
|
|
277
|
+
children: [e.fulfillment.carrier ?? "Carrier", /* @__PURE__ */ h(s, { className: "size-3" })]
|
|
278
|
+
}) : /* @__PURE__ */ h("span", {
|
|
279
|
+
className: "text-text-muted",
|
|
280
|
+
children: "—"
|
|
281
|
+
})
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ h("td", {
|
|
284
|
+
className: "px-4 py-2",
|
|
285
|
+
children: n ? /* @__PURE__ */ g("div", {
|
|
286
|
+
className: "flex flex-col items-start gap-1",
|
|
287
|
+
children: [/* @__PURE__ */ g("span", {
|
|
288
|
+
title: n.note ?? void 0,
|
|
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",
|
|
290
|
+
children: [/* @__PURE__ */ h(c, { className: "size-3" }), x[n.category]]
|
|
291
|
+
}), /* @__PURE__ */ h("button", {
|
|
292
|
+
type: "button",
|
|
293
|
+
onClick: () => void K(n.id),
|
|
294
|
+
disabled: U === n.id,
|
|
295
|
+
className: "text-xs font-medium text-text-secondary underline hover:text-text-primary disabled:opacity-50",
|
|
296
|
+
children: U === n.id ? /* @__PURE__ */ h(l, { className: "size-3 animate-spin" }) : "Mark resolved"
|
|
297
|
+
})]
|
|
298
|
+
}) : /* @__PURE__ */ h("span", {
|
|
299
|
+
className: "text-text-muted",
|
|
300
|
+
children: "—"
|
|
301
|
+
})
|
|
302
|
+
}),
|
|
303
|
+
/* @__PURE__ */ h("td", {
|
|
304
|
+
className: "px-4 py-2 text-xs text-text-secondary whitespace-nowrap",
|
|
305
|
+
children: T(e.createdAt)
|
|
306
|
+
})
|
|
307
|
+
]
|
|
308
|
+
}, e.id);
|
|
309
|
+
})
|
|
310
|
+
})]
|
|
311
|
+
})
|
|
312
|
+
}), j > w && /* @__PURE__ */ g("div", {
|
|
313
|
+
className: "mt-4 flex items-center justify-between text-sm",
|
|
314
|
+
children: [/* @__PURE__ */ g("span", {
|
|
315
|
+
className: "text-text-secondary",
|
|
316
|
+
children: [
|
|
317
|
+
"Showing ",
|
|
318
|
+
L * w + 1,
|
|
319
|
+
"–",
|
|
320
|
+
Math.min((L + 1) * w, j),
|
|
321
|
+
" of",
|
|
322
|
+
" ",
|
|
323
|
+
j
|
|
324
|
+
]
|
|
325
|
+
}), /* @__PURE__ */ g("div", {
|
|
326
|
+
className: "flex items-center gap-2",
|
|
327
|
+
children: [/* @__PURE__ */ h("button", {
|
|
328
|
+
type: "button",
|
|
329
|
+
disabled: L === 0,
|
|
330
|
+
onClick: () => R((e) => e - 1),
|
|
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",
|
|
332
|
+
children: "Previous"
|
|
333
|
+
}), /* @__PURE__ */ h("button", {
|
|
334
|
+
type: "button",
|
|
335
|
+
disabled: (L + 1) * w >= j,
|
|
336
|
+
onClick: () => R((e) => e + 1),
|
|
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",
|
|
338
|
+
children: "Next"
|
|
339
|
+
})]
|
|
340
|
+
})]
|
|
341
|
+
})] })
|
|
342
|
+
]
|
|
343
|
+
})]
|
|
344
|
+
});
|
|
345
|
+
};
|
|
346
|
+
//#endregion
|
|
347
|
+
export { E as AdminOrdersPage };
|
|
348
|
+
|
|
349
|
+
//# sourceMappingURL=AdminOrdersPage.js.map
|
|
@@ -0,0 +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 {\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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"}
|