@djangocfg/ext-support 1.0.24 → 1.0.26

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.
Files changed (38) hide show
  1. package/dist/config.cjs +1 -1
  2. package/dist/config.js +1 -1
  3. package/dist/hooks.cjs +398 -334
  4. package/dist/hooks.d.cts +6 -2
  5. package/dist/hooks.d.ts +6 -2
  6. package/dist/hooks.js +356 -291
  7. package/dist/i18n.cjs +72 -72
  8. package/dist/i18n.js +73 -73
  9. package/dist/index-D-xo66K9.d.cts +862 -0
  10. package/dist/index-D-xo66K9.d.ts +863 -0
  11. package/dist/index-Dov7pn8Z.d.ts +2 -1
  12. package/dist/index-rR_XqXq1.d.cts +838 -0
  13. package/dist/index-rR_XqXq1.d.ts +838 -0
  14. package/dist/index.cjs +398 -334
  15. package/dist/index.d.cts +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +356 -291
  18. package/package.json +8 -8
  19. package/src/adapters/api.ts +4 -5
  20. package/src/adapters/demo.ts +2 -1
  21. package/src/api/generated/ext_support/_utils/fetchers/ext_support__support.ts +4 -4
  22. package/src/api/generated/ext_support/_utils/hooks/ext_support__support.ts +2 -2
  23. package/src/api/generated/ext_support/_utils/schemas/Ticket.schema.ts +1 -1
  24. package/src/api/generated/ext_support/_utils/schemas/TicketRequest.schema.ts +1 -1
  25. package/src/api/generated/ext_support/enums.ts +0 -30
  26. package/src/api/generated/ext_support/ext_support__support/client.ts +6 -6
  27. package/src/api/generated/ext_support/ext_support__support/models.ts +2 -2
  28. package/src/api/generated/ext_support/schema.json +36 -0
  29. package/src/components/CreateTicketSheet.tsx +7 -12
  30. package/src/components/SupportHero.tsx +35 -47
  31. package/src/components/TicketItem.tsx +28 -74
  32. package/src/components/TicketList.tsx +30 -23
  33. package/src/components/TicketSheet.tsx +246 -162
  34. package/src/contexts/SupportExtensionProvider.tsx +4 -4
  35. package/src/contexts/types.ts +2 -2
  36. package/src/i18n/useSupportT.ts +3 -3
  37. package/src/utils/status.ts +44 -0
  38. package/src/utils/time.ts +88 -0
package/dist/hooks.cjs CHANGED
@@ -4,7 +4,7 @@ var api = require('@djangocfg/ext-base/api');
4
4
  var consola = require('consola');
5
5
  var pRetry = require('p-retry');
6
6
  var zod = require('zod');
7
- var React4 = require('react');
7
+ var react = require('react');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
9
  var useSWRInfinite = require('swr/infinite');
10
10
  var auth = require('@djangocfg/api/auth');
@@ -13,7 +13,7 @@ var lucideReact = require('lucide-react');
13
13
  var nextIntl = require('next-intl');
14
14
  var uiCore = require('@djangocfg/ui-core');
15
15
  var lib = require('@djangocfg/ui-core/lib');
16
- var moment2 = require('moment');
16
+ var moment = require('moment');
17
17
  var reactHookForm = require('react-hook-form');
18
18
  var zod$1 = require('@hookform/resolvers/zod');
19
19
  var extBase = require('@djangocfg/ext-base');
@@ -21,10 +21,9 @@ var extBase = require('@djangocfg/ext-base');
21
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
22
 
23
23
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
24
- var React4__default = /*#__PURE__*/_interopDefault(React4);
25
24
  var useSWRInfinite__default = /*#__PURE__*/_interopDefault(useSWRInfinite);
26
25
  var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
27
- var moment2__default = /*#__PURE__*/_interopDefault(moment2);
26
+ var moment__default = /*#__PURE__*/_interopDefault(moment);
28
27
 
29
28
  // src/api/generated/ext_support/ext_support__support/client.ts
30
29
  var ExtSupportSupport = class {
@@ -43,7 +42,7 @@ var ExtSupportSupport = class {
43
42
  if (isParamsObject) {
44
43
  params = args[0];
45
44
  } else {
46
- params = { page: args[0], page_size: args[1] };
45
+ params = { ordering: args[0], page: args[1], page_size: args[2], search: args[3] };
47
46
  }
48
47
  const response = await this.client.request("GET", "/cfg/support/tickets/", { params });
49
48
  return response;
@@ -68,7 +67,7 @@ var ExtSupportSupport = class {
68
67
  if (isParamsObject) {
69
68
  params = args[1];
70
69
  } else {
71
- params = { page: args[1], page_size: args[2] };
70
+ params = { ordering: args[1], page: args[2], page_size: args[3], search: args[4] };
72
71
  }
73
72
  const response = await this.client.request("GET", `/cfg/support/tickets/${ticket_uuid}/messages/`, { params });
74
73
  return response;
@@ -759,22 +758,6 @@ var PatchedTicketRequestStatus = /* @__PURE__ */ ((PatchedTicketRequestStatus2)
759
758
  PatchedTicketRequestStatus2["CLOSED"] = "closed";
760
759
  return PatchedTicketRequestStatus2;
761
760
  })(PatchedTicketRequestStatus || {});
762
- var TicketStatus = /* @__PURE__ */ ((TicketStatus2) => {
763
- TicketStatus2["OPEN"] = "open";
764
- TicketStatus2["WAITING_FOR_USER"] = "waiting_for_user";
765
- TicketStatus2["WAITING_FOR_ADMIN"] = "waiting_for_admin";
766
- TicketStatus2["RESOLVED"] = "resolved";
767
- TicketStatus2["CLOSED"] = "closed";
768
- return TicketStatus2;
769
- })(TicketStatus || {});
770
- var TicketRequestStatus = /* @__PURE__ */ ((TicketRequestStatus2) => {
771
- TicketRequestStatus2["OPEN"] = "open";
772
- TicketRequestStatus2["WAITING_FOR_USER"] = "waiting_for_user";
773
- TicketRequestStatus2["WAITING_FOR_ADMIN"] = "waiting_for_admin";
774
- TicketRequestStatus2["RESOLVED"] = "resolved";
775
- TicketRequestStatus2["CLOSED"] = "closed";
776
- return TicketRequestStatus2;
777
- })(TicketRequestStatus || {});
778
761
  var SenderSchema = zod.z.object({
779
762
  id: zod.z.int(),
780
763
  display_username: zod.z.string(),
@@ -818,7 +801,7 @@ var TicketSchema = zod.z.object({
818
801
  uuid: zod.z.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i),
819
802
  user: zod.z.int(),
820
803
  subject: zod.z.string().max(255),
821
- status: zod.z.nativeEnum(TicketStatus).optional(),
804
+ status: zod.z.nativeEnum(PatchedTicketRequestStatus).optional(),
822
805
  created_at: zod.z.string().datetime({ offset: true }),
823
806
  unanswered_messages_count: zod.z.int()
824
807
  });
@@ -846,7 +829,7 @@ zod.z.object({
846
829
  zod.z.object({
847
830
  user: zod.z.int(),
848
831
  subject: zod.z.string().min(1).max(255),
849
- status: zod.z.nativeEnum(TicketRequestStatus).optional()
832
+ status: zod.z.nativeEnum(PatchedTicketRequestStatus).optional()
850
833
  });
851
834
 
852
835
  // src/api/generated/ext_support/api-instance.ts
@@ -882,7 +865,7 @@ function configureAPI(config) {
882
865
  // src/api/generated/ext_support/_utils/fetchers/ext_support__support.ts
883
866
  async function getSupportTicketsList(params, client) {
884
867
  const api = client || getAPIInstance();
885
- const response = await api.ext_support_support.ticketsList(params?.page, params?.page_size);
868
+ const response = await api.ext_support_support.ticketsList(params?.ordering, params?.page, params?.page_size, params?.search);
886
869
  try {
887
870
  return PaginatedTicketListSchema.parse(response);
888
871
  } catch (error) {
@@ -966,7 +949,7 @@ Method: POST`);
966
949
  }
967
950
  async function getSupportTicketsMessagesList(ticket_uuid, params, client) {
968
951
  const api = client || getAPIInstance();
969
- const response = await api.ext_support_support.ticketsMessagesList(ticket_uuid, params?.page, params?.page_size);
952
+ const response = await api.ext_support_support.ticketsMessagesList(ticket_uuid, params?.ordering, params?.page, params?.page_size, params?.search);
970
953
  try {
971
954
  return PaginatedMessageListSchema.parse(response);
972
955
  } catch (error) {
@@ -1440,13 +1423,13 @@ var API = class {
1440
1423
  // src/api/index.ts
1441
1424
  api.initializeExtensionAPI(configureAPI);
1442
1425
  var apiSupport = api.createExtensionAPI(API);
1443
- var SupportContext = React4.createContext(void 0);
1426
+ var SupportContext = react.createContext(void 0);
1444
1427
  function SupportProvider({ children, adapter }) {
1445
- const value = React4.useMemo(() => ({ adapter }), [adapter]);
1428
+ const value = react.useMemo(() => ({ adapter }), [adapter]);
1446
1429
  return /* @__PURE__ */ jsxRuntime.jsx(SupportContext.Provider, { value, children });
1447
1430
  }
1448
1431
  function useAdapter() {
1449
- const context = React4.useContext(SupportContext);
1432
+ const context = react.useContext(SupportContext);
1450
1433
  if (!context) {
1451
1434
  throw new Error("useSupport hooks must be used within SupportProvider");
1452
1435
  }
@@ -1606,18 +1589,18 @@ function useApiTickets() {
1606
1589
  revalidateFirstPage: false,
1607
1590
  parallel: false
1608
1591
  });
1609
- const tickets = React4.useMemo(() => {
1592
+ const tickets = react.useMemo(() => {
1610
1593
  return ticketsData ? ticketsData.flatMap((page) => page.results) : [];
1611
1594
  }, [ticketsData]);
1612
1595
  const hasMore = ticketsData && ticketsData[ticketsData.length - 1]?.has_next || false;
1613
1596
  const totalCount = ticketsData && ticketsData[ticketsData.length - 1]?.count || 0;
1614
1597
  const isLoadingMore = isValidating && ticketsData && typeof ticketsData[size - 1] !== "undefined";
1615
- const loadMore = React4.useCallback(() => {
1598
+ const loadMore = react.useCallback(() => {
1616
1599
  if (hasMore && !isLoadingMore) {
1617
1600
  setSize(size + 1);
1618
1601
  }
1619
1602
  }, [hasMore, isLoadingMore, size, setSize]);
1620
- const refresh = React4.useCallback(async () => {
1603
+ const refresh = react.useCallback(async () => {
1621
1604
  await mutate();
1622
1605
  }, [mutate]);
1623
1606
  return {
@@ -1651,21 +1634,21 @@ function useApiMessages(ticketUuid) {
1651
1634
  revalidateFirstPage: false,
1652
1635
  parallel: false
1653
1636
  });
1654
- const messages = React4.useMemo(() => {
1637
+ const messages = react.useMemo(() => {
1655
1638
  return data ? data.flatMap((page) => page.results) : [];
1656
1639
  }, [data]);
1657
1640
  const hasMore = data && data[data.length - 1]?.has_next || false;
1658
1641
  const totalCount = data && data[data.length - 1]?.count || 0;
1659
1642
  const isLoadingMore = isValidating && data && typeof data[size - 1] !== "undefined";
1660
- const loadMore = React4.useCallback(() => {
1643
+ const loadMore = react.useCallback(() => {
1661
1644
  if (hasMore && !isLoadingMore) {
1662
1645
  setSize(size + 1);
1663
1646
  }
1664
1647
  }, [hasMore, isLoadingMore, size, setSize]);
1665
- const refresh = React4.useCallback(async () => {
1648
+ const refresh = react.useCallback(async () => {
1666
1649
  await mutate();
1667
1650
  }, [mutate]);
1668
- const addOptimistic = React4.useCallback((message) => {
1651
+ const addOptimistic = react.useCallback((message) => {
1669
1652
  if (!data || !data[0]) return;
1670
1653
  const newData = [...data];
1671
1654
  const firstPage = newData[0];
@@ -1695,7 +1678,7 @@ function useApiOperations() {
1695
1678
  const { user } = auth.useAuth();
1696
1679
  const createTicketMutation = useCreateSupportTicketsCreate();
1697
1680
  const createMessageMutation = useCreateSupportTicketsMessagesCreate();
1698
- const createTicket = React4.useCallback(async (input) => {
1681
+ const createTicket = react.useCallback(async (input) => {
1699
1682
  if (!user?.id) {
1700
1683
  throw new Error("User must be authenticated to create tickets");
1701
1684
  }
@@ -1710,7 +1693,7 @@ function useApiOperations() {
1710
1693
  }
1711
1694
  return ticket;
1712
1695
  }, [user, createTicketMutation, createMessageMutation]);
1713
- const sendMessage = React4.useCallback(async (input) => {
1696
+ const sendMessage = react.useCallback(async (input) => {
1714
1697
  const message = await createMessageMutation(input.ticketUuid, {
1715
1698
  text: input.text
1716
1699
  });
@@ -1738,7 +1721,7 @@ function createApiAdapter() {
1738
1721
  }
1739
1722
  function useApiAdapter() {
1740
1723
  const { createTicket, sendMessage } = useApiOperations();
1741
- return React4.useMemo(() => ({
1724
+ return react.useMemo(() => ({
1742
1725
  useTickets: useApiTickets,
1743
1726
  useMessages: useApiMessages,
1744
1727
  createTicket,
@@ -1887,8 +1870,8 @@ function addMessage(ticketUuid, message) {
1887
1870
  notifySubscribers();
1888
1871
  }
1889
1872
  function useDemoTickets() {
1890
- const [, forceUpdate] = React4.useState({});
1891
- React4.useMemo(() => {
1873
+ const [, forceUpdate] = react.useState({});
1874
+ react.useMemo(() => {
1892
1875
  const update = () => forceUpdate({});
1893
1876
  subscribers.add(update);
1894
1877
  return () => {
@@ -1908,15 +1891,15 @@ function useDemoTickets() {
1908
1891
  };
1909
1892
  }
1910
1893
  function useDemoMessages(ticketUuid) {
1911
- const [, forceUpdate] = React4.useState({});
1912
- React4.useMemo(() => {
1894
+ const [, forceUpdate] = react.useState({});
1895
+ react.useMemo(() => {
1913
1896
  const update = () => forceUpdate({});
1914
1897
  subscribers.add(update);
1915
1898
  return () => {
1916
1899
  subscribers.delete(update);
1917
1900
  };
1918
1901
  }, []);
1919
- const messages = React4.useMemo(() => {
1902
+ const messages = react.useMemo(() => {
1920
1903
  if (!ticketUuid) return [];
1921
1904
  return demoMessages[ticketUuid] || [];
1922
1905
  }, [ticketUuid]);
@@ -2059,78 +2042,6 @@ var en = {
2059
2042
  }
2060
2043
  };
2061
2044
 
2062
- // src/i18n/locales/ru.ts
2063
- var ru = {
2064
- layout: {
2065
- title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
2066
- titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
2067
- 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",
2068
- newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
2069
- },
2070
- hero: {
2071
- openTickets: "\u041E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
2072
- unreadMessages: "\u041D\u0435\u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u0445"
2073
- },
2074
- status: {
2075
- open: "\u041E\u0442\u043A\u0440\u044B\u0442",
2076
- waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
2077
- waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
2078
- resolved: "\u0420\u0435\u0448\u0451\u043D",
2079
- closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
2080
- },
2081
- ticketList: {
2082
- noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
2083
- 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",
2084
- loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
2085
- loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
2086
- allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
2087
- },
2088
- createTicket: {
2089
- title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
2090
- 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",
2091
- subjectLabel: "\u0422\u0435\u043C\u0430",
2092
- subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
2093
- messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
2094
- 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...",
2095
- cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
2096
- creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
2097
- create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
2098
- },
2099
- validation: {
2100
- subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
2101
- 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)",
2102
- messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
2103
- 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)"
2104
- },
2105
- messages: {
2106
- ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
2107
- 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",
2108
- messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
2109
- 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"
2110
- },
2111
- messageInput: {
2112
- placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
2113
- ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
2114
- 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."
2115
- },
2116
- messageList: {
2117
- noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
2118
- 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",
2119
- noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
2120
- 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",
2121
- loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
2122
- loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
2123
- supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
2124
- staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
2125
- },
2126
- time: {
2127
- justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
2128
- minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
2129
- hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
2130
- daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
2131
- }
2132
- };
2133
-
2134
2045
  // src/i18n/locales/ko.ts
2135
2046
  var ko = {
2136
2047
  layout: {
@@ -2203,6 +2114,78 @@ var ko = {
2203
2114
  }
2204
2115
  };
2205
2116
 
2117
+ // src/i18n/locales/ru.ts
2118
+ var ru = {
2119
+ layout: {
2120
+ title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
2121
+ titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
2122
+ 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",
2123
+ newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
2124
+ },
2125
+ hero: {
2126
+ openTickets: "\u041E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
2127
+ unreadMessages: "\u041D\u0435\u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u0445"
2128
+ },
2129
+ status: {
2130
+ open: "\u041E\u0442\u043A\u0440\u044B\u0442",
2131
+ waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
2132
+ waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
2133
+ resolved: "\u0420\u0435\u0448\u0451\u043D",
2134
+ closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
2135
+ },
2136
+ ticketList: {
2137
+ noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
2138
+ 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",
2139
+ loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
2140
+ loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
2141
+ allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
2142
+ },
2143
+ createTicket: {
2144
+ title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
2145
+ 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",
2146
+ subjectLabel: "\u0422\u0435\u043C\u0430",
2147
+ subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
2148
+ messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
2149
+ 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...",
2150
+ cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
2151
+ creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
2152
+ create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
2153
+ },
2154
+ validation: {
2155
+ subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
2156
+ 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)",
2157
+ messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
2158
+ 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)"
2159
+ },
2160
+ messages: {
2161
+ ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
2162
+ 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",
2163
+ messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
2164
+ 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"
2165
+ },
2166
+ messageInput: {
2167
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
2168
+ ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
2169
+ 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."
2170
+ },
2171
+ messageList: {
2172
+ noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
2173
+ 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",
2174
+ noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
2175
+ 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",
2176
+ loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
2177
+ loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
2178
+ supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
2179
+ staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
2180
+ },
2181
+ time: {
2182
+ justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
2183
+ minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
2184
+ hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
2185
+ daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
2186
+ }
2187
+ };
2188
+
2206
2189
  // src/i18n/useSupportT.ts
2207
2190
  var translations = { en, ru, ko };
2208
2191
  function getNestedValue(obj, path) {
@@ -2219,8 +2202,8 @@ function getNestedValue(obj, path) {
2219
2202
  }
2220
2203
  function useSupportT() {
2221
2204
  const locale = nextIntl.useLocale();
2222
- const t = React4.useMemo(() => translations[locale] || translations.en, [locale]);
2223
- return React4.useCallback(
2205
+ const t = react.useMemo(() => translations[locale] || translations.en, [locale]);
2206
+ return react.useCallback(
2224
2207
  (key) => getNestedValue(t, key),
2225
2208
  [t]
2226
2209
  );
@@ -2228,7 +2211,7 @@ function useSupportT() {
2228
2211
  function SupportHero({ onCreateTicket, className }) {
2229
2212
  const st = useSupportT();
2230
2213
  const { tickets, unreadCount, isLoadingTickets, refreshTickets } = useSupport();
2231
- const labels = React4.useMemo(() => ({
2214
+ const labels = react.useMemo(() => ({
2232
2215
  title: st("layout.title"),
2233
2216
  subtitle: st("layout.subtitle"),
2234
2217
  newTicket: st("layout.newTicket"),
@@ -2236,27 +2219,22 @@ function SupportHero({ onCreateTicket, className }) {
2236
2219
  unreadMessages: st("hero.unreadMessages")
2237
2220
  }), [st]);
2238
2221
  const openTicketsCount = tickets.filter((t) => t.status !== "closed" && t.status !== "resolved").length;
2239
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center py-16 px-4", className), children: [
2240
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-6", children: isLoadingTickets ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2241
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-12 w-12 mx-auto mb-4 rounded-full" }),
2242
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-32 mx-auto mb-2" }),
2243
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-4 w-48 mx-auto" })
2244
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2245
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-16 w-16 mx-auto mb-4 bg-primary/10 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LifeBuoy, { className: "h-8 w-8 text-primary" }) }),
2246
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold tracking-tight", children: labels.title }),
2247
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mt-1", children: labels.subtitle })
2248
- ] }) }),
2249
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2250
- /* @__PURE__ */ jsxRuntime.jsxs(
2222
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center py-12 px-4", className), children: [
2223
+ isLoadingTickets ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2224
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-10 w-48 mx-auto mb-2" }),
2225
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-5 w-64 mx-auto" })
2226
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2227
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl font-bold tracking-tight", children: labels.title }),
2228
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground mt-2", children: labels.subtitle })
2229
+ ] }),
2230
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mt-6", children: [
2231
+ /* @__PURE__ */ jsxRuntime.jsx(
2251
2232
  uiCore.Button,
2252
2233
  {
2253
2234
  size: "lg",
2254
2235
  onClick: onCreateTicket,
2255
- className: "rounded-full px-6",
2256
- children: [
2257
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-5 w-5 mr-2" }),
2258
- labels.newTicket
2259
- ]
2236
+ className: "rounded-full px-8 h-12 text-base",
2237
+ children: labels.newTicket
2260
2238
  }
2261
2239
  ),
2262
2240
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2265,46 +2243,103 @@ function SupportHero({ onCreateTicket, className }) {
2265
2243
  size: "icon",
2266
2244
  variant: "ghost",
2267
2245
  onClick: () => refreshTickets(),
2268
- className: "rounded-full",
2246
+ className: "rounded-full h-10 w-10",
2269
2247
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-4 w-4" })
2270
2248
  }
2271
2249
  )
2272
2250
  ] }),
2273
- !isLoadingTickets && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-6 mt-6 text-sm text-muted-foreground", children: [
2274
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2275
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-medium text-foreground text-xl tabular-nums", children: openTicketsCount }),
2276
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.openTickets })
2251
+ !isLoadingTickets && (openTicketsCount > 0 || unreadCount > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-4", children: [
2252
+ openTicketsCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-muted-foreground bg-muted px-3 py-1 rounded-full", children: [
2253
+ openTicketsCount,
2254
+ " ",
2255
+ labels.openTickets.toLowerCase()
2277
2256
  ] }),
2278
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-px bg-border" }),
2279
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
2280
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn(
2281
- "block font-medium text-xl tabular-nums",
2282
- unreadCount > 0 ? "text-destructive" : "text-foreground"
2283
- ), children: unreadCount }),
2284
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.unreadMessages })
2257
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-destructive bg-destructive/10 px-3 py-1 rounded-full", children: [
2258
+ unreadCount,
2259
+ " ",
2260
+ labels.unreadMessages.toLowerCase()
2285
2261
  ] })
2286
2262
  ] })
2287
2263
  ] });
2288
2264
  }
2289
- var getStatusBadgeVariant = (status) => {
2290
- switch (status) {
2291
- case "open":
2292
- return "default";
2293
- case "waiting_for_user":
2294
- return "secondary";
2295
- case "waiting_for_admin":
2296
- return "outline";
2297
- case "resolved":
2298
- return "outline";
2299
- case "closed":
2300
- return "secondary";
2301
- default:
2302
- return "default";
2265
+
2266
+ // src/utils/status.ts
2267
+ var ticketStatusConfig = {
2268
+ open: {
2269
+ color: "text-blue-600 dark:text-blue-400",
2270
+ bg: "bg-blue-500/10"
2271
+ },
2272
+ waiting_for_user: {
2273
+ color: "text-orange-600 dark:text-orange-400",
2274
+ bg: "bg-orange-500/10"
2275
+ },
2276
+ waiting_for_admin: {
2277
+ color: "text-muted-foreground",
2278
+ bg: "bg-muted"
2279
+ },
2280
+ resolved: {
2281
+ color: "text-green-600 dark:text-green-400",
2282
+ bg: "bg-green-500/10"
2283
+ },
2284
+ closed: {
2285
+ color: "text-muted-foreground",
2286
+ bg: "bg-muted"
2303
2287
  }
2304
2288
  };
2289
+ function getStatusConfig(status) {
2290
+ return ticketStatusConfig[status] || ticketStatusConfig.open;
2291
+ }
2292
+ function formatMessageTime(date) {
2293
+ if (!date) return "";
2294
+ return moment__default.default.utc(date).local().format("h:mm A");
2295
+ }
2296
+ function formatDateSeparator(date) {
2297
+ if (!date) return "";
2298
+ const m = moment__default.default.utc(date).local();
2299
+ const now = moment__default.default();
2300
+ const today = now.clone().startOf("day");
2301
+ const yesterday = now.clone().subtract(1, "day").startOf("day");
2302
+ if (m.isSame(today, "day")) {
2303
+ return "Today";
2304
+ }
2305
+ if (m.isSame(yesterday, "day")) {
2306
+ return "Yesterday";
2307
+ }
2308
+ if (m.isSame(now, "year")) {
2309
+ return m.format("MMM D");
2310
+ }
2311
+ return m.format("MMM D, YYYY");
2312
+ }
2313
+ function formatRelativeTime(date, labels) {
2314
+ if (!date) return "N/A";
2315
+ const m = moment__default.default.utc(date).local();
2316
+ const now = moment__default.default();
2317
+ const diffInSeconds = now.diff(m, "seconds");
2318
+ if (diffInSeconds < 60) {
2319
+ return labels.justNow;
2320
+ }
2321
+ if (diffInSeconds < 3600) {
2322
+ return labels.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
2323
+ }
2324
+ if (diffInSeconds < 86400) {
2325
+ return labels.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
2326
+ }
2327
+ if (diffInSeconds < 604800) {
2328
+ return labels.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
2329
+ }
2330
+ return m.format("MMM D, YYYY");
2331
+ }
2332
+ function isSameDay(date1, date2) {
2333
+ if (!date1 || !date2) return false;
2334
+ return moment__default.default.utc(date1).format("YYYY-MM-DD") === moment__default.default.utc(date2).format("YYYY-MM-DD");
2335
+ }
2336
+ function formatTicketDate(date) {
2337
+ if (!date) return "";
2338
+ return moment__default.default.utc(date).local().format("MMM D, YYYY");
2339
+ }
2305
2340
  function TicketItem({ ticket, onClick }) {
2306
2341
  const st = useSupportT();
2307
- const labels = React4.useMemo(() => ({
2342
+ const labels = react.useMemo(() => ({
2308
2343
  status: {
2309
2344
  open: st("status.open"),
2310
2345
  waiting_for_user: st("status.waitingForUser"),
@@ -2319,49 +2354,37 @@ function TicketItem({ ticket, onClick }) {
2319
2354
  daysAgo: st("time.daysAgo")
2320
2355
  }
2321
2356
  }), [st]);
2322
- const formatRelativeTime = React4.useCallback((date) => {
2323
- if (!date) return "N/A";
2324
- const m = moment2__default.default.utc(date).local();
2325
- const now = moment2__default.default();
2326
- const diffInSeconds = now.diff(m, "seconds");
2327
- if (diffInSeconds < 60) return labels.time.justNow;
2328
- if (diffInSeconds < 3600) return labels.time.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
2329
- if (diffInSeconds < 86400) return labels.time.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
2330
- if (diffInSeconds < 604800) return labels.time.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
2331
- return m.format("MMM D, YYYY");
2357
+ const getRelativeTime = react.useCallback((date) => {
2358
+ return formatRelativeTime(date, labels.time);
2332
2359
  }, [labels.time]);
2333
2360
  const statusLabel = labels.status[ticket.status] || ticket.status || labels.status.open;
2361
+ const statusConfig = getStatusConfig(ticket.status || "open");
2334
2362
  const hasUnread = (ticket.unanswered_messages_count || 0) > 0;
2335
2363
  return /* @__PURE__ */ jsxRuntime.jsxs(
2336
- "div",
2364
+ "button",
2337
2365
  {
2366
+ type: "button",
2338
2367
  className: lib.cn(
2339
- "group flex items-center gap-4 p-4 rounded-xl",
2340
- "cursor-pointer transition-all duration-200",
2341
- "hover:bg-accent/50",
2342
- "active:scale-[0.98]",
2343
- hasUnread && "bg-primary/5"
2368
+ "w-full flex items-start gap-3 p-4 text-left",
2369
+ "transition-colors duration-150",
2370
+ "hover:bg-accent/50 active:bg-accent"
2344
2371
  ),
2345
2372
  onClick,
2346
2373
  children: [
2347
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 flex-shrink-0", children: hasUnread && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-primary animate-pulse" }) }),
2374
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 pt-1.5 shrink-0", children: hasUnread && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-blue-500" }) }),
2348
2375
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2349
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2350
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: lib.cn(
2351
- "font-medium text-sm line-clamp-1",
2352
- hasUnread && "font-semibold"
2353
- ), children: ticket.subject }),
2354
- hasUnread && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: "destructive", className: "shrink-0 text-xs", children: ticket.unanswered_messages_count })
2355
- ] }),
2356
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mt-1 text-xs text-muted-foreground", children: [
2357
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs py-0", children: statusLabel }),
2358
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
2359
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
2360
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatRelativeTime(ticket.created_at) })
2376
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: lib.cn(
2377
+ "text-[15px] line-clamp-1",
2378
+ hasUnread ? "font-semibold" : "font-medium"
2379
+ ), children: ticket.subject }),
2380
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
2381
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("text-[13px]", statusConfig.color), children: statusLabel }),
2382
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[13px] text-muted-foreground", children: [
2383
+ "\xB7 ",
2384
+ getRelativeTime(ticket.created_at)
2361
2385
  ] })
2362
2386
  ] })
2363
- ] }),
2364
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5 text-muted-foreground/50 group-hover:text-muted-foreground transition-colors flex-shrink-0" })
2387
+ ] })
2365
2388
  ]
2366
2389
  }
2367
2390
  );
@@ -2376,15 +2399,15 @@ function TicketList({ onTicketClick, className }) {
2376
2399
  totalCount: totalTicketsCount,
2377
2400
  loadMore: loadMoreTickets
2378
2401
  } = useTickets();
2379
- const labels = React4.useMemo(() => ({
2402
+ const labels = react.useMemo(() => ({
2380
2403
  noTickets: st("ticketList.noTickets"),
2381
2404
  noTicketsDescription: st("ticketList.noTicketsDescription"),
2382
2405
  loadingMore: st("ticketList.loadingMore"),
2383
2406
  loadMore: st("ticketList.loadMore")
2384
2407
  }), [st]);
2385
- const observerRef = React4.useRef(null);
2386
- const loadMoreRef = React4.useRef(null);
2387
- React4.useEffect(() => {
2408
+ const observerRef = react.useRef(null);
2409
+ const loadMoreRef = react.useRef(null);
2410
+ react.useEffect(() => {
2388
2411
  if (observerRef.current) {
2389
2412
  observerRef.current.disconnect();
2390
2413
  }
@@ -2406,24 +2429,23 @@ function TicketList({ onTicketClick, className }) {
2406
2429
  };
2407
2430
  }, [hasMoreTickets, isLoadingMoreTickets, loadMoreTickets]);
2408
2431
  if (isLoadingTickets) {
2409
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("px-4 py-6 space-y-3", className), children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx(
2432
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: lib.cn("px-4 py-6", className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border/50", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4", children: /* @__PURE__ */ jsxRuntime.jsx(
2410
2433
  uiCore.Skeleton,
2411
2434
  {
2412
- className: "h-20 w-full",
2435
+ className: "h-12 w-full",
2413
2436
  style: { animationDelay: `${i * 100}ms` }
2414
- },
2415
- i
2416
- )) });
2437
+ }
2438
+ ) }, i)) }) });
2417
2439
  }
2418
2440
  if (!tickets || tickets.length === 0) {
2419
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center justify-center py-16 px-8 text-center", className), children: [
2420
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-16 w-16 mx-auto mb-4 bg-muted rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-8 w-8 text-muted-foreground" }) }),
2421
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTickets }),
2422
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketsDescription })
2441
+ return /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Empty, { className: lib.cn("py-16", className), children: [
2442
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.EmptyMedia, { variant: "icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-8 w-8" }) }),
2443
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.EmptyTitle, { children: labels.noTickets }),
2444
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.EmptyDescription, { children: labels.noTicketsDescription })
2423
2445
  ] });
2424
2446
  }
2425
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("px-4 py-6", className), children: [
2426
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: tickets.map((ticket) => /* @__PURE__ */ jsxRuntime.jsx(
2447
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("px-4 py-2", className), children: [
2448
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border/50", children: tickets.map((ticket) => /* @__PURE__ */ jsxRuntime.jsx(
2427
2449
  TicketItem,
2428
2450
  {
2429
2451
  ticket,
@@ -2453,59 +2475,81 @@ function TicketList({ onTicketClick, className }) {
2453
2475
  ) })
2454
2476
  ] });
2455
2477
  }
2456
- var formatTime = (date) => {
2457
- if (!date) return "";
2458
- return moment2__default.default.utc(date).local().format("hh:mm A");
2459
- };
2460
- var formatDate = (date) => {
2461
- if (!date) return "";
2462
- return moment2__default.default.utc(date).local().format("MMM D, YYYY");
2463
- };
2464
- var getStatusBadgeVariant2 = (status) => {
2465
- switch (status) {
2466
- case "open":
2467
- return "default";
2468
- case "waiting_for_user":
2469
- return "secondary";
2470
- case "waiting_for_admin":
2471
- return "outline";
2472
- case "resolved":
2473
- return "outline";
2474
- case "closed":
2475
- return "secondary";
2476
- default:
2477
- return "default";
2478
+ function groupMessages(messages, currentUserId) {
2479
+ const groups = [];
2480
+ let currentGroup = null;
2481
+ for (const msg of messages) {
2482
+ const isFromUser = msg.sender?.id && currentUserId && String(msg.sender.id) === String(currentUserId) || msg.is_from_author;
2483
+ const senderId = msg.sender?.id || null;
2484
+ const shouldStartNewGroup = !currentGroup || currentGroup.isFromUser !== isFromUser || currentGroup.senderId !== senderId;
2485
+ if (shouldStartNewGroup) {
2486
+ currentGroup = {
2487
+ senderId,
2488
+ isFromUser: !!isFromUser,
2489
+ messages: [msg]
2490
+ };
2491
+ groups.push(currentGroup);
2492
+ } else {
2493
+ currentGroup.messages.push(msg);
2494
+ }
2478
2495
  }
2479
- };
2480
- var MessageBubble = ({ message, isFromUser, currentUser, labels }) => {
2496
+ return groups;
2497
+ }
2498
+ var DateSeparator = ({ date }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center my-4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground bg-muted/60 px-3 py-1 rounded-full", children: formatDateSeparator(date) }) });
2499
+ var MessageBubble = ({
2500
+ message,
2501
+ isFromUser,
2502
+ showAvatar,
2503
+ showTimestamp,
2504
+ currentUser,
2505
+ labels
2506
+ }) => {
2481
2507
  const sender = message.sender;
2482
2508
  const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
2483
2509
  const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
2484
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex gap-3", isFromUser ? "justify-end" : "justify-start"), children: [
2485
- !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 }) }),
2486
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col gap-1 flex-1 max-w-[80%]", isFromUser ? "items-end" : "items-start"), children: [
2487
- !isFromUser && sender && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground px-1", children: [
2488
- sender.display_username || sender.email || labels.supportTeam,
2489
- sender.is_staff && ` (${labels.staff})`
2490
- ] }),
2491
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Card, { className: lib.cn(
2492
- isFromUser ? "bg-primary text-primary-foreground" : "bg-muted"
2493
- ), children: /* @__PURE__ */ jsxRuntime.jsx(uiCore.CardContent, { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm whitespace-pre-wrap break-words", children: message.text }) }) }),
2494
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground px-1", children: formatTime(message.created_at) })
2510
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex gap-2", isFromUser ? "justify-end" : "justify-start"), children: [
2511
+ !isFromUser && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 shrink-0", children: showAvatar && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Avatar, { className: "h-8 w-8", 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 text-xs", children: sender?.is_staff ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Headphones, { className: "h-4 w-4" }) : senderInitial }) }) }),
2512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col gap-0.5 max-w-[85%]", isFromUser ? "items-end" : "items-start"), children: [
2513
+ /* @__PURE__ */ jsxRuntime.jsx(
2514
+ "div",
2515
+ {
2516
+ className: lib.cn(
2517
+ "px-4 py-2.5 rounded-[20px]",
2518
+ isFromUser ? "bg-blue-500 text-white" : "bg-[#e9e9eb] dark:bg-zinc-800 text-foreground"
2519
+ ),
2520
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[15px] leading-relaxed whitespace-pre-wrap break-words", children: message.text })
2521
+ }
2522
+ ),
2523
+ showTimestamp && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground px-2 mt-0.5", children: formatMessageTime(message.created_at) })
2495
2524
  ] }),
2496
- isFromUser && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Avatar, { className: "h-8 w-8 shrink-0", children: currentUser?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarImage, { src: currentUser.avatar, alt: currentUser.display_username || currentUser.email || "You" }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarFallback, { className: "bg-primary/10 text-primary font-semibold", children: userInitial || /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4" }) }) })
2525
+ isFromUser && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 shrink-0", children: showAvatar && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Avatar, { className: "h-8 w-8", children: currentUser?.avatar ? /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarImage, { src: currentUser.avatar, alt: currentUser.display_username || currentUser.email || "You" }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.AvatarFallback, { className: "bg-blue-500 text-white text-xs font-medium", children: userInitial || /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4" }) }) }) })
2497
2526
  ] });
2498
2527
  };
2528
+ var MessageGroupComponent = ({ group, currentUser, labels }) => {
2529
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: group.messages.map((msg, idx) => /* @__PURE__ */ jsxRuntime.jsx(
2530
+ MessageBubble,
2531
+ {
2532
+ message: msg,
2533
+ isFromUser: group.isFromUser,
2534
+ showAvatar: idx === 0,
2535
+ showTimestamp: idx === group.messages.length - 1,
2536
+ currentUser,
2537
+ labels
2538
+ },
2539
+ msg.uuid
2540
+ )) });
2541
+ };
2499
2542
  function TicketSheet({ ticket, open, onOpenChange }) {
2500
2543
  const st = useSupportT();
2501
2544
  const { user } = auth.useAuth();
2502
2545
  const { sendMessage } = useSupport();
2503
2546
  const { toast } = uiCore.useToast();
2504
- const [message, setMessage] = React4.useState("");
2505
- const [isSending, setIsSending] = React4.useState(false);
2506
- const scrollAreaRef = React4.useRef(null);
2507
- const firstRender = React4.useRef(true);
2508
- const labels = React4.useMemo(() => ({
2547
+ const [message, setMessage] = react.useState("");
2548
+ const [isSending, setIsSending] = react.useState(false);
2549
+ const scrollAreaRef = react.useRef(null);
2550
+ const textareaRef = react.useRef(null);
2551
+ const firstRender = react.useRef(true);
2552
+ const labels = react.useMemo(() => ({
2509
2553
  noMessages: st("messageList.noMessages"),
2510
2554
  noMessagesDescription: st("messageList.noMessagesDescription"),
2511
2555
  loadingOlder: st("messageList.loadingOlder"),
@@ -2535,10 +2579,11 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2535
2579
  refresh: refreshMessages,
2536
2580
  addOptimistic
2537
2581
  } = useMessages(ticket?.uuid || null);
2538
- React4.useEffect(() => {
2582
+ const messageGroups = react.useMemo(() => groupMessages(messages, user?.id), [messages, user?.id]);
2583
+ react.useEffect(() => {
2539
2584
  firstRender.current = true;
2540
2585
  }, [ticket?.uuid]);
2541
- React4.useEffect(() => {
2586
+ react.useEffect(() => {
2542
2587
  if (firstRender.current && messages.length > 0) {
2543
2588
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2544
2589
  if (scrollContainer) {
@@ -2547,6 +2592,14 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2547
2592
  firstRender.current = false;
2548
2593
  }
2549
2594
  }, [messages]);
2595
+ const handleTextareaChange = react.useCallback((e) => {
2596
+ setMessage(e.target.value);
2597
+ const textarea = textareaRef.current;
2598
+ if (textarea) {
2599
+ textarea.style.height = "auto";
2600
+ textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`;
2601
+ }
2602
+ }, []);
2550
2603
  const handleSend = async (e) => {
2551
2604
  e.preventDefault();
2552
2605
  if (!message.trim() || !ticket?.uuid) return;
@@ -2572,6 +2625,9 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2572
2625
  addOptimistic(optimisticMessage);
2573
2626
  await sendMessage({ ticketUuid: ticket.uuid, text: messageText });
2574
2627
  setMessage("");
2628
+ if (textareaRef.current) {
2629
+ textareaRef.current.style.height = "auto";
2630
+ }
2575
2631
  await refreshMessages();
2576
2632
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2577
2633
  if (scrollContainer) {
@@ -2592,7 +2648,7 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2592
2648
  handleSend(e);
2593
2649
  }
2594
2650
  };
2595
- const handleLoadMore = React4.useCallback(() => {
2651
+ const handleLoadMore = react.useCallback(() => {
2596
2652
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2597
2653
  const previousHeight = scrollContainer?.scrollHeight || 0;
2598
2654
  loadMore();
@@ -2605,32 +2661,61 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2605
2661
  }, [loadMore]);
2606
2662
  const canSendMessage = ticket?.status !== "closed";
2607
2663
  const statusLabel = ticket ? labels.status[ticket.status] || ticket.status || labels.status.open : "";
2664
+ const statusConfig = getStatusConfig(ticket?.status || "open");
2665
+ const renderMessages = () => {
2666
+ const elements = [];
2667
+ let lastDate = null;
2668
+ for (const group of messageGroups) {
2669
+ const firstMessage = group.messages[0];
2670
+ if (!firstMessage) continue;
2671
+ const messageDate = firstMessage.created_at;
2672
+ if (!lastDate || !isSameDay(lastDate, messageDate)) {
2673
+ elements.push(
2674
+ /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { date: messageDate || "" }, `date-${messageDate}`)
2675
+ );
2676
+ lastDate = messageDate || null;
2677
+ }
2678
+ elements.push(
2679
+ /* @__PURE__ */ jsxRuntime.jsx(
2680
+ MessageGroupComponent,
2681
+ {
2682
+ group,
2683
+ currentUser: user,
2684
+ labels: { supportTeam: labels.supportTeam, staff: labels.staff }
2685
+ },
2686
+ `group-${firstMessage.uuid}`
2687
+ )
2688
+ );
2689
+ }
2690
+ return elements;
2691
+ };
2608
2692
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-xl p-0 flex flex-col h-full", children: [
2609
2693
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetHeader, { className: "px-6 py-4 border-b flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between gap-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2610
2694
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { className: "text-lg font-semibold line-clamp-2", children: ticket?.subject || "Ticket" }),
2611
2695
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
2612
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant2(ticket?.status || "open"), className: "text-xs", children: statusLabel }),
2613
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(ticket?.created_at) })
2696
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("text-[13px] px-2 py-0.5 rounded-full", statusConfig.bg, statusConfig.color), children: statusLabel }),
2697
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px] text-muted-foreground", children: formatTicketDate(ticket?.created_at) })
2614
2698
  ] })
2615
2699
  ] }) }) }),
2616
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: isLoadingMessages ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 space-y-4", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
2617
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-8 rounded-full" }),
2618
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-16 flex-1 max-w-[70%]" })
2700
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: isLoadingMessages ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 space-y-4", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex gap-2", i % 2 === 0 ? "justify-end" : "justify-start"), children: [
2701
+ i % 2 !== 0 && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-8 rounded-full shrink-0" }),
2702
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: lib.cn("h-16 rounded-[20px]", i % 2 === 0 ? "w-[60%]" : "w-[70%]") }),
2703
+ i % 2 === 0 && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-8 rounded-full shrink-0" })
2619
2704
  ] }, i)) }) : messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-full p-8 text-center", children: [
2620
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-12 w-12 text-muted-foreground mb-4" }),
2705
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-16 w-16 mx-auto mb-4 bg-muted rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-8 w-8 text-muted-foreground" }) }),
2621
2706
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noMessages }),
2622
2707
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noMessagesDescription })
2623
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.ScrollArea, { className: "h-full", viewportRef: scrollAreaRef, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-4", children: [
2708
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.ScrollArea, { className: "h-full", viewportRef: scrollAreaRef, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 space-y-3", children: [
2624
2709
  hasMore && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center pb-4", children: isLoadingMore ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-muted-foreground", children: [
2625
2710
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2626
2711
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: labels.loadingOlder })
2627
2712
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
2628
2713
  uiCore.Button,
2629
2714
  {
2630
- variant: "outline",
2715
+ variant: "ghost",
2631
2716
  size: "sm",
2632
2717
  onClick: handleLoadMore,
2633
- className: "text-xs",
2718
+ className: "text-xs text-muted-foreground",
2634
2719
  children: [
2635
2720
  labels.loadOlder,
2636
2721
  " (",
@@ -2639,67 +2724,52 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2639
2724
  ]
2640
2725
  }
2641
2726
  ) }),
2642
- messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2643
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
2644
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(messages[0]?.created_at) }),
2645
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" })
2646
- ] }),
2647
- messages.map((msg, index) => {
2648
- const isFromUser = msg.sender?.id && user?.id && String(msg.sender.id) === String(user.id) || msg.sender?.email && user?.email && msg.sender.email === user.email || msg.is_from_author && ticket?.user && user?.id && String(ticket.user) === String(user.id);
2649
- const previousMessage = index > 0 ? messages[index - 1] : null;
2650
- const showDateSeparator = previousMessage && moment2__default.default.utc(previousMessage.created_at || "").format("YYYY-MM-DD") !== moment2__default.default.utc(msg.created_at || "").format("YYYY-MM-DD");
2651
- return /* @__PURE__ */ jsxRuntime.jsxs(React4__default.default.Fragment, { children: [
2652
- showDateSeparator && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 my-4", children: [
2653
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
2654
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(msg.created_at) }),
2655
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" })
2656
- ] }),
2657
- /* @__PURE__ */ jsxRuntime.jsx(
2658
- MessageBubble,
2659
- {
2660
- message: msg,
2661
- isFromUser: !!isFromUser,
2662
- currentUser: user,
2663
- labels: { supportTeam: labels.supportTeam, staff: labels.staff }
2664
- }
2665
- )
2666
- ] }, msg.uuid);
2667
- })
2727
+ renderMessages()
2668
2728
  ] }) }) }),
2669
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSend, className: "p-4 border-t bg-background flex-shrink-0", children: [
2670
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2671
- /* @__PURE__ */ jsxRuntime.jsx(
2672
- uiCore.Textarea,
2673
- {
2674
- value: message,
2675
- onChange: (e) => setMessage(e.target.value),
2676
- onKeyDown: handleKeyDown,
2677
- placeholder: canSendMessage ? labels.placeholder : labels.ticketClosed,
2678
- className: "min-h-[60px] max-h-[150px] resize-none",
2679
- disabled: !canSendMessage || isSending
2680
- }
2681
- ),
2682
- /* @__PURE__ */ jsxRuntime.jsx(
2683
- uiCore.Button,
2684
- {
2685
- type: "submit",
2686
- size: "icon",
2687
- disabled: !message.trim() || !canSendMessage || isSending,
2688
- className: "shrink-0",
2689
- children: isSending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-4 w-4" })
2690
- }
2691
- )
2692
- ] }),
2693
- !canSendMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-2", children: labels.ticketClosedDescription })
2694
- ] })
2729
+ /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: handleSend, className: "p-3 border-t bg-background/95 backdrop-blur flex-shrink-0", children: canSendMessage ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2 bg-muted/50 rounded-[24px] px-4 py-2", children: [
2730
+ /* @__PURE__ */ jsxRuntime.jsx(
2731
+ "textarea",
2732
+ {
2733
+ ref: textareaRef,
2734
+ value: message,
2735
+ onChange: handleTextareaChange,
2736
+ onKeyDown: handleKeyDown,
2737
+ placeholder: labels.placeholder,
2738
+ className: lib.cn(
2739
+ "flex-1 bg-transparent border-none resize-none",
2740
+ "text-[15px] placeholder:text-muted-foreground",
2741
+ "min-h-[24px] max-h-[120px] focus:outline-none"
2742
+ ),
2743
+ rows: 1,
2744
+ disabled: isSending
2745
+ }
2746
+ ),
2747
+ /* @__PURE__ */ jsxRuntime.jsx(
2748
+ uiCore.Button,
2749
+ {
2750
+ type: "submit",
2751
+ size: "icon",
2752
+ variant: "ghost",
2753
+ className: lib.cn(
2754
+ "rounded-full h-8 w-8 shrink-0",
2755
+ message.trim() ? "bg-blue-500 text-white hover:bg-blue-600" : ""
2756
+ ),
2757
+ disabled: !message.trim() || isSending,
2758
+ children: isSending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" })
2759
+ }
2760
+ )
2761
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-2", children: [
2762
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.ticketClosed }),
2763
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: labels.ticketClosedDescription })
2764
+ ] }) })
2695
2765
  ] }) });
2696
2766
  }
2697
2767
  function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2698
2768
  const st = useSupportT();
2699
2769
  const { createTicket } = useSupport();
2700
2770
  const { toast } = uiCore.useToast();
2701
- const [isSubmitting, setIsSubmitting] = React4.useState(false);
2702
- const labels = React4.useMemo(() => ({
2771
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
2772
+ const labels = react.useMemo(() => ({
2703
2773
  title: st("createTicket.title"),
2704
2774
  description: st("createTicket.description"),
2705
2775
  subjectLabel: st("createTicket.subjectLabel"),
@@ -2718,7 +2788,7 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2718
2788
  messageTooLong: st("validation.messageTooLong")
2719
2789
  }
2720
2790
  }), [st]);
2721
- const createTicketSchema = React4.useMemo(() => zod.z.object({
2791
+ const createTicketSchema = react.useMemo(() => zod.z.object({
2722
2792
  subject: zod.z.string().min(1, labels.validation.subjectRequired).max(200, labels.validation.subjectTooLong),
2723
2793
  message: zod.z.string().min(1, labels.validation.messageRequired).max(5e3, labels.validation.messageTooLong)
2724
2794
  }), [labels.validation]);
@@ -2752,13 +2822,10 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2752
2822
  };
2753
2823
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-lg", children: [
2754
2824
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetHeader, { children: [
2755
- /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetTitle, { className: "flex items-center gap-2", children: [
2756
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-5 w-5" }),
2757
- labels.title
2758
- ] }),
2825
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { children: labels.title }),
2759
2826
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetDescription, { children: labels.description })
2760
2827
  ] }),
2761
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Form, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-6 mt-6", children: [
2828
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Form, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-5 mt-4", children: [
2762
2829
  /* @__PURE__ */ jsxRuntime.jsx(
2763
2830
  uiCore.FormField,
2764
2831
  {
@@ -2798,7 +2865,7 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2798
2865
  ] })
2799
2866
  }
2800
2867
  ),
2801
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 pt-4", children: [
2868
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 pt-2", children: [
2802
2869
  /* @__PURE__ */ jsxRuntime.jsx(
2803
2870
  uiCore.Button,
2804
2871
  {
@@ -2812,23 +2879,20 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2812
2879
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2813
2880
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
2814
2881
  labels.creating
2815
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2816
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2817
- labels.create
2818
- ] }) })
2882
+ ] }) : labels.create })
2819
2883
  ] })
2820
2884
  ] }) })
2821
2885
  ] }) });
2822
2886
  }
2823
2887
  var SupportContent = () => {
2824
- const [createSheetOpen, setCreateSheetOpen] = React4.useState(false);
2825
- const [selectedTicket, setSelectedTicket] = React4.useState(null);
2826
- const [ticketSheetOpen, setTicketSheetOpen] = React4.useState(false);
2827
- const handleTicketClick = React4.useCallback((ticket) => {
2888
+ const [createSheetOpen, setCreateSheetOpen] = react.useState(false);
2889
+ const [selectedTicket, setSelectedTicket] = react.useState(null);
2890
+ const [ticketSheetOpen, setTicketSheetOpen] = react.useState(false);
2891
+ const handleTicketClick = react.useCallback((ticket) => {
2828
2892
  setSelectedTicket(ticket);
2829
2893
  setTicketSheetOpen(true);
2830
2894
  }, []);
2831
- const handleTicketCreated = React4.useCallback((ticket) => {
2895
+ const handleTicketCreated = react.useCallback((ticket) => {
2832
2896
  setSelectedTicket(ticket);
2833
2897
  setTicketSheetOpen(true);
2834
2898
  }, []);
@@ -2870,7 +2934,7 @@ function ApiSupportPage() {
2870
2934
  return /* @__PURE__ */ jsxRuntime.jsx(SupportProvider, { adapter, children: /* @__PURE__ */ jsxRuntime.jsx(SupportContent, {}) });
2871
2935
  }
2872
2936
  function DemoSupportPage() {
2873
- const adapter = React4.useMemo(() => createDemoAdapter(), []);
2937
+ const adapter = react.useMemo(() => createDemoAdapter(), []);
2874
2938
  return /* @__PURE__ */ jsxRuntime.jsx(SupportProvider, { adapter, children: /* @__PURE__ */ jsxRuntime.jsx(SupportContent, {}) });
2875
2939
  }
2876
2940
  function SupportPageInner({ isDemo = false }) {
@@ -2886,7 +2950,7 @@ function SupportPage({ isDemo = false }) {
2886
2950
  // package.json
2887
2951
  var package_default = {
2888
2952
  name: "@djangocfg/ext-support",
2889
- version: "1.0.24",
2953
+ version: "1.0.26",
2890
2954
  description: "Support ticket system extension for DjangoCFG",
2891
2955
  keywords: [
2892
2956
  "django",