@djangocfg/ext-support 1.0.6 → 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 +2 -0
- package/dist/config.js +2 -0
- package/dist/hooks.cjs +74 -79
- package/dist/hooks.js +73 -79
- package/dist/index.cjs +74 -79
- package/dist/index.d.cts +0 -97
- package/dist/index.d.ts +0 -97
- package/dist/index.js +73 -79
- 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 -8
- package/src/api/generated/ext_support/_utils/fetchers/index.ts +1 -8
- package/src/api/generated/ext_support/_utils/hooks/ext_support__support.ts +1 -8
- package/src/api/generated/ext_support/_utils/hooks/index.ts +1 -8
- package/src/api/generated/ext_support/_utils/schemas/index.ts +1 -8
- package/src/api/generated/ext_support/api-instance.ts +1 -8
- package/src/api/generated/ext_support/enums.ts +1 -8
- package/src/api/generated/ext_support/errors.ts +1 -8
- package/src/api/generated/ext_support/ext_support__support/index.ts +1 -8
- package/src/api/generated/ext_support/ext_support__support/models.ts +1 -8
- package/src/api/generated/ext_support/http.ts +1 -8
- package/src/api/generated/ext_support/index.ts +1 -8
- package/src/api/generated/ext_support/logger.ts +1 -8
- package/src/api/generated/ext_support/retry.ts +1 -8
- package/src/api/generated/ext_support/storage.ts +1 -8
- package/src/api/generated/ext_support/validation-events.ts +1 -8
- 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/index.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 { ArrowLeft, LifeBuoy, Plus, MessageSquare, Loader2, Send, Headphones, User, Clock } from 'lucide-react';
|
|
5
6
|
import React7, { createContext, useState, useCallback, useEffect, useContext, useRef } from 'react';
|
|
7
|
+
import { Button, ResizablePanelGroup, ResizablePanel, ResizableHandle, Skeleton, ScrollArea, useToast, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormMessage, Avatar, AvatarImage, AvatarFallback, Card, CardContent, cn, Badge } 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 { Button, ResizablePanelGroup, ResizablePanel, ResizableHandle, Skeleton, ScrollArea, useToast, Textarea, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormMessage, Avatar, AvatarImage, AvatarFallback, Card, CardContent, cn, Badge } from '@djangocfg/ui-nextjs';
|
|
11
|
-
import { ArrowLeft, LifeBuoy, Plus, MessageSquare, Loader2, Send, Headphones, User, Clock } 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" }),
|
|
@@ -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",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ext-support",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Support ticket system extension for DjangoCFG",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"django",
|
|
@@ -59,11 +59,12 @@
|
|
|
59
59
|
"check": "tsc --noEmit"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@djangocfg/api": "^2.1.
|
|
63
|
-
"@djangocfg/ext-base": "^1.0.
|
|
64
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
62
|
+
"@djangocfg/api": "^2.1.43",
|
|
63
|
+
"@djangocfg/ext-base": "^1.0.7",
|
|
64
|
+
"@djangocfg/ui-nextjs": "^2.1.43",
|
|
65
65
|
"consola": "^3.4.2",
|
|
66
66
|
"lucide-react": "^0.545.0",
|
|
67
|
+
"moment": "^2.30.1",
|
|
67
68
|
"next": "^15.5.7",
|
|
68
69
|
"p-retry": "^7.0.0",
|
|
69
70
|
"react": "^18 || ^19",
|
|
@@ -72,12 +73,13 @@
|
|
|
72
73
|
"zod": "^4.1.13"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
|
-
"@djangocfg/api": "^2.1.
|
|
76
|
-
"@djangocfg/ext-base": "^1.0.
|
|
77
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
76
|
+
"@djangocfg/api": "^2.1.43",
|
|
77
|
+
"@djangocfg/ext-base": "^1.0.7",
|
|
78
|
+
"@djangocfg/typescript-config": "^2.1.43",
|
|
78
79
|
"@types/node": "^24.7.2",
|
|
79
80
|
"@types/react": "^19.0.0",
|
|
80
81
|
"consola": "^3.4.2",
|
|
82
|
+
"moment": "^2.30.1",
|
|
81
83
|
"p-retry": "^7.0.0",
|
|
82
84
|
"swr": "^2.3.7",
|
|
83
85
|
"tsup": "^8.5.0",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Django CFG API - Typescript Client
|
|
2
|
+
|
|
3
|
+
Auto-generated. **Do not edit manually.**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python manage.py generate_client --groups ext_support --typescript
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Stats
|
|
10
|
+
|
|
11
|
+
| | |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Version | 3.0.3 |
|
|
14
|
+
| Operations | 12 |
|
|
15
|
+
| Schemas | 11 |
|
|
16
|
+
|
|
17
|
+
## Resources
|
|
18
|
+
|
|
19
|
+
- **support** (12 ops)
|
|
20
|
+
|
|
21
|
+
## Operations
|
|
22
|
+
|
|
23
|
+
**support:**
|
|
24
|
+
- `POST` /cfg/support/tickets/ → `cfg_support_tickets_create`
|
|
25
|
+
- `DELETE` /cfg/support/tickets/{uuid}/ → `cfg_support_tickets_destroy`
|
|
26
|
+
- `GET` /cfg/support/tickets/ → `cfg_support_tickets_list`
|
|
27
|
+
- `POST` /cfg/support/tickets/{ticket_uuid}/messages/ → `cfg_support_tickets_messages_create`
|
|
28
|
+
- `DELETE` /cfg/support/tickets/{ticket_uuid}/messages/{uuid}/ → `cfg_support_tickets_messages_destroy`
|
|
29
|
+
- `GET` /cfg/support/tickets/{ticket_uuid}/messages/ → `cfg_support_tickets_messages_list`
|
|
30
|
+
- `PATCH` /cfg/support/tickets/{ticket_uuid}/messages/{uuid}/ → `cfg_support_tickets_messages_partial_update`
|
|
31
|
+
- `GET` /cfg/support/tickets/{ticket_uuid}/messages/{uuid}/ → `cfg_support_tickets_messages_retrieve`
|
|
32
|
+
- `PUT` /cfg/support/tickets/{ticket_uuid}/messages/{uuid}/ → `cfg_support_tickets_messages_update`
|
|
33
|
+
- `PATCH` /cfg/support/tickets/{uuid}/ → `cfg_support_tickets_partial_update`
|
|
34
|
+
- `GET` /cfg/support/tickets/{uuid}/ → `cfg_support_tickets_retrieve`
|
|
35
|
+
- `PUT` /cfg/support/tickets/{uuid}/ → `cfg_support_tickets_update`
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { APIClient } from './';
|
|
41
|
+
|
|
42
|
+
const client = new APIClient({ baseUrl, token });
|
|
43
|
+
|
|
44
|
+
await client.support.list();
|
|
45
|
+
await client.support.retrieve({ id: 1 });
|
|
46
|
+
await client.support.create({ ... });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**SWR Hooks:**
|
|
50
|
+
```typescript
|
|
51
|
+
import { useSupportList } from './hooks';
|
|
52
|
+
const { data, isLoading } = useSupportList();
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How It Works
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
DRF ViewSets → drf-spectacular → OpenAPI → IR Parser → Generator → This Client
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Configuration** (`api/config.py`):
|
|
62
|
+
```python
|
|
63
|
+
openapi_client = OpenAPIClientConfig(
|
|
64
|
+
enabled=True,
|
|
65
|
+
groups=[OpenAPIGroupConfig(name="ext_support", apps=["..."])],
|
|
66
|
+
generate_zod_schemas=True, # → schemas.ts
|
|
67
|
+
generate_fetchers=True, # → fetchers.ts
|
|
68
|
+
generate_swr_hooks=True, # → hooks.ts
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Copy to Next.js** (if `nextjs_admin` configured):
|
|
73
|
+
```python
|
|
74
|
+
nextjs_admin = NextJsAdminConfig(
|
|
75
|
+
project_path="../frontend/apps/...",
|
|
76
|
+
api_output_path="app/_lib/api/generated",
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
@see https://djangocfg.com/docs/features/api-generation
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Typed fetchers for Support
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Typed Fetchers - Universal API functions
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
'use client';
|
|
10
3
|
|
|
11
4
|
/**
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
'use client';
|
|
10
3
|
|
|
11
4
|
/**
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Zod Schemas - Runtime validation and type inference
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Global API Instance - Singleton configuration
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* * `open` - Open
|
|
11
4
|
* * `waiting_for_user` - Waiting for User
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* API Error Classes
|
|
11
4
|
*
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
export * from "./client";
|
|
10
3
|
export * as Models from "./models";
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
import * as Enums from "../enums";
|
|
10
3
|
|
|
11
4
|
/**
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* HTTP Client Adapter Pattern
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Django CFG API - API Client with JWT Management
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* API Logger with Consola
|
|
11
4
|
* Beautiful console logging for API requests and responses
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Retry Configuration and Utilities
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Storage adapters for cross-platform token storage.
|
|
11
4
|
*
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* AUTO-GENERATED FILE - DO NOT EDIT
|
|
3
|
-
*
|
|
4
|
-
* This file is automatically generated by DjangoCFG.
|
|
5
|
-
* Any manual changes will be lost when the code is regenerated.
|
|
6
|
-
*
|
|
7
|
-
* @see https://djangocfg.com/docs/features/modules/django-client/overview/
|
|
8
|
-
*/
|
|
1
|
+
// Auto-generated by DjangoCFG - see CLAUDE.md
|
|
9
2
|
/**
|
|
10
3
|
* Zod Validation Events - Browser CustomEvent integration
|
|
11
4
|
*
|
package/src/api/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Support Extension API
|
|
3
5
|
*
|
|
4
6
|
* Pre-configured API instance with shared authentication
|
|
5
7
|
*/
|
|
6
8
|
import { API } from './generated/ext_support';
|
|
7
|
-
import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
8
9
|
|
|
9
10
|
export const apiSupport = createExtensionAPI(API);
|
package/src/config.ts
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext,
|
|
3
|
+
import React, { createContext, ReactNode, useContext } from 'react';
|
|
4
|
+
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
useSupportTicketsMessagesList,
|
|
12
|
-
useSupportTicketsMessagesRetrieve,
|
|
13
|
-
useCreateSupportTicketsMessagesCreate,
|
|
14
|
-
useUpdateSupportTicketsMessagesUpdate,
|
|
15
|
-
usePartialUpdateSupportTicketsMessagesPartialUpdate,
|
|
16
|
-
useDeleteSupportTicketsMessagesDestroy,
|
|
6
|
+
useCreateSupportTicketsCreate, useCreateSupportTicketsMessagesCreate,
|
|
7
|
+
useDeleteSupportTicketsDestroy, useDeleteSupportTicketsMessagesDestroy,
|
|
8
|
+
usePartialUpdateSupportTicketsMessagesPartialUpdate,
|
|
9
|
+
usePartialUpdateSupportTicketsPartialUpdate, useSupportTicketsList,
|
|
10
|
+
useSupportTicketsMessagesList, useSupportTicketsMessagesRetrieve, useSupportTicketsRetrieve,
|
|
11
|
+
useUpdateSupportTicketsMessagesUpdate, useUpdateSupportTicketsUpdate
|
|
17
12
|
} from '../api/generated/ext_support/_utils/hooks';
|
|
13
|
+
|
|
18
14
|
import type {
|
|
19
15
|
Ticket,
|
|
20
16
|
TicketRequest,
|
|
@@ -6,22 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
'use client';
|
|
8
8
|
|
|
9
|
+
import { ArrowLeft, LifeBuoy, Plus } from 'lucide-react';
|
|
9
10
|
import React from 'react';
|
|
11
|
+
|
|
12
|
+
import { Button, ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@djangocfg/ui-nextjs';
|
|
13
|
+
|
|
10
14
|
import { SupportProvider } from '../../contexts/SupportContext';
|
|
15
|
+
import { CreateTicketDialog, MessageInput, MessageList, TicketList } from './components';
|
|
11
16
|
import { SupportLayoutProvider, useSupportLayoutContext } from './context';
|
|
12
|
-
import {
|
|
13
|
-
TicketList,
|
|
14
|
-
MessageList,
|
|
15
|
-
MessageInput,
|
|
16
|
-
CreateTicketDialog,
|
|
17
|
-
} from './components';
|
|
18
|
-
import {
|
|
19
|
-
Button,
|
|
20
|
-
ResizablePanelGroup,
|
|
21
|
-
ResizablePanel,
|
|
22
|
-
ResizableHandle,
|
|
23
|
-
} from '@djangocfg/ui-nextjs';
|
|
24
|
-
import { Plus, LifeBuoy, ArrowLeft } from 'lucide-react';
|
|
25
17
|
|
|
26
18
|
// ─────────────────────────────────────────────────────────────────────────
|
|
27
19
|
// Support Layout Content (with context)
|