@djangocfg/ext-support 1.0.21 → 1.0.23
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 +11 -2
- package/dist/config.js +11 -2
- package/dist/hooks.cjs +399 -92
- package/dist/hooks.js +371 -64
- package/dist/i18n.cjs +266 -0
- package/dist/i18n.d.cts +112 -0
- package/dist/i18n.d.ts +112 -0
- package/dist/i18n.js +238 -0
- package/dist/index.cjs +399 -92
- package/dist/index.js +371 -64
- package/package.json +18 -9
- package/src/i18n/index.ts +26 -0
- package/src/i18n/locales/en.ts +76 -0
- package/src/i18n/locales/ko.ts +76 -0
- package/src/i18n/locales/ru.ts +76 -0
- package/src/i18n/types.ts +105 -0
- package/src/i18n/useSupportT.ts +60 -0
- package/src/layouts/SupportLayout/SupportLayout.tsx +16 -7
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +39 -17
- package/src/layouts/SupportLayout/components/MessageInput.tsx +17 -11
- package/src/layouts/SupportLayout/components/MessageList.tsx +29 -11
- package/src/layouts/SupportLayout/components/TicketCard.tsx +34 -14
- package/src/layouts/SupportLayout/components/TicketList.tsx +16 -6
package/dist/hooks.js
CHANGED
|
@@ -3,7 +3,8 @@ import pRetry, { AbortError } from 'p-retry';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { initializeExtensionAPI, createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
5
5
|
import { Clock, MessageSquare, Loader2, Send, Plus, Headphones, User, ArrowLeft, LifeBuoy } from 'lucide-react';
|
|
6
|
-
import
|
|
6
|
+
import React8, { createContext, useContext, useMemo, useCallback, useState, useEffect, useRef } from 'react';
|
|
7
|
+
import { useLocale } from 'next-intl';
|
|
7
8
|
import { Card, CardContent, Badge, Skeleton, ScrollArea, Button, Textarea, useToast as useToast$1, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Form, FormField, FormItem, FormLabel, FormControl, Input, FormMessage, Avatar, AvatarImage, AvatarFallback, ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@djangocfg/ui-core';
|
|
8
9
|
import useSWR, { useSWRConfig } from 'swr';
|
|
9
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -1615,6 +1616,233 @@ var API = class {
|
|
|
1615
1616
|
};
|
|
1616
1617
|
initializeExtensionAPI(configureAPI);
|
|
1617
1618
|
var apiSupport = createExtensionAPI(API);
|
|
1619
|
+
|
|
1620
|
+
// src/i18n/locales/en.ts
|
|
1621
|
+
var en = {
|
|
1622
|
+
layout: {
|
|
1623
|
+
title: "Support Center",
|
|
1624
|
+
titleShort: "Support",
|
|
1625
|
+
subtitle: "Get help from our support team",
|
|
1626
|
+
newTicket: "New Ticket"
|
|
1627
|
+
},
|
|
1628
|
+
status: {
|
|
1629
|
+
open: "Open",
|
|
1630
|
+
waitingForUser: "Waiting for you",
|
|
1631
|
+
waitingForAdmin: "Waiting for support",
|
|
1632
|
+
resolved: "Resolved",
|
|
1633
|
+
closed: "Closed"
|
|
1634
|
+
},
|
|
1635
|
+
ticketList: {
|
|
1636
|
+
noTickets: "No tickets yet",
|
|
1637
|
+
noTicketsDescription: "Create your first support ticket to get help",
|
|
1638
|
+
loadingMore: "Loading more...",
|
|
1639
|
+
loadMore: "Load more",
|
|
1640
|
+
allLoaded: "All {count} tickets loaded"
|
|
1641
|
+
},
|
|
1642
|
+
createTicket: {
|
|
1643
|
+
title: "New Support Ticket",
|
|
1644
|
+
description: "Describe your issue and we will help you",
|
|
1645
|
+
subjectLabel: "Subject",
|
|
1646
|
+
subjectPlaceholder: "Brief description of your issue",
|
|
1647
|
+
messageLabel: "Message",
|
|
1648
|
+
messagePlaceholder: "Describe your issue in detail...",
|
|
1649
|
+
cancel: "Cancel",
|
|
1650
|
+
creating: "Creating...",
|
|
1651
|
+
create: "Create Ticket"
|
|
1652
|
+
},
|
|
1653
|
+
validation: {
|
|
1654
|
+
subjectRequired: "Subject is required",
|
|
1655
|
+
subjectTooLong: "Subject is too long (max 200 characters)",
|
|
1656
|
+
messageRequired: "Message is required",
|
|
1657
|
+
messageTooLong: "Message is too long (max 5000 characters)"
|
|
1658
|
+
},
|
|
1659
|
+
messages: {
|
|
1660
|
+
ticketCreated: "Ticket created successfully",
|
|
1661
|
+
ticketCreateFailed: "Failed to create ticket",
|
|
1662
|
+
messageSent: "Message sent",
|
|
1663
|
+
messageSendFailed: "Failed to send message"
|
|
1664
|
+
},
|
|
1665
|
+
messageInput: {
|
|
1666
|
+
placeholder: "Type your message...",
|
|
1667
|
+
ticketClosed: "Ticket closed",
|
|
1668
|
+
ticketClosedDescription: "This ticket has been closed. Create a new ticket if you need further assistance."
|
|
1669
|
+
},
|
|
1670
|
+
messageList: {
|
|
1671
|
+
noTicketSelected: "No ticket selected",
|
|
1672
|
+
noTicketSelectedDescription: "Select a ticket from the list to view messages",
|
|
1673
|
+
noMessages: "No messages yet",
|
|
1674
|
+
noMessagesDescription: "Start the conversation by sending a message",
|
|
1675
|
+
loadingOlder: "Loading older messages...",
|
|
1676
|
+
loadOlder: "Load older messages",
|
|
1677
|
+
supportTeam: "Support Team",
|
|
1678
|
+
staff: "Staff"
|
|
1679
|
+
},
|
|
1680
|
+
time: {
|
|
1681
|
+
justNow: "Just now",
|
|
1682
|
+
minutesAgo: "{count} min ago",
|
|
1683
|
+
hoursAgo: "{count}h ago",
|
|
1684
|
+
daysAgo: "{count}d ago"
|
|
1685
|
+
}
|
|
1686
|
+
};
|
|
1687
|
+
|
|
1688
|
+
// src/i18n/locales/ru.ts
|
|
1689
|
+
var ru = {
|
|
1690
|
+
layout: {
|
|
1691
|
+
title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
|
|
1692
|
+
titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
|
|
1693
|
+
subtitle: "\u041F\u043E\u043B\u0443\u0447\u0438\u0442\u0435 \u043F\u043E\u043C\u043E\u0449\u044C \u043E\u0442 \u043D\u0430\u0448\u0435\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
|
|
1694
|
+
newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
|
|
1695
|
+
},
|
|
1696
|
+
status: {
|
|
1697
|
+
open: "\u041E\u0442\u043A\u0440\u044B\u0442",
|
|
1698
|
+
waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
|
|
1699
|
+
waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
|
|
1700
|
+
resolved: "\u0420\u0435\u0448\u0451\u043D",
|
|
1701
|
+
closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
|
|
1702
|
+
},
|
|
1703
|
+
ticketList: {
|
|
1704
|
+
noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
|
|
1705
|
+
noTicketsDescription: "\u0421\u043E\u0437\u0434\u0430\u0439\u0442\u0435 \u043F\u0435\u0440\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043F\u043E\u043C\u043E\u0449\u044C",
|
|
1706
|
+
loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
|
|
1707
|
+
loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
|
|
1708
|
+
allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
|
|
1709
|
+
},
|
|
1710
|
+
createTicket: {
|
|
1711
|
+
title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
|
|
1712
|
+
description: "\u041E\u043F\u0438\u0448\u0438\u0442\u0435 \u0432\u0430\u0448\u0443 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0443, \u0438 \u043C\u044B \u043F\u043E\u043C\u043E\u0436\u0435\u043C \u0432\u0430\u043C",
|
|
1713
|
+
subjectLabel: "\u0422\u0435\u043C\u0430",
|
|
1714
|
+
subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
|
|
1715
|
+
messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
|
|
1716
|
+
messagePlaceholder: "\u041E\u043F\u0438\u0448\u0438\u0442\u0435 \u0432\u0430\u0448\u0443 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u0443 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E...",
|
|
1717
|
+
cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
|
|
1718
|
+
creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
|
|
1719
|
+
create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
|
|
1720
|
+
},
|
|
1721
|
+
validation: {
|
|
1722
|
+
subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
|
|
1723
|
+
subjectTooLong: "\u0422\u0435\u043C\u0430 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0434\u043B\u0438\u043D\u043D\u0430\u044F (\u043C\u0430\u043A\u0441. 200 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432)",
|
|
1724
|
+
messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
|
|
1725
|
+
messageTooLong: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0434\u043B\u0438\u043D\u043D\u043E\u0435 (\u043C\u0430\u043A\u0441. 5000 \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432)"
|
|
1726
|
+
},
|
|
1727
|
+
messages: {
|
|
1728
|
+
ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
|
|
1729
|
+
ticketCreateFailed: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
|
|
1730
|
+
messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
|
|
1731
|
+
messageSendFailed: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435"
|
|
1732
|
+
},
|
|
1733
|
+
messageInput: {
|
|
1734
|
+
placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
|
|
1735
|
+
ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
|
|
1736
|
+
ticketClosedDescription: "\u042D\u0442\u043E \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E. \u0421\u043E\u0437\u0434\u0430\u0439\u0442\u0435 \u043D\u043E\u0432\u043E\u0435, \u0435\u0441\u043B\u0438 \u0432\u0430\u043C \u043D\u0443\u0436\u043D\u0430 \u043F\u043E\u043C\u043E\u0449\u044C."
|
|
1737
|
+
},
|
|
1738
|
+
messageList: {
|
|
1739
|
+
noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
|
|
1740
|
+
noTicketSelectedDescription: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430 \u0434\u043B\u044F \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
|
|
1741
|
+
noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
|
|
1742
|
+
noMessagesDescription: "\u041D\u0430\u0447\u043D\u0438\u0442\u0435 \u0434\u0438\u0430\u043B\u043E\u0433, \u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
|
|
1743
|
+
loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
|
|
1744
|
+
loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
|
|
1745
|
+
supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
|
|
1746
|
+
staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
|
|
1747
|
+
},
|
|
1748
|
+
time: {
|
|
1749
|
+
justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
|
|
1750
|
+
minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
|
|
1751
|
+
hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
|
|
1752
|
+
daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
// src/i18n/locales/ko.ts
|
|
1757
|
+
var ko = {
|
|
1758
|
+
layout: {
|
|
1759
|
+
title: "\uACE0\uAC1D\uC9C0\uC6D0 \uC13C\uD130",
|
|
1760
|
+
titleShort: "\uACE0\uAC1D\uC9C0\uC6D0",
|
|
1761
|
+
subtitle: "\uACE0\uAC1D\uC9C0\uC6D0\uD300\uC758 \uB3C4\uC6C0\uC744 \uBC1B\uC73C\uC138\uC694",
|
|
1762
|
+
newTicket: "\uC0C8 \uBB38\uC758"
|
|
1763
|
+
},
|
|
1764
|
+
status: {
|
|
1765
|
+
open: "\uC5F4\uB9BC",
|
|
1766
|
+
waitingForUser: "\uD68C\uC2E0 \uB300\uAE30",
|
|
1767
|
+
waitingForAdmin: "\uC9C0\uC6D0\uD300 \uB300\uAE30",
|
|
1768
|
+
resolved: "\uD574\uACB0\uB428",
|
|
1769
|
+
closed: "\uC885\uB8CC"
|
|
1770
|
+
},
|
|
1771
|
+
ticketList: {
|
|
1772
|
+
noTickets: "\uBB38\uC758 \uB0B4\uC5ED \uC5C6\uC74C",
|
|
1773
|
+
noTicketsDescription: "\uCCAB \uBC88\uC9F8 \uBB38\uC758\uB97C \uB4F1\uB85D\uD558\uC5EC \uB3C4\uC6C0\uC744 \uBC1B\uC73C\uC138\uC694",
|
|
1774
|
+
loadingMore: "\uBD88\uB7EC\uC624\uB294 \uC911...",
|
|
1775
|
+
loadMore: "\uB354 \uBCF4\uAE30",
|
|
1776
|
+
allLoaded: "\uCD1D {count}\uAC1C\uC758 \uBB38\uC758\uAC00 \uB85C\uB4DC\uB428"
|
|
1777
|
+
},
|
|
1778
|
+
createTicket: {
|
|
1779
|
+
title: "\uC0C8 \uBB38\uC758",
|
|
1780
|
+
description: "\uBB38\uC81C\uB97C \uC124\uBA85\uD574 \uC8FC\uC2DC\uBA74 \uB3C4\uC6C0\uC744 \uB4DC\uB9AC\uACA0\uC2B5\uB2C8\uB2E4",
|
|
1781
|
+
subjectLabel: "\uC81C\uBAA9",
|
|
1782
|
+
subjectPlaceholder: "\uBB38\uC81C\uC5D0 \uB300\uD55C \uAC04\uB7B5\uD55C \uC124\uBA85",
|
|
1783
|
+
messageLabel: "\uB0B4\uC6A9",
|
|
1784
|
+
messagePlaceholder: "\uBB38\uC81C\uB97C \uC790\uC138\uD788 \uC124\uBA85\uD574 \uC8FC\uC138\uC694...",
|
|
1785
|
+
cancel: "\uCDE8\uC18C",
|
|
1786
|
+
creating: "\uC0DD\uC131 \uC911...",
|
|
1787
|
+
create: "\uBB38\uC758 \uB4F1\uB85D"
|
|
1788
|
+
},
|
|
1789
|
+
validation: {
|
|
1790
|
+
subjectRequired: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694",
|
|
1791
|
+
subjectTooLong: "\uC81C\uBAA9\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4 (\uCD5C\uB300 200\uC790)",
|
|
1792
|
+
messageRequired: "\uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694",
|
|
1793
|
+
messageTooLong: "\uB0B4\uC6A9\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4 (\uCD5C\uB300 5000\uC790)"
|
|
1794
|
+
},
|
|
1795
|
+
messages: {
|
|
1796
|
+
ticketCreated: "\uBB38\uC758\uAC00 \uB4F1\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
1797
|
+
ticketCreateFailed: "\uBB38\uC758 \uB4F1\uB85D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
|
|
1798
|
+
messageSent: "\uBA54\uC2DC\uC9C0\uAC00 \uC804\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
|
|
1799
|
+
messageSendFailed: "\uBA54\uC2DC\uC9C0 \uC804\uC1A1\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4"
|
|
1800
|
+
},
|
|
1801
|
+
messageInput: {
|
|
1802
|
+
placeholder: "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694...",
|
|
1803
|
+
ticketClosed: "\uBB38\uC758 \uC885\uB8CC\uB428",
|
|
1804
|
+
ticketClosedDescription: "\uC774 \uBB38\uC758\uB294 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uCD94\uAC00 \uB3C4\uC6C0\uC774 \uD544\uC694\uD558\uC2DC\uBA74 \uC0C8 \uBB38\uC758\uB97C \uB4F1\uB85D\uD574 \uC8FC\uC138\uC694."
|
|
1805
|
+
},
|
|
1806
|
+
messageList: {
|
|
1807
|
+
noTicketSelected: "\uBB38\uC758\uAC00 \uC120\uD0DD\uB418\uC9C0 \uC54A\uC74C",
|
|
1808
|
+
noTicketSelectedDescription: "\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB824\uBA74 \uBAA9\uB85D\uC5D0\uC11C \uBB38\uC758\uB97C \uC120\uD0DD\uD558\uC138\uC694",
|
|
1809
|
+
noMessages: "\uBA54\uC2DC\uC9C0 \uC5C6\uC74C",
|
|
1810
|
+
noMessagesDescription: "\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB0B4 \uB300\uD654\uB97C \uC2DC\uC791\uD558\uC138\uC694",
|
|
1811
|
+
loadingOlder: "\uC774\uC804 \uBA54\uC2DC\uC9C0 \uBD88\uB7EC\uC624\uB294 \uC911...",
|
|
1812
|
+
loadOlder: "\uC774\uC804 \uBA54\uC2DC\uC9C0 \uBCF4\uAE30",
|
|
1813
|
+
supportTeam: "\uACE0\uAC1D\uC9C0\uC6D0\uD300",
|
|
1814
|
+
staff: "\uB2F4\uB2F9\uC790"
|
|
1815
|
+
},
|
|
1816
|
+
time: {
|
|
1817
|
+
justNow: "\uBC29\uAE08 \uC804",
|
|
1818
|
+
minutesAgo: "{count}\uBD84 \uC804",
|
|
1819
|
+
hoursAgo: "{count}\uC2DC\uAC04 \uC804",
|
|
1820
|
+
daysAgo: "{count}\uC77C \uC804"
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
// src/i18n/useSupportT.ts
|
|
1825
|
+
var translations = { en, ru, ko };
|
|
1826
|
+
function getNestedValue(obj, path) {
|
|
1827
|
+
const keys = path.split(".");
|
|
1828
|
+
let result = obj;
|
|
1829
|
+
for (const key of keys) {
|
|
1830
|
+
if (result && typeof result === "object" && key in result) {
|
|
1831
|
+
result = result[key];
|
|
1832
|
+
} else {
|
|
1833
|
+
return path;
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
return typeof result === "string" ? result : path;
|
|
1837
|
+
}
|
|
1838
|
+
function useSupportT() {
|
|
1839
|
+
const locale = useLocale();
|
|
1840
|
+
const t = useMemo(() => translations[locale] || translations.en, [locale]);
|
|
1841
|
+
return useCallback(
|
|
1842
|
+
(key) => getNestedValue(t, key),
|
|
1843
|
+
[t]
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1618
1846
|
function useSupportTicketsList(params, client) {
|
|
1619
1847
|
return useSWR(
|
|
1620
1848
|
params ? ["cfg-support-tickets", params] : "cfg-support-tickets",
|
|
@@ -1829,18 +2057,35 @@ var getStatusBadgeVariant = (status) => {
|
|
|
1829
2057
|
return "default";
|
|
1830
2058
|
}
|
|
1831
2059
|
};
|
|
1832
|
-
var formatRelativeTime = (date) => {
|
|
1833
|
-
if (!date) return "N/A";
|
|
1834
|
-
const m = moment2.utc(date).local();
|
|
1835
|
-
const now = moment2();
|
|
1836
|
-
const diffInSeconds = now.diff(m, "seconds");
|
|
1837
|
-
if (diffInSeconds < 60) return "Just now";
|
|
1838
|
-
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
|
|
1839
|
-
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
|
|
1840
|
-
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
|
|
1841
|
-
return m.format("MMM D, YYYY");
|
|
1842
|
-
};
|
|
1843
2060
|
var TicketCard = ({ ticket, isSelected, onClick }) => {
|
|
2061
|
+
const st = useSupportT();
|
|
2062
|
+
const labels = useMemo(() => ({
|
|
2063
|
+
status: {
|
|
2064
|
+
open: st("status.open"),
|
|
2065
|
+
waiting_for_user: st("status.waitingForUser"),
|
|
2066
|
+
waiting_for_admin: st("status.waitingForAdmin"),
|
|
2067
|
+
resolved: st("status.resolved"),
|
|
2068
|
+
closed: st("status.closed")
|
|
2069
|
+
},
|
|
2070
|
+
time: {
|
|
2071
|
+
justNow: st("time.justNow"),
|
|
2072
|
+
minutesAgo: st("time.minutesAgo"),
|
|
2073
|
+
hoursAgo: st("time.hoursAgo"),
|
|
2074
|
+
daysAgo: st("time.daysAgo")
|
|
2075
|
+
}
|
|
2076
|
+
}), [st]);
|
|
2077
|
+
const formatRelativeTime = useCallback((date) => {
|
|
2078
|
+
if (!date) return "N/A";
|
|
2079
|
+
const m = moment2.utc(date).local();
|
|
2080
|
+
const now = moment2();
|
|
2081
|
+
const diffInSeconds = now.diff(m, "seconds");
|
|
2082
|
+
if (diffInSeconds < 60) return labels.time.justNow;
|
|
2083
|
+
if (diffInSeconds < 3600) return labels.time.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
|
|
2084
|
+
if (diffInSeconds < 86400) return labels.time.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
|
|
2085
|
+
if (diffInSeconds < 604800) return labels.time.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
|
|
2086
|
+
return m.format("MMM D, YYYY");
|
|
2087
|
+
}, [labels.time]);
|
|
2088
|
+
const statusLabel = labels.status[ticket.status] || ticket.status || labels.status.open;
|
|
1844
2089
|
return /* @__PURE__ */ jsx(
|
|
1845
2090
|
Card,
|
|
1846
2091
|
{
|
|
@@ -1864,7 +2109,7 @@ var TicketCard = ({ ticket, isSelected, onClick }) => {
|
|
|
1864
2109
|
)
|
|
1865
2110
|
] }),
|
|
1866
2111
|
/* @__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: [
|
|
1867
|
-
/* @__PURE__ */ jsx(Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children:
|
|
2112
|
+
/* @__PURE__ */ jsx(Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: statusLabel }),
|
|
1868
2113
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1869
2114
|
/* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
|
|
1870
2115
|
/* @__PURE__ */ jsx("span", { children: formatRelativeTime(ticket.created_at) })
|
|
@@ -2139,6 +2384,7 @@ function useSupportLayoutContext() {
|
|
|
2139
2384
|
return context;
|
|
2140
2385
|
}
|
|
2141
2386
|
var TicketList = () => {
|
|
2387
|
+
const st = useSupportT();
|
|
2142
2388
|
const { selectedTicket, selectTicket } = useSupportLayoutContext();
|
|
2143
2389
|
const {
|
|
2144
2390
|
tickets,
|
|
@@ -2149,6 +2395,13 @@ var TicketList = () => {
|
|
|
2149
2395
|
totalCount,
|
|
2150
2396
|
refresh
|
|
2151
2397
|
} = useInfiniteTickets();
|
|
2398
|
+
const labels = useMemo(() => ({
|
|
2399
|
+
noTickets: st("ticketList.noTickets"),
|
|
2400
|
+
noTicketsDescription: st("ticketList.noTicketsDescription"),
|
|
2401
|
+
loadingMore: st("ticketList.loadingMore"),
|
|
2402
|
+
loadMore: st("ticketList.loadMore"),
|
|
2403
|
+
allLoaded: st("ticketList.allLoaded")
|
|
2404
|
+
}), [st]);
|
|
2152
2405
|
const scrollRef = useRef(null);
|
|
2153
2406
|
const observerRef = useRef(null);
|
|
2154
2407
|
const loadMoreRef = useRef(null);
|
|
@@ -2194,8 +2447,8 @@ var TicketList = () => {
|
|
|
2194
2447
|
if (!tickets || tickets.length === 0) {
|
|
2195
2448
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full p-8 text-center animate-in fade-in zoom-in-95 duration-300", children: [
|
|
2196
2449
|
/* @__PURE__ */ jsx(MessageSquare, { className: "h-16 w-16 text-muted-foreground mb-4 animate-bounce" }),
|
|
2197
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children:
|
|
2198
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children:
|
|
2450
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTickets }),
|
|
2451
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketsDescription })
|
|
2199
2452
|
] });
|
|
2200
2453
|
}
|
|
2201
2454
|
return /* @__PURE__ */ jsx(ScrollArea, { className: "h-full", viewportRef: scrollRef, children: /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-2", children: [
|
|
@@ -2218,7 +2471,7 @@ var TicketList = () => {
|
|
|
2218
2471
|
/* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "h-2" }),
|
|
2219
2472
|
isLoadingMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
2220
2473
|
/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
2221
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm", children:
|
|
2474
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: labels.loadingMore })
|
|
2222
2475
|
] }) }),
|
|
2223
2476
|
hasMore && !isLoadingMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxs(
|
|
2224
2477
|
Button,
|
|
@@ -2228,17 +2481,14 @@ var TicketList = () => {
|
|
|
2228
2481
|
onClick: loadMore,
|
|
2229
2482
|
className: "text-xs",
|
|
2230
2483
|
children: [
|
|
2231
|
-
|
|
2484
|
+
labels.loadMore,
|
|
2485
|
+
" (",
|
|
2232
2486
|
totalCount > 0 ? `${tickets.length}/${totalCount}` : "",
|
|
2233
2487
|
")"
|
|
2234
2488
|
]
|
|
2235
2489
|
}
|
|
2236
2490
|
) }),
|
|
2237
|
-
!hasMore && tickets.length > 0 && /* @__PURE__ */
|
|
2238
|
-
"All ",
|
|
2239
|
-
totalCount,
|
|
2240
|
-
" tickets loaded"
|
|
2241
|
-
] })
|
|
2491
|
+
!hasMore && tickets.length > 0 && /* @__PURE__ */ jsx("div", { className: "text-center py-4 text-sm text-muted-foreground", children: labels.allLoaded.replace("{count}", String(totalCount)) })
|
|
2242
2492
|
] }) });
|
|
2243
2493
|
};
|
|
2244
2494
|
var formatTime = (date) => {
|
|
@@ -2249,7 +2499,7 @@ var formatDate = (date) => {
|
|
|
2249
2499
|
if (!date) return "";
|
|
2250
2500
|
return moment2.utc(date).local().format("MMM D, YYYY");
|
|
2251
2501
|
};
|
|
2252
|
-
var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
2502
|
+
var MessageBubble = ({ message, isFromUser, currentUser, labels }) => {
|
|
2253
2503
|
const sender = message.sender;
|
|
2254
2504
|
const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
|
|
2255
2505
|
const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
|
|
@@ -2259,11 +2509,11 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
|
2259
2509
|
className: `flex gap-3 ${isFromUser ? "justify-end" : "justify-start"}
|
|
2260
2510
|
animate-in fade-in slide-in-from-bottom-2 duration-300`,
|
|
2261
2511
|
children: [
|
|
2262
|
-
!isFromUser && /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsx(AvatarImage, { src: sender.avatar, alt: sender.display_username ||
|
|
2512
|
+
!isFromUser && /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsx(AvatarImage, { src: sender.avatar, alt: sender.display_username || labels.supportTeam }) : /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsx(Headphones, { className: "h-4 w-4" }) : senderInitial }) }),
|
|
2263
2513
|
/* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-1 flex-1 max-w-[80%] ${isFromUser ? "items-end" : "items-start"}`, children: [
|
|
2264
2514
|
!isFromUser && sender && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground px-1", children: [
|
|
2265
|
-
sender.display_username || sender.email ||
|
|
2266
|
-
sender.is_staff &&
|
|
2515
|
+
sender.display_username || sender.email || labels.supportTeam,
|
|
2516
|
+
sender.is_staff && ` (${labels.staff})`
|
|
2267
2517
|
] }),
|
|
2268
2518
|
/* @__PURE__ */ jsx(
|
|
2269
2519
|
Card,
|
|
@@ -2280,8 +2530,19 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
|
|
|
2280
2530
|
);
|
|
2281
2531
|
};
|
|
2282
2532
|
var MessageList = () => {
|
|
2533
|
+
const st = useSupportT();
|
|
2283
2534
|
const { selectedTicket } = useSupportLayoutContext();
|
|
2284
2535
|
const { user } = useAuth();
|
|
2536
|
+
const labels = useMemo(() => ({
|
|
2537
|
+
noTicketSelected: st("messageList.noTicketSelected"),
|
|
2538
|
+
noTicketSelectedDescription: st("messageList.noTicketSelectedDescription"),
|
|
2539
|
+
noMessages: st("messageList.noMessages"),
|
|
2540
|
+
noMessagesDescription: st("messageList.noMessagesDescription"),
|
|
2541
|
+
loadingOlder: st("messageList.loadingOlder"),
|
|
2542
|
+
loadOlder: st("messageList.loadOlder"),
|
|
2543
|
+
supportTeam: st("messageList.supportTeam"),
|
|
2544
|
+
staff: st("messageList.staff")
|
|
2545
|
+
}), [st]);
|
|
2285
2546
|
const {
|
|
2286
2547
|
messages,
|
|
2287
2548
|
isLoading,
|
|
@@ -2339,8 +2600,8 @@ var MessageList = () => {
|
|
|
2339
2600
|
if (!selectedTicket) {
|
|
2340
2601
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full p-8 text-center animate-in fade-in zoom-in-95 duration-300", children: [
|
|
2341
2602
|
/* @__PURE__ */ jsx(MessageSquare, { className: "h-16 w-16 text-muted-foreground mb-4 animate-bounce" }),
|
|
2342
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children:
|
|
2343
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children:
|
|
2603
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTicketSelected }),
|
|
2604
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketSelectedDescription })
|
|
2344
2605
|
] });
|
|
2345
2606
|
}
|
|
2346
2607
|
if (isLoading) {
|
|
@@ -2360,15 +2621,15 @@ var MessageList = () => {
|
|
|
2360
2621
|
if (!messages || messages.length === 0) {
|
|
2361
2622
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full p-8 text-center animate-in fade-in zoom-in-95 duration-300", children: [
|
|
2362
2623
|
/* @__PURE__ */ jsx(MessageSquare, { className: "h-16 w-16 text-muted-foreground mb-4 animate-bounce" }),
|
|
2363
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children:
|
|
2364
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children:
|
|
2624
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noMessages }),
|
|
2625
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noMessagesDescription })
|
|
2365
2626
|
] });
|
|
2366
2627
|
}
|
|
2367
2628
|
return /* @__PURE__ */ jsx(ScrollArea, { className: "h-full bg-muted/50", viewportRef: scrollAreaRef, children: /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-4", ref: scrollRef, children: [
|
|
2368
2629
|
/* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "h-2" }),
|
|
2369
2630
|
isLoadingMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
2370
2631
|
/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
2371
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm", children:
|
|
2632
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: labels.loadingOlder })
|
|
2372
2633
|
] }) }),
|
|
2373
2634
|
hasMore && !isLoadingMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxs(
|
|
2374
2635
|
Button,
|
|
@@ -2378,7 +2639,8 @@ var MessageList = () => {
|
|
|
2378
2639
|
onClick: handleLoadMore,
|
|
2379
2640
|
className: "text-xs",
|
|
2380
2641
|
children: [
|
|
2381
|
-
|
|
2642
|
+
labels.loadOlder,
|
|
2643
|
+
" (",
|
|
2382
2644
|
totalCount > 0 ? `${messages.length}/${totalCount}` : "",
|
|
2383
2645
|
")"
|
|
2384
2646
|
]
|
|
@@ -2393,7 +2655,7 @@ var MessageList = () => {
|
|
|
2393
2655
|
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);
|
|
2394
2656
|
const previousMessage = index > 0 ? messages[index - 1] : null;
|
|
2395
2657
|
const showDateSeparator = previousMessage && moment2.utc(previousMessage.created_at || "").format("YYYY-MM-DD") !== moment2.utc(message.created_at || "").format("YYYY-MM-DD");
|
|
2396
|
-
return /* @__PURE__ */ jsxs(
|
|
2658
|
+
return /* @__PURE__ */ jsxs(React8.Fragment, { children: [
|
|
2397
2659
|
showDateSeparator && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 my-4", children: [
|
|
2398
2660
|
/* @__PURE__ */ jsx("div", { className: "flex-1 h-px bg-border" }),
|
|
2399
2661
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(message.created_at) }),
|
|
@@ -2404,7 +2666,8 @@ var MessageList = () => {
|
|
|
2404
2666
|
{
|
|
2405
2667
|
message,
|
|
2406
2668
|
isFromUser: !!isFromUser,
|
|
2407
|
-
currentUser: user
|
|
2669
|
+
currentUser: user,
|
|
2670
|
+
labels: { supportTeam: labels.supportTeam, staff: labels.staff }
|
|
2408
2671
|
}
|
|
2409
2672
|
) })
|
|
2410
2673
|
] }, message.uuid);
|
|
@@ -2418,10 +2681,18 @@ var logger = createConsola({
|
|
|
2418
2681
|
}).withTag("ext-support");
|
|
2419
2682
|
var supportLogger = logger;
|
|
2420
2683
|
var MessageInput = () => {
|
|
2684
|
+
const st = useSupportT();
|
|
2421
2685
|
const { selectedTicket, sendMessage } = useSupportLayoutContext();
|
|
2422
2686
|
const { toast } = useToast();
|
|
2423
2687
|
const [message, setMessage] = useState("");
|
|
2424
2688
|
const [isSending, setIsSending] = useState(false);
|
|
2689
|
+
const labels = useMemo(() => ({
|
|
2690
|
+
placeholder: st("messageInput.placeholder"),
|
|
2691
|
+
ticketClosed: st("messageInput.ticketClosed"),
|
|
2692
|
+
ticketClosedDescription: st("messageInput.ticketClosedDescription"),
|
|
2693
|
+
messageSent: st("messages.messageSent"),
|
|
2694
|
+
messageSendFailed: st("messages.messageSendFailed")
|
|
2695
|
+
}), [st]);
|
|
2425
2696
|
const handleSubmit = async (e) => {
|
|
2426
2697
|
e.preventDefault();
|
|
2427
2698
|
if (!message.trim() || !selectedTicket) return;
|
|
@@ -2429,10 +2700,10 @@ var MessageInput = () => {
|
|
|
2429
2700
|
try {
|
|
2430
2701
|
await sendMessage(message.trim());
|
|
2431
2702
|
setMessage("");
|
|
2432
|
-
toast.success(
|
|
2703
|
+
toast.success(labels.messageSent);
|
|
2433
2704
|
} catch (error) {
|
|
2434
2705
|
supportLogger.error("Failed to send message:", error);
|
|
2435
|
-
toast.error(
|
|
2706
|
+
toast.error(labels.messageSendFailed);
|
|
2436
2707
|
} finally {
|
|
2437
2708
|
setIsSending(false);
|
|
2438
2709
|
}
|
|
@@ -2455,8 +2726,8 @@ var MessageInput = () => {
|
|
|
2455
2726
|
value: message,
|
|
2456
2727
|
onChange: (e) => setMessage(e.target.value),
|
|
2457
2728
|
onKeyDown: handleKeyDown,
|
|
2458
|
-
placeholder: canSendMessage ?
|
|
2459
|
-
className: "min-h-[60px] max-h-[200px] transition-all duration-200
|
|
2729
|
+
placeholder: canSendMessage ? labels.placeholder : labels.ticketClosed,
|
|
2730
|
+
className: "min-h-[60px] max-h-[200px] transition-all duration-200\n focus:ring-2 focus:ring-primary/20",
|
|
2460
2731
|
disabled: !canSendMessage || isSending
|
|
2461
2732
|
}
|
|
2462
2733
|
),
|
|
@@ -2471,17 +2742,37 @@ var MessageInput = () => {
|
|
|
2471
2742
|
}
|
|
2472
2743
|
)
|
|
2473
2744
|
] }),
|
|
2474
|
-
!canSendMessage && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-2 animate-in fade-in slide-in-from-top-1 duration-200", children:
|
|
2745
|
+
!canSendMessage && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-2 animate-in fade-in slide-in-from-top-1 duration-200", children: labels.ticketClosedDescription })
|
|
2475
2746
|
] });
|
|
2476
2747
|
};
|
|
2477
|
-
var createTicketSchema = z.object({
|
|
2478
|
-
subject: z.string().min(1, "Subject is required").max(200, "Subject too long"),
|
|
2479
|
-
message: z.string().min(1, "Message is required").max(5e3, "Message too long")
|
|
2480
|
-
});
|
|
2481
2748
|
var CreateTicketDialog = () => {
|
|
2749
|
+
const st = useSupportT();
|
|
2482
2750
|
const { uiState, createTicket, closeCreateDialog } = useSupportLayoutContext();
|
|
2483
2751
|
const { toast } = useToast$1();
|
|
2484
|
-
const [isSubmitting, setIsSubmitting] =
|
|
2752
|
+
const [isSubmitting, setIsSubmitting] = React8.useState(false);
|
|
2753
|
+
const labels = useMemo(() => ({
|
|
2754
|
+
title: st("createTicket.title"),
|
|
2755
|
+
description: st("createTicket.description"),
|
|
2756
|
+
subjectLabel: st("createTicket.subjectLabel"),
|
|
2757
|
+
subjectPlaceholder: st("createTicket.subjectPlaceholder"),
|
|
2758
|
+
messageLabel: st("createTicket.messageLabel"),
|
|
2759
|
+
messagePlaceholder: st("createTicket.messagePlaceholder"),
|
|
2760
|
+
cancel: st("createTicket.cancel"),
|
|
2761
|
+
creating: st("createTicket.creating"),
|
|
2762
|
+
create: st("createTicket.create"),
|
|
2763
|
+
ticketCreated: st("messages.ticketCreated"),
|
|
2764
|
+
ticketCreateFailed: st("messages.ticketCreateFailed"),
|
|
2765
|
+
validation: {
|
|
2766
|
+
subjectRequired: st("validation.subjectRequired"),
|
|
2767
|
+
subjectTooLong: st("validation.subjectTooLong"),
|
|
2768
|
+
messageRequired: st("validation.messageRequired"),
|
|
2769
|
+
messageTooLong: st("validation.messageTooLong")
|
|
2770
|
+
}
|
|
2771
|
+
}), [st]);
|
|
2772
|
+
const createTicketSchema = useMemo(() => z.object({
|
|
2773
|
+
subject: z.string().min(1, labels.validation.subjectRequired).max(200, labels.validation.subjectTooLong),
|
|
2774
|
+
message: z.string().min(1, labels.validation.messageRequired).max(5e3, labels.validation.messageTooLong)
|
|
2775
|
+
}), [labels.validation]);
|
|
2485
2776
|
const form = useForm({
|
|
2486
2777
|
resolver: zodResolver(createTicketSchema),
|
|
2487
2778
|
defaultValues: {
|
|
@@ -2494,10 +2785,10 @@ var CreateTicketDialog = () => {
|
|
|
2494
2785
|
try {
|
|
2495
2786
|
await createTicket(data);
|
|
2496
2787
|
form.reset();
|
|
2497
|
-
toast.success(
|
|
2788
|
+
toast.success(labels.ticketCreated);
|
|
2498
2789
|
} catch (error) {
|
|
2499
2790
|
supportLogger.error("Failed to create ticket:", error);
|
|
2500
|
-
toast.error(
|
|
2791
|
+
toast.error(labels.ticketCreateFailed);
|
|
2501
2792
|
} finally {
|
|
2502
2793
|
setIsSubmitting(false);
|
|
2503
2794
|
}
|
|
@@ -2510,9 +2801,9 @@ var CreateTicketDialog = () => {
|
|
|
2510
2801
|
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2511
2802
|
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
|
|
2512
2803
|
/* @__PURE__ */ jsx(Plus, { className: "h-5 w-5" }),
|
|
2513
|
-
|
|
2804
|
+
labels.title
|
|
2514
2805
|
] }),
|
|
2515
|
-
/* @__PURE__ */ jsx(DialogDescription, { children:
|
|
2806
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: labels.description })
|
|
2516
2807
|
] }),
|
|
2517
2808
|
/* @__PURE__ */ jsx(Form, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-6", children: [
|
|
2518
2809
|
/* @__PURE__ */ jsx(
|
|
@@ -2521,8 +2812,8 @@ var CreateTicketDialog = () => {
|
|
|
2521
2812
|
control: form.control,
|
|
2522
2813
|
name: "subject",
|
|
2523
2814
|
render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
2524
|
-
/* @__PURE__ */ jsx(FormLabel, { children:
|
|
2525
|
-
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(Input, { placeholder:
|
|
2815
|
+
/* @__PURE__ */ jsx(FormLabel, { children: labels.subjectLabel }),
|
|
2816
|
+
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(Input, { placeholder: labels.subjectPlaceholder, ...field }) }),
|
|
2526
2817
|
/* @__PURE__ */ jsx(FormMessage, {})
|
|
2527
2818
|
] })
|
|
2528
2819
|
}
|
|
@@ -2533,11 +2824,11 @@ var CreateTicketDialog = () => {
|
|
|
2533
2824
|
control: form.control,
|
|
2534
2825
|
name: "message",
|
|
2535
2826
|
render: ({ field }) => /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
2536
|
-
/* @__PURE__ */ jsx(FormLabel, { children:
|
|
2827
|
+
/* @__PURE__ */ jsx(FormLabel, { children: labels.messageLabel }),
|
|
2537
2828
|
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
|
|
2538
2829
|
Textarea,
|
|
2539
2830
|
{
|
|
2540
|
-
placeholder:
|
|
2831
|
+
placeholder: labels.messagePlaceholder,
|
|
2541
2832
|
className: "min-h-[120px]",
|
|
2542
2833
|
...field
|
|
2543
2834
|
}
|
|
@@ -2554,24 +2845,31 @@ var CreateTicketDialog = () => {
|
|
|
2554
2845
|
variant: "outline",
|
|
2555
2846
|
onClick: handleClose,
|
|
2556
2847
|
disabled: isSubmitting,
|
|
2557
|
-
children:
|
|
2848
|
+
children: labels.cancel
|
|
2558
2849
|
}
|
|
2559
2850
|
),
|
|
2560
2851
|
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2561
2852
|
/* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
|
|
2562
|
-
|
|
2853
|
+
labels.creating
|
|
2563
2854
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2564
2855
|
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
2565
|
-
|
|
2856
|
+
labels.create
|
|
2566
2857
|
] }) })
|
|
2567
2858
|
] })
|
|
2568
2859
|
] }) })
|
|
2569
2860
|
] }) });
|
|
2570
2861
|
};
|
|
2571
2862
|
var SupportLayoutContent = () => {
|
|
2863
|
+
const st = useSupportT();
|
|
2572
2864
|
const { selectedTicket, selectTicket, openCreateDialog, getUnreadCount } = useSupportLayoutContext();
|
|
2573
|
-
const [isMobile, setIsMobile] =
|
|
2574
|
-
|
|
2865
|
+
const [isMobile, setIsMobile] = React8.useState(false);
|
|
2866
|
+
const labels = useMemo(() => ({
|
|
2867
|
+
title: st("layout.title"),
|
|
2868
|
+
titleShort: st("layout.titleShort"),
|
|
2869
|
+
subtitle: st("layout.subtitle"),
|
|
2870
|
+
newTicket: st("layout.newTicket")
|
|
2871
|
+
}), [st]);
|
|
2872
|
+
React8.useEffect(() => {
|
|
2575
2873
|
const checkMobile = () => setIsMobile(window.innerWidth <= 768);
|
|
2576
2874
|
checkMobile();
|
|
2577
2875
|
window.addEventListener("resize", checkMobile);
|
|
@@ -2592,12 +2890,12 @@ var SupportLayoutContent = () => {
|
|
|
2592
2890
|
children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-5 w-5" })
|
|
2593
2891
|
}
|
|
2594
2892
|
) : /* @__PURE__ */ jsx(LifeBuoy, { className: "h-6 w-6 text-primary" }),
|
|
2595
|
-
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: selectedTicket ? selectedTicket.subject :
|
|
2893
|
+
/* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold", children: selectedTicket ? selectedTicket.subject : labels.titleShort }),
|
|
2596
2894
|
unreadCount > 0 && !selectedTicket && /* @__PURE__ */ jsx("div", { className: "h-5 w-5 bg-red-500 text-white text-xs rounded-full flex items-center justify-center", children: unreadCount })
|
|
2597
2895
|
] }),
|
|
2598
2896
|
!selectedTicket && /* @__PURE__ */ jsxs(Button, { onClick: openCreateDialog, size: "sm", children: [
|
|
2599
2897
|
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
2600
|
-
|
|
2898
|
+
labels.newTicket
|
|
2601
2899
|
] })
|
|
2602
2900
|
] }),
|
|
2603
2901
|
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: selectedTicket ? (
|
|
@@ -2618,14 +2916,14 @@ var SupportLayoutContent = () => {
|
|
|
2618
2916
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2619
2917
|
/* @__PURE__ */ jsx(LifeBuoy, { className: "h-7 w-7 text-primary" }),
|
|
2620
2918
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2621
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children:
|
|
2622
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children:
|
|
2919
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: labels.title }),
|
|
2920
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.subtitle })
|
|
2623
2921
|
] }),
|
|
2624
2922
|
unreadCount > 0 && /* @__PURE__ */ jsx("div", { className: "h-6 w-6 bg-red-500 text-white text-sm rounded-full flex items-center justify-center", children: unreadCount })
|
|
2625
2923
|
] }),
|
|
2626
2924
|
/* @__PURE__ */ jsxs(Button, { onClick: openCreateDialog, children: [
|
|
2627
2925
|
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-2" }),
|
|
2628
|
-
|
|
2926
|
+
labels.newTicket
|
|
2629
2927
|
] })
|
|
2630
2928
|
] }),
|
|
2631
2929
|
/* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxs(ResizablePanelGroup, { direction: "horizontal", className: "h-full", children: [
|
|
@@ -2646,7 +2944,7 @@ var SupportLayout = () => {
|
|
|
2646
2944
|
// package.json
|
|
2647
2945
|
var package_default = {
|
|
2648
2946
|
name: "@djangocfg/ext-support",
|
|
2649
|
-
version: "1.0.
|
|
2947
|
+
version: "1.0.23",
|
|
2650
2948
|
description: "Support ticket system extension for DjangoCFG",
|
|
2651
2949
|
keywords: [
|
|
2652
2950
|
"django",
|
|
@@ -2692,6 +2990,11 @@ var package_default = {
|
|
|
2692
2990
|
types: "./dist/config.d.ts",
|
|
2693
2991
|
import: "./dist/config.js",
|
|
2694
2992
|
require: "./dist/config.cjs"
|
|
2993
|
+
},
|
|
2994
|
+
"./i18n": {
|
|
2995
|
+
types: "./dist/i18n.d.ts",
|
|
2996
|
+
import: "./dist/i18n.js",
|
|
2997
|
+
require: "./dist/i18n.cjs"
|
|
2695
2998
|
}
|
|
2696
2999
|
},
|
|
2697
3000
|
files: [
|
|
@@ -2707,27 +3010,31 @@ var package_default = {
|
|
|
2707
3010
|
peerDependencies: {
|
|
2708
3011
|
"@djangocfg/api": "workspace:*",
|
|
2709
3012
|
"@djangocfg/ext-base": "workspace:*",
|
|
3013
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2710
3014
|
"@djangocfg/ui-core": "workspace:*",
|
|
3015
|
+
"@hookform/resolvers": "^5.2.2",
|
|
2711
3016
|
consola: "^3.4.2",
|
|
2712
3017
|
"lucide-react": "^0.545.0",
|
|
2713
3018
|
moment: "^2.30.1",
|
|
2714
3019
|
next: "^16",
|
|
3020
|
+
"next-intl": "^4",
|
|
2715
3021
|
"p-retry": "^7.0.0",
|
|
2716
3022
|
react: "^19",
|
|
2717
3023
|
"react-hook-form": "^7.69.0",
|
|
2718
|
-
"@hookform/resolvers": "^5.2.2",
|
|
2719
3024
|
swr: "^2.3.7",
|
|
2720
3025
|
zod: "^4.3.4"
|
|
2721
3026
|
},
|
|
2722
3027
|
devDependencies: {
|
|
2723
3028
|
"@djangocfg/api": "workspace:*",
|
|
2724
3029
|
"@djangocfg/ext-base": "workspace:*",
|
|
3030
|
+
"@djangocfg/i18n": "workspace:*",
|
|
2725
3031
|
"@djangocfg/ui-core": "workspace:*",
|
|
2726
3032
|
"@djangocfg/typescript-config": "workspace:*",
|
|
2727
3033
|
"@types/node": "^24.7.2",
|
|
2728
3034
|
"@types/react": "^19.0.0",
|
|
2729
3035
|
consola: "^3.4.2",
|
|
2730
3036
|
moment: "^2.30.1",
|
|
3037
|
+
"next-intl": "^4.1.0",
|
|
2731
3038
|
"p-retry": "^7.0.0",
|
|
2732
3039
|
swr: "^2.3.7",
|
|
2733
3040
|
tsup: "^8.5.0",
|