@open-mercato/core 0.4.6-develop-9ff1d4a9a2 → 0.4.6-develop-219dae16c5

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
@@ -0,0 +1,38 @@
1
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
2
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
3
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
4
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
5
+ async function resolveWidgetScope(req, translate, overrides) {
6
+ const auth = await getAuthFromRequest(req);
7
+ if (!auth) {
8
+ throw new CrudHttpError(401, { error: translate("dashboards.errors.unauthorized", "Unauthorized") });
9
+ }
10
+ const container = await createRequestContainer();
11
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
12
+ const tenantId = overrides?.tenantId ?? auth.tenantId ?? null;
13
+ if (!tenantId) {
14
+ throw new CrudHttpError(400, { error: translate("dashboards.errors.tenant_required", "Tenant context is required") });
15
+ }
16
+ const organizationIds = (() => {
17
+ if (overrides?.organizationId) return [overrides.organizationId];
18
+ if (scope?.selectedId) return [scope.selectedId];
19
+ if (Array.isArray(scope?.filterIds) && scope.filterIds.length > 0) return scope.filterIds;
20
+ if (scope?.allowedIds === null) return null;
21
+ if (auth.orgId) return [auth.orgId];
22
+ return [];
23
+ })();
24
+ if (organizationIds !== null && organizationIds.length === 0) {
25
+ throw new CrudHttpError(400, { error: translate("dashboards.errors.organization_required", "Organization context is required") });
26
+ }
27
+ const em = container.resolve("em");
28
+ return {
29
+ container,
30
+ em,
31
+ tenantId,
32
+ organizationIds
33
+ };
34
+ }
35
+ export {
36
+ resolveWidgetScope
37
+ };
38
+ //# sourceMappingURL=widgetScope.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/dashboards/lib/widgetScope.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer, type AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\n\nexport type WidgetScopeContext = {\n container: AppContainer\n em: EntityManager\n tenantId: string\n organizationIds: string[] | null\n}\n\nexport async function resolveWidgetScope(\n req: Request,\n translate: (key: string, fallback?: string) => string,\n overrides?: { tenantId?: string | null; organizationId?: string | null }\n): Promise<WidgetScopeContext> {\n const auth = await getAuthFromRequest(req)\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('dashboards.errors.unauthorized', 'Unauthorized') })\n }\n\n const container = await createRequestContainer()\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n\n const tenantId = overrides?.tenantId ?? auth.tenantId ?? null\n if (!tenantId) {\n throw new CrudHttpError(400, { error: translate('dashboards.errors.tenant_required', 'Tenant context is required') })\n }\n\n const organizationIds = (() => {\n if (overrides?.organizationId) return [overrides.organizationId]\n if (scope?.selectedId) return [scope.selectedId]\n if (Array.isArray(scope?.filterIds) && scope.filterIds.length > 0) return scope.filterIds\n if (scope?.allowedIds === null) return null\n if (auth.orgId) return [auth.orgId]\n return [] as string[]\n })()\n\n if (organizationIds !== null && organizationIds.length === 0) {\n throw new CrudHttpError(400, { error: translate('dashboards.errors.organization_required', 'Organization context is required') })\n }\n\n const em = (container.resolve('em') as EntityManager)\n\n return {\n container,\n em,\n tenantId,\n organizationIds,\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,8BAAiD;AAC1D,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAC9B,SAAS,0CAA0C;AASnD,eAAsB,mBACpB,KACA,WACA,WAC6B;AAC7B,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,cAAc,EAAE,CAAC;AAAA,EACrG;AAEA,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AAExF,QAAM,WAAW,WAAW,YAAY,KAAK,YAAY;AACzD,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,qCAAqC,4BAA4B,EAAE,CAAC;AAAA,EACtH;AAEA,QAAM,mBAAmB,MAAM;AAC7B,QAAI,WAAW,eAAgB,QAAO,CAAC,UAAU,cAAc;AAC/D,QAAI,OAAO,WAAY,QAAO,CAAC,MAAM,UAAU;AAC/C,QAAI,MAAM,QAAQ,OAAO,SAAS,KAAK,MAAM,UAAU,SAAS,EAAG,QAAO,MAAM;AAChF,QAAI,OAAO,eAAe,KAAM,QAAO;AACvC,QAAI,KAAK,MAAO,QAAO,CAAC,KAAK,KAAK;AAClC,WAAO,CAAC;AAAA,EACV,GAAG;AAEH,MAAI,oBAAoB,QAAQ,gBAAgB,WAAW,GAAG;AAC5D,UAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,2CAA2C,kCAAkC,EAAE,CAAC;AAAA,EAClI;AAEA,QAAM,KAAM,UAAU,QAAQ,IAAI;AAElC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,265 @@
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";
6
+ import { User } from "@open-mercato/core/modules/auth/data/entities";
7
+ import {
8
+ createPagedListResponseSchema as createSharedPagedListResponseSchema,
9
+ defaultOkResponseSchema as sharedDefaultOkResponseSchema
10
+ } from "@open-mercato/shared/lib/openapi/crud";
11
+ function createPagedListResponseSchema(itemSchema) {
12
+ return createSharedPagedListResponseSchema(itemSchema, { paginationMetaOptional: true });
13
+ }
14
+ const defaultOkResponseSchema = sharedDefaultOkResponseSchema;
15
+ const rawBodySchema = z.object({}).passthrough();
16
+ const listSchema = z.object({
17
+ page: z.coerce.number().min(1).default(1),
18
+ pageSize: z.coerce.number().min(1).max(100).default(50),
19
+ entityId: z.string().uuid().optional(),
20
+ sortField: z.string().optional(),
21
+ sortDir: z.enum(["asc", "desc"]).optional()
22
+ }).passthrough();
23
+ const sortFieldMap = {
24
+ occurredAt: "occurred_at",
25
+ createdAt: "created_at",
26
+ updatedAt: "updated_at"
27
+ };
28
+ const activityCreateResponseSchema = z.object({
29
+ id: z.string().uuid().nullable(),
30
+ authorUserId: z.string().uuid().nullable()
31
+ });
32
+ function makeActivityRoute(config) {
33
+ const {
34
+ entity,
35
+ entityId,
36
+ parentFkColumn,
37
+ parentFkParam,
38
+ features,
39
+ createSchema,
40
+ updateSchema,
41
+ commandPrefix,
42
+ logPrefix,
43
+ openApiFactory,
44
+ openApi: openApiConfig
45
+ } = config;
46
+ const routeMetadata = {
47
+ GET: { requireAuth: true, requireFeatures: [features.view] },
48
+ POST: { requireAuth: true, requireFeatures: [features.manage] },
49
+ PUT: { requireAuth: true, requireFeatures: [features.manage] },
50
+ DELETE: { requireAuth: true, requireFeatures: [features.manage] }
51
+ };
52
+ const fields = [
53
+ "id",
54
+ parentFkColumn,
55
+ "activity_type",
56
+ "subject",
57
+ "body",
58
+ "occurred_at",
59
+ "author_user_id",
60
+ "appearance_icon",
61
+ "appearance_color",
62
+ "organization_id",
63
+ "tenant_id",
64
+ "created_at",
65
+ "updated_at"
66
+ ];
67
+ const crud = makeCrudRoute({
68
+ metadata: routeMetadata,
69
+ orm: {
70
+ entity,
71
+ idField: "id",
72
+ orgField: "organizationId",
73
+ tenantField: "tenantId"
74
+ },
75
+ indexer: {
76
+ entityType: entityId
77
+ },
78
+ list: {
79
+ schema: listSchema,
80
+ entityId,
81
+ fields,
82
+ decorateCustomFields: {
83
+ entityIds: entityId
84
+ },
85
+ sortFieldMap,
86
+ buildFilters: async (query) => {
87
+ const filters = {};
88
+ if (query.entityId) filters[parentFkColumn] = { $eq: query.entityId };
89
+ return filters;
90
+ },
91
+ transformItem: (item) => {
92
+ const record = item ?? {};
93
+ const toIsoString = (value) => {
94
+ if (value == null) return null;
95
+ if (value instanceof Date) return value.toISOString();
96
+ if (typeof value === "string") {
97
+ const trimmed = value.trim();
98
+ if (!trimmed.length) return null;
99
+ const date = new Date(trimmed);
100
+ return Number.isNaN(date.getTime()) ? trimmed : date.toISOString();
101
+ }
102
+ return null;
103
+ };
104
+ const readString = (value) => typeof value === "string" ? value : null;
105
+ const idValue = readString(record.id) ?? (record.id != null ? String(record.id) : "");
106
+ const parentId = readString(record[parentFkColumn]) ?? readString(record[parentFkParam]) ?? null;
107
+ const activityType = readString(record["activity_type"]) ?? readString(record["activityType"]) ?? "";
108
+ const subject = readString(record.subject) ?? (record.subject == null ? null : String(record.subject));
109
+ const body = readString(record.body) ?? (record.body == null ? null : String(record.body));
110
+ const authorUserId = readString(record["author_user_id"]) ?? readString(record["authorUserId"]) ?? null;
111
+ const appearanceIconRaw = readString(record["appearance_icon"]) ?? readString(record["appearanceIcon"]);
112
+ const appearanceColorRaw = readString(record["appearance_color"]) ?? readString(record["appearanceColor"]);
113
+ const organizationId = readString(record["organization_id"]) ?? readString(record["organizationId"]);
114
+ const tenantId = readString(record["tenant_id"]) ?? readString(record["tenantId"]);
115
+ const output = {
116
+ id: idValue,
117
+ entityId: parentId,
118
+ [parentFkParam]: parentId,
119
+ activityType,
120
+ subject,
121
+ body,
122
+ occurredAt: toIsoString(record["occurred_at"] ?? record["occurredAt"]),
123
+ createdAt: toIsoString(record["created_at"] ?? record["createdAt"]),
124
+ authorUserId,
125
+ organizationId,
126
+ tenantId,
127
+ appearanceIcon: appearanceIconRaw && appearanceIconRaw.trim().length ? appearanceIconRaw : null,
128
+ appearanceColor: appearanceColorRaw && appearanceColorRaw.trim().length ? appearanceColorRaw : null,
129
+ customFields: Array.isArray(record.customFields) ? record.customFields : void 0,
130
+ customValues: record.customValues ?? void 0
131
+ };
132
+ for (const [key, value] of Object.entries(record)) {
133
+ if (key.startsWith("cf_") || key.startsWith("cf:")) {
134
+ output[key] = value;
135
+ }
136
+ }
137
+ return output;
138
+ }
139
+ },
140
+ actions: {
141
+ create: {
142
+ commandId: `${commandPrefix}.create`,
143
+ schema: rawBodySchema,
144
+ mapInput: async ({ raw, ctx }) => {
145
+ const { translate } = await resolveTranslations();
146
+ return parseScopedCommandInput(createSchema, raw ?? {}, ctx, translate);
147
+ },
148
+ response: ({ result }) => ({
149
+ id: result?.activityId ?? result?.id ?? null,
150
+ authorUserId: result?.authorUserId ?? null
151
+ }),
152
+ status: 201
153
+ },
154
+ update: {
155
+ commandId: `${commandPrefix}.update`,
156
+ schema: rawBodySchema,
157
+ mapInput: async ({ raw, ctx }) => {
158
+ const { translate } = await resolveTranslations();
159
+ return parseScopedCommandInput(updateSchema, raw ?? {}, ctx, translate);
160
+ },
161
+ response: () => ({ ok: true })
162
+ },
163
+ delete: {
164
+ commandId: `${commandPrefix}.delete`,
165
+ schema: rawBodySchema,
166
+ mapInput: async ({ parsed, ctx }) => {
167
+ const { translate } = await resolveTranslations();
168
+ const id = resolveCrudRecordId(parsed, ctx, translate);
169
+ return { id };
170
+ },
171
+ response: () => ({ ok: true })
172
+ }
173
+ },
174
+ hooks: {
175
+ afterList: async (payload, ctx) => {
176
+ const items = Array.isArray(payload.items) ? payload.items : [];
177
+ if (!items.length) return;
178
+ const userIds = /* @__PURE__ */ new Set();
179
+ items.forEach((item) => {
180
+ if (!item || typeof item !== "object") return;
181
+ const record = item;
182
+ const userId = typeof record.author_user_id === "string" ? record.author_user_id : typeof record.authorUserId === "string" ? record.authorUserId : null;
183
+ if (userId) userIds.add(userId);
184
+ });
185
+ if (!userIds.size) return;
186
+ try {
187
+ const em = ctx.container.resolve("em").fork();
188
+ const users = await findWithDecryption(
189
+ em,
190
+ User,
191
+ { id: { $in: Array.from(userIds) } },
192
+ void 0,
193
+ { tenantId: ctx.auth?.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null }
194
+ );
195
+ const map = /* @__PURE__ */ new Map();
196
+ users.forEach((user) => {
197
+ const name = typeof user.name === "string" && user.name.trim().length ? user.name.trim() : null;
198
+ map.set(user.id, { name, email: user.email ?? null });
199
+ });
200
+ items.forEach((item) => {
201
+ if (!item || typeof item !== "object") return;
202
+ const record = item;
203
+ const userId = typeof record.author_user_id === "string" ? record.author_user_id : typeof record.authorUserId === "string" ? record.authorUserId : null;
204
+ if (!userId) return;
205
+ const meta = map.get(userId);
206
+ if (!meta) return;
207
+ record.authorName = meta.name;
208
+ record.authorEmail = meta.email;
209
+ if (!("author_name" in record)) record.author_name = meta.name;
210
+ if (!("author_email" in record)) record.author_email = meta.email;
211
+ });
212
+ } catch (err) {
213
+ console.warn(`${logPrefix} failed to enrich author metadata`, err);
214
+ }
215
+ }
216
+ }
217
+ });
218
+ const activityListItemSchema = z.object({
219
+ id: z.string().uuid(),
220
+ [parentFkColumn]: z.string().uuid().nullable().optional(),
221
+ activity_type: z.string().nullable().optional(),
222
+ subject: z.string().nullable().optional(),
223
+ body: z.string().nullable().optional(),
224
+ occurred_at: z.string().nullable().optional(),
225
+ author_user_id: z.string().uuid().nullable(),
226
+ appearance_icon: z.string().nullable().optional(),
227
+ appearance_color: z.string().nullable().optional(),
228
+ organization_id: z.string().uuid().nullable().optional(),
229
+ tenant_id: z.string().uuid().nullable().optional(),
230
+ created_at: z.string().nullable(),
231
+ updated_at: z.string().nullable().optional()
232
+ }).passthrough();
233
+ const openApi = openApiFactory({
234
+ resourceName: openApiConfig.resourceName,
235
+ querySchema: listSchema,
236
+ listResponseSchema: createPagedListResponseSchema(activityListItemSchema),
237
+ create: {
238
+ schema: createSchema,
239
+ responseSchema: activityCreateResponseSchema,
240
+ description: openApiConfig.createDescription
241
+ },
242
+ update: {
243
+ schema: updateSchema,
244
+ responseSchema: defaultOkResponseSchema,
245
+ description: openApiConfig.updateDescription
246
+ },
247
+ del: {
248
+ schema: z.object({ id: z.string().uuid() }),
249
+ responseSchema: defaultOkResponseSchema,
250
+ description: openApiConfig.deleteDescription
251
+ }
252
+ });
253
+ return {
254
+ metadata: routeMetadata,
255
+ openApi,
256
+ GET: crud.GET,
257
+ POST: crud.POST,
258
+ PUT: crud.PUT,
259
+ DELETE: crud.DELETE
260
+ };
261
+ }
262
+ export {
263
+ makeActivityRoute
264
+ };
265
+ //# sourceMappingURL=makeActivityRoute.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/entities/lib/makeActivityRoute.ts"],
4
+ "sourcesContent": ["import { z, type ZodTypeAny } 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 { User } from '@open-mercato/core/modules/auth/data/entities'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport type { CrudOpenApiOptions } from '@open-mercato/shared/lib/openapi/crud'\nimport {\n createPagedListResponseSchema as createSharedPagedListResponseSchema,\n defaultOkResponseSchema as sharedDefaultOkResponseSchema,\n} from '@open-mercato/shared/lib/openapi/crud'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- MikroORM entity class constructor\ntype EntityClass = new (...args: any[]) => unknown\n\ninterface ActivityRouteConfig {\n entity: EntityClass\n entityId: string\n parentFkColumn: string\n parentFkParam: string\n features: { view: string; manage: string }\n createSchema: ZodTypeAny\n updateSchema: ZodTypeAny\n commandPrefix: string\n logPrefix: string\n openApiFactory: (options: CrudOpenApiOptions) => OpenApiRouteDoc\n openApi: {\n resourceName: string\n createDescription: string\n updateDescription: string\n deleteDescription: string\n }\n}\n\nfunction createPagedListResponseSchema(itemSchema: ZodTypeAny) {\n return createSharedPagedListResponseSchema(itemSchema, { paginationMetaOptional: true })\n}\n\nconst defaultOkResponseSchema = sharedDefaultOkResponseSchema\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 sortFieldMap = {\n occurredAt: 'occurred_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n}\n\nconst activityCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n authorUserId: z.string().uuid().nullable(),\n})\n\nexport function makeActivityRoute(config: ActivityRouteConfig) {\n const {\n entity,\n entityId,\n parentFkColumn,\n parentFkParam,\n features,\n createSchema,\n updateSchema,\n commandPrefix,\n logPrefix,\n openApiFactory,\n openApi: openApiConfig,\n } = config\n\n const routeMetadata = {\n GET: { requireAuth: true, requireFeatures: [features.view] },\n POST: { requireAuth: true, requireFeatures: [features.manage] },\n PUT: { requireAuth: true, requireFeatures: [features.manage] },\n DELETE: { requireAuth: true, requireFeatures: [features.manage] },\n }\n\n const fields = [\n 'id',\n parentFkColumn,\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\n const crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n },\n indexer: {\n entityType: entityId,\n },\n list: {\n schema: listSchema,\n entityId,\n fields,\n decorateCustomFields: {\n entityIds: entityId,\n },\n sortFieldMap,\n buildFilters: async (query) => {\n const filters: Record<string, unknown> = {}\n if (query.entityId) filters[parentFkColumn] = { $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 parentId = readString(record[parentFkColumn]) ?? readString(record[parentFkParam]) ?? 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: parentId,\n [parentFkParam]: parentId,\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: `${commandPrefix}.create`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(createSchema, 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: `${commandPrefix}.update`,\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n return parseScopedCommandInput(updateSchema, raw ?? {}, ctx, translate)\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: `${commandPrefix}.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(`${logPrefix} failed to enrich author metadata`, err)\n }\n },\n },\n })\n\n const activityListItemSchema = z\n .object({\n id: z.string().uuid(),\n [parentFkColumn]: 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\n const openApi = openApiFactory({\n resourceName: openApiConfig.resourceName,\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(activityListItemSchema),\n create: {\n schema: createSchema,\n responseSchema: activityCreateResponseSchema,\n description: openApiConfig.createDescription,\n },\n update: {\n schema: updateSchema,\n responseSchema: defaultOkResponseSchema,\n description: openApiConfig.updateDescription,\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: openApiConfig.deleteDescription,\n },\n })\n\n return {\n metadata: routeMetadata,\n openApi,\n GET: crud.GET,\n POST: crud.POST,\n PUT: crud.PUT,\n DELETE: crud.DELETE,\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -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 { ResourcesResourceActivity } from "../data/entities.js";
7
3
  import {
8
4
  resourcesResourceActivityCreateSchema,
9
5
  resourcesResourceActivityUpdateSchema
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 { createResourcesCrudOpenApi, 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: ["resources.view"] },
24
- POST: { requireAuth: true, requireFeatures: ["resources.manage_resources"] },
25
- PUT: { requireAuth: true, requireFeatures: ["resources.manage_resources"] },
26
- DELETE: { requireAuth: true, requireFeatures: ["resources.manage_resources"] }
27
- };
28
- const metadata = routeMetadata;
29
- const crud = makeCrudRoute({
30
- metadata: routeMetadata,
31
- orm: {
32
- entity: ResourcesResourceActivity,
33
- idField: "id",
34
- orgField: "organizationId",
35
- tenantField: "tenantId"
36
- },
37
- indexer: {
38
- entityType: E.resources.resources_resource_activity
39
- },
40
- list: {
41
- schema: listSchema,
42
- entityId: E.resources.resources_resource_activity,
43
- fields: [
44
- "id",
45
- "resource_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.resources.resources_resource_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.resource_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 resourceId = readString(record["resource_id"]) ?? readString(record["resourceId"]) ?? 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: resourceId,
98
- resourceId,
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: "resources.resource-activities.create",
123
- schema: rawBodySchema,
124
- mapInput: async ({ raw, ctx }) => {
125
- const { translate } = await resolveTranslations();
126
- return parseScopedCommandInput(resourcesResourceActivityCreateSchema, 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: "resources.resource-activities.update",
136
- schema: rawBodySchema,
137
- mapInput: async ({ raw, ctx }) => {
138
- const { translate } = await resolveTranslations();
139
- return parseScopedCommandInput(resourcesResourceActivityUpdateSchema, raw ?? {}, ctx, translate);
140
- },
141
- response: () => ({ ok: true })
142
- },
143
- delete: {
144
- commandId: "resources.resource-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("[resources.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
- resource_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 = createResourcesCrudOpenApi({
222
- resourceName: "ResourceActivity",
223
- querySchema: listSchema,
224
- listResponseSchema: createPagedListResponseSchema(activityListItemSchema),
225
- create: {
226
- schema: resourcesResourceActivityCreateSchema,
227
- responseSchema: activityCreateResponseSchema,
228
- description: "Adds an activity to a resource timeline."
229
- },
230
- update: {
231
- schema: resourcesResourceActivityUpdateSchema,
232
- responseSchema: defaultOkResponseSchema,
233
- description: "Updates a resource activity."
234
- },
235
- del: {
236
- schema: z.object({ id: z.string().uuid() }),
237
- responseSchema: defaultOkResponseSchema,
238
- description: "Deletes a resource activity."
8
+ import { createResourcesCrudOpenApi } from "./openapi.js";
9
+ const route = makeActivityRoute({
10
+ entity: ResourcesResourceActivity,
11
+ entityId: E.resources.resources_resource_activity,
12
+ parentFkColumn: "resource_id",
13
+ parentFkParam: "resourceId",
14
+ features: { view: "resources.view", manage: "resources.manage_resources" },
15
+ createSchema: resourcesResourceActivityCreateSchema,
16
+ updateSchema: resourcesResourceActivityUpdateSchema,
17
+ commandPrefix: "resources.resource-activities",
18
+ logPrefix: "[resources.activities]",
19
+ openApiFactory: createResourcesCrudOpenApi,
20
+ openApi: {
21
+ resourceName: "ResourceActivity",
22
+ createDescription: "Adds an activity to a resource timeline.",
23
+ updateDescription: "Updates a resource activity.",
24
+ deleteDescription: "Deletes a resource 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,