@consilioweb/payload-support 0.8.2 → 0.9.5

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 (50) hide show
  1. package/dist/components/RichTextEditor/index.cjs +233 -0
  2. package/dist/components/RichTextEditor/index.js +232 -0
  3. package/dist/components/TicketConversation/components/CodeBlock.cjs +24 -7
  4. package/dist/components/TicketConversation/components/CodeBlock.js +23 -9
  5. package/dist/index.cjs +19 -1
  6. package/dist/index.js +19 -1
  7. package/dist/views/BillingView/client.cjs +260 -103
  8. package/dist/views/BillingView/client.js +259 -103
  9. package/dist/views/ChatView/client.cjs +184 -137
  10. package/dist/views/ChatView/client.js +180 -137
  11. package/dist/views/CrmView/client.cjs +270 -122
  12. package/dist/views/CrmView/client.js +266 -122
  13. package/dist/views/EmailTrackingView/client.cjs +80 -69
  14. package/dist/views/EmailTrackingView/client.js +80 -70
  15. package/dist/views/ImportConversationView/client.cjs +127 -94
  16. package/dist/views/ImportConversationView/client.js +123 -94
  17. package/dist/views/LogsView/client.cjs +56 -58
  18. package/dist/views/LogsView/client.js +52 -58
  19. package/dist/views/NewTicketView/client.cjs +39 -55
  20. package/dist/views/NewTicketView/client.js +38 -55
  21. package/dist/views/PendingEmailsView/client.cjs +399 -102
  22. package/dist/views/PendingEmailsView/client.js +396 -103
  23. package/dist/views/SupportDashboardView/client.cjs +276 -137
  24. package/dist/views/SupportDashboardView/client.js +275 -137
  25. package/dist/views/TicketDetailView/client.cjs +487 -204
  26. package/dist/views/TicketDetailView/client.js +486 -204
  27. package/dist/views/TicketInboxView/client.cjs +62 -65
  28. package/dist/views/TicketInboxView/client.js +62 -66
  29. package/dist/views/TicketingSettingsView/client.cjs +10 -8
  30. package/dist/views/TicketingSettingsView/client.js +10 -8
  31. package/dist/views/TimeDashboardView/client.cjs +70 -59
  32. package/dist/views/TimeDashboardView/client.js +69 -59
  33. package/package.json +7 -3
  34. package/src/components/RichTextEditor/index.tsx +261 -0
  35. package/src/components/TicketConversation/components/CodeBlock.tsx +53 -14
  36. package/src/plugin.ts +2 -0
  37. package/src/utils/emailTemplate.ts +37 -0
  38. package/src/views/BillingView/client.tsx +362 -69
  39. package/src/views/ChatView/client.tsx +225 -140
  40. package/src/views/CrmView/client.tsx +447 -189
  41. package/src/views/EmailTrackingView/client.tsx +111 -71
  42. package/src/views/ImportConversationView/client.tsx +255 -70
  43. package/src/views/LogsView/client.tsx +85 -50
  44. package/src/views/NewTicketView/client.tsx +37 -53
  45. package/src/views/PendingEmailsView/client.tsx +512 -92
  46. package/src/views/SupportDashboardView/client.tsx +294 -134
  47. package/src/views/TicketDetailView/client.tsx +486 -213
  48. package/src/views/TicketInboxView/client.tsx +52 -61
  49. package/src/views/TicketingSettingsView/client.tsx +10 -9
  50. package/src/views/TimeDashboardView/client.tsx +184 -69
@@ -2,11 +2,14 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
+ var Skeleton = require('../shared/Skeleton');
5
6
  var useTranslation = require('../../components/TicketConversation/hooks/useTranslation');
7
+ var styles = require('../../styles/TimeDashboard.module.scss');
6
8
 
7
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
10
 
9
11
  var React__default = /*#__PURE__*/_interopDefault(React);
12
+ var styles__default = /*#__PURE__*/_interopDefault(styles);
10
13
 
11
14
  function formatDuration(minutes) {
12
15
  const h = Math.floor(minutes / 60);
@@ -36,7 +39,11 @@ const TimeDashboardClient = () => {
36
39
  const fetchEntries = React.useCallback(async () => {
37
40
  setLoading(true);
38
41
  try {
39
- const params = new URLSearchParams({ limit: "500", depth: "2", sort: "-date" });
42
+ const params = new URLSearchParams({
43
+ limit: "500",
44
+ depth: "2",
45
+ sort: "-date"
46
+ });
40
47
  if (from) params.set("where[date][greater_than_equal]", from);
41
48
  if (to) params.set("where[date][less_than_equal]", to);
42
49
  const res = await fetch(`/api/time-entries?${params}`);
@@ -52,6 +59,7 @@ const TimeDashboardClient = () => {
52
59
  fetchEntries();
53
60
  }, [fetchEntries]);
54
61
  const totalMinutes = entries.reduce((sum, e) => sum + (e.duration || 0), 0);
62
+ const totalEntries = entries.length;
55
63
  const grouped = React__default.default.useMemo(() => {
56
64
  const map = /* @__PURE__ */ new Map();
57
65
  for (const entry of entries) {
@@ -69,13 +77,19 @@ const TimeDashboardClient = () => {
69
77
  if (!map.has(key)) map.set(key, []);
70
78
  map.get(key).push(entry);
71
79
  }
72
- return Array.from(map.entries()).map(([label, items]) => ({ label, entries: items, totalMinutes: items.reduce((sum, e) => sum + (e.duration || 0), 0) }));
80
+ return Array.from(map.entries()).map(([label, items]) => ({
81
+ label,
82
+ entries: items,
83
+ totalMinutes: items.reduce((sum, e) => sum + (e.duration || 0), 0)
84
+ }));
73
85
  }, [entries, groupBy]);
74
86
  const dailyChart = React__default.default.useMemo(() => {
75
87
  const dayMap = /* @__PURE__ */ new Map();
76
88
  for (const entry of entries) {
77
89
  const day = entry.date ? entry.date.split("T")[0] : null;
78
- if (day) dayMap.set(day, (dayMap.get(day) || 0) + (entry.duration || 0));
90
+ if (day) {
91
+ dayMap.set(day, (dayMap.get(day) || 0) + (entry.duration || 0));
92
+ }
79
93
  }
80
94
  return Array.from(dayMap.entries()).sort(([a], [b]) => a.localeCompare(b)).slice(-30).map(([day, mins]) => ({ day, minutes: mins }));
81
95
  }, [entries]);
@@ -84,43 +98,32 @@ const TimeDashboardClient = () => {
84
98
  setFrom(range.from);
85
99
  setTo(range.to);
86
100
  };
87
- const S = {
88
- page: { padding: "20px 30px", maxWidth: 1100, margin: "0 auto" },
89
- btn: { padding: "6px 12px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, cursor: "pointer", background: "var(--theme-elevation-0)", color: "var(--theme-text)" },
90
- btnPrimary: { padding: "6px 12px", borderRadius: 6, border: "none", fontSize: 12, cursor: "pointer", background: "#2563eb", color: "#fff", fontWeight: 600 },
91
- kpis: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, marginBottom: 20 },
92
- kpiCard: { padding: "16px 20px", borderRadius: 10, border: "1px solid var(--theme-elevation-150)" },
93
- groupCard: { marginBottom: 12, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", overflow: "hidden" },
94
- groupHeader: { display: "flex", justifyContent: "space-between", padding: "10px 16px", background: "var(--theme-elevation-50)", borderBottom: "1px solid var(--theme-elevation-150)" },
95
- table: { width: "100%", borderCollapse: "collapse", fontSize: 12 },
96
- td: { padding: "6px 8px", borderBottom: "1px solid var(--theme-elevation-100)" }
97
- };
98
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.page, children: [
99
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }, children: [
100
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
101
- /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: 22, fontWeight: 700, margin: 0 }, children: t("timeDashboard.title") }),
102
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 13, color: "var(--theme-elevation-500)", margin: "4px 0 0" }, children: t("timeDashboard.subtitle") })
101
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.page, children: [
102
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.header, children: [
103
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.headerLeft, children: [
104
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: styles__default.default.title, children: t("timeDashboard.title") }),
105
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: styles__default.default.subtitle, children: t("timeDashboard.subtitle") })
103
106
  ] }),
104
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/admin/collections/time-entries/create", style: { ...S.btnPrimary, textDecoration: "none", display: "inline-block" }, children: t("timeDashboard.newEntry") })
107
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: "/admin/collections/time-entries/create", className: styles__default.default.newEntryBtn, children: t("timeDashboard.newEntry") })
105
108
  ] }),
106
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 16 }, children: [
107
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, marginBottom: 8 }, children: [
108
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: S.btnPrimary, onClick: () => setPeriod(getMonthRange(0)), children: t("timeDashboard.filters.thisMonth") }),
109
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: S.btn, onClick: () => setPeriod(getMonthRange(-1)), children: t("timeDashboard.filters.lastMonth") }),
110
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: S.btn, onClick: () => setPeriod(getMonthRange(-2)), children: t("timeDashboard.filters.twoMonthsAgo") })
109
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.filters, children: [
110
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.quickPeriod, children: [
111
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: styles__default.default.btnPrimary, onClick: () => setPeriod(getMonthRange(0)), children: t("timeDashboard.filters.thisMonth") }),
112
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: styles__default.default.btnSecondary, onClick: () => setPeriod(getMonthRange(-1)), children: t("timeDashboard.filters.lastMonth") }),
113
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: styles__default.default.btnAmber, onClick: () => setPeriod(getMonthRange(-2)), children: t("timeDashboard.filters.twoMonthsAgo") })
111
114
  ] }),
112
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 12, alignItems: "flex-end" }, children: [
113
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
114
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 4, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.filters.from") }),
115
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", value: from, onChange: (e) => setFrom(e.target.value), style: { padding: "6px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" } })
115
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.filterRow, children: [
116
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.fieldGroup, children: [
117
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: styles__default.default.label, children: t("timeDashboard.filters.from") }),
118
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", value: from, onChange: (e) => setFrom(e.target.value), className: styles__default.default.input })
116
119
  ] }),
117
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
118
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 4, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.filters.to") }),
119
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", value: to, onChange: (e) => setTo(e.target.value), style: { padding: "6px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" } })
120
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.fieldGroup, children: [
121
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: styles__default.default.label, children: t("timeDashboard.filters.to") }),
122
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "date", value: to, onChange: (e) => setTo(e.target.value), className: styles__default.default.input })
120
123
  ] }),
121
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
122
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 4, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.filters.groupBy") }),
123
- /* @__PURE__ */ jsxRuntime.jsxs("select", { value: groupBy, onChange: (e) => setGroupBy(e.target.value), style: { padding: "6px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, children: [
124
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.fieldGroup, children: [
125
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: styles__default.default.label, children: t("timeDashboard.filters.groupBy") }),
126
+ /* @__PURE__ */ jsxRuntime.jsxs("select", { value: groupBy, onChange: (e) => setGroupBy(e.target.value), className: styles__default.default.select, children: [
124
127
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "day", children: t("timeDashboard.filters.day") }),
125
128
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "week", children: t("timeDashboard.filters.week") }),
126
129
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "project", children: t("timeDashboard.filters.project") })
@@ -128,37 +131,45 @@ const TimeDashboardClient = () => {
128
131
  ] })
129
132
  ] })
130
133
  ] }),
131
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 40, textAlign: "center", color: "#94a3b8" }, children: t("common.loading") }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
132
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.kpis, children: [
133
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.kpiCard, children: [
134
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.kpis.totalTime") }),
135
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 24, fontWeight: 700, color: "#2563eb" }, children: formatDuration(totalMinutes) })
134
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.loading, children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton.SkeletonDashboard, {}) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
135
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.kpis, children: [
136
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.kpiCardPrimary, children: [
137
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.kpiLabel, children: t("timeDashboard.kpis.totalTime") }),
138
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.kpiPrimary, children: formatDuration(totalMinutes) })
136
139
  ] }),
137
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.kpiCard, children: [
138
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.kpis.entries") }),
139
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 24, fontWeight: 700, color: "#d97706" }, children: entries.length })
140
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.kpiCardAmber, children: [
141
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.kpiLabel, children: t("timeDashboard.kpis.entries") }),
142
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.kpiAmber, children: totalEntries })
140
143
  ] }),
141
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.kpiCard, children: [
142
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.kpis.dailyAverage") }),
143
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 24, fontWeight: 700, color: "#ea580c" }, children: dailyChart.length > 0 ? formatDuration(Math.round(totalMinutes / dailyChart.length)) : "-" })
144
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.kpiCardOrange, children: [
145
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.kpiLabel, children: t("timeDashboard.kpis.dailyAverage") }),
146
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.kpiOrange, children: dailyChart.length > 0 ? formatDuration(Math.round(totalMinutes / dailyChart.length)) : "-" })
144
147
  ] })
145
148
  ] }),
146
- dailyChart.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 16, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", marginBottom: 20 }, children: [
147
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 700, marginBottom: 8 }, children: t("timeDashboard.chart.title") }),
148
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "flex-end", gap: 3, height: 80 }, children: dailyChart.map((d) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, background: "#3b82f6", borderRadius: "3px 3px 0 0", height: `${Math.max(d.minutes / maxDailyMinutes * 100, 4)}%` }, title: `${d.day}: ${formatDuration(d.minutes)}` }, d.day)) })
149
+ dailyChart.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.chartWrap, children: [
150
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.chartHeader, children: t("timeDashboard.chart.title") }),
151
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.chartBars, children: dailyChart.map((d) => /* @__PURE__ */ jsxRuntime.jsx(
152
+ "div",
153
+ {
154
+ className: styles__default.default.chartBar,
155
+ style: { height: `${Math.max(d.minutes / maxDailyMinutes * 100, 4)}%` },
156
+ title: `${d.day}: ${formatDuration(d.minutes)}`
157
+ },
158
+ d.day
159
+ )) })
149
160
  ] }),
150
- grouped.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 40, textAlign: "center", color: "#94a3b8" }, children: t("timeDashboard.empty") }) : grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.groupCard, children: [
151
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: S.groupHeader, children: [
152
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 700 }, children: group.label }),
153
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 700, color: "#2563eb" }, children: formatDuration(group.totalMinutes) })
161
+ grouped.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles__default.default.empty, children: t("timeDashboard.empty") }) : grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.groupCard, children: [
162
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.groupHeader, children: [
163
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.groupLabel, children: group.label }),
164
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles__default.default.groupTotal, children: formatDuration(group.totalMinutes) })
154
165
  ] }),
155
- /* @__PURE__ */ jsxRuntime.jsx("table", { style: S.table, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: group.entries.map((entry) => {
166
+ /* @__PURE__ */ jsxRuntime.jsx("table", { className: styles__default.default.table, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: group.entries.map((entry) => {
156
167
  const ticket = typeof entry.ticket === "object" ? entry.ticket : null;
157
- return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
158
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: S.td, children: ticket ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: `/admin/collections/tickets/${ticket.id}`, style: { color: "#2563eb", textDecoration: "none", fontWeight: 600 }, children: ticket.ticketNumber || `#${ticket.id}` }) : "-" }),
159
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: S.td, children: ticket?.subject || "" }),
160
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: { ...S.td, color: "var(--theme-elevation-500)" }, children: entry.description || "-" }),
161
- /* @__PURE__ */ jsxRuntime.jsx("td", { style: { ...S.td, textAlign: "right", fontWeight: 600 }, children: formatDuration(entry.duration) })
168
+ return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: styles__default.default.entryRow, children: [
169
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: styles__default.default.tdTicket, children: ticket ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: `/admin/collections/tickets/${ticket.id}`, className: styles__default.default.ticketLink, children: ticket.ticketNumber || `#${ticket.id}` }) : "-" }),
170
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: styles__default.default.tdSubject, children: ticket?.subject || "" }),
171
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: styles__default.default.tdDescription, children: entry.description || "-" }),
172
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: styles__default.default.tdDuration, children: formatDuration(entry.duration) })
162
173
  ] }, entry.id);
163
174
  }) }) })
164
175
  ] }, group.label))
@@ -1,7 +1,9 @@
1
1
  "use client";
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import React, { useState, useCallback, useEffect } from 'react';
4
+ import { SkeletonDashboard } from '../shared/Skeleton.js';
4
5
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation.js';
6
+ import styles from '../../styles/TimeDashboard.module.scss';
5
7
 
6
8
  function formatDuration(minutes) {
7
9
  const h = Math.floor(minutes / 60);
@@ -31,7 +33,11 @@ const TimeDashboardClient = () => {
31
33
  const fetchEntries = useCallback(async () => {
32
34
  setLoading(true);
33
35
  try {
34
- const params = new URLSearchParams({ limit: "500", depth: "2", sort: "-date" });
36
+ const params = new URLSearchParams({
37
+ limit: "500",
38
+ depth: "2",
39
+ sort: "-date"
40
+ });
35
41
  if (from) params.set("where[date][greater_than_equal]", from);
36
42
  if (to) params.set("where[date][less_than_equal]", to);
37
43
  const res = await fetch(`/api/time-entries?${params}`);
@@ -47,6 +53,7 @@ const TimeDashboardClient = () => {
47
53
  fetchEntries();
48
54
  }, [fetchEntries]);
49
55
  const totalMinutes = entries.reduce((sum, e) => sum + (e.duration || 0), 0);
56
+ const totalEntries = entries.length;
50
57
  const grouped = React.useMemo(() => {
51
58
  const map = /* @__PURE__ */ new Map();
52
59
  for (const entry of entries) {
@@ -64,13 +71,19 @@ const TimeDashboardClient = () => {
64
71
  if (!map.has(key)) map.set(key, []);
65
72
  map.get(key).push(entry);
66
73
  }
67
- return Array.from(map.entries()).map(([label, items]) => ({ label, entries: items, totalMinutes: items.reduce((sum, e) => sum + (e.duration || 0), 0) }));
74
+ return Array.from(map.entries()).map(([label, items]) => ({
75
+ label,
76
+ entries: items,
77
+ totalMinutes: items.reduce((sum, e) => sum + (e.duration || 0), 0)
78
+ }));
68
79
  }, [entries, groupBy]);
69
80
  const dailyChart = React.useMemo(() => {
70
81
  const dayMap = /* @__PURE__ */ new Map();
71
82
  for (const entry of entries) {
72
83
  const day = entry.date ? entry.date.split("T")[0] : null;
73
- if (day) dayMap.set(day, (dayMap.get(day) || 0) + (entry.duration || 0));
84
+ if (day) {
85
+ dayMap.set(day, (dayMap.get(day) || 0) + (entry.duration || 0));
86
+ }
74
87
  }
75
88
  return Array.from(dayMap.entries()).sort(([a], [b]) => a.localeCompare(b)).slice(-30).map(([day, mins]) => ({ day, minutes: mins }));
76
89
  }, [entries]);
@@ -79,43 +92,32 @@ const TimeDashboardClient = () => {
79
92
  setFrom(range.from);
80
93
  setTo(range.to);
81
94
  };
82
- const S = {
83
- page: { padding: "20px 30px", maxWidth: 1100, margin: "0 auto" },
84
- btn: { padding: "6px 12px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, cursor: "pointer", background: "var(--theme-elevation-0)", color: "var(--theme-text)" },
85
- btnPrimary: { padding: "6px 12px", borderRadius: 6, border: "none", fontSize: 12, cursor: "pointer", background: "#2563eb", color: "#fff", fontWeight: 600 },
86
- kpis: { display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12, marginBottom: 20 },
87
- kpiCard: { padding: "16px 20px", borderRadius: 10, border: "1px solid var(--theme-elevation-150)" },
88
- groupCard: { marginBottom: 12, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", overflow: "hidden" },
89
- groupHeader: { display: "flex", justifyContent: "space-between", padding: "10px 16px", background: "var(--theme-elevation-50)", borderBottom: "1px solid var(--theme-elevation-150)" },
90
- table: { width: "100%", borderCollapse: "collapse", fontSize: 12 },
91
- td: { padding: "6px 8px", borderBottom: "1px solid var(--theme-elevation-100)" }
92
- };
93
- return /* @__PURE__ */ jsxs("div", { style: S.page, children: [
94
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }, children: [
95
- /* @__PURE__ */ jsxs("div", { children: [
96
- /* @__PURE__ */ jsx("h1", { style: { fontSize: 22, fontWeight: 700, margin: 0 }, children: t("timeDashboard.title") }),
97
- /* @__PURE__ */ jsx("p", { style: { fontSize: 13, color: "var(--theme-elevation-500)", margin: "4px 0 0" }, children: t("timeDashboard.subtitle") })
95
+ return /* @__PURE__ */ jsxs("div", { className: styles.page, children: [
96
+ /* @__PURE__ */ jsxs("div", { className: styles.header, children: [
97
+ /* @__PURE__ */ jsxs("div", { className: styles.headerLeft, children: [
98
+ /* @__PURE__ */ jsx("h1", { className: styles.title, children: t("timeDashboard.title") }),
99
+ /* @__PURE__ */ jsx("p", { className: styles.subtitle, children: t("timeDashboard.subtitle") })
98
100
  ] }),
99
- /* @__PURE__ */ jsx("a", { href: "/admin/collections/time-entries/create", style: { ...S.btnPrimary, textDecoration: "none", display: "inline-block" }, children: t("timeDashboard.newEntry") })
101
+ /* @__PURE__ */ jsx("a", { href: "/admin/collections/time-entries/create", className: styles.newEntryBtn, children: t("timeDashboard.newEntry") })
100
102
  ] }),
101
- /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
102
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, marginBottom: 8 }, children: [
103
- /* @__PURE__ */ jsx("button", { style: S.btnPrimary, onClick: () => setPeriod(getMonthRange(0)), children: t("timeDashboard.filters.thisMonth") }),
104
- /* @__PURE__ */ jsx("button", { style: S.btn, onClick: () => setPeriod(getMonthRange(-1)), children: t("timeDashboard.filters.lastMonth") }),
105
- /* @__PURE__ */ jsx("button", { style: S.btn, onClick: () => setPeriod(getMonthRange(-2)), children: t("timeDashboard.filters.twoMonthsAgo") })
103
+ /* @__PURE__ */ jsxs("div", { className: styles.filters, children: [
104
+ /* @__PURE__ */ jsxs("div", { className: styles.quickPeriod, children: [
105
+ /* @__PURE__ */ jsx("button", { className: styles.btnPrimary, onClick: () => setPeriod(getMonthRange(0)), children: t("timeDashboard.filters.thisMonth") }),
106
+ /* @__PURE__ */ jsx("button", { className: styles.btnSecondary, onClick: () => setPeriod(getMonthRange(-1)), children: t("timeDashboard.filters.lastMonth") }),
107
+ /* @__PURE__ */ jsx("button", { className: styles.btnAmber, onClick: () => setPeriod(getMonthRange(-2)), children: t("timeDashboard.filters.twoMonthsAgo") })
106
108
  ] }),
107
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 12, alignItems: "flex-end" }, children: [
108
- /* @__PURE__ */ jsxs("div", { children: [
109
- /* @__PURE__ */ jsx("label", { style: { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 4, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.filters.from") }),
110
- /* @__PURE__ */ jsx("input", { type: "date", value: from, onChange: (e) => setFrom(e.target.value), style: { padding: "6px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" } })
109
+ /* @__PURE__ */ jsxs("div", { className: styles.filterRow, children: [
110
+ /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
111
+ /* @__PURE__ */ jsx("label", { className: styles.label, children: t("timeDashboard.filters.from") }),
112
+ /* @__PURE__ */ jsx("input", { type: "date", value: from, onChange: (e) => setFrom(e.target.value), className: styles.input })
111
113
  ] }),
112
- /* @__PURE__ */ jsxs("div", { children: [
113
- /* @__PURE__ */ jsx("label", { style: { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 4, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.filters.to") }),
114
- /* @__PURE__ */ jsx("input", { type: "date", value: to, onChange: (e) => setTo(e.target.value), style: { padding: "6px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" } })
114
+ /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
115
+ /* @__PURE__ */ jsx("label", { className: styles.label, children: t("timeDashboard.filters.to") }),
116
+ /* @__PURE__ */ jsx("input", { type: "date", value: to, onChange: (e) => setTo(e.target.value), className: styles.input })
115
117
  ] }),
116
- /* @__PURE__ */ jsxs("div", { children: [
117
- /* @__PURE__ */ jsx("label", { style: { fontSize: 11, fontWeight: 600, display: "block", marginBottom: 4, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.filters.groupBy") }),
118
- /* @__PURE__ */ jsxs("select", { value: groupBy, onChange: (e) => setGroupBy(e.target.value), style: { padding: "6px 10px", borderRadius: 6, border: "1px solid var(--theme-elevation-200)", fontSize: 12, color: "var(--theme-text)", background: "var(--theme-elevation-0)" }, children: [
118
+ /* @__PURE__ */ jsxs("div", { className: styles.fieldGroup, children: [
119
+ /* @__PURE__ */ jsx("label", { className: styles.label, children: t("timeDashboard.filters.groupBy") }),
120
+ /* @__PURE__ */ jsxs("select", { value: groupBy, onChange: (e) => setGroupBy(e.target.value), className: styles.select, children: [
119
121
  /* @__PURE__ */ jsx("option", { value: "day", children: t("timeDashboard.filters.day") }),
120
122
  /* @__PURE__ */ jsx("option", { value: "week", children: t("timeDashboard.filters.week") }),
121
123
  /* @__PURE__ */ jsx("option", { value: "project", children: t("timeDashboard.filters.project") })
@@ -123,37 +125,45 @@ const TimeDashboardClient = () => {
123
125
  ] })
124
126
  ] })
125
127
  ] }),
126
- loading ? /* @__PURE__ */ jsx("div", { style: { padding: 40, textAlign: "center", color: "#94a3b8" }, children: t("common.loading") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
127
- /* @__PURE__ */ jsxs("div", { style: S.kpis, children: [
128
- /* @__PURE__ */ jsxs("div", { style: S.kpiCard, children: [
129
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.kpis.totalTime") }),
130
- /* @__PURE__ */ jsx("div", { style: { fontSize: 24, fontWeight: 700, color: "#2563eb" }, children: formatDuration(totalMinutes) })
128
+ loading ? /* @__PURE__ */ jsx("div", { className: styles.loading, children: /* @__PURE__ */ jsx(SkeletonDashboard, {}) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
129
+ /* @__PURE__ */ jsxs("div", { className: styles.kpis, children: [
130
+ /* @__PURE__ */ jsxs("div", { className: styles.kpiCardPrimary, children: [
131
+ /* @__PURE__ */ jsx("div", { className: styles.kpiLabel, children: t("timeDashboard.kpis.totalTime") }),
132
+ /* @__PURE__ */ jsx("div", { className: styles.kpiPrimary, children: formatDuration(totalMinutes) })
131
133
  ] }),
132
- /* @__PURE__ */ jsxs("div", { style: S.kpiCard, children: [
133
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.kpis.entries") }),
134
- /* @__PURE__ */ jsx("div", { style: { fontSize: 24, fontWeight: 700, color: "#d97706" }, children: entries.length })
134
+ /* @__PURE__ */ jsxs("div", { className: styles.kpiCardAmber, children: [
135
+ /* @__PURE__ */ jsx("div", { className: styles.kpiLabel, children: t("timeDashboard.kpis.entries") }),
136
+ /* @__PURE__ */ jsx("div", { className: styles.kpiAmber, children: totalEntries })
135
137
  ] }),
136
- /* @__PURE__ */ jsxs("div", { style: S.kpiCard, children: [
137
- /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "var(--theme-elevation-500)" }, children: t("timeDashboard.kpis.dailyAverage") }),
138
- /* @__PURE__ */ jsx("div", { style: { fontSize: 24, fontWeight: 700, color: "#ea580c" }, children: dailyChart.length > 0 ? formatDuration(Math.round(totalMinutes / dailyChart.length)) : "-" })
138
+ /* @__PURE__ */ jsxs("div", { className: styles.kpiCardOrange, children: [
139
+ /* @__PURE__ */ jsx("div", { className: styles.kpiLabel, children: t("timeDashboard.kpis.dailyAverage") }),
140
+ /* @__PURE__ */ jsx("div", { className: styles.kpiOrange, children: dailyChart.length > 0 ? formatDuration(Math.round(totalMinutes / dailyChart.length)) : "-" })
139
141
  ] })
140
142
  ] }),
141
- dailyChart.length > 0 && /* @__PURE__ */ jsxs("div", { style: { padding: 16, borderRadius: 10, border: "1px solid var(--theme-elevation-150)", marginBottom: 20 }, children: [
142
- /* @__PURE__ */ jsx("div", { style: { fontSize: 13, fontWeight: 700, marginBottom: 8 }, children: t("timeDashboard.chart.title") }),
143
- /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "flex-end", gap: 3, height: 80 }, children: dailyChart.map((d) => /* @__PURE__ */ jsx("div", { style: { flex: 1, background: "#3b82f6", borderRadius: "3px 3px 0 0", height: `${Math.max(d.minutes / maxDailyMinutes * 100, 4)}%` }, title: `${d.day}: ${formatDuration(d.minutes)}` }, d.day)) })
143
+ dailyChart.length > 0 && /* @__PURE__ */ jsxs("div", { className: styles.chartWrap, children: [
144
+ /* @__PURE__ */ jsx("div", { className: styles.chartHeader, children: t("timeDashboard.chart.title") }),
145
+ /* @__PURE__ */ jsx("div", { className: styles.chartBars, children: dailyChart.map((d) => /* @__PURE__ */ jsx(
146
+ "div",
147
+ {
148
+ className: styles.chartBar,
149
+ style: { height: `${Math.max(d.minutes / maxDailyMinutes * 100, 4)}%` },
150
+ title: `${d.day}: ${formatDuration(d.minutes)}`
151
+ },
152
+ d.day
153
+ )) })
144
154
  ] }),
145
- grouped.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: 40, textAlign: "center", color: "#94a3b8" }, children: t("timeDashboard.empty") }) : grouped.map((group) => /* @__PURE__ */ jsxs("div", { style: S.groupCard, children: [
146
- /* @__PURE__ */ jsxs("div", { style: S.groupHeader, children: [
147
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 700 }, children: group.label }),
148
- /* @__PURE__ */ jsx("span", { style: { fontWeight: 700, color: "#2563eb" }, children: formatDuration(group.totalMinutes) })
155
+ grouped.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.empty, children: t("timeDashboard.empty") }) : grouped.map((group) => /* @__PURE__ */ jsxs("div", { className: styles.groupCard, children: [
156
+ /* @__PURE__ */ jsxs("div", { className: styles.groupHeader, children: [
157
+ /* @__PURE__ */ jsx("span", { className: styles.groupLabel, children: group.label }),
158
+ /* @__PURE__ */ jsx("span", { className: styles.groupTotal, children: formatDuration(group.totalMinutes) })
149
159
  ] }),
150
- /* @__PURE__ */ jsx("table", { style: S.table, children: /* @__PURE__ */ jsx("tbody", { children: group.entries.map((entry) => {
160
+ /* @__PURE__ */ jsx("table", { className: styles.table, children: /* @__PURE__ */ jsx("tbody", { children: group.entries.map((entry) => {
151
161
  const ticket = typeof entry.ticket === "object" ? entry.ticket : null;
152
- return /* @__PURE__ */ jsxs("tr", { children: [
153
- /* @__PURE__ */ jsx("td", { style: S.td, children: ticket ? /* @__PURE__ */ jsx("a", { href: `/admin/collections/tickets/${ticket.id}`, style: { color: "#2563eb", textDecoration: "none", fontWeight: 600 }, children: ticket.ticketNumber || `#${ticket.id}` }) : "-" }),
154
- /* @__PURE__ */ jsx("td", { style: S.td, children: ticket?.subject || "" }),
155
- /* @__PURE__ */ jsx("td", { style: { ...S.td, color: "var(--theme-elevation-500)" }, children: entry.description || "-" }),
156
- /* @__PURE__ */ jsx("td", { style: { ...S.td, textAlign: "right", fontWeight: 600 }, children: formatDuration(entry.duration) })
162
+ return /* @__PURE__ */ jsxs("tr", { className: styles.entryRow, children: [
163
+ /* @__PURE__ */ jsx("td", { className: styles.tdTicket, children: ticket ? /* @__PURE__ */ jsx("a", { href: `/admin/collections/tickets/${ticket.id}`, className: styles.ticketLink, children: ticket.ticketNumber || `#${ticket.id}` }) : "-" }),
164
+ /* @__PURE__ */ jsx("td", { className: styles.tdSubject, children: ticket?.subject || "" }),
165
+ /* @__PURE__ */ jsx("td", { className: styles.tdDescription, children: entry.description || "-" }),
166
+ /* @__PURE__ */ jsx("td", { className: styles.tdDuration, children: formatDuration(entry.duration) })
157
167
  ] }, entry.id);
158
168
  }) }) })
159
169
  ] }, group.label))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@consilioweb/payload-support",
3
- "version": "0.8.2",
3
+ "version": "0.9.5",
4
4
  "description": "Payload CMS plugin — professional support & ticketing system with AI, SLA, time tracking, live chat, and more",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -52,7 +52,7 @@
52
52
  "scripts": {
53
53
  "build": "tsup",
54
54
  "typecheck": "tsc --noEmit",
55
- "prepublishOnly": "pnpm build"
55
+ "prepublishOnly": "npm run build"
56
56
  },
57
57
  "keywords": [
58
58
  "payload",
@@ -96,7 +96,8 @@
96
96
  "next": "^14.0.0 || ^15.0.0 || ^16.0.0",
97
97
  "payload": "^3.0.0",
98
98
  "react": "^18.0.0 || ^19.0.0",
99
- "react-dom": "^18.0.0 || ^19.0.0"
99
+ "react-dom": "^18.0.0 || ^19.0.0",
100
+ "lucide-react": ">=0.300.0"
100
101
  },
101
102
  "peerDependenciesMeta": {
102
103
  "@payloadcms/ui": {
@@ -108,6 +109,9 @@
108
109
  "@payloadcms/next": {
109
110
  "optional": true
110
111
  },
112
+ "lucide-react": {
113
+ "optional": true
114
+ },
111
115
  "next": {
112
116
  "optional": true
113
117
  },