@develemit/commerce-admin 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +147 -2
  2. package/dist/client/admin-client.d.ts +37 -0
  3. package/dist/client/errors.d.ts +47 -0
  4. package/dist/client/index.d.ts +3 -0
  5. package/dist/client/index.js +1 -0
  6. package/dist/client/map-error.d.ts +2 -0
  7. package/dist/client/transport.d.ts +19 -0
  8. package/dist/client--_j5r8ag.js +168 -0
  9. package/dist/client--_j5r8ag.js.map +1 -0
  10. package/dist/components/admin-layout/admin-layout.d.ts +13 -0
  11. package/dist/components/admin-table/admin-table.d.ts +13 -0
  12. package/dist/components/confirm-dialog/confirm-dialog.d.ts +12 -0
  13. package/dist/components/error-message/error-message.d.ts +5 -0
  14. package/dist/components/money/money.d.ts +5 -0
  15. package/dist/components/notice/notice.d.ts +9 -0
  16. package/dist/components/status-badge/status-badge.d.ts +3 -0
  17. package/dist/index.d.ts +27 -0
  18. package/dist/index.js +2977 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/lib/admin-labels.d.ts +26 -0
  21. package/dist/lib/discount-form.d.ts +28 -0
  22. package/dist/lib/discount-labels.d.ts +56 -0
  23. package/dist/lib/discount-status.d.ts +5 -0
  24. package/dist/lib/image-labels.d.ts +27 -0
  25. package/dist/lib/image-rules.d.ts +4 -0
  26. package/dist/lib/merge-labels.d.ts +19 -0
  27. package/dist/lib/money-input.d.ts +2 -0
  28. package/dist/lib/order-action-labels.d.ts +28 -0
  29. package/dist/lib/order-labels.d.ts +66 -0
  30. package/dist/lib/product-filter.d.ts +6 -0
  31. package/dist/lib/product-form.d.ts +29 -0
  32. package/dist/lib/product-labels.d.ts +52 -0
  33. package/dist/lib/provider.d.ts +45 -0
  34. package/dist/lib/refund-rules.d.ts +7 -0
  35. package/dist/lib/shipping-labels.d.ts +33 -0
  36. package/dist/lib/shipping-rates.d.ts +31 -0
  37. package/dist/lib/use-admin-mutation.d.ts +7 -0
  38. package/dist/lib/use-admin-query.d.ts +7 -0
  39. package/dist/lib/use-debounced.d.ts +1 -0
  40. package/dist/lib/use-discount-pages.d.ts +26 -0
  41. package/dist/lib/use-image-upload.d.ts +18 -0
  42. package/dist/lib/use-order-pages.d.ts +17 -0
  43. package/dist/lib/use-product-pages.d.ts +30 -0
  44. package/dist/styles.css +418 -0
  45. package/dist/views/discounts/admin-discount-editor.d.ts +6 -0
  46. package/dist/views/discounts/admin-discount-list.d.ts +1 -0
  47. package/dist/views/discounts/discount-fields.d.ts +10 -0
  48. package/dist/views/discounts/discount-value.d.ts +4 -0
  49. package/dist/views/orders/admin-order-detail.d.ts +10 -0
  50. package/dist/views/orders/admin-order-list.d.ts +1 -0
  51. package/dist/views/orders/order-actions.d.ts +7 -0
  52. package/dist/views/orders/order-fulfillment.d.ts +4 -0
  53. package/dist/views/orders/order-items.d.ts +4 -0
  54. package/dist/views/orders/order-refunds.d.ts +4 -0
  55. package/dist/views/orders/refund-dialog.d.ts +8 -0
  56. package/dist/views/orders/resolve-button.d.ts +7 -0
  57. package/dist/views/orders/ship-form.d.ts +7 -0
  58. package/dist/views/products/admin-product-editor.d.ts +9 -0
  59. package/dist/views/products/admin-product-images.d.ts +6 -0
  60. package/dist/views/products/admin-product-list.d.ts +1 -0
  61. package/dist/views/products/image-alt-editor.d.ts +9 -0
  62. package/dist/views/products/image-tile.d.ts +15 -0
  63. package/dist/views/products/product-fields.d.ts +12 -0
  64. package/dist/views/products/test-helpers.d.ts +6 -0
  65. package/dist/views/products/upload-picker.d.ts +7 -0
  66. package/dist/views/shipping/admin-shipping-rates.d.ts +4 -0
  67. package/dist/views/shipping/shipping-rate-row.d.ts +15 -0
  68. package/package.json +44 -3
package/README.md CHANGED
@@ -1,3 +1,148 @@
1
- # Temporary Holding Version
1
+ # @develemit/commerce-admin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Admin UI for the develemit commerce kit: product, order and shipping-rate screens for the `@develemit/commerce-server` admin API.
4
+
5
+ Depends on `@develemit/commerce-contracts` only. Themed through the same `--sf-*` CSS variables as `@develemit/storefront`; import `@develemit/commerce-admin/styles.css` for the admin-only `sfa-` classes.
6
+
7
+ ```tsx
8
+ import { AdminProvider } from '@develemit/commerce-admin';
9
+ import '@develemit/commerce-admin/styles.css';
10
+ ```
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @develemit/commerce-admin @develemit/commerce-contracts
16
+ ```
17
+
18
+ Peers: `react` and `react-dom` `^19`. Import `@develemit/commerce-admin/styles.css` in the admin layout only, so the `sfa-` classes stay out of your storefront pages.
19
+
20
+ ## Provider and client
21
+
22
+ ```tsx
23
+ 'use client';
24
+ import { AdminLayout, AdminProvider } from '@develemit/commerce-admin';
25
+ import { createAdminClient } from '@develemit/commerce-admin/client';
26
+ import Link from 'next/link';
27
+ import Image from 'next/image';
28
+
29
+ const client = createAdminClient({ baseUrl: '/api' });
30
+
31
+ export function Admin({ children }) {
32
+ return (
33
+ <AdminProvider
34
+ client={client}
35
+ renderLink={({ href, className, children }) => (
36
+ <Link href={href} className={className}>
37
+ {children}
38
+ </Link>
39
+ )}
40
+ renderImage={({ src, alt, className }) => (
41
+ <Image
42
+ src={src}
43
+ alt={alt}
44
+ className={className}
45
+ width={96}
46
+ height={96}
47
+ unoptimized
48
+ />
49
+ )}
50
+ hrefs={{ storefrontProduct: (slug) => `/shop/${slug}` }}
51
+ >
52
+ <AdminLayout>{children}</AdminLayout>
53
+ </AdminProvider>
54
+ );
55
+ }
56
+ ```
57
+
58
+ The client calls `${baseUrl}/admin/...`, matching the server's admin routes. `hrefs` defaults to `/admin/products`, `/admin/products/:id`, `/admin/products/new`, `/admin/orders`, `/admin/orders/:id`, `/admin/shipping-rates`, `/admin/discounts`, `/admin/discounts/:id` and `/admin/discounts/new`.
59
+
60
+ ## Auth
61
+
62
+ The server's `admin.authorize(req)` decides who may call the admin API. Never put an admin token in browser code. Two patterns:
63
+
64
+ 1. **Server-side proxy (recommended).** A route handler in your web app checks your own session (for example your login cookie), then forwards the request to the commerce API with the admin token from a server-only env var. The browser client points at the proxy (`baseUrl: '/api'`). See `apps/playground-web/src/app/api/admin/[...path]/route.ts`; it forwards method, query, body (including multipart image uploads) and `idempotency-key`.
65
+ 2. **Cookie session.** Serve the commerce API on the same site and have `authorize` read your session cookie. The client then needs no token; use `headers` or a custom `fetch` with `credentials: 'include'` if the API is on another origin.
66
+
67
+ ## Primitives
68
+
69
+ This version ships `AdminProvider`, `useAdmin()` and the shared primitives:
70
+
71
+ - `AdminLayout` — nav from `hrefs`, plus `extraNav` (`{ href, label, position? }`) and `header`/`footer` slots for host content.
72
+ - `AdminTable` — stacks into labeled cards below 640px.
73
+ - `StatusBadge` — text comes from `labels.statuses`.
74
+ - `Money` — integer cents and an ISO-4217 currency, formatted with `locale`.
75
+ - `ConfirmDialog` — modal with focus trap, Escape and focus return.
76
+ - `Notice` — `success`/`warning` use `role="status"`, `error` uses `role="alert"`; `toast` auto-hides after `duration` and sits above the safe-area inset.
77
+ - `ErrorMessage` — maps the typed client errors to copy in `labels.errors`.
78
+ - `useAdminQuery(key, fn)` → `{ data, error, loading, reload }` (ignores stale responses) and `useAdminMutation(fn)` → `{ run, pending, error, reset }`. No cache; call `reload()` after a mutation.
79
+
80
+ Override any copy through `AdminProvider`'s `labels` (`nav`, `statuses` and `errors` merge key by key).
81
+
82
+ ## Product screens
83
+
84
+ ```tsx
85
+ <AdminProvider
86
+ client={client}
87
+ hrefs={{ storefrontProduct: (slug) => `/shop/${slug}` }}
88
+ >
89
+ <AdminProductList />
90
+ </AdminProvider>
91
+
92
+ <AdminProductEditor
93
+ productId={id} // omit to create
94
+ onSaved={(product) => router.push(hrefs.product(product.id))}
95
+ imagesSlot={null}
96
+ categories={['Necklaces', 'Rings']} // optional suggestions
97
+ />
98
+ ```
99
+
100
+ - `AdminProductList` — status filters (All, Live, Sold, Drafts, Archived; "Sold" is active with zero stock), name search over the loaded list, a single row of filter buttons that scrolls sideways on narrow screens, thumbnails, "View in shop" when `hrefs.storefrontProduct` is set, and confirmed Mark sold / Archive actions.
101
+ - `AdminProductEditor` — create or edit every product field. Validates with the contract schema, shows inline errors with `aria-invalid`/`aria-describedby`, focuses the first invalid field, maps a server `INVALID_INPUT` onto fields, and PATCHes only changed fields. `imagesSlot` renders in edit mode only. Pass `categories` (the categories already in use) to offer them as `<datalist>` suggestions on the Category input; free text is still allowed, and without it the input is a plain text field.
102
+ - Prices are typed in dollars and parsed to integer cents from the string (`parseMoneyInput`, `formatMoneyInput`).
103
+ - Copy lives under `labels.products` and merges key by key.
104
+
105
+ ## Order screens
106
+
107
+ ```tsx
108
+ <AdminOrderList />
109
+
110
+ <AdminOrderDetail
111
+ orderId={id}
112
+ actions={({ order, reload }) => null} // slot for ship/refund/resolve
113
+ />
114
+ ```
115
+
116
+ - `AdminOrderList` — tabs To ship (default), Shipped, Needs attention, Pending refund and All; debounced (300ms) search by email or order id; "Load more" cursor paging. Changing a tab or search restarts from the first page.
117
+ - `AdminOrderDetail` — items and totals, buyer, shipping address, fulfillment (carrier and tracking link, or "Not shipped"), refund history with pending refunds marked distinctly, and the remaining balance. Dates use the provider `locale`.
118
+ - Rows link through `hrefs.order(id)` and `renderLink`. Copy lives under `labels.orders`.
119
+
120
+ ## Discount screens
121
+
122
+ ```tsx
123
+ <AdminDiscountList />
124
+ <AdminDiscountEditor discountId={id} onSaved={(d) => router.push(`/admin/discounts/${d.id}`)} />
125
+ ```
126
+
127
+ - `AdminDiscountList` — Active (default), Scheduled, Expired and Inactive tabs sent to the server as `status`; debounced (300ms) code search; "Load more" paging; usage shown as "12 / 50 used" (committed plus reserved redemptions).
128
+ - `AdminDiscountEditor` — omit `discountId` to create. Percent or fixed toggle, money input in the provider `currency`, optional minimum subtotal, start and end (`datetime-local`, sent as ISO) and usage limit. Once a discount has been used, the code, type and value are disabled with an explanation; a duplicate code is shown on the code field. Deactivate asks for confirmation; Reactivate does not.
129
+ - Rows link through `hrefs.discount(id)`, `hrefs.newDiscount` and `renderLink`. Add a nav entry with `AdminLayout`'s `extraNav`. Copy lives under `labels.discounts`.
130
+
131
+ ## Shipping rates
132
+
133
+ ```tsx
134
+ <AdminShippingRates onDirtyChange={(dirty) => setBlockNavigation(dirty)} />
135
+ ```
136
+
137
+ - `AdminShippingRates` — loads the shop's rates into local state, then one "Save changes" sends the whole ordered list in a single `PUT`. Rates can be added, edited, reordered (move earlier/later), deactivated and removed.
138
+ - Removing a rate asks for confirmation and warns that it disappears from checkout once saved. Past orders keep the rate name they were placed with.
139
+ - Saving is blocked client-side for duplicate names, a fastest-days value above slowest-days, bad prices, and zero active rates (checkout needs at least one). A server `INVALID_INPUT` is shown on the offending rows and local edits are kept.
140
+ - `onDirtyChange(dirty)` fires when unsaved edits appear or are cleared, so a host can guard its router. Copy lives under `labels.shipping`.
141
+
142
+ ## Images
143
+
144
+ The default `renderImage` is a lazy, async `<img>` (`priority` makes it eager). Uploads can be up to 8MB and the default does not resize, so production hosts should supply a resizing `renderImage` (for example `next/image`) or serve through a CDN.
145
+
146
+ ## Theming
147
+
148
+ The admin reads the same `--sf-*` variables as the storefront (`--sf-color-accent`, spacing, radius, typography), so a host that already themes its shop gets a matching admin. Admin-only classes carry the `sfa-` prefix.
@@ -0,0 +1,37 @@
1
+ import { AdminDiscountListQuery, AdminDiscountPage, AdminOrderDetail, AdminOrderListQuery, AdminOrderPage, AdminProduct, AdminProductListQuery, AdminProductPage, AdminProductInput, AdminProductPatch, Discount, DiscountInput, DiscountPatch, RefundOrderInput, ProductImagePatch, ShippingRate, ShippingRateInput, ShipOrderInput } from '@develemit/commerce-contracts';
2
+ import { AdminClientOptions } from './transport';
3
+ export type AdminOrderQuery = Partial<Pick<AdminOrderListQuery, 'status' | 'q' | 'hasPendingRefund' | 'cursor'>> & {
4
+ limit?: number;
5
+ };
6
+ export type AdminProductQuery = Pick<AdminProductListQuery, 'status' | 'stock' | 'q' | 'cursor' | 'limit'>;
7
+ export type AdminDiscountQuery = Partial<Pick<AdminDiscountListQuery, 'status' | 'q' | 'cursor' | 'limit'>>;
8
+ export interface RefundOptions {
9
+ idempotencyKey?: string;
10
+ }
11
+ export interface AdminClient {
12
+ listProducts(query?: AdminProductQuery): Promise<AdminProductPage>;
13
+ getProduct(id: string): Promise<AdminProduct>;
14
+ createProduct(input: AdminProductInput): Promise<AdminProduct>;
15
+ updateProduct(id: string, patch: AdminProductPatch): Promise<AdminProduct>;
16
+ uploadProductImage(id: string, file: Blob, alt?: string): Promise<AdminProduct>;
17
+ reorderProductImages(id: string, imageIds: string[]): Promise<AdminProduct>;
18
+ updateProductImage(id: string, imageId: string, patch: ProductImagePatch): Promise<AdminProduct>;
19
+ deleteProductImage(id: string, imageId: string): Promise<AdminProduct>;
20
+ listDiscounts(query?: AdminDiscountQuery): Promise<AdminDiscountPage>;
21
+ getDiscount(id: string): Promise<Discount>;
22
+ createDiscount(input: DiscountInput): Promise<Discount>;
23
+ updateDiscount(id: string, patch: DiscountPatch): Promise<Discount>;
24
+ listShippingRates(): Promise<ShippingRate[]>;
25
+ replaceShippingRates(rates: ShippingRateInput[]): Promise<ShippingRate[]>;
26
+ listOrders(query?: AdminOrderQuery): Promise<AdminOrderPage>;
27
+ getOrder(id: string): Promise<AdminOrderDetail>;
28
+ shipOrder(id: string, input?: ShipOrderInput): Promise<AdminOrderDetail>;
29
+ resolveOrder(id: string): Promise<AdminOrderDetail>;
30
+ /**
31
+ * Retrying the same intent must reuse the same key. When none is passed one
32
+ * is generated, and it is set on any thrown error as `idempotencyKey` so the
33
+ * caller can pass it back on retry.
34
+ */
35
+ refundOrder(id: string, input?: RefundOrderInput, options?: RefundOptions): Promise<AdminOrderDetail>;
36
+ }
37
+ export declare function createAdminClient(options: AdminClientOptions): AdminClient;
@@ -0,0 +1,47 @@
1
+ export declare class AdminApiError extends Error {
2
+ readonly status: number;
3
+ readonly url: string;
4
+ readonly code?: string | undefined;
5
+ idempotencyKey?: string;
6
+ constructor(status: number, url: string, code?: string | undefined, message?: string);
7
+ }
8
+ export declare class AdminUnauthorizedError extends AdminApiError {
9
+ constructor(url: string);
10
+ }
11
+ export declare class AdminNotFoundError extends AdminApiError {
12
+ constructor(url: string, code?: string, message?: string);
13
+ }
14
+ export declare class AdminInvalidInputError extends AdminApiError {
15
+ readonly issues: unknown[];
16
+ constructor(url: string, issues: unknown[]);
17
+ }
18
+ export declare class InvalidOrderStateError extends AdminApiError {
19
+ readonly orderStatus: string;
20
+ constructor(url: string, orderStatus: string, message?: string);
21
+ }
22
+ export declare class InsufficientStockError extends AdminApiError {
23
+ readonly productIds: string[];
24
+ constructor(url: string, productIds: string[], message?: string);
25
+ }
26
+ export declare class RefundExceedsBalanceError extends AdminApiError {
27
+ readonly balanceCents: number | undefined;
28
+ constructor(url: string, balanceCents: number | undefined, message?: string);
29
+ }
30
+ export declare class RefundGatewayError extends AdminApiError {
31
+ constructor(url: string, message?: string);
32
+ }
33
+ export declare class RefundOutcomeUnknownError extends AdminApiError {
34
+ constructor(url: string, message?: string);
35
+ }
36
+ export declare class InvalidImageError extends AdminApiError {
37
+ constructor(url: string, code: string, message?: string);
38
+ }
39
+ export declare class ImageTooLargeError extends AdminApiError {
40
+ constructor(url: string);
41
+ }
42
+ export declare class DiscountCodeTakenError extends AdminApiError {
43
+ constructor(url: string, message?: string);
44
+ }
45
+ export declare class DiscountInUseError extends AdminApiError {
46
+ constructor(url: string, message?: string);
47
+ }
@@ -0,0 +1,3 @@
1
+ export * from './admin-client';
2
+ export * from './errors';
3
+ export type { AdminClientOptions } from './transport';
@@ -0,0 +1 @@
1
+ export { n as AdminApiError, r as AdminInvalidInputError, i as AdminNotFoundError, a as AdminUnauthorizedError, o as DiscountCodeTakenError, s as DiscountInUseError, c as ImageTooLargeError, l as InsufficientStockError, u as InvalidImageError, d as InvalidOrderStateError, f as RefundExceedsBalanceError, p as RefundGatewayError, m as RefundOutcomeUnknownError, t as createAdminClient } from "../client--_j5r8ag.js";
@@ -0,0 +1,2 @@
1
+ import { AdminApiError } from './errors';
2
+ export declare function mapError(url: string, res: Response): Promise<AdminApiError>;
@@ -0,0 +1,19 @@
1
+ export interface AdminClientOptions {
2
+ baseUrl: string;
3
+ fetch?: typeof fetch;
4
+ headers?: () => HeadersInit | Promise<HeadersInit>;
5
+ }
6
+ type Schema<T> = {
7
+ parse(data: unknown): T;
8
+ };
9
+ export interface RequestOptions {
10
+ method?: string;
11
+ body?: unknown;
12
+ form?: FormData;
13
+ headers?: Record<string, string>;
14
+ }
15
+ export interface Transport {
16
+ request<T>(path: string, schema: Schema<T>, options?: RequestOptions): Promise<T>;
17
+ }
18
+ export declare function createTransport({ baseUrl, fetch: fetchImpl, headers: getHeaders, }: AdminClientOptions): Transport;
19
+ export {};
@@ -0,0 +1,168 @@
1
+ import { adminDiscountPageSchema as e, adminOrderDetailSchema as t, adminOrderPageSchema as n, adminProductPageSchema as r, adminProductSchema as i, discountSchema as a, shippingRateSchema as o } from "@develemit/commerce-contracts";
2
+ // src/client/errors.ts
3
+ var s = class extends Error {
4
+ status;
5
+ url;
6
+ code;
7
+ idempotencyKey;
8
+ constructor(e, t, n, r) {
9
+ super(r ?? `Admin API request failed (${e}${n ? ` ${n}` : ""}): ${t}`), this.status = e, this.url = t, this.code = n, this.name = "AdminApiError";
10
+ }
11
+ }, c = class extends s {
12
+ constructor(e) {
13
+ super(401, e, "UNAUTHORIZED", `Not authorized: ${e}`), this.name = "AdminUnauthorizedError";
14
+ }
15
+ }, l = class extends s {
16
+ constructor(e, t, n) {
17
+ super(404, e, t, n ?? `Not found: ${e}`), this.name = "AdminNotFoundError";
18
+ }
19
+ }, u = class extends s {
20
+ issues;
21
+ constructor(e, t) {
22
+ super(400, e, "INVALID_INPUT", `Invalid input: ${e}`), this.issues = t, this.name = "AdminInvalidInputError";
23
+ }
24
+ }, d = class extends s {
25
+ orderStatus;
26
+ constructor(e, t, n) {
27
+ super(409, e, "INVALID_ORDER_STATE", n), this.orderStatus = t, this.name = "InvalidOrderStateError";
28
+ }
29
+ }, f = class extends s {
30
+ productIds;
31
+ constructor(e, t, n) {
32
+ super(409, e, "INSUFFICIENT_STOCK", n), this.productIds = t, this.name = "InsufficientStockError";
33
+ }
34
+ }, p = class extends s {
35
+ balanceCents;
36
+ constructor(e, t, n) {
37
+ super(400, e, "REFUND_EXCEEDS_BALANCE", n), this.balanceCents = t, this.name = "RefundExceedsBalanceError";
38
+ }
39
+ }, m = class extends s {
40
+ constructor(e, t) {
41
+ super(502, e, "REFUND_GATEWAY_ERROR", t), this.name = "RefundGatewayError";
42
+ }
43
+ }, h = class extends s {
44
+ constructor(e, t) {
45
+ super(502, e, "REFUND_OUTCOME_UNKNOWN", t), this.name = "RefundOutcomeUnknownError";
46
+ }
47
+ }, g = class extends s {
48
+ constructor(e, t, n) {
49
+ super(400, e, t, n), this.name = "InvalidImageError";
50
+ }
51
+ }, _ = class extends s {
52
+ constructor(e) {
53
+ super(413, e, "IMAGE_TOO_LARGE", `Image is too large: ${e}`), this.name = "ImageTooLargeError";
54
+ }
55
+ }, v = class extends s {
56
+ constructor(e, t) {
57
+ super(409, e, "DISCOUNT_CODE_TAKEN", t), this.name = "DiscountCodeTakenError";
58
+ }
59
+ }, y = class extends s {
60
+ constructor(e, t) {
61
+ super(409, e, "DISCOUNT_IN_USE", t), this.name = "DiscountInUseError";
62
+ }
63
+ };
64
+ // src/client/map-error.ts
65
+ async function b(e) {
66
+ try {
67
+ let t = await e.json();
68
+ return t && typeof t == "object" ? t : {};
69
+ } catch {
70
+ return {};
71
+ }
72
+ }
73
+ async function x(e, t) {
74
+ let n = await b(t), { error: r, message: i } = n;
75
+ return t.status === 401 ? new c(e) : t.status === 404 ? new l(e, r, i) : t.status === 413 ? new _(e) : r === "INVALID_INPUT" ? new u(e, n.issues ?? []) : r === "INVALID_ORDER_STATE" ? new d(e, n.status ?? "", i) : r === "INSUFFICIENT_STOCK" ? new f(e, n.productIds ?? [], i) : r === "REFUND_EXCEEDS_BALANCE" ? new p(e, n.balanceCents, i) : r === "REFUND_GATEWAY_ERROR" ? new m(e, i) : r === "REFUND_OUTCOME_UNKNOWN" ? new h(e, i) : r === "DISCOUNT_CODE_TAKEN" ? new v(e, i) : r === "DISCOUNT_IN_USE" ? new y(e, i) : r === "INVALID_IMAGE" || r === "INVALID_IMAGE_ORDER" ? new g(e, r, i) : new s(t.status, e, r, i);
76
+ }
77
+ // src/client/transport.ts
78
+ function S({ baseUrl: e, fetch: t = (...e) => globalThis.fetch(...e), headers: n }) {
79
+ let r = e.replace(/\/+$/, "");
80
+ return { async request(e, i, { method: a = "GET", body: o, form: s, headers: c } = {}) {
81
+ let l = `${r}${e}`, u = new Headers(n ? await n() : void 0);
82
+ u.set("accept", "application/json"), o !== void 0 && u.set("content-type", "application/json");
83
+ for (let [e, t] of Object.entries(c ?? {})) u.set(e, t);
84
+ let d = await t(l, {
85
+ method: a,
86
+ headers: u,
87
+ body: s ?? (o === void 0 ? void 0 : JSON.stringify(o))
88
+ });
89
+ if (!d.ok) throw await x(l, d);
90
+ return i.parse(await d.json());
91
+ } };
92
+ }
93
+ // src/client/admin-client.ts
94
+ var C = encodeURIComponent;
95
+ function w(e) {
96
+ let t = new URLSearchParams();
97
+ for (let [n, r] of Object.entries(e)) r !== void 0 && t.set(n, String(r));
98
+ let n = t.toString();
99
+ return n ? `?${n}` : "";
100
+ }
101
+ function T(s) {
102
+ let { request: c } = S(s), l = i, u = a, d = o.array();
103
+ return {
104
+ listProducts: (e = {}) => c(`/admin/products${w(e)}`, r),
105
+ getProduct: (e) => c(`/admin/products/${C(e)}`, l),
106
+ createProduct: (e) => c("/admin/products", l, {
107
+ method: "POST",
108
+ body: e
109
+ }),
110
+ updateProduct: (e, t) => c(`/admin/products/${C(e)}`, l, {
111
+ method: "PATCH",
112
+ body: t
113
+ }),
114
+ uploadProductImage(e, t, n) {
115
+ let r = new FormData();
116
+ return n !== void 0 && r.append("alt", n), r.append("file", t), c(`/admin/products/${C(e)}/images`, l, {
117
+ method: "POST",
118
+ form: r
119
+ });
120
+ },
121
+ reorderProductImages: (e, t) => c(`/admin/products/${C(e)}/images/order`, l, {
122
+ method: "PATCH",
123
+ body: { imageIds: t }
124
+ }),
125
+ updateProductImage: (e, t, n) => c(`/admin/products/${C(e)}/images/${C(t)}`, l, {
126
+ method: "PATCH",
127
+ body: n
128
+ }),
129
+ deleteProductImage: (e, t) => c(`/admin/products/${C(e)}/images/${C(t)}`, l, { method: "DELETE" }),
130
+ listDiscounts: (t = {}) => c(`/admin/discounts${w(t)}`, e),
131
+ getDiscount: (e) => c(`/admin/discounts/${C(e)}`, u),
132
+ createDiscount: (e) => c("/admin/discounts", u, {
133
+ method: "POST",
134
+ body: e
135
+ }),
136
+ updateDiscount: (e, t) => c(`/admin/discounts/${C(e)}`, u, {
137
+ method: "PATCH",
138
+ body: t
139
+ }),
140
+ listShippingRates: () => c("/admin/shipping-rates", d),
141
+ replaceShippingRates: (e) => c("/admin/shipping-rates", d, {
142
+ method: "PUT",
143
+ body: e
144
+ }),
145
+ listOrders: (e = {}) => c(`/admin/orders${w(e)}`, n),
146
+ getOrder: (e) => c(`/admin/orders/${C(e)}`, t),
147
+ shipOrder: (e, n = {}) => c(`/admin/orders/${C(e)}/ship`, t, {
148
+ method: "POST",
149
+ body: n
150
+ }),
151
+ resolveOrder: (e) => c(`/admin/orders/${C(e)}/resolve`, t, { method: "POST" }),
152
+ async refundOrder(e, n = { restock: !1 }, r = {}) {
153
+ let i = r.idempotencyKey ?? crypto.randomUUID();
154
+ try {
155
+ return await c(`/admin/orders/${C(e)}/refunds`, t, {
156
+ method: "POST",
157
+ body: n,
158
+ headers: { "idempotency-key": i }
159
+ });
160
+ } catch (e) {
161
+ throw e instanceof Error && (e.idempotencyKey = i), e;
162
+ }
163
+ }
164
+ };
165
+ }
166
+ export { c as a, _ as c, d, p as f, l as i, f as l, h as m, s as n, v as o, m as p, u as r, y as s, T as t, g as u };
167
+
168
+ //# sourceMappingURL=client--_j5r8ag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client--_j5r8ag.js","names":["AdminApiError","message","status","code","url","AdminUnauthorizedError","AdminNotFoundError","AdminInvalidInputError","issues","InvalidOrderStateError","orderStatus","InsufficientStockError","productIds","RefundExceedsBalanceError","balanceCents","RefundGatewayError","RefundOutcomeUnknownError","InvalidImageError","ImageTooLargeError","DiscountCodeTakenError","DiscountInUseError","readBody","body","res","mapError","createTransport","fetchImpl","args","getHeaders","root","baseUrl","path","merged","name","value","headers","form","schema","enc","queryString","params","key","query","qs","createAdminClient","options","product","adminProductSchema","discount","discountSchema","rates","shippingRateSchema","request","adminProductPageSchema","id","input","patch","alt","file","imageId","adminDiscountPageSchema","adminOrderPageSchema","adminOrderDetailSchema","refund","err"],"sources":["../src/client/errors.ts","../src/client/map-error.ts","../src/client/transport.ts","../src/client/admin-client.ts"],"sourcesContent":["export class AdminApiError extends Error {\n idempotencyKey?: string;\n\n constructor(\n readonly status: number,\n readonly url: string,\n readonly code?: string,\n message?: string,\n ) {\n super(\n message ??\n `Admin API request failed (${status}${code ? ` ${code}` : ''}): ${url}`,\n );\n this.name = 'AdminApiError';\n }\n}\n\nexport class AdminUnauthorizedError extends AdminApiError {\n constructor(url: string) {\n super(401, url, 'UNAUTHORIZED', `Not authorized: ${url}`);\n this.name = 'AdminUnauthorizedError';\n }\n}\n\nexport class AdminNotFoundError extends AdminApiError {\n constructor(url: string, code?: string, message?: string) {\n super(404, url, code, message ?? `Not found: ${url}`);\n this.name = 'AdminNotFoundError';\n }\n}\n\nexport class AdminInvalidInputError extends AdminApiError {\n constructor(\n url: string,\n readonly issues: unknown[],\n ) {\n super(400, url, 'INVALID_INPUT', `Invalid input: ${url}`);\n this.name = 'AdminInvalidInputError';\n }\n}\n\nexport class InvalidOrderStateError extends AdminApiError {\n constructor(\n url: string,\n readonly orderStatus: string,\n message?: string,\n ) {\n super(409, url, 'INVALID_ORDER_STATE', message);\n this.name = 'InvalidOrderStateError';\n }\n}\n\nexport class InsufficientStockError extends AdminApiError {\n constructor(\n url: string,\n readonly productIds: string[],\n message?: string,\n ) {\n super(409, url, 'INSUFFICIENT_STOCK', message);\n this.name = 'InsufficientStockError';\n }\n}\n\nexport class RefundExceedsBalanceError extends AdminApiError {\n constructor(\n url: string,\n readonly balanceCents: number | undefined,\n message?: string,\n ) {\n super(400, url, 'REFUND_EXCEEDS_BALANCE', message);\n this.name = 'RefundExceedsBalanceError';\n }\n}\n\nexport class RefundGatewayError extends AdminApiError {\n constructor(url: string, message?: string) {\n super(502, url, 'REFUND_GATEWAY_ERROR', message);\n this.name = 'RefundGatewayError';\n }\n}\n\nexport class RefundOutcomeUnknownError extends AdminApiError {\n constructor(url: string, message?: string) {\n super(502, url, 'REFUND_OUTCOME_UNKNOWN', message);\n this.name = 'RefundOutcomeUnknownError';\n }\n}\n\nexport class InvalidImageError extends AdminApiError {\n constructor(url: string, code: string, message?: string) {\n super(400, url, code, message);\n this.name = 'InvalidImageError';\n }\n}\n\nexport class ImageTooLargeError extends AdminApiError {\n constructor(url: string) {\n super(413, url, 'IMAGE_TOO_LARGE', `Image is too large: ${url}`);\n this.name = 'ImageTooLargeError';\n }\n}\n\nexport class DiscountCodeTakenError extends AdminApiError {\n constructor(url: string, message?: string) {\n super(409, url, 'DISCOUNT_CODE_TAKEN', message);\n this.name = 'DiscountCodeTakenError';\n }\n}\n\nexport class DiscountInUseError extends AdminApiError {\n constructor(url: string, message?: string) {\n super(409, url, 'DISCOUNT_IN_USE', message);\n this.name = 'DiscountInUseError';\n }\n}\n","import {\n AdminApiError,\n AdminInvalidInputError,\n AdminNotFoundError,\n AdminUnauthorizedError,\n DiscountCodeTakenError,\n DiscountInUseError,\n ImageTooLargeError,\n InsufficientStockError,\n InvalidImageError,\n InvalidOrderStateError,\n RefundExceedsBalanceError,\n RefundGatewayError,\n RefundOutcomeUnknownError,\n} from './errors';\n\ninterface ErrorBody {\n error?: string;\n message?: string;\n issues?: unknown[];\n status?: string;\n productIds?: string[];\n balanceCents?: number;\n}\n\nasync function readBody(res: Response): Promise<ErrorBody> {\n try {\n const body: unknown = await res.json();\n return body && typeof body === 'object' ? (body as ErrorBody) : {};\n } catch {\n return {};\n }\n}\n\nexport async function mapError(\n url: string,\n res: Response,\n): Promise<AdminApiError> {\n const body = await readBody(res);\n const { error: code, message } = body;\n if (res.status === 401) return new AdminUnauthorizedError(url);\n if (res.status === 404) return new AdminNotFoundError(url, code, message);\n if (res.status === 413) return new ImageTooLargeError(url);\n if (code === 'INVALID_INPUT') {\n return new AdminInvalidInputError(url, body.issues ?? []);\n }\n if (code === 'INVALID_ORDER_STATE') {\n return new InvalidOrderStateError(url, body.status ?? '', message);\n }\n if (code === 'INSUFFICIENT_STOCK') {\n return new InsufficientStockError(url, body.productIds ?? [], message);\n }\n if (code === 'REFUND_EXCEEDS_BALANCE') {\n return new RefundExceedsBalanceError(url, body.balanceCents, message);\n }\n if (code === 'REFUND_GATEWAY_ERROR')\n return new RefundGatewayError(url, message);\n if (code === 'REFUND_OUTCOME_UNKNOWN') {\n return new RefundOutcomeUnknownError(url, message);\n }\n if (code === 'DISCOUNT_CODE_TAKEN') {\n return new DiscountCodeTakenError(url, message);\n }\n if (code === 'DISCOUNT_IN_USE') return new DiscountInUseError(url, message);\n if (code === 'INVALID_IMAGE' || code === 'INVALID_IMAGE_ORDER') {\n return new InvalidImageError(url, code, message);\n }\n return new AdminApiError(res.status, url, code, message);\n}\n","import { mapError } from './map-error';\n\nexport interface AdminClientOptions {\n baseUrl: string;\n fetch?: typeof fetch;\n headers?: () => HeadersInit | Promise<HeadersInit>;\n}\n\ntype Schema<T> = { parse(data: unknown): T };\n\nexport interface RequestOptions {\n method?: string;\n body?: unknown;\n form?: FormData;\n headers?: Record<string, string>;\n}\n\nexport interface Transport {\n request<T>(\n path: string,\n schema: Schema<T>,\n options?: RequestOptions,\n ): Promise<T>;\n}\n\nexport function createTransport({\n baseUrl,\n fetch: fetchImpl = (...args) => globalThis.fetch(...args),\n headers: getHeaders,\n}: AdminClientOptions): Transport {\n const root = baseUrl.replace(/\\/+$/, '');\n\n return {\n async request(path, schema, { method = 'GET', body, form, headers } = {}) {\n const url = `${root}${path}`;\n const merged = new Headers(getHeaders ? await getHeaders() : undefined);\n merged.set('accept', 'application/json');\n if (body !== undefined) merged.set('content-type', 'application/json');\n for (const [name, value] of Object.entries(headers ?? {})) {\n merged.set(name, value);\n }\n const res = await fetchImpl(url, {\n method,\n headers: merged,\n body: form ?? (body === undefined ? undefined : JSON.stringify(body)),\n });\n if (!res.ok) throw await mapError(url, res);\n return schema.parse(await res.json());\n },\n };\n}\n","import {\n adminDiscountPageSchema,\n adminOrderDetailSchema,\n adminOrderPageSchema,\n adminProductPageSchema,\n adminProductSchema,\n discountSchema,\n shippingRateSchema,\n type AdminDiscountListQuery,\n type AdminDiscountPage,\n type AdminOrderDetail,\n type AdminOrderListQuery,\n type AdminOrderPage,\n type AdminProduct,\n type AdminProductListQuery,\n type AdminProductPage,\n type AdminProductInput,\n type AdminProductPatch,\n type Discount,\n type DiscountInput,\n type DiscountPatch,\n type RefundOrderInput,\n type ProductImagePatch,\n type ShippingRate,\n type ShippingRateInput,\n type ShipOrderInput,\n} from '@develemit/commerce-contracts';\nimport { createTransport, type AdminClientOptions } from './transport';\n\nexport type AdminOrderQuery = Partial<\n Pick<AdminOrderListQuery, 'status' | 'q' | 'hasPendingRefund' | 'cursor'>\n> & { limit?: number };\n\nexport type AdminProductQuery = Pick<\n AdminProductListQuery,\n 'status' | 'stock' | 'q' | 'cursor' | 'limit'\n>;\n\nexport type AdminDiscountQuery = Partial<\n Pick<AdminDiscountListQuery, 'status' | 'q' | 'cursor' | 'limit'>\n>;\n\nexport interface RefundOptions {\n idempotencyKey?: string;\n}\n\nexport interface AdminClient {\n listProducts(query?: AdminProductQuery): Promise<AdminProductPage>;\n getProduct(id: string): Promise<AdminProduct>;\n createProduct(input: AdminProductInput): Promise<AdminProduct>;\n updateProduct(id: string, patch: AdminProductPatch): Promise<AdminProduct>;\n uploadProductImage(\n id: string,\n file: Blob,\n alt?: string,\n ): Promise<AdminProduct>;\n reorderProductImages(id: string, imageIds: string[]): Promise<AdminProduct>;\n updateProductImage(\n id: string,\n imageId: string,\n patch: ProductImagePatch,\n ): Promise<AdminProduct>;\n deleteProductImage(id: string, imageId: string): Promise<AdminProduct>;\n listDiscounts(query?: AdminDiscountQuery): Promise<AdminDiscountPage>;\n getDiscount(id: string): Promise<Discount>;\n createDiscount(input: DiscountInput): Promise<Discount>;\n updateDiscount(id: string, patch: DiscountPatch): Promise<Discount>;\n listShippingRates(): Promise<ShippingRate[]>;\n replaceShippingRates(rates: ShippingRateInput[]): Promise<ShippingRate[]>;\n listOrders(query?: AdminOrderQuery): Promise<AdminOrderPage>;\n getOrder(id: string): Promise<AdminOrderDetail>;\n shipOrder(id: string, input?: ShipOrderInput): Promise<AdminOrderDetail>;\n resolveOrder(id: string): Promise<AdminOrderDetail>;\n /**\n * Retrying the same intent must reuse the same key. When none is passed one\n * is generated, and it is set on any thrown error as `idempotencyKey` so the\n * caller can pass it back on retry.\n */\n refundOrder(\n id: string,\n input?: RefundOrderInput,\n options?: RefundOptions,\n ): Promise<AdminOrderDetail>;\n}\n\nconst enc = encodeURIComponent;\n\nfunction queryString(query: object): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined) params.set(key, String(value));\n }\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\nexport function createAdminClient(options: AdminClientOptions): AdminClient {\n const { request } = createTransport(options);\n const product = adminProductSchema;\n const discount = discountSchema;\n const rates = shippingRateSchema.array();\n\n return {\n listProducts: (query = {}) =>\n request(`/admin/products${queryString(query)}`, adminProductPageSchema),\n getProduct: (id) => request(`/admin/products/${enc(id)}`, product),\n createProduct: (input) =>\n request('/admin/products', product, { method: 'POST', body: input }),\n updateProduct: (id, patch) =>\n request(`/admin/products/${enc(id)}`, product, {\n method: 'PATCH',\n body: patch,\n }),\n uploadProductImage(id, file, alt) {\n const form = new FormData();\n if (alt !== undefined) form.append('alt', alt);\n form.append('file', file);\n return request(`/admin/products/${enc(id)}/images`, product, {\n method: 'POST',\n form,\n });\n },\n reorderProductImages: (id, imageIds) =>\n request(`/admin/products/${enc(id)}/images/order`, product, {\n method: 'PATCH',\n body: { imageIds },\n }),\n updateProductImage: (id, imageId, patch) =>\n request(`/admin/products/${enc(id)}/images/${enc(imageId)}`, product, {\n method: 'PATCH',\n body: patch,\n }),\n deleteProductImage: (id, imageId) =>\n request(`/admin/products/${enc(id)}/images/${enc(imageId)}`, product, {\n method: 'DELETE',\n }),\n listDiscounts: (query = {}) =>\n request(`/admin/discounts${queryString(query)}`, adminDiscountPageSchema),\n getDiscount: (id) => request(`/admin/discounts/${enc(id)}`, discount),\n createDiscount: (input) =>\n request('/admin/discounts', discount, { method: 'POST', body: input }),\n updateDiscount: (id, patch) =>\n request(`/admin/discounts/${enc(id)}`, discount, {\n method: 'PATCH',\n body: patch,\n }),\n listShippingRates: () => request('/admin/shipping-rates', rates),\n replaceShippingRates: (input) =>\n request('/admin/shipping-rates', rates, { method: 'PUT', body: input }),\n listOrders: (query = {}) =>\n request(`/admin/orders${queryString(query)}`, adminOrderPageSchema),\n getOrder: (id) =>\n request(`/admin/orders/${enc(id)}`, adminOrderDetailSchema),\n shipOrder: (id, input = {}) =>\n request(`/admin/orders/${enc(id)}/ship`, adminOrderDetailSchema, {\n method: 'POST',\n body: input,\n }),\n resolveOrder: (id) =>\n request(`/admin/orders/${enc(id)}/resolve`, adminOrderDetailSchema, {\n method: 'POST',\n }),\n async refundOrder(id, input = { restock: false }, refund = {}) {\n const key = refund.idempotencyKey ?? crypto.randomUUID();\n try {\n return await request(\n `/admin/orders/${enc(id)}/refunds`,\n adminOrderDetailSchema,\n {\n method: 'POST',\n body: input,\n headers: { 'idempotency-key': key },\n },\n );\n } catch (err) {\n if (err instanceof Error) {\n (err as Error & { idempotencyKey?: string }).idempotencyKey = key;\n }\n throw err;\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ExtraNavItem {
3
+ href: string;
4
+ label: string;
5
+ position?: 'start' | 'end';
6
+ }
7
+ export interface AdminLayoutProps {
8
+ children: ReactNode;
9
+ extraNav?: ExtraNavItem[];
10
+ header?: ReactNode;
11
+ footer?: ReactNode;
12
+ }
13
+ export declare function AdminLayout({ children, extraNav, header, footer, }: AdminLayoutProps): import('react').JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { ReactNode } from 'react';
2
+ export interface AdminColumn<T> {
3
+ key: string;
4
+ header: string;
5
+ render: (row: T) => ReactNode;
6
+ }
7
+ export interface AdminTableProps<T> {
8
+ columns: AdminColumn<T>[];
9
+ rows: T[];
10
+ rowKey: (row: T) => string;
11
+ caption?: string;
12
+ }
13
+ export declare function AdminTable<T>({ columns, rows, rowKey, caption, }: AdminTableProps<T>): import('react').JSX.Element;
@@ -0,0 +1,12 @@
1
+ import { ReactNode } from 'react';
2
+ export interface ConfirmDialogProps {
3
+ open: boolean;
4
+ title: string;
5
+ children?: ReactNode;
6
+ confirmLabel?: string;
7
+ destructive?: boolean;
8
+ pending?: boolean;
9
+ onConfirm: () => void;
10
+ onCancel: () => void;
11
+ }
12
+ export declare function ConfirmDialog({ open, title, children, confirmLabel, destructive, pending, onConfirm, onCancel, }: ConfirmDialogProps): import('react').JSX.Element | null;
@@ -0,0 +1,5 @@
1
+ import { AdminErrorKey } from '../../lib/provider';
2
+ export declare function errorKey(error: unknown): AdminErrorKey;
3
+ export declare function ErrorMessage({ error }: {
4
+ error: unknown;
5
+ }): import('react').JSX.Element | null;
@@ -0,0 +1,5 @@
1
+ export interface MoneyProps {
2
+ cents: number;
3
+ currency: string;
4
+ }
5
+ export declare function Money({ cents, currency }: MoneyProps): import('react').JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ export interface NoticeProps {
3
+ variant: 'success' | 'error' | 'warning';
4
+ children: ReactNode;
5
+ toast?: boolean;
6
+ duration?: number;
7
+ onDismiss?: () => void;
8
+ }
9
+ export declare function Notice({ variant, children, toast, duration, onDismiss, }: NoticeProps): import('react').JSX.Element;
@@ -0,0 +1,3 @@
1
+ export declare function StatusBadge({ status }: {
2
+ status: string;
3
+ }): import('react').JSX.Element;
@@ -0,0 +1,27 @@
1
+ export * from './lib/provider';
2
+ export * from './lib/use-admin-query';
3
+ export * from './lib/use-admin-mutation';
4
+ export * from './components/admin-layout/admin-layout';
5
+ export * from './components/admin-table/admin-table';
6
+ export * from './components/status-badge/status-badge';
7
+ export * from './components/money/money';
8
+ export * from './components/confirm-dialog/confirm-dialog';
9
+ export * from './components/notice/notice';
10
+ export * from './components/error-message/error-message';
11
+ export type { AdminClient } from './client';
12
+ export * from './views/products/admin-product-list';
13
+ export * from './views/products/admin-product-editor';
14
+ export * from './views/products/admin-product-images';
15
+ export type { ImageLabels } from './lib/image-labels';
16
+ export type { ProductLabels, ProductFilter } from './lib/product-labels';
17
+ export { parseMoneyInput, formatMoneyInput } from './lib/money-input';
18
+ export * from './views/orders/admin-order-list';
19
+ export * from './views/orders/admin-order-detail';
20
+ export type { OrderLabels, OrderTab } from './lib/order-labels';
21
+ export * from './views/orders/order-actions';
22
+ export type { OrderActionLabels } from './lib/order-action-labels';
23
+ export * from './views/shipping/admin-shipping-rates';
24
+ export type { ShippingLabels } from './lib/shipping-labels';
25
+ export * from './views/discounts/admin-discount-list';
26
+ export * from './views/discounts/admin-discount-editor';
27
+ export type { DiscountLabels, DiscountTab } from './lib/discount-labels';