@open-mercato/core 0.4.5-develop-610fbb24ec → 0.4.5-develop-811deeb983

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 (141) hide show
  1. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +17 -2
  2. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  3. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +15 -0
  4. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  5. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +30 -0
  6. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  7. package/dist/modules/catalog/data/validators.js +4 -3
  8. package/dist/modules/catalog/data/validators.js.map +2 -2
  9. package/dist/modules/catalog/lib/messageObjectPreviews.js +146 -0
  10. package/dist/modules/catalog/lib/messageObjectPreviews.js.map +7 -0
  11. package/dist/modules/catalog/message-objects.js +95 -0
  12. package/dist/modules/catalog/message-objects.js.map +7 -0
  13. package/dist/modules/currencies/backend/currencies/[id]/page.js +21 -0
  14. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  15. package/dist/modules/currencies/lib/messageObjectPreviews.js +51 -0
  16. package/dist/modules/currencies/lib/messageObjectPreviews.js.map +7 -0
  17. package/dist/modules/currencies/message-objects.js +41 -0
  18. package/dist/modules/currencies/message-objects.js.map +7 -0
  19. package/dist/modules/customers/backend/customers/companies/[id]/page.js +20 -0
  20. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  21. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -1
  22. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  23. package/dist/modules/customers/backend/customers/people/[id]/page.js +20 -0
  24. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  25. package/dist/modules/customers/components/detail/CompanyHighlights.js +18 -14
  26. package/dist/modules/customers/components/detail/CompanyHighlights.js.map +2 -2
  27. package/dist/modules/customers/components/detail/PersonHighlights.js +18 -14
  28. package/dist/modules/customers/components/detail/PersonHighlights.js.map +2 -2
  29. package/dist/modules/customers/lib/messageObjectPreviews.js +41 -5
  30. package/dist/modules/customers/lib/messageObjectPreviews.js.map +2 -2
  31. package/dist/modules/customers/message-objects.js +31 -11
  32. package/dist/modules/customers/message-objects.js.map +2 -2
  33. package/dist/modules/messages/commands/messages.js +3 -0
  34. package/dist/modules/messages/commands/messages.js.map +2 -2
  35. package/dist/modules/messages/components/message-detail/panels/objects-panel.js +6 -1
  36. package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +2 -2
  37. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +4 -1
  38. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
  39. package/dist/modules/messages/frontend/messages/view/[token]/page.js +1 -0
  40. package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +2 -2
  41. package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -7
  42. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  43. package/dist/modules/resources/lib/messageObjectPreviews.js +43 -0
  44. package/dist/modules/resources/lib/messageObjectPreviews.js.map +7 -0
  45. package/dist/modules/resources/message-objects.js +37 -0
  46. package/dist/modules/resources/message-objects.js.map +7 -0
  47. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +19 -0
  48. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  49. package/dist/modules/sales/backend/sales/documents/[id]/page.js +23 -2
  50. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  51. package/dist/modules/sales/backend/sales/quotes/[id]/page.js +1 -1
  52. package/dist/modules/sales/backend/sales/quotes/[id]/page.js.map +2 -2
  53. package/dist/modules/sales/lib/messageObjectPreviews.js +49 -4
  54. package/dist/modules/sales/lib/messageObjectPreviews.js.map +2 -2
  55. package/dist/modules/sales/message-objects.js +44 -2
  56. package/dist/modules/sales/message-objects.js.map +2 -2
  57. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +59 -30
  58. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +2 -2
  59. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +1 -1
  60. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +1 -1
  61. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +8 -30
  62. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  63. package/dist/modules/staff/backend/staff/my-availability/page.js +13 -0
  64. package/dist/modules/staff/backend/staff/my-availability/page.js.map +2 -2
  65. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +8 -31
  66. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  67. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +32 -10
  68. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  69. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +14 -1
  70. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  71. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +14 -1
  72. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  73. package/dist/modules/staff/components/TeamForm.js +4 -2
  74. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  75. package/dist/modules/staff/components/TeamRoleForm.js +4 -2
  76. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  77. package/dist/modules/staff/lib/messageObjectPreviews.js +111 -2
  78. package/dist/modules/staff/lib/messageObjectPreviews.js.map +2 -2
  79. package/dist/modules/staff/message-objects.js +79 -8
  80. package/dist/modules/staff/message-objects.js.map +2 -2
  81. package/package.json +3 -3
  82. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +19 -5
  83. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +14 -0
  84. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +40 -0
  85. package/src/modules/catalog/data/validators.ts +47 -45
  86. package/src/modules/catalog/lib/messageObjectPreviews.ts +176 -0
  87. package/src/modules/catalog/message-objects.ts +102 -0
  88. package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -0
  89. package/src/modules/currencies/lib/messageObjectPreviews.ts +65 -0
  90. package/src/modules/currencies/message-objects.ts +40 -0
  91. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +19 -0
  92. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +13 -0
  93. package/src/modules/customers/backend/customers/people/[id]/page.tsx +19 -0
  94. package/src/modules/customers/components/detail/CompanyHighlights.tsx +14 -9
  95. package/src/modules/customers/components/detail/PersonHighlights.tsx +14 -9
  96. package/src/modules/customers/lib/messageObjectPreviews.ts +43 -3
  97. package/src/modules/customers/message-objects.ts +31 -11
  98. package/src/modules/customers/migrations/.snapshot-open-mercato.json +236 -0
  99. package/src/modules/customers/migrations/.snapshot-openmercato.json +236 -0
  100. package/src/modules/messages/commands/messages.ts +4 -0
  101. package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +8 -1
  102. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +3 -0
  103. package/src/modules/messages/frontend/messages/view/[token]/page.tsx +1 -0
  104. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +20 -4
  105. package/src/modules/resources/lib/messageObjectPreviews.ts +55 -0
  106. package/src/modules/resources/message-objects.ts +36 -0
  107. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +18 -0
  108. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -0
  109. package/src/modules/sales/backend/sales/quotes/[id]/page.tsx +1 -1
  110. package/src/modules/sales/lib/messageObjectPreviews.ts +54 -4
  111. package/src/modules/sales/message-objects.ts +44 -2
  112. package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +72 -34
  113. package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +1 -1
  114. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +7 -29
  115. package/src/modules/staff/backend/staff/my-availability/page.tsx +14 -0
  116. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +8 -30
  117. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +28 -7
  118. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +12 -0
  119. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +12 -0
  120. package/src/modules/staff/components/TeamForm.tsx +3 -0
  121. package/src/modules/staff/components/TeamRoleForm.tsx +3 -0
  122. package/src/modules/staff/lib/messageObjectPreviews.ts +133 -2
  123. package/src/modules/staff/message-objects.ts +79 -8
  124. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +0 -51
  125. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +0 -7
  126. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +0 -35
  127. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +0 -7
  128. package/dist/modules/customers/widgets/messages/index.js +0 -7
  129. package/dist/modules/customers/widgets/messages/index.js.map +0 -7
  130. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +0 -51
  131. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +0 -7
  132. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +0 -34
  133. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +0 -7
  134. package/dist/modules/staff/widgets/messages/index.js +0 -7
  135. package/dist/modules/staff/widgets/messages/index.js.map +0 -7
  136. package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +0 -57
  137. package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +0 -49
  138. package/src/modules/customers/widgets/messages/index.ts +0 -2
  139. package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +0 -57
  140. package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +0 -44
  141. package/src/modules/staff/widgets/messages/index.ts +0 -2
@@ -2,7 +2,7 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import SalesDocumentDetailPage from "../../documents/[id]/page.js";
4
4
  function SalesQuoteDetailPage(props) {
5
- return /* @__PURE__ */ jsx(SalesDocumentDetailPage, { ...props, initialKind: "quote" });
5
+ return /* @__PURE__ */ jsx(SalesDocumentDetailPage, { ...props, initialKind: "quote", includeAmountInMessageMetadata: true });
6
6
  }
7
7
  export {
8
8
  SalesQuoteDetailPage as default
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/sales/backend/sales/quotes/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport SalesDocumentDetailPage from '../../documents/[id]/page'\n\nexport default function SalesQuoteDetailPage(props: { params: { id: string } }) {\n return <SalesDocumentDetailPage {...props} initialKind=\"quote\" />\n}\n"],
5
- "mappings": ";AAKS;AAHT,OAAO,6BAA6B;AAErB,SAAR,qBAAsC,OAAmC;AAC9E,SAAO,oBAAC,2BAAyB,GAAG,OAAO,aAAY,SAAQ;AACjE;",
4
+ "sourcesContent": ["\"use client\"\n\nimport SalesDocumentDetailPage from '../../documents/[id]/page'\n\nexport default function SalesQuoteDetailPage(props: { params: { id: string } }) {\n return <SalesDocumentDetailPage {...props} initialKind=\"quote\" includeAmountInMessageMetadata />\n}\n"],
5
+ "mappings": ";AAKS;AAHT,OAAO,6BAA6B;AAErB,SAAR,qBAAsC,OAAmC;AAC9E,SAAO,oBAAC,2BAAyB,GAAG,OAAO,aAAY,SAAQ,gCAA8B,MAAC;AAChG;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
2
2
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
3
3
  import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
- import { SalesOrder, SalesQuote } from "../data/entities.js";
4
+ import { SalesChannel, SalesOrder, SalesQuote } from "../data/entities.js";
5
5
  function resolveCustomerName(snapshot) {
6
6
  if (!snapshot) return null;
7
7
  const customer = snapshot.customer;
@@ -56,9 +56,12 @@ async function buildPreview(kind, entityId, record) {
56
56
  const subtitleParts = [customerName, total].filter((part) => Boolean(part && part.trim().length > 0));
57
57
  const subtitle = subtitleParts.length > 0 ? subtitleParts.join(" \u2022 ") : entityId;
58
58
  const metadata = {};
59
- if (number) metadata.Number = number;
60
- if (customerName) metadata.Customer = customerName;
61
- if (total) metadata.Total = total;
59
+ const numberLabel = t("sales.documents.detail.number");
60
+ const customerLabel = t("sales.documents.detail.customer");
61
+ const totalLabel = t("sales.documents.detail.totals.grandTotalGross");
62
+ if (number) metadata[numberLabel] = number;
63
+ if (customerName) metadata[customerLabel] = customerName;
64
+ if (total) metadata[totalLabel] = total;
62
65
  return {
63
66
  title: number && number.trim().length > 0 ? number : defaultTitle,
64
67
  subtitle,
@@ -107,7 +110,49 @@ async function loadSalesOrderPreview(entityId, ctx) {
107
110
  const record = await loadDocumentRecord("order", entityId, ctx);
108
111
  return await buildPreview("order", entityId, record);
109
112
  }
113
+ async function loadSalesChannelPreview(entityId, ctx) {
114
+ const { t } = await resolveTranslations();
115
+ const defaultTitle = t("sales.messageObjects.channel.title");
116
+ if (!ctx.organizationId) {
117
+ return { title: defaultTitle, subtitle: entityId };
118
+ }
119
+ const { resolve } = await createRequestContainer();
120
+ const em = resolve("em");
121
+ const entity = await findOneWithDecryption(
122
+ em,
123
+ SalesChannel,
124
+ {
125
+ id: entityId,
126
+ tenantId: ctx.tenantId,
127
+ organizationId: ctx.organizationId,
128
+ deletedAt: null
129
+ },
130
+ void 0,
131
+ { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
132
+ );
133
+ if (!entity) {
134
+ return {
135
+ title: defaultTitle,
136
+ subtitle: entityId,
137
+ status: t("sales.messageObjects.notFound"),
138
+ statusColor: "gray"
139
+ };
140
+ }
141
+ const metadata = {};
142
+ const contactEmailLabel = t("sales.channels.form.contactEmail");
143
+ const websiteUrlLabel = t("sales.channels.form.websiteUrl");
144
+ if (entity.contactEmail && entity.contactEmail.trim().length > 0) metadata[contactEmailLabel] = entity.contactEmail;
145
+ if (entity.websiteUrl && entity.websiteUrl.trim().length > 0) metadata[websiteUrlLabel] = entity.websiteUrl;
146
+ return {
147
+ title: entity.name,
148
+ subtitle: entity.status ?? void 0,
149
+ status: entity.status ?? void 0,
150
+ statusColor: statusColor(entity.status),
151
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0
152
+ };
153
+ }
110
154
  export {
155
+ loadSalesChannelPreview,
111
156
  loadSalesOrderPreview,
112
157
  loadSalesQuotePreview
113
158
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/lib/messageObjectPreviews.ts"],
4
- "sourcesContent": ["import { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesOrder, SalesQuote } from '../data/entities'\n\ntype PreviewContext = {\n tenantId: string\n organizationId?: string | null\n}\n\ntype DocumentKind = 'order' | 'quote'\n\ntype SalesDocumentPreviewRecord = {\n id: string\n status?: string | null\n customerSnapshot?: Record<string, unknown> | null\n currencyCode?: string | null\n grandTotalGrossAmount?: string | null\n orderNumber?: string\n quoteNumber?: string\n}\n\nfunction resolveCustomerName(snapshot: Record<string, unknown> | null): string | null {\n if (!snapshot) return null\n const customer = snapshot.customer as Record<string, unknown> | undefined\n const contact = snapshot.contact as Record<string, unknown> | undefined\n const displayName = typeof customer?.displayName === 'string' ? customer.displayName : null\n if (displayName && displayName.trim().length > 0) return displayName.trim()\n const first = typeof contact?.firstName === 'string' ? contact.firstName : null\n const last = typeof contact?.lastName === 'string' ? contact.lastName : null\n const preferred = typeof contact?.preferredName === 'string' ? contact.preferredName : null\n const parts = [preferred ?? first, last]\n .map((part) => (typeof part === 'string' ? part.trim() : ''))\n .filter((part) => part.length > 0)\n return parts.length > 0 ? parts.join(' ') : null\n}\n\nfunction formatTotal(amount: string | null | undefined, currency: string | null | undefined): string | null {\n if (!amount) return null\n const value = Number(amount)\n if (!Number.isFinite(value)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return value.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)\n } catch {\n return `${value.toLocaleString()} ${currency}`\n }\n}\n\nfunction statusColor(status: string | null | undefined): string | undefined {\n if (!status) return undefined\n const value = status.toLowerCase()\n if (value.includes('approved') || value.includes('paid') || value.includes('fulfilled') || value.includes('accepted')) {\n return 'green'\n }\n if (value.includes('cancel') || value.includes('rejected') || value.includes('void')) {\n return 'red'\n }\n if (value.includes('draft') || value.includes('pending') || value.includes('open') || value.includes('sent')) {\n return 'amber'\n }\n return 'blue'\n}\n\nasync function buildPreview(kind: DocumentKind, entityId: string, record: SalesDocumentPreviewRecord | null): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = kind === 'quote' ? t('sales.messageObjects.quote.title') : t('sales.messageObjects.order.title')\n if (!record) {\n return {\n title: defaultTitle,\n subtitle: entityId,\n status: t('sales.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n const number = kind === 'quote' ? record.quoteNumber : record.orderNumber\n const customerName = resolveCustomerName(record.customerSnapshot ?? null)\n const total = formatTotal(record.grandTotalGrossAmount, record.currencyCode)\n\n const subtitleParts = [customerName, total].filter((part): part is string => Boolean(part && part.trim().length > 0))\n const subtitle = subtitleParts.length > 0 ? subtitleParts.join(' \u2022 ') : entityId\n\n const metadata: Record<string, string> = {}\n if (number) metadata.Number = number\n if (customerName) metadata.Customer = customerName\n if (total) metadata.Total = total\n\n return {\n title: number && number.trim().length > 0 ? number : defaultTitle,\n subtitle,\n status: record.status ?? undefined,\n statusColor: statusColor(record.status),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n }\n}\n\nasync function loadDocumentRecord(\n kind: DocumentKind,\n entityId: string,\n ctx: PreviewContext,\n): Promise<SalesDocumentPreviewRecord | null> {\n if (!ctx.organizationId) return null\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n\n if (kind === 'quote') {\n return await findOneWithDecryption(\n em,\n SalesQuote,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n return await findOneWithDecryption(\n em,\n SalesOrder,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nexport async function loadSalesQuotePreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const record = await loadDocumentRecord('quote', entityId, ctx)\n return await buildPreview('quote', entityId, record)\n}\n\nexport async function loadSalesOrderPreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const record = await loadDocumentRecord('order', entityId, ctx)\n return await buildPreview('order', entityId, record)\n}\n\n\n"],
5
- "mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AACtC,SAAS,2BAA2B;AAGpC,SAAS,YAAY,kBAAkB;AAmBvC,SAAS,oBAAoB,UAAyD;AACpF,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,WAAW,SAAS;AAC1B,QAAM,UAAU,SAAS;AACzB,QAAM,cAAc,OAAO,UAAU,gBAAgB,WAAW,SAAS,cAAc;AACvF,MAAI,eAAe,YAAY,KAAK,EAAE,SAAS,EAAG,QAAO,YAAY,KAAK;AAC1E,QAAM,QAAQ,OAAO,SAAS,cAAc,WAAW,QAAQ,YAAY;AAC3E,QAAM,OAAO,OAAO,SAAS,aAAa,WAAW,QAAQ,WAAW;AACxE,QAAM,YAAY,OAAO,SAAS,kBAAkB,WAAW,QAAQ,gBAAgB;AACvF,QAAM,QAAQ,CAAC,aAAa,OAAO,IAAI,EACpC,IAAI,CAAC,SAAU,OAAO,SAAS,WAAW,KAAK,KAAK,IAAI,EAAG,EAC3D,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC;AACnC,SAAO,MAAM,SAAS,IAAI,MAAM,KAAK,GAAG,IAAI;AAC9C;AAEA,SAAS,YAAY,QAAmC,UAAoD;AAC1G,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,OAAO,MAAM;AAC3B,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,WAAW,GAAG,MAAM,IAAI,QAAQ,KAAK;AACzE,MAAI,CAAC,SAAU,QAAO,MAAM,eAAe;AAC3C,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,QAAW,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,KAAK;AAAA,EACvF,QAAQ;AACN,WAAO,GAAG,MAAM,eAAe,CAAC,IAAI,QAAQ;AAAA,EAC9C;AACF;AAEA,SAAS,YAAY,QAAuD;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAQ,OAAO,YAAY;AACjC,MAAI,MAAM,SAAS,UAAU,KAAK,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,WAAW,KAAK,MAAM,SAAS,UAAU,GAAG;AACrH,WAAO;AAAA,EACT;AACA,MAAI,MAAM,SAAS,QAAQ,KAAK,MAAM,SAAS,UAAU,KAAK,MAAM,SAAS,MAAM,GAAG;AACpF,WAAO;AAAA,EACT;AACA,MAAI,MAAM,SAAS,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK,MAAM,SAAS,MAAM,KAAK,MAAM,SAAS,MAAM,GAAG;AAC5G,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAe,aAAa,MAAoB,UAAkB,QAAuE;AACvI,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,QAAM,eAAe,SAAS,UAAU,EAAE,kCAAkC,IAAI,EAAE,kCAAkC;AACpH,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ,EAAE,+BAA+B;AAAA,MACzC,aAAa;AAAA,IACf;AAAA,EACF;AAEA,QAAM,SAAS,SAAS,UAAU,OAAO,cAAc,OAAO;AAC9D,QAAM,eAAe,oBAAoB,OAAO,oBAAoB,IAAI;AACxE,QAAM,QAAQ,YAAY,OAAO,uBAAuB,OAAO,YAAY;AAE3E,QAAM,gBAAgB,CAAC,cAAc,KAAK,EAAE,OAAO,CAAC,SAAyB,QAAQ,QAAQ,KAAK,KAAK,EAAE,SAAS,CAAC,CAAC;AACpH,QAAM,WAAW,cAAc,SAAS,IAAI,cAAc,KAAK,UAAK,IAAI;AAExE,QAAM,WAAmC,CAAC;AAC1C,MAAI,OAAQ,UAAS,SAAS;AAC9B,MAAI,aAAc,UAAS,WAAW;AACtC,MAAI,MAAO,UAAS,QAAQ;AAE5B,SAAO;AAAA,IACL,OAAO,UAAU,OAAO,KAAK,EAAE,SAAS,IAAI,SAAS;AAAA,IACrD;AAAA,IACA,QAAQ,OAAO,UAAU;AAAA,IACzB,aAAa,YAAY,OAAO,MAAM;AAAA,IACtC,UAAU,OAAO,KAAK,QAAQ,EAAE,SAAS,IAAI,WAAW;AAAA,EAC1D;AACF;AAEA,eAAe,mBACb,MACA,UACA,KAC4C;AAC5C,MAAI,CAAC,IAAI,eAAgB,QAAO;AAEhC,QAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,QAAM,KAAK,QAAQ,IAAI;AACvB,QAAM,QAAQ,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAE3E,MAAI,SAAS,SAAS;AACpB,WAAO,MAAM;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,UAAU,IAAI;AAAA,QACd,gBAAgB,IAAI;AAAA,QACpB,WAAW;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,MACpB,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,sBAAsB,UAAkB,KAAiD;AAC7G,QAAM,SAAS,MAAM,mBAAmB,SAAS,UAAU,GAAG;AAC9D,SAAO,MAAM,aAAa,SAAS,UAAU,MAAM;AACrD;AAEA,eAAsB,sBAAsB,UAAkB,KAAiD;AAC7G,QAAM,SAAS,MAAM,mBAAmB,SAAS,UAAU,GAAG;AAC9D,SAAO,MAAM,aAAa,SAAS,UAAU,MAAM;AACrD;",
4
+ "sourcesContent": ["import { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesChannel, SalesOrder, SalesQuote } from '../data/entities'\n\ntype PreviewContext = {\n tenantId: string\n organizationId?: string | null\n}\n\ntype DocumentKind = 'order' | 'quote'\n\ntype SalesDocumentPreviewRecord = {\n id: string\n status?: string | null\n customerSnapshot?: Record<string, unknown> | null\n currencyCode?: string | null\n grandTotalGrossAmount?: string | null\n orderNumber?: string\n quoteNumber?: string\n}\n\nfunction resolveCustomerName(snapshot: Record<string, unknown> | null): string | null {\n if (!snapshot) return null\n const customer = snapshot.customer as Record<string, unknown> | undefined\n const contact = snapshot.contact as Record<string, unknown> | undefined\n const displayName = typeof customer?.displayName === 'string' ? customer.displayName : null\n if (displayName && displayName.trim().length > 0) return displayName.trim()\n const first = typeof contact?.firstName === 'string' ? contact.firstName : null\n const last = typeof contact?.lastName === 'string' ? contact.lastName : null\n const preferred = typeof contact?.preferredName === 'string' ? contact.preferredName : null\n const parts = [preferred ?? first, last]\n .map((part) => (typeof part === 'string' ? part.trim() : ''))\n .filter((part) => part.length > 0)\n return parts.length > 0 ? parts.join(' ') : null\n}\n\nfunction formatTotal(amount: string | null | undefined, currency: string | null | undefined): string | null {\n if (!amount) return null\n const value = Number(amount)\n if (!Number.isFinite(value)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return value.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)\n } catch {\n return `${value.toLocaleString()} ${currency}`\n }\n}\n\nfunction statusColor(status: string | null | undefined): string | undefined {\n if (!status) return undefined\n const value = status.toLowerCase()\n if (value.includes('approved') || value.includes('paid') || value.includes('fulfilled') || value.includes('accepted')) {\n return 'green'\n }\n if (value.includes('cancel') || value.includes('rejected') || value.includes('void')) {\n return 'red'\n }\n if (value.includes('draft') || value.includes('pending') || value.includes('open') || value.includes('sent')) {\n return 'amber'\n }\n return 'blue'\n}\n\nasync function buildPreview(kind: DocumentKind, entityId: string, record: SalesDocumentPreviewRecord | null): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = kind === 'quote' ? t('sales.messageObjects.quote.title') : t('sales.messageObjects.order.title')\n if (!record) {\n return {\n title: defaultTitle,\n subtitle: entityId,\n status: t('sales.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n const number = kind === 'quote' ? record.quoteNumber : record.orderNumber\n const customerName = resolveCustomerName(record.customerSnapshot ?? null)\n const total = formatTotal(record.grandTotalGrossAmount, record.currencyCode)\n\n const subtitleParts = [customerName, total].filter((part): part is string => Boolean(part && part.trim().length > 0))\n const subtitle = subtitleParts.length > 0 ? subtitleParts.join(' \u2022 ') : entityId\n\n const metadata: Record<string, string> = {}\n const numberLabel = t('sales.documents.detail.number')\n const customerLabel = t('sales.documents.detail.customer')\n const totalLabel = t('sales.documents.detail.totals.grandTotalGross')\n if (number) metadata[numberLabel] = number\n if (customerName) metadata[customerLabel] = customerName\n if (total) metadata[totalLabel] = total\n\n return {\n title: number && number.trim().length > 0 ? number : defaultTitle,\n subtitle,\n status: record.status ?? undefined,\n statusColor: statusColor(record.status),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n }\n}\n\nasync function loadDocumentRecord(\n kind: DocumentKind,\n entityId: string,\n ctx: PreviewContext,\n): Promise<SalesDocumentPreviewRecord | null> {\n if (!ctx.organizationId) return null\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n\n if (kind === 'quote') {\n return await findOneWithDecryption(\n em,\n SalesQuote,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n return await findOneWithDecryption(\n em,\n SalesOrder,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nexport async function loadSalesQuotePreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const record = await loadDocumentRecord('quote', entityId, ctx)\n return await buildPreview('quote', entityId, record)\n}\n\nexport async function loadSalesOrderPreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const record = await loadDocumentRecord('order', entityId, ctx)\n return await buildPreview('order', entityId, record)\n}\n\nexport async function loadSalesChannelPreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = t('sales.messageObjects.channel.title')\n\n if (!ctx.organizationId) {\n return { title: defaultTitle, subtitle: entityId }\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const entity = await findOneWithDecryption(\n em,\n SalesChannel,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: ctx.tenantId, organizationId: ctx.organizationId },\n )\n\n if (!entity) {\n return {\n title: defaultTitle,\n subtitle: entityId,\n status: t('sales.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n const metadata: Record<string, string> = {}\n const contactEmailLabel = t('sales.channels.form.contactEmail')\n const websiteUrlLabel = t('sales.channels.form.websiteUrl')\n if (entity.contactEmail && entity.contactEmail.trim().length > 0) metadata[contactEmailLabel] = entity.contactEmail\n if (entity.websiteUrl && entity.websiteUrl.trim().length > 0) metadata[websiteUrlLabel] = entity.websiteUrl\n\n return {\n title: entity.name,\n subtitle: entity.status ?? undefined,\n status: entity.status ?? undefined,\n statusColor: statusColor(entity.status),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n }\n}\n\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,3 +1,4 @@
1
+ import { MessageObjectDetail, MessageObjectPreview } from "@open-mercato/ui/backend/messages";
1
2
  import { SalesDocumentMessageDetail } from "./widgets/messages/SalesDocumentMessageDetail.js";
2
3
  import { SalesDocumentMessagePreview } from "./widgets/messages/SalesDocumentMessagePreview.js";
3
4
  const objectMessageTypes = ["default", "messages.defaultWithObjects"];
@@ -13,7 +14,14 @@ const messageObjectTypes = [
13
14
  icon: "receipt-text",
14
15
  PreviewComponent: SalesDocumentMessagePreview,
15
16
  DetailComponent: SalesDocumentMessageDetail,
16
- actions: [],
17
+ actions: [
18
+ {
19
+ id: "view",
20
+ labelKey: "common.view",
21
+ variant: "outline",
22
+ href: "/backend/sales/orders/{entityId}"
23
+ }
24
+ ],
17
25
  loadPreview: async (entityId, ctx) => {
18
26
  if (typeof window !== "undefined") {
19
27
  return {
@@ -36,7 +44,14 @@ const messageObjectTypes = [
36
44
  icon: "file-text",
37
45
  PreviewComponent: SalesDocumentMessagePreview,
38
46
  DetailComponent: SalesDocumentMessageDetail,
39
- actions: [],
47
+ actions: [
48
+ {
49
+ id: "view",
50
+ labelKey: "common.view",
51
+ variant: "outline",
52
+ href: "/backend/sales/quotes/{entityId}"
53
+ }
54
+ ],
40
55
  loadPreview: async (entityId, ctx) => {
41
56
  if (typeof window !== "undefined") {
42
57
  return {
@@ -47,6 +62,33 @@ const messageObjectTypes = [
47
62
  const { loadSalesQuotePreview } = await import("./lib/messageObjectPreviews.js");
48
63
  return loadSalesQuotePreview(entityId, ctx);
49
64
  }
65
+ },
66
+ {
67
+ module: "sales",
68
+ entityType: "channel",
69
+ messageTypes: objectMessageTypes,
70
+ entityId: "sales:sales_channel",
71
+ optionLabelField: "name",
72
+ optionSubtitleField: "status",
73
+ labelKey: "sales.messageObjects.channel.title",
74
+ icon: "store",
75
+ PreviewComponent: MessageObjectPreview,
76
+ DetailComponent: MessageObjectDetail,
77
+ actions: [
78
+ {
79
+ id: "view",
80
+ labelKey: "common.view",
81
+ variant: "outline",
82
+ href: "/backend/sales/channels/{entityId}/edit"
83
+ }
84
+ ],
85
+ loadPreview: async (entityId, ctx) => {
86
+ if (typeof window !== "undefined") {
87
+ return { title: "Sales channel", subtitle: entityId };
88
+ }
89
+ const { loadSalesChannelPreview } = await import("./lib/messageObjectPreviews.js");
90
+ return loadSalesChannelPreview(entityId, ctx);
91
+ }
50
92
  }
51
93
  ];
52
94
  var message_objects_default = messageObjectTypes;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/sales/message-objects.ts"],
4
- "sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { SalesDocumentMessageDetail } from './widgets/messages/SalesDocumentMessageDetail'\nimport { SalesDocumentMessagePreview } from './widgets/messages/SalesDocumentMessagePreview'\n\nconst objectMessageTypes = ['default', 'messages.defaultWithObjects']\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'sales',\n entityType: 'order',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_order',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.order',\n icon: 'receipt-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales order',\n subtitle: entityId,\n }\n }\n const { loadSalesOrderPreview } = await import('./lib/messageObjectPreviews')\n return loadSalesOrderPreview(entityId, ctx)\n },\n },\n {\n module: 'sales',\n entityType: 'quote',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_quote',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.quote',\n icon: 'file-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales quote',\n subtitle: entityId,\n }\n }\n const { loadSalesQuotePreview } = await import('./lib/messageObjectPreviews')\n return loadSalesQuotePreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
5
- "mappings": "AACA,SAAS,kCAAkC;AAC3C,SAAS,mCAAmC;AAE5C,MAAM,qBAAqB,CAAC,WAAW,6BAA6B;AAE7D,MAAM,qBAAoD;AAAA,EAC/D;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS,CAAC;AAAA,IACV,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS,CAAC;AAAA,IACV,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
4
+ "sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'\nimport { SalesDocumentMessageDetail } from './widgets/messages/SalesDocumentMessageDetail'\nimport { SalesDocumentMessagePreview } from './widgets/messages/SalesDocumentMessagePreview'\n\nconst objectMessageTypes = ['default', 'messages.defaultWithObjects']\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'sales',\n entityType: 'order',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_order',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.order',\n icon: 'receipt-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/sales/orders/{entityId}',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales order',\n subtitle: entityId,\n }\n }\n const { loadSalesOrderPreview } = await import('./lib/messageObjectPreviews')\n return loadSalesOrderPreview(entityId, ctx)\n },\n },\n {\n module: 'sales',\n entityType: 'quote',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_quote',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.quote',\n icon: 'file-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/sales/quotes/{entityId}',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales quote',\n subtitle: entityId,\n }\n }\n const { loadSalesQuotePreview } = await import('./lib/messageObjectPreviews')\n return loadSalesQuotePreview(entityId, ctx)\n },\n },\n {\n module: 'sales',\n entityType: 'channel',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_channel',\n optionLabelField: 'name',\n optionSubtitleField: 'status',\n labelKey: 'sales.messageObjects.channel.title',\n icon: 'store',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/sales/channels/{entityId}/edit',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return { title: 'Sales channel', subtitle: entityId }\n }\n const { loadSalesChannelPreview } = await import('./lib/messageObjectPreviews')\n return loadSalesChannelPreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
5
+ "mappings": "AACA,SAAS,qBAAqB,4BAA4B;AAC1D,SAAS,kCAAkC;AAC3C,SAAS,mCAAmC;AAE5C,MAAM,qBAAqB,CAAC,WAAW,6BAA6B;AAE7D,MAAM,qBAAoD;AAAA,EAC/D;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,EAAE,OAAO,iBAAiB,UAAU,SAAS;AAAA,MACtD;AACA,YAAM,EAAE,wBAAwB,IAAI,MAAM,OAAO,6BAA6B;AAC9E,aAAO,wBAAwB,UAAU,GAAG;AAAA,IAC9C;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
6
6
  "names": []
7
7
  }
@@ -1,46 +1,75 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
+ import Link from "next/link";
4
5
  import { useT } from "@open-mercato/shared/lib/i18n/context";
5
6
  import { Button } from "@open-mercato/ui/primitives/button";
6
7
  import { SalesDocumentMessagePreview } from "./SalesDocumentMessagePreview.js";
8
+ function resolveActionHref(template, entityId) {
9
+ return template.replace("{entityId}", encodeURIComponent(entityId));
10
+ }
7
11
  function SalesDocumentMessageDetail(props) {
8
12
  const t = useT();
9
13
  const [executingActionId, setExecutingActionId] = React.useState(null);
14
+ const viewAction = props.actions.find((a) => a.id === "view");
15
+ const otherActions = props.actions.filter((a) => a.id !== "view");
16
+ const preview = /* @__PURE__ */ jsx(
17
+ SalesDocumentMessagePreview,
18
+ {
19
+ entityId: props.entityId,
20
+ entityModule: props.entityModule,
21
+ entityType: props.entityType,
22
+ snapshot: props.snapshot,
23
+ previewData: props.previewData,
24
+ actionRequired: props.actionRequired,
25
+ actionType: props.actionType,
26
+ actionLabel: props.actionLabel
27
+ }
28
+ );
10
29
  return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded border p-3", children: [
11
- /* @__PURE__ */ jsx(
12
- SalesDocumentMessagePreview,
30
+ viewAction?.href ? /* @__PURE__ */ jsx(
31
+ Link,
13
32
  {
14
- entityId: props.entityId,
15
- entityModule: props.entityModule,
16
- entityType: props.entityType,
17
- snapshot: props.snapshot,
18
- previewData: props.previewData,
19
- actionRequired: props.actionRequired,
20
- actionType: props.actionType,
21
- actionLabel: props.actionLabel
33
+ href: resolveActionHref(viewAction.href, props.entityId),
34
+ className: "block rounded-md transition-opacity hover:opacity-75",
35
+ children: preview
22
36
  }
23
- ),
24
- props.actions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: props.actions.map((action) => /* @__PURE__ */ jsx(
25
- Button,
26
- {
27
- type: "button",
28
- size: "sm",
29
- variant: action.variant ?? "default",
30
- disabled: executingActionId !== null,
31
- onClick: async () => {
32
- if (executingActionId) return;
33
- setExecutingActionId(action.id);
34
- try {
35
- await props.onAction(action.id, { id: props.entityId });
36
- } finally {
37
- setExecutingActionId(null);
38
- }
37
+ ) : preview,
38
+ otherActions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: otherActions.map((action) => {
39
+ if (action.href) {
40
+ return /* @__PURE__ */ jsx(
41
+ Button,
42
+ {
43
+ type: "button",
44
+ size: "sm",
45
+ variant: action.variant ?? "default",
46
+ asChild: true,
47
+ children: /* @__PURE__ */ jsx(Link, { href: resolveActionHref(action.href, props.entityId), children: t(action.labelKey ?? action.id, action.id) })
48
+ },
49
+ action.id
50
+ );
51
+ }
52
+ return /* @__PURE__ */ jsx(
53
+ Button,
54
+ {
55
+ type: "button",
56
+ size: "sm",
57
+ variant: action.variant ?? "default",
58
+ disabled: executingActionId !== null,
59
+ onClick: async () => {
60
+ if (executingActionId) return;
61
+ setExecutingActionId(action.id);
62
+ try {
63
+ await props.onAction(action.id, { id: props.entityId });
64
+ } finally {
65
+ setExecutingActionId(null);
66
+ }
67
+ },
68
+ children: executingActionId === action.id ? t("messages.actions.executing", "Executing...") : t(action.labelKey ?? action.id, action.id)
39
69
  },
40
- children: executingActionId === action.id ? t("messages.actions.executing", "Executing...") : t(action.labelKey ?? action.id, action.id)
41
- },
42
- action.id
43
- )) }) : null
70
+ action.id
71
+ );
72
+ }) }) : null
44
73
  ] });
45
74
  }
46
75
  var SalesDocumentMessageDetail_default = SalesDocumentMessageDetail;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'\n\nexport function SalesDocumentMessageDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n\n return (\n <div className=\"space-y-3 rounded border p-3\">\n <SalesDocumentMessagePreview\n entityId={props.entityId}\n entityModule={props.entityModule}\n entityType={props.entityType}\n snapshot={props.snapshot}\n previewData={props.previewData}\n actionRequired={props.actionRequired}\n actionType={props.actionType}\n actionLabel={props.actionLabel}\n />\n\n {props.actions.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {props.actions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n disabled={executingActionId !== null}\n onClick={async () => {\n if (executingActionId) return\n setExecutingActionId(action.id)\n try {\n await props.onAction(action.id, { id: props.entityId })\n } finally {\n setExecutingActionId(null)\n }\n }}\n >\n {executingActionId === action.id\n ? t('messages.actions.executing', 'Executing...')\n : t(action.labelKey ?? action.id, action.id)}\n </Button>\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport default SalesDocumentMessageDetail\n\n"],
5
- "mappings": ";AAaI,SACE,KADF;AAXJ,YAAY,WAAW;AACvB,SAAS,YAAY;AAErB,SAAS,cAAc;AACvB,SAAS,mCAAmC;AAErC,SAAS,2BAA2B,OAA0B;AACnE,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAwB,IAAI;AAEpF,SACE,qBAAC,SAAI,WAAU,gCACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,MAAM;AAAA,QAChB,cAAc,MAAM;AAAA,QACpB,YAAY,MAAM;AAAA,QAClB,UAAU,MAAM;AAAA,QAChB,aAAa,MAAM;AAAA,QACnB,gBAAgB,MAAM;AAAA,QACtB,YAAY,MAAM;AAAA,QAClB,aAAa,MAAM;AAAA;AAAA,IACrB;AAAA,IAEC,MAAM,QAAQ,SAAS,IACtB,oBAAC,SAAI,WAAU,wBACZ,gBAAM,QAAQ,IAAI,CAAC,WAClB;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,SAAS,OAAO,WAAW;AAAA,QAC3B,UAAU,sBAAsB;AAAA,QAChC,SAAS,YAAY;AACnB,cAAI,kBAAmB;AACvB,+BAAqB,OAAO,EAAE;AAC9B,cAAI;AACF,kBAAM,MAAM,SAAS,OAAO,IAAI,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,UACxD,UAAE;AACA,iCAAqB,IAAI;AAAA,UAC3B;AAAA,QACF;AAAA,QAEC,gCAAsB,OAAO,KAC1B,EAAE,8BAA8B,cAAc,IAC9C,EAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE;AAAA;AAAA,MAjBxC,OAAO;AAAA,IAkBd,CACD,GACH,IACE;AAAA,KACN;AAEJ;AAEA,IAAO,qCAAQ;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'\n\nfunction resolveActionHref(template: string, entityId: string): string {\n return template.replace('{entityId}', encodeURIComponent(entityId))\n}\n\nexport function SalesDocumentMessageDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n\n const viewAction = props.actions.find((a) => a.id === 'view')\n const otherActions = props.actions.filter((a) => a.id !== 'view')\n\n const preview = (\n <SalesDocumentMessagePreview\n entityId={props.entityId}\n entityModule={props.entityModule}\n entityType={props.entityType}\n snapshot={props.snapshot}\n previewData={props.previewData}\n actionRequired={props.actionRequired}\n actionType={props.actionType}\n actionLabel={props.actionLabel}\n />\n )\n\n return (\n <div className=\"space-y-3 rounded border p-3\">\n {viewAction?.href ? (\n <Link\n href={resolveActionHref(viewAction.href, props.entityId)}\n className=\"block rounded-md transition-opacity hover:opacity-75\"\n >\n {preview}\n </Link>\n ) : (\n preview\n )}\n\n {otherActions.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {otherActions.map((action) => {\n if (action.href) {\n return (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n asChild\n >\n <Link href={resolveActionHref(action.href, props.entityId)}>\n {t(action.labelKey ?? action.id, action.id)}\n </Link>\n </Button>\n )\n }\n return (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n disabled={executingActionId !== null}\n onClick={async () => {\n if (executingActionId) return\n setExecutingActionId(action.id)\n try {\n await props.onAction(action.id, { id: props.entityId })\n } finally {\n setExecutingActionId(null)\n }\n }}\n >\n {executingActionId === action.id\n ? t('messages.actions.executing', 'Executing...')\n : t(action.labelKey ?? action.id, action.id)}\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport default SalesDocumentMessageDetail\n\n"],
5
+ "mappings": ";AAqBI,cAaA,YAbA;AAnBJ,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,YAAY;AAErB,SAAS,cAAc;AACvB,SAAS,mCAAmC;AAE5C,SAAS,kBAAkB,UAAkB,UAA0B;AACrE,SAAO,SAAS,QAAQ,cAAc,mBAAmB,QAAQ,CAAC;AACpE;AAEO,SAAS,2BAA2B,OAA0B;AACnE,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAwB,IAAI;AAEpF,QAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC5D,QAAM,eAAe,MAAM,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM;AAEhE,QAAM,UACJ;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,MAAM;AAAA,MAChB,cAAc,MAAM;AAAA,MACpB,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,gBAAgB,MAAM;AAAA,MACtB,YAAY,MAAM;AAAA,MAClB,aAAa,MAAM;AAAA;AAAA,EACrB;AAGF,SACE,qBAAC,SAAI,WAAU,gCACZ;AAAA,gBAAY,OACX;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,kBAAkB,WAAW,MAAM,MAAM,QAAQ;AAAA,QACvD,WAAU;AAAA,QAET;AAAA;AAAA,IACH,IAEA;AAAA,IAGD,aAAa,SAAS,IACrB,oBAAC,SAAI,WAAU,wBACZ,uBAAa,IAAI,CAAC,WAAW;AAC5B,UAAI,OAAO,MAAM;AACf,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,SAAS,OAAO,WAAW;AAAA,YAC3B,SAAO;AAAA,YAEP,8BAAC,QAAK,MAAM,kBAAkB,OAAO,MAAM,MAAM,QAAQ,GACtD,YAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE,GAC5C;AAAA;AAAA,UARK,OAAO;AAAA,QASd;AAAA,MAEJ;AACA,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS,OAAO,WAAW;AAAA,UAC3B,UAAU,sBAAsB;AAAA,UAChC,SAAS,YAAY;AACnB,gBAAI,kBAAmB;AACvB,iCAAqB,OAAO,EAAE;AAC9B,gBAAI;AACF,oBAAM,MAAM,SAAS,OAAO,IAAI,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,YACxD,UAAE;AACA,mCAAqB,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,UAEC,gCAAsB,OAAO,KAC1B,EAAE,8BAA8B,cAAc,IAC9C,EAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE;AAAA;AAAA,QAjBxC,OAAO;AAAA,MAkBd;AAAA,IAEJ,CAAC,GACH,IACE;AAAA,KACN;AAEJ;AAEA,IAAO,qCAAQ;",
6
6
  "names": []
7
7
  }
@@ -15,7 +15,7 @@ function SalesDocumentMessagePreview({
15
15
  const Icon = isQuote ? FileText : ReceiptText;
16
16
  const fallbackTitle = isQuote ? t("sales.documents.detail.quote", "Sales quote") : t("sales.documents.detail.order", "Sales order");
17
17
  const title = previewData?.title || fallbackTitle;
18
- const subtitle = previewData?.subtitle || entityId;
18
+ const subtitle = previewData?.subtitle || "";
19
19
  return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border bg-muted/20 p-3", children: [
20
20
  /* @__PURE__ */ jsx(Icon, { className: "mt-0.5 h-4 w-4 text-muted-foreground" }),
21
21
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport { FileText, ReceiptText } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\nexport function SalesDocumentMessagePreview({\n entityType,\n entityId,\n previewData,\n actionRequired,\n actionLabel,\n}: ObjectPreviewProps) {\n const t = useT()\n const isQuote = entityType === 'quote'\n const Icon = isQuote ? FileText : ReceiptText\n const fallbackTitle = isQuote\n ? t('sales.documents.detail.quote', 'Sales quote')\n : t('sales.documents.detail.order', 'Sales order')\n const title = previewData?.title || fallbackTitle\n const subtitle = previewData?.subtitle || entityId\n\n return (\n <div className=\"flex items-start gap-3 rounded-md border bg-muted/20 p-3\">\n <Icon className=\"mt-0.5 h-4 w-4 text-muted-foreground\" />\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium\">{title}</p>\n {actionRequired ? (\n <Badge variant=\"secondary\" className=\"text-xs\">\n {actionLabel || t('messages.composer.objectActionRequired', 'Action required')}\n </Badge>\n ) : null}\n </div>\n <p className=\"truncate text-xs text-muted-foreground\">{subtitle}</p>\n {previewData?.status ? (\n <Badge variant=\"outline\" className=\"text-xs\">{previewData.status}</Badge>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport default SalesDocumentMessagePreview\n\n"],
4
+ "sourcesContent": ["\"use client\"\n\nimport { FileText, ReceiptText } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\nexport function SalesDocumentMessagePreview({\n entityType,\n entityId,\n previewData,\n actionRequired,\n actionLabel,\n}: ObjectPreviewProps) {\n const t = useT()\n const isQuote = entityType === 'quote'\n const Icon = isQuote ? FileText : ReceiptText\n const fallbackTitle = isQuote\n ? t('sales.documents.detail.quote', 'Sales quote')\n : t('sales.documents.detail.order', 'Sales order')\n const title = previewData?.title || fallbackTitle\n const subtitle = previewData?.subtitle || \"\"\n\n return (\n <div className=\"flex items-start gap-3 rounded-md border bg-muted/20 p-3\">\n <Icon className=\"mt-0.5 h-4 w-4 text-muted-foreground\" />\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium\">{title}</p>\n {actionRequired ? (\n <Badge variant=\"secondary\" className=\"text-xs\">\n {actionLabel || t('messages.composer.objectActionRequired', 'Action required')}\n </Badge>\n ) : null}\n </div>\n <p className=\"truncate text-xs text-muted-foreground\">{subtitle}</p>\n {previewData?.status ? (\n <Badge variant=\"outline\" className=\"text-xs\">{previewData.status}</Badge>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport default SalesDocumentMessagePreview\n\n"],
5
5
  "mappings": ";AAyBM,cAEE,YAFF;AAvBN,SAAS,UAAU,mBAAmB;AACtC,SAAS,YAAY;AAErB,SAAS,aAAa;AAEf,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,IAAI,KAAK;AACf,QAAM,UAAU,eAAe;AAC/B,QAAM,OAAO,UAAU,WAAW;AAClC,QAAM,gBAAgB,UAClB,EAAE,gCAAgC,aAAa,IAC/C,EAAE,gCAAgC,aAAa;AACnD,QAAM,QAAQ,aAAa,SAAS;AACpC,QAAM,WAAW,aAAa,YAAY;AAE1C,SACE,qBAAC,SAAI,WAAU,4DACb;AAAA,wBAAC,QAAK,WAAU,wCAAuC;AAAA,IACvD,qBAAC,SAAI,WAAU,4BACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OAAE,WAAU,gCAAgC,iBAAM;AAAA,QAClD,iBACC,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,yBAAe,EAAE,0CAA0C,iBAAiB,GAC/E,IACE;AAAA,SACN;AAAA,MACA,oBAAC,OAAE,WAAU,0CAA0C,oBAAS;AAAA,MAC/D,aAAa,SACZ,oBAAC,SAAM,SAAQ,WAAU,WAAU,WAAW,sBAAY,QAAO,IAC/D;AAAA,OACN;AAAA,KACF;AAEJ;AAEA,IAAO,sCAAQ;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,6 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { useRouter } from "next/navigation";
5
- import { Send } from "lucide-react";
6
5
  import { Page, PageBody } from "@open-mercato/ui/backend/Page";
7
6
  import { Badge } from "@open-mercato/ui/primitives/badge";
8
7
  import { Button } from "@open-mercato/ui/primitives/button";
@@ -80,19 +79,6 @@ function StaffLeaveRequestDetailPage({ params }) {
80
79
  unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,
81
80
  note: record?.note ?? null
82
81
  }), [record, memberLabel]);
83
- const messageContextPreview = React.useMemo(() => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
84
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: t("staff.leaveRequests.messages.contextTitle", "Linked leave request") }),
85
- memberLabel ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
86
- t("staff.leaveRequests.detail.member", "Team member"),
87
- ": ",
88
- memberLabel
89
- ] }) : null,
90
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
91
- t("staff.leaveRequests.detail.dates", "Dates"),
92
- ": ",
93
- dateSummary
94
- ] })
95
- ] }), [dateSummary, memberLabel, t]);
96
82
  const handleSubmit = React.useCallback(async (values) => {
97
83
  if (!record?.id) return;
98
84
  const payload = buildLeaveRequestPayload(values, { id: record.id });
@@ -181,8 +167,14 @@ function StaffLeaveRequestDetailPage({ params }) {
181
167
  entityType: "leave_request",
182
168
  entityId: record.id,
183
169
  sourceEntityType: "staff:leave_request",
184
- sourceEntityId: record.id
170
+ sourceEntityId: record.id,
171
+ previewData: {
172
+ title: memberLabel || t("staff.leaveRequests.messages.contextTitle", "Linked leave request"),
173
+ subtitle: dateSummary || void 0,
174
+ status: record?.status ?? void 0
175
+ }
185
176
  },
177
+ viewHref: `/backend/staff/leave-requests/${record.id}`,
186
178
  lockedType: "staff.leave_request_approval",
187
179
  requiredActionConfig: {
188
180
  mode: "required",
@@ -195,21 +187,7 @@ function StaffLeaveRequestDetailPage({ params }) {
195
187
  type: "staff.leave_request_approval",
196
188
  subject: t("staff.leaveRequests.messages.compose.subject", "Leave request approval needed"),
197
189
  body: t("staff.leaveRequests.messages.compose.body", "Please review this leave request and take action.")
198
- },
199
- contextPreview: messageContextPreview,
200
- renderTrigger: ({ openComposer, disabled }) => /* @__PURE__ */ jsx(
201
- Button,
202
- {
203
- type: "button",
204
- size: "icon",
205
- variant: "outline",
206
- onClick: openComposer,
207
- disabled,
208
- "aria-label": t("staff.leaveRequests.messages.compose.action", "Send for review"),
209
- title: t("staff.leaveRequests.messages.compose.action", "Send for review"),
210
- children: /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
211
- }
212
- )
190
+ }
213
191
  }
214
192
  ) : null
215
193
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/backend/staff/leave-requests/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Send } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LeaveRequestForm, buildLeaveRequestPayload, type LeaveRequestFormValues } from '@open-mercato/core/modules/staff/components/LeaveRequestForm'\n\ntype LeaveRequestRecord = {\n id: string\n member?: { id?: string; displayName?: string }\n memberId?: string | null\n member_id?: string | null\n startDate?: string | null\n start_date?: string | null\n endDate?: string | null\n end_date?: string | null\n timezone?: string | null\n status?: 'pending' | 'approved' | 'rejected'\n unavailabilityReasonEntryId?: string | null\n unavailability_reason_entry_id?: string | null\n unavailabilityReasonValue?: string | null\n unavailability_reason_value?: string | null\n note?: string | null\n decisionComment?: string | null\n decision_comment?: string | null\n decidedAt?: string | null\n decided_at?: string | null\n} & Record<string, unknown>\n\ntype LeaveRequestsResponse = {\n items?: LeaveRequestRecord[]\n}\n\nexport default function StaffLeaveRequestDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const router = useRouter()\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [record, setRecord] = React.useState<LeaveRequestRecord | null>(null)\n const [decisionComment, setDecisionComment] = React.useState('')\n\n React.useEffect(() => {\n if (!id) {\n setError(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n setIsLoading(false)\n return\n }\n const requestId = id\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: requestId })\n const payload = await readApiResultOrThrow<LeaveRequestsResponse>(\n `/api/staff/leave-requests?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.leaveRequests.errors.load', 'Failed to load leave request.') },\n )\n const entry = Array.isArray(payload.items) ? payload.items[0] : null\n if (!entry) throw new Error(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n if (!cancelled) {\n setRecord(entry)\n setDecisionComment(\n typeof entry.decisionComment === 'string'\n ? entry.decisionComment\n : typeof entry.decision_comment === 'string'\n ? entry.decision_comment\n : ''\n )\n }\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('staff.leaveRequests.errors.load', 'Failed to load leave request.')\n setError(message)\n setRecord(null)\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n void load()\n return () => { cancelled = true }\n }, [id, t])\n\n const status = record?.status ?? 'pending'\n const memberLabel = record?.member?.displayName ?? null\n const dateSummary = formatDateRange(\n record?.startDate ?? record?.start_date ?? null,\n record?.endDate ?? record?.end_date ?? null,\n )\n const initialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n id: record?.id,\n memberId: record?.memberId ?? record?.member_id ?? null,\n memberLabel,\n startDate: record?.startDate ?? record?.start_date ?? null,\n endDate: record?.endDate ?? record?.end_date ?? null,\n timezone: record?.timezone ?? null,\n unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? record?.unavailability_reason_entry_id ?? null,\n unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,\n note: record?.note ?? null,\n }), [record, memberLabel])\n\n const messageContextPreview = React.useMemo(() => (\n <div className=\"space-y-1\">\n <p className=\"font-medium\">{t('staff.leaveRequests.messages.contextTitle', 'Linked leave request')}</p>\n {memberLabel ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}\n </p>\n ) : null}\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}\n </p>\n </div>\n ), [dateSummary, memberLabel, t])\n\n const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {\n if (!record?.id) return\n const payload = buildLeaveRequestPayload(values, { id: record.id })\n await updateCrud('staff/leave-requests', payload, {\n errorMessage: t('staff.leaveRequests.form.errors.update', 'Failed to update leave request.'),\n })\n flash(t('staff.leaveRequests.form.flash.updated', 'Leave request updated.'), 'success')\n router.push('/backend/staff/leave-requests')\n }, [record?.id, router, t])\n\n const handleDecision = React.useCallback(async (action: 'accept' | 'reject') => {\n if (!record?.id) return\n const endpoint = action === 'accept' ? '/api/staff/leave-requests/accept' : '/api/staff/leave-requests/reject'\n await apiCallOrThrow(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, decisionComment: decisionComment || null }),\n })\n flash(\n action === 'accept'\n ? t('staff.leaveRequests.messages.accepted', 'Leave request approved.')\n : t('staff.leaveRequests.messages.rejected', 'Leave request rejected.'),\n 'success',\n )\n router.refresh()\n }, [decisionComment, record?.id, router, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.leaveRequests.form.loading', 'Loading leave request...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !record) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.leaveRequests.errors.load', 'Failed to load leave request.')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"mb-6 space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <Badge variant={resolveStatusVariant(status)}>\n {t(`staff.leaveRequests.status.${status}`, status)}\n </Badge>\n {record.decided_at || record.decidedAt ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.decision.at', 'Decision at')} {formatDateLabel(record.decidedAt ?? record.decided_at ?? null)}\n </span>\n ) : null}\n </div>\n {memberLabel ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}\n </p>\n ) : null}\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}\n </p>\n </div>\n\n {status === 'pending' ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4\">\n <div className=\"mb-3 text-sm font-medium\">{t('staff.leaveRequests.decision.title', 'Decision')}</div>\n <Textarea\n value={decisionComment}\n onChange={(event) => setDecisionComment(event.target.value)}\n placeholder={t('staff.leaveRequests.decision.placeholder', 'Add a comment (optional)')}\n className=\"mb-3\"\n />\n <div className=\"flex flex-wrap gap-2\">\n <Button onClick={() => handleDecision('accept')}>\n {t('staff.leaveRequests.actions.accept', 'Approve')}\n </Button>\n <Button variant=\"destructive\" onClick={() => handleDecision('reject')}>\n {t('staff.leaveRequests.actions.reject', 'Reject')}\n </Button>\n </div>\n </div>\n ) : record.decisionComment || record.decision_comment ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <div className=\"mb-1 font-medium text-foreground\">{t('staff.leaveRequests.decision.comment', 'Decision comment')}</div>\n <p>{record.decisionComment ?? record.decision_comment}</p>\n </div>\n ) : null}\n\n <LeaveRequestForm\n title={t('staff.leaveRequests.form.editTitle', 'Leave request')}\n submitLabel={t('staff.leaveRequests.form.actions.save', 'Save')}\n backHref=\"/backend/staff/leave-requests\"\n cancelHref=\"/backend/staff/leave-requests\"\n initialValues={initialValues}\n onSubmit={handleSubmit}\n allowMemberSelect\n memberLabel={memberLabel}\n extraActions={record.id ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'leave_request',\n entityId: record.id,\n sourceEntityType: 'staff:leave_request',\n sourceEntityId: record.id,\n }}\n lockedType=\"staff.leave_request_approval\"\n requiredActionConfig={{\n mode: 'required',\n options: [\n { id: 'approve', label: t('staff.notifications.leaveRequest.actions.approve', 'Approve') },\n { id: 'reject', label: t('staff.notifications.leaveRequest.actions.reject', 'Reject') },\n ],\n }}\n defaultValues={{\n type: 'staff.leave_request_approval',\n subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),\n body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),\n }}\n contextPreview={messageContextPreview}\n renderTrigger={({ openComposer, disabled }) => (\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"outline\"\n onClick={openComposer}\n disabled={disabled}\n aria-label={t('staff.leaveRequests.messages.compose.action', 'Send for review')}\n title={t('staff.leaveRequests.messages.compose.action', 'Send for review')}\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n )}\n />\n ) : null}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction resolveStatusVariant(status: 'pending' | 'approved' | 'rejected') {\n if (status === 'approved') return 'default'\n if (status === 'rejected') return 'destructive'\n return 'secondary'\n}\n\nfunction formatDateLabel(value?: string | null): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return value\n return date.toLocaleDateString()\n}\n\nfunction formatDateRange(start?: string | null, end?: string | null): string {\n const startLabel = formatDateLabel(start)\n const endLabel = formatDateLabel(end)\n if (startLabel && endLabel) return `${startLabel} -> ${endLabel}`\n return startLabel || endLabel || '-'\n}\n"],
5
- "mappings": ";AAoHM,cAEE,YAFF;AAlHN,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,YAAY;AACrB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,aAAa;AACtB,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,gBAAgB,oBAAoB;AAC7C,SAAS,+BAA+B;AACxC,SAAS,gBAAgB,4BAA4B;AACrD,SAAS,kBAAkB;AAC3B,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,kBAAkB,gCAA6D;AA4BzE,SAAR,4BAA6C,EAAE,OAAO,GAAiC;AAC5F,QAAM,KAAK,QAAQ;AACnB,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAoC,IAAI;AAC1E,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAS,EAAE;AAE/D,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,IAAI;AACP,eAAS,EAAE,uCAAuC,0BAA0B,CAAC;AAC7E,mBAAa,KAAK;AAClB;AAAA,IACF;AACA,UAAM,YAAY;AAClB,QAAI,YAAY;AAChB,mBAAe,OAAO;AACpB,mBAAa,IAAI;AACjB,eAAS,IAAI;AACb,UAAI;AACF,cAAMA,UAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,KAAK,KAAK,UAAU,CAAC;AAC/E,cAAM,UAAU,MAAM;AAAA,UACpB,6BAA6BA,QAAO,SAAS,CAAC;AAAA,UAC9C;AAAA,UACA,EAAE,cAAc,EAAE,mCAAmC,+BAA+B,EAAE;AAAA,QACxF;AACA,cAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,MAAM,CAAC,IAAI;AAChE,YAAI,CAAC,MAAO,OAAM,IAAI,MAAM,EAAE,uCAAuC,0BAA0B,CAAC;AAChG,YAAI,CAAC,WAAW;AACd,oBAAU,KAAK;AACf;AAAA,YACE,OAAO,MAAM,oBAAoB,WAC7B,MAAM,kBACN,OAAO,MAAM,qBAAqB,WAChC,MAAM,mBACN;AAAA,UACR;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,YAAI,CAAC,WAAW;AACd,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,EAAE,mCAAmC,+BAA+B;AACzH,mBAAS,OAAO;AAChB,oBAAU,IAAI;AAAA,QAChB;AAAA,MACF,UAAE;AACA,YAAI,CAAC,UAAW,cAAa,KAAK;AAAA,MACpC;AAAA,IACF;AACA,SAAK,KAAK;AACV,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,IAAI,CAAC,CAAC;AAEV,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,cAAc,QAAQ,QAAQ,eAAe;AACnD,QAAM,cAAc;AAAA,IAClB,QAAQ,aAAa,QAAQ,cAAc;AAAA,IAC3C,QAAQ,WAAW,QAAQ,YAAY;AAAA,EACzC;AACA,QAAM,gBAAgB,MAAM,QAAgC,OAAO;AAAA,IACjE,IAAI,QAAQ;AAAA,IACZ,UAAU,QAAQ,YAAY,QAAQ,aAAa;AAAA,IACnD;AAAA,IACA,WAAW,QAAQ,aAAa,QAAQ,cAAc;AAAA,IACtD,SAAS,QAAQ,WAAW,QAAQ,YAAY;AAAA,IAChD,UAAU,QAAQ,YAAY;AAAA,IAC9B,6BAA6B,QAAQ,+BAA+B,QAAQ,kCAAkC;AAAA,IAC9G,2BAA2B,QAAQ,6BAA6B,QAAQ,+BAA+B;AAAA,IACvG,MAAM,QAAQ,QAAQ;AAAA,EACxB,IAAI,CAAC,QAAQ,WAAW,CAAC;AAEzB,QAAM,wBAAwB,MAAM,QAAQ,MAC1C,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,OAAE,WAAU,eAAe,YAAE,6CAA6C,sBAAsB,GAAE;AAAA,IAClG,cACC,qBAAC,OAAE,WAAU,iCACV;AAAA,QAAE,qCAAqC,aAAa;AAAA,MAAE;AAAA,MAAG;AAAA,OAC5D,IACE;AAAA,IACJ,qBAAC,OAAE,WAAU,iCACV;AAAA,QAAE,oCAAoC,OAAO;AAAA,MAAE;AAAA,MAAG;AAAA,OACrD;AAAA,KACF,GACC,CAAC,aAAa,aAAa,CAAC,CAAC;AAEhC,QAAM,eAAe,MAAM,YAAY,OAAO,WAAmC;AAC/E,QAAI,CAAC,QAAQ,GAAI;AACjB,UAAM,UAAU,yBAAyB,QAAQ,EAAE,IAAI,OAAO,GAAG,CAAC;AAClE,UAAM,WAAW,wBAAwB,SAAS;AAAA,MAChD,cAAc,EAAE,0CAA0C,iCAAiC;AAAA,IAC7F,CAAC;AACD,UAAM,EAAE,0CAA0C,wBAAwB,GAAG,SAAS;AACtF,WAAO,KAAK,+BAA+B;AAAA,EAC7C,GAAG,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;AAE1B,QAAM,iBAAiB,MAAM,YAAY,OAAO,WAAgC;AAC9E,QAAI,CAAC,QAAQ,GAAI;AACjB,UAAM,WAAW,WAAW,WAAW,qCAAqC;AAC5E,UAAM,eAAe,UAAU;AAAA,MAC7B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,OAAO,IAAI,iBAAiB,mBAAmB,KAAK,CAAC;AAAA,IAClF,CAAC;AACD;AAAA,MACE,WAAW,WACP,EAAE,yCAAyC,yBAAyB,IACpE,EAAE,yCAAyC,yBAAyB;AAAA,MACxE;AAAA,IACF;AACA,WAAO,QAAQ;AAAA,EACjB,GAAG,CAAC,iBAAiB,QAAQ,IAAI,QAAQ,CAAC,CAAC;AAE3C,MAAI,WAAW;AACb,WACE,oBAAC,QACC,8BAAC,YACC,8BAAC,kBAAe,OAAO,EAAE,oCAAoC,0BAA0B,GAAG,GAC5F,GACF;AAAA,EAEJ;AAEA,MAAI,SAAS,CAAC,QAAQ;AACpB,WACE,oBAAC,QACC,8BAAC,YACC,8BAAC,gBAAa,OAAO,SAAS,EAAE,mCAAmC,+BAA+B,GAAG,GACvG,GACF;AAAA,EAEJ;AAEA,SACE,oBAAC,QACC,+BAAC,YACC;AAAA,yBAAC,SAAI,WAAU,gDACb;AAAA,2BAAC,SAAI,WAAU,qCACb;AAAA,4BAAC,SAAM,SAAS,qBAAqB,MAAM,GACxC,YAAE,8BAA8B,MAAM,IAAI,MAAM,GACnD;AAAA,QACC,OAAO,cAAc,OAAO,YAC3B,qBAAC,UAAK,WAAU,iCACb;AAAA,YAAE,mCAAmC,aAAa;AAAA,UAAE;AAAA,UAAE,gBAAgB,OAAO,aAAa,OAAO,cAAc,IAAI;AAAA,WACtH,IACE;AAAA,SACN;AAAA,MACC,cACC,qBAAC,OAAE,WAAU,iCACV;AAAA,UAAE,qCAAqC,aAAa;AAAA,QAAE;AAAA,QAAG;AAAA,SAC5D,IACE;AAAA,MACJ,qBAAC,OAAE,WAAU,iCACV;AAAA,UAAE,oCAAoC,OAAO;AAAA,QAAE;AAAA,QAAG;AAAA,SACrD;AAAA,OACF;AAAA,IAEC,WAAW,YACV,qBAAC,SAAI,WAAU,sCACb;AAAA,0BAAC,SAAI,WAAU,4BAA4B,YAAE,sCAAsC,UAAU,GAAE;AAAA,MAC/F;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,UAAU,CAAC,UAAU,mBAAmB,MAAM,OAAO,KAAK;AAAA,UAC1D,aAAa,EAAE,4CAA4C,0BAA0B;AAAA,UACrF,WAAU;AAAA;AAAA,MACZ;AAAA,MACA,qBAAC,SAAI,WAAU,wBACb;AAAA,4BAAC,UAAO,SAAS,MAAM,eAAe,QAAQ,GAC3C,YAAE,sCAAsC,SAAS,GACpD;AAAA,QACA,oBAAC,UAAO,SAAQ,eAAc,SAAS,MAAM,eAAe,QAAQ,GACjE,YAAE,sCAAsC,QAAQ,GACnD;AAAA,SACF;AAAA,OACF,IACE,OAAO,mBAAmB,OAAO,mBACnC,qBAAC,SAAI,WAAU,oEACb;AAAA,0BAAC,SAAI,WAAU,oCAAoC,YAAE,wCAAwC,kBAAkB,GAAE;AAAA,MACjH,oBAAC,OAAG,iBAAO,mBAAmB,OAAO,kBAAiB;AAAA,OACxD,IACE;AAAA,IAEJ;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,sCAAsC,eAAe;AAAA,QAC9D,aAAa,EAAE,yCAAyC,MAAM;AAAA,QAC9D,UAAS;AAAA,QACT,YAAW;AAAA,QACX;AAAA,QACA,UAAU;AAAA,QACV,mBAAiB;AAAA,QACjB;AAAA,QACA,cAAc,OAAO,KACnB;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ;AAAA,cACN,cAAc;AAAA,cACd,YAAY;AAAA,cACZ,UAAU,OAAO;AAAA,cACjB,kBAAkB;AAAA,cAClB,gBAAgB,OAAO;AAAA,YACzB;AAAA,YACA,YAAW;AAAA,YACX,sBAAsB;AAAA,cACpB,MAAM;AAAA,cACN,SAAS;AAAA,gBACP,EAAE,IAAI,WAAW,OAAO,EAAE,oDAAoD,SAAS,EAAE;AAAA,gBACzF,EAAE,IAAI,UAAU,OAAO,EAAE,mDAAmD,QAAQ,EAAE;AAAA,cACxF;AAAA,YACF;AAAA,YACA,eAAe;AAAA,cACb,MAAM;AAAA,cACN,SAAS,EAAE,gDAAgD,+BAA+B;AAAA,cAC1F,MAAM,EAAE,6CAA6C,mDAAmD;AAAA,YAC1G;AAAA,YACA,gBAAgB;AAAA,YAChB,eAAe,CAAC,EAAE,cAAc,SAAS,MACvC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,SAAS;AAAA,gBACT;AAAA,gBACA,cAAY,EAAE,+CAA+C,iBAAiB;AAAA,gBAC9E,OAAO,EAAE,+CAA+C,iBAAiB;AAAA,gBAEzE,8BAAC,QAAK,WAAU,WAAU;AAAA;AAAA,YAC5B;AAAA;AAAA,QAEJ,IACE;AAAA;AAAA,IACN;AAAA,KACF,GACF;AAEJ;AAEA,SAAS,qBAAqB,QAA6C;AACzE,MAAI,WAAW,WAAY,QAAO;AAClC,MAAI,WAAW,WAAY,QAAO;AAClC,SAAO;AACT;AAEA,SAAS,gBAAgB,OAA+B;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,SAAO,KAAK,mBAAmB;AACjC;AAEA,SAAS,gBAAgB,OAAuB,KAA6B;AAC3E,QAAM,aAAa,gBAAgB,KAAK;AACxC,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,cAAc,SAAU,QAAO,GAAG,UAAU,OAAO,QAAQ;AAC/D,SAAO,cAAc,YAAY;AACnC;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Send } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LeaveRequestForm, buildLeaveRequestPayload, type LeaveRequestFormValues } from '@open-mercato/core/modules/staff/components/LeaveRequestForm'\n\ntype LeaveRequestRecord = {\n id: string\n member?: { id?: string; displayName?: string }\n memberId?: string | null\n member_id?: string | null\n startDate?: string | null\n start_date?: string | null\n endDate?: string | null\n end_date?: string | null\n timezone?: string | null\n status?: 'pending' | 'approved' | 'rejected'\n unavailabilityReasonEntryId?: string | null\n unavailability_reason_entry_id?: string | null\n unavailabilityReasonValue?: string | null\n unavailability_reason_value?: string | null\n note?: string | null\n decisionComment?: string | null\n decision_comment?: string | null\n decidedAt?: string | null\n decided_at?: string | null\n} & Record<string, unknown>\n\ntype LeaveRequestsResponse = {\n items?: LeaveRequestRecord[]\n}\n\nexport default function StaffLeaveRequestDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const router = useRouter()\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [record, setRecord] = React.useState<LeaveRequestRecord | null>(null)\n const [decisionComment, setDecisionComment] = React.useState('')\n\n React.useEffect(() => {\n if (!id) {\n setError(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n setIsLoading(false)\n return\n }\n const requestId = id\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: requestId })\n const payload = await readApiResultOrThrow<LeaveRequestsResponse>(\n `/api/staff/leave-requests?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.leaveRequests.errors.load', 'Failed to load leave request.') },\n )\n const entry = Array.isArray(payload.items) ? payload.items[0] : null\n if (!entry) throw new Error(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n if (!cancelled) {\n setRecord(entry)\n setDecisionComment(\n typeof entry.decisionComment === 'string'\n ? entry.decisionComment\n : typeof entry.decision_comment === 'string'\n ? entry.decision_comment\n : ''\n )\n }\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('staff.leaveRequests.errors.load', 'Failed to load leave request.')\n setError(message)\n setRecord(null)\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n void load()\n return () => { cancelled = true }\n }, [id, t])\n\n const status = record?.status ?? 'pending'\n const memberLabel = record?.member?.displayName ?? null\n const dateSummary = formatDateRange(\n record?.startDate ?? record?.start_date ?? null,\n record?.endDate ?? record?.end_date ?? null,\n )\n const initialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n id: record?.id,\n memberId: record?.memberId ?? record?.member_id ?? null,\n memberLabel,\n startDate: record?.startDate ?? record?.start_date ?? null,\n endDate: record?.endDate ?? record?.end_date ?? null,\n timezone: record?.timezone ?? null,\n unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? record?.unavailability_reason_entry_id ?? null,\n unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,\n note: record?.note ?? null,\n }), [record, memberLabel])\n\nconst handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {\n if (!record?.id) return\n const payload = buildLeaveRequestPayload(values, { id: record.id })\n await updateCrud('staff/leave-requests', payload, {\n errorMessage: t('staff.leaveRequests.form.errors.update', 'Failed to update leave request.'),\n })\n flash(t('staff.leaveRequests.form.flash.updated', 'Leave request updated.'), 'success')\n router.push('/backend/staff/leave-requests')\n }, [record?.id, router, t])\n\n const handleDecision = React.useCallback(async (action: 'accept' | 'reject') => {\n if (!record?.id) return\n const endpoint = action === 'accept' ? '/api/staff/leave-requests/accept' : '/api/staff/leave-requests/reject'\n await apiCallOrThrow(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, decisionComment: decisionComment || null }),\n })\n flash(\n action === 'accept'\n ? t('staff.leaveRequests.messages.accepted', 'Leave request approved.')\n : t('staff.leaveRequests.messages.rejected', 'Leave request rejected.'),\n 'success',\n )\n router.refresh()\n }, [decisionComment, record?.id, router, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.leaveRequests.form.loading', 'Loading leave request...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !record) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.leaveRequests.errors.load', 'Failed to load leave request.')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"mb-6 space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <Badge variant={resolveStatusVariant(status)}>\n {t(`staff.leaveRequests.status.${status}`, status)}\n </Badge>\n {record.decided_at || record.decidedAt ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.decision.at', 'Decision at')} {formatDateLabel(record.decidedAt ?? record.decided_at ?? null)}\n </span>\n ) : null}\n </div>\n {memberLabel ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}\n </p>\n ) : null}\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}\n </p>\n </div>\n\n {status === 'pending' ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4\">\n <div className=\"mb-3 text-sm font-medium\">{t('staff.leaveRequests.decision.title', 'Decision')}</div>\n <Textarea\n value={decisionComment}\n onChange={(event) => setDecisionComment(event.target.value)}\n placeholder={t('staff.leaveRequests.decision.placeholder', 'Add a comment (optional)')}\n className=\"mb-3\"\n />\n <div className=\"flex flex-wrap gap-2\">\n <Button onClick={() => handleDecision('accept')}>\n {t('staff.leaveRequests.actions.accept', 'Approve')}\n </Button>\n <Button variant=\"destructive\" onClick={() => handleDecision('reject')}>\n {t('staff.leaveRequests.actions.reject', 'Reject')}\n </Button>\n </div>\n </div>\n ) : record.decisionComment || record.decision_comment ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <div className=\"mb-1 font-medium text-foreground\">{t('staff.leaveRequests.decision.comment', 'Decision comment')}</div>\n <p>{record.decisionComment ?? record.decision_comment}</p>\n </div>\n ) : null}\n\n <LeaveRequestForm\n title={t('staff.leaveRequests.form.editTitle', 'Leave request')}\n submitLabel={t('staff.leaveRequests.form.actions.save', 'Save')}\n backHref=\"/backend/staff/leave-requests\"\n cancelHref=\"/backend/staff/leave-requests\"\n initialValues={initialValues}\n onSubmit={handleSubmit}\n allowMemberSelect\n memberLabel={memberLabel}\n extraActions={record.id ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'leave_request',\n entityId: record.id,\n sourceEntityType: 'staff:leave_request',\n sourceEntityId: record.id,\n previewData: {\n title: memberLabel || t('staff.leaveRequests.messages.contextTitle', 'Linked leave request'),\n subtitle: dateSummary || undefined,\n status: record?.status ?? undefined,\n },\n }}\n viewHref={`/backend/staff/leave-requests/${record.id}`}\n lockedType=\"staff.leave_request_approval\"\n requiredActionConfig={{\n mode: 'required',\n options: [\n { id: 'approve', label: t('staff.notifications.leaveRequest.actions.approve', 'Approve') },\n { id: 'reject', label: t('staff.notifications.leaveRequest.actions.reject', 'Reject') },\n ],\n }}\n defaultValues={{\n type: 'staff.leave_request_approval',\n subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),\n body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),\n }}\n />\n ) : null}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction resolveStatusVariant(status: 'pending' | 'approved' | 'rejected') {\n if (status === 'approved') return 'default'\n if (status === 'rejected') return 'destructive'\n return 'secondary'\n}\n\nfunction formatDateLabel(value?: string | null): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return value\n return date.toLocaleDateString()\n}\n\nfunction formatDateRange(start?: string | null, end?: string | null): string {\n const startLabel = formatDateLabel(start)\n const endLabel = formatDateLabel(end)\n if (startLabel && endLabel) return `${startLabel} -> ${endLabel}`\n return startLabel || endLabel || '-'\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["params"]
7
7
  }
@@ -9,6 +9,7 @@ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
9
9
  import { AvailabilityRulesEditor } from "@open-mercato/core/modules/planner/components/AvailabilityRulesEditor";
10
10
  import { buildMemberScheduleItems } from "@open-mercato/core/modules/staff/lib/memberSchedule";
11
11
  import { useT } from "@open-mercato/shared/lib/i18n/context";
12
+ import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
12
13
  function StaffMyAvailabilityPage() {
13
14
  const t = useT();
14
15
  const [member, setMember] = React.useState(null);
@@ -70,6 +71,18 @@ function StaffMyAvailabilityPage() {
70
71
  ] }) }) });
71
72
  }
72
73
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
74
+ member.availabilityRuleSetId ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
75
+ SendObjectMessageDialog,
76
+ {
77
+ object: {
78
+ entityModule: "staff",
79
+ entityType: "my_availability",
80
+ entityId: member.availabilityRuleSetId,
81
+ previewData: { title: member.displayName ?? t("staff.myAvailability.title", "My Availability") }
82
+ },
83
+ viewHref: "/backend/staff/my-availability"
84
+ }
85
+ ) }) : null,
73
86
  !canManageAvailability ? /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-lg border bg-card p-4 text-sm text-muted-foreground", children: [
74
87
  /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: t("staff.myAvailability.readOnly.title", "Only an administrator can manage your availability.") }),
75
88
  /* @__PURE__ */ jsx("p", { children: t("staff.myAvailability.readOnly.body", "Use leave requests to request changes.") }),