@open-mercato/core 0.6.5-develop.4588.1.ecaa16cfc0 → 0.6.5-develop.4616.1.0cd64e1448

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 (23) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/crudFormFields.js +33 -0
  3. package/dist/helpers/integration/crudFormFields.js.map +7 -0
  4. package/dist/helpers/integration/crudFormPersistence.js +105 -0
  5. package/dist/helpers/integration/crudFormPersistence.js.map +7 -0
  6. package/dist/modules/customers/components/formConfig.js +22 -7
  7. package/dist/modules/customers/components/formConfig.js.map +2 -2
  8. package/dist/modules/customers/data/validators.js +17 -4
  9. package/dist/modules/customers/data/validators.js.map +2 -2
  10. package/dist/modules/dictionaries/commands/entry-operations.js +8 -0
  11. package/dist/modules/dictionaries/commands/entry-operations.js.map +2 -2
  12. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js +6 -2
  13. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
  14. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -1
  15. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  16. package/package.json +7 -7
  17. package/src/helpers/integration/crudFormFields.ts +48 -0
  18. package/src/helpers/integration/crudFormPersistence.ts +166 -0
  19. package/src/modules/customers/components/formConfig.tsx +59 -25
  20. package/src/modules/customers/data/validators.ts +25 -9
  21. package/src/modules/dictionaries/commands/entry-operations.ts +19 -0
  22. package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +23 -2
  23. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -1
@@ -1,4 +1,4 @@
1
- [build:core] found 3151 entry points
1
+ [build:core] found 3172 entry points
2
2
  [build:core] built successfully
3
3
  [build:core:generated] found 185 entry points
4
4
  [build:core:generated] built successfully
@@ -0,0 +1,33 @@
1
+ import { parseBooleanWithDefault } from "@open-mercato/shared/lib/boolean";
2
+ const CRUDFORM_EXTENSION_TESTS_DISABLED_ENV = "OM_INTEGRATION_CRUDFORM_EXTENSION_TESTS_DISABLED";
3
+ function crudFormExtensionTestsDisabled(env = process.env) {
4
+ return parseBooleanWithDefault(env[CRUDFORM_EXTENSION_TESTS_DISABLED_ENV], false);
5
+ }
6
+ function getCustomFieldValue(record, fieldName) {
7
+ const customValues = record.customValues;
8
+ if (customValues && typeof customValues === "object" && fieldName in customValues) {
9
+ return customValues[fieldName];
10
+ }
11
+ const prefixedUnderscore = record[`cf_${fieldName}`];
12
+ if (prefixedUnderscore !== void 0) return prefixedUnderscore;
13
+ const prefixedColon = record[`cf:${fieldName}`];
14
+ if (prefixedColon !== void 0) return prefixedColon;
15
+ const customFields = record.customFields;
16
+ if (Array.isArray(customFields)) {
17
+ const match = customFields.find((entry) => {
18
+ if (!entry || typeof entry !== "object") return false;
19
+ const candidate = entry;
20
+ return candidate.key === fieldName || candidate.id === fieldName || candidate.name === fieldName;
21
+ });
22
+ if (match && typeof match === "object") {
23
+ return match.value;
24
+ }
25
+ }
26
+ return void 0;
27
+ }
28
+ export {
29
+ CRUDFORM_EXTENSION_TESTS_DISABLED_ENV,
30
+ crudFormExtensionTestsDisabled,
31
+ getCustomFieldValue
32
+ };
33
+ //# sourceMappingURL=crudFormFields.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/helpers/integration/crudFormFields.ts"],
4
+ "sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean';\n\n/**\n * Pure, runner-agnostic helpers for the CrudForm field-persistence sweep (umbrella #2466).\n *\n * Kept free of any `@playwright/test` import so this logic is unit-testable under jest.\n * The Playwright harness (`crudFormPersistence.ts`) re-exports everything here.\n */\n\nexport const CRUDFORM_EXTENSION_TESTS_DISABLED_ENV = 'OM_INTEGRATION_CRUDFORM_EXTENSION_TESTS_DISABLED';\n\nexport type CrudRecord = Record<string, unknown>;\n\n/**\n * Reads the sweep disable flag. Default `false` so the sweep runs unless explicitly turned off\n * via `OM_INTEGRATION_CRUDFORM_EXTENSION_TESTS_DISABLED=1` (or `true`/`yes`/`on`).\n */\nexport function crudFormExtensionTestsDisabled(env: NodeJS.ProcessEnv = process.env): boolean {\n return parseBooleanWithDefault(env[CRUDFORM_EXTENSION_TESTS_DISABLED_ENV], false);\n}\n\n/**\n * Resolves a custom-field value from a CRUD response record, tolerating every shape the\n * platform emits: bare keys under `customValues`, top-level `cf_<name>` / `cf:<name>`, or a\n * `customFields` definition array carrying `value`. Returns `undefined` when absent.\n */\nexport function getCustomFieldValue(record: CrudRecord, fieldName: string): unknown {\n const customValues = record.customValues;\n if (customValues && typeof customValues === 'object' && fieldName in (customValues as CrudRecord)) {\n return (customValues as CrudRecord)[fieldName];\n }\n const prefixedUnderscore = record[`cf_${fieldName}`];\n if (prefixedUnderscore !== undefined) return prefixedUnderscore;\n const prefixedColon = record[`cf:${fieldName}`];\n if (prefixedColon !== undefined) return prefixedColon;\n const customFields = record.customFields;\n if (Array.isArray(customFields)) {\n const match = customFields.find((entry) => {\n if (!entry || typeof entry !== 'object') return false;\n const candidate = entry as CrudRecord;\n return candidate.key === fieldName || candidate.id === fieldName || candidate.name === fieldName;\n });\n if (match && typeof match === 'object') {\n return (match as CrudRecord).value;\n }\n }\n return undefined;\n}\n"],
5
+ "mappings": "AAAA,SAAS,+BAA+B;AASjC,MAAM,wCAAwC;AAQ9C,SAAS,+BAA+B,MAAyB,QAAQ,KAAc;AAC5F,SAAO,wBAAwB,IAAI,qCAAqC,GAAG,KAAK;AAClF;AAOO,SAAS,oBAAoB,QAAoB,WAA4B;AAClF,QAAM,eAAe,OAAO;AAC5B,MAAI,gBAAgB,OAAO,iBAAiB,YAAY,aAAc,cAA6B;AACjG,WAAQ,aAA4B,SAAS;AAAA,EAC/C;AACA,QAAM,qBAAqB,OAAO,MAAM,SAAS,EAAE;AACnD,MAAI,uBAAuB,OAAW,QAAO;AAC7C,QAAM,gBAAgB,OAAO,MAAM,SAAS,EAAE;AAC9C,MAAI,kBAAkB,OAAW,QAAO;AACxC,QAAM,eAAe,OAAO;AAC5B,MAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,UAAM,QAAQ,aAAa,KAAK,CAAC,UAAU;AACzC,UAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,YAAM,YAAY;AAClB,aAAO,UAAU,QAAQ,aAAa,UAAU,OAAO,aAAa,UAAU,SAAS;AAAA,IACzF,CAAC;AACD,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,aAAQ,MAAqB;AAAA,IAC/B;AAAA,EACF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,105 @@
1
+ import { expect, test } from "@playwright/test";
2
+ import { apiRequest } from "./api.js";
3
+ import { expectId, readJsonSafe } from "./generalFixtures.js";
4
+ import {
5
+ CRUDFORM_EXTENSION_TESTS_DISABLED_ENV,
6
+ crudFormExtensionTestsDisabled,
7
+ getCustomFieldValue
8
+ } from "./crudFormFields.js";
9
+ function skipIfCrudFormExtensionTestsDisabled() {
10
+ test.skip(
11
+ crudFormExtensionTestsDisabled(),
12
+ `${CRUDFORM_EXTENSION_TESTS_DISABLED_ENV} is set \u2014 CrudForm field-persistence sweep skipped`
13
+ );
14
+ }
15
+ function assertScalarFieldsPersisted(record, expected, label = "record") {
16
+ for (const [key, value] of Object.entries(expected)) {
17
+ expect(record[key], `${label}.${key} should persist`).toEqual(value);
18
+ }
19
+ }
20
+ function assertCustomFieldsPersisted(record, expected, label = "record") {
21
+ for (const [name, value] of Object.entries(expected)) {
22
+ expect(getCustomFieldValue(record, name), `${label} custom field "${name}" should persist`).toEqual(value);
23
+ }
24
+ }
25
+ async function safeText(response) {
26
+ try {
27
+ return (await response.text()).slice(0, 500);
28
+ } catch {
29
+ return "<unreadable body>";
30
+ }
31
+ }
32
+ async function defaultReadById(request, token, collectionPath, id) {
33
+ const separator = collectionPath.includes("?") ? "&" : "?";
34
+ const response = await apiRequest(
35
+ request,
36
+ "GET",
37
+ `${collectionPath}${separator}id=${encodeURIComponent(id)}&page=1&pageSize=100`,
38
+ { token }
39
+ );
40
+ expect(response.status(), `read-back ${collectionPath} failed: ${response.status()}`).toBe(200);
41
+ const body = await readJsonSafe(response);
42
+ if (Array.isArray(body?.items)) {
43
+ return body.items.find((item) => item.id === id) ?? null;
44
+ }
45
+ return body && body.id === id ? body : null;
46
+ }
47
+ async function runCrudFormRoundTrip(config) {
48
+ const { request, token, collectionPath } = config;
49
+ const read = config.readById ?? ((id2) => defaultReadById(request, token, collectionPath, id2));
50
+ let id = null;
51
+ try {
52
+ const createResponse = await apiRequest(request, "POST", collectionPath, {
53
+ token,
54
+ data: config.create.payload
55
+ });
56
+ expect(
57
+ createResponse.status(),
58
+ `create ${collectionPath} failed (${createResponse.status()}): ${await safeText(createResponse)}`
59
+ ).toBe(config.create.expectedStatus ?? 201);
60
+ const createBody = await readJsonSafe(createResponse) ?? {};
61
+ const rawId = config.idFromCreate ? config.idFromCreate(createBody) : createBody.id ?? createBody.entityId;
62
+ id = expectId(rawId, `create response should include an id (${collectionPath})`);
63
+ const afterCreate = await read(id);
64
+ expect(afterCreate, `created record ${id} should be readable from ${collectionPath}`).toBeTruthy();
65
+ if (config.expectAfterCreate.scalars) {
66
+ assertScalarFieldsPersisted(afterCreate, config.expectAfterCreate.scalars, "after-create");
67
+ }
68
+ if (config.expectAfterCreate.customFields) {
69
+ assertCustomFieldsPersisted(afterCreate, config.expectAfterCreate.customFields, "after-create");
70
+ }
71
+ const updateResponse = await apiRequest(request, "PUT", collectionPath, {
72
+ token,
73
+ data: config.update.payload(id)
74
+ });
75
+ expect(
76
+ updateResponse.status(),
77
+ `update ${collectionPath} failed (${updateResponse.status()}): ${await safeText(updateResponse)}`
78
+ ).toBe(config.update.expectedStatus ?? 200);
79
+ const afterUpdate = await read(id);
80
+ expect(afterUpdate, `updated record ${id} should be readable from ${collectionPath}`).toBeTruthy();
81
+ if (config.expectAfterUpdate.scalars) {
82
+ assertScalarFieldsPersisted(afterUpdate, config.expectAfterUpdate.scalars, "after-update");
83
+ }
84
+ if (config.expectAfterUpdate.customFields) {
85
+ assertCustomFieldsPersisted(afterUpdate, config.expectAfterUpdate.customFields, "after-update");
86
+ }
87
+ } finally {
88
+ if (id && config.cleanup !== false) {
89
+ const separator = collectionPath.includes("?") ? "&" : "?";
90
+ await apiRequest(request, "DELETE", `${collectionPath}${separator}id=${encodeURIComponent(id)}`, {
91
+ token
92
+ }).catch(() => void 0);
93
+ }
94
+ }
95
+ }
96
+ export {
97
+ CRUDFORM_EXTENSION_TESTS_DISABLED_ENV,
98
+ assertCustomFieldsPersisted,
99
+ assertScalarFieldsPersisted,
100
+ crudFormExtensionTestsDisabled,
101
+ getCustomFieldValue,
102
+ runCrudFormRoundTrip,
103
+ skipIfCrudFormExtensionTestsDisabled
104
+ };
105
+ //# sourceMappingURL=crudFormPersistence.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/helpers/integration/crudFormPersistence.ts"],
4
+ "sourcesContent": ["import { expect, test, type APIRequestContext, type APIResponse } from '@playwright/test';\nimport { apiRequest } from './api';\nimport { expectId, readJsonSafe } from './generalFixtures';\nimport {\n CRUDFORM_EXTENSION_TESTS_DISABLED_ENV,\n crudFormExtensionTestsDisabled,\n getCustomFieldValue,\n type CrudRecord,\n} from './crudFormFields';\n\n/**\n * Playwright harness for the CrudForm field-persistence sweep (umbrella #2466).\n *\n * Every spec proves a CrudForm surface saves AND reloads every field type \u2014 scalars,\n * dictionary references, multiselect/array values, and **custom fields** \u2014 on both create\n * and update.\n *\n * The whole sweep is gated by `OM_INTEGRATION_CRUDFORM_EXTENSION_TESTS_DISABLED`\n * (default `false` \u2192 tests run). When set truthy the specs `test.skip()` themselves, so the\n * sweep can be disabled wholesale without deleting any spec.\n */\n\nexport {\n CRUDFORM_EXTENSION_TESTS_DISABLED_ENV,\n crudFormExtensionTestsDisabled,\n getCustomFieldValue,\n type CrudRecord,\n};\n\n/**\n * Call inside `test.beforeAll` (or a test body) to skip the spec when the sweep is disabled.\n * Uses Playwright's `test.skip(condition, reason)` so the spec is reported as skipped, not failed.\n */\nexport function skipIfCrudFormExtensionTestsDisabled(): void {\n test.skip(\n crudFormExtensionTestsDisabled(),\n `${CRUDFORM_EXTENSION_TESTS_DISABLED_ENV} is set \u2014 CrudForm field-persistence sweep skipped`,\n );\n}\n\n/** Asserts each expected scalar field round-tripped (deep equality so arrays/objects work). */\nexport function assertScalarFieldsPersisted(record: CrudRecord, expected: CrudRecord, label = 'record'): void {\n for (const [key, value] of Object.entries(expected)) {\n expect(record[key], `${label}.${key} should persist`).toEqual(value);\n }\n}\n\n/** Asserts each expected custom field round-tripped, regardless of response shape. */\nexport function assertCustomFieldsPersisted(record: CrudRecord, expected: CrudRecord, label = 'record'): void {\n for (const [name, value] of Object.entries(expected)) {\n expect(getCustomFieldValue(record, name), `${label} custom field \"${name}\" should persist`).toEqual(value);\n }\n}\n\nasync function safeText(response: APIResponse): Promise<string> {\n try {\n return (await response.text()).slice(0, 500);\n } catch {\n return '<unreadable body>';\n }\n}\n\nexport type CrudFormExpectation = {\n scalars?: CrudRecord;\n customFields?: CrudRecord;\n};\n\nexport type CrudFormRoundTripConfig = {\n request: APIRequestContext;\n token: string;\n /** Collection route handling POST/PUT/DELETE and a `?id=` list GET, e.g. `/api/currencies/currencies`. */\n collectionPath: string;\n create: { payload: CrudRecord; expectedStatus?: number };\n /** Build the PUT body from the created id. MUST include the id the route expects. */\n update: { payload: (id: string) => CrudRecord; expectedStatus?: number };\n expectAfterCreate: CrudFormExpectation;\n expectAfterUpdate: CrudFormExpectation;\n /** Override id extraction from the create response (default: `id` ?? `entityId`). */\n idFromCreate?: (body: CrudRecord) => string;\n /** Override read-back (default: list `?id=` and match on `id`). Useful for detail-GET routes. */\n readById?: (id: string) => Promise<CrudRecord | null>;\n /** Delete the fixture in `finally` (default true). */\n cleanup?: boolean;\n};\n\nasync function defaultReadById(\n request: APIRequestContext,\n token: string,\n collectionPath: string,\n id: string,\n): Promise<CrudRecord | null> {\n const separator = collectionPath.includes('?') ? '&' : '?';\n const response = await apiRequest(\n request,\n 'GET',\n `${collectionPath}${separator}id=${encodeURIComponent(id)}&page=1&pageSize=100`,\n { token },\n );\n expect(response.status(), `read-back ${collectionPath} failed: ${response.status()}`).toBe(200);\n const body = await readJsonSafe<{ items?: CrudRecord[] } & CrudRecord>(response);\n if (Array.isArray(body?.items)) {\n return body!.items.find((item) => item.id === id) ?? null;\n }\n // Detail routes may return the record directly.\n return body && body.id === id ? (body as CrudRecord) : null;\n}\n\n/**\n * Runs the canonical create \u2192 read-back \u2192 assert \u2192 update \u2192 read-back \u2192 assert \u2192 delete\n * cycle for a makeCrud collection route and asserts every declared field persisted.\n */\nexport async function runCrudFormRoundTrip(config: CrudFormRoundTripConfig): Promise<void> {\n const { request, token, collectionPath } = config;\n const read = config.readById ?? ((id: string) => defaultReadById(request, token, collectionPath, id));\n let id: string | null = null;\n\n try {\n const createResponse = await apiRequest(request, 'POST', collectionPath, {\n token,\n data: config.create.payload,\n });\n expect(\n createResponse.status(),\n `create ${collectionPath} failed (${createResponse.status()}): ${await safeText(createResponse)}`,\n ).toBe(config.create.expectedStatus ?? 201);\n const createBody = (await readJsonSafe<CrudRecord>(createResponse)) ?? {};\n const rawId = config.idFromCreate\n ? config.idFromCreate(createBody)\n : (createBody.id ?? createBody.entityId);\n id = expectId(rawId, `create response should include an id (${collectionPath})`);\n\n const afterCreate = await read(id);\n expect(afterCreate, `created record ${id} should be readable from ${collectionPath}`).toBeTruthy();\n if (config.expectAfterCreate.scalars) {\n assertScalarFieldsPersisted(afterCreate as CrudRecord, config.expectAfterCreate.scalars, 'after-create');\n }\n if (config.expectAfterCreate.customFields) {\n assertCustomFieldsPersisted(afterCreate as CrudRecord, config.expectAfterCreate.customFields, 'after-create');\n }\n\n const updateResponse = await apiRequest(request, 'PUT', collectionPath, {\n token,\n data: config.update.payload(id),\n });\n expect(\n updateResponse.status(),\n `update ${collectionPath} failed (${updateResponse.status()}): ${await safeText(updateResponse)}`,\n ).toBe(config.update.expectedStatus ?? 200);\n\n const afterUpdate = await read(id);\n expect(afterUpdate, `updated record ${id} should be readable from ${collectionPath}`).toBeTruthy();\n if (config.expectAfterUpdate.scalars) {\n assertScalarFieldsPersisted(afterUpdate as CrudRecord, config.expectAfterUpdate.scalars, 'after-update');\n }\n if (config.expectAfterUpdate.customFields) {\n assertCustomFieldsPersisted(afterUpdate as CrudRecord, config.expectAfterUpdate.customFields, 'after-update');\n }\n } finally {\n if (id && config.cleanup !== false) {\n const separator = collectionPath.includes('?') ? '&' : '?';\n await apiRequest(request, 'DELETE', `${collectionPath}${separator}id=${encodeURIComponent(id)}`, {\n token,\n }).catch(() => undefined);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["id"]
7
+ }
@@ -1077,14 +1077,19 @@ function buildCompanyPayload(values, organizationId) {
1077
1077
  return payload;
1078
1078
  }
1079
1079
  const optionalString = () => z.string().trim().optional().or(z.literal("")).transform((val) => val === "" ? void 0 : val).optional();
1080
+ const clearableUrlField = () => z.string().trim().url().optional().or(z.literal("")).transform((val) => val === "" ? null : val).optional();
1081
+ const clearableEmailField = () => z.string().trim().email().optional().or(z.literal("")).transform((val) => val === "" ? null : val).optional();
1080
1082
  const createCompanyEditSchema = () => createCompanyFormSchema().extend({
1081
- id: z.string().uuid()
1083
+ id: z.string().uuid(),
1084
+ primaryEmail: clearableEmailField(),
1085
+ websiteUrl: clearableUrlField()
1082
1086
  });
1083
1087
  const createPersonEditSchema = () => createPersonFormSchema().extend({
1084
1088
  id: z.string().uuid(),
1085
1089
  department: optionalString(),
1086
- linkedInUrl: z.string().trim().url().optional().or(z.literal("")).transform((val) => val === "" ? void 0 : val).optional(),
1087
- twitterUrl: z.string().trim().url().optional().or(z.literal("")).transform((val) => val === "" ? void 0 : val).optional()
1090
+ primaryEmail: clearableEmailField(),
1091
+ linkedInUrl: clearableUrlField(),
1092
+ twitterUrl: clearableUrlField()
1088
1093
  });
1089
1094
  const buildIndustryLabels = (t) => ({
1090
1095
  placeholder: t("customers.companies.form.industry.placeholder", "Select industry\u2026"),
@@ -1327,9 +1332,20 @@ const createPersonPersonalDataGroups = (t, options) => {
1327
1332
  }
1328
1333
  ];
1329
1334
  };
1335
+ const assignClearable = (payload, key, raw) => {
1336
+ if (raw === null) {
1337
+ payload[key] = null;
1338
+ return;
1339
+ }
1340
+ if (typeof raw !== "string") return;
1341
+ const trimmed = raw.trim();
1342
+ payload[key] = trimmed.length ? trimmed : null;
1343
+ };
1330
1344
  function buildCompanyEditPayload(values, organizationId) {
1331
1345
  const payload = buildCompanyPayload(values, organizationId);
1332
1346
  payload.id = values.id;
1347
+ assignClearable(payload, "primaryEmail", values.primaryEmail);
1348
+ assignClearable(payload, "websiteUrl", values.websiteUrl);
1333
1349
  return payload;
1334
1350
  }
1335
1351
  function buildPersonEditPayload(values, organizationId) {
@@ -1337,10 +1353,9 @@ function buildPersonEditPayload(values, organizationId) {
1337
1353
  payload.id = values.id;
1338
1354
  const department = typeof values.department === "string" ? values.department.trim() : "";
1339
1355
  if (department.length) payload.department = department;
1340
- const linkedInUrl = typeof values.linkedInUrl === "string" ? values.linkedInUrl.trim() : "";
1341
- if (linkedInUrl.length) payload.linkedInUrl = linkedInUrl;
1342
- const twitterUrl = typeof values.twitterUrl === "string" ? values.twitterUrl.trim() : "";
1343
- if (twitterUrl.length) payload.twitterUrl = twitterUrl;
1356
+ assignClearable(payload, "primaryEmail", values.primaryEmail);
1357
+ assignClearable(payload, "linkedInUrl", values.linkedInUrl);
1358
+ assignClearable(payload, "twitterUrl", values.twitterUrl);
1344
1359
  return payload;
1345
1360
  }
1346
1361
  function mapCompanyOverviewToFormValues(overview) {