@catandbox/schrodinger-shopify-adapter 0.1.15 → 0.1.17
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.
|
@@ -16,6 +16,8 @@ const STATUS_OPTIONS = [
|
|
|
16
16
|
{ label: "Archived", value: "Archived" }
|
|
17
17
|
];
|
|
18
18
|
const SORT_OPTIONS = [
|
|
19
|
+
{ label: "Newest first", value: "created_desc" },
|
|
20
|
+
{ label: "Oldest first", value: "created_asc" },
|
|
19
21
|
{ label: "Updated (newest)", value: "updated_desc" },
|
|
20
22
|
{ label: "Updated (oldest)", value: "updated_asc" },
|
|
21
23
|
{ label: "Title (A-Z)", value: "title_asc" },
|
|
@@ -26,8 +28,9 @@ export function SupportTicketList(props) {
|
|
|
26
28
|
const [selectedTab, setSelectedTab] = useState(0);
|
|
27
29
|
const isArchivedTab = selectedTab === 1;
|
|
28
30
|
const [status, setStatus] = useState(props.initialStatus ?? "All");
|
|
31
|
+
const [category, setCategory] = useState("All");
|
|
29
32
|
const [search, setSearch] = useState("");
|
|
30
|
-
const [sort, setSort] = useState("
|
|
33
|
+
const [sort, setSort] = useState("created_desc");
|
|
31
34
|
const [selectedIds, setSelectedIds] = useState({});
|
|
32
35
|
const [archiveError, setArchiveError] = useState(null);
|
|
33
36
|
const [toastContent, setToastContent] = useState(null);
|
|
@@ -35,6 +38,7 @@ export function SupportTicketList(props) {
|
|
|
35
38
|
const effectiveStatus = isArchivedTab ? "Archived" : status;
|
|
36
39
|
const tickets = useTickets({
|
|
37
40
|
status: effectiveStatus,
|
|
41
|
+
category,
|
|
38
42
|
search,
|
|
39
43
|
sort
|
|
40
44
|
}, {
|
|
@@ -48,6 +52,15 @@ export function SupportTicketList(props) {
|
|
|
48
52
|
}
|
|
49
53
|
return map;
|
|
50
54
|
}, [portalConfig.data]);
|
|
55
|
+
const categoryOptions = useMemo(() => {
|
|
56
|
+
const options = [
|
|
57
|
+
{ label: "All categories", value: "All" }
|
|
58
|
+
];
|
|
59
|
+
for (const cat of portalConfig.data?.categories ?? []) {
|
|
60
|
+
options.push({ label: cat.name, value: cat.id });
|
|
61
|
+
}
|
|
62
|
+
return options;
|
|
63
|
+
}, [portalConfig.data]);
|
|
51
64
|
const allItems = tickets.data ?? [];
|
|
52
65
|
const selectedTicketIds = useMemo(() => allItems.filter((ticket) => selectedIds[ticket.id]).map((ticket) => ticket.id), [allItems, selectedIds]);
|
|
53
66
|
const allChecked = allItems.length > 0 && selectedTicketIds.length === allItems.length;
|
|
@@ -105,17 +118,21 @@ export function SupportTicketList(props) {
|
|
|
105
118
|
{ id: "active", content: "My Tickets" },
|
|
106
119
|
{ id: "archived", content: "Archived" }
|
|
107
120
|
];
|
|
108
|
-
return (_jsx(Frame, { children: _jsx(Page, { title: "Support tickets", subtitle: "Customer portal queue", children: _jsxs(BlockStack, { gap: "400", children: [_jsx(Card, { padding: "0", children: _jsx(Tabs, { tabs: tabs, selected: selectedTab, onSelect: handleTabChange, fitted: true, children: _jsx(Box, { padding: "400", children: _jsxs(BlockStack, { gap: "300", children: [_jsxs(InlineGrid, { columns: {
|
|
121
|
+
return (_jsx(Frame, { children: _jsx(Page, { title: "Support tickets", subtitle: "Customer portal queue", children: _jsxs(BlockStack, { gap: "400", children: [_jsx(Card, { padding: "0", children: _jsx(Tabs, { tabs: tabs, selected: selectedTab, onSelect: handleTabChange, fitted: true, children: _jsx(Box, { padding: "400", children: _jsxs(BlockStack, { gap: "300", children: [_jsxs(InlineGrid, { columns: {
|
|
122
|
+
xs: "1fr",
|
|
123
|
+
md: isArchivedTab ? "1fr 1fr 1fr" : "2fr 1fr 1fr 1fr"
|
|
124
|
+
}, gap: "300", children: [_jsx(TextField, { label: "Search", value: search, onChange: setSearch, placeholder: "Search by title or ticket id", autoComplete: "off" }), !isArchivedTab ? (_jsx(Select, { label: "Status", options: STATUS_OPTIONS.map((option) => ({
|
|
109
125
|
label: option.label,
|
|
110
126
|
value: option.value
|
|
111
|
-
})), value: status, onChange: (value) => setStatus(value) })) : null, _jsx(Select, { label: "
|
|
127
|
+
})), value: status, onChange: (value) => setStatus(value) })) : null, _jsx(Select, { label: "Category", options: categoryOptions, value: category, onChange: setCategory }), _jsx(Select, { label: "Order", options: SORT_OPTIONS, value: sort, onChange: (value) => setSort(value) })] }), !isArchivedTab ? (_jsxs(InlineStack, { align: "space-between", blockAlign: "center", children: [_jsx(Checkbox, { label: "Select all", checked: allChecked, onChange: toggleSelectAll }), _jsx(Button, { tone: "critical", disabled: !hasSelection || archiving, loading: archiving, onClick: () => void runBulkArchive(), children: "Archive selected" })] })) : null] }) }) }) }), _jsx(ErrorBanner, { error: tickets.error }), _jsx(ErrorBanner, { error: archiveError }), tickets.loading ? (_jsx(Card, { children: _jsx(InlineStack, { align: "center", children: _jsx(Spinner, { size: "large" }) }) })) : allItems.length === 0 ? (_jsx(Card, { children: _jsx(EmptyState, { heading: isArchivedTab ? "No archived tickets" : "No tickets match the current filters", action: isArchivedTab
|
|
112
128
|
? undefined
|
|
113
129
|
: {
|
|
114
130
|
content: "Reset filters",
|
|
115
131
|
onAction: () => {
|
|
116
132
|
setSearch("");
|
|
117
133
|
setStatus("All");
|
|
118
|
-
|
|
134
|
+
setCategory("All");
|
|
135
|
+
setSort("created_desc");
|
|
119
136
|
}
|
|
120
137
|
}, image: "https://cdn.shopify.com/s/files/1/0262/4071/2726/files/emptystate-files.png", children: _jsx(Text, { as: "p", variant: "bodyMd", children: isArchivedTab
|
|
121
138
|
? "Archived tickets will appear here."
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Ticket, TicketStatus } from "@catandbox/schrodinger-contracts";
|
|
2
2
|
import type { QueryHookOptions, QueryHookState } from "./types";
|
|
3
|
-
export type TicketSort = "updated_desc" | "updated_asc" | "title_asc" | "title_desc";
|
|
3
|
+
export type TicketSort = "updated_desc" | "updated_asc" | "created_desc" | "created_asc" | "title_asc" | "title_desc";
|
|
4
4
|
interface UseTicketsParams {
|
|
5
5
|
status?: TicketStatus | "All";
|
|
6
|
+
category?: string | "All";
|
|
6
7
|
search?: string;
|
|
7
8
|
sort?: TicketSort;
|
|
8
9
|
}
|
|
@@ -45,14 +45,24 @@ function toTicketListItem(ticket) {
|
|
|
45
45
|
}
|
|
46
46
|
function applyTicketFiltersAndSort(items, params) {
|
|
47
47
|
const search = (params.search ?? "").trim().toLowerCase();
|
|
48
|
-
|
|
48
|
+
let filtered = search
|
|
49
49
|
? items.filter((item) => item.title.toLowerCase().includes(search) || item.id.toLowerCase().includes(search))
|
|
50
50
|
: items.slice();
|
|
51
|
+
const category = params.category;
|
|
52
|
+
if (category && category !== "All") {
|
|
53
|
+
filtered = filtered.filter((item) => item.categoryId === category);
|
|
54
|
+
}
|
|
51
55
|
const sort = params.sort ?? "updated_desc";
|
|
52
56
|
switch (sort) {
|
|
53
57
|
case "updated_asc":
|
|
54
58
|
filtered.sort((left, right) => left.updatedAt - right.updatedAt);
|
|
55
59
|
break;
|
|
60
|
+
case "created_desc":
|
|
61
|
+
filtered.sort((left, right) => right.createdAt - left.createdAt);
|
|
62
|
+
break;
|
|
63
|
+
case "created_asc":
|
|
64
|
+
filtered.sort((left, right) => left.createdAt - right.createdAt);
|
|
65
|
+
break;
|
|
56
66
|
case "title_asc":
|
|
57
67
|
filtered.sort((left, right) => left.title.localeCompare(right.title));
|
|
58
68
|
break;
|