@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/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)
|
|
@@ -6,30 +6,20 @@
|
|
|
6
6
|
|
|
7
7
|
'use client';
|
|
8
8
|
|
|
9
|
+
import { Loader2, Plus } from 'lucide-react';
|
|
9
10
|
import React from 'react';
|
|
11
|
+
import { useForm } from 'react-hook-form';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
|
|
10
14
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
DialogHeader,
|
|
14
|
-
DialogTitle,
|
|
15
|
-
DialogDescription,
|
|
16
|
-
Button,
|
|
17
|
-
Form,
|
|
18
|
-
FormField,
|
|
19
|
-
FormItem,
|
|
20
|
-
FormLabel,
|
|
21
|
-
FormControl,
|
|
22
|
-
FormMessage,
|
|
23
|
-
Input,
|
|
24
|
-
Textarea,
|
|
15
|
+
Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Form, FormControl,
|
|
16
|
+
FormField, FormItem, FormLabel, FormMessage, Input, Textarea, useToast
|
|
25
17
|
} from '@djangocfg/ui-nextjs';
|
|
26
|
-
import { useForm } from 'react-hook-form';
|
|
27
18
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
28
|
-
|
|
29
|
-
import { Plus, Loader2 } from 'lucide-react';
|
|
19
|
+
|
|
30
20
|
import { supportLogger } from '../../../utils/logger';
|
|
31
21
|
import { useSupportLayoutContext } from '../context';
|
|
32
|
-
|
|
22
|
+
|
|
33
23
|
import type { TicketFormData } from '../types';
|
|
34
24
|
|
|
35
25
|
const createTicketSchema = z.object({
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
+
import { Send } from 'lucide-react';
|
|
8
9
|
import React, { useState } from 'react';
|
|
10
|
+
|
|
9
11
|
import { Button, Textarea, useToast } from '@djangocfg/ui-nextjs';
|
|
10
|
-
|
|
12
|
+
|
|
11
13
|
import { supportLogger } from '../../../utils/logger';
|
|
12
14
|
import { useSupportLayoutContext } from '../context';
|
|
13
15
|
|
|
@@ -5,29 +5,28 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
8
|
+
import { Headphones, Loader2, MessageSquare, User } from 'lucide-react';
|
|
9
|
+
import moment from 'moment';
|
|
10
|
+
import React, { useCallback, useEffect, useRef } from 'react';
|
|
11
|
+
|
|
12
|
+
import { useAuth } from '@djangocfg/api/auth';
|
|
13
|
+
import {
|
|
14
|
+
Avatar, AvatarFallback, AvatarImage, Button, Card, CardContent, ScrollArea, Skeleton
|
|
15
|
+
} from '@djangocfg/ui-nextjs';
|
|
16
|
+
|
|
11
17
|
import { useSupportLayoutContext } from '../context';
|
|
12
18
|
import { useInfiniteMessages } from '../hooks';
|
|
13
|
-
|
|
19
|
+
|
|
14
20
|
import type { Message } from '../../../api/generated/ext_support/_utils/schemas';
|
|
15
21
|
|
|
16
22
|
const formatTime = (date: string | null | undefined): string => {
|
|
17
23
|
if (!date) return '';
|
|
18
|
-
return
|
|
19
|
-
hour: '2-digit',
|
|
20
|
-
minute: '2-digit',
|
|
21
|
-
});
|
|
24
|
+
return moment.utc(date).local().format('hh:mm A');
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
const formatDate = (date: string | null | undefined): string => {
|
|
25
28
|
if (!date) return '';
|
|
26
|
-
return
|
|
27
|
-
year: 'numeric',
|
|
28
|
-
month: 'short',
|
|
29
|
-
day: 'numeric',
|
|
30
|
-
});
|
|
29
|
+
return moment.utc(date).local().format('MMM D, YYYY');
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
interface MessageBubbleProps {
|
|
@@ -39,6 +38,15 @@ interface MessageBubbleProps {
|
|
|
39
38
|
const MessageBubble: React.FC<MessageBubbleProps> = ({ message, isFromUser, currentUser }) => {
|
|
40
39
|
const sender = message.sender;
|
|
41
40
|
|
|
41
|
+
// Pre-compute initials
|
|
42
|
+
const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase()
|
|
43
|
+
|| sender?.initials
|
|
44
|
+
|| 'S';
|
|
45
|
+
const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase()
|
|
46
|
+
|| currentUser?.email?.charAt(0)?.toUpperCase()
|
|
47
|
+
|| currentUser?.initials
|
|
48
|
+
|| null;
|
|
49
|
+
|
|
42
50
|
return (
|
|
43
51
|
<div
|
|
44
52
|
className={`flex gap-3 ${isFromUser ? 'justify-end' : 'justify-start'}
|
|
@@ -54,9 +62,7 @@ const MessageBubble: React.FC<MessageBubbleProps> = ({ message, isFromUser, curr
|
|
|
54
62
|
{sender?.is_staff ? (
|
|
55
63
|
<Headphones className="h-4 w-4" />
|
|
56
64
|
) : (
|
|
57
|
-
|
|
58
|
-
sender?.initials ||
|
|
59
|
-
'S'
|
|
65
|
+
senderInitial
|
|
60
66
|
)}
|
|
61
67
|
</AvatarFallback>
|
|
62
68
|
)}
|
|
@@ -101,10 +107,7 @@ const MessageBubble: React.FC<MessageBubbleProps> = ({ message, isFromUser, curr
|
|
|
101
107
|
<AvatarImage src={currentUser.avatar} alt={currentUser.display_username || currentUser.email || 'You'} />
|
|
102
108
|
) : (
|
|
103
109
|
<AvatarFallback className="bg-primary/10 text-primary font-semibold">
|
|
104
|
-
{
|
|
105
|
-
currentUser?.email?.charAt(0)?.toUpperCase() ||
|
|
106
|
-
currentUser?.initials ||
|
|
107
|
-
<User className="h-4 w-4" />}
|
|
110
|
+
{userInitial || <User className="h-4 w-4" />}
|
|
108
111
|
</AvatarFallback>
|
|
109
112
|
)}
|
|
110
113
|
</Avatar>
|
|
@@ -282,8 +285,8 @@ export const MessageList: React.FC = () => {
|
|
|
282
285
|
// Show date separator if date changes
|
|
283
286
|
const previousMessage = index > 0 ? messages[index - 1] : null;
|
|
284
287
|
const showDateSeparator = previousMessage &&
|
|
285
|
-
|
|
286
|
-
|
|
288
|
+
moment.utc(previousMessage.created_at || '').format('YYYY-MM-DD') !==
|
|
289
|
+
moment.utc(message.created_at || '').format('YYYY-MM-DD');
|
|
287
290
|
|
|
288
291
|
return (
|
|
289
292
|
<React.Fragment key={message.uuid}>
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
+
import { Clock, MessageSquare } from 'lucide-react';
|
|
9
|
+
import moment from 'moment';
|
|
8
10
|
import React from 'react';
|
|
11
|
+
|
|
9
12
|
import { Badge, Card, CardContent, cn } from '@djangocfg/ui-nextjs';
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
import type { Ticket } from '../../../api/generated/ext_support/_utils/schemas';
|
|
12
15
|
|
|
13
16
|
interface TicketCardProps {
|
|
@@ -38,20 +41,16 @@ const getStatusBadgeVariant = (
|
|
|
38
41
|
const formatRelativeTime = (date: string | null | undefined): string => {
|
|
39
42
|
if (!date) return 'N/A';
|
|
40
43
|
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const diffInSeconds =
|
|
44
|
+
const m = moment.utc(date).local();
|
|
45
|
+
const now = moment();
|
|
46
|
+
const diffInSeconds = now.diff(m, 'seconds');
|
|
44
47
|
|
|
45
48
|
if (diffInSeconds < 60) return 'Just now';
|
|
46
49
|
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
47
50
|
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
48
51
|
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
49
52
|
|
|
50
|
-
return
|
|
51
|
-
year: 'numeric',
|
|
52
|
-
month: 'short',
|
|
53
|
-
day: 'numeric',
|
|
54
|
-
});
|
|
53
|
+
return m.format('MMM D, YYYY');
|
|
55
54
|
};
|
|
56
55
|
|
|
57
56
|
export const TicketCard: React.FC<TicketCardProps> = ({ ticket, isSelected, onClick }) => {
|
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
+
import { Loader2, MessageSquare } from 'lucide-react';
|
|
8
9
|
import React, { useEffect, useRef } from 'react';
|
|
9
|
-
|
|
10
|
-
import {
|
|
10
|
+
|
|
11
|
+
import { Button, ScrollArea, Skeleton } from '@djangocfg/ui-nextjs';
|
|
12
|
+
|
|
11
13
|
import { useSupportLayoutContext } from '../context';
|
|
12
|
-
import { MessageSquare, Loader2 } from 'lucide-react';
|
|
13
|
-
import { useInfiniteTickets } from '../hooks';
|
|
14
14
|
import { SUPPORT_LAYOUT_EVENTS } from '../events';
|
|
15
|
+
import { useInfiniteTickets } from '../hooks';
|
|
16
|
+
import { TicketCard } from './TicketCard';
|
|
15
17
|
|
|
16
18
|
export const TicketList: React.FC = () => {
|
|
17
19
|
const { selectedTicket, selectTicket } = useSupportLayoutContext();
|
|
@@ -5,11 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
-
import React, {
|
|
9
|
-
|
|
8
|
+
import React, {
|
|
9
|
+
createContext, ReactNode, useCallback, useContext, useEffect, useState
|
|
10
|
+
} from 'react';
|
|
11
|
+
|
|
10
12
|
import { useAuth } from '@djangocfg/api/auth';
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
Message, MessageCreateRequest, Ticket, useSupportContext
|
|
16
|
+
} from '../../../contexts/SupportContext';
|
|
11
17
|
import { SUPPORT_LAYOUT_EVENTS } from '../events';
|
|
12
18
|
import { useInfiniteMessages } from '../hooks';
|
|
19
|
+
|
|
13
20
|
import type { SupportUIState, TicketFormData } from '../types';
|
|
14
21
|
|
|
15
22
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import useSWRInfinite from 'swr/infinite';
|
|
7
|
+
|
|
7
8
|
import * as Fetchers from '../../../api/generated/ext_support/_utils/fetchers';
|
|
9
|
+
|
|
8
10
|
import type { PaginatedMessageList, Message } from '../../../api/generated/ext_support/_utils/schemas';
|
|
9
11
|
|
|
10
12
|
const PAGE_SIZE = 20;
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import useSWRInfinite from 'swr/infinite';
|
|
7
|
+
|
|
7
8
|
import * as Fetchers from '../../../api/generated/ext_support/_utils/fetchers';
|
|
9
|
+
|
|
8
10
|
import type { PaginatedTicketList, Ticket } from '../../../api/generated/ext_support/_utils/schemas';
|
|
9
11
|
|
|
10
12
|
const PAGE_SIZE = 20;
|