@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/index.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,9 +21,8 @@ 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
- var moment2__default = /*#__PURE__*/_interopDefault(moment2);
25
+ var moment__default = /*#__PURE__*/_interopDefault(moment);
27
26
 
28
27
  // src/api/generated/ext_support/ext_support__support/client.ts
29
28
  var ExtSupportSupport = class {
@@ -42,7 +41,7 @@ var ExtSupportSupport = class {
42
41
  if (isParamsObject) {
43
42
  params = args[0];
44
43
  } else {
45
- params = { page: args[0], page_size: args[1] };
44
+ params = { ordering: args[0], page: args[1], page_size: args[2], search: args[3] };
46
45
  }
47
46
  const response = await this.client.request("GET", "/cfg/support/tickets/", { params });
48
47
  return response;
@@ -67,7 +66,7 @@ var ExtSupportSupport = class {
67
66
  if (isParamsObject) {
68
67
  params = args[1];
69
68
  } else {
70
- params = { page: args[1], page_size: args[2] };
69
+ params = { ordering: args[1], page: args[2], page_size: args[3], search: args[4] };
71
70
  }
72
71
  const response = await this.client.request("GET", `/cfg/support/tickets/${ticket_uuid}/messages/`, { params });
73
72
  return response;
@@ -758,22 +757,6 @@ var PatchedTicketRequestStatus = /* @__PURE__ */ ((PatchedTicketRequestStatus2)
758
757
  PatchedTicketRequestStatus2["CLOSED"] = "closed";
759
758
  return PatchedTicketRequestStatus2;
760
759
  })(PatchedTicketRequestStatus || {});
761
- var TicketStatus = /* @__PURE__ */ ((TicketStatus2) => {
762
- TicketStatus2["OPEN"] = "open";
763
- TicketStatus2["WAITING_FOR_USER"] = "waiting_for_user";
764
- TicketStatus2["WAITING_FOR_ADMIN"] = "waiting_for_admin";
765
- TicketStatus2["RESOLVED"] = "resolved";
766
- TicketStatus2["CLOSED"] = "closed";
767
- return TicketStatus2;
768
- })(TicketStatus || {});
769
- var TicketRequestStatus = /* @__PURE__ */ ((TicketRequestStatus2) => {
770
- TicketRequestStatus2["OPEN"] = "open";
771
- TicketRequestStatus2["WAITING_FOR_USER"] = "waiting_for_user";
772
- TicketRequestStatus2["WAITING_FOR_ADMIN"] = "waiting_for_admin";
773
- TicketRequestStatus2["RESOLVED"] = "resolved";
774
- TicketRequestStatus2["CLOSED"] = "closed";
775
- return TicketRequestStatus2;
776
- })(TicketRequestStatus || {});
777
760
  var SenderSchema = zod.z.object({
778
761
  id: zod.z.int(),
779
762
  display_username: zod.z.string(),
@@ -817,7 +800,7 @@ var TicketSchema = zod.z.object({
817
800
  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),
818
801
  user: zod.z.int(),
819
802
  subject: zod.z.string().max(255),
820
- status: zod.z.nativeEnum(TicketStatus).optional(),
803
+ status: zod.z.nativeEnum(PatchedTicketRequestStatus).optional(),
821
804
  created_at: zod.z.string().datetime({ offset: true }),
822
805
  unanswered_messages_count: zod.z.int()
823
806
  });
@@ -845,7 +828,7 @@ zod.z.object({
845
828
  zod.z.object({
846
829
  user: zod.z.int(),
847
830
  subject: zod.z.string().min(1).max(255),
848
- status: zod.z.nativeEnum(TicketRequestStatus).optional()
831
+ status: zod.z.nativeEnum(PatchedTicketRequestStatus).optional()
849
832
  });
850
833
 
851
834
  // src/api/generated/ext_support/api-instance.ts
@@ -881,7 +864,7 @@ function configureAPI(config) {
881
864
  // src/api/generated/ext_support/_utils/fetchers/ext_support__support.ts
882
865
  async function getSupportTicketsList(params, client) {
883
866
  const api = getAPIInstance();
884
- const response = await api.ext_support_support.ticketsList(params?.page, params?.page_size);
867
+ const response = await api.ext_support_support.ticketsList(params?.ordering, params?.page, params?.page_size, params?.search);
885
868
  try {
886
869
  return PaginatedTicketListSchema.parse(response);
887
870
  } catch (error) {
@@ -965,7 +948,7 @@ Method: POST`);
965
948
  }
966
949
  async function getSupportTicketsMessagesList(ticket_uuid, params, client) {
967
950
  const api = getAPIInstance();
968
- const response = await api.ext_support_support.ticketsMessagesList(ticket_uuid, params?.page, params?.page_size);
951
+ const response = await api.ext_support_support.ticketsMessagesList(ticket_uuid, params?.ordering, params?.page, params?.page_size, params?.search);
969
952
  try {
970
953
  return PaginatedMessageListSchema.parse(response);
971
954
  } catch (error) {
@@ -1177,13 +1160,13 @@ var API = class {
1177
1160
  // src/api/index.ts
1178
1161
  api.initializeExtensionAPI(configureAPI);
1179
1162
  var apiSupport = api.createExtensionAPI(API);
1180
- var SupportContext = React4.createContext(void 0);
1163
+ var SupportContext = react.createContext(void 0);
1181
1164
  function SupportProvider({ children, adapter }) {
1182
- const value = React4.useMemo(() => ({ adapter }), [adapter]);
1165
+ const value = react.useMemo(() => ({ adapter }), [adapter]);
1183
1166
  return /* @__PURE__ */ jsxRuntime.jsx(SupportContext.Provider, { value, children });
1184
1167
  }
1185
1168
  function useAdapter() {
1186
- const context = React4.useContext(SupportContext);
1169
+ const context = react.useContext(SupportContext);
1187
1170
  if (!context) {
1188
1171
  throw new Error("useSupport hooks must be used within SupportProvider");
1189
1172
  }
@@ -1267,18 +1250,18 @@ function useApiTickets() {
1267
1250
  revalidateFirstPage: false,
1268
1251
  parallel: false
1269
1252
  });
1270
- const tickets = React4.useMemo(() => {
1253
+ const tickets = react.useMemo(() => {
1271
1254
  return ticketsData ? ticketsData.flatMap((page) => page.results) : [];
1272
1255
  }, [ticketsData]);
1273
1256
  const hasMore = ticketsData && ticketsData[ticketsData.length - 1]?.has_next || false;
1274
1257
  const totalCount = ticketsData && ticketsData[ticketsData.length - 1]?.count || 0;
1275
1258
  const isLoadingMore = isValidating && ticketsData && typeof ticketsData[size - 1] !== "undefined";
1276
- const loadMore = React4.useCallback(() => {
1259
+ const loadMore = react.useCallback(() => {
1277
1260
  if (hasMore && !isLoadingMore) {
1278
1261
  setSize(size + 1);
1279
1262
  }
1280
1263
  }, [hasMore, isLoadingMore, size, setSize]);
1281
- const refresh = React4.useCallback(async () => {
1264
+ const refresh = react.useCallback(async () => {
1282
1265
  await mutate();
1283
1266
  }, [mutate]);
1284
1267
  return {
@@ -1312,21 +1295,21 @@ function useApiMessages(ticketUuid) {
1312
1295
  revalidateFirstPage: false,
1313
1296
  parallel: false
1314
1297
  });
1315
- const messages = React4.useMemo(() => {
1298
+ const messages = react.useMemo(() => {
1316
1299
  return data ? data.flatMap((page) => page.results) : [];
1317
1300
  }, [data]);
1318
1301
  const hasMore = data && data[data.length - 1]?.has_next || false;
1319
1302
  const totalCount = data && data[data.length - 1]?.count || 0;
1320
1303
  const isLoadingMore = isValidating && data && typeof data[size - 1] !== "undefined";
1321
- const loadMore = React4.useCallback(() => {
1304
+ const loadMore = react.useCallback(() => {
1322
1305
  if (hasMore && !isLoadingMore) {
1323
1306
  setSize(size + 1);
1324
1307
  }
1325
1308
  }, [hasMore, isLoadingMore, size, setSize]);
1326
- const refresh = React4.useCallback(async () => {
1309
+ const refresh = react.useCallback(async () => {
1327
1310
  await mutate();
1328
1311
  }, [mutate]);
1329
- const addOptimistic = React4.useCallback((message) => {
1312
+ const addOptimistic = react.useCallback((message) => {
1330
1313
  if (!data || !data[0]) return;
1331
1314
  const newData = [...data];
1332
1315
  const firstPage = newData[0];
@@ -1356,7 +1339,7 @@ function useApiOperations() {
1356
1339
  const { user } = auth.useAuth();
1357
1340
  const createTicketMutation = useCreateSupportTicketsCreate();
1358
1341
  const createMessageMutation = useCreateSupportTicketsMessagesCreate();
1359
- const createTicket = React4.useCallback(async (input) => {
1342
+ const createTicket = react.useCallback(async (input) => {
1360
1343
  if (!user?.id) {
1361
1344
  throw new Error("User must be authenticated to create tickets");
1362
1345
  }
@@ -1371,7 +1354,7 @@ function useApiOperations() {
1371
1354
  }
1372
1355
  return ticket;
1373
1356
  }, [user, createTicketMutation, createMessageMutation]);
1374
- const sendMessage = React4.useCallback(async (input) => {
1357
+ const sendMessage = react.useCallback(async (input) => {
1375
1358
  const message = await createMessageMutation(input.ticketUuid, {
1376
1359
  text: input.text
1377
1360
  });
@@ -1399,7 +1382,7 @@ function createApiAdapter() {
1399
1382
  }
1400
1383
  function useApiAdapter() {
1401
1384
  const { createTicket, sendMessage } = useApiOperations();
1402
- return React4.useMemo(() => ({
1385
+ return react.useMemo(() => ({
1403
1386
  useTickets: useApiTickets,
1404
1387
  useMessages: useApiMessages,
1405
1388
  createTicket,
@@ -1548,8 +1531,8 @@ function addMessage(ticketUuid, message) {
1548
1531
  notifySubscribers();
1549
1532
  }
1550
1533
  function useDemoTickets() {
1551
- const [, forceUpdate] = React4.useState({});
1552
- React4.useMemo(() => {
1534
+ const [, forceUpdate] = react.useState({});
1535
+ react.useMemo(() => {
1553
1536
  const update = () => forceUpdate({});
1554
1537
  subscribers.add(update);
1555
1538
  return () => {
@@ -1569,15 +1552,15 @@ function useDemoTickets() {
1569
1552
  };
1570
1553
  }
1571
1554
  function useDemoMessages(ticketUuid) {
1572
- const [, forceUpdate] = React4.useState({});
1573
- React4.useMemo(() => {
1555
+ const [, forceUpdate] = react.useState({});
1556
+ react.useMemo(() => {
1574
1557
  const update = () => forceUpdate({});
1575
1558
  subscribers.add(update);
1576
1559
  return () => {
1577
1560
  subscribers.delete(update);
1578
1561
  };
1579
1562
  }, []);
1580
- const messages = React4.useMemo(() => {
1563
+ const messages = react.useMemo(() => {
1581
1564
  if (!ticketUuid) return [];
1582
1565
  return demoMessages[ticketUuid] || [];
1583
1566
  }, [ticketUuid]);
@@ -1720,78 +1703,6 @@ var en = {
1720
1703
  }
1721
1704
  };
1722
1705
 
1723
- // src/i18n/locales/ru.ts
1724
- var ru = {
1725
- layout: {
1726
- title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1727
- titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
1728
- 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",
1729
- newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1730
- },
1731
- hero: {
1732
- openTickets: "\u041E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
1733
- unreadMessages: "\u041D\u0435\u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u0445"
1734
- },
1735
- status: {
1736
- open: "\u041E\u0442\u043A\u0440\u044B\u0442",
1737
- waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
1738
- waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
1739
- resolved: "\u0420\u0435\u0448\u0451\u043D",
1740
- closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
1741
- },
1742
- ticketList: {
1743
- noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
1744
- 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",
1745
- loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
1746
- loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
1747
- allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
1748
- },
1749
- createTicket: {
1750
- title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
1751
- 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",
1752
- subjectLabel: "\u0422\u0435\u043C\u0430",
1753
- subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
1754
- messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
1755
- 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...",
1756
- cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
1757
- creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
1758
- create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1759
- },
1760
- validation: {
1761
- subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
1762
- 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)",
1763
- messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
1764
- 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)"
1765
- },
1766
- messages: {
1767
- ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
1768
- 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",
1769
- messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
1770
- 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"
1771
- },
1772
- messageInput: {
1773
- placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
1774
- ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
1775
- 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."
1776
- },
1777
- messageList: {
1778
- noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
1779
- 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",
1780
- noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
1781
- 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",
1782
- loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
1783
- loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
1784
- supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1785
- staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
1786
- },
1787
- time: {
1788
- justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
1789
- minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
1790
- hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
1791
- daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
1792
- }
1793
- };
1794
-
1795
1706
  // src/i18n/locales/ko.ts
1796
1707
  var ko = {
1797
1708
  layout: {
@@ -1864,6 +1775,78 @@ var ko = {
1864
1775
  }
1865
1776
  };
1866
1777
 
1778
+ // src/i18n/locales/ru.ts
1779
+ var ru = {
1780
+ layout: {
1781
+ title: "\u0426\u0435\u043D\u0442\u0440 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1782
+ titleShort: "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430",
1783
+ 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",
1784
+ newTicket: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1785
+ },
1786
+ hero: {
1787
+ openTickets: "\u041E\u0442\u043A\u0440\u044B\u0442\u044B\u0445 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
1788
+ unreadMessages: "\u041D\u0435\u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u044B\u0445"
1789
+ },
1790
+ status: {
1791
+ open: "\u041E\u0442\u043A\u0440\u044B\u0442",
1792
+ waitingForUser: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u0432\u0430\u0441",
1793
+ waitingForAdmin: "\u041E\u0436\u0438\u0434\u0430\u0435\u0442 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0443",
1794
+ resolved: "\u0420\u0435\u0448\u0451\u043D",
1795
+ closed: "\u0417\u0430\u043A\u0440\u044B\u0442"
1796
+ },
1797
+ ticketList: {
1798
+ noTickets: "\u041D\u0435\u0442 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439",
1799
+ 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",
1800
+ loadingMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430...",
1801
+ loadMore: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0451",
1802
+ allLoaded: "\u0412\u0441\u0435 {count} \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E"
1803
+ },
1804
+ createTicket: {
1805
+ title: "\u041D\u043E\u0432\u043E\u0435 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435",
1806
+ 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",
1807
+ subjectLabel: "\u0422\u0435\u043C\u0430",
1808
+ subjectPlaceholder: "\u041A\u0440\u0430\u0442\u043A\u043E\u0435 \u043E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u043F\u0440\u043E\u0431\u043B\u0435\u043C\u044B",
1809
+ messageLabel: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435",
1810
+ 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...",
1811
+ cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
1812
+ creating: "\u0421\u043E\u0437\u0434\u0430\u043D\u0438\u0435...",
1813
+ create: "\u0421\u043E\u0437\u0434\u0430\u0442\u044C \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435"
1814
+ },
1815
+ validation: {
1816
+ subjectRequired: "\u0422\u0435\u043C\u0430 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u0430",
1817
+ 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)",
1818
+ messageRequired: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E",
1819
+ 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)"
1820
+ },
1821
+ messages: {
1822
+ ticketCreated: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0441\u043E\u0437\u0434\u0430\u043D\u043E",
1823
+ 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",
1824
+ messageSent: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043E",
1825
+ 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"
1826
+ },
1827
+ messageInput: {
1828
+ placeholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435...",
1829
+ ticketClosed: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u0437\u0430\u043A\u0440\u044B\u0442\u043E",
1830
+ 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."
1831
+ },
1832
+ messageList: {
1833
+ noTicketSelected: "\u041E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0435 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E",
1834
+ 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",
1835
+ noMessages: "\u041D\u0435\u0442 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439",
1836
+ 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",
1837
+ loadingOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430 \u0441\u0442\u0430\u0440\u044B\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439...",
1838
+ loadOlder: "\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0440\u044B\u0435",
1839
+ supportTeam: "\u0421\u043B\u0443\u0436\u0431\u0430 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0438",
1840
+ staff: "\u0421\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A"
1841
+ },
1842
+ time: {
1843
+ justNow: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E",
1844
+ minutesAgo: "{count} \u043C\u0438\u043D \u043D\u0430\u0437\u0430\u0434",
1845
+ hoursAgo: "{count}\u0447 \u043D\u0430\u0437\u0430\u0434",
1846
+ daysAgo: "{count}\u0434 \u043D\u0430\u0437\u0430\u0434"
1847
+ }
1848
+ };
1849
+
1867
1850
  // src/i18n/useSupportT.ts
1868
1851
  var translations = { en, ru, ko };
1869
1852
  function getNestedValue(obj, path) {
@@ -1880,8 +1863,8 @@ function getNestedValue(obj, path) {
1880
1863
  }
1881
1864
  function useSupportT() {
1882
1865
  const locale = nextIntl.useLocale();
1883
- const t = React4.useMemo(() => translations[locale] || translations.en, [locale]);
1884
- return React4.useCallback(
1866
+ const t = react.useMemo(() => translations[locale] || translations.en, [locale]);
1867
+ return react.useCallback(
1885
1868
  (key) => getNestedValue(t, key),
1886
1869
  [t]
1887
1870
  );
@@ -1889,7 +1872,7 @@ function useSupportT() {
1889
1872
  function SupportHero({ onCreateTicket, className }) {
1890
1873
  const st = useSupportT();
1891
1874
  const { tickets, unreadCount, isLoadingTickets, refreshTickets } = useSupport();
1892
- const labels = React4.useMemo(() => ({
1875
+ const labels = react.useMemo(() => ({
1893
1876
  title: st("layout.title"),
1894
1877
  subtitle: st("layout.subtitle"),
1895
1878
  newTicket: st("layout.newTicket"),
@@ -1897,27 +1880,22 @@ function SupportHero({ onCreateTicket, className }) {
1897
1880
  unreadMessages: st("hero.unreadMessages")
1898
1881
  }), [st]);
1899
1882
  const openTicketsCount = tickets.filter((t) => t.status !== "closed" && t.status !== "resolved").length;
1900
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center py-16 px-4", className), children: [
1901
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center mb-6", children: isLoadingTickets ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1902
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-12 w-12 mx-auto mb-4 rounded-full" }),
1903
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-32 mx-auto mb-2" }),
1904
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-4 w-48 mx-auto" })
1905
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1906
- /* @__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" }) }),
1907
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-3xl font-bold tracking-tight", children: labels.title }),
1908
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mt-1", children: labels.subtitle })
1909
- ] }) }),
1910
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1911
- /* @__PURE__ */ jsxRuntime.jsxs(
1883
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center py-12 px-4", className), children: [
1884
+ isLoadingTickets ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
1885
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-10 w-48 mx-auto mb-2" }),
1886
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-5 w-64 mx-auto" })
1887
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
1888
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-4xl font-bold tracking-tight", children: labels.title }),
1889
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground mt-2", children: labels.subtitle })
1890
+ ] }),
1891
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mt-6", children: [
1892
+ /* @__PURE__ */ jsxRuntime.jsx(
1912
1893
  uiCore.Button,
1913
1894
  {
1914
1895
  size: "lg",
1915
1896
  onClick: onCreateTicket,
1916
- className: "rounded-full px-6",
1917
- children: [
1918
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-5 w-5 mr-2" }),
1919
- labels.newTicket
1920
- ]
1897
+ className: "rounded-full px-8 h-12 text-base",
1898
+ children: labels.newTicket
1921
1899
  }
1922
1900
  ),
1923
1901
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1926,46 +1904,103 @@ function SupportHero({ onCreateTicket, className }) {
1926
1904
  size: "icon",
1927
1905
  variant: "ghost",
1928
1906
  onClick: () => refreshTickets(),
1929
- className: "rounded-full",
1907
+ className: "rounded-full h-10 w-10",
1930
1908
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-4 w-4" })
1931
1909
  }
1932
1910
  )
1933
1911
  ] }),
1934
- !isLoadingTickets && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-6 mt-6 text-sm text-muted-foreground", children: [
1935
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
1936
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block font-medium text-foreground text-xl tabular-nums", children: openTicketsCount }),
1937
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.openTickets })
1912
+ !isLoadingTickets && (openTicketsCount > 0 || unreadCount > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-4", children: [
1913
+ openTicketsCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-muted-foreground bg-muted px-3 py-1 rounded-full", children: [
1914
+ openTicketsCount,
1915
+ " ",
1916
+ labels.openTickets.toLowerCase()
1938
1917
  ] }),
1939
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-px bg-border" }),
1940
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
1941
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn(
1942
- "block font-medium text-xl tabular-nums",
1943
- unreadCount > 0 ? "text-destructive" : "text-foreground"
1944
- ), children: unreadCount }),
1945
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.unreadMessages })
1918
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-destructive bg-destructive/10 px-3 py-1 rounded-full", children: [
1919
+ unreadCount,
1920
+ " ",
1921
+ labels.unreadMessages.toLowerCase()
1946
1922
  ] })
1947
1923
  ] })
1948
1924
  ] });
1949
1925
  }
1950
- var getStatusBadgeVariant = (status) => {
1951
- switch (status) {
1952
- case "open":
1953
- return "default";
1954
- case "waiting_for_user":
1955
- return "secondary";
1956
- case "waiting_for_admin":
1957
- return "outline";
1958
- case "resolved":
1959
- return "outline";
1960
- case "closed":
1961
- return "secondary";
1962
- default:
1963
- return "default";
1926
+
1927
+ // src/utils/status.ts
1928
+ var ticketStatusConfig = {
1929
+ open: {
1930
+ color: "text-blue-600 dark:text-blue-400",
1931
+ bg: "bg-blue-500/10"
1932
+ },
1933
+ waiting_for_user: {
1934
+ color: "text-orange-600 dark:text-orange-400",
1935
+ bg: "bg-orange-500/10"
1936
+ },
1937
+ waiting_for_admin: {
1938
+ color: "text-muted-foreground",
1939
+ bg: "bg-muted"
1940
+ },
1941
+ resolved: {
1942
+ color: "text-green-600 dark:text-green-400",
1943
+ bg: "bg-green-500/10"
1944
+ },
1945
+ closed: {
1946
+ color: "text-muted-foreground",
1947
+ bg: "bg-muted"
1964
1948
  }
1965
1949
  };
1950
+ function getStatusConfig(status) {
1951
+ return ticketStatusConfig[status] || ticketStatusConfig.open;
1952
+ }
1953
+ function formatMessageTime(date) {
1954
+ if (!date) return "";
1955
+ return moment__default.default.utc(date).local().format("h:mm A");
1956
+ }
1957
+ function formatDateSeparator(date) {
1958
+ if (!date) return "";
1959
+ const m = moment__default.default.utc(date).local();
1960
+ const now = moment__default.default();
1961
+ const today = now.clone().startOf("day");
1962
+ const yesterday = now.clone().subtract(1, "day").startOf("day");
1963
+ if (m.isSame(today, "day")) {
1964
+ return "Today";
1965
+ }
1966
+ if (m.isSame(yesterday, "day")) {
1967
+ return "Yesterday";
1968
+ }
1969
+ if (m.isSame(now, "year")) {
1970
+ return m.format("MMM D");
1971
+ }
1972
+ return m.format("MMM D, YYYY");
1973
+ }
1974
+ function formatRelativeTime(date, labels) {
1975
+ if (!date) return "N/A";
1976
+ const m = moment__default.default.utc(date).local();
1977
+ const now = moment__default.default();
1978
+ const diffInSeconds = now.diff(m, "seconds");
1979
+ if (diffInSeconds < 60) {
1980
+ return labels.justNow;
1981
+ }
1982
+ if (diffInSeconds < 3600) {
1983
+ return labels.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
1984
+ }
1985
+ if (diffInSeconds < 86400) {
1986
+ return labels.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
1987
+ }
1988
+ if (diffInSeconds < 604800) {
1989
+ return labels.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
1990
+ }
1991
+ return m.format("MMM D, YYYY");
1992
+ }
1993
+ function isSameDay(date1, date2) {
1994
+ if (!date1 || !date2) return false;
1995
+ return moment__default.default.utc(date1).format("YYYY-MM-DD") === moment__default.default.utc(date2).format("YYYY-MM-DD");
1996
+ }
1997
+ function formatTicketDate(date) {
1998
+ if (!date) return "";
1999
+ return moment__default.default.utc(date).local().format("MMM D, YYYY");
2000
+ }
1966
2001
  function TicketItem({ ticket, onClick }) {
1967
2002
  const st = useSupportT();
1968
- const labels = React4.useMemo(() => ({
2003
+ const labels = react.useMemo(() => ({
1969
2004
  status: {
1970
2005
  open: st("status.open"),
1971
2006
  waiting_for_user: st("status.waitingForUser"),
@@ -1980,49 +2015,37 @@ function TicketItem({ ticket, onClick }) {
1980
2015
  daysAgo: st("time.daysAgo")
1981
2016
  }
1982
2017
  }), [st]);
1983
- const formatRelativeTime = React4.useCallback((date) => {
1984
- if (!date) return "N/A";
1985
- const m = moment2__default.default.utc(date).local();
1986
- const now = moment2__default.default();
1987
- const diffInSeconds = now.diff(m, "seconds");
1988
- if (diffInSeconds < 60) return labels.time.justNow;
1989
- if (diffInSeconds < 3600) return labels.time.minutesAgo.replace("{count}", String(Math.floor(diffInSeconds / 60)));
1990
- if (diffInSeconds < 86400) return labels.time.hoursAgo.replace("{count}", String(Math.floor(diffInSeconds / 3600)));
1991
- if (diffInSeconds < 604800) return labels.time.daysAgo.replace("{count}", String(Math.floor(diffInSeconds / 86400)));
1992
- return m.format("MMM D, YYYY");
2018
+ const getRelativeTime = react.useCallback((date) => {
2019
+ return formatRelativeTime(date, labels.time);
1993
2020
  }, [labels.time]);
1994
2021
  const statusLabel = labels.status[ticket.status] || ticket.status || labels.status.open;
2022
+ const statusConfig = getStatusConfig(ticket.status || "open");
1995
2023
  const hasUnread = (ticket.unanswered_messages_count || 0) > 0;
1996
2024
  return /* @__PURE__ */ jsxRuntime.jsxs(
1997
- "div",
2025
+ "button",
1998
2026
  {
2027
+ type: "button",
1999
2028
  className: lib.cn(
2000
- "group flex items-center gap-4 p-4 rounded-xl",
2001
- "cursor-pointer transition-all duration-200",
2002
- "hover:bg-accent/50",
2003
- "active:scale-[0.98]",
2004
- hasUnread && "bg-primary/5"
2029
+ "w-full flex items-start gap-3 p-4 text-left",
2030
+ "transition-colors duration-150",
2031
+ "hover:bg-accent/50 active:bg-accent"
2005
2032
  ),
2006
2033
  onClick,
2007
2034
  children: [
2008
- /* @__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" }) }),
2035
+ /* @__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" }) }),
2009
2036
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
2010
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2011
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: lib.cn(
2012
- "font-medium text-sm line-clamp-1",
2013
- hasUnread && "font-semibold"
2014
- ), children: ticket.subject }),
2015
- hasUnread && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: "destructive", className: "shrink-0 text-xs", children: ticket.unanswered_messages_count })
2016
- ] }),
2017
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mt-1 text-xs text-muted-foreground", children: [
2018
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant(ticket.status || "open"), className: "text-xs py-0", children: statusLabel }),
2019
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
2020
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
2021
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatRelativeTime(ticket.created_at) })
2037
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: lib.cn(
2038
+ "text-[15px] line-clamp-1",
2039
+ hasUnread ? "font-semibold" : "font-medium"
2040
+ ), children: ticket.subject }),
2041
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
2042
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("text-[13px]", statusConfig.color), children: statusLabel }),
2043
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[13px] text-muted-foreground", children: [
2044
+ "\xB7 ",
2045
+ getRelativeTime(ticket.created_at)
2022
2046
  ] })
2023
2047
  ] })
2024
- ] }),
2025
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5 text-muted-foreground/50 group-hover:text-muted-foreground transition-colors flex-shrink-0" })
2048
+ ] })
2026
2049
  ]
2027
2050
  }
2028
2051
  );
@@ -2037,15 +2060,15 @@ function TicketList({ onTicketClick, className }) {
2037
2060
  totalCount: totalTicketsCount,
2038
2061
  loadMore: loadMoreTickets
2039
2062
  } = useTickets();
2040
- const labels = React4.useMemo(() => ({
2063
+ const labels = react.useMemo(() => ({
2041
2064
  noTickets: st("ticketList.noTickets"),
2042
2065
  noTicketsDescription: st("ticketList.noTicketsDescription"),
2043
2066
  loadingMore: st("ticketList.loadingMore"),
2044
2067
  loadMore: st("ticketList.loadMore")
2045
2068
  }), [st]);
2046
- const observerRef = React4.useRef(null);
2047
- const loadMoreRef = React4.useRef(null);
2048
- React4.useEffect(() => {
2069
+ const observerRef = react.useRef(null);
2070
+ const loadMoreRef = react.useRef(null);
2071
+ react.useEffect(() => {
2049
2072
  if (observerRef.current) {
2050
2073
  observerRef.current.disconnect();
2051
2074
  }
@@ -2067,24 +2090,23 @@ function TicketList({ onTicketClick, className }) {
2067
2090
  };
2068
2091
  }, [hasMoreTickets, isLoadingMoreTickets, loadMoreTickets]);
2069
2092
  if (isLoadingTickets) {
2070
- 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(
2093
+ 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(
2071
2094
  uiCore.Skeleton,
2072
2095
  {
2073
- className: "h-20 w-full",
2096
+ className: "h-12 w-full",
2074
2097
  style: { animationDelay: `${i * 100}ms` }
2075
- },
2076
- i
2077
- )) });
2098
+ }
2099
+ ) }, i)) }) });
2078
2100
  }
2079
2101
  if (!tickets || tickets.length === 0) {
2080
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col items-center justify-center py-16 px-8 text-center", className), children: [
2081
- /* @__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" }) }),
2082
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noTickets }),
2083
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noTicketsDescription })
2102
+ return /* @__PURE__ */ jsxRuntime.jsxs(uiCore.Empty, { className: lib.cn("py-16", className), children: [
2103
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.EmptyMedia, { variant: "icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-8 w-8" }) }),
2104
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.EmptyTitle, { children: labels.noTickets }),
2105
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.EmptyDescription, { children: labels.noTicketsDescription })
2084
2106
  ] });
2085
2107
  }
2086
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("px-4 py-6", className), children: [
2087
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: tickets.map((ticket) => /* @__PURE__ */ jsxRuntime.jsx(
2108
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("px-4 py-2", className), children: [
2109
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-border/50", children: tickets.map((ticket) => /* @__PURE__ */ jsxRuntime.jsx(
2088
2110
  TicketItem,
2089
2111
  {
2090
2112
  ticket,
@@ -2114,59 +2136,81 @@ function TicketList({ onTicketClick, className }) {
2114
2136
  ) })
2115
2137
  ] });
2116
2138
  }
2117
- var formatTime = (date) => {
2118
- if (!date) return "";
2119
- return moment2__default.default.utc(date).local().format("hh:mm A");
2120
- };
2121
- var formatDate = (date) => {
2122
- if (!date) return "";
2123
- return moment2__default.default.utc(date).local().format("MMM D, YYYY");
2124
- };
2125
- var getStatusBadgeVariant2 = (status) => {
2126
- switch (status) {
2127
- case "open":
2128
- return "default";
2129
- case "waiting_for_user":
2130
- return "secondary";
2131
- case "waiting_for_admin":
2132
- return "outline";
2133
- case "resolved":
2134
- return "outline";
2135
- case "closed":
2136
- return "secondary";
2137
- default:
2138
- return "default";
2139
+ function groupMessages(messages, currentUserId) {
2140
+ const groups = [];
2141
+ let currentGroup = null;
2142
+ for (const msg of messages) {
2143
+ const isFromUser = msg.sender?.id && currentUserId && String(msg.sender.id) === String(currentUserId) || msg.is_from_author;
2144
+ const senderId = msg.sender?.id || null;
2145
+ const shouldStartNewGroup = !currentGroup || currentGroup.isFromUser !== isFromUser || currentGroup.senderId !== senderId;
2146
+ if (shouldStartNewGroup) {
2147
+ currentGroup = {
2148
+ senderId,
2149
+ isFromUser: !!isFromUser,
2150
+ messages: [msg]
2151
+ };
2152
+ groups.push(currentGroup);
2153
+ } else {
2154
+ currentGroup.messages.push(msg);
2155
+ }
2139
2156
  }
2140
- };
2141
- var MessageBubble = ({ message, isFromUser, currentUser, labels }) => {
2157
+ return groups;
2158
+ }
2159
+ 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) }) });
2160
+ var MessageBubble = ({
2161
+ message,
2162
+ isFromUser,
2163
+ showAvatar,
2164
+ showTimestamp,
2165
+ currentUser,
2166
+ labels
2167
+ }) => {
2142
2168
  const sender = message.sender;
2143
2169
  const senderInitial = sender?.display_username?.charAt(0)?.toUpperCase() || sender?.initials || "S";
2144
2170
  const userInitial = currentUser?.display_username?.charAt(0)?.toUpperCase() || currentUser?.email?.charAt(0)?.toUpperCase() || currentUser?.initials || null;
2145
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex gap-3", isFromUser ? "justify-end" : "justify-start"), children: [
2146
- !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 }) }),
2147
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col gap-1 flex-1 max-w-[80%]", isFromUser ? "items-end" : "items-start"), children: [
2148
- !isFromUser && sender && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground px-1", children: [
2149
- sender.display_username || sender.email || labels.supportTeam,
2150
- sender.is_staff && ` (${labels.staff})`
2151
- ] }),
2152
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Card, { className: lib.cn(
2153
- isFromUser ? "bg-primary text-primary-foreground" : "bg-muted"
2154
- ), 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 }) }) }),
2155
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground px-1", children: formatTime(message.created_at) })
2171
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex gap-2", isFromUser ? "justify-end" : "justify-start"), children: [
2172
+ !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 }) }) }),
2173
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: lib.cn("flex flex-col gap-0.5 max-w-[85%]", isFromUser ? "items-end" : "items-start"), children: [
2174
+ /* @__PURE__ */ jsxRuntime.jsx(
2175
+ "div",
2176
+ {
2177
+ className: lib.cn(
2178
+ "px-4 py-2.5 rounded-[20px]",
2179
+ isFromUser ? "bg-blue-500 text-white" : "bg-[#e9e9eb] dark:bg-zinc-800 text-foreground"
2180
+ ),
2181
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[15px] leading-relaxed whitespace-pre-wrap break-words", children: message.text })
2182
+ }
2183
+ ),
2184
+ showTimestamp && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground px-2 mt-0.5", children: formatMessageTime(message.created_at) })
2156
2185
  ] }),
2157
- 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" }) }) })
2186
+ 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" }) }) }) })
2158
2187
  ] });
2159
2188
  };
2189
+ var MessageGroupComponent = ({ group, currentUser, labels }) => {
2190
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: group.messages.map((msg, idx) => /* @__PURE__ */ jsxRuntime.jsx(
2191
+ MessageBubble,
2192
+ {
2193
+ message: msg,
2194
+ isFromUser: group.isFromUser,
2195
+ showAvatar: idx === 0,
2196
+ showTimestamp: idx === group.messages.length - 1,
2197
+ currentUser,
2198
+ labels
2199
+ },
2200
+ msg.uuid
2201
+ )) });
2202
+ };
2160
2203
  function TicketSheet({ ticket, open, onOpenChange }) {
2161
2204
  const st = useSupportT();
2162
2205
  const { user } = auth.useAuth();
2163
2206
  const { sendMessage } = useSupport();
2164
2207
  const { toast } = uiCore.useToast();
2165
- const [message, setMessage] = React4.useState("");
2166
- const [isSending, setIsSending] = React4.useState(false);
2167
- const scrollAreaRef = React4.useRef(null);
2168
- const firstRender = React4.useRef(true);
2169
- const labels = React4.useMemo(() => ({
2208
+ const [message, setMessage] = react.useState("");
2209
+ const [isSending, setIsSending] = react.useState(false);
2210
+ const scrollAreaRef = react.useRef(null);
2211
+ const textareaRef = react.useRef(null);
2212
+ const firstRender = react.useRef(true);
2213
+ const labels = react.useMemo(() => ({
2170
2214
  noMessages: st("messageList.noMessages"),
2171
2215
  noMessagesDescription: st("messageList.noMessagesDescription"),
2172
2216
  loadingOlder: st("messageList.loadingOlder"),
@@ -2196,10 +2240,11 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2196
2240
  refresh: refreshMessages,
2197
2241
  addOptimistic
2198
2242
  } = useMessages(ticket?.uuid || null);
2199
- React4.useEffect(() => {
2243
+ const messageGroups = react.useMemo(() => groupMessages(messages, user?.id), [messages, user?.id]);
2244
+ react.useEffect(() => {
2200
2245
  firstRender.current = true;
2201
2246
  }, [ticket?.uuid]);
2202
- React4.useEffect(() => {
2247
+ react.useEffect(() => {
2203
2248
  if (firstRender.current && messages.length > 0) {
2204
2249
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2205
2250
  if (scrollContainer) {
@@ -2208,6 +2253,14 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2208
2253
  firstRender.current = false;
2209
2254
  }
2210
2255
  }, [messages]);
2256
+ const handleTextareaChange = react.useCallback((e) => {
2257
+ setMessage(e.target.value);
2258
+ const textarea = textareaRef.current;
2259
+ if (textarea) {
2260
+ textarea.style.height = "auto";
2261
+ textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`;
2262
+ }
2263
+ }, []);
2211
2264
  const handleSend = async (e) => {
2212
2265
  e.preventDefault();
2213
2266
  if (!message.trim() || !ticket?.uuid) return;
@@ -2233,6 +2286,9 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2233
2286
  addOptimistic(optimisticMessage);
2234
2287
  await sendMessage({ ticketUuid: ticket.uuid, text: messageText });
2235
2288
  setMessage("");
2289
+ if (textareaRef.current) {
2290
+ textareaRef.current.style.height = "auto";
2291
+ }
2236
2292
  await refreshMessages();
2237
2293
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2238
2294
  if (scrollContainer) {
@@ -2253,7 +2309,7 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2253
2309
  handleSend(e);
2254
2310
  }
2255
2311
  };
2256
- const handleLoadMore = React4.useCallback(() => {
2312
+ const handleLoadMore = react.useCallback(() => {
2257
2313
  const scrollContainer = scrollAreaRef.current?.querySelector("[data-radix-scroll-area-viewport]");
2258
2314
  const previousHeight = scrollContainer?.scrollHeight || 0;
2259
2315
  loadMore();
@@ -2266,32 +2322,61 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2266
2322
  }, [loadMore]);
2267
2323
  const canSendMessage = ticket?.status !== "closed";
2268
2324
  const statusLabel = ticket ? labels.status[ticket.status] || ticket.status || labels.status.open : "";
2325
+ const statusConfig = getStatusConfig(ticket?.status || "open");
2326
+ const renderMessages = () => {
2327
+ const elements = [];
2328
+ let lastDate = null;
2329
+ for (const group of messageGroups) {
2330
+ const firstMessage = group.messages[0];
2331
+ if (!firstMessage) continue;
2332
+ const messageDate = firstMessage.created_at;
2333
+ if (!lastDate || !isSameDay(lastDate, messageDate)) {
2334
+ elements.push(
2335
+ /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { date: messageDate || "" }, `date-${messageDate}`)
2336
+ );
2337
+ lastDate = messageDate || null;
2338
+ }
2339
+ elements.push(
2340
+ /* @__PURE__ */ jsxRuntime.jsx(
2341
+ MessageGroupComponent,
2342
+ {
2343
+ group,
2344
+ currentUser: user,
2345
+ labels: { supportTeam: labels.supportTeam, staff: labels.staff }
2346
+ },
2347
+ `group-${firstMessage.uuid}`
2348
+ )
2349
+ );
2350
+ }
2351
+ return elements;
2352
+ };
2269
2353
  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: [
2270
2354
  /* @__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: [
2271
2355
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { className: "text-lg font-semibold line-clamp-2", children: ticket?.subject || "Ticket" }),
2272
2356
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
2273
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Badge, { variant: getStatusBadgeVariant2(ticket?.status || "open"), className: "text-xs", children: statusLabel }),
2274
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(ticket?.created_at) })
2357
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: lib.cn("text-[13px] px-2 py-0.5 rounded-full", statusConfig.bg, statusConfig.color), children: statusLabel }),
2358
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[13px] text-muted-foreground", children: formatTicketDate(ticket?.created_at) })
2275
2359
  ] })
2276
2360
  ] }) }) }),
2277
- /* @__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: [
2278
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-8 rounded-full" }),
2279
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-16 flex-1 max-w-[70%]" })
2361
+ /* @__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: [
2362
+ i % 2 !== 0 && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-8 rounded-full shrink-0" }),
2363
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: lib.cn("h-16 rounded-[20px]", i % 2 === 0 ? "w-[60%]" : "w-[70%]") }),
2364
+ i % 2 === 0 && /* @__PURE__ */ jsxRuntime.jsx(uiCore.Skeleton, { className: "h-8 w-8 rounded-full shrink-0" })
2280
2365
  ] }, i)) }) : messages.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-full p-8 text-center", children: [
2281
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-12 w-12 text-muted-foreground mb-4" }),
2366
+ /* @__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" }) }),
2282
2367
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: labels.noMessages }),
2283
2368
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground max-w-sm", children: labels.noMessagesDescription })
2284
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.ScrollArea, { className: "h-full", viewportRef: scrollAreaRef, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-4", children: [
2369
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(uiCore.ScrollArea, { className: "h-full", viewportRef: scrollAreaRef, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 space-y-3", children: [
2285
2370
  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: [
2286
2371
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
2287
2372
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: labels.loadingOlder })
2288
2373
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
2289
2374
  uiCore.Button,
2290
2375
  {
2291
- variant: "outline",
2376
+ variant: "ghost",
2292
2377
  size: "sm",
2293
2378
  onClick: handleLoadMore,
2294
- className: "text-xs",
2379
+ className: "text-xs text-muted-foreground",
2295
2380
  children: [
2296
2381
  labels.loadOlder,
2297
2382
  " (",
@@ -2300,67 +2385,52 @@ function TicketSheet({ ticket, open, onOpenChange }) {
2300
2385
  ]
2301
2386
  }
2302
2387
  ) }),
2303
- messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2304
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
2305
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(messages[0]?.created_at) }),
2306
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" })
2307
- ] }),
2308
- messages.map((msg, index) => {
2309
- 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);
2310
- const previousMessage = index > 0 ? messages[index - 1] : null;
2311
- 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");
2312
- return /* @__PURE__ */ jsxRuntime.jsxs(React4__default.default.Fragment, { children: [
2313
- showDateSeparator && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 my-4", children: [
2314
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" }),
2315
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: formatDate(msg.created_at) }),
2316
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 h-px bg-border" })
2317
- ] }),
2318
- /* @__PURE__ */ jsxRuntime.jsx(
2319
- MessageBubble,
2320
- {
2321
- message: msg,
2322
- isFromUser: !!isFromUser,
2323
- currentUser: user,
2324
- labels: { supportTeam: labels.supportTeam, staff: labels.staff }
2325
- }
2326
- )
2327
- ] }, msg.uuid);
2328
- })
2388
+ renderMessages()
2329
2389
  ] }) }) }),
2330
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSend, className: "p-4 border-t bg-background flex-shrink-0", children: [
2331
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
2332
- /* @__PURE__ */ jsxRuntime.jsx(
2333
- uiCore.Textarea,
2334
- {
2335
- value: message,
2336
- onChange: (e) => setMessage(e.target.value),
2337
- onKeyDown: handleKeyDown,
2338
- placeholder: canSendMessage ? labels.placeholder : labels.ticketClosed,
2339
- className: "min-h-[60px] max-h-[150px] resize-none",
2340
- disabled: !canSendMessage || isSending
2341
- }
2342
- ),
2343
- /* @__PURE__ */ jsxRuntime.jsx(
2344
- uiCore.Button,
2345
- {
2346
- type: "submit",
2347
- size: "icon",
2348
- disabled: !message.trim() || !canSendMessage || isSending,
2349
- className: "shrink-0",
2350
- children: isSending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-4 w-4" })
2351
- }
2352
- )
2353
- ] }),
2354
- !canSendMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-2", children: labels.ticketClosedDescription })
2355
- ] })
2390
+ /* @__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: [
2391
+ /* @__PURE__ */ jsxRuntime.jsx(
2392
+ "textarea",
2393
+ {
2394
+ ref: textareaRef,
2395
+ value: message,
2396
+ onChange: handleTextareaChange,
2397
+ onKeyDown: handleKeyDown,
2398
+ placeholder: labels.placeholder,
2399
+ className: lib.cn(
2400
+ "flex-1 bg-transparent border-none resize-none",
2401
+ "text-[15px] placeholder:text-muted-foreground",
2402
+ "min-h-[24px] max-h-[120px] focus:outline-none"
2403
+ ),
2404
+ rows: 1,
2405
+ disabled: isSending
2406
+ }
2407
+ ),
2408
+ /* @__PURE__ */ jsxRuntime.jsx(
2409
+ uiCore.Button,
2410
+ {
2411
+ type: "submit",
2412
+ size: "icon",
2413
+ variant: "ghost",
2414
+ className: lib.cn(
2415
+ "rounded-full h-8 w-8 shrink-0",
2416
+ message.trim() ? "bg-blue-500 text-white hover:bg-blue-600" : ""
2417
+ ),
2418
+ disabled: !message.trim() || isSending,
2419
+ children: isSending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" })
2420
+ }
2421
+ )
2422
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-2", children: [
2423
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: labels.ticketClosed }),
2424
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-1", children: labels.ticketClosedDescription })
2425
+ ] }) })
2356
2426
  ] }) });
2357
2427
  }
2358
2428
  function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2359
2429
  const st = useSupportT();
2360
2430
  const { createTicket } = useSupport();
2361
2431
  const { toast } = uiCore.useToast();
2362
- const [isSubmitting, setIsSubmitting] = React4.useState(false);
2363
- const labels = React4.useMemo(() => ({
2432
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
2433
+ const labels = react.useMemo(() => ({
2364
2434
  title: st("createTicket.title"),
2365
2435
  description: st("createTicket.description"),
2366
2436
  subjectLabel: st("createTicket.subjectLabel"),
@@ -2379,7 +2449,7 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2379
2449
  messageTooLong: st("validation.messageTooLong")
2380
2450
  }
2381
2451
  }), [st]);
2382
- const createTicketSchema = React4.useMemo(() => zod.z.object({
2452
+ const createTicketSchema = react.useMemo(() => zod.z.object({
2383
2453
  subject: zod.z.string().min(1, labels.validation.subjectRequired).max(200, labels.validation.subjectTooLong),
2384
2454
  message: zod.z.string().min(1, labels.validation.messageRequired).max(5e3, labels.validation.messageTooLong)
2385
2455
  }), [labels.validation]);
@@ -2413,13 +2483,10 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2413
2483
  };
2414
2484
  return /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheet, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetContent, { className: "sm:max-w-lg", children: [
2415
2485
  /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetHeader, { children: [
2416
- /* @__PURE__ */ jsxRuntime.jsxs(uiCore.ResponsiveSheetTitle, { className: "flex items-center gap-2", children: [
2417
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-5 w-5" }),
2418
- labels.title
2419
- ] }),
2486
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetTitle, { children: labels.title }),
2420
2487
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.ResponsiveSheetDescription, { children: labels.description })
2421
2488
  ] }),
2422
- /* @__PURE__ */ jsxRuntime.jsx(uiCore.Form, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-6 mt-6", children: [
2489
+ /* @__PURE__ */ jsxRuntime.jsx(uiCore.Form, { ...form, children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "space-y-5 mt-4", children: [
2423
2490
  /* @__PURE__ */ jsxRuntime.jsx(
2424
2491
  uiCore.FormField,
2425
2492
  {
@@ -2459,7 +2526,7 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2459
2526
  ] })
2460
2527
  }
2461
2528
  ),
2462
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 pt-4", children: [
2529
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 pt-2", children: [
2463
2530
  /* @__PURE__ */ jsxRuntime.jsx(
2464
2531
  uiCore.Button,
2465
2532
  {
@@ -2473,23 +2540,20 @@ function CreateTicketSheet({ open, onOpenChange, onSuccess }) {
2473
2540
  /* @__PURE__ */ jsxRuntime.jsx(uiCore.Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2474
2541
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
2475
2542
  labels.creating
2476
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2477
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 mr-2" }),
2478
- labels.create
2479
- ] }) })
2543
+ ] }) : labels.create })
2480
2544
  ] })
2481
2545
  ] }) })
2482
2546
  ] }) });
2483
2547
  }
2484
2548
  var SupportContent = () => {
2485
- const [createSheetOpen, setCreateSheetOpen] = React4.useState(false);
2486
- const [selectedTicket, setSelectedTicket] = React4.useState(null);
2487
- const [ticketSheetOpen, setTicketSheetOpen] = React4.useState(false);
2488
- const handleTicketClick = React4.useCallback((ticket) => {
2549
+ const [createSheetOpen, setCreateSheetOpen] = react.useState(false);
2550
+ const [selectedTicket, setSelectedTicket] = react.useState(null);
2551
+ const [ticketSheetOpen, setTicketSheetOpen] = react.useState(false);
2552
+ const handleTicketClick = react.useCallback((ticket) => {
2489
2553
  setSelectedTicket(ticket);
2490
2554
  setTicketSheetOpen(true);
2491
2555
  }, []);
2492
- const handleTicketCreated = React4.useCallback((ticket) => {
2556
+ const handleTicketCreated = react.useCallback((ticket) => {
2493
2557
  setSelectedTicket(ticket);
2494
2558
  setTicketSheetOpen(true);
2495
2559
  }, []);
@@ -2531,7 +2595,7 @@ function ApiSupportPage() {
2531
2595
  return /* @__PURE__ */ jsxRuntime.jsx(SupportProvider, { adapter, children: /* @__PURE__ */ jsxRuntime.jsx(SupportContent, {}) });
2532
2596
  }
2533
2597
  function DemoSupportPage() {
2534
- const adapter = React4.useMemo(() => createDemoAdapter(), []);
2598
+ const adapter = react.useMemo(() => createDemoAdapter(), []);
2535
2599
  return /* @__PURE__ */ jsxRuntime.jsx(SupportProvider, { adapter, children: /* @__PURE__ */ jsxRuntime.jsx(SupportContent, {}) });
2536
2600
  }
2537
2601
  function SupportPageInner({ isDemo = false }) {
@@ -2547,7 +2611,7 @@ function SupportPage({ isDemo = false }) {
2547
2611
  // package.json
2548
2612
  var package_default = {
2549
2613
  name: "@djangocfg/ext-support",
2550
- version: "1.0.24",
2614
+ version: "1.0.26",
2551
2615
  description: "Support ticket system extension for DjangoCFG",
2552
2616
  keywords: [
2553
2617
  "django",