@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.
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +17 -154
- package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +3 -3
- package/dist/modules/currencies/backend/exchange-rates/create/page.js +14 -152
- package/dist/modules/currencies/backend/exchange-rates/create/page.js.map +2 -2
- package/dist/modules/currencies/lib/exchangeRateFormConfig.js +167 -0
- package/dist/modules/currencies/lib/exchangeRateFormConfig.js.map +7 -0
- package/dist/modules/customers/api/dashboard/widgets/utils.js +1 -34
- package/dist/modules/customers/api/dashboard/widgets/utils.js.map +2 -2
- package/dist/modules/customers/commands/activities.js +3 -8
- package/dist/modules/customers/commands/activities.js.map +2 -2
- package/dist/modules/customers/commands/comments.js +2 -8
- package/dist/modules/customers/commands/comments.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +38 -0
- package/dist/modules/dashboards/lib/widgetScope.js.map +7 -0
- package/dist/modules/entities/lib/makeActivityRoute.js +265 -0
- package/dist/modules/entities/lib/makeActivityRoute.js.map +7 -0
- package/dist/modules/resources/api/activities.js +24 -232
- package/dist/modules/resources/api/activities.js.map +2 -2
- package/dist/modules/resources/commands/activities.js +3 -8
- package/dist/modules/resources/commands/activities.js.map +2 -2
- package/dist/modules/resources/commands/comments.js +2 -8
- package/dist/modules/resources/commands/comments.js.map +2 -2
- package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js +27 -182
- package/dist/modules/sales/api/dashboard/widgets/new-orders/route.js.map +2 -2
- package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js +28 -183
- package/dist/modules/sales/api/dashboard/widgets/new-quotes/route.js.map +2 -2
- package/dist/modules/sales/api/order-line-statuses/route.js +15 -194
- package/dist/modules/sales/api/order-line-statuses/route.js.map +2 -2
- package/dist/modules/sales/api/order-lines/route.js +15 -281
- package/dist/modules/sales/api/order-lines/route.js.map +2 -2
- package/dist/modules/sales/api/order-statuses/route.js +15 -194
- package/dist/modules/sales/api/order-statuses/route.js.map +2 -2
- package/dist/modules/sales/api/payment-statuses/route.js +15 -194
- package/dist/modules/sales/api/payment-statuses/route.js.map +2 -2
- package/dist/modules/sales/api/quote-lines/route.js +15 -279
- package/dist/modules/sales/api/quote-lines/route.js.map +2 -2
- package/dist/modules/sales/api/shipment-statuses/route.js +15 -194
- package/dist/modules/sales/api/shipment-statuses/route.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +3 -84
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ProviderFieldInput.js +86 -0
- package/dist/modules/sales/components/ProviderFieldInput.js.map +7 -0
- package/dist/modules/sales/components/ShippingMethodsSettings.js +3 -82
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/lib/makeSalesLineRoute.js +308 -0
- package/dist/modules/sales/lib/makeSalesLineRoute.js.map +7 -0
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js +206 -0
- package/dist/modules/sales/lib/makeStatusDictionaryRoute.js.map +7 -0
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +178 -0
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +7 -0
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +1 -39
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +1 -39
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/shared.js +46 -0
- package/dist/modules/sales/widgets/dashboard/shared.js.map +7 -0
- package/dist/modules/staff/api/activities.js +24 -232
- package/dist/modules/staff/api/activities.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +14 -34
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +15 -34
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/commands/activities.js +3 -8
- package/dist/modules/staff/commands/activities.js.map +2 -2
- package/dist/modules/staff/commands/comments.js +2 -8
- package/dist/modules/staff/commands/comments.js.map +2 -2
- package/dist/modules/staff/lib/leaveRequestHelpers.js +41 -0
- package/dist/modules/staff/lib/leaveRequestHelpers.js.map +7 -0
- package/package.json +2 -2
- package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +20 -180
- package/src/modules/currencies/backend/exchange-rates/create/page.tsx +16 -175
- package/src/modules/currencies/lib/exchangeRateFormConfig.ts +200 -0
- package/src/modules/customers/api/dashboard/widgets/utils.ts +1 -53
- package/src/modules/customers/commands/activities.ts +2 -8
- package/src/modules/customers/commands/comments.ts +2 -8
- package/src/modules/dashboards/i18n/de.json +3 -0
- package/src/modules/dashboards/i18n/en.json +3 -0
- package/src/modules/dashboards/i18n/es.json +3 -0
- package/src/modules/dashboards/i18n/pl.json +3 -0
- package/src/modules/dashboards/lib/widgetScope.ts +53 -0
- package/src/modules/entities/lib/makeActivityRoute.ts +327 -0
- package/src/modules/resources/api/activities.ts +25 -269
- package/src/modules/resources/commands/activities.ts +2 -7
- package/src/modules/resources/commands/comments.ts +2 -8
- package/src/modules/sales/api/dashboard/widgets/new-orders/route.ts +29 -244
- package/src/modules/sales/api/dashboard/widgets/new-quotes/route.ts +30 -245
- package/src/modules/sales/api/order-line-statuses/route.ts +16 -209
- package/src/modules/sales/api/order-lines/route.ts +16 -300
- package/src/modules/sales/api/order-statuses/route.ts +16 -209
- package/src/modules/sales/api/payment-statuses/route.ts +16 -209
- package/src/modules/sales/api/quote-lines/route.ts +16 -298
- package/src/modules/sales/api/shipment-statuses/route.ts +16 -209
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +3 -88
- package/src/modules/sales/components/ProviderFieldInput.tsx +85 -0
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +3 -86
- package/src/modules/sales/lib/makeSalesLineRoute.ts +345 -0
- package/src/modules/sales/lib/makeStatusDictionaryRoute.ts +229 -0
- package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +247 -0
- package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +7 -50
- package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +7 -49
- package/src/modules/sales/widgets/dashboard/shared.ts +44 -0
- package/src/modules/staff/api/activities.ts +25 -269
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +15 -69
- package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +16 -65
- package/src/modules/staff/commands/activities.ts +2 -7
- package/src/modules/staff/commands/comments.ts +2 -8
- 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 {
|
|
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
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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,
|