@open-mercato/core 0.4.6-develop-f7d3079656 → 0.4.6-develop-0861f05ea9

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 (107) hide show
  1. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +17 -154
  2. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +3 -3
  3. package/dist/modules/currencies/backend/exchange-rates/create/page.js +14 -152
  4. package/dist/modules/currencies/backend/exchange-rates/create/page.js.map +2 -2
  5. package/dist/modules/currencies/lib/exchangeRateFormConfig.js +167 -0
  6. package/dist/modules/currencies/lib/exchangeRateFormConfig.js.map +7 -0
  7. package/dist/modules/customers/api/dashboard/widgets/utils.js +1 -34
  8. package/dist/modules/customers/api/dashboard/widgets/utils.js.map +2 -2
  9. package/dist/modules/customers/commands/activities.js +3 -8
  10. package/dist/modules/customers/commands/activities.js.map +2 -2
  11. package/dist/modules/customers/commands/comments.js +2 -8
  12. package/dist/modules/customers/commands/comments.js.map +2 -2
  13. package/dist/modules/dashboards/lib/widgetScope.js +38 -0
  14. package/dist/modules/dashboards/lib/widgetScope.js.map +7 -0
  15. package/dist/modules/entities/lib/makeActivityRoute.js +265 -0
  16. package/dist/modules/entities/lib/makeActivityRoute.js.map +7 -0
  17. package/dist/modules/resources/api/activities.js +24 -232
  18. package/dist/modules/resources/api/activities.js.map +2 -2
  19. package/dist/modules/resources/commands/activities.js +3 -8
  20. package/dist/modules/resources/commands/activities.js.map +2 -2
  21. package/dist/modules/resources/commands/comments.js +2 -8
  22. package/dist/modules/resources/commands/comments.js.map +2 -2
  23. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +27 -182
  24. package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
  25. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +28 -183
  26. package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
  27. package/dist/modules/sales/api/order-line-statuses/route.js +15 -194
  28. package/dist/modules/sales/api/order-line-statuses/route.js.map +2 -2
  29. package/dist/modules/sales/api/order-lines/route.js +15 -281
  30. package/dist/modules/sales/api/order-lines/route.js.map +2 -2
  31. package/dist/modules/sales/api/order-statuses/route.js +15 -194
  32. package/dist/modules/sales/api/order-statuses/route.js.map +2 -2
  33. package/dist/modules/sales/api/payment-statuses/route.js +15 -194
  34. package/dist/modules/sales/api/payment-statuses/route.js.map +2 -2
  35. package/dist/modules/sales/api/quote-lines/route.js +15 -279
  36. package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
  37. package/dist/modules/sales/api/shipment-statuses/route.js +15 -194
  38. package/dist/modules/sales/api/shipment-statuses/route.js.map +2 -2
  39. package/dist/modules/sales/components/PaymentMethodsSettings.js +3 -84
  40. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  41. package/dist/modules/sales/components/ProviderFieldInput.js +86 -0
  42. package/dist/modules/sales/components/ProviderFieldInput.js.map +7 -0
  43. package/dist/modules/sales/components/ShippingMethodsSettings.js +3 -82
  44. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  45. package/dist/modules/sales/lib/makeSalesLineRoute.js +308 -0
  46. package/dist/modules/sales/lib/makeSalesLineRoute.js.map +7 -0
  47. package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +206 -0
  48. package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +7 -0
  49. package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +178 -0
  50. package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +7 -0
  51. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +1 -39
  52. package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
  53. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +1 -39
  54. package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
  55. package/dist/modules/sales/widgets/dashboard/shared.js +46 -0
  56. package/dist/modules/sales/widgets/dashboard/shared.js.map +7 -0
  57. package/dist/modules/staff/api/activities.js +24 -232
  58. package/dist/modules/staff/api/activities.js.map +2 -2
  59. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +14 -34
  60. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  61. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +15 -34
  62. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  63. package/dist/modules/staff/commands/activities.js +3 -8
  64. package/dist/modules/staff/commands/activities.js.map +2 -2
  65. package/dist/modules/staff/commands/comments.js +2 -8
  66. package/dist/modules/staff/commands/comments.js.map +2 -2
  67. package/dist/modules/staff/lib/leaveRequestHelpers.js +41 -0
  68. package/dist/modules/staff/lib/leaveRequestHelpers.js.map +7 -0
  69. package/package.json +2 -2
  70. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +20 -180
  71. package/src/modules/currencies/backend/exchange-rates/create/page.tsx +16 -175
  72. package/src/modules/currencies/lib/exchangeRateFormConfig.ts +200 -0
  73. package/src/modules/customers/api/dashboard/widgets/utils.ts +1 -53
  74. package/src/modules/customers/commands/activities.ts +2 -8
  75. package/src/modules/customers/commands/comments.ts +2 -8
  76. package/src/modules/dashboards/i18n/de.json +3 -0
  77. package/src/modules/dashboards/i18n/en.json +3 -0
  78. package/src/modules/dashboards/i18n/es.json +3 -0
  79. package/src/modules/dashboards/i18n/pl.json +3 -0
  80. package/src/modules/dashboards/lib/widgetScope.ts +53 -0
  81. package/src/modules/entities/lib/makeActivityRoute.ts +327 -0
  82. package/src/modules/resources/api/activities.ts +25 -269
  83. package/src/modules/resources/commands/activities.ts +2 -7
  84. package/src/modules/resources/commands/comments.ts +2 -8
  85. package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +29 -244
  86. package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +30 -245
  87. package/src/modules/sales/api/order-line-statuses/route.ts +16 -209
  88. package/src/modules/sales/api/order-lines/route.ts +16 -300
  89. package/src/modules/sales/api/order-statuses/route.ts +16 -209
  90. package/src/modules/sales/api/payment-statuses/route.ts +16 -209
  91. package/src/modules/sales/api/quote-lines/route.ts +16 -298
  92. package/src/modules/sales/api/shipment-statuses/route.ts +16 -209
  93. package/src/modules/sales/components/PaymentMethodsSettings.tsx +3 -88
  94. package/src/modules/sales/components/ProviderFieldInput.tsx +85 -0
  95. package/src/modules/sales/components/ShippingMethodsSettings.tsx +3 -86
  96. package/src/modules/sales/lib/makeSalesLineRoute.ts +345 -0
  97. package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +229 -0
  98. package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +247 -0
  99. package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +7 -50
  100. package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +7 -49
  101. package/src/modules/sales/widgets/dashboard/shared.ts +44 -0
  102. package/src/modules/staff/api/activities.ts +25 -269
  103. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +15 -69
  104. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +16 -65
  105. package/src/modules/staff/commands/activities.ts +2 -7
  106. package/src/modules/staff/commands/comments.ts +2 -8
  107. package/src/modules/staff/lib/leaveRequestHelpers.ts +78 -0
@@ -1,243 +1,35 @@
1
- import { z } from "zod";
2
- import { makeCrudRoute } from "@open-mercato/shared/lib/crud/factory";
3
- import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
4
- import { resolveCrudRecordId, parseScopedCommandInput } from "@open-mercato/shared/lib/api/scoped";
5
- import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
1
+ import { makeActivityRoute } from "@open-mercato/core/modules/entities/lib/makeActivityRoute";
6
2
  import { StaffTeamMemberActivity } from "../data/entities.js";
7
3
  import {
8
4
  staffTeamMemberActivityCreateSchema,
9
5
  staffTeamMemberActivityUpdateSchema
10
6
  } from "../data/validators.js";
11
- import { User } from "@open-mercato/core/modules/auth/data/entities";
12
7
  import { E } from "../../../generated/entities.ids.generated.js";
13
- import { createStaffCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from "./openapi.js";
14
- const rawBodySchema = z.object({}).passthrough();
15
- const listSchema = z.object({
16
- page: z.coerce.number().min(1).default(1),
17
- pageSize: z.coerce.number().min(1).max(100).default(50),
18
- entityId: z.string().uuid().optional(),
19
- sortField: z.string().optional(),
20
- sortDir: z.enum(["asc", "desc"]).optional()
21
- }).passthrough();
22
- const routeMetadata = {
23
- GET: { requireAuth: true, requireFeatures: ["staff.view"] },
24
- POST: { requireAuth: true, requireFeatures: ["staff.manage_team"] },
25
- PUT: { requireAuth: true, requireFeatures: ["staff.manage_team"] },
26
- DELETE: { requireAuth: true, requireFeatures: ["staff.manage_team"] }
27
- };
28
- const metadata = routeMetadata;
29
- const crud = makeCrudRoute({
30
- metadata: routeMetadata,
31
- orm: {
32
- entity: StaffTeamMemberActivity,
33
- idField: "id",
34
- orgField: "organizationId",
35
- tenantField: "tenantId"
36
- },
37
- indexer: {
38
- entityType: E.staff.staff_team_member_activity
39
- },
40
- list: {
41
- schema: listSchema,
42
- entityId: E.staff.staff_team_member_activity,
43
- fields: [
44
- "id",
45
- "member_id",
46
- "activity_type",
47
- "subject",
48
- "body",
49
- "occurred_at",
50
- "author_user_id",
51
- "appearance_icon",
52
- "appearance_color",
53
- "organization_id",
54
- "tenant_id",
55
- "created_at",
56
- "updated_at"
57
- ],
58
- decorateCustomFields: {
59
- entityIds: E.staff.staff_team_member_activity
60
- },
61
- sortFieldMap: {
62
- occurredAt: "occurred_at",
63
- createdAt: "created_at",
64
- updatedAt: "updated_at"
65
- },
66
- buildFilters: async (query) => {
67
- const filters = {};
68
- if (query.entityId) filters.member_id = { $eq: query.entityId };
69
- return filters;
70
- },
71
- transformItem: (item) => {
72
- const record = item ?? {};
73
- const toIsoString = (value) => {
74
- if (value == null) return null;
75
- if (value instanceof Date) return value.toISOString();
76
- if (typeof value === "string") {
77
- const trimmed = value.trim();
78
- if (!trimmed.length) return null;
79
- const date = new Date(trimmed);
80
- return Number.isNaN(date.getTime()) ? trimmed : date.toISOString();
81
- }
82
- return null;
83
- };
84
- const readString = (value) => typeof value === "string" ? value : null;
85
- const idValue = readString(record.id) ?? (record.id != null ? String(record.id) : "");
86
- const memberId = readString(record["member_id"]) ?? readString(record["memberId"]) ?? null;
87
- const activityType = readString(record["activity_type"]) ?? readString(record["activityType"]) ?? "";
88
- const subject = readString(record.subject) ?? (record.subject == null ? null : String(record.subject));
89
- const body = readString(record.body) ?? (record.body == null ? null : String(record.body));
90
- const authorUserId = readString(record["author_user_id"]) ?? readString(record["authorUserId"]) ?? null;
91
- const appearanceIconRaw = readString(record["appearance_icon"]) ?? readString(record["appearanceIcon"]);
92
- const appearanceColorRaw = readString(record["appearance_color"]) ?? readString(record["appearanceColor"]);
93
- const organizationId = readString(record["organization_id"]) ?? readString(record["organizationId"]);
94
- const tenantId = readString(record["tenant_id"]) ?? readString(record["tenantId"]);
95
- const output = {
96
- id: idValue,
97
- entityId: memberId,
98
- memberId,
99
- activityType,
100
- subject,
101
- body,
102
- occurredAt: toIsoString(record["occurred_at"] ?? record["occurredAt"]),
103
- createdAt: toIsoString(record["created_at"] ?? record["createdAt"]),
104
- authorUserId,
105
- organizationId,
106
- tenantId,
107
- appearanceIcon: appearanceIconRaw && appearanceIconRaw.trim().length ? appearanceIconRaw : null,
108
- appearanceColor: appearanceColorRaw && appearanceColorRaw.trim().length ? appearanceColorRaw : null,
109
- customFields: Array.isArray(record.customFields) ? record.customFields : void 0,
110
- customValues: record.customValues ?? void 0
111
- };
112
- for (const [key, value] of Object.entries(record)) {
113
- if (key.startsWith("cf_") || key.startsWith("cf:")) {
114
- output[key] = value;
115
- }
116
- }
117
- return output;
118
- }
119
- },
120
- actions: {
121
- create: {
122
- commandId: "staff.team-member-activities.create",
123
- schema: rawBodySchema,
124
- mapInput: async ({ raw, ctx }) => {
125
- const { translate } = await resolveTranslations();
126
- return parseScopedCommandInput(staffTeamMemberActivityCreateSchema, raw ?? {}, ctx, translate);
127
- },
128
- response: ({ result }) => ({
129
- id: result?.activityId ?? result?.id ?? null,
130
- authorUserId: result?.authorUserId ?? null
131
- }),
132
- status: 201
133
- },
134
- update: {
135
- commandId: "staff.team-member-activities.update",
136
- schema: rawBodySchema,
137
- mapInput: async ({ raw, ctx }) => {
138
- const { translate } = await resolveTranslations();
139
- return parseScopedCommandInput(staffTeamMemberActivityUpdateSchema, raw ?? {}, ctx, translate);
140
- },
141
- response: () => ({ ok: true })
142
- },
143
- delete: {
144
- commandId: "staff.team-member-activities.delete",
145
- schema: rawBodySchema,
146
- mapInput: async ({ parsed, ctx }) => {
147
- const { translate } = await resolveTranslations();
148
- const id = resolveCrudRecordId(parsed, ctx, translate);
149
- return { id };
150
- },
151
- response: () => ({ ok: true })
152
- }
153
- },
154
- hooks: {
155
- afterList: async (payload, ctx) => {
156
- const items = Array.isArray(payload.items) ? payload.items : [];
157
- if (!items.length) return;
158
- const userIds = /* @__PURE__ */ new Set();
159
- items.forEach((item) => {
160
- if (!item || typeof item !== "object") return;
161
- const record = item;
162
- const userId = typeof record.author_user_id === "string" ? record.author_user_id : typeof record.authorUserId === "string" ? record.authorUserId : null;
163
- if (userId) userIds.add(userId);
164
- });
165
- if (!userIds.size) return;
166
- try {
167
- const em = ctx.container.resolve("em").fork();
168
- const users = await findWithDecryption(
169
- em,
170
- User,
171
- { id: { $in: Array.from(userIds) } },
172
- void 0,
173
- { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null }
174
- );
175
- const map = /* @__PURE__ */ new Map();
176
- users.forEach((user) => {
177
- const name = typeof user.name === "string" && user.name.trim().length ? user.name.trim() : null;
178
- map.set(user.id, { name, email: user.email ?? null });
179
- });
180
- items.forEach((item) => {
181
- if (!item || typeof item !== "object") return;
182
- const record = item;
183
- const userId = typeof record.author_user_id === "string" ? record.author_user_id : typeof record.authorUserId === "string" ? record.authorUserId : null;
184
- if (!userId) return;
185
- const meta = map.get(userId);
186
- if (!meta) return;
187
- record.authorName = meta.name;
188
- record.authorEmail = meta.email;
189
- if (!("author_name" in record)) record.author_name = meta.name;
190
- if (!("author_email" in record)) record.author_email = meta.email;
191
- });
192
- } catch (err) {
193
- console.warn("[staff.activities] failed to enrich author metadata", err);
194
- }
195
- }
196
- }
197
- });
198
- const GET = crud.GET;
199
- const POST = crud.POST;
200
- const PUT = crud.PUT;
201
- const DELETE = crud.DELETE;
202
- const activityListItemSchema = z.object({
203
- id: z.string().uuid(),
204
- member_id: z.string().uuid().nullable().optional(),
205
- activity_type: z.string().nullable().optional(),
206
- subject: z.string().nullable().optional(),
207
- body: z.string().nullable().optional(),
208
- occurred_at: z.string().nullable().optional(),
209
- author_user_id: z.string().uuid().nullable(),
210
- appearance_icon: z.string().nullable().optional(),
211
- appearance_color: z.string().nullable().optional(),
212
- organization_id: z.string().uuid().nullable().optional(),
213
- tenant_id: z.string().uuid().nullable().optional(),
214
- created_at: z.string().nullable(),
215
- updated_at: z.string().nullable().optional()
216
- }).passthrough();
217
- const activityCreateResponseSchema = z.object({
218
- id: z.string().uuid().nullable(),
219
- authorUserId: z.string().uuid().nullable()
220
- });
221
- const openApi = createStaffCrudOpenApi({
222
- resourceName: "TeamMemberActivity",
223
- querySchema: listSchema,
224
- listResponseSchema: createPagedListResponseSchema(activityListItemSchema),
225
- create: {
226
- schema: staffTeamMemberActivityCreateSchema,
227
- responseSchema: activityCreateResponseSchema,
228
- description: "Adds an activity to a team member timeline."
229
- },
230
- update: {
231
- schema: staffTeamMemberActivityUpdateSchema,
232
- responseSchema: defaultOkResponseSchema,
233
- description: "Updates a team member activity."
234
- },
235
- del: {
236
- schema: z.object({ id: z.string().uuid() }),
237
- responseSchema: defaultOkResponseSchema,
238
- description: "Deletes a team member activity."
8
+ import { createStaffCrudOpenApi } from "./openapi.js";
9
+ const route = makeActivityRoute({
10
+ entity: StaffTeamMemberActivity,
11
+ entityId: E.staff.staff_team_member_activity,
12
+ parentFkColumn: "member_id",
13
+ parentFkParam: "memberId",
14
+ features: { view: "staff.view", manage: "staff.manage_team" },
15
+ createSchema: staffTeamMemberActivityCreateSchema,
16
+ updateSchema: staffTeamMemberActivityUpdateSchema,
17
+ commandPrefix: "staff.team-member-activities",
18
+ logPrefix: "[staff.activities]",
19
+ openApiFactory: createStaffCrudOpenApi,
20
+ openApi: {
21
+ resourceName: "TeamMemberActivity",
22
+ createDescription: "Adds an activity to a team member timeline.",
23
+ updateDescription: "Updates a team member activity.",
24
+ deleteDescription: "Deletes a team member activity."
239
25
  }
240
26
  });
27
+ const metadata = route.metadata;
28
+ const openApi = route.openApi;
29
+ const GET = route.GET;
30
+ const POST = route.POST;
31
+ const PUT = route.PUT;
32
+ const DELETE = route.DELETE;
241
33
  export {
242
34
  DELETE,
243
35
  GET,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/staff/api/activities.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { resolveCrudRecordId, parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { StaffTeamMemberActivity } from '../data/entities'\nimport {\n staffTeamMemberActivityCreateSchema,\n staffTeamMemberActivityUpdateSchema,\n} from '../data/validators'\nimport { User } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { createStaffCrudOpenApi, createPagedListResponseSchema, defaultOkResponseSchema } from './openapi'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n entityId: z.string().uuid().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['staff.view'] },\n POST: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n PUT: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n DELETE: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: StaffTeamMemberActivity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n },\n indexer: {\n entityType: E.staff.staff_team_member_activity,\n },\n list: {\n schema: listSchema,\n entityId: E.staff.staff_team_member_activity,\n fields: [\n 'id',\n 'member_id',\n 'activity_type',\n 'subject',\n 'body',\n 'occurred_at',\n 'author_user_id',\n 'appearance_icon',\n 'appearance_color',\n 'organization_id',\n 'tenant_id',\n 'created_at',\n 'updated_at',\n ],\n decorateCustomFields: {\n entityIds: E.staff.staff_team_member_activity,\n },\n sortFieldMap: {\n occurredAt: 'occurred_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n if (query.entityId) filters.member_id = { $eq: query.entityId }\n return filters\n },\n transformItem: (item: Record<string, unknown>) => {\n const record = (item ?? {}) as Record<string, unknown>\n const toIsoString = (value: unknown): string | null => {\n if (value == null) return null\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed.length) return null\n const date = new Date(trimmed)\n return Number.isNaN(date.getTime()) ? trimmed : date.toISOString()\n }\n return null\n }\n const readString = (value: unknown): string | null => (typeof value === 'string' ? value : null)\n const idValue = readString(record.id) ?? (record.id != null ? String(record.id) : '')\n const memberId = readString(record['member_id']) ?? readString(record['memberId']) ?? null\n const activityType =\n readString(record['activity_type']) ??\n readString(record['activityType']) ??\n ''\n const subject =\n readString(record.subject) ??\n (record.subject == null ? null : String(record.subject))\n const body =\n readString(record.body) ??\n (record.body == null ? null : String(record.body))\n const authorUserId =\n readString(record['author_user_id']) ?? readString(record['authorUserId']) ?? null\n const appearanceIconRaw =\n readString(record['appearance_icon']) ?? readString(record['appearanceIcon'])\n const appearanceColorRaw =\n readString(record['appearance_color']) ?? readString(record['appearanceColor'])\n const organizationId =\n readString(record['organization_id']) ?? readString(record['organizationId'])\n const tenantId =\n readString(record['tenant_id']) ?? readString(record['tenantId'])\n const output: Record<string, unknown> = {\n id: idValue,\n entityId: memberId,\n memberId,\n activityType,\n subject,\n body,\n occurredAt: toIsoString(record['occurred_at'] ?? record['occurredAt']),\n createdAt: toIsoString(record['created_at'] ?? record['createdAt']),\n authorUserId,\n organizationId,\n tenantId,\n appearanceIcon: appearanceIconRaw && appearanceIconRaw.trim().length ? appearanceIconRaw : null,\n appearanceColor: appearanceColorRaw && appearanceColorRaw.trim().length ? appearanceColorRaw : null,\n customFields: Array.isArray(record.customFields) ? record.customFields : undefined,\n customValues: record.customValues ?? undefined,\n }\n for (const [key, value] of Object.entries(record)) {\n if (key.startsWith('cf_') || key.startsWith('cf:')) {\n output[key] = value\n }\n }\n return output\n },\n },\n actions: {\n create: {\n commandId: 'staff.team-member-activities.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(staffTeamMemberActivityCreateSchema, raw ?? {}, ctx, translate)\n },\n response: ({ result }) => ({\n id: result?.activityId ?? result?.id ?? null,\n authorUserId: result?.authorUserId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: 'staff.team-member-activities.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(staffTeamMemberActivityUpdateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'staff.team-member-activities.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id = resolveCrudRecordId(parsed, ctx, translate)\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload.items) ? payload.items : []\n if (!items.length) return\n const userIds = new Set<string>()\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const record = item as Record<string, unknown>\n const userId =\n typeof record.author_user_id === 'string'\n ? record.author_user_id\n : typeof record.authorUserId === 'string'\n ? record.authorUserId\n : null\n if (userId) userIds.add(userId)\n })\n if (!userIds.size) return\n try {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const users = await findWithDecryption(\n em,\n User,\n { id: { $in: Array.from(userIds) } },\n undefined,\n { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null },\n )\n const map = new Map<string, { name: string | null; email: string | null }>()\n users.forEach((user) => {\n const name = typeof user.name === 'string' && user.name.trim().length\n ? user.name.trim()\n : null\n map.set(user.id, { name, email: user.email ?? null })\n })\n items.forEach((item: unknown) => {\n if (!item || typeof item !== 'object') return\n const record = item as Record<string, unknown>\n const userId =\n typeof record.author_user_id === 'string'\n ? record.author_user_id\n : typeof record.authorUserId === 'string'\n ? record.authorUserId\n : null\n if (!userId) return\n const meta = map.get(userId)\n if (!meta) return\n record.authorName = meta.name\n record.authorEmail = meta.email\n if (!('author_name' in record)) record.author_name = meta.name\n if (!('author_email' in record)) record.author_email = meta.email\n })\n } catch (err) {\n console.warn('[staff.activities] failed to enrich author metadata', err)\n }\n },\n },\n})\n\nexport const GET = crud.GET\nexport const POST = crud.POST\nexport const PUT = crud.PUT\nexport const DELETE = crud.DELETE\n\nconst activityListItemSchema = z\n .object({\n id: z.string().uuid(),\n member_id: z.string().uuid().nullable().optional(),\n activity_type: z.string().nullable().optional(),\n subject: z.string().nullable().optional(),\n body: z.string().nullable().optional(),\n occurred_at: z.string().nullable().optional(),\n author_user_id: z.string().uuid().nullable(),\n appearance_icon: z.string().nullable().optional(),\n appearance_color: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable(),\n updated_at: z.string().nullable().optional(),\n })\n .passthrough()\n\nconst activityCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n authorUserId: z.string().uuid().nullable(),\n})\n\nexport const openApi = createStaffCrudOpenApi({\n resourceName: 'TeamMemberActivity',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(activityListItemSchema),\n create: {\n schema: staffTeamMemberActivityCreateSchema,\n responseSchema: activityCreateResponseSchema,\n description: 'Adds an activity to a team member timeline.',\n },\n update: {\n schema: staffTeamMemberActivityUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates a team member activity.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a team member activity.',\n },\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { makeActivityRoute } from '@open-mercato/core/modules/entities/lib/makeActivityRoute'\nimport { StaffTeamMemberActivity } from '../data/entities'\nimport {\n staffTeamMemberActivityCreateSchema,\n staffTeamMemberActivityUpdateSchema,\n} from '../data/validators'\nimport { E } from '#generated/entities.ids.generated'\nimport { createStaffCrudOpenApi } from './openapi'\n\nconst route = makeActivityRoute({\n entity: StaffTeamMemberActivity,\n entityId: E.staff.staff_team_member_activity,\n parentFkColumn: 'member_id',\n parentFkParam: 'memberId',\n features: { view: 'staff.view', manage: 'staff.manage_team' },\n createSchema: staffTeamMemberActivityCreateSchema,\n updateSchema: staffTeamMemberActivityUpdateSchema,\n commandPrefix: 'staff.team-member-activities',\n logPrefix: '[staff.activities]',\n openApiFactory: createStaffCrudOpenApi,\n openApi: {\n resourceName: 'TeamMemberActivity',\n createDescription: 'Adds an activity to a team member timeline.',\n updateDescription: 'Updates a team member activity.',\n deleteDescription: 'Deletes a team member activity.',\n },\n})\n\nexport const metadata = route.metadata\nexport const openApi = route.openApi\nexport const GET = route.GET\nexport const POST = route.POST\nexport const PUT = route.PUT\nexport const DELETE = route.DELETE\n"],
5
+ "mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS;AAClB,SAAS,8BAA8B;AAEvC,MAAM,QAAQ,kBAAkB;AAAA,EAC9B,QAAQ;AAAA,EACR,UAAU,EAAE,MAAM;AAAA,EAClB,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,UAAU,EAAE,MAAM,cAAc,QAAQ,oBAAoB;AAAA,EAC5D,cAAc;AAAA,EACd,cAAc;AAAA,EACd,eAAe;AAAA,EACf,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,SAAS;AAAA,IACP,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,EACrB;AACF,CAAC;AAEM,MAAM,WAAW,MAAM;AACvB,MAAM,UAAU,MAAM;AACtB,MAAM,MAAM,MAAM;AAClB,MAAM,OAAO,MAAM;AACnB,MAAM,MAAM,MAAM;AAClB,MAAM,SAAS,MAAM;",
6
6
  "names": []
7
7
  }
@@ -13,6 +13,7 @@ import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
13
13
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
14
14
  import { useT } from "@open-mercato/shared/lib/i18n/context";
15
15
  import { LeaveRequestForm, buildLeaveRequestPayload } from "@open-mercato/core/modules/staff/components/LeaveRequestForm";
16
+ import { normalizeLeaveRequest, resolveStatusVariant, formatDateLabel, formatDateRange } from "../../../../lib/leaveRequestHelpers.js";
16
17
  function StaffLeaveRequestDetailPage({ params }) {
17
18
  const id = params?.id;
18
19
  const t = useT();
@@ -42,10 +43,9 @@ function StaffLeaveRequestDetailPage({ params }) {
42
43
  const entry = Array.isArray(payload.items) ? payload.items[0] : null;
43
44
  if (!entry) throw new Error(t("staff.leaveRequests.errors.notFound", "Leave request not found."));
44
45
  if (!cancelled) {
45
- setRecord(entry);
46
- setDecisionComment(
47
- typeof entry.decisionComment === "string" ? entry.decisionComment : typeof entry.decision_comment === "string" ? entry.decision_comment : ""
48
- );
46
+ const normalized = normalizeLeaveRequest(entry);
47
+ setRecord(normalized);
48
+ setDecisionComment(normalized.decisionComment ?? "");
49
49
  }
50
50
  } catch (err) {
51
51
  if (!cancelled) {
@@ -64,19 +64,16 @@ function StaffLeaveRequestDetailPage({ params }) {
64
64
  }, [id, t]);
65
65
  const status = record?.status ?? "pending";
66
66
  const memberLabel = record?.member?.displayName ?? null;
67
- const dateSummary = formatDateRange(
68
- record?.startDate ?? record?.start_date ?? null,
69
- record?.endDate ?? record?.end_date ?? null
70
- );
67
+ const dateSummary = formatDateRange(record?.startDate, record?.endDate);
71
68
  const initialValues = React.useMemo(() => ({
72
69
  id: record?.id,
73
- memberId: record?.memberId ?? record?.member_id ?? null,
70
+ memberId: record?.memberId ?? null,
74
71
  memberLabel,
75
- startDate: record?.startDate ?? record?.start_date ?? null,
76
- endDate: record?.endDate ?? record?.end_date ?? null,
72
+ startDate: record?.startDate ?? null,
73
+ endDate: record?.endDate ?? null,
77
74
  timezone: record?.timezone ?? null,
78
- unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? record?.unavailability_reason_entry_id ?? null,
79
- unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,
75
+ unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? null,
76
+ unavailabilityReasonValue: record?.unavailabilityReasonValue ?? null,
80
77
  note: record?.note ?? null
81
78
  }), [record, memberLabel]);
82
79
  const handleSubmit = React.useCallback(async (values) => {
@@ -112,10 +109,10 @@ function StaffLeaveRequestDetailPage({ params }) {
112
109
  /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-2 rounded-lg border bg-card p-4", children: [
113
110
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
114
111
  /* @__PURE__ */ jsx(Badge, { variant: resolveStatusVariant(status), children: t(`staff.leaveRequests.status.${status}`, status) }),
115
- record.decided_at || record.decidedAt ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
112
+ record.decidedAt ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
116
113
  t("staff.leaveRequests.decision.at", "Decision at"),
117
114
  " ",
118
- formatDateLabel(record.decidedAt ?? record.decided_at ?? null)
115
+ formatDateLabel(record.decidedAt)
119
116
  ] }) : null
120
117
  ] }),
121
118
  memberLabel ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
@@ -144,9 +141,9 @@ function StaffLeaveRequestDetailPage({ params }) {
144
141
  /* @__PURE__ */ jsx(Button, { onClick: () => handleDecision("accept"), children: t("staff.leaveRequests.actions.accept", "Approve") }),
145
142
  /* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: () => handleDecision("reject"), children: t("staff.leaveRequests.actions.reject", "Reject") })
146
143
  ] })
147
- ] }) : record.decisionComment || record.decision_comment ? /* @__PURE__ */ jsxs("div", { className: "mb-6 rounded-lg border bg-card p-4 text-sm text-muted-foreground", children: [
144
+ ] }) : record.decisionComment ? /* @__PURE__ */ jsxs("div", { className: "mb-6 rounded-lg border bg-card p-4 text-sm text-muted-foreground", children: [
148
145
  /* @__PURE__ */ jsx("div", { className: "mb-1 font-medium text-foreground", children: t("staff.leaveRequests.decision.comment", "Decision comment") }),
149
- /* @__PURE__ */ jsx("p", { children: record.decisionComment ?? record.decision_comment })
146
+ /* @__PURE__ */ jsx("p", { children: record.decisionComment })
150
147
  ] }) : null,
151
148
  /* @__PURE__ */ jsx(
152
149
  LeaveRequestForm,
@@ -194,23 +191,6 @@ function StaffLeaveRequestDetailPage({ params }) {
194
191
  )
195
192
  ] }) });
196
193
  }
197
- function resolveStatusVariant(status) {
198
- if (status === "approved") return "default";
199
- if (status === "rejected") return "destructive";
200
- return "secondary";
201
- }
202
- function formatDateLabel(value) {
203
- if (!value) return "";
204
- const date = new Date(value);
205
- if (Number.isNaN(date.getTime())) return value;
206
- return date.toLocaleDateString();
207
- }
208
- function formatDateRange(start, end) {
209
- const startLabel = formatDateLabel(start);
210
- const endLabel = formatDateLabel(end);
211
- if (startLabel && endLabel) return `${startLabel} -> ${endLabel}`;
212
- return startLabel || endLabel || "-";
213
- }
214
194
  export {
215
195
  StaffLeaveRequestDetailPage as default
216
196
  };
@@ -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\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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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\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'\nimport { type LeaveRequestRecord, type LeaveRequestsResponse, type NormalizedLeaveRequest, normalizeLeaveRequest, resolveStatusVariant, formatDateLabel, formatDateRange } from '../../../../lib/leaveRequestHelpers'\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<NormalizedLeaveRequest | 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 const normalized = normalizeLeaveRequest(entry)\n setRecord(normalized)\n setDecisionComment(normalized.decisionComment ?? '')\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(record?.startDate, record?.endDate)\n const initialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n id: record?.id,\n memberId: record?.memberId ?? null,\n memberLabel,\n startDate: record?.startDate ?? null,\n endDate: record?.endDate ?? null,\n timezone: record?.timezone ?? null,\n unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? null,\n unavailabilityReasonValue: record?.unavailabilityReasonValue ?? 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.decidedAt ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.decision.at', 'Decision at')} {formatDateLabel(record.decidedAt)}\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 ? (\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}</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"],
5
+ "mappings": "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",
6
6
  "names": ["params"]
7
7
  }
@@ -11,6 +11,7 @@ import { updateCrud } from "@open-mercato/ui/backend/utils/crud";
11
11
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
12
12
  import { useT } from "@open-mercato/shared/lib/i18n/context";
13
13
  import { LeaveRequestForm, buildLeaveRequestPayload } from "@open-mercato/core/modules/staff/components/LeaveRequestForm";
14
+ import { normalizeLeaveRequest, resolveStatusVariant, formatDateLabel, formatDateRange } from "../../../../lib/leaveRequestHelpers.js";
14
15
  function StaffMyLeaveRequestDetailPage({ params }) {
15
16
  const id = params?.id;
16
17
  const t = useT();
@@ -39,7 +40,7 @@ function StaffMyLeaveRequestDetailPage({ params }) {
39
40
  const entry = Array.isArray(payload.items) ? payload.items[0] : null;
40
41
  if (!entry) throw new Error(t("staff.leaveRequests.errors.notFound", "Leave request not found."));
41
42
  if (!cancelled) {
42
- setRecord(entry);
43
+ setRecord(normalizeLeaveRequest(entry));
43
44
  }
44
45
  } catch (err) {
45
46
  if (!cancelled) {
@@ -60,19 +61,16 @@ function StaffMyLeaveRequestDetailPage({ params }) {
60
61
  const memberLabel = record?.member?.displayName ?? null;
61
62
  const initialValues = React.useMemo(() => ({
62
63
  id: record?.id,
63
- memberId: record?.memberId ?? record?.member_id ?? null,
64
+ memberId: record?.memberId ?? null,
64
65
  memberLabel,
65
- startDate: record?.startDate ?? record?.start_date ?? null,
66
- endDate: record?.endDate ?? record?.end_date ?? null,
66
+ startDate: record?.startDate ?? null,
67
+ endDate: record?.endDate ?? null,
67
68
  timezone: record?.timezone ?? null,
68
- unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? record?.unavailability_reason_entry_id ?? null,
69
- unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,
69
+ unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? null,
70
+ unavailabilityReasonValue: record?.unavailabilityReasonValue ?? null,
70
71
  note: record?.note ?? null
71
72
  }), [record, memberLabel]);
72
- const dateSummary = formatDateRange(
73
- record?.startDate ?? record?.start_date ?? null,
74
- record?.endDate ?? record?.end_date ?? null
75
- );
73
+ const dateSummary = formatDateRange(record?.startDate, record?.endDate);
76
74
  const handleSubmit = React.useCallback(async (values) => {
77
75
  if (!record?.id) return;
78
76
  const payload = buildLeaveRequestPayload(values, { id: record.id });
@@ -92,15 +90,15 @@ function StaffMyLeaveRequestDetailPage({ params }) {
92
90
  /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-2 rounded-lg border bg-card p-4", children: [
93
91
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
94
92
  /* @__PURE__ */ jsx(Badge, { variant: resolveStatusVariant(status), children: t(`staff.leaveRequests.status.${status}`, status) }),
95
- record.decided_at || record.decidedAt ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
93
+ record.decidedAt ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
96
94
  t("staff.leaveRequests.decision.at", "Decision at"),
97
95
  " ",
98
- formatDateLabel(record.decidedAt ?? record.decided_at ?? null)
96
+ formatDateLabel(record.decidedAt)
99
97
  ] }) : null
100
98
  ] }),
101
- record.decisionComment || record.decision_comment ? /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
99
+ record.decisionComment ? /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
102
100
  /* @__PURE__ */ jsx("div", { className: "font-medium text-foreground", children: t("staff.leaveRequests.decision.comment", "Decision comment") }),
103
- /* @__PURE__ */ jsx("p", { children: record.decisionComment ?? record.decision_comment })
101
+ /* @__PURE__ */ jsx("p", { children: record.decisionComment })
104
102
  ] }) : null
105
103
  ] }),
106
104
  status === "pending" ? /* @__PURE__ */ jsx(
@@ -152,12 +150,12 @@ function StaffMyLeaveRequestDetailPage({ params }) {
152
150
  /* @__PURE__ */ jsxs("p", { children: [
153
151
  t("staff.leaveRequests.detail.dates", "Dates"),
154
152
  ": ",
155
- formatDateRange(record.startDate ?? record.start_date ?? null, record.endDate ?? record.end_date ?? null)
153
+ formatDateRange(record.startDate, record.endDate)
156
154
  ] }),
157
- record.unavailabilityReasonValue || record.unavailability_reason_value ? /* @__PURE__ */ jsxs("p", { children: [
155
+ record.unavailabilityReasonValue ? /* @__PURE__ */ jsxs("p", { children: [
158
156
  t("staff.leaveRequests.detail.reason", "Reason"),
159
157
  ": ",
160
- record.unavailabilityReasonValue ?? record.unavailability_reason_value
158
+ record.unavailabilityReasonValue
161
159
  ] }) : null,
162
160
  record.note ? /* @__PURE__ */ jsxs("p", { children: [
163
161
  t("staff.leaveRequests.detail.note", "Note"),
@@ -167,23 +165,6 @@ function StaffMyLeaveRequestDetailPage({ params }) {
167
165
  ] })
168
166
  ] }) });
169
167
  }
170
- function resolveStatusVariant(status) {
171
- if (status === "approved") return "default";
172
- if (status === "rejected") return "destructive";
173
- return "secondary";
174
- }
175
- function formatDateLabel(value) {
176
- if (!value) return "";
177
- const date = new Date(value);
178
- if (Number.isNaN(date.getTime())) return value;
179
- return date.toLocaleDateString();
180
- }
181
- function formatDateRange(start, end) {
182
- const startLabel = formatDateLabel(start);
183
- const endLabel = formatDateLabel(end);
184
- if (startLabel && endLabel) return `${startLabel} -> ${endLabel}`;
185
- return startLabel || endLabel || "-";
186
- }
187
168
  export {
188
169
  StaffMyLeaveRequestDetailPage as default
189
170
  };