@supercycle/cli 0.1.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 (77) hide show
  1. package/README.md +107 -0
  2. package/dist/cli.js +9 -0
  3. package/dist/commands/comments/delete.js +48 -0
  4. package/dist/commands/comments/index.js +3 -0
  5. package/dist/commands/conditions/index.js +3 -0
  6. package/dist/commands/conditions/list.js +20 -0
  7. package/dist/commands/custom-fields/definition.js +58 -0
  8. package/dist/commands/custom-fields/definitions.js +25 -0
  9. package/dist/commands/custom-fields/delete.js +45 -0
  10. package/dist/commands/custom-fields/index.js +3 -0
  11. package/dist/commands/custom-fields/set.js +43 -0
  12. package/dist/commands/custom-fields/update.js +19 -0
  13. package/dist/commands/cycles/comment.js +29 -0
  14. package/dist/commands/cycles/fulfil.js +24 -0
  15. package/dist/commands/cycles/get.js +50 -0
  16. package/dist/commands/cycles/index.js +3 -0
  17. package/dist/commands/cycles/list.js +124 -0
  18. package/dist/commands/cycles/overdue.js +25 -0
  19. package/dist/commands/cycles/pack.js +21 -0
  20. package/dist/commands/cycles/reassign.js +28 -0
  21. package/dist/commands/cycles/receive.js +23 -0
  22. package/dist/commands/cycles/reschedule.js +74 -0
  23. package/dist/commands/cycles/return.js +52 -0
  24. package/dist/commands/cycles/tag.js +45 -0
  25. package/dist/commands/cycles/to-fulfill.js +9 -0
  26. package/dist/commands/cycles/to-receive.js +9 -0
  27. package/dist/commands/cycles/today.js +18 -0
  28. package/dist/commands/items/availability.js +75 -0
  29. package/dist/commands/items/comment.js +29 -0
  30. package/dist/commands/items/create.js +57 -0
  31. package/dist/commands/items/get.js +50 -0
  32. package/dist/commands/items/index.js +3 -0
  33. package/dist/commands/items/list.js +25 -0
  34. package/dist/commands/items/update.js +60 -0
  35. package/dist/commands/locations/index.js +3 -0
  36. package/dist/commands/locations/list.js +19 -0
  37. package/dist/commands/login.js +76 -0
  38. package/dist/commands/products/import.js +65 -0
  39. package/dist/commands/products/index.js +3 -0
  40. package/dist/commands/products/list.js +19 -0
  41. package/dist/commands/returns/comment.js +29 -0
  42. package/dist/commands/returns/get.js +50 -0
  43. package/dist/commands/returns/index.js +3 -0
  44. package/dist/commands/returns/list.js +27 -0
  45. package/dist/commands/returns/update.js +68 -0
  46. package/dist/components/CommentResultView.js +40 -0
  47. package/dist/components/CustomFieldResultView.js +46 -0
  48. package/dist/components/CycleActionView.js +36 -0
  49. package/dist/components/CycleDetail.js +60 -0
  50. package/dist/components/CycleTable.js +68 -0
  51. package/dist/components/CyclesListView.js +30 -0
  52. package/dist/components/ErrorMessage.js +13 -0
  53. package/dist/components/ExitError.js +13 -0
  54. package/dist/components/ItemActionView.js +36 -0
  55. package/dist/components/ItemDetail.js +48 -0
  56. package/dist/components/ItemTable.js +58 -0
  57. package/dist/components/ItemsListView.js +30 -0
  58. package/dist/components/ItemsResultView.js +37 -0
  59. package/dist/components/JsonOutput.js +12 -0
  60. package/dist/components/Loading.js +11 -0
  61. package/dist/components/ProductTable.js +49 -0
  62. package/dist/components/ProductsListView.js +30 -0
  63. package/dist/components/ReferenceListView.js +49 -0
  64. package/dist/components/ReturnActionView.js +36 -0
  65. package/dist/components/ReturnDetail.js +55 -0
  66. package/dist/components/ReturnResultView.js +49 -0
  67. package/dist/components/ReturnTable.js +58 -0
  68. package/dist/components/ReturnsListView.js +30 -0
  69. package/dist/components/Status.js +33 -0
  70. package/dist/hooks/useAsync.js +32 -0
  71. package/dist/lib/client.js +302 -0
  72. package/dist/lib/config.js +38 -0
  73. package/dist/lib/format.js +49 -0
  74. package/dist/lib/listViewOptions.js +39 -0
  75. package/dist/lib/resolveClient.js +11 -0
  76. package/dist/lib/types.js +3 -0
  77. package/package.json +44 -0
@@ -0,0 +1,21 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import CycleActionView from "../../components/CycleActionView.js";
5
+ export const description = "Set a cycle’s packing status";
6
+ export const args = zod.tuple([
7
+ zod.string().describe(argument({ name: "id", description: "Numeric cycle ID" })),
8
+ ]);
9
+ export const options = zod.object({
10
+ status: zod
11
+ .enum(["pending", "printed", "packed"])
12
+ .describe(option({ description: "Packing status: pending|printed|packed" })),
13
+ json: zod
14
+ .boolean()
15
+ .default(false)
16
+ .describe(option({ description: "Output raw JSON" })),
17
+ });
18
+ export default function Pack({ args, options }) {
19
+ const [id] = args;
20
+ return (React.createElement(CycleActionView, { loadingLabel: `Updating packing status for cycle ${id}…`, action: (client) => client.updateCycle(id, { packingStatus: options.status }), successLabel: (c) => `Cycle ${c.id} packing status set to ${options.status}`, json: options.json }));
21
+ }
@@ -0,0 +1,28 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import CycleActionView from "../../components/CycleActionView.js";
5
+ export const description = "Reassign a cycle to a different inventory item";
6
+ export const args = zod.tuple([
7
+ zod.string().describe(argument({ name: "id", description: "Numeric cycle ID" })),
8
+ ]);
9
+ export const options = zod.object({
10
+ itemId: zod.number().describe(option({ description: "ID of the item to assign the cycle to" })),
11
+ reallocate: zod
12
+ .boolean()
13
+ .default(false)
14
+ .describe(option({
15
+ description: "Re-allocate other rentals on the new item to resolve conflicts",
16
+ })),
17
+ json: zod
18
+ .boolean()
19
+ .default(false)
20
+ .describe(option({ description: "Output raw JSON" })),
21
+ });
22
+ export default function Reassign({ args, options }) {
23
+ const [id] = args;
24
+ return (React.createElement(CycleActionView, { loadingLabel: `Reassigning cycle ${id} to item ${options.itemId}…`, action: (client) => client.updateCycle(id, {
25
+ itemId: options.itemId,
26
+ reallocateConflictingRentals: options.reallocate || undefined,
27
+ }), successLabel: (c) => `Cycle ${c.id} reassigned to item ${options.itemId}`, json: options.json }));
28
+ }
@@ -0,0 +1,23 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import CycleActionView from "../../components/CycleActionView.js";
5
+ export const description = "Mark a cycle as received back from the customer";
6
+ export const args = zod.tuple([
7
+ zod.string().describe(argument({ name: "id", description: "Numeric cycle ID" })),
8
+ ]);
9
+ export const options = zod.object({
10
+ at: zod
11
+ .string()
12
+ .optional()
13
+ .describe(option({ description: "Receive time (ISO date-time; defaults to now)" })),
14
+ json: zod
15
+ .boolean()
16
+ .default(false)
17
+ .describe(option({ description: "Output raw JSON" })),
18
+ });
19
+ export default function Receive({ args, options }) {
20
+ const [id] = args;
21
+ const receivedAt = options.at ?? new Date().toISOString();
22
+ return (React.createElement(CycleActionView, { loadingLabel: `Receiving cycle ${id}…`, action: (client) => client.updateCycle(id, { receivedAt }), successLabel: (c) => `Cycle ${c.id} marked as received`, json: options.json }));
23
+ }
@@ -0,0 +1,74 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import CycleActionView from "../../components/CycleActionView.js";
5
+ export const description = "Change a cycle’s scheduled dates";
6
+ export const args = zod.tuple([
7
+ zod.string().describe(argument({ name: "id", description: "Numeric cycle ID" })),
8
+ ]);
9
+ export const options = zod.object({
10
+ fulfillAt: zod
11
+ .string()
12
+ .optional()
13
+ .describe(option({ description: "Scheduled dispatch (ISO date-time)" })),
14
+ receiveAt: zod
15
+ .string()
16
+ .optional()
17
+ .describe(option({ description: "Scheduled receive (ISO date-time)" })),
18
+ minimumRentalEnd: zod
19
+ .string()
20
+ .optional()
21
+ .describe(option({ description: "Earliest the rental may end (ISO date-time)" })),
22
+ rentalStartBefore: zod
23
+ .string()
24
+ .optional()
25
+ .describe(option({ description: "Move rental start to before (ISO date-time)" })),
26
+ rentalStartAfter: zod
27
+ .string()
28
+ .optional()
29
+ .describe(option({ description: "Move rental start to after (ISO date-time)" })),
30
+ rentalEndBefore: zod
31
+ .string()
32
+ .optional()
33
+ .describe(option({ description: "Move rental end to before (ISO date-time)" })),
34
+ rentalEndAfter: zod
35
+ .string()
36
+ .optional()
37
+ .describe(option({ description: "Move rental end to after (ISO date-time)" })),
38
+ json: zod
39
+ .boolean()
40
+ .default(false)
41
+ .describe(option({ description: "Output raw JSON" })),
42
+ });
43
+ function buildUpdate(options) {
44
+ const update = {};
45
+ if (options.fulfillAt)
46
+ update.fulfillAt = options.fulfillAt;
47
+ if (options.receiveAt)
48
+ update.receiveAt = options.receiveAt;
49
+ if (options.minimumRentalEnd)
50
+ update.minimumRentalEnd = options.minimumRentalEnd;
51
+ if (options.rentalStartBefore || options.rentalStartAfter) {
52
+ update.rentalStart = {
53
+ before: options.rentalStartBefore,
54
+ after: options.rentalStartAfter,
55
+ };
56
+ }
57
+ if (options.rentalEndBefore || options.rentalEndAfter) {
58
+ update.rentalEnd = {
59
+ before: options.rentalEndBefore,
60
+ after: options.rentalEndAfter,
61
+ };
62
+ }
63
+ return update;
64
+ }
65
+ export default function Reschedule({ args, options }) {
66
+ const [id] = args;
67
+ const update = buildUpdate(options);
68
+ return (React.createElement(CycleActionView, { loadingLabel: `Rescheduling cycle ${id}…`, action: (client) => {
69
+ if (Object.keys(update).length === 0) {
70
+ throw new Error("Provide at least one date to change (e.g. --fulfill-at, --receive-at).");
71
+ }
72
+ return client.updateCycle(id, update);
73
+ }, successLabel: (c) => `Cycle ${c.id} rescheduled`, json: options.json }));
74
+ }
@@ -0,0 +1,52 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import ReturnResultView from "../../components/ReturnResultView.js";
5
+ export const description = "Start a return for a cycle";
6
+ export const args = zod.tuple([
7
+ zod.string().describe(argument({ name: "id", description: "Numeric cycle ID" })),
8
+ ]);
9
+ export const options = zod.object({
10
+ status: zod
11
+ .enum(["awaiting", "received", "missing"])
12
+ .optional()
13
+ .describe(option({ description: "Return line status: awaiting|received|missing" })),
14
+ method: zod
15
+ .enum(["collection", "label"])
16
+ .optional()
17
+ .describe(option({ description: "Return method: collection|label" })),
18
+ trackingUrl: zod
19
+ .string()
20
+ .optional()
21
+ .describe(option({ description: "Tracking URL (for label method)" })),
22
+ collectionDate: zod
23
+ .string()
24
+ .optional()
25
+ .describe(option({ description: "Collection date (ISO date, for collection method)" })),
26
+ addressId: zod
27
+ .number()
28
+ .optional()
29
+ .describe(option({ description: "Shopify address ID (for collection method)" })),
30
+ json: zod
31
+ .boolean()
32
+ .default(false)
33
+ .describe(option({ description: "Output raw JSON" })),
34
+ });
35
+ function buildBody(id, options) {
36
+ const body = {
37
+ data: [{ rentalId: Number(id), status: options.status ?? null }],
38
+ };
39
+ if (options.method) {
40
+ body.returnMethodAttributes = {
41
+ type: options.method === "collection" ? "ReturnMethod::Collection" : "ReturnMethod::ReturnLabel",
42
+ trackingUrl: options.trackingUrl ?? null,
43
+ collectionDate: options.collectionDate ?? null,
44
+ addressShopifyId: options.addressId ?? null,
45
+ };
46
+ }
47
+ return body;
48
+ }
49
+ export default function ReturnCycle({ args, options }) {
50
+ const [id] = args;
51
+ return (React.createElement(ReturnResultView, { loadingLabel: `Creating return for cycle ${id}…`, action: (client) => client.createReturn(buildBody(id, options)), json: options.json }));
52
+ }
@@ -0,0 +1,45 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import CycleActionView from "../../components/CycleActionView.js";
5
+ export const description = "Add or remove tags on a cycle";
6
+ export const args = zod.tuple([
7
+ zod.string().describe(argument({ name: "id", description: "Numeric cycle ID" })),
8
+ ]);
9
+ export const options = zod.object({
10
+ add: zod
11
+ .string()
12
+ .optional()
13
+ .describe(option({ description: "Tags to add (comma-separated)" })),
14
+ remove: zod
15
+ .string()
16
+ .optional()
17
+ .describe(option({ description: "Tags to remove (comma-separated)" })),
18
+ json: zod
19
+ .boolean()
20
+ .default(false)
21
+ .describe(option({ description: "Output raw JSON" })),
22
+ });
23
+ function splitTags(value) {
24
+ if (!value)
25
+ return [];
26
+ return value
27
+ .split(",")
28
+ .map((t) => t.trim())
29
+ .filter(Boolean);
30
+ }
31
+ export default function Tag({ args, options }) {
32
+ const [id] = args;
33
+ const add = splitTags(options.add);
34
+ const remove = splitTags(options.remove);
35
+ const tagsAttributes = [
36
+ ...add.map((title) => ({ title })),
37
+ ...remove.map((title) => ({ title, _destroy: true })),
38
+ ];
39
+ return (React.createElement(CycleActionView, { loadingLabel: `Updating tags on cycle ${id}…`, action: (client) => {
40
+ if (tagsAttributes.length === 0) {
41
+ throw new Error("Provide tags via --add and/or --remove.");
42
+ }
43
+ return client.updateCycle(id, { tagsAttributes });
44
+ }, successLabel: (c) => `Cycle ${c.id} tags updated`, json: options.json }));
45
+ }
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import zod from "zod";
3
+ import CyclesListView from "../../components/CyclesListView.js";
4
+ import { listViewShape, viewFilters } from "../../lib/listViewOptions.js";
5
+ export const description = "Cycles awaiting dispatch (unfulfilled)";
6
+ export const options = zod.object({ ...listViewShape() });
7
+ export default function ToFulfill({ options }) {
8
+ return (React.createElement(CyclesListView, { filters: { ...viewFilters(options), unfulfilled: true }, all: options.all, json: options.json }));
9
+ }
@@ -0,0 +1,9 @@
1
+ import React from "react";
2
+ import zod from "zod";
3
+ import CyclesListView from "../../components/CyclesListView.js";
4
+ import { listViewShape, viewFilters } from "../../lib/listViewOptions.js";
5
+ export const description = "Cycles awaiting return (receival due)";
6
+ export const options = zod.object({ ...listViewShape() });
7
+ export default function ToReceive({ options }) {
8
+ return (React.createElement(CyclesListView, { filters: { ...viewFilters(options), receivalStatus: "due" }, all: options.all, json: options.json }));
9
+ }
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import zod from "zod";
3
+ import CyclesListView from "../../components/CyclesListView.js";
4
+ import { listViewShape, viewFilters } from "../../lib/listViewOptions.js";
5
+ export const description = "Cycles scheduled to be received back today";
6
+ export const options = zod.object({ ...listViewShape() });
7
+ // Local YYYY-MM-DD for today.
8
+ function today() {
9
+ const now = new Date();
10
+ const y = now.getFullYear();
11
+ const m = String(now.getMonth() + 1).padStart(2, "0");
12
+ const d = String(now.getDate()).padStart(2, "0");
13
+ return `${y}-${m}-${d}`;
14
+ }
15
+ export default function Today({ options }) {
16
+ const date = today();
17
+ return (React.createElement(CyclesListView, { filters: { ...viewFilters(options), receiveAt: { gte: date, lte: date } }, all: options.all, json: options.json }));
18
+ }
@@ -0,0 +1,75 @@
1
+ import { Box, Text } from "ink";
2
+ import { option } from "pastel";
3
+ import React from "react";
4
+ import zod from "zod";
5
+ import ErrorMessage from "../../components/ErrorMessage.js";
6
+ import ExitError from "../../components/ExitError.js";
7
+ import JsonOutput from "../../components/JsonOutput.js";
8
+ import Loading from "../../components/Loading.js";
9
+ import { useAsync } from "../../hooks/useAsync.js";
10
+ import { resolveClient, NO_TOKEN_HINT } from "../../lib/resolveClient.js";
11
+ export const description = "Show a variant's per-day availability timeline";
12
+ export const options = zod.object({
13
+ variantShopifyId: zod
14
+ .string()
15
+ .describe(option({ description: "Shopify variant ID (numeric segment, required)" })),
16
+ locationId: zod
17
+ .string()
18
+ .optional()
19
+ .describe(option({ description: "Filter to a Shopify location ID" })),
20
+ deliveryMethod: zod
21
+ .enum(["shipping", "pick_up"])
22
+ .optional()
23
+ .describe(option({ description: "Logistics profile: shipping|pick_up (default shipping)" })),
24
+ json: zod
25
+ .boolean()
26
+ .default(false)
27
+ .describe(option({ description: "Output raw JSON" })),
28
+ });
29
+ function Field({ label, children }) {
30
+ return (React.createElement(Box, null,
31
+ React.createElement(Box, { width: 24 },
32
+ React.createElement(Text, { dimColor: true }, label)),
33
+ React.createElement(Text, null, children)));
34
+ }
35
+ export default function Availability({ options }) {
36
+ const resolved = resolveClient();
37
+ const state = useAsync(async () => {
38
+ if (!resolved.client)
39
+ throw new Error(resolved.error);
40
+ return resolved.client.getVariantAvailability({
41
+ variantShopifyId: options.variantShopifyId,
42
+ locationId: options.locationId,
43
+ deliveryMethodType: options.deliveryMethod,
44
+ });
45
+ }, []);
46
+ if (state.loading) {
47
+ return options.json ? null : React.createElement(Loading, { label: "Fetching availability\u2026" });
48
+ }
49
+ if (state.error) {
50
+ if (options.json)
51
+ return React.createElement(ExitError, { message: state.error.message });
52
+ return (React.createElement(ErrorMessage, { message: state.error.message, hint: resolved.error ? NO_TOKEN_HINT : undefined }));
53
+ }
54
+ if (options.json) {
55
+ return React.createElement(JsonOutput, { data: state.data });
56
+ }
57
+ const timeline = state.data;
58
+ // Object.entries returns a fresh array, so sorting in place is safe here.
59
+ // oxlint-disable-next-line no-array-sort
60
+ const days = Object.entries(timeline.occupancy ?? {}).sort(([a], [b]) => a.localeCompare(b));
61
+ return (React.createElement(Box, { flexDirection: "column" },
62
+ React.createElement(Text, { bold: true, underline: true },
63
+ "Variant ",
64
+ options.variantShopifyId,
65
+ " availability"),
66
+ React.createElement(Field, { label: "Inventory count" }, String(timeline.inventoryCount)),
67
+ React.createElement(Field, { label: "Future availability" }, String(timeline.futureAvailabilityInventoryCount)),
68
+ React.createElement(Field, { label: "Uncommitted" }, String(timeline.uncommitedInventoryCount)),
69
+ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, days.length === 0 ? (React.createElement(Text, { dimColor: true }, "No occupancy data in the window.")) : (days.map(([date, count]) => (React.createElement(Box, { key: date },
70
+ React.createElement(Box, { width: 14 },
71
+ React.createElement(Text, { dimColor: true }, date)),
72
+ React.createElement(Text, null,
73
+ count,
74
+ " available"))))))));
75
+ }
@@ -0,0 +1,29 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import CommentResultView from "../../components/CommentResultView.js";
5
+ import ErrorMessage from "../../components/ErrorMessage.js";
6
+ export const description = "Add a comment to an item's timeline";
7
+ export const args = zod.tuple([
8
+ zod.string().describe(argument({ name: "id", description: "Numeric item ID" })),
9
+ zod.string().describe(argument({ name: "message", description: "Comment text" })),
10
+ ]);
11
+ export const options = zod.object({
12
+ json: zod
13
+ .boolean()
14
+ .default(false)
15
+ .describe(option({ description: "Output raw JSON" })),
16
+ });
17
+ export default function Comment({ args, options }) {
18
+ const [id, message] = args;
19
+ if (!message.trim()) {
20
+ return React.createElement(ErrorMessage, { message: "Comment message cannot be empty." });
21
+ }
22
+ return (React.createElement(CommentResultView, { loadingLabel: `Adding comment to item ${id}…`, action: (client) => client.createTimelineComment({
23
+ timelineEvent: {
24
+ eventableId: Number(id),
25
+ eventableType: "Item",
26
+ message,
27
+ },
28
+ }), json: options.json }));
29
+ }
@@ -0,0 +1,57 @@
1
+ import { option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import ItemsResultView from "../../components/ItemsResultView.js";
5
+ export const description = "Create one or more items against an imported product";
6
+ export const options = zod.object({
7
+ shopifyVariantId: zod
8
+ .number()
9
+ .describe(option({ description: "Shopify variant ID to create items for (required)" })),
10
+ visibility: zod
11
+ .enum(["available", "unavailable", "retired", "sold"])
12
+ .describe(option({ description: "Visibility: available|unavailable|retired|sold (required)" })),
13
+ quantity: zod
14
+ .number()
15
+ .min(1)
16
+ .optional()
17
+ .describe(option({ description: "Number of items to create" })),
18
+ serials: zod
19
+ .string()
20
+ .optional()
21
+ .describe(option({ description: "Comma-separated serials to assign" })),
22
+ conditionId: zod
23
+ .number()
24
+ .optional()
25
+ .describe(option({ description: "Condition ID to set (see `conditions list`)" })),
26
+ locationShopifyId: zod
27
+ .number()
28
+ .optional()
29
+ .describe(option({ description: "Shopify location ID (see `locations list`)" })),
30
+ pickLocation: zod
31
+ .string()
32
+ .optional()
33
+ .describe(option({ description: "Pick location, Zone-Aisle-Shelf-Bin (e.g. A1-02-B3)" })),
34
+ json: zod
35
+ .boolean()
36
+ .default(false)
37
+ .describe(option({ description: "Output raw JSON" })),
38
+ });
39
+ export default function Create({ options }) {
40
+ const body = {
41
+ shopifyVariantId: options.shopifyVariantId,
42
+ visibility: options.visibility,
43
+ quantity: options.quantity,
44
+ conditionId: options.conditionId,
45
+ locationShopifyId: options.locationShopifyId,
46
+ pickLocation: options.pickLocation,
47
+ };
48
+ if (options.serials) {
49
+ const serials = options.serials
50
+ .split(",")
51
+ .map((s) => s.trim())
52
+ .filter(Boolean);
53
+ if (serials.length > 0)
54
+ body.serials = serials;
55
+ }
56
+ return (React.createElement(ItemsResultView, { loadingLabel: "Creating items\u2026", action: (client) => client.createItems(body), successLabel: (items) => `Created ${items.length} item${items.length === 1 ? "" : "s"}`, json: options.json }));
57
+ }
@@ -0,0 +1,50 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import ErrorMessage from "../../components/ErrorMessage.js";
5
+ import ExitError from "../../components/ExitError.js";
6
+ import ItemDetail from "../../components/ItemDetail.js";
7
+ import JsonOutput from "../../components/JsonOutput.js";
8
+ import Loading from "../../components/Loading.js";
9
+ import { useAsync } from "../../hooks/useAsync.js";
10
+ import { resolveClient, NO_TOKEN_HINT } from "../../lib/resolveClient.js";
11
+ export const description = "Retrieve a single item by ID";
12
+ export const args = zod.tuple([
13
+ zod.string().describe(argument({
14
+ name: "id",
15
+ description: "Numeric ID of the item",
16
+ })),
17
+ ]);
18
+ export const options = zod.object({
19
+ include: zod
20
+ .string()
21
+ .optional()
22
+ .describe(option({
23
+ description: "Relations to include, e.g. timelineEvents",
24
+ })),
25
+ json: zod
26
+ .boolean()
27
+ .default(false)
28
+ .describe(option({ description: "Output raw JSON" })),
29
+ });
30
+ export default function Get({ args, options }) {
31
+ const [id] = args;
32
+ const resolved = resolveClient();
33
+ const state = useAsync(async () => {
34
+ if (!resolved.client)
35
+ throw new Error(resolved.error);
36
+ return resolved.client.getItem(id, { include: options.include });
37
+ }, []);
38
+ if (state.loading) {
39
+ return options.json ? null : React.createElement(Loading, { label: `Fetching item ${id}…` });
40
+ }
41
+ if (state.error) {
42
+ if (options.json)
43
+ return React.createElement(ExitError, { message: state.error.message });
44
+ return (React.createElement(ErrorMessage, { message: state.error.message, hint: resolved.error ? NO_TOKEN_HINT : undefined }));
45
+ }
46
+ if (options.json) {
47
+ return React.createElement(JsonOutput, { data: state.data });
48
+ }
49
+ return React.createElement(ItemDetail, { item: state.data });
50
+ }
@@ -0,0 +1,3 @@
1
+ // Group landing: description only (no default export) so Pastel falls through
2
+ // to Commander's auto-generated help listing the subcommands.
3
+ export const description = "List, inspect, and manage inventory items";
@@ -0,0 +1,25 @@
1
+ import { option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import ItemsListView from "../../components/ItemsListView.js";
5
+ import { listViewShape } from "../../lib/listViewOptions.js";
6
+ export const description = "List inventory items, optionally filtered";
7
+ const visibility = zod.enum(["available", "unavailable", "sold", "retired"]);
8
+ export const options = zod.object({
9
+ ...listViewShape(),
10
+ visibility: visibility
11
+ .optional()
12
+ .describe(option({ description: "Visibility: available|unavailable|sold|retired" })),
13
+ });
14
+ function buildFilters(options) {
15
+ const view = options;
16
+ return {
17
+ search: view.search,
18
+ limit: view.limit,
19
+ page: view.page,
20
+ visibility: options.visibility,
21
+ };
22
+ }
23
+ export default function List({ options }) {
24
+ return React.createElement(ItemsListView, { filters: buildFilters(options), all: options.all, json: options.json });
25
+ }
@@ -0,0 +1,60 @@
1
+ import { argument, option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import ErrorMessage from "../../components/ErrorMessage.js";
5
+ import ItemActionView from "../../components/ItemActionView.js";
6
+ export const description = "Update an item's serial, condition, status, etc.";
7
+ export const args = zod.tuple([
8
+ zod.string().describe(argument({ name: "id", description: "Numeric item ID" })),
9
+ ]);
10
+ export const options = zod.object({
11
+ serial: zod
12
+ .string()
13
+ .optional()
14
+ .describe(option({ description: "Serial number" })),
15
+ shopifyVariantId: zod
16
+ .number()
17
+ .optional()
18
+ .describe(option({ description: "Shopify variant ID" })),
19
+ status: zod
20
+ .enum(["processed", "unprocessed"])
21
+ .optional()
22
+ .describe(option({ description: "Status: processed|unprocessed" })),
23
+ visibility: zod
24
+ .enum(["available", "unavailable", "retired", "sold"])
25
+ .optional()
26
+ .describe(option({ description: "Visibility: available|unavailable|retired|sold" })),
27
+ conditionId: zod
28
+ .number()
29
+ .optional()
30
+ .describe(option({ description: "Condition ID (see `conditions list`)" })),
31
+ locationShopifyId: zod
32
+ .number()
33
+ .optional()
34
+ .describe(option({ description: "Shopify location ID (see `locations list`)" })),
35
+ pickLocation: zod
36
+ .string()
37
+ .optional()
38
+ .describe(option({ description: "Pick location, Zone-Aisle-Shelf-Bin (e.g. A1-02-B3)" })),
39
+ json: zod
40
+ .boolean()
41
+ .default(false)
42
+ .describe(option({ description: "Output raw JSON" })),
43
+ });
44
+ export default function Update({ args, options }) {
45
+ const [id] = args;
46
+ const body = {
47
+ serial: options.serial,
48
+ shopifyVariantId: options.shopifyVariantId,
49
+ status: options.status,
50
+ visibility: options.visibility,
51
+ conditionId: options.conditionId,
52
+ locationShopifyId: options.locationShopifyId,
53
+ pickLocation: options.pickLocation,
54
+ };
55
+ const hasUpdate = Object.values(body).some((v) => v !== undefined);
56
+ if (!hasUpdate) {
57
+ return React.createElement(ErrorMessage, { message: "Nothing to update. Pass at least one field to change." });
58
+ }
59
+ return (React.createElement(ItemActionView, { loadingLabel: `Updating item ${id}…`, action: (client) => client.updateItem(id, body), successLabel: (item) => `Item #${item.sequentialId ?? item.id} updated`, json: options.json }));
60
+ }
@@ -0,0 +1,3 @@
1
+ // Group landing: description only (no default export) so Pastel falls through
2
+ // to Commander's auto-generated help listing the subcommands.
3
+ export const description = "List inventory locations";
@@ -0,0 +1,19 @@
1
+ import { option } from "pastel";
2
+ import React from "react";
3
+ import zod from "zod";
4
+ import ReferenceListView from "../../components/ReferenceListView.js";
5
+ import { dash } from "../../lib/format.js";
6
+ export const description = "List all locations";
7
+ const COLUMNS = [
8
+ { label: "Shopify ID", width: 16, value: (l) => dash(l.shopifyId) },
9
+ { label: "Name", value: (l) => dash(l.name) },
10
+ ];
11
+ export const options = zod.object({
12
+ json: zod
13
+ .boolean()
14
+ .default(false)
15
+ .describe(option({ description: "Output raw JSON" })),
16
+ });
17
+ export default function List({ options }) {
18
+ return (React.createElement(ReferenceListView, { fetch: (client) => client.listLocations(), columns: COLUMNS, rowKey: (l) => l.shopifyId, loadingLabel: "Fetching locations\u2026", emptyLabel: "No locations found.", noun: "location", json: options.json }));
19
+ }