@djangocfg/ext-support 1.0.21 → 1.0.22

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,9 @@ 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 i18n = require('@djangocfg/ext-base/i18n');
10
+ var i18n$1 = require('@djangocfg/i18n');
9
11
  var uiCore = require('@djangocfg/ui-core');
10
12
  var useSWR = require('swr');
11
13
  var jsxRuntime = require('react/jsx-runtime');
@@ -21,7 +23,7 @@ var extBase = require('@djangocfg/ext-base');
21
23
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
24
 
23
25
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
24
- var React7__default = /*#__PURE__*/_interopDefault(React7);
26
+ var React8__default = /*#__PURE__*/_interopDefault(React8);
25
27
  var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
26
28
  var moment2__default = /*#__PURE__*/_interopDefault(moment2);
27
29
  var useSWRInfinite__default = /*#__PURE__*/_interopDefault(useSWRInfinite);
@@ -1625,6 +1627,219 @@ var API = class {
1625
1627
  };
1626
1628
  api.initializeExtensionAPI(configureAPI);
1627
1629
  var apiSupport = api.createExtensionAPI(API);
1630
+
1631
+ // src/i18n/locales/en.ts
1632
+ var en = {
1633
+ layout: {
1634
+ title: "Support Center",
1635
+ titleShort: "Support",
1636
+ subtitle: "Get help from our support team",
1637
+ newTicket: "New Ticket"
1638
+ },
1639
+ status: {
1640
+ open: "Open",
1641
+ waitingForUser: "Waiting for you",
1642
+ waitingForAdmin: "Waiting for support",
1643
+ resolved: "Resolved",
1644
+ closed: "Closed"
1645
+ },
1646
+ ticketList: {
1647
+ noTickets: "No tickets yet",
1648
+ noTicketsDescription: "Create your first support ticket to get help",
1649
+ loadingMore: "Loading more...",
1650
+ loadMore: "Load more",
1651
+ allLoaded: "All {count} tickets loaded"
1652
+ },
1653
+ createTicket: {
1654
+ title: "New Support Ticket",
1655
+ description: "Describe your issue and we will help you",
1656
+ subjectLabel: "Subject",
1657
+ subjectPlaceholder: "Brief description of your issue",
1658
+ messageLabel: "Message",
1659
+ messagePlaceholder: "Describe your issue in detail...",
1660
+ cancel: "Cancel",
1661
+ creating: "Creating...",
1662
+ create: "Create Ticket"
1663
+ },
1664
+ validation: {
1665
+ subjectRequired: "Subject is required",
1666
+ subjectTooLong: "Subject is too long (max 200 characters)",
1667
+ messageRequired: "Message is required",
1668
+ messageTooLong: "Message is too long (max 5000 characters)"
1669
+ },
1670
+ messages: {
1671
+ ticketCreated: "Ticket created successfully",
1672
+ ticketCreateFailed: "Failed to create ticket",
1673
+ messageSent: "Message sent",
1674
+ messageSendFailed: "Failed to send message"
1675
+ },
1676
+ messageInput: {
1677
+ placeholder: "Type your message...",
1678
+ ticketClosed: "Ticket closed",
1679
+ ticketClosedDescription: "This ticket has been closed. Create a new ticket if you need further assistance."
1680
+ },
1681
+ messageList: {
1682
+ noTicketSelected: "No ticket selected",
1683
+ noTicketSelectedDescription: "Select a ticket from the list to view messages",
1684
+ noMessages: "No messages yet",
1685
+ noMessagesDescription: "Start the conversation by sending a message",
1686
+ loadingOlder: "Loading older messages...",
1687
+ loadOlder: "Load older messages",
1688
+ supportTeam: "Support Team",
1689
+ staff: "Staff"
1690
+ },
1691
+ time: {
1692
+ justNow: "Just now",
1693
+ minutesAgo: "{count} min ago",
1694
+ hoursAgo: "{count}h ago",
1695
+ daysAgo: "{count}d ago"
1696
+ }
1697
+ };
1698
+
1699
+ // src/i18n/locales/ru.ts
1700
+ var ru = {
1701
+ layout: {
1702
+ title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1703
+ titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
1704
+ 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",
1705
+ newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1706
+ },
1707
+ status: {
1708
+ open: "\u041E\u0442\u043A\u0440\u044B\u0442",
1709
+ waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
1710
+ waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
1711
+ resolved: "\u0420\u0435\u0448\u0451\u043D",
1712
+ closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
1713
+ },
1714
+ ticketList: {
1715
+ noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
1716
+ 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",
1717
+ loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
1718
+ loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
1719
+ allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
1720
+ },
1721
+ createTicket: {
1722
+ title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
1723
+ 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",
1724
+ subjectLabel: "\u0422\u0435\u043C\u0430",
1725
+ subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
1726
+ messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
1727
+ 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...",
1728
+ cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
1729
+ creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
1730
+ create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1731
+ },
1732
+ validation: {
1733
+ subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
1734
+ 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)",
1735
+ messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
1736
+ 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)"
1737
+ },
1738
+ messages: {
1739
+ ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
1740
+ 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",
1741
+ messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
1742
+ 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"
1743
+ },
1744
+ messageInput: {
1745
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
1746
+ ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
1747
+ 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."
1748
+ },
1749
+ messageList: {
1750
+ noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
1751
+ 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",
1752
+ noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
1753
+ 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",
1754
+ loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
1755
+ loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
1756
+ supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1757
+ staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
1758
+ },
1759
+ time: {
1760
+ justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
1761
+ minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
1762
+ hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
1763
+ daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
1764
+ }
1765
+ };
1766
+
1767
+ // src/i18n/locales/ko.ts
1768
+ var ko = {
1769
+ layout: {
1770
+ title: "\uACE0\uAC1D\uC9C0\uC6D0 \uC13C\uD130",
1771
+ titleShort: "\uACE0\uAC1D\uC9C0\uC6D0",
1772
+ subtitle: "\uACE0\uAC1D\uC9C0\uC6D0\uD300\uC758 \uB3C4\uC6C0\uC744 \uBC1B\uC73C\uC138\uC694",
1773
+ newTicket: "\uC0C8 \uBB38\uC758"
1774
+ },
1775
+ status: {
1776
+ open: "\uC5F4\uB9BC",
1777
+ waitingForUser: "\uD68C\uC2E0 \uB300\uAE30",
1778
+ waitingForAdmin: "\uC9C0\uC6D0\uD300 \uB300\uAE30",
1779
+ resolved: "\uD574\uACB0\uB428",
1780
+ closed: "\uC885\uB8CC"
1781
+ },
1782
+ ticketList: {
1783
+ noTickets: "\uBB38\uC758 \uB0B4\uC5ED \uC5C6\uC74C",
1784
+ noTicketsDescription: "\uCCAB \uBC88\uC9F8 \uBB38\uC758\uB97C \uB4F1\uB85D\uD558\uC5EC \uB3C4\uC6C0\uC744 \uBC1B\uC73C\uC138\uC694",
1785
+ loadingMore: "\uBD88\uB7EC\uC624\uB294 \uC911...",
1786
+ loadMore: "\uB354 \uBCF4\uAE30",
1787
+ allLoaded: "\uCD1D {count}\uAC1C\uC758 \uBB38\uC758\uAC00 \uB85C\uB4DC\uB428"
1788
+ },
1789
+ createTicket: {
1790
+ title: "\uC0C8 \uBB38\uC758",
1791
+ description: "\uBB38\uC81C\uB97C \uC124\uBA85\uD574 \uC8FC\uC2DC\uBA74 \uB3C4\uC6C0\uC744 \uB4DC\uB9AC\uACA0\uC2B5\uB2C8\uB2E4",
1792
+ subjectLabel: "\uC81C\uBAA9",
1793
+ subjectPlaceholder: "\uBB38\uC81C\uC5D0 \uB300\uD55C \uAC04\uB7B5\uD55C \uC124\uBA85",
1794
+ messageLabel: "\uB0B4\uC6A9",
1795
+ messagePlaceholder: "\uBB38\uC81C\uB97C \uC790\uC138\uD788 \uC124\uBA85\uD574 \uC8FC\uC138\uC694...",
1796
+ cancel: "\uCDE8\uC18C",
1797
+ creating: "\uC0DD\uC131 \uC911...",
1798
+ create: "\uBB38\uC758 \uB4F1\uB85D"
1799
+ },
1800
+ validation: {
1801
+ subjectRequired: "\uC81C\uBAA9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694",
1802
+ subjectTooLong: "\uC81C\uBAA9\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4 (\uCD5C\uB300 200\uC790)",
1803
+ messageRequired: "\uB0B4\uC6A9\uC744 \uC785\uB825\uD574 \uC8FC\uC138\uC694",
1804
+ messageTooLong: "\uB0B4\uC6A9\uC774 \uB108\uBB34 \uAE41\uB2C8\uB2E4 (\uCD5C\uB300 5000\uC790)"
1805
+ },
1806
+ messages: {
1807
+ ticketCreated: "\uBB38\uC758\uAC00 \uB4F1\uB85D\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
1808
+ ticketCreateFailed: "\uBB38\uC758 \uB4F1\uB85D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4",
1809
+ messageSent: "\uBA54\uC2DC\uC9C0\uAC00 \uC804\uC1A1\uB418\uC5C8\uC2B5\uB2C8\uB2E4",
1810
+ messageSendFailed: "\uBA54\uC2DC\uC9C0 \uC804\uC1A1\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4"
1811
+ },
1812
+ messageInput: {
1813
+ placeholder: "\uBA54\uC2DC\uC9C0\uB97C \uC785\uB825\uD558\uC138\uC694...",
1814
+ ticketClosed: "\uBB38\uC758 \uC885\uB8CC\uB428",
1815
+ 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."
1816
+ },
1817
+ messageList: {
1818
+ noTicketSelected: "\uBB38\uC758\uAC00 \uC120\uD0DD\uB418\uC9C0 \uC54A\uC74C",
1819
+ noTicketSelectedDescription: "\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB824\uBA74 \uBAA9\uB85D\uC5D0\uC11C \uBB38\uC758\uB97C \uC120\uD0DD\uD558\uC138\uC694",
1820
+ noMessages: "\uBA54\uC2DC\uC9C0 \uC5C6\uC74C",
1821
+ noMessagesDescription: "\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB0B4 \uB300\uD654\uB97C \uC2DC\uC791\uD558\uC138\uC694",
1822
+ loadingOlder: "\uC774\uC804 \uBA54\uC2DC\uC9C0 \uBD88\uB7EC\uC624\uB294 \uC911...",
1823
+ loadOlder: "\uC774\uC804 \uBA54\uC2DC\uC9C0 \uBCF4\uAE30",
1824
+ supportTeam: "\uACE0\uAC1D\uC9C0\uC6D0\uD300",
1825
+ staff: "\uB2F4\uB2F9\uC790"
1826
+ },
1827
+ time: {
1828
+ justNow: "\uBC29\uAE08 \uC804",
1829
+ minutesAgo: "{count}\uBD84 \uC804",
1830
+ hoursAgo: "{count}\uC2DC\uAC04 \uC804",
1831
+ daysAgo: "{count}\uC77C \uC804"
1832
+ }
1833
+ };
1834
+
1835
+ // src/i18n/index.ts
1836
+ var SUPPORT_NAMESPACE = "support";
1837
+ var supportI18n = i18n.createExtensionI18n({
1838
+ namespace: SUPPORT_NAMESPACE,
1839
+ defaultLocale: "en",
1840
+ locales: { en, ru, ko }
1841
+ });
1842
+ supportI18n.getAllTranslations();
1628
1843
  function useSupportTicketsList(params, client) {
1629
1844
  return useSWR__default.default(
1630
1845
  params ? ["cfg-support-tickets", params] : "cfg-support-tickets",
@@ -1717,7 +1932,7 @@ function useDeleteSupportTicketsDestroy() {
1717
1932
  return result;
1718
1933
  };
1719
1934
  }
1720
- var SupportContext = React7.createContext(void 0);
1935
+ var SupportContext = React8.createContext(void 0);
1721
1936
  function SupportProvider({ children }) {
1722
1937
  const {
1723
1938
  data: ticketsData,
@@ -1817,7 +2032,7 @@ function SupportProvider({ children }) {
1817
2032
  return /* @__PURE__ */ jsxRuntime.jsx(SupportContext.Provider, { value, children });
1818
2033
  }
1819
2034
  function useSupportContext() {
1820
- const context = React7.useContext(SupportContext);
2035
+ const context = React8.useContext(SupportContext);
1821
2036
  if (!context) {
1822
2037
  throw new Error("useSupportContext must be used within SupportProvider");
1823
2038
  }
@@ -1839,18 +2054,36 @@ var getStatusBadgeVariant = (status) => {
1839
2054
  return "default";
1840
2055
  }
1841
2056
  };
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
2057
  var TicketCard = ({ ticket, isSelected, onClick }) => {
2058
+ const baseT = i18n$1.useT();
2059
+ const st = i18n.createTypedExtensionT(baseT, SUPPORT_NAMESPACE);
2060
+ const labels = React8.useMemo(() => ({
2061
+ status: {
2062
+ open: st("status.open"),
2063
+ waiting_for_user: st("status.waitingForUser"),
2064
+ waiting_for_admin: st("status.waitingForAdmin"),
2065
+ resolved: st("status.resolved"),
2066
+ closed: st("status.closed")
2067
+ },
2068
+ time: {
2069
+ justNow: st("time.justNow"),
2070
+ minutesAgo: st("time.minutesAgo"),
2071
+ hoursAgo: st("time.hoursAgo"),
2072
+ daysAgo: st("time.daysAgo")
2073
+ }
2074
+ }), [st]);
2075
+ const formatRelativeTime = React8.useCallback((date) => {
2076
+ if (!date) return "N/A";
2077
+ const m = moment2__default.default.utc(date).local();
2078
+ const now = moment2__default.default();
2079
+ const diffInSeconds = now.diff(m, "seconds");
2080
+ if (diffInSeconds < 60) return labels.time.justNow;
2081
+ if (diffInSeconds < 3600) return labels.time.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
2082
+ if (diffInSeconds < 86400) return labels.time.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
2083
+ if (diffInSeconds < 604800) return labels.time.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
2084
+ return m.format("MMM D, YYYY");
2085
+ }, [labels.time]);
2086
+ const statusLabel = labels.status[ticket.status] || ticket.status || labels.status.open;
1854
2087
  return /* @__PURE__ */ jsxRuntime.jsx(
1855
2088
  uiCore.Card,
1856
2089
  {
@@ -1874,7 +2107,7 @@ var TicketCard = ({ ticket, isSelected, onClick }) => {
1874
2107
  )
1875
2108
  ] }),
1876
2109
  /* @__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" }),
2110
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs", children: statusLabel }),
1878
2111
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
1879
2112
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
1880
2113
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatRelativeTime(ticket.created_at) })
@@ -2018,11 +2251,11 @@ function useInfiniteMessages(ticketUuid) {
2018
2251
  addMessage
2019
2252
  };
2020
2253
  }
2021
- var SupportLayoutContext = React7.createContext(void 0);
2254
+ var SupportLayoutContext = React8.createContext(void 0);
2022
2255
  function SupportLayoutProvider({ children }) {
2023
2256
  const support = useSupportContext();
2024
2257
  const { user } = auth.useAuth();
2025
- const [uiState, setUIState] = React7.useState({
2258
+ const [uiState, setUIState] = React8.useState({
2026
2259
  selectedTicketUuid: null,
2027
2260
  isCreateDialogOpen: false,
2028
2261
  viewMode: "list"
@@ -2038,7 +2271,7 @@ function SupportLayoutProvider({ children }) {
2038
2271
  refresh: refreshMessages,
2039
2272
  addMessage: addMessageOptimistically
2040
2273
  } = useInfiniteMessages(selectedTicket?.uuid || null);
2041
- const selectTicket = React7.useCallback(async (ticket) => {
2274
+ const selectTicket = React8.useCallback(async (ticket) => {
2042
2275
  setUIState((prev) => ({ ...prev, selectedTicketUuid: ticket?.uuid || null }));
2043
2276
  if (ticket?.uuid) {
2044
2277
  window.dispatchEvent(
@@ -2046,7 +2279,7 @@ function SupportLayoutProvider({ children }) {
2046
2279
  );
2047
2280
  }
2048
2281
  }, []);
2049
- const createTicket = React7.useCallback(async (data) => {
2282
+ const createTicket = React8.useCallback(async (data) => {
2050
2283
  if (!user?.id) {
2051
2284
  throw new Error("User must be authenticated to create tickets");
2052
2285
  }
@@ -2069,7 +2302,7 @@ function SupportLayoutProvider({ children }) {
2069
2302
  new CustomEvent(SUPPORT_LAYOUT_EVENTS.TICKET_SELECTED, { detail: { ticket } })
2070
2303
  );
2071
2304
  }, [support, user]);
2072
- const sendMessage = React7.useCallback(async (message) => {
2305
+ const sendMessage = React8.useCallback(async (message) => {
2073
2306
  if (!selectedTicket?.uuid) return;
2074
2307
  const messageData = {
2075
2308
  text: message
@@ -2099,16 +2332,16 @@ function SupportLayoutProvider({ children }) {
2099
2332
  new CustomEvent(SUPPORT_LAYOUT_EVENTS.MESSAGE_SENT, { detail: { message: newMessage } })
2100
2333
  );
2101
2334
  }, [selectedTicket, support, user, addMessageOptimistically, refreshMessages]);
2102
- const openCreateDialog = React7.useCallback(() => {
2335
+ const openCreateDialog = React8.useCallback(() => {
2103
2336
  setUIState((prev) => ({ ...prev, isCreateDialogOpen: true }));
2104
2337
  }, []);
2105
- const closeCreateDialog = React7.useCallback(() => {
2338
+ const closeCreateDialog = React8.useCallback(() => {
2106
2339
  setUIState((prev) => ({ ...prev, isCreateDialogOpen: false }));
2107
2340
  }, []);
2108
- const getUnreadCount = React7.useCallback(() => {
2341
+ const getUnreadCount = React8.useCallback(() => {
2109
2342
  return support.tickets?.reduce((count, ticket) => count + (ticket.unanswered_messages_count || 0), 0) || 0;
2110
2343
  }, [support.tickets]);
2111
- React7.useEffect(() => {
2344
+ React8.useEffect(() => {
2112
2345
  const handleOpenDialog = () => openCreateDialog();
2113
2346
  const handleCloseDialog = () => closeCreateDialog();
2114
2347
  window.addEventListener(SUPPORT_LAYOUT_EVENTS.OPEN_CREATE_DIALOG, handleOpenDialog);
@@ -2142,13 +2375,15 @@ function SupportLayoutProvider({ children }) {
2142
2375
  return /* @__PURE__ */ jsxRuntime.jsx(SupportLayoutContext.Provider, { value, children });
2143
2376
  }
2144
2377
  function useSupportLayoutContext() {
2145
- const context = React7.useContext(SupportLayoutContext);
2378
+ const context = React8.useContext(SupportLayoutContext);
2146
2379
  if (!context) {
2147
2380
  throw new Error("useSupportLayoutContext must be used within SupportLayoutProvider");
2148
2381
  }
2149
2382
  return context;
2150
2383
  }
2151
2384
  var TicketList = () => {
2385
+ const baseT = i18n$1.useT();
2386
+ const st = i18n.createTypedExtensionT(baseT, SUPPORT_NAMESPACE);
2152
2387
  const { selectedTicket, selectTicket } = useSupportLayoutContext();
2153
2388
  const {
2154
2389
  tickets,
@@ -2159,10 +2394,17 @@ var TicketList = () => {
2159
2394
  totalCount,
2160
2395
  refresh
2161
2396
  } = useInfiniteTickets();
2162
- const scrollRef = React7.useRef(null);
2163
- const observerRef = React7.useRef(null);
2164
- const loadMoreRef = React7.useRef(null);
2165
- React7.useEffect(() => {
2397
+ const labels = React8.useMemo(() => ({
2398
+ noTickets: st("ticketList.noTickets"),
2399
+ noTicketsDescription: st("ticketList.noTicketsDescription"),
2400
+ loadingMore: st("ticketList.loadingMore"),
2401
+ loadMore: st("ticketList.loadMore"),
2402
+ allLoaded: st("ticketList.allLoaded")
2403
+ }), [st]);
2404
+ const scrollRef = React8.useRef(null);
2405
+ const observerRef = React8.useRef(null);
2406
+ const loadMoreRef = React8.useRef(null);
2407
+ React8.useEffect(() => {
2166
2408
  const handleTicketCreated = () => {
2167
2409
  refresh();
2168
2410
  };
@@ -2171,7 +2413,7 @@ var TicketList = () => {
2171
2413
  window.removeEventListener(SUPPORT_LAYOUT_EVENTS.TICKET_CREATED, handleTicketCreated);
2172
2414
  };
2173
2415
  }, [refresh]);
2174
- React7.useEffect(() => {
2416
+ React8.useEffect(() => {
2175
2417
  if (observerRef.current) {
2176
2418
  observerRef.current.disconnect();
2177
2419
  }
@@ -2204,8 +2446,8 @@ var TicketList = () => {
2204
2446
  if (!tickets || tickets.length === 0) {
2205
2447
  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
2448
  /* @__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" })
2449
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTickets }),
2450
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketsDescription })
2209
2451
  ] });
2210
2452
  }
2211
2453
  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 +2470,7 @@ var TicketList = () => {
2228
2470
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "h-2" }),
2229
2471
  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
2472
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2231
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: "Loading more tickets..." })
2473
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: labels.loadingMore })
2232
2474
  ] }) }),
2233
2475
  hasMore && !isLoadingMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
2234
2476
  uiCore.Button,
@@ -2238,17 +2480,14 @@ var TicketList = () => {
2238
2480
  onClick: loadMore,
2239
2481
  className: "text-xs",
2240
2482
  children: [
2241
- "Load more (",
2483
+ labels.loadMore,
2484
+ " (",
2242
2485
  totalCount > 0 ? `${tickets.length}/${totalCount}` : "",
2243
2486
  ")"
2244
2487
  ]
2245
2488
  }
2246
2489
  ) }),
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
- ] })
2490
+ !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
2491
  ] }) });
2253
2492
  };
2254
2493
  var formatTime = (date) => {
@@ -2259,7 +2498,7 @@ var formatDate = (date) => {
2259
2498
  if (!date) return "";
2260
2499
  return moment2__default.default.utc(date).local().format("MMM D, YYYY");
2261
2500
  };
2262
- var MessageBubble = ({ message, isFromUser, currentUser }) => {
2501
+ var MessageBubble = ({ message, isFromUser, currentUser, labels }) => {
2263
2502
  const sender = message.sender;
2264
2503
  const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
2265
2504
  const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
@@ -2269,11 +2508,11 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
2269
2508
  className: `flex gap-3 ${isFromUser ? "justify-end" : "justify-start"}
2270
2509
  animate-in fade-in slide-in-from-bottom-2 duration-300`,
2271
2510
  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 }) }),
2511
+ !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
2512
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col gap-1 flex-1 max-w-[80%] ${isFromUser ? "items-end" : "items-start"}`, children: [
2274
2513
  !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)"
2514
+ sender.display_username || sender.email || labels.supportTeam,
2515
+ sender.is_staff && ` (${labels.staff})`
2277
2516
  ] }),
2278
2517
  /* @__PURE__ */ jsxRuntime.jsx(
2279
2518
  uiCore.Card,
@@ -2290,8 +2529,20 @@ var MessageBubble = ({ message, isFromUser, currentUser }) => {
2290
2529
  );
2291
2530
  };
2292
2531
  var MessageList = () => {
2532
+ const baseT = i18n$1.useT();
2533
+ const st = i18n.createTypedExtensionT(baseT, SUPPORT_NAMESPACE);
2293
2534
  const { selectedTicket } = useSupportLayoutContext();
2294
2535
  const { user } = auth.useAuth();
2536
+ const labels = React8.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]);
2295
2546
  const {
2296
2547
  messages,
2297
2548
  isLoading,
@@ -2300,12 +2551,12 @@ var MessageList = () => {
2300
2551
  totalCount,
2301
2552
  loadMore
2302
2553
  } = 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(() => {
2554
+ const scrollRef = React8.useRef(null);
2555
+ const scrollAreaRef = React8.useRef(null);
2556
+ const observerRef = React8.useRef(null);
2557
+ const loadMoreRef = React8.useRef(null);
2558
+ const firstRender = React8.useRef(true);
2559
+ React8.useEffect(() => {
2309
2560
  if (observerRef.current) {
2310
2561
  observerRef.current.disconnect();
2311
2562
  }
@@ -2326,7 +2577,7 @@ var MessageList = () => {
2326
2577
  }
2327
2578
  };
2328
2579
  }, [hasMore, isLoadingMore, loadMore]);
2329
- React7.useEffect(() => {
2580
+ React8.useEffect(() => {
2330
2581
  if (firstRender.current && messages.length > 0) {
2331
2582
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2332
2583
  if (scrollContainer) {
@@ -2335,7 +2586,7 @@ var MessageList = () => {
2335
2586
  firstRender.current = false;
2336
2587
  }
2337
2588
  }, [messages]);
2338
- const handleLoadMore = React7.useCallback(() => {
2589
+ const handleLoadMore = React8.useCallback(() => {
2339
2590
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2340
2591
  const previousHeight = scrollContainer?.scrollHeight || 0;
2341
2592
  loadMore();
@@ -2349,8 +2600,8 @@ var MessageList = () => {
2349
2600
  if (!selectedTicket) {
2350
2601
  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
2602
  /* @__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" })
2603
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTicketSelected }),
2604
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketSelectedDescription })
2354
2605
  ] });
2355
2606
  }
2356
2607
  if (isLoading) {
@@ -2370,15 +2621,15 @@ var MessageList = () => {
2370
2621
  if (!messages || messages.length === 0) {
2371
2622
  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
2623
  /* @__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" })
2624
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noMessages }),
2625
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noMessagesDescription })
2375
2626
  ] });
2376
2627
  }
2377
2628
  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
2629
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "h-2" }),
2379
2630
  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
2631
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2381
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: "Loading older messages..." })
2632
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: labels.loadingOlder })
2382
2633
  ] }) }),
2383
2634
  hasMore && !isLoadingMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
2384
2635
  uiCore.Button,
@@ -2388,7 +2639,8 @@ var MessageList = () => {
2388
2639
  onClick: handleLoadMore,
2389
2640
  className: "text-xs",
2390
2641
  children: [
2391
- "Load older messages (",
2642
+ labels.loadOlder,
2643
+ " (",
2392
2644
  totalCount > 0 ? `${messages.length}/${totalCount}` : "",
2393
2645
  ")"
2394
2646
  ]
@@ -2403,7 +2655,7 @@ var MessageList = () => {
2403
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);
2404
2656
  const previousMessage = index > 0 ? messages[index - 1] : null;
2405
2657
  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: [
2658
+ return /* @__PURE__ */ jsxRuntime.jsxs(React8__default.default.Fragment, { children: [
2407
2659
  showDateSeparator && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 my-4", children: [
2408
2660
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
2409
2661
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(message.created_at) }),
@@ -2414,7 +2666,8 @@ var MessageList = () => {
2414
2666
  {
2415
2667
  message,
2416
2668
  isFromUser: !!isFromUser,
2417
- currentUser: user
2669
+ currentUser: user,
2670
+ labels: { supportTeam: labels.supportTeam, staff: labels.staff }
2418
2671
  }
2419
2672
  ) })
2420
2673
  ] }, message.uuid);
@@ -2428,10 +2681,19 @@ var logger = consola.createConsola({
2428
2681
  }).withTag("ext-support");
2429
2682
  var supportLogger = logger;
2430
2683
  var MessageInput = () => {
2684
+ const baseT = i18n$1.useT();
2685
+ const st = i18n.createTypedExtensionT(baseT, SUPPORT_NAMESPACE);
2431
2686
  const { selectedTicket, sendMessage } = useSupportLayoutContext();
2432
2687
  const { toast } = hooks.useToast();
2433
- const [message, setMessage] = React7.useState("");
2434
- const [isSending, setIsSending] = React7.useState(false);
2688
+ const [message, setMessage] = React8.useState("");
2689
+ const [isSending, setIsSending] = React8.useState(false);
2690
+ const labels = React8.useMemo(() => ({
2691
+ placeholder: st("messageInput.placeholder"),
2692
+ ticketClosed: st("messageInput.ticketClosed"),
2693
+ ticketClosedDescription: st("messageInput.ticketClosedDescription"),
2694
+ messageSent: st("messages.messageSent"),
2695
+ messageSendFailed: st("messages.messageSendFailed")
2696
+ }), [st]);
2435
2697
  const handleSubmit = async (e) => {
2436
2698
  e.preventDefault();
2437
2699
  if (!message.trim() || !selectedTicket) return;
@@ -2439,10 +2701,10 @@ var MessageInput = () => {
2439
2701
  try {
2440
2702
  await sendMessage(message.trim());
2441
2703
  setMessage("");
2442
- toast.success("Message sent successfully");
2704
+ toast.success(labels.messageSent);
2443
2705
  } catch (error) {
2444
2706
  supportLogger.error("Failed to send message:", error);
2445
- toast.error("Failed to send message");
2707
+ toast.error(labels.messageSendFailed);
2446
2708
  } finally {
2447
2709
  setIsSending(false);
2448
2710
  }
@@ -2465,8 +2727,8 @@ var MessageInput = () => {
2465
2727
  value: message,
2466
2728
  onChange: (e) => setMessage(e.target.value),
2467
2729
  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",
2730
+ placeholder: canSendMessage ? labels.placeholder : labels.ticketClosed,
2731
+ className: "min-h-[60px] max-h-[200px] transition-all duration-200\n focus:ring-2 focus:ring-primary/20",
2470
2732
  disabled: !canSendMessage || isSending
2471
2733
  }
2472
2734
  ),
@@ -2481,17 +2743,38 @@ var MessageInput = () => {
2481
2743
  }
2482
2744
  )
2483
2745
  ] }),
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." })
2746
+ !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
2747
  ] });
2486
2748
  };
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
2749
  var CreateTicketDialog = () => {
2750
+ const baseT = i18n$1.useT();
2751
+ const st = i18n.createTypedExtensionT(baseT, SUPPORT_NAMESPACE);
2492
2752
  const { uiState, createTicket, closeCreateDialog } = useSupportLayoutContext();
2493
2753
  const { toast } = uiCore.useToast();
2494
- const [isSubmitting, setIsSubmitting] = React7__default.default.useState(false);
2754
+ const [isSubmitting, setIsSubmitting] = React8__default.default.useState(false);
2755
+ const labels = React8.useMemo(() => ({
2756
+ title: st("createTicket.title"),
2757
+ description: st("createTicket.description"),
2758
+ subjectLabel: st("createTicket.subjectLabel"),
2759
+ subjectPlaceholder: st("createTicket.subjectPlaceholder"),
2760
+ messageLabel: st("createTicket.messageLabel"),
2761
+ messagePlaceholder: st("createTicket.messagePlaceholder"),
2762
+ cancel: st("createTicket.cancel"),
2763
+ creating: st("createTicket.creating"),
2764
+ create: st("createTicket.create"),
2765
+ ticketCreated: st("messages.ticketCreated"),
2766
+ ticketCreateFailed: st("messages.ticketCreateFailed"),
2767
+ validation: {
2768
+ subjectRequired: st("validation.subjectRequired"),
2769
+ subjectTooLong: st("validation.subjectTooLong"),
2770
+ messageRequired: st("validation.messageRequired"),
2771
+ messageTooLong: st("validation.messageTooLong")
2772
+ }
2773
+ }), [st]);
2774
+ const createTicketSchema = React8.useMemo(() => zod.z.object({
2775
+ subject: zod.z.string().min(1, labels.validation.subjectRequired).max(200, labels.validation.subjectTooLong),
2776
+ message: zod.z.string().min(1, labels.validation.messageRequired).max(5e3, labels.validation.messageTooLong)
2777
+ }), [labels.validation]);
2495
2778
  const form = reactHookForm.useForm({
2496
2779
  resolver: zod$1.zodResolver(createTicketSchema),
2497
2780
  defaultValues: {
@@ -2504,10 +2787,10 @@ var CreateTicketDialog = () => {
2504
2787
  try {
2505
2788
  await createTicket(data);
2506
2789
  form.reset();
2507
- toast.success("Support ticket created successfully");
2790
+ toast.success(labels.ticketCreated);
2508
2791
  } catch (error) {
2509
2792
  supportLogger.error("Failed to create ticket:", error);
2510
- toast.error("Failed to create ticket. Please try again.");
2793
+ toast.error(labels.ticketCreateFailed);
2511
2794
  } finally {
2512
2795
  setIsSubmitting(false);
2513
2796
  }
@@ -2520,9 +2803,9 @@ var CreateTicketDialog = () => {
2520
2803
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.DialogHeader, { children: [
2521
2804
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.DialogTitle, { className: "flex items-center gap-2", children: [
2522
2805
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-5 w-5" }),
2523
- "Create Support Ticket"
2806
+ labels.title
2524
2807
  ] }),
2525
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogDescription, { children: "Describe your issue and we'll help you resolve it as quickly as possible." })
2808
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.DialogDescription, { children: labels.description })
2526
2809
  ] }),
2527
2810
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Form, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-6", children: [
2528
2811
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2531,8 +2814,8 @@ var CreateTicketDialog = () => {
2531
2814
  control: form.control,
2532
2815
  name: "subject",
2533
2816
  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 }) }),
2817
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: labels.subjectLabel }),
2818
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.Input, { placeholder: labels.subjectPlaceholder, ...field }) }),
2536
2819
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormMessage, {})
2537
2820
  ] })
2538
2821
  }
@@ -2543,11 +2826,11 @@ var CreateTicketDialog = () => {
2543
2826
  control: form.control,
2544
2827
  name: "message",
2545
2828
  render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs(uiCore.FormItem, { children: [
2546
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: "Message" }),
2829
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormLabel, { children: labels.messageLabel }),
2547
2830
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(
2548
2831
  uiCore.Textarea,
2549
2832
  {
2550
- placeholder: "Describe your issue in detail. Include any error messages, steps to reproduce, or relevant information...",
2833
+ placeholder: labels.messagePlaceholder,
2551
2834
  className: "min-h-[120px]",
2552
2835
  ...field
2553
2836
  }
@@ -2564,24 +2847,32 @@ var CreateTicketDialog = () => {
2564
2847
  variant: "outline",
2565
2848
  onClick: handleClose,
2566
2849
  disabled: isSubmitting,
2567
- children: "Cancel"
2850
+ children: labels.cancel
2568
2851
  }
2569
2852
  ),
2570
2853
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2571
2854
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
2572
- "Creating..."
2855
+ labels.creating
2573
2856
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2574
2857
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2575
- "Create Ticket"
2858
+ labels.create
2576
2859
  ] }) })
2577
2860
  ] })
2578
2861
  ] }) })
2579
2862
  ] }) });
2580
2863
  };
2581
2864
  var SupportLayoutContent = () => {
2865
+ const baseT = i18n$1.useT();
2866
+ const st = i18n.createTypedExtensionT(baseT, SUPPORT_NAMESPACE);
2582
2867
  const { selectedTicket, selectTicket, openCreateDialog, getUnreadCount } = useSupportLayoutContext();
2583
- const [isMobile, setIsMobile] = React7__default.default.useState(false);
2584
- React7__default.default.useEffect(() => {
2868
+ const [isMobile, setIsMobile] = React8__default.default.useState(false);
2869
+ const labels = React8.useMemo(() => ({
2870
+ title: st("layout.title"),
2871
+ titleShort: st("layout.titleShort"),
2872
+ subtitle: st("layout.subtitle"),
2873
+ newTicket: st("layout.newTicket")
2874
+ }), [st]);
2875
+ React8__default.default.useEffect(() => {
2585
2876
  const checkMobile = () => setIsMobile(window.innerWidth <= 768);
2586
2877
  checkMobile();
2587
2878
  window.addEventListener("resize", checkMobile);
@@ -2602,12 +2893,12 @@ var SupportLayoutContent = () => {
2602
2893
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-5 w-5" })
2603
2894
  }
2604
2895
  ) : /* @__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" }),
2896
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: selectedTicket ? selectedTicket.subject : labels.titleShort }),
2606
2897
  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
2898
  ] }),
2608
2899
  !selectedTicket && /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Button, { onClick: openCreateDialog, size: "sm", children: [
2609
2900
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2610
- "New Ticket"
2901
+ labels.newTicket
2611
2902
  ] })
2612
2903
  ] }),
2613
2904
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: selectedTicket ? (
@@ -2628,14 +2919,14 @@ var SupportLayoutContent = () => {
2628
2919
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2629
2920
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LifeBuoy, { className: "h-7 w-7 text-primary" }),
2630
2921
  /* @__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" })
2922
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold", children: labels.title }),
2923
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.subtitle })
2633
2924
  ] }),
2634
2925
  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
2926
  ] }),
2636
2927
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Button, { onClick: openCreateDialog, children: [
2637
2928
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2638
- "New Ticket"
2929
+ labels.newTicket
2639
2930
  ] })
2640
2931
  ] }),
2641
2932
  /* @__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 +2947,7 @@ var SupportLayout = () => {
2656
2947
  // package.json
2657
2948
  var package_default = {
2658
2949
  name: "@djangocfg/ext-support",
2659
- version: "1.0.21",
2950
+ version: "1.0.22",
2660
2951
  description: "Support ticket system extension for DjangoCFG",
2661
2952
  keywords: [
2662
2953
  "django",
@@ -2702,6 +2993,11 @@ var package_default = {
2702
2993
  types: "./dist/config.d.ts",
2703
2994
  import: "./dist/config.js",
2704
2995
  require: "./dist/config.cjs"
2996
+ },
2997
+ "./i18n": {
2998
+ types: "./dist/i18n.d.ts",
2999
+ import: "./dist/i18n.js",
3000
+ require: "./dist/i18n.cjs"
2705
3001
  }
2706
3002
  },
2707
3003
  files: [
@@ -2717,6 +3013,7 @@ var package_default = {
2717
3013
  peerDependencies: {
2718
3014
  "@djangocfg/api": "workspace:*",
2719
3015
  "@djangocfg/ext-base": "workspace:*",
3016
+ "@djangocfg/i18n": "workspace:*",
2720
3017
  "@djangocfg/ui-core": "workspace:*",
2721
3018
  consola: "^3.4.2",
2722
3019
  "lucide-react": "^0.545.0",
@@ -2732,6 +3029,7 @@ var package_default = {
2732
3029
  devDependencies: {
2733
3030
  "@djangocfg/api": "workspace:*",
2734
3031
  "@djangocfg/ext-base": "workspace:*",
3032
+ "@djangocfg/i18n": "workspace:*",
2735
3033
  "@djangocfg/ui-core": "workspace:*",
2736
3034
  "@djangocfg/typescript-config": "workspace:*",
2737
3035
  "@types/node": "^24.7.2",