@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.
- package/README.md +107 -0
- package/dist/cli.js +9 -0
- package/dist/commands/comments/delete.js +48 -0
- package/dist/commands/comments/index.js +3 -0
- package/dist/commands/conditions/index.js +3 -0
- package/dist/commands/conditions/list.js +20 -0
- package/dist/commands/custom-fields/definition.js +58 -0
- package/dist/commands/custom-fields/definitions.js +25 -0
- package/dist/commands/custom-fields/delete.js +45 -0
- package/dist/commands/custom-fields/index.js +3 -0
- package/dist/commands/custom-fields/set.js +43 -0
- package/dist/commands/custom-fields/update.js +19 -0
- package/dist/commands/cycles/comment.js +29 -0
- package/dist/commands/cycles/fulfil.js +24 -0
- package/dist/commands/cycles/get.js +50 -0
- package/dist/commands/cycles/index.js +3 -0
- package/dist/commands/cycles/list.js +124 -0
- package/dist/commands/cycles/overdue.js +25 -0
- package/dist/commands/cycles/pack.js +21 -0
- package/dist/commands/cycles/reassign.js +28 -0
- package/dist/commands/cycles/receive.js +23 -0
- package/dist/commands/cycles/reschedule.js +74 -0
- package/dist/commands/cycles/return.js +52 -0
- package/dist/commands/cycles/tag.js +45 -0
- package/dist/commands/cycles/to-fulfill.js +9 -0
- package/dist/commands/cycles/to-receive.js +9 -0
- package/dist/commands/cycles/today.js +18 -0
- package/dist/commands/items/availability.js +75 -0
- package/dist/commands/items/comment.js +29 -0
- package/dist/commands/items/create.js +57 -0
- package/dist/commands/items/get.js +50 -0
- package/dist/commands/items/index.js +3 -0
- package/dist/commands/items/list.js +25 -0
- package/dist/commands/items/update.js +60 -0
- package/dist/commands/locations/index.js +3 -0
- package/dist/commands/locations/list.js +19 -0
- package/dist/commands/login.js +76 -0
- package/dist/commands/products/import.js +65 -0
- package/dist/commands/products/index.js +3 -0
- package/dist/commands/products/list.js +19 -0
- package/dist/commands/returns/comment.js +29 -0
- package/dist/commands/returns/get.js +50 -0
- package/dist/commands/returns/index.js +3 -0
- package/dist/commands/returns/list.js +27 -0
- package/dist/commands/returns/update.js +68 -0
- package/dist/components/CommentResultView.js +40 -0
- package/dist/components/CustomFieldResultView.js +46 -0
- package/dist/components/CycleActionView.js +36 -0
- package/dist/components/CycleDetail.js +60 -0
- package/dist/components/CycleTable.js +68 -0
- package/dist/components/CyclesListView.js +30 -0
- package/dist/components/ErrorMessage.js +13 -0
- package/dist/components/ExitError.js +13 -0
- package/dist/components/ItemActionView.js +36 -0
- package/dist/components/ItemDetail.js +48 -0
- package/dist/components/ItemTable.js +58 -0
- package/dist/components/ItemsListView.js +30 -0
- package/dist/components/ItemsResultView.js +37 -0
- package/dist/components/JsonOutput.js +12 -0
- package/dist/components/Loading.js +11 -0
- package/dist/components/ProductTable.js +49 -0
- package/dist/components/ProductsListView.js +30 -0
- package/dist/components/ReferenceListView.js +49 -0
- package/dist/components/ReturnActionView.js +36 -0
- package/dist/components/ReturnDetail.js +55 -0
- package/dist/components/ReturnResultView.js +49 -0
- package/dist/components/ReturnTable.js +58 -0
- package/dist/components/ReturnsListView.js +30 -0
- package/dist/components/Status.js +33 -0
- package/dist/hooks/useAsync.js +32 -0
- package/dist/lib/client.js +302 -0
- package/dist/lib/config.js +38 -0
- package/dist/lib/format.js +49 -0
- package/dist/lib/listViewOptions.js +39 -0
- package/dist/lib/resolveClient.js +11 -0
- package/dist/lib/types.js +3 -0
- 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,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,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
|
+
}
|