@djangocfg/ext-support 1.0.4 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.cjs +3 -1
- package/dist/config.js +3 -1
- package/dist/hooks.cjs +75 -80
- package/dist/hooks.js +74 -80
- package/dist/index.cjs +75 -80
- package/dist/index.js +74 -80
- package/package.json +9 -7
- package/src/api/generated/ext_support/CLAUDE.md +80 -0
- package/src/api/generated/ext_support/_utils/fetchers/ext_support__support.ts +1 -0
- package/src/api/generated/ext_support/_utils/fetchers/index.ts +1 -0
- package/src/api/generated/ext_support/_utils/hooks/ext_support__support.ts +1 -0
- package/src/api/generated/ext_support/_utils/hooks/index.ts +1 -0
- package/src/api/generated/ext_support/_utils/schemas/index.ts +1 -0
- package/src/api/generated/ext_support/api-instance.ts +1 -0
- package/src/api/generated/ext_support/enums.ts +1 -0
- package/src/api/generated/ext_support/errors.ts +1 -0
- package/src/api/generated/ext_support/ext_support__support/index.ts +1 -0
- package/src/api/generated/ext_support/ext_support__support/models.ts +1 -0
- package/src/api/generated/ext_support/http.ts +1 -0
- package/src/api/generated/ext_support/index.ts +1 -0
- package/src/api/generated/ext_support/logger.ts +1 -0
- package/src/api/generated/ext_support/retry.ts +1 -0
- package/src/api/generated/ext_support/storage.ts +1 -0
- package/src/api/generated/ext_support/validation-events.ts +1 -0
- package/src/api/index.ts +2 -1
- package/src/config.ts +1 -0
- package/src/contexts/SupportContext.tsx +9 -13
- package/src/contexts/SupportExtensionProvider.tsx +1 -0
- package/src/layouts/SupportLayout/SupportLayout.tsx +5 -13
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +8 -18
- package/src/layouts/SupportLayout/components/MessageInput.tsx +3 -1
- package/src/layouts/SupportLayout/components/MessageList.tsx +25 -22
- package/src/layouts/SupportLayout/components/TicketCard.tsx +8 -9
- package/src/layouts/SupportLayout/components/TicketList.tsx +6 -4
- package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +9 -2
- package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +2 -0
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +2 -0
package/dist/config.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var import_ext_base = require("@djangocfg/ext-base");
|
|
|
27
27
|
// package.json
|
|
28
28
|
var package_default = {
|
|
29
29
|
name: "@djangocfg/ext-support",
|
|
30
|
-
version: "1.0.
|
|
30
|
+
version: "1.0.6",
|
|
31
31
|
description: "Support ticket system extension for DjangoCFG",
|
|
32
32
|
keywords: [
|
|
33
33
|
"django",
|
|
@@ -91,6 +91,7 @@ var package_default = {
|
|
|
91
91
|
"@djangocfg/ui-nextjs": "workspace:*",
|
|
92
92
|
consola: "^3.4.2",
|
|
93
93
|
"lucide-react": "^0.545.0",
|
|
94
|
+
moment: "^2.30.1",
|
|
94
95
|
next: "^15.5.7",
|
|
95
96
|
"p-retry": "^7.0.0",
|
|
96
97
|
react: "^18 || ^19",
|
|
@@ -105,6 +106,7 @@ var package_default = {
|
|
|
105
106
|
"@types/node": "^24.7.2",
|
|
106
107
|
"@types/react": "^19.0.0",
|
|
107
108
|
consola: "^3.4.2",
|
|
109
|
+
moment: "^2.30.1",
|
|
108
110
|
"p-retry": "^7.0.0",
|
|
109
111
|
swr: "^2.3.7",
|
|
110
112
|
tsup: "^8.5.0",
|
package/dist/config.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createExtensionConfig } from "@djangocfg/ext-base";
|
|
|
4
4
|
// package.json
|
|
5
5
|
var package_default = {
|
|
6
6
|
name: "@djangocfg/ext-support",
|
|
7
|
-
version: "1.0.
|
|
7
|
+
version: "1.0.6",
|
|
8
8
|
description: "Support ticket system extension for DjangoCFG",
|
|
9
9
|
keywords: [
|
|
10
10
|
"django",
|
|
@@ -68,6 +68,7 @@ var package_default = {
|
|
|
68
68
|
"@djangocfg/ui-nextjs": "workspace:*",
|
|
69
69
|
consola: "^3.4.2",
|
|
70
70
|
"lucide-react": "^0.545.0",
|
|
71
|
+
moment: "^2.30.1",
|
|
71
72
|
next: "^15.5.7",
|
|
72
73
|
"p-retry": "^7.0.0",
|
|
73
74
|
react: "^18 || ^19",
|
|
@@ -82,6 +83,7 @@ var package_default = {
|
|
|
82
83
|
"@types/node": "^24.7.2",
|
|
83
84
|
"@types/react": "^19.0.0",
|
|
84
85
|
consola: "^3.4.2",
|
|
86
|
+
moment: "^2.30.1",
|
|
85
87
|
"p-retry": "^7.0.0",
|
|
86
88
|
swr: "^2.3.7",
|
|
87
89
|
tsup: "^8.5.0",
|
package/dist/hooks.cjs
CHANGED
|
@@ -4,13 +4,14 @@ var consola = require('consola');
|
|
|
4
4
|
var pRetry = require('p-retry');
|
|
5
5
|
var zod = require('zod');
|
|
6
6
|
var api = require('@djangocfg/ext-base/api');
|
|
7
|
+
var lucideReact = require('lucide-react');
|
|
7
8
|
var React7 = require('react');
|
|
9
|
+
var uiNextjs = require('@djangocfg/ui-nextjs');
|
|
8
10
|
var useSWR = require('swr');
|
|
9
11
|
var jsxRuntime = require('react/jsx-runtime');
|
|
12
|
+
var moment2 = require('moment');
|
|
10
13
|
var auth = require('@djangocfg/api/auth');
|
|
11
14
|
var useSWRInfinite = require('swr/infinite');
|
|
12
|
-
var uiNextjs = require('@djangocfg/ui-nextjs');
|
|
13
|
-
var lucideReact = require('lucide-react');
|
|
14
15
|
var reactHookForm = require('react-hook-form');
|
|
15
16
|
var zod$1 = require('@hookform/resolvers/zod');
|
|
16
17
|
var extBase = require('@djangocfg/ext-base');
|
|
@@ -20,6 +21,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
20
21
|
var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
|
|
21
22
|
var React7__default = /*#__PURE__*/_interopDefault(React7);
|
|
22
23
|
var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
|
|
24
|
+
var moment2__default = /*#__PURE__*/_interopDefault(moment2);
|
|
23
25
|
var useSWRInfinite__default = /*#__PURE__*/_interopDefault(useSWRInfinite);
|
|
24
26
|
|
|
25
27
|
var __defProp = Object.defineProperty;
|
|
@@ -1784,6 +1786,67 @@ function useSupportContext() {
|
|
|
1784
1786
|
}
|
|
1785
1787
|
return context;
|
|
1786
1788
|
}
|
|
1789
|
+
var getStatusBadgeVariant = (status) => {
|
|
1790
|
+
switch (status) {
|
|
1791
|
+
case "open":
|
|
1792
|
+
return "default";
|
|
1793
|
+
case "waiting_for_user":
|
|
1794
|
+
return "secondary";
|
|
1795
|
+
case "waiting_for_admin":
|
|
1796
|
+
return "outline";
|
|
1797
|
+
case "resolved":
|
|
1798
|
+
return "outline";
|
|
1799
|
+
case "closed":
|
|
1800
|
+
return "secondary";
|
|
1801
|
+
default:
|
|
1802
|
+
return "default";
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
var formatRelativeTime = (date) => {
|
|
1806
|
+
if (!date) return "N/A";
|
|
1807
|
+
const m = moment2__default.default.utc(date).local();
|
|
1808
|
+
const now = moment2__default.default();
|
|
1809
|
+
const diffInSeconds = now.diff(m, "seconds");
|
|
1810
|
+
if (diffInSeconds < 60) return "Just now";
|
|
1811
|
+
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
1812
|
+
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
1813
|
+
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
1814
|
+
return m.format("MMM D, YYYY");
|
|
1815
|
+
};
|
|
1816
|
+
var TicketCard = ({ ticket, isSelected, onClick }) => {
|
|
1817
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1818
|
+
uiNextjs.Card,
|
|
1819
|
+
{
|
|
1820
|
+
className: uiNextjs.cn(
|
|
1821
|
+
"cursor-pointer transition-all duration-200 ease-out",
|
|
1822
|
+
"hover:bg-accent/50 hover:shadow-md hover:scale-[1.02]",
|
|
1823
|
+
"active:scale-[0.98]",
|
|
1824
|
+
isSelected && "bg-accent border-primary shadow-sm"
|
|
1825
|
+
),
|
|
1826
|
+
onClick,
|
|
1827
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(uiNextjs.CardContent, { className: "p-4", children: [
|
|
1828
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between mb-2", children: [
|
|
1829
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-sm line-clamp-2 flex-1", children: ticket.subject }),
|
|
1830
|
+
(ticket.unanswered_messages_count || 0) > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1831
|
+
uiNextjs.Badge,
|
|
1832
|
+
{
|
|
1833
|
+
variant: "destructive",
|
|
1834
|
+
className: "ml-2 shrink-0 animate-pulse",
|
|
1835
|
+
children: ticket.unanswered_messages_count
|
|
1836
|
+
}
|
|
1837
|
+
)
|
|
1838
|
+
] }),
|
|
1839
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1840
|
+
/* @__PURE__ */ jsxRuntime.jsx(uiNextjs.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: ticket.status || "open" }),
|
|
1841
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1842
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
|
|
1843
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatRelativeTime(ticket.created_at) })
|
|
1844
|
+
] })
|
|
1845
|
+
] }) })
|
|
1846
|
+
] })
|
|
1847
|
+
}
|
|
1848
|
+
);
|
|
1849
|
+
};
|
|
1787
1850
|
|
|
1788
1851
|
// src/layouts/SupportLayout/events.ts
|
|
1789
1852
|
var SUPPORT_LAYOUT_EVENTS = {
|
|
@@ -2048,71 +2111,6 @@ function useSupportLayoutContext() {
|
|
|
2048
2111
|
}
|
|
2049
2112
|
return context;
|
|
2050
2113
|
}
|
|
2051
|
-
var getStatusBadgeVariant = (status) => {
|
|
2052
|
-
switch (status) {
|
|
2053
|
-
case "open":
|
|
2054
|
-
return "default";
|
|
2055
|
-
case "waiting_for_user":
|
|
2056
|
-
return "secondary";
|
|
2057
|
-
case "waiting_for_admin":
|
|
2058
|
-
return "outline";
|
|
2059
|
-
case "resolved":
|
|
2060
|
-
return "outline";
|
|
2061
|
-
case "closed":
|
|
2062
|
-
return "secondary";
|
|
2063
|
-
default:
|
|
2064
|
-
return "default";
|
|
2065
|
-
}
|
|
2066
|
-
};
|
|
2067
|
-
var formatRelativeTime = (date) => {
|
|
2068
|
-
if (!date) return "N/A";
|
|
2069
|
-
const now = /* @__PURE__ */ new Date();
|
|
2070
|
-
const messageDate = new Date(date);
|
|
2071
|
-
const diffInSeconds = Math.floor((now.getTime() - messageDate.getTime()) / 1e3);
|
|
2072
|
-
if (diffInSeconds < 60) return "Just now";
|
|
2073
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
2074
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
2075
|
-
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
2076
|
-
return new Date(date).toLocaleDateString("en-US", {
|
|
2077
|
-
year: "numeric",
|
|
2078
|
-
month: "short",
|
|
2079
|
-
day: "numeric"
|
|
2080
|
-
});
|
|
2081
|
-
};
|
|
2082
|
-
var TicketCard = ({ ticket, isSelected, onClick }) => {
|
|
2083
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2084
|
-
uiNextjs.Card,
|
|
2085
|
-
{
|
|
2086
|
-
className: uiNextjs.cn(
|
|
2087
|
-
"cursor-pointer transition-all duration-200 ease-out",
|
|
2088
|
-
"hover:bg-accent/50 hover:shadow-md hover:scale-[1.02]",
|
|
2089
|
-
"active:scale-[0.98]",
|
|
2090
|
-
isSelected && "bg-accent border-primary shadow-sm"
|
|
2091
|
-
),
|
|
2092
|
-
onClick,
|
|
2093
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(uiNextjs.CardContent, { className: "p-4", children: [
|
|
2094
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between mb-2", children: [
|
|
2095
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-sm line-clamp-2 flex-1", children: ticket.subject }),
|
|
2096
|
-
(ticket.unanswered_messages_count || 0) > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2097
|
-
uiNextjs.Badge,
|
|
2098
|
-
{
|
|
2099
|
-
variant: "destructive",
|
|
2100
|
-
className: "ml-2 shrink-0 animate-pulse",
|
|
2101
|
-
children: ticket.unanswered_messages_count
|
|
2102
|
-
}
|
|
2103
|
-
)
|
|
2104
|
-
] }),
|
|
2105
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2106
|
-
/* @__PURE__ */ jsxRuntime.jsx(uiNextjs.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: ticket.status || "open" }),
|
|
2107
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2108
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
|
|
2109
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatRelativeTime(ticket.created_at) })
|
|
2110
|
-
] })
|
|
2111
|
-
] }) })
|
|
2112
|
-
] })
|
|
2113
|
-
}
|
|
2114
|
-
);
|
|
2115
|
-
};
|
|
2116
2114
|
var TicketList = () => {
|
|
2117
2115
|
const { selectedTicket, selectTicket } = useSupportLayoutContext();
|
|
2118
2116
|
const {
|
|
@@ -2218,28 +2216,23 @@ var TicketList = () => {
|
|
|
2218
2216
|
};
|
|
2219
2217
|
var formatTime = (date) => {
|
|
2220
2218
|
if (!date) return "";
|
|
2221
|
-
return
|
|
2222
|
-
hour: "2-digit",
|
|
2223
|
-
minute: "2-digit"
|
|
2224
|
-
});
|
|
2219
|
+
return moment2__default.default.utc(date).local().format("hh:mm A");
|
|
2225
2220
|
};
|
|
2226
2221
|
var formatDate = (date) => {
|
|
2227
2222
|
if (!date) return "";
|
|
2228
|
-
return
|
|
2229
|
-
year: "numeric",
|
|
2230
|
-
month: "short",
|
|
2231
|
-
day: "numeric"
|
|
2232
|
-
});
|
|
2223
|
+
return moment2__default.default.utc(date).local().format("MMM D, YYYY");
|
|
2233
2224
|
};
|
|
2234
2225
|
var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
2235
2226
|
const sender = message.sender;
|
|
2227
|
+
const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
|
|
2228
|
+
const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
|
|
2236
2229
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2237
2230
|
"div",
|
|
2238
2231
|
{
|
|
2239
2232
|
className: `flex gap-3 ${isFromUser ? "justify-end" : "justify-start"}
|
|
2240
2233
|
animate-in fade-in slide-in-from-bottom-2 duration-300`,
|
|
2241
2234
|
children: [
|
|
2242
|
-
!isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarImage, { src: sender.avatar, alt: sender.display_username || "Support" }) : /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Headphones, { className: "h-4 w-4" }) :
|
|
2235
|
+
!isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarImage, { src: sender.avatar, alt: sender.display_username || "Support" }) : /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Headphones, { className: "h-4 w-4" }) : senderInitial }) }),
|
|
2243
2236
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col gap-1 flex-1 max-w-[80%] ${isFromUser ? "items-end" : "items-start"}`, children: [
|
|
2244
2237
|
!isFromUser && sender && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground px-1", children: [
|
|
2245
2238
|
sender.display_username || sender.email || "Support Team",
|
|
@@ -2254,7 +2247,7 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
|
2254
2247
|
),
|
|
2255
2248
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground px-1", children: formatTime(message.created_at) })
|
|
2256
2249
|
] }),
|
|
2257
|
-
isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.Avatar, { className: "h-8 w-8 shrink-0", children: currentUser?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarImage, { src: currentUser.avatar, alt: currentUser.display_username || currentUser.email || "You" }) : /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarFallback, { className: "bg-primary/10 text-primary font-semibold", children:
|
|
2250
|
+
isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.Avatar, { className: "h-8 w-8 shrink-0", children: currentUser?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarImage, { src: currentUser.avatar, alt: currentUser.display_username || currentUser.email || "You" }) : /* @__PURE__ */ jsxRuntime.jsx(uiNextjs.AvatarFallback, { className: "bg-primary/10 text-primary font-semibold", children: userInitial || /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4" }) }) })
|
|
2258
2251
|
]
|
|
2259
2252
|
}
|
|
2260
2253
|
);
|
|
@@ -2372,7 +2365,7 @@ var MessageList = () => {
|
|
|
2372
2365
|
messages.map((message, index) => {
|
|
2373
2366
|
const isFromUser = message.sender?.id && user?.id && String(message.sender.id) === String(user.id) || message.sender?.email && user?.email && message.sender.email === user.email || message.is_from_author && selectedTicket?.user && user?.id && String(selectedTicket.user) === String(user.id);
|
|
2374
2367
|
const previousMessage = index > 0 ? messages[index - 1] : null;
|
|
2375
|
-
const showDateSeparator = previousMessage &&
|
|
2368
|
+
const showDateSeparator = previousMessage && moment2__default.default.utc(previousMessage.created_at || "").format("YYYY-MM-DD") !== moment2__default.default.utc(message.created_at || "").format("YYYY-MM-DD");
|
|
2376
2369
|
return /* @__PURE__ */ jsxRuntime.jsxs(React7__default.default.Fragment, { children: [
|
|
2377
2370
|
showDateSeparator && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 my-4", children: [
|
|
2378
2371
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
|
|
@@ -2640,7 +2633,7 @@ var SupportLayout = () => {
|
|
|
2640
2633
|
// package.json
|
|
2641
2634
|
var package_default = {
|
|
2642
2635
|
name: "@djangocfg/ext-support",
|
|
2643
|
-
version: "1.0.
|
|
2636
|
+
version: "1.0.6",
|
|
2644
2637
|
description: "Support ticket system extension for DjangoCFG",
|
|
2645
2638
|
keywords: [
|
|
2646
2639
|
"django",
|
|
@@ -2704,6 +2697,7 @@ var package_default = {
|
|
|
2704
2697
|
"@djangocfg/ui-nextjs": "workspace:*",
|
|
2705
2698
|
consola: "^3.4.2",
|
|
2706
2699
|
"lucide-react": "^0.545.0",
|
|
2700
|
+
moment: "^2.30.1",
|
|
2707
2701
|
next: "^15.5.7",
|
|
2708
2702
|
"p-retry": "^7.0.0",
|
|
2709
2703
|
react: "^18 || ^19",
|
|
@@ -2718,6 +2712,7 @@ var package_default = {
|
|
|
2718
2712
|
"@types/node": "^24.7.2",
|
|
2719
2713
|
"@types/react": "^19.0.0",
|
|
2720
2714
|
consola: "^3.4.2",
|
|
2715
|
+
moment: "^2.30.1",
|
|
2721
2716
|
"p-retry": "^7.0.0",
|
|
2722
2717
|
swr: "^2.3.7",
|
|
2723
2718
|
tsup: "^8.5.0",
|
package/dist/hooks.js
CHANGED
|
@@ -2,13 +2,14 @@ import { createConsola, consola } from 'consola';
|
|
|
2
2
|
import pRetry, { AbortError } from 'p-retry';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
5
|
+
import { Clock, MessageSquare, Loader2, Send, Plus, Headphones, User, ArrowLeft, LifeBuoy } from 'lucide-react';
|
|
5
6
|
import React7, { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';
|
|
7
|
+
import { Card, cn, CardContent, Badge, Skeleton, ScrollArea, Button, useToast, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormMessage, Avatar, AvatarImage, AvatarFallback, ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@djangocfg/ui-nextjs';
|
|
6
8
|
import useSWR, { useSWRConfig } from 'swr';
|
|
7
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
|
+
import moment2 from 'moment';
|
|
8
11
|
import { useAuth } from '@djangocfg/api/auth';
|
|
9
12
|
import useSWRInfinite from 'swr/infinite';
|
|
10
|
-
import { Card, cn, CardContent, Badge, Skeleton, ScrollArea, Button, useToast, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormMessage, Avatar, AvatarImage, AvatarFallback, ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@djangocfg/ui-nextjs';
|
|
11
|
-
import { Clock, MessageSquare, Loader2, Send, Plus, Headphones, User, ArrowLeft, LifeBuoy } from 'lucide-react';
|
|
12
13
|
import { useForm } from 'react-hook-form';
|
|
13
14
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
14
15
|
import { createExtensionConfig } from '@djangocfg/ext-base';
|
|
@@ -1775,6 +1776,67 @@ function useSupportContext() {
|
|
|
1775
1776
|
}
|
|
1776
1777
|
return context;
|
|
1777
1778
|
}
|
|
1779
|
+
var getStatusBadgeVariant = (status) => {
|
|
1780
|
+
switch (status) {
|
|
1781
|
+
case "open":
|
|
1782
|
+
return "default";
|
|
1783
|
+
case "waiting_for_user":
|
|
1784
|
+
return "secondary";
|
|
1785
|
+
case "waiting_for_admin":
|
|
1786
|
+
return "outline";
|
|
1787
|
+
case "resolved":
|
|
1788
|
+
return "outline";
|
|
1789
|
+
case "closed":
|
|
1790
|
+
return "secondary";
|
|
1791
|
+
default:
|
|
1792
|
+
return "default";
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
var formatRelativeTime = (date) => {
|
|
1796
|
+
if (!date) return "N/A";
|
|
1797
|
+
const m = moment2.utc(date).local();
|
|
1798
|
+
const now = moment2();
|
|
1799
|
+
const diffInSeconds = now.diff(m, "seconds");
|
|
1800
|
+
if (diffInSeconds < 60) return "Just now";
|
|
1801
|
+
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
1802
|
+
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
1803
|
+
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
1804
|
+
return m.format("MMM D, YYYY");
|
|
1805
|
+
};
|
|
1806
|
+
var TicketCard = ({ ticket, isSelected, onClick }) => {
|
|
1807
|
+
return /* @__PURE__ */ jsx(
|
|
1808
|
+
Card,
|
|
1809
|
+
{
|
|
1810
|
+
className: cn(
|
|
1811
|
+
"cursor-pointer transition-all duration-200 ease-out",
|
|
1812
|
+
"hover:bg-accent/50 hover:shadow-md hover:scale-[1.02]",
|
|
1813
|
+
"active:scale-[0.98]",
|
|
1814
|
+
isSelected && "bg-accent border-primary shadow-sm"
|
|
1815
|
+
),
|
|
1816
|
+
onClick,
|
|
1817
|
+
children: /* @__PURE__ */ jsxs(CardContent, { className: "p-4", children: [
|
|
1818
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-2", children: [
|
|
1819
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-sm line-clamp-2 flex-1", children: ticket.subject }),
|
|
1820
|
+
(ticket.unanswered_messages_count || 0) > 0 && /* @__PURE__ */ jsx(
|
|
1821
|
+
Badge,
|
|
1822
|
+
{
|
|
1823
|
+
variant: "destructive",
|
|
1824
|
+
className: "ml-2 shrink-0 animate-pulse",
|
|
1825
|
+
children: ticket.unanswered_messages_count
|
|
1826
|
+
}
|
|
1827
|
+
)
|
|
1828
|
+
] }),
|
|
1829
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1830
|
+
/* @__PURE__ */ jsx(Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: ticket.status || "open" }),
|
|
1831
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1832
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
|
|
1833
|
+
/* @__PURE__ */ jsx("span", { children: formatRelativeTime(ticket.created_at) })
|
|
1834
|
+
] })
|
|
1835
|
+
] }) })
|
|
1836
|
+
] })
|
|
1837
|
+
}
|
|
1838
|
+
);
|
|
1839
|
+
};
|
|
1778
1840
|
|
|
1779
1841
|
// src/layouts/SupportLayout/events.ts
|
|
1780
1842
|
var SUPPORT_LAYOUT_EVENTS = {
|
|
@@ -2039,71 +2101,6 @@ function useSupportLayoutContext() {
|
|
|
2039
2101
|
}
|
|
2040
2102
|
return context;
|
|
2041
2103
|
}
|
|
2042
|
-
var getStatusBadgeVariant = (status) => {
|
|
2043
|
-
switch (status) {
|
|
2044
|
-
case "open":
|
|
2045
|
-
return "default";
|
|
2046
|
-
case "waiting_for_user":
|
|
2047
|
-
return "secondary";
|
|
2048
|
-
case "waiting_for_admin":
|
|
2049
|
-
return "outline";
|
|
2050
|
-
case "resolved":
|
|
2051
|
-
return "outline";
|
|
2052
|
-
case "closed":
|
|
2053
|
-
return "secondary";
|
|
2054
|
-
default:
|
|
2055
|
-
return "default";
|
|
2056
|
-
}
|
|
2057
|
-
};
|
|
2058
|
-
var formatRelativeTime = (date) => {
|
|
2059
|
-
if (!date) return "N/A";
|
|
2060
|
-
const now = /* @__PURE__ */ new Date();
|
|
2061
|
-
const messageDate = new Date(date);
|
|
2062
|
-
const diffInSeconds = Math.floor((now.getTime() - messageDate.getTime()) / 1e3);
|
|
2063
|
-
if (diffInSeconds < 60) return "Just now";
|
|
2064
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
2065
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
2066
|
-
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
2067
|
-
return new Date(date).toLocaleDateString("en-US", {
|
|
2068
|
-
year: "numeric",
|
|
2069
|
-
month: "short",
|
|
2070
|
-
day: "numeric"
|
|
2071
|
-
});
|
|
2072
|
-
};
|
|
2073
|
-
var TicketCard = ({ ticket, isSelected, onClick }) => {
|
|
2074
|
-
return /* @__PURE__ */ jsx(
|
|
2075
|
-
Card,
|
|
2076
|
-
{
|
|
2077
|
-
className: cn(
|
|
2078
|
-
"cursor-pointer transition-all duration-200 ease-out",
|
|
2079
|
-
"hover:bg-accent/50 hover:shadow-md hover:scale-[1.02]",
|
|
2080
|
-
"active:scale-[0.98]",
|
|
2081
|
-
isSelected && "bg-accent border-primary shadow-sm"
|
|
2082
|
-
),
|
|
2083
|
-
onClick,
|
|
2084
|
-
children: /* @__PURE__ */ jsxs(CardContent, { className: "p-4", children: [
|
|
2085
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-2", children: [
|
|
2086
|
-
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-sm line-clamp-2 flex-1", children: ticket.subject }),
|
|
2087
|
-
(ticket.unanswered_messages_count || 0) > 0 && /* @__PURE__ */ jsx(
|
|
2088
|
-
Badge,
|
|
2089
|
-
{
|
|
2090
|
-
variant: "destructive",
|
|
2091
|
-
className: "ml-2 shrink-0 animate-pulse",
|
|
2092
|
-
children: ticket.unanswered_messages_count
|
|
2093
|
-
}
|
|
2094
|
-
)
|
|
2095
|
-
] }),
|
|
2096
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2097
|
-
/* @__PURE__ */ jsx(Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: ticket.status || "open" }),
|
|
2098
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2099
|
-
/* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
|
|
2100
|
-
/* @__PURE__ */ jsx("span", { children: formatRelativeTime(ticket.created_at) })
|
|
2101
|
-
] })
|
|
2102
|
-
] }) })
|
|
2103
|
-
] })
|
|
2104
|
-
}
|
|
2105
|
-
);
|
|
2106
|
-
};
|
|
2107
2104
|
var TicketList = () => {
|
|
2108
2105
|
const { selectedTicket, selectTicket } = useSupportLayoutContext();
|
|
2109
2106
|
const {
|
|
@@ -2209,28 +2206,23 @@ var TicketList = () => {
|
|
|
2209
2206
|
};
|
|
2210
2207
|
var formatTime = (date) => {
|
|
2211
2208
|
if (!date) return "";
|
|
2212
|
-
return
|
|
2213
|
-
hour: "2-digit",
|
|
2214
|
-
minute: "2-digit"
|
|
2215
|
-
});
|
|
2209
|
+
return moment2.utc(date).local().format("hh:mm A");
|
|
2216
2210
|
};
|
|
2217
2211
|
var formatDate = (date) => {
|
|
2218
2212
|
if (!date) return "";
|
|
2219
|
-
return
|
|
2220
|
-
year: "numeric",
|
|
2221
|
-
month: "short",
|
|
2222
|
-
day: "numeric"
|
|
2223
|
-
});
|
|
2213
|
+
return moment2.utc(date).local().format("MMM D, YYYY");
|
|
2224
2214
|
};
|
|
2225
2215
|
var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
2226
2216
|
const sender = message.sender;
|
|
2217
|
+
const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
|
|
2218
|
+
const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
|
|
2227
2219
|
return /* @__PURE__ */ jsxs(
|
|
2228
2220
|
"div",
|
|
2229
2221
|
{
|
|
2230
2222
|
className: `flex gap-3 ${isFromUser ? "justify-end" : "justify-start"}
|
|
2231
2223
|
animate-in fade-in slide-in-from-bottom-2 duration-300`,
|
|
2232
2224
|
children: [
|
|
2233
|
-
!isFromUser && /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsx(AvatarImage, { src: sender.avatar, alt: sender.display_username || "Support" }) : /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsx(Headphones, { className: "h-4 w-4" }) :
|
|
2225
|
+
!isFromUser && /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsx(AvatarImage, { src: sender.avatar, alt: sender.display_username || "Support" }) : /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsx(Headphones, { className: "h-4 w-4" }) : senderInitial }) }),
|
|
2234
2226
|
/* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-1 flex-1 max-w-[80%] ${isFromUser ? "items-end" : "items-start"}`, children: [
|
|
2235
2227
|
!isFromUser && sender && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground px-1", children: [
|
|
2236
2228
|
sender.display_username || sender.email || "Support Team",
|
|
@@ -2245,7 +2237,7 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
|
2245
2237
|
),
|
|
2246
2238
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground px-1", children: formatTime(message.created_at) })
|
|
2247
2239
|
] }),
|
|
2248
|
-
isFromUser && /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: currentUser?.avatar ? /* @__PURE__ */ jsx(AvatarImage, { src: currentUser.avatar, alt: currentUser.display_username || currentUser.email || "You" }) : /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/10 text-primary font-semibold", children:
|
|
2240
|
+
isFromUser && /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: currentUser?.avatar ? /* @__PURE__ */ jsx(AvatarImage, { src: currentUser.avatar, alt: currentUser.display_username || currentUser.email || "You" }) : /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/10 text-primary font-semibold", children: userInitial || /* @__PURE__ */ jsx(User, { className: "h-4 w-4" }) }) })
|
|
2249
2241
|
]
|
|
2250
2242
|
}
|
|
2251
2243
|
);
|
|
@@ -2363,7 +2355,7 @@ var MessageList = () => {
|
|
|
2363
2355
|
messages.map((message, index) => {
|
|
2364
2356
|
const isFromUser = message.sender?.id && user?.id && String(message.sender.id) === String(user.id) || message.sender?.email && user?.email && message.sender.email === user.email || message.is_from_author && selectedTicket?.user && user?.id && String(selectedTicket.user) === String(user.id);
|
|
2365
2357
|
const previousMessage = index > 0 ? messages[index - 1] : null;
|
|
2366
|
-
const showDateSeparator = previousMessage &&
|
|
2358
|
+
const showDateSeparator = previousMessage && moment2.utc(previousMessage.created_at || "").format("YYYY-MM-DD") !== moment2.utc(message.created_at || "").format("YYYY-MM-DD");
|
|
2367
2359
|
return /* @__PURE__ */ jsxs(React7.Fragment, { children: [
|
|
2368
2360
|
showDateSeparator && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 my-4", children: [
|
|
2369
2361
|
/* @__PURE__ */ jsx("div", { className: "flex-1 h-px bg-border" }),
|
|
@@ -2631,7 +2623,7 @@ var SupportLayout = () => {
|
|
|
2631
2623
|
// package.json
|
|
2632
2624
|
var package_default = {
|
|
2633
2625
|
name: "@djangocfg/ext-support",
|
|
2634
|
-
version: "1.0.
|
|
2626
|
+
version: "1.0.6",
|
|
2635
2627
|
description: "Support ticket system extension for DjangoCFG",
|
|
2636
2628
|
keywords: [
|
|
2637
2629
|
"django",
|
|
@@ -2695,6 +2687,7 @@ var package_default = {
|
|
|
2695
2687
|
"@djangocfg/ui-nextjs": "workspace:*",
|
|
2696
2688
|
consola: "^3.4.2",
|
|
2697
2689
|
"lucide-react": "^0.545.0",
|
|
2690
|
+
moment: "^2.30.1",
|
|
2698
2691
|
next: "^15.5.7",
|
|
2699
2692
|
"p-retry": "^7.0.0",
|
|
2700
2693
|
react: "^18 || ^19",
|
|
@@ -2709,6 +2702,7 @@ var package_default = {
|
|
|
2709
2702
|
"@types/node": "^24.7.2",
|
|
2710
2703
|
"@types/react": "^19.0.0",
|
|
2711
2704
|
consola: "^3.4.2",
|
|
2705
|
+
moment: "^2.30.1",
|
|
2712
2706
|
"p-retry": "^7.0.0",
|
|
2713
2707
|
swr: "^2.3.7",
|
|
2714
2708
|
tsup: "^8.5.0",
|