@open-mercato/core 0.4.5-develop-636d33c995 → 0.4.5-develop-3d8e759e45

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 (211) 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/inbox-actions.js +51 -0
  8. package/dist/modules/catalog/inbox-actions.js.map +7 -0
  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/inbox-actions.js +230 -0
  30. package/dist/modules/customers/inbox-actions.js.map +7 -0
  31. package/dist/modules/customers/lib/messageObjectPreviews.js +41 -5
  32. package/dist/modules/customers/lib/messageObjectPreviews.js.map +2 -2
  33. package/dist/modules/customers/message-objects.js +31 -11
  34. package/dist/modules/customers/message-objects.js.map +2 -2
  35. package/dist/modules/inbox_ops/api/emails/[id]/route.js +40 -1
  36. package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
  37. package/dist/modules/inbox_ops/api/extract/route.js +87 -0
  38. package/dist/modules/inbox_ops/api/extract/route.js.map +7 -0
  39. package/dist/modules/inbox_ops/api/proposals/[id]/translate/route.js +6 -1
  40. package/dist/modules/inbox_ops/api/proposals/[id]/translate/route.js.map +2 -2
  41. package/dist/modules/inbox_ops/api/proposals/counts/route.js.map +2 -2
  42. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +40 -14
  43. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
  44. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.meta.js +2 -2
  45. package/dist/modules/inbox_ops/backend/inbox-ops/log/page.meta.js.map +2 -2
  46. package/dist/modules/inbox_ops/backend/inbox-ops/page.js +161 -79
  47. package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
  48. package/dist/modules/inbox_ops/backend/inbox-ops/page.meta.js +2 -2
  49. package/dist/modules/inbox_ops/backend/inbox-ops/page.meta.js.map +2 -2
  50. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js +109 -62
  51. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.js.map +3 -3
  52. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.js +2 -2
  53. package/dist/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.js.map +2 -2
  54. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +36 -14
  55. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
  56. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.meta.js +2 -2
  57. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.meta.js.map +2 -2
  58. package/dist/modules/inbox_ops/components/proposals/ActionCard.js +65 -10
  59. package/dist/modules/inbox_ops/components/proposals/ActionCard.js.map +2 -2
  60. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +58 -10
  61. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  62. package/dist/modules/inbox_ops/lib/constants.js.map +2 -2
  63. package/dist/modules/inbox_ops/lib/contactValidation.js +40 -0
  64. package/dist/modules/inbox_ops/lib/contactValidation.js.map +7 -0
  65. package/dist/modules/inbox_ops/lib/executionEngine.js +31 -826
  66. package/dist/modules/inbox_ops/lib/executionEngine.js.map +3 -3
  67. package/dist/modules/inbox_ops/lib/executionHelpers.js +368 -0
  68. package/dist/modules/inbox_ops/lib/executionHelpers.js.map +7 -0
  69. package/dist/modules/inbox_ops/lib/extractionPrompt.js +28 -35
  70. package/dist/modules/inbox_ops/lib/extractionPrompt.js.map +3 -3
  71. package/dist/modules/inbox_ops/lib/inbox-actions-generated.d.js +1 -0
  72. package/dist/modules/inbox_ops/lib/inbox-actions-generated.d.js.map +7 -0
  73. package/dist/modules/inbox_ops/lib/translationProvider.js +15 -10
  74. package/dist/modules/inbox_ops/lib/translationProvider.js.map +2 -2
  75. package/dist/modules/inbox_ops/subscribers/extractionWorker.js +16 -16
  76. package/dist/modules/inbox_ops/subscribers/extractionWorker.js.map +2 -2
  77. package/dist/modules/messages/commands/messages.js +3 -0
  78. package/dist/modules/messages/commands/messages.js.map +2 -2
  79. package/dist/modules/messages/components/message-detail/panels/objects-panel.js +6 -1
  80. package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +2 -2
  81. package/dist/modules/messages/components/message-detail/panels/thread-panel.js +4 -1
  82. package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
  83. package/dist/modules/messages/frontend/messages/view/[token]/page.js +1 -0
  84. package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +2 -2
  85. package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -7
  86. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  87. package/dist/modules/resources/lib/messageObjectPreviews.js +43 -0
  88. package/dist/modules/resources/lib/messageObjectPreviews.js.map +7 -0
  89. package/dist/modules/resources/message-objects.js +37 -0
  90. package/dist/modules/resources/message-objects.js.map +7 -0
  91. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +19 -0
  92. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  93. package/dist/modules/sales/backend/sales/documents/[id]/page.js +23 -2
  94. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  95. package/dist/modules/sales/backend/sales/quotes/[id]/page.js +1 -1
  96. package/dist/modules/sales/backend/sales/quotes/[id]/page.js.map +2 -2
  97. package/dist/modules/sales/inbox-actions.js +278 -0
  98. package/dist/modules/sales/inbox-actions.js.map +7 -0
  99. package/dist/modules/sales/lib/messageObjectPreviews.js +49 -4
  100. package/dist/modules/sales/lib/messageObjectPreviews.js.map +2 -2
  101. package/dist/modules/sales/message-objects.js +44 -2
  102. package/dist/modules/sales/message-objects.js.map +2 -2
  103. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +59 -30
  104. package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +2 -2
  105. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +1 -1
  106. package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +1 -1
  107. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +8 -30
  108. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  109. package/dist/modules/staff/backend/staff/my-availability/page.js +13 -0
  110. package/dist/modules/staff/backend/staff/my-availability/page.js.map +2 -2
  111. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +8 -31
  112. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  113. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +32 -10
  114. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  115. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +14 -1
  116. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  117. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +14 -1
  118. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  119. package/dist/modules/staff/components/TeamForm.js +4 -2
  120. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  121. package/dist/modules/staff/components/TeamRoleForm.js +4 -2
  122. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  123. package/dist/modules/staff/lib/messageObjectPreviews.js +111 -2
  124. package/dist/modules/staff/lib/messageObjectPreviews.js.map +2 -2
  125. package/dist/modules/staff/message-objects.js +79 -8
  126. package/dist/modules/staff/message-objects.js.map +2 -2
  127. package/jest.config.cjs +1 -0
  128. package/jest.mocks/inbox-actions.generated.js +5 -0
  129. package/package.json +2 -2
  130. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +19 -5
  131. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +14 -0
  132. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +40 -0
  133. package/src/modules/catalog/inbox-actions.ts +60 -0
  134. package/src/modules/catalog/lib/messageObjectPreviews.ts +176 -0
  135. package/src/modules/catalog/message-objects.ts +102 -0
  136. package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -0
  137. package/src/modules/currencies/lib/messageObjectPreviews.ts +65 -0
  138. package/src/modules/currencies/message-objects.ts +40 -0
  139. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +19 -0
  140. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +13 -0
  141. package/src/modules/customers/backend/customers/people/[id]/page.tsx +19 -0
  142. package/src/modules/customers/components/detail/CompanyHighlights.tsx +14 -9
  143. package/src/modules/customers/components/detail/PersonHighlights.tsx +14 -9
  144. package/src/modules/customers/inbox-actions.ts +285 -0
  145. package/src/modules/customers/lib/messageObjectPreviews.ts +43 -3
  146. package/src/modules/customers/message-objects.ts +31 -11
  147. package/src/modules/inbox_ops/api/emails/[id]/route.ts +44 -0
  148. package/src/modules/inbox_ops/api/extract/route.ts +94 -0
  149. package/src/modules/inbox_ops/api/proposals/[id]/translate/route.ts +6 -1
  150. package/src/modules/inbox_ops/api/proposals/counts/route.ts +2 -0
  151. package/src/modules/inbox_ops/backend/inbox-ops/log/page.meta.ts +2 -2
  152. package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +43 -13
  153. package/src/modules/inbox_ops/backend/inbox-ops/page.meta.ts +2 -2
  154. package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +176 -81
  155. package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.meta.ts +2 -2
  156. package/src/modules/inbox_ops/backend/inbox-ops/proposals/[id]/page.tsx +122 -68
  157. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.meta.ts +2 -2
  158. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +36 -14
  159. package/src/modules/inbox_ops/components/proposals/ActionCard.tsx +91 -7
  160. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +64 -12
  161. package/src/modules/inbox_ops/lib/constants.ts +9 -0
  162. package/src/modules/inbox_ops/lib/contactValidation.ts +54 -0
  163. package/src/modules/inbox_ops/lib/executionEngine.ts +47 -1060
  164. package/src/modules/inbox_ops/lib/executionHelpers.ts +527 -0
  165. package/src/modules/inbox_ops/lib/extractionPrompt.ts +45 -34
  166. package/src/modules/inbox_ops/lib/inbox-actions-generated.d.ts +11 -0
  167. package/src/modules/inbox_ops/lib/translationProvider.ts +16 -10
  168. package/src/modules/inbox_ops/subscribers/extractionWorker.ts +16 -18
  169. package/src/modules/messages/commands/messages.ts +4 -0
  170. package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +8 -1
  171. package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +3 -0
  172. package/src/modules/messages/frontend/messages/view/[token]/page.tsx +1 -0
  173. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +20 -4
  174. package/src/modules/resources/lib/messageObjectPreviews.ts +55 -0
  175. package/src/modules/resources/message-objects.ts +36 -0
  176. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +18 -0
  177. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -0
  178. package/src/modules/sales/backend/sales/quotes/[id]/page.tsx +1 -1
  179. package/src/modules/sales/inbox-actions.ts +359 -0
  180. package/src/modules/sales/lib/messageObjectPreviews.ts +54 -4
  181. package/src/modules/sales/message-objects.ts +44 -2
  182. package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +72 -34
  183. package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +1 -1
  184. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +7 -29
  185. package/src/modules/staff/backend/staff/my-availability/page.tsx +14 -0
  186. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +8 -30
  187. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +28 -7
  188. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +12 -0
  189. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +12 -0
  190. package/src/modules/staff/components/TeamForm.tsx +3 -0
  191. package/src/modules/staff/components/TeamRoleForm.tsx +3 -0
  192. package/src/modules/staff/lib/messageObjectPreviews.ts +133 -2
  193. package/src/modules/staff/message-objects.ts +79 -8
  194. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +0 -51
  195. package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +0 -7
  196. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +0 -35
  197. package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +0 -7
  198. package/dist/modules/customers/widgets/messages/index.js +0 -7
  199. package/dist/modules/customers/widgets/messages/index.js.map +0 -7
  200. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +0 -51
  201. package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +0 -7
  202. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +0 -34
  203. package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +0 -7
  204. package/dist/modules/staff/widgets/messages/index.js +0 -7
  205. package/dist/modules/staff/widgets/messages/index.js.map +0 -7
  206. package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +0 -57
  207. package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +0 -49
  208. package/src/modules/customers/widgets/messages/index.ts +0 -2
  209. package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +0 -57
  210. package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +0 -44
  211. package/src/modules/staff/widgets/messages/index.ts +0 -2
@@ -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": "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",
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.") }),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/staff/backend/staff/my-availability/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { buildMemberScheduleItems } from '@open-mercato/core/modules/staff/lib/memberSchedule'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype SelfMemberResponse = {\n member?: {\n id?: string\n displayName?: string\n availabilityRuleSetId?: string | null\n } | null\n}\n\ntype FeatureCheckResponse = {\n ok?: boolean\n granted?: string[]\n}\n\nexport default function StaffMyAvailabilityPage() {\n const t = useT()\n const [member, setMember] = React.useState<SelfMemberResponse['member']>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [canManageAvailability, setCanManageAvailability] = React.useState(false)\n const [canManageUnavailability, setCanManageUnavailability] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const memberCall = await apiCall<SelfMemberResponse>('/api/staff/team-members/self')\n const featureCall = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n features: [\n 'planner.manage_availability',\n 'staff.my_availability.manage',\n 'staff.my_availability.unavailability',\n ],\n }),\n })\n if (!cancelled) {\n setMember(memberCall.result?.member ?? null)\n const granted = Array.isArray(featureCall.result?.granted) ? featureCall.result?.granted ?? [] : []\n const hasPlannerManage = granted.includes('planner.manage_availability')\n const hasSelfManage = granted.includes('staff.my_availability.manage')\n const hasSelfUnavailability = granted.includes('staff.my_availability.unavailability')\n const canManage = hasPlannerManage || hasSelfManage\n setCanManageAvailability(canManage)\n setCanManageUnavailability(canManage && (hasPlannerManage || hasSelfUnavailability))\n }\n } catch (err) {\n if (!cancelled) {\n setError(t('staff.myAvailability.errors.load', 'Failed to load availability.'))\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n void load()\n return () => { cancelled = true }\n }, [t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.myAvailability.loading', 'Loading availability...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n if (!member?.id) {\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-3 rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n <p>{t('staff.myAvailability.empty.profileRequired', 'Create your team member profile to manage availability.')}</p>\n <Button asChild size=\"sm\">\n <Link href=\"/backend/staff/profile/create\">\n {t('staff.leaveRequests.actions.createProfile', 'Create my profile')}\n </Link>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-4\">\n {!canManageAvailability ? (\n <div className=\"space-y-2 rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <p className=\"font-medium text-foreground\">\n {t('staff.myAvailability.readOnly.title', 'Only an administrator can manage your availability.')}\n </p>\n <p>\n {t('staff.myAvailability.readOnly.body', 'Use leave requests to request changes.')}\n </p>\n <Button asChild size=\"sm\" variant=\"outline\">\n <Link href=\"/backend/staff/my-leave-requests\">\n {t('staff.leaveRequests.my.title', 'My leave requests')}\n </Link>\n </Button>\n </div>\n ) : null}\n <AvailabilityRulesEditor\n subjectType=\"member\"\n subjectId={member.id}\n labelPrefix=\"staff.teamMembers\"\n mode=\"availability\"\n rulesetId={member.availabilityRuleSetId ?? null}\n buildScheduleItems={({ availabilityRules, translate }) => (\n buildMemberScheduleItems({ availabilityRules, translate })\n )}\n readOnly={!canManageAvailability}\n allowUnavailability={canManageUnavailability}\n />\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { buildMemberScheduleItems } from '@open-mercato/core/modules/staff/lib/memberSchedule'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\n\ntype SelfMemberResponse = {\n member?: {\n id?: string\n displayName?: string\n availabilityRuleSetId?: string | null\n } | null\n}\n\ntype FeatureCheckResponse = {\n ok?: boolean\n granted?: string[]\n}\n\nexport default function StaffMyAvailabilityPage() {\n const t = useT()\n const [member, setMember] = React.useState<SelfMemberResponse['member']>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [canManageAvailability, setCanManageAvailability] = React.useState(false)\n const [canManageUnavailability, setCanManageUnavailability] = React.useState(false)\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const memberCall = await apiCall<SelfMemberResponse>('/api/staff/team-members/self')\n const featureCall = await apiCall<FeatureCheckResponse>('/api/auth/feature-check', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n features: [\n 'planner.manage_availability',\n 'staff.my_availability.manage',\n 'staff.my_availability.unavailability',\n ],\n }),\n })\n if (!cancelled) {\n setMember(memberCall.result?.member ?? null)\n const granted = Array.isArray(featureCall.result?.granted) ? featureCall.result?.granted ?? [] : []\n const hasPlannerManage = granted.includes('planner.manage_availability')\n const hasSelfManage = granted.includes('staff.my_availability.manage')\n const hasSelfUnavailability = granted.includes('staff.my_availability.unavailability')\n const canManage = hasPlannerManage || hasSelfManage\n setCanManageAvailability(canManage)\n setCanManageUnavailability(canManage && (hasPlannerManage || hasSelfUnavailability))\n }\n } catch (err) {\n if (!cancelled) {\n setError(t('staff.myAvailability.errors.load', 'Failed to load availability.'))\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n void load()\n return () => { cancelled = true }\n }, [t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.myAvailability.loading', 'Loading availability...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error} />\n </PageBody>\n </Page>\n )\n }\n\n if (!member?.id) {\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-3 rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n <p>{t('staff.myAvailability.empty.profileRequired', 'Create your team member profile to manage availability.')}</p>\n <Button asChild size=\"sm\">\n <Link href=\"/backend/staff/profile/create\">\n {t('staff.leaveRequests.actions.createProfile', 'Create my profile')}\n </Link>\n </Button>\n </div>\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-4\">\n {member.availabilityRuleSetId ? (\n <div className=\"flex justify-end\">\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'my_availability',\n entityId: member.availabilityRuleSetId,\n previewData: { title: member.displayName ?? t('staff.myAvailability.title', 'My Availability') },\n }}\n viewHref=\"/backend/staff/my-availability\"\n />\n </div>\n ) : null}\n {!canManageAvailability ? (\n <div className=\"space-y-2 rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <p className=\"font-medium text-foreground\">\n {t('staff.myAvailability.readOnly.title', 'Only an administrator can manage your availability.')}\n </p>\n <p>\n {t('staff.myAvailability.readOnly.body', 'Use leave requests to request changes.')}\n </p>\n <Button asChild size=\"sm\" variant=\"outline\">\n <Link href=\"/backend/staff/my-leave-requests\">\n {t('staff.leaveRequests.my.title', 'My leave requests')}\n </Link>\n </Button>\n </div>\n ) : null}\n <AvailabilityRulesEditor\n subjectType=\"member\"\n subjectId={member.id}\n labelPrefix=\"staff.teamMembers\"\n mode=\"availability\"\n rulesetId={member.availabilityRuleSetId ?? null}\n buildScheduleItems={({ availabilityRules, translate }) => (\n buildMemberScheduleItems({ availabilityRules, translate })\n )}\n readOnly={!canManageAvailability}\n allowUnavailability={canManageUnavailability}\n />\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,10 +2,8 @@
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 { Leaf } 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
- import { Button } from "@open-mercato/ui/primitives/button";
9
7
  import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
10
8
  import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
11
9
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
@@ -75,19 +73,6 @@ function StaffMyLeaveRequestDetailPage({ params }) {
75
73
  record?.startDate ?? record?.start_date ?? null,
76
74
  record?.endDate ?? record?.end_date ?? null
77
75
  );
78
- const messageContextPreview = React.useMemo(() => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
79
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: t("staff.leaveRequests.messages.contextTitle", "Linked leave request") }),
80
- memberLabel ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
81
- t("staff.leaveRequests.detail.member", "Team member"),
82
- ": ",
83
- memberLabel
84
- ] }) : null,
85
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
86
- t("staff.leaveRequests.detail.dates", "Dates"),
87
- ": ",
88
- dateSummary
89
- ] })
90
- ] }), [dateSummary, memberLabel, t]);
91
76
  const handleSubmit = React.useCallback(async (values) => {
92
77
  if (!record?.id) return;
93
78
  const payload = buildLeaveRequestPayload(values, { id: record.id });
@@ -137,8 +122,14 @@ function StaffMyLeaveRequestDetailPage({ params }) {
137
122
  entityType: "leave_request",
138
123
  entityId: record.id,
139
124
  sourceEntityType: "staff:leave_request",
140
- sourceEntityId: record.id
125
+ sourceEntityId: record.id,
126
+ previewData: {
127
+ title: memberLabel || t("staff.leaveRequests.messages.contextTitle", "Linked leave request"),
128
+ subtitle: dateSummary || void 0,
129
+ status: record?.status ?? void 0
130
+ }
141
131
  },
132
+ viewHref: `/backend/staff/leave-requests/${record.id}`,
142
133
  lockedType: "staff.leave_request_approval",
143
134
  requiredActionConfig: {
144
135
  mode: "required",
@@ -151,21 +142,7 @@ function StaffMyLeaveRequestDetailPage({ params }) {
151
142
  type: "staff.leave_request_approval",
152
143
  subject: t("staff.leaveRequests.messages.compose.subject", "Leave request approval needed"),
153
144
  body: t("staff.leaveRequests.messages.compose.body", "Please review this leave request and take action.")
154
- },
155
- contextPreview: messageContextPreview,
156
- renderTrigger: ({ openComposer, disabled }) => /* @__PURE__ */ jsx(
157
- Button,
158
- {
159
- type: "button",
160
- size: "icon",
161
- variant: "outline",
162
- onClick: openComposer,
163
- disabled,
164
- "aria-label": t("staff.leaveRequests.messages.compose.action", "Send for review"),
165
- title: t("staff.leaveRequests.messages.compose.action", "Send for review"),
166
- children: /* @__PURE__ */ jsx(Leaf, { className: "h-4 w-4" })
167
- }
168
- )
145
+ }
169
146
  }
170
147
  ) : null
171
148
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/backend/staff/my-leave-requests/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Leaf } 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 { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { 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 StaffMyLeaveRequestDetailPage({ 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\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 }\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 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 const dateSummary = formatDateRange(\n record?.startDate ?? record?.start_date ?? null,\n record?.endDate ?? record?.end_date ?? null,\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/my-leave-requests')\n }, [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 {record.decisionComment || record.decision_comment ? (\n <div className=\"text-sm text-muted-foreground\">\n <div className=\"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 </div>\n\n {status === 'pending' ? (\n <LeaveRequestForm\n title={t('staff.leaveRequests.form.editTitle', 'Leave request')}\n submitLabel={t('staff.leaveRequests.form.actions.save', 'Save')}\n backHref=\"/backend/staff/my-leave-requests\"\n cancelHref=\"/backend/staff/my-leave-requests\"\n initialValues={initialValues}\n onSubmit={handleSubmit}\n allowMemberSelect={false}\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 <Leaf className=\"h-4 w-4\" />\n </Button>\n )}\n />\n ) : null}\n />\n ) : (\n <div className=\"rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <div className=\"font-medium text-foreground\">{t('staff.leaveRequests.detail.summary', 'Request details')}</div>\n <p>{memberLabel ? t('staff.leaveRequests.detail.member', 'Team member') + `: ${memberLabel}` : null}</p>\n <p>{t('staff.leaveRequests.detail.dates', 'Dates')}: {formatDateRange(record.startDate ?? record.start_date ?? null, record.endDate ?? record.end_date ?? null)}</p>\n {record.unavailabilityReasonValue || record.unavailability_reason_value ? (\n <p>{t('staff.leaveRequests.detail.reason', 'Reason')}: {record.unavailabilityReasonValue ?? record.unavailability_reason_value}</p>\n ) : null}\n {record.note ? <p>{t('staff.leaveRequests.detail.note', 'Note')}: {record.note}</p> : null}\n </div>\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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",
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 { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { 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 StaffMyLeaveRequestDetailPage({ 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\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 }\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 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 const dateSummary = formatDateRange(\n record?.startDate ?? record?.start_date ?? null,\n record?.endDate ?? record?.end_date ?? null,\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/my-leave-requests')\n }, [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 {record.decisionComment || record.decision_comment ? (\n <div className=\"text-sm text-muted-foreground\">\n <div className=\"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 </div>\n\n {status === 'pending' ? (\n <LeaveRequestForm\n title={t('staff.leaveRequests.form.editTitle', 'Leave request')}\n submitLabel={t('staff.leaveRequests.form.actions.save', 'Save')}\n backHref=\"/backend/staff/my-leave-requests\"\n cancelHref=\"/backend/staff/my-leave-requests\"\n initialValues={initialValues}\n onSubmit={handleSubmit}\n allowMemberSelect={false}\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 ) : (\n <div className=\"rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <div className=\"font-medium text-foreground\">{t('staff.leaveRequests.detail.summary', 'Request details')}</div>\n <p>{memberLabel ? t('staff.leaveRequests.detail.member', 'Team member') + `: ${memberLabel}` : null}</p>\n <p>{t('staff.leaveRequests.detail.dates', 'Dates')}: {formatDateRange(record.startDate ?? record.start_date ?? null, record.endDate ?? record.end_date ?? null)}</p>\n {record.unavailabilityReasonValue || record.unavailability_reason_value ? (\n <p>{t('staff.leaveRequests.detail.reason', 'Reason')}: {record.unavailabilityReasonValue ?? record.unavailability_reason_value}</p>\n ) : null}\n {record.note ? <p>{t('staff.leaveRequests.detail.note', 'Note')}: {record.note}</p> : null}\n </div>\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
  }