@develemit/commerce-admin 0.0.0-stage → 0.3.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.
- package/README.md +136 -2
- package/dist/client/admin-client.d.ts +31 -0
- package/dist/client/errors.d.ts +41 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.js +1 -0
- package/dist/client/map-error.d.ts +2 -0
- package/dist/client/transport.d.ts +19 -0
- package/dist/client-n_IYb4iK.js +146 -0
- package/dist/client-n_IYb4iK.js.map +1 -0
- package/dist/components/admin-layout/admin-layout.d.ts +13 -0
- package/dist/components/admin-table/admin-table.d.ts +13 -0
- package/dist/components/confirm-dialog/confirm-dialog.d.ts +12 -0
- package/dist/components/error-message/error-message.d.ts +5 -0
- package/dist/components/money/money.d.ts +5 -0
- package/dist/components/notice/notice.d.ts +9 -0
- package/dist/components/status-badge/status-badge.d.ts +3 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +2346 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/image-labels.d.ts +24 -0
- package/dist/lib/image-rules.d.ts +4 -0
- package/dist/lib/money-input.d.ts +2 -0
- package/dist/lib/order-action-labels.d.ts +28 -0
- package/dist/lib/order-labels.d.ts +66 -0
- package/dist/lib/product-filter.d.ts +6 -0
- package/dist/lib/product-form.d.ts +29 -0
- package/dist/lib/product-labels.d.ts +52 -0
- package/dist/lib/provider.d.ts +73 -0
- package/dist/lib/refund-rules.d.ts +7 -0
- package/dist/lib/shipping-labels.d.ts +33 -0
- package/dist/lib/shipping-rates.d.ts +31 -0
- package/dist/lib/use-admin-mutation.d.ts +7 -0
- package/dist/lib/use-admin-query.d.ts +7 -0
- package/dist/lib/use-debounced.d.ts +1 -0
- package/dist/lib/use-image-upload.d.ts +18 -0
- package/dist/lib/use-order-pages.d.ts +17 -0
- package/dist/lib/use-product-pages.d.ts +30 -0
- package/dist/styles.css +405 -0
- package/dist/views/orders/admin-order-detail.d.ts +10 -0
- package/dist/views/orders/admin-order-list.d.ts +1 -0
- package/dist/views/orders/order-actions.d.ts +7 -0
- package/dist/views/orders/order-fulfillment.d.ts +4 -0
- package/dist/views/orders/order-items.d.ts +4 -0
- package/dist/views/orders/order-refunds.d.ts +4 -0
- package/dist/views/orders/refund-dialog.d.ts +8 -0
- package/dist/views/orders/resolve-button.d.ts +7 -0
- package/dist/views/orders/ship-form.d.ts +7 -0
- package/dist/views/orders/test-helpers.d.ts +3 -0
- package/dist/views/products/admin-product-editor.d.ts +9 -0
- package/dist/views/products/admin-product-images.d.ts +6 -0
- package/dist/views/products/admin-product-list.d.ts +1 -0
- package/dist/views/products/image-tile.d.ts +14 -0
- package/dist/views/products/product-fields.d.ts +12 -0
- package/dist/views/products/test-helpers.d.ts +6 -0
- package/dist/views/products/upload-picker.d.ts +7 -0
- package/dist/views/shipping/admin-shipping-rates.d.ts +4 -0
- package/dist/views/shipping/shipping-rate-row.d.ts +15 -0
- package/package.json +44 -3
package/README.md
CHANGED
|
@@ -1,3 +1,137 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @develemit/commerce-admin
|
|
2
2
|
|
|
3
|
-
|
|
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` and `/admin/shipping-rates`.
|
|
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
|
+
## Shipping rates
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<AdminShippingRates onDirtyChange={(dirty) => setBlockNavigation(dirty)} />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `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.
|
|
127
|
+
- 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.
|
|
128
|
+
- 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.
|
|
129
|
+
- `onDirtyChange(dirty)` fires when unsaved edits appear or are cleared, so a host can guard its router. Copy lives under `labels.shipping`.
|
|
130
|
+
|
|
131
|
+
## Images
|
|
132
|
+
|
|
133
|
+
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.
|
|
134
|
+
|
|
135
|
+
## Theming
|
|
136
|
+
|
|
137
|
+
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,31 @@
|
|
|
1
|
+
import { AdminOrderDetail, AdminOrderListQuery, AdminOrderPage, AdminProduct, AdminProductListQuery, AdminProductPage, AdminProductInput, AdminProductPatch, RefundOrderInput, 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 interface RefundOptions {
|
|
8
|
+
idempotencyKey?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AdminClient {
|
|
11
|
+
listProducts(query?: AdminProductQuery): Promise<AdminProductPage>;
|
|
12
|
+
getProduct(id: string): Promise<AdminProduct>;
|
|
13
|
+
createProduct(input: AdminProductInput): Promise<AdminProduct>;
|
|
14
|
+
updateProduct(id: string, patch: AdminProductPatch): Promise<AdminProduct>;
|
|
15
|
+
uploadProductImage(id: string, file: Blob, alt?: string): Promise<AdminProduct>;
|
|
16
|
+
reorderProductImages(id: string, imageIds: string[]): Promise<AdminProduct>;
|
|
17
|
+
deleteProductImage(id: string, imageId: string): Promise<AdminProduct>;
|
|
18
|
+
listShippingRates(): Promise<ShippingRate[]>;
|
|
19
|
+
replaceShippingRates(rates: ShippingRateInput[]): Promise<ShippingRate[]>;
|
|
20
|
+
listOrders(query?: AdminOrderQuery): Promise<AdminOrderPage>;
|
|
21
|
+
getOrder(id: string): Promise<AdminOrderDetail>;
|
|
22
|
+
shipOrder(id: string, input?: ShipOrderInput): Promise<AdminOrderDetail>;
|
|
23
|
+
resolveOrder(id: string): Promise<AdminOrderDetail>;
|
|
24
|
+
/**
|
|
25
|
+
* Retrying the same intent must reuse the same key. When none is passed one
|
|
26
|
+
* is generated, and it is set on any thrown error as `idempotencyKey` so the
|
|
27
|
+
* caller can pass it back on retry.
|
|
28
|
+
*/
|
|
29
|
+
refundOrder(id: string, input?: RefundOrderInput, options?: RefundOptions): Promise<AdminOrderDetail>;
|
|
30
|
+
}
|
|
31
|
+
export declare function createAdminClient(options: AdminClientOptions): AdminClient;
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { n as AdminApiError, r as AdminInvalidInputError, i as AdminNotFoundError, a as AdminUnauthorizedError, o as ImageTooLargeError, s as InsufficientStockError, c as InvalidImageError, l as InvalidOrderStateError, u as RefundExceedsBalanceError, d as RefundGatewayError, f as RefundOutcomeUnknownError, t as createAdminClient } from "../client-n_IYb4iK.js";
|
|
@@ -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,146 @@
|
|
|
1
|
+
import { adminOrderDetailSchema as e, adminOrderPageSchema as t, adminProductPageSchema as n, adminProductSchema as r, shippingRateSchema as i } from "@develemit/commerce-contracts";
|
|
2
|
+
// src/client/errors.ts
|
|
3
|
+
var a = 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
|
+
}, o = class extends a {
|
|
12
|
+
constructor(e) {
|
|
13
|
+
super(401, e, "UNAUTHORIZED", `Not authorized: ${e}`), this.name = "AdminUnauthorizedError";
|
|
14
|
+
}
|
|
15
|
+
}, s = class extends a {
|
|
16
|
+
constructor(e, t, n) {
|
|
17
|
+
super(404, e, t, n ?? `Not found: ${e}`), this.name = "AdminNotFoundError";
|
|
18
|
+
}
|
|
19
|
+
}, c = class extends a {
|
|
20
|
+
issues;
|
|
21
|
+
constructor(e, t) {
|
|
22
|
+
super(400, e, "INVALID_INPUT", `Invalid input: ${e}`), this.issues = t, this.name = "AdminInvalidInputError";
|
|
23
|
+
}
|
|
24
|
+
}, l = class extends a {
|
|
25
|
+
orderStatus;
|
|
26
|
+
constructor(e, t, n) {
|
|
27
|
+
super(409, e, "INVALID_ORDER_STATE", n), this.orderStatus = t, this.name = "InvalidOrderStateError";
|
|
28
|
+
}
|
|
29
|
+
}, u = class extends a {
|
|
30
|
+
productIds;
|
|
31
|
+
constructor(e, t, n) {
|
|
32
|
+
super(409, e, "INSUFFICIENT_STOCK", n), this.productIds = t, this.name = "InsufficientStockError";
|
|
33
|
+
}
|
|
34
|
+
}, d = class extends a {
|
|
35
|
+
balanceCents;
|
|
36
|
+
constructor(e, t, n) {
|
|
37
|
+
super(400, e, "REFUND_EXCEEDS_BALANCE", n), this.balanceCents = t, this.name = "RefundExceedsBalanceError";
|
|
38
|
+
}
|
|
39
|
+
}, f = class extends a {
|
|
40
|
+
constructor(e, t) {
|
|
41
|
+
super(502, e, "REFUND_GATEWAY_ERROR", t), this.name = "RefundGatewayError";
|
|
42
|
+
}
|
|
43
|
+
}, p = class extends a {
|
|
44
|
+
constructor(e, t) {
|
|
45
|
+
super(502, e, "REFUND_OUTCOME_UNKNOWN", t), this.name = "RefundOutcomeUnknownError";
|
|
46
|
+
}
|
|
47
|
+
}, m = class extends a {
|
|
48
|
+
constructor(e, t, n) {
|
|
49
|
+
super(400, e, t, n), this.name = "InvalidImageError";
|
|
50
|
+
}
|
|
51
|
+
}, h = class extends a {
|
|
52
|
+
constructor(e) {
|
|
53
|
+
super(413, e, "IMAGE_TOO_LARGE", `Image is too large: ${e}`), this.name = "ImageTooLargeError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
// src/client/map-error.ts
|
|
57
|
+
async function g(e) {
|
|
58
|
+
try {
|
|
59
|
+
let t = await e.json();
|
|
60
|
+
return t && typeof t == "object" ? t : {};
|
|
61
|
+
} catch {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function _(e, t) {
|
|
66
|
+
let n = await g(t), { error: r, message: i } = n;
|
|
67
|
+
return t.status === 401 ? new o(e) : t.status === 404 ? new s(e, r, i) : t.status === 413 ? new h(e) : r === "INVALID_INPUT" ? new c(e, n.issues ?? []) : r === "INVALID_ORDER_STATE" ? new l(e, n.status ?? "", i) : r === "INSUFFICIENT_STOCK" ? new u(e, n.productIds ?? [], i) : r === "REFUND_EXCEEDS_BALANCE" ? new d(e, n.balanceCents, i) : r === "REFUND_GATEWAY_ERROR" ? new f(e, i) : r === "REFUND_OUTCOME_UNKNOWN" ? new p(e, i) : r === "INVALID_IMAGE" || r === "INVALID_IMAGE_ORDER" ? new m(e, r, i) : new a(t.status, e, r, i);
|
|
68
|
+
}
|
|
69
|
+
// src/client/transport.ts
|
|
70
|
+
function v({ baseUrl: e, fetch: t = (...e) => globalThis.fetch(...e), headers: n }) {
|
|
71
|
+
let r = e.replace(/\/+$/, "");
|
|
72
|
+
return { async request(e, i, { method: a = "GET", body: o, form: s, headers: c } = {}) {
|
|
73
|
+
let l = `${r}${e}`, u = new Headers(n ? await n() : void 0);
|
|
74
|
+
u.set("accept", "application/json"), o !== void 0 && u.set("content-type", "application/json");
|
|
75
|
+
for (let [e, t] of Object.entries(c ?? {})) u.set(e, t);
|
|
76
|
+
let d = await t(l, {
|
|
77
|
+
method: a,
|
|
78
|
+
headers: u,
|
|
79
|
+
body: s ?? (o === void 0 ? void 0 : JSON.stringify(o))
|
|
80
|
+
});
|
|
81
|
+
if (!d.ok) throw await _(l, d);
|
|
82
|
+
return i.parse(await d.json());
|
|
83
|
+
} };
|
|
84
|
+
}
|
|
85
|
+
// src/client/admin-client.ts
|
|
86
|
+
var y = encodeURIComponent;
|
|
87
|
+
function b(e) {
|
|
88
|
+
let t = new URLSearchParams();
|
|
89
|
+
for (let [n, r] of Object.entries(e)) r !== void 0 && t.set(n, String(r));
|
|
90
|
+
let n = t.toString();
|
|
91
|
+
return n ? `?${n}` : "";
|
|
92
|
+
}
|
|
93
|
+
function x(a) {
|
|
94
|
+
let { request: o } = v(a), s = r, c = i.array();
|
|
95
|
+
return {
|
|
96
|
+
listProducts: (e = {}) => o(`/admin/products${b(e)}`, n),
|
|
97
|
+
getProduct: (e) => o(`/admin/products/${y(e)}`, s),
|
|
98
|
+
createProduct: (e) => o("/admin/products", s, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
body: e
|
|
101
|
+
}),
|
|
102
|
+
updateProduct: (e, t) => o(`/admin/products/${y(e)}`, s, {
|
|
103
|
+
method: "PATCH",
|
|
104
|
+
body: t
|
|
105
|
+
}),
|
|
106
|
+
uploadProductImage(e, t, n) {
|
|
107
|
+
let r = new FormData();
|
|
108
|
+
return n !== void 0 && r.append("alt", n), r.append("file", t), o(`/admin/products/${y(e)}/images`, s, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
form: r
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
reorderProductImages: (e, t) => o(`/admin/products/${y(e)}/images/order`, s, {
|
|
114
|
+
method: "PATCH",
|
|
115
|
+
body: { imageIds: t }
|
|
116
|
+
}),
|
|
117
|
+
deleteProductImage: (e, t) => o(`/admin/products/${y(e)}/images/${y(t)}`, s, { method: "DELETE" }),
|
|
118
|
+
listShippingRates: () => o("/admin/shipping-rates", c),
|
|
119
|
+
replaceShippingRates: (e) => o("/admin/shipping-rates", c, {
|
|
120
|
+
method: "PUT",
|
|
121
|
+
body: e
|
|
122
|
+
}),
|
|
123
|
+
listOrders: (e = {}) => o(`/admin/orders${b(e)}`, t),
|
|
124
|
+
getOrder: (t) => o(`/admin/orders/${y(t)}`, e),
|
|
125
|
+
shipOrder: (t, n = {}) => o(`/admin/orders/${y(t)}/ship`, e, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
body: n
|
|
128
|
+
}),
|
|
129
|
+
resolveOrder: (t) => o(`/admin/orders/${y(t)}/resolve`, e, { method: "POST" }),
|
|
130
|
+
async refundOrder(t, n = { restock: !1 }, r = {}) {
|
|
131
|
+
let i = r.idempotencyKey ?? crypto.randomUUID();
|
|
132
|
+
try {
|
|
133
|
+
return await o(`/admin/orders/${y(t)}/refunds`, e, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
body: n,
|
|
136
|
+
headers: { "idempotency-key": i }
|
|
137
|
+
});
|
|
138
|
+
} catch (e) {
|
|
139
|
+
throw e instanceof Error && (e.idempotencyKey = i), e;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export { o as a, m as c, f as d, p as f, s as i, l, a as n, h as o, c as r, u as s, x as t, d as u };
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=client-n_IYb4iK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-n_IYb4iK.js","names":["AdminApiError","message","status","code","url","AdminUnauthorizedError","AdminNotFoundError","AdminInvalidInputError","issues","InvalidOrderStateError","orderStatus","InsufficientStockError","productIds","RefundExceedsBalanceError","balanceCents","RefundGatewayError","RefundOutcomeUnknownError","InvalidImageError","ImageTooLargeError","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","rates","shippingRateSchema","request","adminProductPageSchema","id","input","patch","alt","file","imageId","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","import {\n AdminApiError,\n AdminInvalidInputError,\n AdminNotFoundError,\n AdminUnauthorizedError,\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 === '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 adminOrderDetailSchema,\n adminOrderPageSchema,\n adminProductPageSchema,\n adminProductSchema,\n shippingRateSchema,\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 RefundOrderInput,\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 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 deleteProductImage(id: string, imageId: string): Promise<AdminProduct>;\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 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 deleteProductImage: (id, imageId) =>\n request(`/admin/products/${enc(id)}/images/${enc(imageId)}`, product, {\n method: 'DELETE',\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,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;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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';
|