@consilioweb/payload-support 0.5.1 → 0.6.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 (86) hide show
  1. package/dist/components/TicketConversation/components/AISummaryPanel.js +76 -0
  2. package/dist/components/TicketConversation/components/ActionPanels.js +118 -0
  3. package/dist/components/TicketConversation/components/ActivityLog.js +23 -0
  4. package/dist/components/TicketConversation/components/ClientBar.js +42 -0
  5. package/dist/components/TicketConversation/components/ClientHistory.js +137 -0
  6. package/dist/components/TicketConversation/components/CodeBlock.js +152 -0
  7. package/dist/components/TicketConversation/components/CodeBlockInserter.js +155 -0
  8. package/dist/components/TicketConversation/components/QuickActions.js +65 -0
  9. package/dist/components/TicketConversation/components/TicketHeader.js +92 -0
  10. package/dist/components/TicketConversation/components/TimeTrackingPanel.js +133 -0
  11. package/dist/components/TicketConversation/config.js +41 -0
  12. package/dist/components/TicketConversation/constants.js +96 -0
  13. package/dist/components/TicketConversation/context.js +11 -0
  14. package/dist/components/TicketConversation/hooks/useAI.js +176 -0
  15. package/dist/components/TicketConversation/hooks/useMessageActions.js +135 -0
  16. package/dist/components/TicketConversation/hooks/useReply.js +187 -0
  17. package/dist/components/TicketConversation/hooks/useTicketActions.js +230 -0
  18. package/dist/components/TicketConversation/hooks/useTimeTracking.js +122 -0
  19. package/dist/components/TicketConversation/hooks/useTranslation.js +70 -0
  20. package/dist/components/TicketConversation/index.js +1123 -0
  21. package/dist/components/TicketConversation/locales/en.json +878 -0
  22. package/dist/components/TicketConversation/locales/fr.json +878 -0
  23. package/dist/components/TicketConversation/types.js +2 -0
  24. package/dist/components/TicketConversation/utils.js +25 -0
  25. package/dist/index.cjs +2 -2
  26. package/dist/index.js +2 -2
  27. package/dist/styles/BillingView.module.scss +311 -0
  28. package/dist/styles/ChatView.module.scss +438 -0
  29. package/dist/styles/CommandPalette.module.scss +160 -0
  30. package/dist/styles/CrmView.module.scss +554 -0
  31. package/dist/styles/EmailTracking.module.scss +238 -0
  32. package/dist/styles/ImportConversation.module.scss +267 -0
  33. package/dist/styles/Layout.module.scss +55 -0
  34. package/dist/styles/Logs.module.scss +164 -0
  35. package/dist/styles/NewTicket.module.scss +143 -0
  36. package/dist/styles/PendingEmails.module.scss +629 -0
  37. package/dist/styles/SupportDashboard.module.scss +649 -0
  38. package/dist/styles/TicketDetail.module.scss +1050 -0
  39. package/dist/styles/TicketInbox.module.scss +296 -0
  40. package/dist/styles/TicketingSettings.module.scss +358 -0
  41. package/dist/styles/TimeDashboard.module.scss +287 -0
  42. package/dist/styles/_tokens.scss +78 -0
  43. package/dist/styles/theme.css +633 -0
  44. package/dist/views/BillingView/client.js +204 -0
  45. package/dist/views/BillingView/index.js +29 -0
  46. package/dist/views/ChatView/client.js +252 -0
  47. package/dist/views/ChatView/index.js +29 -0
  48. package/dist/views/CrmView/client.js +232 -0
  49. package/dist/views/CrmView/index.js +29 -0
  50. package/dist/views/EmailTrackingView/client.js +154 -0
  51. package/dist/views/EmailTrackingView/index.js +29 -0
  52. package/dist/views/ImportConversationView/client.js +204 -0
  53. package/dist/views/ImportConversationView/index.js +29 -0
  54. package/dist/views/LogsView/client.js +148 -0
  55. package/dist/views/LogsView/index.js +27 -0
  56. package/dist/views/NewTicketView/client.js +224 -0
  57. package/dist/views/NewTicketView/index.js +27 -0
  58. package/dist/views/PendingEmailsView/client.js +172 -0
  59. package/dist/views/PendingEmailsView/index.js +29 -0
  60. package/dist/views/SupportDashboardView/client.js +296 -0
  61. package/dist/views/SupportDashboardView/index.js +29 -0
  62. package/dist/views/TicketDetailView/client.js +844 -0
  63. package/dist/views/TicketDetailView/index.js +29 -0
  64. package/dist/views/TicketInboxView/client.js +294 -0
  65. package/dist/views/TicketInboxView/index.js +27 -0
  66. package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +350 -0
  67. package/dist/views/TicketingSettingsView/client.js +728 -0
  68. package/dist/views/TicketingSettingsView/index.js +29 -0
  69. package/dist/views/TimeDashboardView/client.js +164 -0
  70. package/dist/views/TimeDashboardView/index.js +29 -0
  71. package/dist/views/shared/AdminViewHeader.js +67 -0
  72. package/dist/views/shared/ErrorBoundary.js +50 -0
  73. package/dist/views/shared/Skeleton.js +72 -0
  74. package/dist/views/shared/adminTokens.js +32 -0
  75. package/dist/views/shared/config.js +41 -0
  76. package/dist/views/shared/index.js +6 -0
  77. package/package.json +9 -12
  78. package/src/collections/Tickets.ts +1 -1
  79. package/src/components/TicketConversation/index.tsx +21 -2
  80. package/src/plugin.ts +3 -2
  81. package/src/styles/TicketDetail.module.scss +8 -1
  82. package/src/views/TicketingSettingsView/TicketingSettings.module.scss +350 -0
  83. package/src/views/TicketingSettingsView/client.tsx +1 -1
  84. package/dist/views.d.cts +0 -30
  85. package/dist/views.d.ts +0 -30
  86. package/src/views.ts +0 -16
@@ -0,0 +1,29 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { DefaultTemplate } from '@payloadcms/next/templates';
3
+ import { redirect } from 'next/navigation';
4
+ import { AdminErrorBoundary } from '../shared/ErrorBoundary';
5
+ import { TicketDetailClient } from './client';
6
+
7
+ const TicketDetailView = ({ initPageResult }) => {
8
+ const { req, visibleEntities } = initPageResult;
9
+ if (!req.user) {
10
+ redirect("/admin/login");
11
+ }
12
+ return /* @__PURE__ */ jsx(
13
+ DefaultTemplate,
14
+ {
15
+ i18n: req.i18n,
16
+ locale: initPageResult.locale,
17
+ params: {},
18
+ payload: req.payload,
19
+ permissions: initPageResult.permissions,
20
+ searchParams: {},
21
+ user: req.user,
22
+ visibleEntities,
23
+ children: /* @__PURE__ */ jsx(AdminErrorBoundary, { viewName: "TicketDetailView", children: /* @__PURE__ */ jsx(TicketDetailClient, {}) })
24
+ }
25
+ );
26
+ };
27
+ var TicketDetailView_default = TicketDetailView;
28
+
29
+ export { TicketDetailView, TicketDetailView_default as default };
@@ -0,0 +1,294 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useState, useCallback, useEffect } from 'react';
4
+ import { useSearchParams } from 'next/navigation';
5
+ import Link from 'next/link';
6
+ import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation';
7
+
8
+ const STATUS_DOTS = {
9
+ open: "#22c55e",
10
+ waiting_client: "#eab308",
11
+ resolved: "#94a3b8"
12
+ };
13
+ const STATUS_LABEL_KEYS = {
14
+ open: "inbox.tabs.open",
15
+ waiting_client: "inbox.tabs.waiting",
16
+ resolved: "inbox.tabs.resolved"
17
+ };
18
+ const PRIORITY_COLORS = {
19
+ urgent: "#ef4444",
20
+ high: "#f97316",
21
+ normal: "transparent",
22
+ low: "transparent"
23
+ };
24
+ const CATEGORY_LABEL_KEYS = {
25
+ bug: "ticket.category.bug",
26
+ content: "ticket.category.content",
27
+ feature: "ticket.category.feature",
28
+ question: "ticket.category.question",
29
+ hosting: "ticket.category.hosting"
30
+ };
31
+ function relativeTime(dateStr) {
32
+ const diff = Date.now() - new Date(dateStr).getTime();
33
+ const mins = Math.floor(diff / 6e4);
34
+ if (mins < 1) return "maintenant";
35
+ if (mins < 60) return `${mins}m`;
36
+ const hours = Math.floor(mins / 60);
37
+ if (hours < 24) return `${hours}h`;
38
+ const days = Math.floor(hours / 24);
39
+ if (days < 7) return `${days}j`;
40
+ return new Date(dateStr).toLocaleDateString("fr-FR", { day: "numeric", month: "short" });
41
+ }
42
+ const TicketInboxClient = () => {
43
+ const { t } = useTranslation();
44
+ const searchParams = useSearchParams();
45
+ const [tickets, setTickets] = useState([]);
46
+ const [loading, setLoading] = useState(true);
47
+ const [tab, setTab] = useState(() => {
48
+ const urlTab = searchParams.get("tab");
49
+ if (urlTab && ["all", "open", "waiting_client", "resolved"].includes(urlTab)) return urlTab;
50
+ return "all";
51
+ });
52
+ const [search, setSearch] = useState("");
53
+ const [sort, setSort] = useState("-updatedAt");
54
+ const [counts, setCounts] = useState({ all: 0, open: 0, waiting: 0, resolved: 0 });
55
+ const [selectedIdx, setSelectedIdx] = useState(-1);
56
+ const [checkedIds, setCheckedIds] = useState(/* @__PURE__ */ new Set());
57
+ const [bulkAction, setBulkAction] = useState("");
58
+ const [bulkProcessing, setBulkProcessing] = useState(false);
59
+ const toggleCheck = (id) => {
60
+ setCheckedIds((prev) => {
61
+ const next = new Set(prev);
62
+ if (next.has(id)) next.delete(id);
63
+ else next.add(id);
64
+ return next;
65
+ });
66
+ };
67
+ const handleBulkAction = async (action) => {
68
+ if (checkedIds.size === 0) return;
69
+ setBulkProcessing(true);
70
+ try {
71
+ const res = await fetch("/api/support/bulk-action", {
72
+ method: "POST",
73
+ headers: { "Content-Type": "application/json" },
74
+ credentials: "include",
75
+ body: JSON.stringify({ ticketIds: Array.from(checkedIds), action })
76
+ });
77
+ if (res.ok) {
78
+ setCheckedIds(/* @__PURE__ */ new Set());
79
+ setBulkAction("");
80
+ fetchTickets();
81
+ }
82
+ } catch {
83
+ }
84
+ setBulkProcessing(false);
85
+ };
86
+ const fetchTickets = useCallback(async () => {
87
+ const params = [`limit=30`, `sort=${sort}`, `depth=1`, `select[id]=true`, `select[ticketNumber]=true`, `select[subject]=true`, `select[status]=true`, `select[priority]=true`, `select[category]=true`, `select[client]=true`, `select[updatedAt]=true`, `select[lastClientMessageAt]=true`, `select[lastAdminReadAt]=true`];
88
+ if (tab !== "all") params.push(`where[status][equals]=${tab}`);
89
+ if (search.trim()) {
90
+ params.push(`where[or][0][subject][contains]=${encodeURIComponent(search)}`);
91
+ params.push(`where[or][1][ticketNumber][contains]=${encodeURIComponent(search)}`);
92
+ }
93
+ try {
94
+ const url = `/api/tickets?${params.join("&")}`;
95
+ const res = await fetch(url, { credentials: "include" });
96
+ if (res.ok) {
97
+ const d = await res.json();
98
+ setTickets(d.docs || []);
99
+ } else {
100
+ console.error("[inbox] Fetch failed:", res.status, await res.text().catch(() => ""));
101
+ }
102
+ } catch (err) {
103
+ console.error("[inbox] Fetch error:", err);
104
+ }
105
+ setLoading(false);
106
+ }, [tab, sort, search]);
107
+ useEffect(() => {
108
+ setLoading(true);
109
+ fetchTickets();
110
+ }, [fetchTickets]);
111
+ useEffect(() => {
112
+ const fetchCounts = async () => {
113
+ try {
114
+ const [all, openRes, waiting, resolved] = await Promise.all([
115
+ fetch("/api/tickets?limit=0&depth=0", { credentials: "include" }),
116
+ fetch("/api/tickets?limit=0&depth=0&where[status][equals]=open", { credentials: "include" }),
117
+ fetch("/api/tickets?limit=0&depth=0&where[status][equals]=waiting_client", { credentials: "include" }),
118
+ fetch("/api/tickets?limit=0&depth=0&where[status][equals]=resolved", { credentials: "include" })
119
+ ]);
120
+ const [a, o, w, r] = await Promise.all([all.json(), openRes.json(), waiting.json(), resolved.json()]);
121
+ setCounts({ all: a.totalDocs || 0, open: o.totalDocs || 0, waiting: w.totalDocs || 0, resolved: r.totalDocs || 0 });
122
+ } catch {
123
+ }
124
+ };
125
+ fetchCounts();
126
+ }, []);
127
+ useEffect(() => {
128
+ const iv = setInterval(fetchTickets, 6e4);
129
+ return () => clearInterval(iv);
130
+ }, [fetchTickets]);
131
+ useEffect(() => {
132
+ const handleKey = (e) => {
133
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
134
+ if (e.key === "j" || e.key === "ArrowDown") {
135
+ e.preventDefault();
136
+ setSelectedIdx((p) => Math.min(p + 1, tickets.length - 1));
137
+ }
138
+ if (e.key === "k" || e.key === "ArrowUp") {
139
+ e.preventDefault();
140
+ setSelectedIdx((p) => Math.max(p - 1, 0));
141
+ }
142
+ if (e.key === "Enter" && selectedIdx >= 0 && tickets[selectedIdx]) {
143
+ window.location.href = `/admin/support/ticket?id=${tickets[selectedIdx].id}`;
144
+ }
145
+ };
146
+ window.addEventListener("keydown", handleKey);
147
+ return () => window.removeEventListener("keydown", handleKey);
148
+ }, [tickets, selectedIdx]);
149
+ const tabs = [
150
+ { key: "all", label: t("inbox.tabs.all"), count: counts.all },
151
+ { key: "open", label: t("inbox.tabs.open"), count: counts.open },
152
+ { key: "waiting_client", label: t("inbox.tabs.waiting"), count: counts.waiting },
153
+ { key: "resolved", label: t("inbox.tabs.resolved"), count: counts.resolved }
154
+ ];
155
+ const S = {
156
+ page: { padding: "20px 30px", maxWidth: 1100, margin: "0 auto" },
157
+ header: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 },
158
+ title: { fontSize: 24, fontWeight: 700, margin: 0, color: "var(--theme-text)" },
159
+ headerRight: { display: "flex", gap: 10, alignItems: "center" },
160
+ searchWrap: { position: "relative", display: "flex", alignItems: "center" },
161
+ searchInput: { padding: "6px 12px 6px 30px", borderRadius: 8, border: "1px solid var(--theme-elevation-200)", fontSize: 13, background: "var(--theme-elevation-0)", color: "var(--theme-text)", width: 200 },
162
+ newTicketBtn: { padding: "7px 14px", borderRadius: 8, background: "#2563eb", color: "#fff", fontSize: 13, fontWeight: 600, textDecoration: "none", whiteSpace: "nowrap" },
163
+ tabsRow: { display: "flex", gap: 4, marginBottom: 12, borderBottom: "1px solid var(--theme-elevation-200)", paddingBottom: 8 },
164
+ tab: { padding: "6px 12px", borderRadius: 6, border: "none", background: "none", cursor: "pointer", fontSize: 13, color: "var(--theme-elevation-500)", fontWeight: 500 },
165
+ tabActive: { background: "var(--theme-elevation-100)", color: "var(--theme-text)", fontWeight: 700 },
166
+ sortRow: { display: "flex", alignItems: "center", marginBottom: 12 },
167
+ sortSelect: { padding: "4px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, background: "var(--theme-elevation-0)", color: "var(--theme-text)", cursor: "pointer" },
168
+ loading: { padding: 40, textAlign: "center", color: "#94a3b8" },
169
+ empty: { padding: 60, textAlign: "center", color: "#94a3b8" },
170
+ list: { display: "flex", flexDirection: "column", gap: 1 },
171
+ row: { display: "grid", gridTemplateColumns: "20px 10px 60px 70px 1fr 140px 80px 4px 50px 10px", gap: 8, alignItems: "center", padding: "10px 12px", borderRadius: 8, textDecoration: "none", color: "var(--theme-text)", fontSize: 13, cursor: "pointer", transition: "background 100ms", background: "var(--theme-elevation-0)" },
172
+ statusDot: { width: 8, height: 8, borderRadius: "50%" },
173
+ priorityBar: { width: 4, height: 20, borderRadius: 2 },
174
+ unreadDot: { width: 8, height: 8, borderRadius: "50%", background: "#2563eb" },
175
+ keyboardHints: { display: "flex", gap: 12, marginTop: 16, fontSize: 11, color: "var(--theme-elevation-400)" }
176
+ };
177
+ return /* @__PURE__ */ jsxs("div", { style: S.page, children: [
178
+ /* @__PURE__ */ jsxs("div", { style: S.header, children: [
179
+ /* @__PURE__ */ jsx("h1", { style: S.title, children: t("inbox.title") }),
180
+ /* @__PURE__ */ jsxs("div", { style: S.headerRight, children: [
181
+ /* @__PURE__ */ jsx("div", { style: S.searchWrap, children: /* @__PURE__ */ jsx(
182
+ "input",
183
+ {
184
+ type: "text",
185
+ style: S.searchInput,
186
+ placeholder: t("inbox.searchPlaceholder"),
187
+ value: search,
188
+ onChange: (e) => setSearch(e.target.value)
189
+ }
190
+ ) }),
191
+ /* @__PURE__ */ jsx(Link, { href: "/admin/support/new-ticket", style: S.newTicketBtn, children: t("inbox.newTicketBtn") })
192
+ ] })
193
+ ] }),
194
+ /* @__PURE__ */ jsx("div", { style: S.tabsRow, children: tabs.map((tb) => /* @__PURE__ */ jsxs(
195
+ "button",
196
+ {
197
+ style: { ...S.tab, ...tab === tb.key ? S.tabActive : {} },
198
+ onClick: () => {
199
+ setTab(tb.key);
200
+ setSelectedIdx(-1);
201
+ },
202
+ children: [
203
+ tb.label,
204
+ " ",
205
+ /* @__PURE__ */ jsxs("span", { style: { opacity: 0.6 }, children: [
206
+ "(",
207
+ tb.count,
208
+ ")"
209
+ ] })
210
+ ]
211
+ },
212
+ tb.key
213
+ )) }),
214
+ /* @__PURE__ */ jsx("div", { style: S.sortRow, children: checkedIds.size > 0 ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", flex: 1 }, children: [
215
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "var(--theme-text)" }, children: checkedIds.size > 1 ? t("inbox.selectedPlural", { count: String(checkedIds.size) }) : t("inbox.selected", { count: String(checkedIds.size) }) }),
216
+ /* @__PURE__ */ jsx("button", { style: S.sortSelect, onClick: () => handleBulkAction("close"), disabled: bulkProcessing, children: t("inbox.closeAction") }),
217
+ /* @__PURE__ */ jsx("button", { style: S.sortSelect, onClick: () => handleBulkAction("reopen"), disabled: bulkProcessing, children: t("inbox.reopenAction") }),
218
+ /* @__PURE__ */ jsxs("select", { style: S.sortSelect, value: bulkAction, onChange: (e) => {
219
+ if (e.target.value) handleBulkAction(e.target.value);
220
+ setBulkAction("");
221
+ }, children: [
222
+ /* @__PURE__ */ jsx("option", { value: "", children: t("inbox.moreActions") }),
223
+ /* @__PURE__ */ jsx("option", { value: "set_priority", children: t("inbox.changePriority") }),
224
+ /* @__PURE__ */ jsx("option", { value: "delete", children: t("inbox.deleteAction") })
225
+ ] }),
226
+ /* @__PURE__ */ jsx("button", { style: { ...S.sortSelect, marginLeft: "auto" }, onClick: () => setCheckedIds(/* @__PURE__ */ new Set()), children: t("inbox.deselect") })
227
+ ] }) : /* @__PURE__ */ jsxs("select", { style: S.sortSelect, value: sort, onChange: (e) => setSort(e.target.value), children: [
228
+ /* @__PURE__ */ jsx("option", { value: "-updatedAt", children: t("inbox.sort.newest") }),
229
+ /* @__PURE__ */ jsx("option", { value: "updatedAt", children: t("inbox.sort.oldest") }),
230
+ /* @__PURE__ */ jsx("option", { value: "-createdAt", children: t("inbox.sort.created") }),
231
+ /* @__PURE__ */ jsx("option", { value: "priority", children: t("inbox.sort.priority") })
232
+ ] }) }),
233
+ loading ? /* @__PURE__ */ jsx("div", { style: S.loading, children: t("common.loading") }) : tickets.length === 0 ? /* @__PURE__ */ jsx("div", { style: S.empty, children: t("inbox.empty") }) : /* @__PURE__ */ jsx("div", { style: S.list, children: tickets.map((tk, idx) => {
234
+ const clientObj = typeof tk.client === "object" ? tk.client : null;
235
+ const clientName = clientObj ? `${clientObj.firstName || ""} ${clientObj.lastName || ""}`.trim() : "";
236
+ const clientCompany = clientObj?.company || "";
237
+ const displayClient = clientName ? `${clientName}${clientCompany ? `, ${clientCompany}` : ""}` : "--";
238
+ const isUnread = tk.lastClientMessageAt && (!tk.lastAdminReadAt || new Date(tk.lastClientMessageAt) > new Date(tk.lastAdminReadAt));
239
+ const priorityColor = PRIORITY_COLORS[tk.priority] || "transparent";
240
+ return /* @__PURE__ */ jsxs(
241
+ "a",
242
+ {
243
+ href: `/admin/support/ticket?id=${tk.id}`,
244
+ style: { ...S.row, ...idx === selectedIdx ? { background: "var(--theme-elevation-100)" } : {} },
245
+ onClick: (e) => {
246
+ e.preventDefault();
247
+ window.location.href = `/admin/support/ticket?id=${tk.id}`;
248
+ },
249
+ children: [
250
+ /* @__PURE__ */ jsx(
251
+ "input",
252
+ {
253
+ type: "checkbox",
254
+ checked: checkedIds.has(tk.id),
255
+ onClick: (e) => e.stopPropagation(),
256
+ onChange: () => toggleCheck(tk.id),
257
+ style: { width: 14, height: 14, cursor: "pointer", accentColor: "#2563eb" }
258
+ }
259
+ ),
260
+ /* @__PURE__ */ jsx("div", { style: { ...S.statusDot, backgroundColor: STATUS_DOTS[tk.status] || "#94a3b8" } }),
261
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: STATUS_LABEL_KEYS[tk.status] ? t(STATUS_LABEL_KEYS[tk.status]) : tk.status }),
262
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, fontSize: 12, color: "var(--theme-elevation-400)" }, children: tk.ticketNumber }),
263
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 500 }, children: tk.subject }),
264
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "var(--theme-elevation-500)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: displayClient }),
265
+ tk.category ? /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: "var(--theme-elevation-400)" }, children: [
266
+ "[",
267
+ CATEGORY_LABEL_KEYS[tk.category] ? t(CATEGORY_LABEL_KEYS[tk.category]) : tk.category,
268
+ "]"
269
+ ] }) : /* @__PURE__ */ jsx("span", {}),
270
+ /* @__PURE__ */ jsx("div", { style: { ...S.priorityBar, backgroundColor: priorityColor } }),
271
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "var(--theme-elevation-400)", textAlign: "right" }, children: relativeTime(tk.updatedAt) }),
272
+ isUnread ? /* @__PURE__ */ jsx("div", { style: S.unreadDot }) : /* @__PURE__ */ jsx("span", {})
273
+ ]
274
+ },
275
+ tk.id
276
+ );
277
+ }) }),
278
+ /* @__PURE__ */ jsxs("div", { style: S.keyboardHints, children: [
279
+ /* @__PURE__ */ jsxs("span", { children: [
280
+ /* @__PURE__ */ jsx("kbd", { children: "\u2191" }),
281
+ /* @__PURE__ */ jsx("kbd", { children: "\u2193" }),
282
+ " ",
283
+ t("inbox.keyboardNavigate")
284
+ ] }),
285
+ /* @__PURE__ */ jsxs("span", { children: [
286
+ /* @__PURE__ */ jsx("kbd", { children: "\u21B5" }),
287
+ " ",
288
+ t("inbox.keyboardOpen")
289
+ ] })
290
+ ] })
291
+ ] });
292
+ };
293
+
294
+ export { TicketInboxClient };
@@ -0,0 +1,27 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { DefaultTemplate } from '@payloadcms/next/templates';
3
+ import { redirect } from 'next/navigation';
4
+ import { AdminErrorBoundary } from '../shared/ErrorBoundary';
5
+ import { TicketInboxClient } from './client';
6
+
7
+ const TicketInboxView = ({ initPageResult }) => {
8
+ const { req, visibleEntities } = initPageResult;
9
+ if (!req.user) redirect("/admin/login");
10
+ return /* @__PURE__ */ jsx(
11
+ DefaultTemplate,
12
+ {
13
+ i18n: req.i18n,
14
+ locale: initPageResult.locale,
15
+ params: {},
16
+ payload: req.payload,
17
+ permissions: initPageResult.permissions,
18
+ searchParams: {},
19
+ user: req.user,
20
+ visibleEntities,
21
+ children: /* @__PURE__ */ jsx(AdminErrorBoundary, { viewName: "TicketInboxView", children: /* @__PURE__ */ jsx(TicketInboxClient, {}) })
22
+ }
23
+ );
24
+ };
25
+ var TicketInboxView_default = TicketInboxView;
26
+
27
+ export { TicketInboxView, TicketInboxView_default as default };
@@ -0,0 +1,350 @@
1
+ .page {
2
+ max-width: 900px;
3
+ margin: 0 auto;
4
+ padding: 0 1.5rem 3rem;
5
+ }
6
+
7
+ .intro {
8
+ font-size: 0.875rem;
9
+ color: var(--theme-elevation-600);
10
+ margin-bottom: 1.5rem;
11
+ line-height: 1.5;
12
+ }
13
+
14
+ // ── Sections ──────────────────────────────────
15
+
16
+ .sectionWrapper {
17
+ border: 1px solid var(--theme-elevation-150);
18
+ border-radius: 10px;
19
+ margin-bottom: 1rem;
20
+ overflow: hidden;
21
+ background: var(--theme-elevation-0);
22
+ }
23
+
24
+ .sectionHeader {
25
+ display: flex;
26
+ align-items: center;
27
+ gap: 0.75rem;
28
+ padding: 0.875rem 1.25rem;
29
+ cursor: pointer;
30
+ user-select: none;
31
+ transition: background 150ms;
32
+
33
+ &:hover {
34
+ background: var(--theme-elevation-50);
35
+ }
36
+ }
37
+
38
+ .sectionIcon {
39
+ width: 32px;
40
+ height: 32px;
41
+ border-radius: 8px;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ color: #fff;
46
+ flex-shrink: 0;
47
+ }
48
+
49
+ .sectionTitle {
50
+ font-weight: 600;
51
+ font-size: 0.9375rem;
52
+ color: var(--theme-text);
53
+ flex: 1;
54
+ }
55
+
56
+ .sectionChevron {
57
+ font-size: 0.625rem;
58
+ color: var(--theme-elevation-400);
59
+ transition: transform 200ms;
60
+
61
+ &.open {
62
+ transform: rotate(180deg);
63
+ }
64
+ }
65
+
66
+ .sectionBody {
67
+ padding: 1rem 1.25rem 1.25rem;
68
+ border-top: 1px solid var(--theme-elevation-100);
69
+ }
70
+
71
+ .sectionDescription {
72
+ font-size: 0.8125rem;
73
+ color: var(--theme-elevation-500);
74
+ margin: 0 0 1rem;
75
+ line-height: 1.5;
76
+ }
77
+
78
+ // ── Badges ────────────────────────────────────
79
+
80
+ .badge {
81
+ font-size: 0.6875rem;
82
+ font-weight: 600;
83
+ padding: 0.125rem 0.5rem;
84
+ border-radius: 9999px;
85
+ white-space: nowrap;
86
+ }
87
+
88
+ // ── Feature cards ─────────────────────────────
89
+
90
+ .categoryGroup {
91
+ margin-bottom: 1.25rem;
92
+ }
93
+
94
+ .categoryHeading {
95
+ font-size: 0.75rem;
96
+ font-weight: 700;
97
+ text-transform: uppercase;
98
+ letter-spacing: 0.05em;
99
+ margin: 0 0 0.5rem;
100
+ display: flex;
101
+ align-items: center;
102
+ gap: 0.5rem;
103
+ }
104
+
105
+ .categoryDot {
106
+ width: 8px;
107
+ height: 8px;
108
+ border-radius: 50%;
109
+ display: inline-block;
110
+ }
111
+
112
+ .featureList {
113
+ display: flex;
114
+ flex-direction: column;
115
+ gap: 0.375rem;
116
+ }
117
+
118
+ .featureCard {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 0.75rem;
122
+ padding: 0.75rem 1rem;
123
+ border-radius: 8px;
124
+ border: 1px solid var(--theme-elevation-150);
125
+ cursor: pointer;
126
+ transition: all 150ms;
127
+
128
+ &:hover {
129
+ background: var(--theme-elevation-50);
130
+ }
131
+
132
+ &.enabled {
133
+ border-color: var(--theme-elevation-200);
134
+ background: var(--theme-elevation-50);
135
+ }
136
+ }
137
+
138
+ .featureLabel {
139
+ font-weight: 600;
140
+ font-size: 0.8125rem;
141
+ color: var(--theme-text);
142
+ }
143
+
144
+ .featureDesc {
145
+ font-size: 0.75rem;
146
+ color: var(--theme-elevation-500);
147
+ margin-top: 0.125rem;
148
+ }
149
+
150
+ // ── Fields ────────────────────────────────────
151
+
152
+ .fieldRow {
153
+ display: flex;
154
+ align-items: flex-start;
155
+ gap: 1rem;
156
+ padding: 0.625rem 0;
157
+ }
158
+
159
+ .fieldLabel {
160
+ flex: 0 0 200px;
161
+ font-size: 0.8125rem;
162
+ font-weight: 600;
163
+ color: var(--theme-text);
164
+ padding-top: 0.5rem;
165
+ }
166
+
167
+ .fieldDescription {
168
+ font-size: 0.75rem;
169
+ color: var(--theme-elevation-500);
170
+ font-weight: 400;
171
+ margin-top: 0.125rem;
172
+ }
173
+
174
+ .fieldContent {
175
+ flex: 1;
176
+ }
177
+
178
+ .input {
179
+ width: 100%;
180
+ padding: 0.5rem 0.75rem;
181
+ border: 1px solid var(--theme-elevation-200);
182
+ border-radius: 6px;
183
+ font-size: 0.8125rem;
184
+ background: var(--theme-input-bg, var(--theme-elevation-0));
185
+ color: var(--theme-text);
186
+ outline: none;
187
+ transition: border-color 150ms;
188
+
189
+ &:focus {
190
+ border-color: var(--theme-success-500);
191
+ }
192
+ }
193
+
194
+ .inputReadonly {
195
+ composes: input;
196
+ background: var(--theme-elevation-50);
197
+ color: var(--theme-elevation-500);
198
+ cursor: not-allowed;
199
+ }
200
+
201
+ .select {
202
+ composes: input;
203
+ appearance: auto;
204
+ }
205
+
206
+ .numberInput {
207
+ composes: input;
208
+ max-width: 120px;
209
+ }
210
+
211
+ .separator {
212
+ border-top: 1px solid var(--theme-elevation-100);
213
+ margin: 1rem 0;
214
+ }
215
+
216
+ // ── Toggle rows ───────────────────────────────
217
+
218
+ .toggleRow {
219
+ display: flex;
220
+ align-items: flex-start;
221
+ gap: 0.75rem;
222
+ padding: 0.5rem 0;
223
+ }
224
+
225
+ .inlineLabel {
226
+ font-size: 0.8125rem;
227
+ font-weight: 600;
228
+ color: var(--theme-text);
229
+ }
230
+
231
+ .inlineDesc {
232
+ font-size: 0.75rem;
233
+ color: var(--theme-elevation-500);
234
+ margin-top: 0.125rem;
235
+ }
236
+
237
+ // ── SLA ───────────────────────────────────────
238
+
239
+ .slaInline {
240
+ display: flex;
241
+ align-items: center;
242
+ gap: 0.5rem;
243
+ }
244
+
245
+ .slaHint {
246
+ font-size: 0.75rem;
247
+ color: var(--theme-elevation-500);
248
+ }
249
+
250
+ // ── API Key ───────────────────────────────────
251
+
252
+ .apiKeyRow {
253
+ display: flex;
254
+ gap: 0.5rem;
255
+ }
256
+
257
+ .apiKeyToggle {
258
+ padding: 0.5rem 0.75rem;
259
+ border: 1px solid var(--theme-elevation-200);
260
+ border-radius: 6px;
261
+ background: var(--theme-elevation-50);
262
+ color: var(--theme-text);
263
+ font-size: 0.75rem;
264
+ cursor: pointer;
265
+ white-space: nowrap;
266
+ }
267
+
268
+ // ── AI sub features ───────────────────────────
269
+
270
+ .aiSubFeaturesLabel {
271
+ font-size: 0.8125rem;
272
+ font-weight: 600;
273
+ color: var(--theme-text);
274
+ margin: 0 0 0.5rem;
275
+ }
276
+
277
+ .aiToggleRow {
278
+ display: flex;
279
+ align-items: center;
280
+ gap: 0.75rem;
281
+ padding: 0.375rem 0;
282
+ }
283
+
284
+ .aiToggleLabel {
285
+ font-size: 0.8125rem;
286
+ font-weight: 500;
287
+ color: var(--theme-text);
288
+ }
289
+
290
+ .aiToggleDesc {
291
+ display: block;
292
+ font-size: 0.6875rem;
293
+ color: var(--theme-elevation-500);
294
+ }
295
+
296
+ // ── Signature ─────────────────────────────────
297
+
298
+ .signatureTextarea {
299
+ width: 100%;
300
+ padding: 0.75rem;
301
+ border: 1px solid var(--theme-elevation-200);
302
+ border-radius: 6px;
303
+ font-size: 0.8125rem;
304
+ font-family: inherit;
305
+ background: var(--theme-input-bg, var(--theme-elevation-0));
306
+ color: var(--theme-text);
307
+ resize: vertical;
308
+ outline: none;
309
+
310
+ &:focus {
311
+ border-color: var(--theme-success-500);
312
+ }
313
+ }
314
+
315
+ // ── Purge ─────────────────────────────────────
316
+
317
+ .purgeGroup {
318
+ display: flex;
319
+ flex-direction: column;
320
+ gap: 0.75rem;
321
+ }
322
+
323
+ .purgeCategory {
324
+ display: flex;
325
+ align-items: center;
326
+ gap: 1rem;
327
+ }
328
+
329
+ .purgeCategoryLabel {
330
+ font-size: 0.8125rem;
331
+ font-weight: 600;
332
+ color: var(--theme-text);
333
+ min-width: 100px;
334
+ }
335
+
336
+ .purgeButtons {
337
+ display: flex;
338
+ gap: 0.375rem;
339
+ }
340
+
341
+ // ── Bottom bar ────────────────────────────────
342
+
343
+ .bottomBar {
344
+ display: flex;
345
+ justify-content: flex-end;
346
+ gap: 0.5rem;
347
+ padding: 1.5rem 0;
348
+ border-top: 1px solid var(--theme-elevation-100);
349
+ margin-top: 1rem;
350
+ }