@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/index.cjs CHANGED
@@ -5,7 +5,8 @@ var pRetry = require('p-retry');
5
5
  var zod = require('zod');
6
6
  var api = require('@djangocfg/ext-base/api');
7
7
  var lucideReact = require('lucide-react');
8
- var React7 = require('react');
8
+ var React8 = require('react');
9
+ var nextIntl = require('next-intl');
9
10
  var uiCore = require('@djangocfg/ui-core');
10
11
  var useSWR = require('swr');
11
12
  var jsxRuntime = require('react/jsx-runtime');
@@ -21,7 +22,7 @@ var extBase = require('@djangocfg/ext-base');
21
22
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
23
 
23
24
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
24
- var React7__default = /*#__PURE__*/_interopDefault(React7);
25
+ var React8__default = /*#__PURE__*/_interopDefault(React8);
25
26
  var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
26
27
  var moment2__default = /*#__PURE__*/_interopDefault(moment2);
27
28
  var useSWRInfinite__default = /*#__PURE__*/_interopDefault(useSWRInfinite);
@@ -1625,6 +1626,233 @@ var API = class {
1625
1626
  };
1626
1627
  api.initializeExtensionAPI(configureAPI);
1627
1628
  var apiSupport = api.createExtensionAPI(API);
1629
+
1630
+ // src/i18n/locales/en.ts
1631
+ var en = {
1632
+ layout: {
1633
+ title: "Support Center",
1634
+ titleShort: "Support",
1635
+ subtitle: "Get help from our support team",
1636
+ newTicket: "New Ticket"
1637
+ },
1638
+ status: {
1639
+ open: "Open",
1640
+ waitingForUser: "Waiting for you",
1641
+ waitingForAdmin: "Waiting for support",
1642
+ resolved: "Resolved",
1643
+ closed: "Closed"
1644
+ },
1645
+ ticketList: {
1646
+ noTickets: "No tickets yet",
1647
+ noTicketsDescription: "Create your first support ticket to get help",
1648
+ loadingMore: "Loading more...",
1649
+ loadMore: "Load more",
1650
+ allLoaded: "All {count} tickets loaded"
1651
+ },
1652
+ createTicket: {
1653
+ title: "New Support Ticket",
1654
+ description: "Describe your issue and we will help you",
1655
+ subjectLabel: "Subject",
1656
+ subjectPlaceholder: "Brief description of your issue",
1657
+ messageLabel: "Message",
1658
+ messagePlaceholder: "Describe your issue in detail...",
1659
+ cancel: "Cancel",
1660
+ creating: "Creating...",
1661
+ create: "Create Ticket"
1662
+ },
1663
+ validation: {
1664
+ subjectRequired: "Subject is required",
1665
+ subjectTooLong: "Subject is too long (max 200 characters)",
1666
+ messageRequired: "Message is required",
1667
+ messageTooLong: "Message is too long (max 5000 characters)"
1668
+ },
1669
+ messages: {
1670
+ ticketCreated: "Ticket created successfully",
1671
+ ticketCreateFailed: "Failed to create ticket",
1672
+ messageSent: "Message sent",
1673
+ messageSendFailed: "Failed to send message"
1674
+ },
1675
+ messageInput: {
1676
+ placeholder: "Type your message...",
1677
+ ticketClosed: "Ticket closed",
1678
+ ticketClosedDescription: "This ticket has been closed. Create a new ticket if you need further assistance."
1679
+ },
1680
+ messageList: {
1681
+ noTicketSelected: "No ticket selected",
1682
+ noTicketSelectedDescription: "Select a ticket from the list to view messages",
1683
+ noMessages: "No messages yet",
1684
+ noMessagesDescription: "Start the conversation by sending a message",
1685
+ loadingOlder: "Loading older messages...",
1686
+ loadOlder: "Load older messages",
1687
+ supportTeam: "Support Team",
1688
+ staff: "Staff"
1689
+ },
1690
+ time: {
1691
+ justNow: "Just now",
1692
+ minutesAgo: "{count} min ago",
1693
+ hoursAgo: "{count}h ago",
1694
+ daysAgo: "{count}d ago"
1695
+ }
1696
+ };
1697
+
1698
+ // src/i18n/locales/ru.ts
1699
+ var ru = {
1700
+ layout: {
1701
+ title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1702
+ titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
1703
+ 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",
1704
+ newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1705
+ },
1706
+ status: {
1707
+ open: "\u041E\u0442\u043A\u0440\u044B\u0442",
1708
+ waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
1709
+ waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
1710
+ resolved: "\u0420\u0435\u0448\u0451\u043D",
1711
+ closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
1712
+ },
1713
+ ticketList: {
1714
+ noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
1715
+ 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",
1716
+ loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
1717
+ loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
1718
+ allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
1719
+ },
1720
+ createTicket: {
1721
+ title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
1722
+ 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",
1723
+ subjectLabel: "\u0422\u0435\u043C\u0430",
1724
+ subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
1725
+ messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
1726
+ 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...",
1727
+ cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
1728
+ creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
1729
+ create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1730
+ },
1731
+ validation: {
1732
+ subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
1733
+ 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)",
1734
+ messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
1735
+ 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)"
1736
+ },
1737
+ messages: {
1738
+ ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
1739
+ 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",
1740
+ messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
1741
+ 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"
1742
+ },
1743
+ messageInput: {
1744
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
1745
+ ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
1746
+ 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."
1747
+ },
1748
+ messageList: {
1749
+ noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
1750
+ 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",
1751
+ noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
1752
+ 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",
1753
+ loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
1754
+ loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
1755
+ supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1756
+ staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
1757
+ },
1758
+ time: {
1759
+ justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
1760
+ minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
1761
+ hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
1762
+ daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
1763
+ }
1764
+ };
1765
+
1766
+ // src/i18n/locales/ko.ts
1767
+ var ko = {
1768
+ layout: {
1769
+ title: "\uACE0\uAC1D\uC9C0\uC6D0 \uC13C\uD130",
1770
+ titleShort: "\uACE0\uAC1D\uC9C0\uC6D0",
1771
+ subtitle: "\uACE0\uAC1D\uC9C0\uC6D0\uD300\uC758 \uB3C4\uC6C0\uC744 \uBC1B\uC73C\uC138\uC694",
1772
+ newTicket: "\uC0C8 \uBB38\uC758"
1773
+ },
1774
+ status: {
1775
+ open: "\uC5F4\uB9BC",
1776
+ waitingForUser: "\uD68C\uC2E0 \uB300\uAE30",
1777
+ waitingForAdmin: "\uC9C0\uC6D0\uD300 \uB300\uAE30",
1778
+ resolved: "\uD574\uACB0\uB428",
1779
+ closed: "\uC885\uB8CC"
1780
+ },
1781
+ ticketList: {
1782
+ noTickets: "\uBB38\uC758 \uB0B4\uC5ED \uC5C6\uC74C",
1783
+ noTicketsDescription: "\uCCAB \uBC88\uC9F8 \uBB38\uC758\uB97C \uB4F1\uB85D\uD558\uC5EC \uB3C4\uC6C0\uC744 \uBC1B\uC73C\uC138\uC694",
1784
+ loadingMore: "\uBD88\uB7EC\uC624\uB294 \uC911...",
1785
+ loadMore: "\uB354 \uBCF4\uAE30",
1786
+ allLoaded: "\uCD1D {count}\uAC1C\uC758 \uBB38\uC758\uAC00 \uB85C\uB4DC\uB428"
1787
+ },
1788
+ createTicket: {
1789
+ title: "\uC0C8 \uBB38\uC758",
1790
+ description: "\uBB38\uC81C\uB97C \uC124\uBA85\uD574 \uC8FC\uC2DC\uBA74 \uB3C4\uC6C0\uC744 \uB4DC\uB9AC\uACA0\uC2B5\uB2C8\uB2E4",
1791
+ subjectLabel: "\uC81C\uBAA9",
1792
+ subjectPlaceholder: "\uBB38\uC81C\uC5D0 \uB300\uD55C \uAC04\uB7B5\uD55C \uC124\uBA85",
1793
+ messageLabel: "\uB0B4\uC6A9",
1794
+ messagePlaceholder: "\uBB38\uC81C\uB97C \uC790\uC138\uD788 \uC124\uBA85\uD574 \uC8FC\uC138\uC694...",
1795
+ cancel: "\uCDE8\uC18C",
1796
+ creating: "\uC0DD\uC131 \uC911...",
1797
+ create: "\uBB38\uC758 \uB4F1\uB85D"
1798
+ },
1799
+ validation: {
1800
+ subjectRequired: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694",
1801
+ subjectTooLong: "\uC81C\uBAA9\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4 (\uCD5C\uB300 200\uC790)",
1802
+ messageRequired: "\uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694",
1803
+ messageTooLong: "\uB0B4\uC6A9\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4 (\uCD5C\uB300 5000\uC790)"
1804
+ },
1805
+ messages: {
1806
+ ticketCreated: "\uBB38\uC758\uAC00 \uB4F1\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
1807
+ ticketCreateFailed: "\uBB38\uC758 \uB4F1\uB85D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
1808
+ messageSent: "\uBA54\uC2DC\uC9C0\uAC00 \uC804\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
1809
+ messageSendFailed: "\uBA54\uC2DC\uC9C0 \uC804\uC1A1\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4"
1810
+ },
1811
+ messageInput: {
1812
+ placeholder: "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694...",
1813
+ ticketClosed: "\uBB38\uC758 \uC885\uB8CC\uB428",
1814
+ 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."
1815
+ },
1816
+ messageList: {
1817
+ noTicketSelected: "\uBB38\uC758\uAC00 \uC120\uD0DD\uB418\uC9C0 \uC54A\uC74C",
1818
+ noTicketSelectedDescription: "\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB824\uBA74 \uBAA9\uB85D\uC5D0\uC11C \uBB38\uC758\uB97C \uC120\uD0DD\uD558\uC138\uC694",
1819
+ noMessages: "\uBA54\uC2DC\uC9C0 \uC5C6\uC74C",
1820
+ noMessagesDescription: "\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB0B4 \uB300\uD654\uB97C \uC2DC\uC791\uD558\uC138\uC694",
1821
+ loadingOlder: "\uC774\uC804 \uBA54\uC2DC\uC9C0 \uBD88\uB7EC\uC624\uB294 \uC911...",
1822
+ loadOlder: "\uC774\uC804 \uBA54\uC2DC\uC9C0 \uBCF4\uAE30",
1823
+ supportTeam: "\uACE0\uAC1D\uC9C0\uC6D0\uD300",
1824
+ staff: "\uB2F4\uB2F9\uC790"
1825
+ },
1826
+ time: {
1827
+ justNow: "\uBC29\uAE08 \uC804",
1828
+ minutesAgo: "{count}\uBD84 \uC804",
1829
+ hoursAgo: "{count}\uC2DC\uAC04 \uC804",
1830
+ daysAgo: "{count}\uC77C \uC804"
1831
+ }
1832
+ };
1833
+
1834
+ // src/i18n/useSupportT.ts
1835
+ var translations = { en, ru, ko };
1836
+ function getNestedValue(obj, path) {
1837
+ const keys = path.split(".");
1838
+ let result = obj;
1839
+ for (const key of keys) {
1840
+ if (result && typeof result === "object" && key in result) {
1841
+ result = result[key];
1842
+ } else {
1843
+ return path;
1844
+ }
1845
+ }
1846
+ return typeof result === "string" ? result : path;
1847
+ }
1848
+ function useSupportT() {
1849
+ const locale = nextIntl.useLocale();
1850
+ const t = React8.useMemo(() => translations[locale] || translations.en, [locale]);
1851
+ return React8.useCallback(
1852
+ (key) => getNestedValue(t, key),
1853
+ [t]
1854
+ );
1855
+ }
1628
1856
  function useSupportTicketsList(params, client) {
1629
1857
  return useSWR__default.default(
1630
1858
  params ? ["cfg-support-tickets", params] : "cfg-support-tickets",
@@ -1717,7 +1945,7 @@ function useDeleteSupportTicketsDestroy() {
1717
1945
  return result;
1718
1946
  };
1719
1947
  }
1720
- var SupportContext = React7.createContext(void 0);
1948
+ var SupportContext = React8.createContext(void 0);
1721
1949
  function SupportProvider({ children }) {
1722
1950
  const {
1723
1951
  data: ticketsData,
@@ -1817,7 +2045,7 @@ function SupportProvider({ children }) {
1817
2045
  return /* @__PURE__ */ jsxRuntime.jsx(SupportContext.Provider, { value, children });
1818
2046
  }
1819
2047
  function useSupportContext() {
1820
- const context = React7.useContext(SupportContext);
2048
+ const context = React8.useContext(SupportContext);
1821
2049
  if (!context) {
1822
2050
  throw new Error("useSupportContext must be used within SupportProvider");
1823
2051
  }
@@ -1839,18 +2067,35 @@ var getStatusBadgeVariant = (status) => {
1839
2067
  return "default";
1840
2068
  }
1841
2069
  };
1842
- var formatRelativeTime = (date) => {
1843
- if (!date) return "N/A";
1844
- const m = moment2__default.default.utc(date).local();
1845
- const now = moment2__default.default();
1846
- const diffInSeconds = now.diff(m, "seconds");
1847
- if (diffInSeconds < 60) return "Just now";
1848
- if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
1849
- if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
1850
- if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
1851
- return m.format("MMM D, YYYY");
1852
- };
1853
2070
  var TicketCard = ({ ticket, isSelected, onClick }) => {
2071
+ const st = useSupportT();
2072
+ const labels = React8.useMemo(() => ({
2073
+ status: {
2074
+ open: st("status.open"),
2075
+ waiting_for_user: st("status.waitingForUser"),
2076
+ waiting_for_admin: st("status.waitingForAdmin"),
2077
+ resolved: st("status.resolved"),
2078
+ closed: st("status.closed")
2079
+ },
2080
+ time: {
2081
+ justNow: st("time.justNow"),
2082
+ minutesAgo: st("time.minutesAgo"),
2083
+ hoursAgo: st("time.hoursAgo"),
2084
+ daysAgo: st("time.daysAgo")
2085
+ }
2086
+ }), [st]);
2087
+ const formatRelativeTime = React8.useCallback((date) => {
2088
+ if (!date) return "N/A";
2089
+ const m = moment2__default.default.utc(date).local();
2090
+ const now = moment2__default.default();
2091
+ const diffInSeconds = now.diff(m, "seconds");
2092
+ if (diffInSeconds < 60) return labels.time.justNow;
2093
+ if (diffInSeconds < 3600) return labels.time.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
2094
+ if (diffInSeconds < 86400) return labels.time.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
2095
+ if (diffInSeconds < 604800) return labels.time.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
2096
+ return m.format("MMM D, YYYY");
2097
+ }, [labels.time]);
2098
+ const statusLabel = labels.status[ticket.status] || ticket.status || labels.status.open;
1854
2099
  return /* @__PURE__ */ jsxRuntime.jsx(
1855
2100
  uiCore.Card,
1856
2101
  {
@@ -1874,7 +2119,7 @@ var TicketCard = ({ ticket, isSelected, onClick }) => {
1874
2119
  )
1875
2120
  ] }),
1876
2121
  /* @__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: [
1877
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: ticket.status || "open" }),
2122
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: statusLabel }),
1878
2123
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
1879
2124
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
1880
2125
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatRelativeTime(ticket.created_at) })
@@ -2018,11 +2263,11 @@ function useInfiniteMessages(ticketUuid) {
2018
2263
  addMessage
2019
2264
  };
2020
2265
  }
2021
- var SupportLayoutContext = React7.createContext(void 0);
2266
+ var SupportLayoutContext = React8.createContext(void 0);
2022
2267
  function SupportLayoutProvider({ children }) {
2023
2268
  const support = useSupportContext();
2024
2269
  const { user } = auth.useAuth();
2025
- const [uiState, setUIState] = React7.useState({
2270
+ const [uiState, setUIState] = React8.useState({
2026
2271
  selectedTicketUuid: null,
2027
2272
  isCreateDialogOpen: false,
2028
2273
  viewMode: "list"
@@ -2038,7 +2283,7 @@ function SupportLayoutProvider({ children }) {
2038
2283
  refresh: refreshMessages,
2039
2284
  addMessage: addMessageOptimistically
2040
2285
  } = useInfiniteMessages(selectedTicket?.uuid || null);
2041
- const selectTicket = React7.useCallback(async (ticket) => {
2286
+ const selectTicket = React8.useCallback(async (ticket) => {
2042
2287
  setUIState((prev) => ({ ...prev, selectedTicketUuid: ticket?.uuid || null }));
2043
2288
  if (ticket?.uuid) {
2044
2289
  window.dispatchEvent(
@@ -2046,7 +2291,7 @@ function SupportLayoutProvider({ children }) {
2046
2291
  );
2047
2292
  }
2048
2293
  }, []);
2049
- const createTicket = React7.useCallback(async (data) => {
2294
+ const createTicket = React8.useCallback(async (data) => {
2050
2295
  if (!user?.id) {
2051
2296
  throw new Error("User must be authenticated to create tickets");
2052
2297
  }
@@ -2069,7 +2314,7 @@ function SupportLayoutProvider({ children }) {
2069
2314
  new CustomEvent(SUPPORT_LAYOUT_EVENTS.TICKET_SELECTED, { detail: { ticket } })
2070
2315
  );
2071
2316
  }, [support, user]);
2072
- const sendMessage = React7.useCallback(async (message) => {
2317
+ const sendMessage = React8.useCallback(async (message) => {
2073
2318
  if (!selectedTicket?.uuid) return;
2074
2319
  const messageData = {
2075
2320
  text: message
@@ -2099,16 +2344,16 @@ function SupportLayoutProvider({ children }) {
2099
2344
  new CustomEvent(SUPPORT_LAYOUT_EVENTS.MESSAGE_SENT, { detail: { message: newMessage } })
2100
2345
  );
2101
2346
  }, [selectedTicket, support, user, addMessageOptimistically, refreshMessages]);
2102
- const openCreateDialog = React7.useCallback(() => {
2347
+ const openCreateDialog = React8.useCallback(() => {
2103
2348
  setUIState((prev) => ({ ...prev, isCreateDialogOpen: true }));
2104
2349
  }, []);
2105
- const closeCreateDialog = React7.useCallback(() => {
2350
+ const closeCreateDialog = React8.useCallback(() => {
2106
2351
  setUIState((prev) => ({ ...prev, isCreateDialogOpen: false }));
2107
2352
  }, []);
2108
- const getUnreadCount = React7.useCallback(() => {
2353
+ const getUnreadCount = React8.useCallback(() => {
2109
2354
  return support.tickets?.reduce((count, ticket) => count + (ticket.unanswered_messages_count || 0), 0) || 0;
2110
2355
  }, [support.tickets]);
2111
- React7.useEffect(() => {
2356
+ React8.useEffect(() => {
2112
2357
  const handleOpenDialog = () => openCreateDialog();
2113
2358
  const handleCloseDialog = () => closeCreateDialog();
2114
2359
  window.addEventListener(SUPPORT_LAYOUT_EVENTS.OPEN_CREATE_DIALOG, handleOpenDialog);
@@ -2142,13 +2387,14 @@ function SupportLayoutProvider({ children }) {
2142
2387
  return /* @__PURE__ */ jsxRuntime.jsx(SupportLayoutContext.Provider, { value, children });
2143
2388
  }
2144
2389
  function useSupportLayoutContext() {
2145
- const context = React7.useContext(SupportLayoutContext);
2390
+ const context = React8.useContext(SupportLayoutContext);
2146
2391
  if (!context) {
2147
2392
  throw new Error("useSupportLayoutContext must be used within SupportLayoutProvider");
2148
2393
  }
2149
2394
  return context;
2150
2395
  }
2151
2396
  var TicketList = () => {
2397
+ const st = useSupportT();
2152
2398
  const { selectedTicket, selectTicket } = useSupportLayoutContext();
2153
2399
  const {
2154
2400
  tickets,
@@ -2159,10 +2405,17 @@ var TicketList = () => {
2159
2405
  totalCount,
2160
2406
  refresh
2161
2407
  } = useInfiniteTickets();
2162
- const scrollRef = React7.useRef(null);
2163
- const observerRef = React7.useRef(null);
2164
- const loadMoreRef = React7.useRef(null);
2165
- React7.useEffect(() => {
2408
+ const labels = React8.useMemo(() => ({
2409
+ noTickets: st("ticketList.noTickets"),
2410
+ noTicketsDescription: st("ticketList.noTicketsDescription"),
2411
+ loadingMore: st("ticketList.loadingMore"),
2412
+ loadMore: st("ticketList.loadMore"),
2413
+ allLoaded: st("ticketList.allLoaded")
2414
+ }), [st]);
2415
+ const scrollRef = React8.useRef(null);
2416
+ const observerRef = React8.useRef(null);
2417
+ const loadMoreRef = React8.useRef(null);
2418
+ React8.useEffect(() => {
2166
2419
  const handleTicketCreated = () => {
2167
2420
  refresh();
2168
2421
  };
@@ -2171,7 +2424,7 @@ var TicketList = () => {
2171
2424
  window.removeEventListener(SUPPORT_LAYOUT_EVENTS.TICKET_CREATED, handleTicketCreated);
2172
2425
  };
2173
2426
  }, [refresh]);
2174
- React7.useEffect(() => {
2427
+ React8.useEffect(() => {
2175
2428
  if (observerRef.current) {
2176
2429
  observerRef.current.disconnect();
2177
2430
  }
@@ -2204,8 +2457,8 @@ var TicketList = () => {
2204
2457
  if (!tickets || tickets.length === 0) {
2205
2458
  return /* @__PURE__ */ jsxRuntime.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: [
2206
2459
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-16 w-16 text-muted-foreground mb-4 animate-bounce" }),
2207
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: "No tickets yet" }),
2208
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: "Create your first support ticket to get help from our team" })
2460
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTickets }),
2461
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketsDescription })
2209
2462
  ] });
2210
2463
  }
2211
2464
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ScrollArea, { className: "h-full", viewportRef: scrollRef, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 space-y-2", children: [
@@ -2228,7 +2481,7 @@ var TicketList = () => {
2228
2481
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "h-2" }),
2229
2482
  isLoadingMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
2230
2483
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2231
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: "Loading more tickets..." })
2484
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: labels.loadingMore })
2232
2485
  ] }) }),
2233
2486
  hasMore && !isLoadingMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
2234
2487
  uiCore.Button,
@@ -2238,17 +2491,14 @@ var TicketList = () => {
2238
2491
  onClick: loadMore,
2239
2492
  className: "text-xs",
2240
2493
  children: [
2241
- "Load more (",
2494
+ labels.loadMore,
2495
+ " (",
2242
2496
  totalCount > 0 ? `${tickets.length}/${totalCount}` : "",
2243
2497
  ")"
2244
2498
  ]
2245
2499
  }
2246
2500
  ) }),
2247
- !hasMore && tickets.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-4 text-sm text-muted-foreground", children: [
2248
- "All ",
2249
- totalCount,
2250
- " tickets loaded"
2251
- ] })
2501
+ !hasMore && tickets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center py-4 text-sm text-muted-foreground", children: labels.allLoaded.replace("{count}", String(totalCount)) })
2252
2502
  ] }) });
2253
2503
  };
2254
2504
  var formatTime = (date) => {
@@ -2259,7 +2509,7 @@ var formatDate = (date) => {
2259
2509
  if (!date) return "";
2260
2510
  return moment2__default.default.utc(date).local().format("MMM D, YYYY");
2261
2511
  };
2262
- var MessageBubble = ({ message, isFromUser, currentUser }) => {
2512
+ var MessageBubble = ({ message, isFromUser, currentUser, labels }) => {
2263
2513
  const sender = message.sender;
2264
2514
  const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
2265
2515
  const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
@@ -2269,11 +2519,11 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
2269
2519
  className: `flex gap-3 ${isFromUser ? "justify-end" : "justify-start"}
2270
2520
  animate-in fade-in slide-in-from-bottom-2 duration-300`,
2271
2521
  children: [
2272
- !isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarImage, { src: sender.avatar, alt: sender.display_username || "Support" }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Headphones, { className: "h-4 w-4" }) : senderInitial }) }),
2522
+ !isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Avatar, { className: "h-8 w-8 shrink-0", children: sender?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarImage, { src: sender.avatar, alt: sender.display_username || labels.supportTeam }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarFallback, { className: "bg-primary text-primary-foreground", children: sender?.is_staff ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Headphones, { className: "h-4 w-4" }) : senderInitial }) }),
2273
2523
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col gap-1 flex-1 max-w-[80%] ${isFromUser ? "items-end" : "items-start"}`, children: [
2274
2524
  !isFromUser && sender && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground px-1", children: [
2275
- sender.display_username || sender.email || "Support Team",
2276
- sender.is_staff && " (Staff)"
2525
+ sender.display_username || sender.email || labels.supportTeam,
2526
+ sender.is_staff && ` (${labels.staff})`
2277
2527
  ] }),
2278
2528
  /* @__PURE__ */ jsxRuntime.jsx(
2279
2529
  uiCore.Card,
@@ -2290,8 +2540,19 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
2290
2540
  );
2291
2541
  };
2292
2542
  var MessageList = () => {
2543
+ const st = useSupportT();
2293
2544
  const { selectedTicket } = useSupportLayoutContext();
2294
2545
  const { user } = auth.useAuth();
2546
+ const labels = React8.useMemo(() => ({
2547
+ noTicketSelected: st("messageList.noTicketSelected"),
2548
+ noTicketSelectedDescription: st("messageList.noTicketSelectedDescription"),
2549
+ noMessages: st("messageList.noMessages"),
2550
+ noMessagesDescription: st("messageList.noMessagesDescription"),
2551
+ loadingOlder: st("messageList.loadingOlder"),
2552
+ loadOlder: st("messageList.loadOlder"),
2553
+ supportTeam: st("messageList.supportTeam"),
2554
+ staff: st("messageList.staff")
2555
+ }), [st]);
2295
2556
  const {
2296
2557
  messages,
2297
2558
  isLoading,
@@ -2300,12 +2561,12 @@ var MessageList = () => {
2300
2561
  totalCount,
2301
2562
  loadMore
2302
2563
  } = useInfiniteMessages(selectedTicket?.uuid || null);
2303
- const scrollRef = React7.useRef(null);
2304
- const scrollAreaRef = React7.useRef(null);
2305
- const observerRef = React7.useRef(null);
2306
- const loadMoreRef = React7.useRef(null);
2307
- const firstRender = React7.useRef(true);
2308
- React7.useEffect(() => {
2564
+ const scrollRef = React8.useRef(null);
2565
+ const scrollAreaRef = React8.useRef(null);
2566
+ const observerRef = React8.useRef(null);
2567
+ const loadMoreRef = React8.useRef(null);
2568
+ const firstRender = React8.useRef(true);
2569
+ React8.useEffect(() => {
2309
2570
  if (observerRef.current) {
2310
2571
  observerRef.current.disconnect();
2311
2572
  }
@@ -2326,7 +2587,7 @@ var MessageList = () => {
2326
2587
  }
2327
2588
  };
2328
2589
  }, [hasMore, isLoadingMore, loadMore]);
2329
- React7.useEffect(() => {
2590
+ React8.useEffect(() => {
2330
2591
  if (firstRender.current && messages.length > 0) {
2331
2592
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2332
2593
  if (scrollContainer) {
@@ -2335,7 +2596,7 @@ var MessageList = () => {
2335
2596
  firstRender.current = false;
2336
2597
  }
2337
2598
  }, [messages]);
2338
- const handleLoadMore = React7.useCallback(() => {
2599
+ const handleLoadMore = React8.useCallback(() => {
2339
2600
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2340
2601
  const previousHeight = scrollContainer?.scrollHeight || 0;
2341
2602
  loadMore();
@@ -2349,8 +2610,8 @@ var MessageList = () => {
2349
2610
  if (!selectedTicket) {
2350
2611
  return /* @__PURE__ */ jsxRuntime.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: [
2351
2612
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-16 w-16 text-muted-foreground mb-4 animate-bounce" }),
2352
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: "No ticket selected" }),
2353
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: "Select a ticket from the list to view the conversation" })
2613
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTicketSelected }),
2614
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketSelectedDescription })
2354
2615
  ] });
2355
2616
  }
2356
2617
  if (isLoading) {
@@ -2370,15 +2631,15 @@ var MessageList = () => {
2370
2631
  if (!messages || messages.length === 0) {
2371
2632
  return /* @__PURE__ */ jsxRuntime.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: [
2372
2633
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-16 w-16 text-muted-foreground mb-4 animate-bounce" }),
2373
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: "No messages yet" }),
2374
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: "Start the conversation by sending a message below" })
2634
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noMessages }),
2635
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noMessagesDescription })
2375
2636
  ] });
2376
2637
  }
2377
2638
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ScrollArea, { className: "h-full bg-muted/50", viewportRef: scrollAreaRef, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-4", ref: scrollRef, children: [
2378
2639
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "h-2" }),
2379
2640
  isLoadingMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
2380
2641
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2381
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: "Loading older messages..." })
2642
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: labels.loadingOlder })
2382
2643
  ] }) }),
2383
2644
  hasMore && !isLoadingMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
2384
2645
  uiCore.Button,
@@ -2388,7 +2649,8 @@ var MessageList = () => {
2388
2649
  onClick: handleLoadMore,
2389
2650
  className: "text-xs",
2390
2651
  children: [
2391
- "Load older messages (",
2652
+ labels.loadOlder,
2653
+ " (",
2392
2654
  totalCount > 0 ? `${messages.length}/${totalCount}` : "",
2393
2655
  ")"
2394
2656
  ]
@@ -2403,7 +2665,7 @@ var MessageList = () => {
2403
2665
  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);
2404
2666
  const previousMessage = index > 0 ? messages[index - 1] : null;
2405
2667
  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");
2406
- return /* @__PURE__ */ jsxRuntime.jsxs(React7__default.default.Fragment, { children: [
2668
+ return /* @__PURE__ */ jsxRuntime.jsxs(React8__default.default.Fragment, { children: [
2407
2669
  showDateSeparator && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 my-4", children: [
2408
2670
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
2409
2671
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(message.created_at) }),
@@ -2414,7 +2676,8 @@ var MessageList = () => {
2414
2676
  {
2415
2677
  message,
2416
2678
  isFromUser: !!isFromUser,
2417
- currentUser: user
2679
+ currentUser: user,
2680
+ labels: { supportTeam: labels.supportTeam, staff: labels.staff }
2418
2681
  }
2419
2682
  ) })
2420
2683
  ] }, message.uuid);
@@ -2428,10 +2691,18 @@ var logger = consola.createConsola({
2428
2691
  }).withTag("ext-support");
2429
2692
  var supportLogger = logger;
2430
2693
  var MessageInput = () => {
2694
+ const st = useSupportT();
2431
2695
  const { selectedTicket, sendMessage } = useSupportLayoutContext();
2432
2696
  const { toast } = hooks.useToast();
2433
- const [message, setMessage] = React7.useState("");
2434
- const [isSending, setIsSending] = React7.useState(false);
2697
+ const [message, setMessage] = React8.useState("");
2698
+ const [isSending, setIsSending] = React8.useState(false);
2699
+ const labels = React8.useMemo(() => ({
2700
+ placeholder: st("messageInput.placeholder"),
2701
+ ticketClosed: st("messageInput.ticketClosed"),
2702
+ ticketClosedDescription: st("messageInput.ticketClosedDescription"),
2703
+ messageSent: st("messages.messageSent"),
2704
+ messageSendFailed: st("messages.messageSendFailed")
2705
+ }), [st]);
2435
2706
  const handleSubmit = async (e) => {
2436
2707
  e.preventDefault();
2437
2708
  if (!message.trim() || !selectedTicket) return;
@@ -2439,10 +2710,10 @@ var MessageInput = () => {
2439
2710
  try {
2440
2711
  await sendMessage(message.trim());
2441
2712
  setMessage("");
2442
- toast.success("Message sent successfully");
2713
+ toast.success(labels.messageSent);
2443
2714
  } catch (error) {
2444
2715
  supportLogger.error("Failed to send message:", error);
2445
- toast.error("Failed to send message");
2716
+ toast.error(labels.messageSendFailed);
2446
2717
  } finally {
2447
2718
  setIsSending(false);
2448
2719
  }
@@ -2465,8 +2736,8 @@ var MessageInput = () => {
2465
2736
  value: message,
2466
2737
  onChange: (e) => setMessage(e.target.value),
2467
2738
  onKeyDown: handleKeyDown,
2468
- placeholder: canSendMessage ? "Type your message... (Shift+Enter for new line)" : "This ticket is closed",
2469
- className: "min-h-[60px] max-h-[200px] transition-all duration-200 \n focus:ring-2 focus:ring-primary/20",
2739
+ placeholder: canSendMessage ? labels.placeholder : labels.ticketClosed,
2740
+ className: "min-h-[60px] max-h-[200px] transition-all duration-200\n focus:ring-2 focus:ring-primary/20",
2470
2741
  disabled: !canSendMessage || isSending
2471
2742
  }
2472
2743
  ),
@@ -2481,17 +2752,37 @@ var MessageInput = () => {
2481
2752
  }
2482
2753
  )
2483
2754
  ] }),
2484
- !canSendMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-2 animate-in fade-in slide-in-from-top-1 duration-200", children: "This ticket is closed. You cannot send new messages." })
2755
+ !canSendMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-2 animate-in fade-in slide-in-from-top-1 duration-200", children: labels.ticketClosedDescription })
2485
2756
  ] });
2486
2757
  };
2487
- var createTicketSchema = zod.z.object({
2488
- subject: zod.z.string().min(1, "Subject is required").max(200, "Subject too long"),
2489
- message: zod.z.string().min(1, "Message is required").max(5e3, "Message too long")
2490
- });
2491
2758
  var CreateTicketDialog = () => {
2759
+ const st = useSupportT();
2492
2760
  const { uiState, createTicket, closeCreateDialog } = useSupportLayoutContext();
2493
2761
  const { toast } = uiCore.useToast();
2494
- const [isSubmitting, setIsSubmitting] = React7__default.default.useState(false);
2762
+ const [isSubmitting, setIsSubmitting] = React8__default.default.useState(false);
2763
+ const labels = React8.useMemo(() => ({
2764
+ title: st("createTicket.title"),
2765
+ description: st("createTicket.description"),
2766
+ subjectLabel: st("createTicket.subjectLabel"),
2767
+ subjectPlaceholder: st("createTicket.subjectPlaceholder"),
2768
+ messageLabel: st("createTicket.messageLabel"),
2769
+ messagePlaceholder: st("createTicket.messagePlaceholder"),
2770
+ cancel: st("createTicket.cancel"),
2771
+ creating: st("createTicket.creating"),
2772
+ create: st("createTicket.create"),
2773
+ ticketCreated: st("messages.ticketCreated"),
2774
+ ticketCreateFailed: st("messages.ticketCreateFailed"),
2775
+ validation: {
2776
+ subjectRequired: st("validation.subjectRequired"),
2777
+ subjectTooLong: st("validation.subjectTooLong"),
2778
+ messageRequired: st("validation.messageRequired"),
2779
+ messageTooLong: st("validation.messageTooLong")
2780
+ }
2781
+ }), [st]);
2782
+ const createTicketSchema = React8.useMemo(() => zod.z.object({
2783
+ subject: zod.z.string().min(1, labels.validation.subjectRequired).max(200, labels.validation.subjectTooLong),
2784
+ message: zod.z.string().min(1, labels.validation.messageRequired).max(5e3, labels.validation.messageTooLong)
2785
+ }), [labels.validation]);
2495
2786
  const form = reactHookForm.useForm({
2496
2787
  resolver: zod$1.zodResolver(createTicketSchema),
2497
2788
  defaultValues: {
@@ -2504,10 +2795,10 @@ var CreateTicketDialog = () => {
2504
2795
  try {
2505
2796
  await createTicket(data);
2506
2797
  form.reset();
2507
- toast.success("Support ticket created successfully");
2798
+ toast.success(labels.ticketCreated);
2508
2799
  } catch (error) {
2509
2800
  supportLogger.error("Failed to create ticket:", error);
2510
- toast.error("Failed to create ticket. Please try again.");
2801
+ toast.error(labels.ticketCreateFailed);
2511
2802
  } finally {
2512
2803
  setIsSubmitting(false);
2513
2804
  }
@@ -2520,9 +2811,9 @@ var CreateTicketDialog = () => {
2520
2811
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.DialogHeader, { children: [
2521
2812
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.DialogTitle, { className: "flex items-center gap-2", children: [
2522
2813
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-5 w-5" }),
2523
- "Create Support Ticket"
2814
+ labels.title
2524
2815
  ] }),
2525
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogDescription, { children: "Describe your issue and we'll help you resolve it as quickly as possible." })
2816
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogDescription, { children: labels.description })
2526
2817
  ] }),
2527
2818
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Form, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-6", children: [
2528
2819
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2531,8 +2822,8 @@ var CreateTicketDialog = () => {
2531
2822
  control: form.control,
2532
2823
  name: "subject",
2533
2824
  render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(uiCore.FormItem, { children: [
2534
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: "Subject" }),
2535
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.Input, { placeholder: "Brief description of your issue...", ...field }) }),
2825
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: labels.subjectLabel }),
2826
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.Input, { placeholder: labels.subjectPlaceholder, ...field }) }),
2536
2827
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormMessage, {})
2537
2828
  ] })
2538
2829
  }
@@ -2543,11 +2834,11 @@ var CreateTicketDialog = () => {
2543
2834
  control: form.control,
2544
2835
  name: "message",
2545
2836
  render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(uiCore.FormItem, { children: [
2546
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: "Message" }),
2837
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: labels.messageLabel }),
2547
2838
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(
2548
2839
  uiCore.Textarea,
2549
2840
  {
2550
- placeholder: "Describe your issue in detail. Include any error messages, steps to reproduce, or relevant information...",
2841
+ placeholder: labels.messagePlaceholder,
2551
2842
  className: "min-h-[120px]",
2552
2843
  ...field
2553
2844
  }
@@ -2564,24 +2855,31 @@ var CreateTicketDialog = () => {
2564
2855
  variant: "outline",
2565
2856
  onClick: handleClose,
2566
2857
  disabled: isSubmitting,
2567
- children: "Cancel"
2858
+ children: labels.cancel
2568
2859
  }
2569
2860
  ),
2570
2861
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2571
2862
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
2572
- "Creating..."
2863
+ labels.creating
2573
2864
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2574
2865
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2575
- "Create Ticket"
2866
+ labels.create
2576
2867
  ] }) })
2577
2868
  ] })
2578
2869
  ] }) })
2579
2870
  ] }) });
2580
2871
  };
2581
2872
  var SupportLayoutContent = () => {
2873
+ const st = useSupportT();
2582
2874
  const { selectedTicket, selectTicket, openCreateDialog, getUnreadCount } = useSupportLayoutContext();
2583
- const [isMobile, setIsMobile] = React7__default.default.useState(false);
2584
- React7__default.default.useEffect(() => {
2875
+ const [isMobile, setIsMobile] = React8__default.default.useState(false);
2876
+ const labels = React8.useMemo(() => ({
2877
+ title: st("layout.title"),
2878
+ titleShort: st("layout.titleShort"),
2879
+ subtitle: st("layout.subtitle"),
2880
+ newTicket: st("layout.newTicket")
2881
+ }), [st]);
2882
+ React8__default.default.useEffect(() => {
2585
2883
  const checkMobile = () => setIsMobile(window.innerWidth <= 768);
2586
2884
  checkMobile();
2587
2885
  window.addEventListener("resize", checkMobile);
@@ -2602,12 +2900,12 @@ var SupportLayoutContent = () => {
2602
2900
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-5 w-5" })
2603
2901
  }
2604
2902
  ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LifeBuoy, { className: "h-6 w-6 text-primary" }),
2605
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: selectedTicket ? selectedTicket.subject : "Support" }),
2903
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: selectedTicket ? selectedTicket.subject : labels.titleShort }),
2606
2904
  unreadCount > 0 && !selectedTicket && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-5 bg-red-500 text-white text-xs rounded-full flex items-center justify-center", children: unreadCount })
2607
2905
  ] }),
2608
2906
  !selectedTicket && /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Button, { onClick: openCreateDialog, size: "sm", children: [
2609
2907
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2610
- "New Ticket"
2908
+ labels.newTicket
2611
2909
  ] })
2612
2910
  ] }),
2613
2911
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: selectedTicket ? (
@@ -2628,14 +2926,14 @@ var SupportLayoutContent = () => {
2628
2926
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2629
2927
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LifeBuoy, { className: "h-7 w-7 text-primary" }),
2630
2928
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2631
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold", children: "Support Center" }),
2632
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Get help from our support team" })
2929
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold", children: labels.title }),
2930
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.subtitle })
2633
2931
  ] }),
2634
2932
  unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-6 bg-red-500 text-white text-sm rounded-full flex items-center justify-center", children: unreadCount })
2635
2933
  ] }),
2636
2934
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Button, { onClick: openCreateDialog, children: [
2637
2935
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2638
- "New Ticket"
2936
+ labels.newTicket
2639
2937
  ] })
2640
2938
  ] }),
2641
2939
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResizablePanelGroup, { direction: "horizontal", className: "h-full", children: [
@@ -2656,7 +2954,7 @@ var SupportLayout = () => {
2656
2954
  // package.json
2657
2955
  var package_default = {
2658
2956
  name: "@djangocfg/ext-support",
2659
- version: "1.0.21",
2957
+ version: "1.0.23",
2660
2958
  description: "Support ticket system extension for DjangoCFG",
2661
2959
  keywords: [
2662
2960
  "django",
@@ -2702,6 +3000,11 @@ var package_default = {
2702
3000
  types: "./dist/config.d.ts",
2703
3001
  import: "./dist/config.js",
2704
3002
  require: "./dist/config.cjs"
3003
+ },
3004
+ "./i18n": {
3005
+ types: "./dist/i18n.d.ts",
3006
+ import: "./dist/i18n.js",
3007
+ require: "./dist/i18n.cjs"
2705
3008
  }
2706
3009
  },
2707
3010
  files: [
@@ -2717,27 +3020,31 @@ var package_default = {
2717
3020
  peerDependencies: {
2718
3021
  "@djangocfg/api": "workspace:*",
2719
3022
  "@djangocfg/ext-base": "workspace:*",
3023
+ "@djangocfg/i18n": "workspace:*",
2720
3024
  "@djangocfg/ui-core": "workspace:*",
3025
+ "@hookform/resolvers": "^5.2.2",
2721
3026
  consola: "^3.4.2",
2722
3027
  "lucide-react": "^0.545.0",
2723
3028
  moment: "^2.30.1",
2724
3029
  next: "^16",
3030
+ "next-intl": "^4",
2725
3031
  "p-retry": "^7.0.0",
2726
3032
  react: "^19",
2727
3033
  "react-hook-form": "^7.69.0",
2728
- "@hookform/resolvers": "^5.2.2",
2729
3034
  swr: "^2.3.7",
2730
3035
  zod: "^4.3.4"
2731
3036
  },
2732
3037
  devDependencies: {
2733
3038
  "@djangocfg/api": "workspace:*",
2734
3039
  "@djangocfg/ext-base": "workspace:*",
3040
+ "@djangocfg/i18n": "workspace:*",
2735
3041
  "@djangocfg/ui-core": "workspace:*",
2736
3042
  "@djangocfg/typescript-config": "workspace:*",
2737
3043
  "@types/node": "^24.7.2",
2738
3044
  "@types/react": "^19.0.0",
2739
3045
  consola: "^3.4.2",
2740
3046
  moment: "^2.30.1",
3047
+ "next-intl": "^4.1.0",
2741
3048
  "p-retry": "^7.0.0",
2742
3049
  swr: "^2.3.7",
2743
3050
  tsup: "^8.5.0",