@consilioweb/payload-support 0.10.1 → 0.12.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 (39) hide show
  1. package/dist/components/TicketConversation/locales/en.json +25 -1
  2. package/dist/components/TicketConversation/locales/fr.json +25 -1
  3. package/dist/index.cjs +799 -4
  4. package/dist/index.d.cts +15 -1
  5. package/dist/index.d.ts +15 -1
  6. package/dist/index.js +800 -6
  7. package/dist/styles/TicketDetail.module.scss +899 -353
  8. package/dist/views/TicketDetailView/client.cjs +504 -282
  9. package/dist/views/TicketDetailView/client.js +505 -283
  10. package/package.json +1 -1
  11. package/src/collections/ClientSummaries.ts +16 -0
  12. package/src/collections/TicketCollaborators.ts +93 -0
  13. package/src/collections/TicketFeedback.ts +116 -0
  14. package/src/collections/TicketMessages.ts +9 -0
  15. package/src/collections/Tickets.ts +31 -1
  16. package/src/collections/index.ts +2 -0
  17. package/src/components/TicketConversation/locales/en.json +25 -1
  18. package/src/components/TicketConversation/locales/fr.json +25 -1
  19. package/src/endpoints/escalate.ts +44 -0
  20. package/src/endpoints/index.ts +15 -0
  21. package/src/endpoints/invite-collaborator.ts +215 -0
  22. package/src/endpoints/kb-search.ts +156 -0
  23. package/src/endpoints/ticket-feedback.ts +104 -0
  24. package/src/endpoints/transfer-ticket.ts +248 -0
  25. package/src/index.ts +1 -0
  26. package/src/plugin.ts +4 -0
  27. package/src/portal/auth/ChatWidget.tsx +11 -0
  28. package/src/portal/auth/dashboard/DashboardClient.tsx +85 -99
  29. package/src/portal/auth/dashboard/page.tsx +11 -2
  30. package/src/portal/auth/tickets/detail/TransferAndInviteActions.tsx +402 -0
  31. package/src/portal/auth/tickets/detail/page.tsx +122 -23
  32. package/src/portal/auth/tickets/new/KbDeflection.tsx +128 -0
  33. package/src/portal/auth/tickets/new/page.tsx +203 -100
  34. package/src/portal/locales/en.json +5 -0
  35. package/src/portal/locales/fr.json +5 -0
  36. package/src/styles/TicketDetail.module.scss +899 -353
  37. package/src/types.ts +19 -0
  38. package/src/utils/slugs.ts +2 -0
  39. package/src/views/TicketDetailView/client.tsx +404 -121
@@ -18,6 +18,10 @@ var React__default = /*#__PURE__*/_interopDefault(React);
18
18
  var Link__default = /*#__PURE__*/_interopDefault(Link);
19
19
  var s__default = /*#__PURE__*/_interopDefault(s);
20
20
 
21
+ const ALIASES = [
22
+ { value: "Support Consilioweb", label: "En tant que : Support Consilioweb" },
23
+ { value: "contact@consilioweb.fr", label: "En tant que : contact@consilioweb.fr" }
24
+ ];
21
25
  const STATUS_STYLE = {
22
26
  open: { bg: "#dbeafe", color: "#1e40af" },
23
27
  waiting_client: { bg: "#fef3c7", color: "#92400e" },
@@ -119,6 +123,34 @@ const RewriteDropdown = ({ disabled, loading, onSelect, toolbarBtnClass }) => {
119
123
  )) })
120
124
  ] });
121
125
  };
126
+ function NextActionItem({ action, index, onToggle }) {
127
+ const [pending, setPending] = React.useState(false);
128
+ const labelMap = { now: "Maintenant", today: "Aujourd'hui", "this-week": "Cette semaine" };
129
+ const done = !!action.done;
130
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: s__default.default.aiCardNextAction, children: [
131
+ /* @__PURE__ */ jsxRuntime.jsx(
132
+ "button",
133
+ {
134
+ type: "button",
135
+ className: `${s__default.default.aiCardNextCheck} ${done ? s__default.default.aiCardNextCheckDone : ""}`,
136
+ onClick: async () => {
137
+ setPending(true);
138
+ try {
139
+ await onToggle(!done);
140
+ } finally {
141
+ setPending(false);
142
+ }
143
+ },
144
+ "aria-pressed": done,
145
+ "aria-label": done ? "Marquer non fait" : "Marquer fait",
146
+ disabled: pending,
147
+ children: done && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" })
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${s__default.default.aiCardNextLabel} ${done ? s__default.default.aiCardNextLabelDone : ""}`, children: action.label }),
151
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardNextWhen, children: labelMap[action.priority] || action.priority })
152
+ ] });
153
+ }
122
154
  const TicketDetailClient = () => {
123
155
  const { t } = useTranslation.useTranslation();
124
156
  const searchParams = navigation.useSearchParams();
@@ -140,6 +172,7 @@ const TicketDetailClient = () => {
140
172
  const [isInternal, setIsInternal] = React.useState(false);
141
173
  const [notifyClient, setNotifyClient] = React.useState(true);
142
174
  const [sendAsClient, setSendAsClient] = React.useState(false);
175
+ const [fromAlias, setFromAlias] = React.useState("");
143
176
  const [editingMsgId, setEditingMsgId] = React.useState(null);
144
177
  const [editingBody, setEditingBody] = React.useState("");
145
178
  const [editingHtml, setEditingHtml] = React.useState("");
@@ -149,10 +182,20 @@ const TicketDetailClient = () => {
149
182
  const [showMenu, setShowMenu] = React.useState(false);
150
183
  const [clientTyping, setClientTyping] = React.useState(false);
151
184
  const [aiReplying, setAiReplying] = React.useState(false);
185
+ const [aiSuggestion, setAiSuggestion] = React.useState(null);
152
186
  const [aiRewriting, setAiRewriting] = React.useState(false);
153
187
  const [sentiment, setSentiment] = React.useState(null);
154
188
  const [statusUpdating, setStatusUpdating] = React.useState(false);
155
- const [showActivity, setShowActivity] = React.useState(false);
189
+ const [sidebarTab, setSidebarTab] = React.useState(() => {
190
+ if (typeof window === "undefined") return "overview";
191
+ const v = localStorage.getItem("support_sidebar_tab");
192
+ return v === "client" || v === "activity" ? v : "overview";
193
+ });
194
+ React.useEffect(() => {
195
+ if (typeof window === "undefined") return;
196
+ localStorage.setItem("support_sidebar_tab", sidebarTab);
197
+ }, [sidebarTab]);
198
+ const [previousTickets, setPreviousTickets] = React.useState([]);
156
199
  const [clientSummary, setClientSummary] = React.useState(null);
157
200
  const [summaryLoading, setSummaryLoading] = React.useState(false);
158
201
  const [timerRunning, setTimerRunning] = React.useState(() => {
@@ -229,6 +272,22 @@ const TicketDetailClient = () => {
229
272
  }).catch(() => {
230
273
  }).finally(() => setSummaryLoading(false));
231
274
  }, [ticket?.id]);
275
+ React.useEffect(() => {
276
+ if (sidebarTab !== "client" || !ticket || !ticketId) return;
277
+ const clientId = typeof ticket.client === "object" ? ticket.client?.id : ticket.client;
278
+ if (!clientId) return;
279
+ let aborted = false;
280
+ fetch(
281
+ `/api/tickets?where[client][equals]=${clientId}&where[id][not_equals]=${ticketId}&limit=5&sort=-createdAt&depth=0`,
282
+ { credentials: "include" }
283
+ ).then((r) => r.ok ? r.json() : null).then((d) => {
284
+ if (!aborted && d?.docs) setPreviousTickets(d.docs);
285
+ }).catch(() => {
286
+ });
287
+ return () => {
288
+ aborted = true;
289
+ };
290
+ }, [sidebarTab, ticket?.id, ticketId]);
232
291
  React.useEffect(() => {
233
292
  if (!ticketId || loading) return;
234
293
  const iv = setInterval(async () => {
@@ -432,6 +491,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
432
491
  ...finalHtml ? { bodyHtml: finalHtml } : {},
433
492
  authorType: sendAsClient ? "client" : "admin",
434
493
  ...sendAsClient && client ? { authorClient: client.id } : {},
494
+ ...fromAlias && !sendAsClient ? { fromAlias } : {},
435
495
  isInternal: sendAsClient ? false : isInternal,
436
496
  skipNotification: sendAsClient || isInternal || !notifyClient
437
497
  })
@@ -441,6 +501,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
441
501
  setReplyHtml("");
442
502
  setIsInternal(false);
443
503
  setSendAsClient(false);
504
+ setFromAlias("");
444
505
  setPendingFiles([]);
445
506
  editorRef.current?.clear();
446
507
  fetchAll();
@@ -535,21 +596,29 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
535
596
  method: "POST",
536
597
  headers: { "Content-Type": "application/json" },
537
598
  credentials: "include",
538
- body: JSON.stringify({ action: "suggest_reply", messages: messages.slice(-10).map((m) => ({ authorType: m.authorType, body: m.body })), clientName: `${client?.firstName || ""} ${client?.lastName || ""}`.trim(), clientCompany: client?.company })
599
+ body: JSON.stringify({
600
+ action: "suggest_reply",
601
+ messages: messages.slice(-10).map((m) => ({ authorType: m.authorType, body: m.body })),
602
+ clientName: `${client?.firstName || ""} ${client?.lastName || ""}`.trim(),
603
+ clientCompany: client?.company
604
+ })
539
605
  });
540
606
  if (r.ok) {
541
607
  const d = await r.json();
542
- if (d.reply) {
543
- setReplyBody(d.reply);
544
- setReplyHtml(d.reply.replace(/\n/g, "<br/>"));
545
- editorRef.current?.setContent(d.reply.replace(/\n/g, "<br/>"));
546
- }
608
+ if (d.reply) setAiSuggestion(d.reply);
547
609
  }
548
610
  } catch {
549
611
  } finally {
550
612
  setAiReplying(false);
551
613
  }
552
614
  };
615
+ const handleAiSuggestionInsert = () => {
616
+ if (!aiSuggestion) return;
617
+ setReplyBody(aiSuggestion);
618
+ setReplyHtml(aiSuggestion.replace(/\n/g, "<br/>"));
619
+ editorRef.current?.setContent(aiSuggestion.replace(/\n/g, "<br/>"));
620
+ setAiSuggestion(null);
621
+ };
553
622
  const handleAiRewrite = async (style = "auto") => {
554
623
  if (!replyBody.trim()) return;
555
624
  setAiRewriting(true);
@@ -643,8 +712,22 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
643
712
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.topBar, children: [
644
713
  /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: "/admin/support/inbox", className: s__default.default.backLink, "aria-label": "Retour \xE0 la bo\xEEte de r\xE9ception", children: "\u2190" }),
645
714
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.ticketMeta, children: [
646
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketNumber, children: ticket.ticketNumber }),
647
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketSubject, children: ticket.subject })
715
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: s__default.default.ticketNumber, children: [
716
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: ticket.ticketNumber }),
717
+ ticket.category ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
718
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketNumberSep, children: "\xB7" }),
719
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketNumberCat, children: ticket.category })
720
+ ] }) : null,
721
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.ticketNumberSep, children: "\xB7" }),
722
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: s__default.default.ticketNumberSource, "aria-label": t("detail.source"), children: [
723
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
724
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
725
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 7 9 6 9-6" })
726
+ ] }),
727
+ ticket.source === "email" ? "Email" : ticket.source || t("ticket.source.portal")
728
+ ] })
729
+ ] }),
730
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: s__default.default.ticketSubject, children: ticket.subject })
648
731
  ] }),
649
732
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.topBarRight, children: [
650
733
  /* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.statusChip, style: { background: st.bg, color: st.color }, value: ticket.status || "open", onChange: (e) => handleStatusChange(e.target.value), disabled: statusUpdating, "aria-label": t("ticket.status.label"), children: [
@@ -749,7 +832,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
749
832
  ),
750
833
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.messageContent, children: [
751
834
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.messageHeader, children: [
752
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.messageAuthor, children: isAdmin ? "Support" : msg.authorType === "email" ? "Email" : client?.firstName || "Client" }),
835
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.messageAuthor, children: msg.fromAlias || (isAdmin ? "Support" : msg.authorType === "email" ? "Email" : client?.firstName || "Client") }),
753
836
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.messageTime, children: timeAgo(msg.createdAt) }),
754
837
  msg.isInternal && /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.badge, style: { background: "#fef3c7", color: "#92400e" }, children: "Interne" }),
755
838
  msg.isSolution && /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.badge, style: { background: "#dcfce7", color: "#166534" }, children: "Solution" }),
@@ -835,6 +918,27 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
835
918
  ] }),
836
919
  t("detail.typing", { name: client?.firstName || "Client" })
837
920
  ] }),
921
+ aiSuggestion && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.aiSuggestionPreview, role: "region", "aria-label": t("composer.aiSuggestion.label"), children: [
922
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.aiSuggestionPreviewHeader, children: [
923
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiSuggestionIcon, "aria-hidden": true, children: "\u2728" }),
924
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiSuggestionLabel, children: t("composer.aiSuggestion.label") }),
925
+ /* @__PURE__ */ jsxRuntime.jsx(
926
+ "button",
927
+ {
928
+ type: "button",
929
+ className: s__default.default.aiSuggestionDismiss,
930
+ onClick: () => setAiSuggestion(null),
931
+ "aria-label": t("composer.aiSuggestion.ignore"),
932
+ children: "\xD7"
933
+ }
934
+ )
935
+ ] }),
936
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.aiSuggestionBody, children: aiSuggestion }),
937
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.aiSuggestionActions, children: [
938
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: s__default.default.aiSuggestionIgnore, onClick: () => setAiSuggestion(null), children: t("composer.aiSuggestion.ignore") }),
939
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: s__default.default.aiSuggestionInsert, onClick: handleAiSuggestionInsert, children: t("composer.aiSuggestion.insert") })
940
+ ] })
941
+ ] }),
838
942
  /* @__PURE__ */ jsxRuntime.jsxs(
839
943
  "div",
840
944
  {
@@ -856,7 +960,7 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
856
960
  "aria-label": t("detail.iaSuggestion"),
857
961
  onClick: handleAiSuggest,
858
962
  disabled: aiReplying || messages.length === 0,
859
- children: aiReplying ? "\u2026" : `\u2728 ${t("detail.iaSuggestion")}`
963
+ children: aiReplying ? `\u23F3 ${t("composer.aiSuggestion.loading")}` : `\u2728 ${t("detail.iaSuggestion")}`
860
964
  }
861
965
  ),
862
966
  /* @__PURE__ */ jsxRuntime.jsx(RewriteDropdown, { disabled: aiRewriting || !replyBody.trim(), loading: aiRewriting, onSelect: (style) => handleAiRewrite(style), toolbarBtnClass: s__default.default.toolbarBtn })
@@ -914,39 +1018,41 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
914
1018
  cannedResponses.map((cr) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(cr.id), children: cr.title }, cr.id))
915
1019
  ] })
916
1020
  ] }),
917
- /* @__PURE__ */ jsxRuntime.jsx(
918
- index.RichTextEditor,
919
- {
920
- ref: editorRef,
921
- onChange: (html, text) => {
922
- setReplyHtml(html);
923
- setReplyBody(text);
924
- },
925
- placeholder: isInternal ? t("composer.placeholderInternal") : t("composer.placeholderReplyTo", { name: client?.firstName || "client" }),
926
- minHeight: 100,
927
- borderColor: "transparent",
928
- onFileUpload: async (file) => {
929
- try {
930
- const formData = new FormData();
931
- formData.append("file", file);
932
- formData.append("_payload", JSON.stringify({ alt: file.name }));
933
- const ur = await fetch("/api/media", { method: "POST", credentials: "include", body: formData });
934
- if (!ur.ok) return null;
935
- const ud = await ur.json();
936
- return ud.doc?.url || null;
937
- } catch {
938
- return null;
1021
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.composerBox, children: [
1022
+ /* @__PURE__ */ jsxRuntime.jsx(
1023
+ index.RichTextEditor,
1024
+ {
1025
+ ref: editorRef,
1026
+ onChange: (html, text) => {
1027
+ setReplyHtml(html);
1028
+ setReplyBody(text);
1029
+ },
1030
+ placeholder: isInternal ? t("composer.placeholderInternal") : t("composer.placeholderReplyTo", { name: client?.firstName || "client" }),
1031
+ minHeight: 100,
1032
+ borderColor: "transparent",
1033
+ onFileUpload: async (file) => {
1034
+ try {
1035
+ const formData = new FormData();
1036
+ formData.append("file", file);
1037
+ formData.append("_payload", JSON.stringify({ alt: file.name }));
1038
+ const ur = await fetch("/api/media", { method: "POST", credentials: "include", body: formData });
1039
+ if (!ur.ok) return null;
1040
+ const ud = await ur.json();
1041
+ return ud.doc?.url || null;
1042
+ } catch {
1043
+ return null;
1044
+ }
939
1045
  }
940
1046
  }
941
- }
942
- ),
943
- pendingFiles.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.uploadPreview, children: pendingFiles.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.uploadPreviewItem, children: [
944
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
945
- "PJ ",
946
- f.name
947
- ] }),
948
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.uploadRemoveBtn, "aria-label": `Retirer ${f.name}`, onClick: () => setPendingFiles((prev) => prev.filter((_, j) => j !== i)), children: "\xD7" })
949
- ] }, i)) }),
1047
+ ),
1048
+ pendingFiles.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.uploadPreview, children: pendingFiles.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.uploadPreviewItem, children: [
1049
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1050
+ "PJ ",
1051
+ f.name
1052
+ ] }),
1053
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.uploadRemoveBtn, "aria-label": `Retirer ${f.name}`, onClick: () => setPendingFiles((prev) => prev.filter((_, j) => j !== i)), children: "\xD7" })
1054
+ ] }, i)) })
1055
+ ] }),
950
1056
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.composerFooter, children: [
951
1057
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.composerOptions, children: [
952
1058
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -968,6 +1074,20 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
968
1074
  ]
969
1075
  }
970
1076
  ),
1077
+ !sendAsClient && /* @__PURE__ */ jsxRuntime.jsxs(
1078
+ "select",
1079
+ {
1080
+ className: s__default.default.composerAliasSelect,
1081
+ value: fromAlias,
1082
+ onChange: (e) => setFromAlias(e.target.value),
1083
+ "aria-label": t("composer.aliases.label"),
1084
+ title: t("composer.aliases.tooltip"),
1085
+ children: [
1086
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("composer.aliases.self") }),
1087
+ ALIASES.map((a) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: a.value, children: a.label }, a.value))
1088
+ ]
1089
+ }
1090
+ ),
971
1091
  !sendAsClient && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
972
1092
  /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
973
1093
  /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked: isInternal, onChange: (e) => setIsInternal(e.target.checked) }),
@@ -992,294 +1112,396 @@ ${uploadedLinks.join("\n")}` : replyBody.trim() || "[Contenu enrichi]";
992
1112
  )
993
1113
  ] }),
994
1114
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sidebar, children: [
995
- client && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
996
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientCard, children: [
997
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.clientAvatar, children: initials }),
998
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientInfo, children: [
999
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientName, children: [
1000
- client.firstName,
1001
- " ",
1002
- client.lastName
1003
- ] }),
1004
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.clientCompany, children: client.company }),
1005
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${client.email}`, className: s__default.default.clientEmail, children: client.email })
1006
- ] })
1007
- ] }),
1008
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientActions, children: [
1009
- /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: `/admin/collections/support-clients/${client.id}`, className: s__default.default.smallBtn, children: t("client.clientSheet") }),
1010
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.smallBtn, onClick: () => window.open(`/api/admin/impersonate?clientId=${client.id}`, "_blank"), children: t("client.clientPortal") })
1011
- ] })
1012
- ] }),
1013
- clientSummary && clientSummary.summary && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${s__default.default.sideSection} ${s__default.default.aiCard}`, children: [
1014
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.aiCardTitle, children: [
1015
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardIcon, "aria-hidden": true, children: "\u2728" }),
1016
- "Synth\xE8se client"
1017
- ] }),
1018
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: s__default.default.aiCardLead, children: clientSummary.summary }),
1019
- clientSummary.recurringTopics && clientSummary.recurringTopics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.aiCardChips, children: clientSummary.recurringTopics.slice(0, 5).map((tp, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiChip, children: tp.topic }, `tp-${i}`)) }),
1020
- clientSummary.keyFacts && clientSummary.keyFacts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: s__default.default.aiCardFacts, children: clientSummary.keyFacts.slice(0, 4).map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: s__default.default.aiCardFact, children: [
1021
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardDot, "aria-hidden": true }),
1022
- f
1023
- ] }, `kf-${i}`)) })
1115
+ /* @__PURE__ */ jsxRuntime.jsxs("nav", { role: "tablist", "aria-label": t("detail.sidebar.tabs.overview"), className: s__default.default.sidebarTabs, children: [
1116
+ /* @__PURE__ */ jsxRuntime.jsx(
1117
+ "button",
1118
+ {
1119
+ type: "button",
1120
+ role: "tab",
1121
+ id: "sidebar-tab-overview",
1122
+ "aria-selected": sidebarTab === "overview",
1123
+ "aria-controls": "sidebar-panel-overview",
1124
+ tabIndex: sidebarTab === "overview" ? 0 : -1,
1125
+ className: `${s__default.default.sidebarTab} ${sidebarTab === "overview" ? s__default.default.sidebarTabActive : ""}`,
1126
+ onClick: () => setSidebarTab("overview"),
1127
+ children: t("detail.sidebar.tabs.overview")
1128
+ }
1129
+ ),
1130
+ /* @__PURE__ */ jsxRuntime.jsx(
1131
+ "button",
1132
+ {
1133
+ type: "button",
1134
+ role: "tab",
1135
+ id: "sidebar-tab-client",
1136
+ "aria-selected": sidebarTab === "client",
1137
+ "aria-controls": "sidebar-panel-client",
1138
+ tabIndex: sidebarTab === "client" ? 0 : -1,
1139
+ className: `${s__default.default.sidebarTab} ${sidebarTab === "client" ? s__default.default.sidebarTabActive : ""}`,
1140
+ onClick: () => setSidebarTab("client"),
1141
+ children: t("detail.sidebar.tabs.client")
1142
+ }
1143
+ ),
1144
+ /* @__PURE__ */ jsxRuntime.jsx(
1145
+ "button",
1146
+ {
1147
+ type: "button",
1148
+ role: "tab",
1149
+ id: "sidebar-tab-activity",
1150
+ "aria-selected": sidebarTab === "activity",
1151
+ "aria-controls": "sidebar-panel-activity",
1152
+ tabIndex: sidebarTab === "activity" ? 0 : -1,
1153
+ className: `${s__default.default.sidebarTab} ${sidebarTab === "activity" ? s__default.default.sidebarTabActive : ""}`,
1154
+ onClick: () => setSidebarTab("activity"),
1155
+ children: t("detail.sidebar.tabs.activity")
1156
+ }
1157
+ )
1024
1158
  ] }),
1025
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1026
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: t("detail.details") }),
1027
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1028
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.priority") }),
1029
- /* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.sideSelect, value: ticket.priority || "normal", onChange: (e) => handleFieldPatch("priority", e.target.value), "aria-label": t("detail.priority"), children: [
1030
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "low", children: t("ticket.priority.low") }),
1031
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "normal", children: t("ticket.priority.normal") }),
1032
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "high", children: t("ticket.priority.high") }),
1033
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "urgent", children: t("ticket.priority.urgent") })
1159
+ sidebarTab === "overview" && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "tabpanel", id: "sidebar-panel-overview", "aria-labelledby": "sidebar-tab-overview", children: [
1160
+ client && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1161
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientCard, children: [
1162
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.clientAvatar, children: initials }),
1163
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientInfo, children: [
1164
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientName, children: [
1165
+ client.firstName,
1166
+ " ",
1167
+ client.lastName
1168
+ ] }),
1169
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.clientCompany, children: client.company }),
1170
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${client.email}`, className: s__default.default.clientEmail, children: client.email })
1171
+ ] })
1172
+ ] }),
1173
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientActions, children: [
1174
+ /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: `/admin/collections/support-clients/${client.id}`, className: s__default.default.smallBtn, children: t("client.clientSheet") }),
1175
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.smallBtn, onClick: () => window.open(`/api/admin/impersonate?clientId=${client.id}`, "_blank"), children: t("client.clientPortal") })
1034
1176
  ] })
1035
1177
  ] }),
1036
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1037
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.category") }),
1038
- /* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.sideSelect, value: ticket.category || "", onChange: (e) => handleFieldPatch("category", e.target.value), "aria-label": t("detail.category"), children: [
1039
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014" }),
1040
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "bug", children: t("ticket.category.bug") }),
1041
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "content", children: t("ticket.category.content") }),
1042
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "feature", children: t("ticket.category.feature") }),
1043
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "question", children: t("ticket.category.question") }),
1044
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "hosting", children: t("ticket.category.hosting") })
1178
+ clientSummary && clientSummary.summary && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${s__default.default.sideSection} ${s__default.default.aiCard}`, children: [
1179
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.aiCardTitle, children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardIcon, "aria-hidden": true, children: "\u2728" }),
1181
+ "Synth\xE8se client"
1182
+ ] }),
1183
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: s__default.default.aiCardLead, children: clientSummary.summary }),
1184
+ clientSummary.recurringTopics && clientSummary.recurringTopics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.aiCardChips, children: clientSummary.recurringTopics.slice(0, 5).map((tp, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiChip, children: tp.topic }, `tp-${i}`)) }),
1185
+ clientSummary.keyFacts && clientSummary.keyFacts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: s__default.default.aiCardFacts, children: clientSummary.keyFacts.slice(0, 4).map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: s__default.default.aiCardFact, children: [
1186
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.aiCardDot, "aria-hidden": true }),
1187
+ f
1188
+ ] }, `kf-${i}`)) }),
1189
+ clientSummary?.nextActions && clientSummary.nextActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1190
+ /* @__PURE__ */ jsxRuntime.jsx("h5", { className: s__default.default.aiCardNextTitle, children: t("detail.aiCard.nextActions.title") }),
1191
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: s__default.default.aiCardNextActions, children: clientSummary.nextActions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsx(
1192
+ NextActionItem,
1193
+ {
1194
+ action,
1195
+ index: i,
1196
+ onToggle: async (done) => {
1197
+ const updatedActions = (clientSummary.nextActions || []).map(
1198
+ (a, j) => j === i ? { ...a, done } : a
1199
+ );
1200
+ if (clientSummary.id != null) {
1201
+ try {
1202
+ await fetch(`/api/client-summaries/${clientSummary.id}`, {
1203
+ method: "PATCH",
1204
+ credentials: "include",
1205
+ headers: { "Content-Type": "application/json" },
1206
+ body: JSON.stringify({ nextActions: updatedActions })
1207
+ });
1208
+ } catch {
1209
+ }
1210
+ }
1211
+ setClientSummary({ ...clientSummary, nextActions: updatedActions });
1212
+ }
1213
+ },
1214
+ action.id || `na-${i}`
1215
+ )) })
1045
1216
  ] })
1046
1217
  ] }),
1047
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1048
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.source") }),
1049
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideValue, children: ticket.source || t("ticket.source.portal") })
1218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1219
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: t("detail.details") }),
1220
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1221
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.priority") }),
1222
+ /* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.sideSelect, value: ticket.priority || "normal", onChange: (e) => handleFieldPatch("priority", e.target.value), "aria-label": t("detail.priority"), children: [
1223
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "low", children: t("ticket.priority.low") }),
1224
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "normal", children: t("ticket.priority.normal") }),
1225
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "high", children: t("ticket.priority.high") }),
1226
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "urgent", children: t("ticket.priority.urgent") })
1227
+ ] })
1228
+ ] }),
1229
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1230
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.category") }),
1231
+ /* @__PURE__ */ jsxRuntime.jsxs("select", { className: s__default.default.sideSelect, value: ticket.category || "", onChange: (e) => handleFieldPatch("category", e.target.value), "aria-label": t("detail.category"), children: [
1232
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014" }),
1233
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "bug", children: t("ticket.category.bug") }),
1234
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "content", children: t("ticket.category.content") }),
1235
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "feature", children: t("ticket.category.feature") }),
1236
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "question", children: t("ticket.category.question") }),
1237
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "hosting", children: t("ticket.category.hosting") })
1238
+ ] })
1239
+ ] }),
1240
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1241
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.source") }),
1242
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideValue, children: ticket.source || t("ticket.source.portal") })
1243
+ ] }),
1244
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1245
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.assigned") }),
1246
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideValue, children: typeof ticket.assignedTo === "object" && ticket.assignedTo ? ticket.assignedTo.firstName || "Admin" : "\u2014" })
1247
+ ] })
1050
1248
  ] }),
1051
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideField, children: [
1052
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideLabel, children: t("detail.assigned") }),
1053
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.sideValue, children: typeof ticket.assignedTo === "object" && ticket.assignedTo ? ticket.assignedTo.firstName || "Admin" : "\u2014" })
1054
- ] })
1055
- ] }),
1056
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1057
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: t("detail.tags") }),
1058
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.tagsWrap, children: [
1059
- tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: s__default.default.tagChip, children: [
1060
- tag,
1061
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.tagRemove, "aria-label": `Retirer le tag ${tag}`, onClick: () => handleRemoveTag(tag), children: "\xD7" })
1062
- ] }, tag)),
1063
- addingTag ? /* @__PURE__ */ jsxRuntime.jsx(
1064
- "input",
1065
- {
1066
- className: s__default.default.tagInput,
1067
- value: newTagValue,
1068
- onChange: (e) => setNewTagValue(e.target.value),
1069
- onKeyDown: (e) => {
1070
- if (e.key === "Enter") handleAddTag();
1071
- if (e.key === "Escape") {
1072
- setAddingTag(false);
1073
- setNewTagValue("");
1249
+ features.timeTracking && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1250
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSectionTitle, children: [
1251
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("detail.time") }),
1252
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--theme-text)", fontWeight: 600, textTransform: "none", letterSpacing: 0 }, children: totalMin > 0 ? `${Math.floor(totalMin / 60)}h${String(totalMin % 60).padStart(2, "0")} ${t("detail.total")}` : "0min" })
1253
+ ] }),
1254
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.timer, children: [
1255
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${s__default.default.timerDisplay} ${timerRunning ? s__default.default.timerActive : ""}`, children: [
1256
+ String(Math.floor(timerSeconds / 60)).padStart(2, "0"),
1257
+ ":",
1258
+ String(timerSeconds % 60).padStart(2, "0")
1259
+ ] }),
1260
+ !timerRunning ? /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.timerBtn, onClick: () => setTimerRunning(true), "aria-label": "D\xE9marrer le timer", children: timerSeconds > 0 ? "\u25B6" : "\u25B6 Go" }) : /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.timerBtn, onClick: () => setTimerRunning(false), "aria-label": "Mettre en pause le timer", children: "\u23F8 Pause" }),
1261
+ timerSeconds >= 60 && !timerRunning && /* @__PURE__ */ jsxRuntime.jsxs("button", { className: s__default.default.timerBtn, onClick: () => {
1262
+ handleTimerSave();
1263
+ localStorage.removeItem(`timer-sec-${ticketId}`);
1264
+ localStorage.removeItem(`timer-run-${ticketId}`);
1265
+ }, "aria-label": "Sauvegarder le temps", children: [
1266
+ "\u{1F4BE} ",
1267
+ Math.round(timerSeconds / 60),
1268
+ "m"
1269
+ ] })
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.timerBar, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.timerBarFill, style: { width: `${Math.min(100, (totalMin + Math.floor(timerSeconds / 60)) / 120 * 100)}%` } }) }),
1272
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, marginTop: 8, alignItems: "center" }, children: [
1273
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "number", min: "1", placeholder: "min", style: { width: 60, padding: "4px 8px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, id: "manual-time-input" }),
1274
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.timerBtn, onClick: async () => {
1275
+ const input = document.getElementById("manual-time-input");
1276
+ const mins = Number(input?.value);
1277
+ if (!mins || mins < 1 || !ticketId) return;
1278
+ try {
1279
+ await fetch("/api/time-entries", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ ticket: Number(ticketId), duration: mins, date: (/* @__PURE__ */ new Date()).toISOString(), description: "Saisie manuelle" }) });
1280
+ if (input) input.value = "";
1281
+ fetchAll();
1282
+ } catch {
1283
+ }
1284
+ }, style: { fontSize: 11 }, children: "+ Ajouter" })
1285
+ ] }),
1286
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8, fontSize: 11, color: "var(--theme-elevation-500)" }, children: [
1287
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "2px 0", alignItems: "center" }, children: [
1288
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Facturable" }),
1289
+ /* @__PURE__ */ jsxRuntime.jsx(
1290
+ "button",
1291
+ {
1292
+ onClick: async () => {
1293
+ const newVal = ticket.billable === false ? true : false;
1294
+ try {
1295
+ await fetch(`/api/tickets/${ticketId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ billable: newVal }) });
1296
+ fetchAll();
1297
+ } catch {
1298
+ }
1299
+ },
1300
+ style: { fontWeight: 600, color: ticket.billable !== false ? "#16a34a" : "#dc2626", background: "none", border: "none", cursor: "pointer", fontSize: 11, textDecoration: "underline" },
1301
+ children: ticket.billable !== false ? "Oui" : "Non"
1074
1302
  }
1075
- },
1076
- onBlur: handleAddTag,
1077
- placeholder: "Tag...",
1078
- autoFocus: true
1079
- }
1080
- ) : /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.tagAddBtn, onClick: () => setAddingTag(true), "aria-label": "Ajouter un tag", children: "+ Tag" })
1081
- ] })
1082
- ] }),
1083
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1084
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: "Facturation" }),
1085
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1086
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1087
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Type" }),
1088
- /* @__PURE__ */ jsxRuntime.jsxs(
1089
- "select",
1303
+ )
1304
+ ] }),
1305
+ totalMin > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "2px 0" }, children: [
1306
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Montant estim\xE9" }),
1307
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 700, color: "var(--theme-text)" }, children: [
1308
+ (totalMin / 60 * 60).toFixed(0),
1309
+ "\u20AC"
1310
+ ] })
1311
+ ] })
1312
+ ] }),
1313
+ timeEntries.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 8, fontSize: 11 }, children: timeEntries.slice(0, 6).map((e) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "3px 0", color: "var(--theme-elevation-500)" }, children: [
1314
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(e.date).toLocaleDateString("fr-FR", { day: "numeric", month: "short" }) }),
1315
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { title: e.description, style: { fontWeight: 600, cursor: e.description ? "help" : "default" }, children: [
1316
+ e.duration,
1317
+ "min"
1318
+ ] })
1319
+ ] }, e.id)) })
1320
+ ] }),
1321
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1322
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: t("detail.tags") }),
1323
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.tagsWrap, children: [
1324
+ tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: s__default.default.tagChip, children: [
1325
+ tag,
1326
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.tagRemove, "aria-label": `Retirer le tag ${tag}`, onClick: () => handleRemoveTag(tag), children: "\xD7" })
1327
+ ] }, tag)),
1328
+ addingTag ? /* @__PURE__ */ jsxRuntime.jsx(
1329
+ "input",
1090
1330
  {
1091
- value: ticket?.billingType || "hourly",
1092
- onChange: async (e) => {
1093
- try {
1094
- await fetch(`/api/tickets/${ticketId}`, {
1095
- method: "PATCH",
1096
- credentials: "include",
1097
- headers: { "Content-Type": "application/json" },
1098
- body: JSON.stringify({ billingType: e.target.value })
1099
- });
1100
- fetchAll();
1101
- } catch {
1331
+ className: s__default.default.tagInput,
1332
+ value: newTagValue,
1333
+ onChange: (e) => setNewTagValue(e.target.value),
1334
+ onKeyDown: (e) => {
1335
+ if (e.key === "Enter") handleAddTag();
1336
+ if (e.key === "Escape") {
1337
+ setAddingTag(false);
1338
+ setNewTagValue("");
1102
1339
  }
1103
1340
  },
1104
- style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff" },
1105
- children: [
1106
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "hourly", children: "Au temps" }),
1107
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "flat", children: "Forfait" })
1108
- ]
1341
+ onBlur: handleAddTag,
1342
+ placeholder: "Tag...",
1343
+ autoFocus: true
1109
1344
  }
1110
- )
1111
- ] }),
1112
- ticket?.billingType === "flat" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1113
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Montant forfait" }),
1114
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
1115
- /* @__PURE__ */ jsxRuntime.jsx(
1116
- "input",
1345
+ ) : /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.tagAddBtn, onClick: () => setAddingTag(true), "aria-label": "Ajouter un tag", children: "+ Tag" })
1346
+ ] })
1347
+ ] }),
1348
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1349
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: "Facturation" }),
1350
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
1351
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1352
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Type" }),
1353
+ /* @__PURE__ */ jsxRuntime.jsxs(
1354
+ "select",
1117
1355
  {
1118
- type: "number",
1119
- defaultValue: ticket?.flatRateAmount ?? "",
1120
- placeholder: "0",
1121
- onBlur: async (e) => {
1122
- const val = e.target.value ? Number(e.target.value) : null;
1356
+ value: ticket?.billingType || "hourly",
1357
+ onChange: async (e) => {
1123
1358
  try {
1124
1359
  await fetch(`/api/tickets/${ticketId}`, {
1125
1360
  method: "PATCH",
1126
1361
  credentials: "include",
1127
1362
  headers: { "Content-Type": "application/json" },
1128
- body: JSON.stringify({ flatRateAmount: val })
1363
+ body: JSON.stringify({ billingType: e.target.value })
1129
1364
  });
1130
1365
  fetchAll();
1131
1366
  } catch {
1132
1367
  }
1133
1368
  },
1134
- style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", width: 80, textAlign: "right" }
1369
+ style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff" },
1370
+ children: [
1371
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "hourly", children: "Au temps" }),
1372
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "flat", children: "Forfait" })
1373
+ ]
1135
1374
  }
1136
- ),
1137
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#9ca3af" }, children: "\u20AC" })
1138
- ] })
1139
- ] }),
1140
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1141
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Montant factur\xE9" }),
1142
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
1143
- /* @__PURE__ */ jsxRuntime.jsx(
1144
- "input",
1375
+ )
1376
+ ] }),
1377
+ ticket?.billingType === "flat" && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1378
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Montant forfait" }),
1379
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
1380
+ /* @__PURE__ */ jsxRuntime.jsx(
1381
+ "input",
1382
+ {
1383
+ type: "number",
1384
+ defaultValue: ticket?.flatRateAmount ?? "",
1385
+ placeholder: "0",
1386
+ onBlur: async (e) => {
1387
+ const val = e.target.value ? Number(e.target.value) : null;
1388
+ try {
1389
+ await fetch(`/api/tickets/${ticketId}`, {
1390
+ method: "PATCH",
1391
+ credentials: "include",
1392
+ headers: { "Content-Type": "application/json" },
1393
+ body: JSON.stringify({ flatRateAmount: val })
1394
+ });
1395
+ fetchAll();
1396
+ } catch {
1397
+ }
1398
+ },
1399
+ style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", width: 80, textAlign: "right" }
1400
+ }
1401
+ ),
1402
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#9ca3af" }, children: "\u20AC" })
1403
+ ] })
1404
+ ] }),
1405
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1406
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Montant factur\xE9" }),
1407
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
1408
+ /* @__PURE__ */ jsxRuntime.jsx(
1409
+ "input",
1410
+ {
1411
+ type: "number",
1412
+ defaultValue: ticket?.billedAmount ?? "",
1413
+ placeholder: "0",
1414
+ onBlur: async (e) => {
1415
+ const val = e.target.value ? Number(e.target.value) : null;
1416
+ try {
1417
+ await fetch(`/api/tickets/${ticketId}`, {
1418
+ method: "PATCH",
1419
+ credentials: "include",
1420
+ headers: { "Content-Type": "application/json" },
1421
+ body: JSON.stringify({ billedAmount: val })
1422
+ });
1423
+ fetchAll();
1424
+ } catch {
1425
+ }
1426
+ },
1427
+ style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", width: 80, textAlign: "right" }
1428
+ }
1429
+ ),
1430
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#9ca3af" }, children: "\u20AC" })
1431
+ ] })
1432
+ ] }),
1433
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1434
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Paiement" }),
1435
+ /* @__PURE__ */ jsxRuntime.jsxs(
1436
+ "select",
1145
1437
  {
1146
- type: "number",
1147
- defaultValue: ticket?.billedAmount ?? "",
1148
- placeholder: "0",
1149
- onBlur: async (e) => {
1150
- const val = e.target.value ? Number(e.target.value) : null;
1438
+ value: ticket?.paymentStatus || "unpaid",
1439
+ onChange: async (e) => {
1151
1440
  try {
1152
1441
  await fetch(`/api/tickets/${ticketId}`, {
1153
1442
  method: "PATCH",
1154
1443
  credentials: "include",
1155
1444
  headers: { "Content-Type": "application/json" },
1156
- body: JSON.stringify({ billedAmount: val })
1445
+ body: JSON.stringify({ paymentStatus: e.target.value })
1157
1446
  });
1158
1447
  fetchAll();
1159
1448
  } catch {
1160
1449
  }
1161
1450
  },
1162
- style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", width: 80, textAlign: "right" }
1451
+ style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff" },
1452
+ children: [
1453
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "unpaid", children: "Non pay\xE9" }),
1454
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "partial", children: "Partiel" }),
1455
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "paid", children: "Pay\xE9" })
1456
+ ]
1163
1457
  }
1164
- ),
1165
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "#9ca3af" }, children: "\u20AC" })
1458
+ )
1166
1459
  ] })
1167
- ] }),
1168
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1169
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12 }, children: "Paiement" }),
1170
- /* @__PURE__ */ jsxRuntime.jsxs(
1171
- "select",
1172
- {
1173
- value: ticket?.paymentStatus || "unpaid",
1174
- onChange: async (e) => {
1175
- try {
1176
- await fetch(`/api/tickets/${ticketId}`, {
1177
- method: "PATCH",
1178
- credentials: "include",
1179
- headers: { "Content-Type": "application/json" },
1180
- body: JSON.stringify({ paymentStatus: e.target.value })
1181
- });
1182
- fetchAll();
1183
- } catch {
1184
- }
1185
- },
1186
- style: { fontSize: 12, padding: "4px 8px", borderRadius: 6, border: "1px solid #e5e7eb", background: "#fff" },
1187
- children: [
1188
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "unpaid", children: "Non pay\xE9" }),
1189
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "partial", children: "Partiel" }),
1190
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "paid", children: "Pay\xE9" })
1191
- ]
1192
- }
1193
- )
1194
1460
  ] })
1195
- ] })
1196
- ] }),
1197
- features.timeTracking && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1198
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSectionTitle, children: [
1199
- t("detail.time"),
1200
- " ",
1201
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 700, fontSize: 13, color: "#d97706" }, children: totalMin > 0 ? `${Math.floor(totalMin / 60)}h${String(totalMin % 60).padStart(2, "0")} ${t("detail.total")}` : "0min" })
1202
1461
  ] }),
1203
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.timer, children: [
1204
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${s__default.default.timerDisplay} ${timerRunning ? s__default.default.timerActive : ""}`, children: [
1205
- String(Math.floor(timerSeconds / 60)).padStart(2, "0"),
1206
- ":",
1207
- String(timerSeconds % 60).padStart(2, "0")
1462
+ summaryLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSection, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-400)", textAlign: "center", padding: 8 }, children: "\u2728 Chargement de la synth\xE8se\u2026" }) })
1463
+ ] }),
1464
+ sidebarTab === "client" && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "tabpanel", id: "sidebar-panel-client", "aria-labelledby": "sidebar-tab-client", children: [
1465
+ client && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1466
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientCard, children: [
1467
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.clientAvatar, children: initials }),
1468
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientInfo, children: [
1469
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientName, children: [
1470
+ client.firstName,
1471
+ " ",
1472
+ client.lastName
1473
+ ] }),
1474
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.clientCompany, children: client.company }),
1475
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${client.email}`, className: s__default.default.clientEmail, children: client.email }),
1476
+ client.phone && /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${client.phone}`, className: s__default.default.clientEmail, children: client.phone })
1477
+ ] })
1208
1478
  ] }),
1209
- !timerRunning ? /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.timerBtn, onClick: () => setTimerRunning(true), style: { color: "#dc2626", borderColor: "#dc2626" }, "aria-label": "D\xE9marrer le timer", children: timerSeconds > 0 ? "\u25B6" : "\u25B6 Go" }) : /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.timerBtn, onClick: () => setTimerRunning(false), "aria-label": "Mettre en pause le timer", children: "\u23F8" }),
1210
- timerSeconds >= 60 && !timerRunning && /* @__PURE__ */ jsxRuntime.jsxs("button", { className: s__default.default.timerBtn, onClick: () => {
1211
- handleTimerSave();
1212
- localStorage.removeItem(`timer-sec-${ticketId}`);
1213
- localStorage.removeItem(`timer-run-${ticketId}`);
1214
- }, style: { color: "#16a34a", borderColor: "#16a34a" }, "aria-label": "Sauvegarder le temps", children: [
1215
- "\u{1F4BE} ",
1216
- Math.round(timerSeconds / 60),
1217
- "m"
1479
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.clientActions, children: [
1480
+ /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: `/admin/collections/support-clients/${client.id}`, className: s__default.default.smallBtn, children: t("client.clientSheet") }),
1481
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.smallBtn, onClick: () => window.open(`/api/admin/impersonate?clientId=${client.id}`, "_blank"), children: t("client.clientPortal") })
1218
1482
  ] })
1219
1483
  ] }),
1220
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, marginTop: 8, alignItems: "center" }, children: [
1221
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "number", min: "1", placeholder: "min", style: { width: 60, padding: "4px 8px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, id: "manual-time-input" }),
1222
- /* @__PURE__ */ jsxRuntime.jsx("button", { className: s__default.default.timerBtn, onClick: async () => {
1223
- const input = document.getElementById("manual-time-input");
1224
- const mins = Number(input?.value);
1225
- if (!mins || mins < 1 || !ticketId) return;
1226
- try {
1227
- await fetch("/api/time-entries", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ ticket: Number(ticketId), duration: mins, date: (/* @__PURE__ */ new Date()).toISOString(), description: "Saisie manuelle" }) });
1228
- if (input) input.value = "";
1229
- fetchAll();
1230
- } catch {
1231
- }
1232
- }, style: { fontSize: 11 }, children: "+ Ajouter" })
1233
- ] }),
1234
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8, fontSize: 11, color: "var(--theme-elevation-500)" }, children: [
1235
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "2px 0", alignItems: "center" }, children: [
1236
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Facturable" }),
1237
- /* @__PURE__ */ jsxRuntime.jsx(
1238
- "button",
1239
- {
1240
- onClick: async () => {
1241
- const newVal = ticket.billable === false ? true : false;
1242
- try {
1243
- await fetch(`/api/tickets/${ticketId}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ billable: newVal }) });
1244
- fetchAll();
1245
- } catch {
1246
- }
1247
- },
1248
- style: { fontWeight: 600, color: ticket.billable !== false ? "#16a34a" : "#dc2626", background: "none", border: "none", cursor: "pointer", fontSize: 11, textDecoration: "underline" },
1249
- children: ticket.billable !== false ? "Oui" : "Non"
1250
- }
1251
- )
1252
- ] }),
1253
- totalMin > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "2px 0" }, children: [
1254
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Montant estim\xE9" }),
1255
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 700, color: "var(--theme-text)" }, children: [
1256
- (totalMin / 60 * 60).toFixed(0),
1257
- "\u20AC"
1484
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1485
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: t("detail.sidebar.previousTickets") }),
1486
+ previousTickets.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.previousTicketsEmpty, children: "\u2014" }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: s__default.default.previousTicketsList, children: previousTickets.map((pt) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: s__default.default.previousTicketsItem, children: /* @__PURE__ */ jsxRuntime.jsxs(Link__default.default, { href: `/admin/collections/tickets/${pt.id}`, className: s__default.default.previousTicketsLink, children: [
1487
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.previousTicketsSubject, children: pt.subject || `#${pt.id}` }),
1488
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: s__default.default.previousTicketsMeta, children: [
1489
+ pt.status ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.previousTicketsStatus, children: pt.status }) : null,
1490
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: s__default.default.previousTicketsDate, children: new Date(pt.createdAt).toLocaleDateString("fr-FR", { day: "numeric", month: "short", year: "numeric" }) })
1258
1491
  ] })
1259
- ] })
1260
- ] }),
1261
- timeEntries.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 8, fontSize: 11 }, children: timeEntries.slice(0, 6).map((e) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", padding: "3px 0", color: "var(--theme-elevation-500)" }, children: [
1262
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: new Date(e.date).toLocaleDateString("fr-FR", { day: "numeric", month: "short" }) }),
1263
- /* @__PURE__ */ jsxRuntime.jsxs("span", { title: e.description, style: { fontWeight: 600, cursor: e.description ? "help" : "default" }, children: [
1264
- e.duration,
1265
- "min"
1266
- ] })
1267
- ] }, e.id)) })
1492
+ ] }) }, pt.id)) })
1493
+ ] })
1268
1494
  ] }),
1269
- features.activityLog && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1270
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { className: s__default.default.collapseBtn, onClick: () => setShowActivity(!showActivity), "aria-label": showActivity ? "Masquer le journal" : "Afficher le journal", children: [
1271
- "Activit\xE9 ",
1272
- showActivity ? "\u25BE" : "\u25B8"
1273
- ] }) }),
1274
- showActivity && activityLog.slice(0, 8).map((a) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.activityItem, children: [
1495
+ sidebarTab === "activity" && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "tabpanel", id: "sidebar-panel-activity", "aria-labelledby": "sidebar-tab-activity", children: features.activityLog ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.sideSection, children: [
1496
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSectionTitle, children: t("detail.activity") }),
1497
+ activityLog.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.previousTicketsEmpty, children: "\u2014" }) : activityLog.slice(0, 30).map((a) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.activityItem, children: [
1275
1498
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.activityDot, style: { backgroundColor: a.actorType === "admin" ? "#2563eb" : a.actorType === "system" ? "#6b7280" : "#16a34a" } }),
1276
1499
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.activityContent, children: [
1277
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.activityText, children: (a.detail || a.action).slice(0, 60) }),
1500
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.activityText, children: (a.detail || a.action).slice(0, 120) }),
1278
1501
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.activityTime, children: timeAgo(a.createdAt) })
1279
1502
  ] })
1280
1503
  ] }, a.id))
1281
- ] }),
1282
- summaryLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSection, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-400)", textAlign: "center", padding: 8 }, children: "\u2728 Chargement de la synth\xE8se\u2026" }) })
1504
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.sideSection, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: s__default.default.previousTicketsEmpty, children: "\u2014" }) }) })
1283
1505
  ] })
1284
1506
  ] }),
1285
1507
  undoToast && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: s__default.default.undoToast, role: "alert", children: [