@endora-commerce/mod-custom-fields 0.0.0-stage → 0.100.0
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/LICENSE +21 -0
- package/README.md +58 -2
- package/dist/admin/api/custom-fields-client.d.ts +10 -0
- package/dist/admin/api/custom-fields-client.d.ts.map +1 -0
- package/dist/admin/api/custom-fields-client.js +11 -0
- package/dist/admin/api/custom-fields-client.js.map +1 -0
- package/dist/admin/index.d.ts +44 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +51 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CustomFieldsPage.d.ts +10 -0
- package/dist/admin/pages/CustomFieldsPage.d.ts.map +1 -0
- package/dist/admin/pages/CustomFieldsPage.js +118 -0
- package/dist/admin/pages/CustomFieldsPage.js.map +1 -0
- package/dist/backend/commands/definition-commands.d.ts +22 -0
- package/dist/backend/commands/definition-commands.d.ts.map +1 -0
- package/dist/backend/commands/definition-commands.js +115 -0
- package/dist/backend/commands/definition-commands.js.map +1 -0
- package/dist/backend/entities/custom-field-definition.entity.d.ts +37 -0
- package/dist/backend/entities/custom-field-definition.entity.d.ts.map +1 -0
- package/dist/backend/entities/custom-field-definition.entity.js +98 -0
- package/dist/backend/entities/custom-field-definition.entity.js.map +1 -0
- package/dist/backend/entities/custom-field-option.entity.d.ts +25 -0
- package/dist/backend/entities/custom-field-option.entity.d.ts.map +1 -0
- package/dist/backend/entities/custom-field-option.entity.js +79 -0
- package/dist/backend/entities/custom-field-option.entity.js.map +1 -0
- package/dist/backend/index.d.ts +79 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +94 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +14 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +179 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/services/custom-field-definition-apply.d.ts +63 -0
- package/dist/backend/services/custom-field-definition-apply.d.ts.map +1 -0
- package/dist/backend/services/custom-field-definition-apply.js +221 -0
- package/dist/backend/services/custom-field-definition-apply.js.map +1 -0
- package/dist/backend/services/custom-field-definition.service.d.ts +68 -0
- package/dist/backend/services/custom-field-definition.service.d.ts.map +1 -0
- package/dist/backend/services/custom-field-definition.service.js +205 -0
- package/dist/backend/services/custom-field-definition.service.js.map +1 -0
- package/dist/backend/services/custom-field-definitions-cache.d.ts +46 -0
- package/dist/backend/services/custom-field-definitions-cache.d.ts.map +1 -0
- package/dist/backend/services/custom-field-definitions-cache.js +82 -0
- package/dist/backend/services/custom-field-definitions-cache.js.map +1 -0
- package/dist/backend/services/custom-field-read-port.d.ts +61 -0
- package/dist/backend/services/custom-field-read-port.d.ts.map +1 -0
- package/dist/backend/services/custom-field-read-port.js +82 -0
- package/dist/backend/services/custom-field-read-port.js.map +1 -0
- package/dist/backend/services/custom-field-registry.d.ts +41 -0
- package/dist/backend/services/custom-field-registry.d.ts.map +1 -0
- package/dist/backend/services/custom-field-registry.js +24 -0
- package/dist/backend/services/custom-field-registry.js.map +1 -0
- package/dist/backend/services/custom-field-value.service.d.ts +56 -0
- package/dist/backend/services/custom-field-value.service.d.ts.map +1 -0
- package/dist/backend/services/custom-field-value.service.js +175 -0
- package/dist/backend/services/custom-field-value.service.js.map +1 -0
- package/dist/manifest.d.ts +177 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +130 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260718T200338_custom_fields_init.d.ts +20 -0
- package/dist/migrations/20260718T200338_custom_fields_init.d.ts.map +1 -0
- package/dist/migrations/20260718T200338_custom_fields_init.js +60 -0
- package/dist/migrations/20260718T200338_custom_fields_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +123 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/i18n/en.json +15 -0
- package/i18n/pl.json +15 -0
- package/package.json +99 -3
- package/tailwind.css +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Endora sp. z o.o. and the Endora Commerce contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,59 @@
|
|
|
1
|
-
|
|
1
|
+
<!-- Generated by `pnpm --filter backend run manifests:generate`. Delete this line to take the file over; a README without it is never regenerated. -->
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# @endora-commerce/mod-custom-fields
|
|
4
|
+
|
|
5
|
+
Entity-agnostic runtime custom fields for core entities.
|
|
6
|
+
|
|
7
|
+
## What this is
|
|
8
|
+
|
|
9
|
+
An **Endora Commerce module package**. Its module id is `custom_fields` — the identity of record in the platform's registries, in its migrations and in its setting, permission and translation codes.
|
|
10
|
+
|
|
11
|
+
A module is not imported by application code. The platform discovers the extension packages an instance has installed, reads the manifest on the root subpath and composes the module from it, so installing this package and starting the instance is the whole integration.
|
|
12
|
+
|
|
13
|
+
## Entry points
|
|
14
|
+
|
|
15
|
+
| Import | Contents |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `@endora-commerce/mod-custom-fields` | the module manifest — its id, version, dependencies, settings and activation |
|
|
18
|
+
| `@endora-commerce/mod-custom-fields/admin` | the Admin UI contribution: the screens, navigation and palette actions it adds |
|
|
19
|
+
| `@endora-commerce/mod-custom-fields/backend` | the composition root the platform calls, with the entities, services, routes and workers it registers |
|
|
20
|
+
| `@endora-commerce/mod-custom-fields/migrations` | the module’s own schema migrations, in the order the platform runs them |
|
|
21
|
+
| `@endora-commerce/mod-custom-fields/ports` | type-only — the port interfaces other modules resolve this one through |
|
|
22
|
+
| `@endora-commerce/mod-custom-fields/tailwind.css` | the Tailwind source declaration for the admin code above |
|
|
23
|
+
|
|
24
|
+
## Depends on
|
|
25
|
+
|
|
26
|
+
Everything below is a **peer** dependency, so the application resolves exactly one copy of each — two copies of a host or of React are a runtime failure rather than a type error. An entry marked *optional* is needed only by the layers that use it.
|
|
27
|
+
|
|
28
|
+
**Endora packages**
|
|
29
|
+
|
|
30
|
+
- `@endora-commerce/admin-kit` — *optional*
|
|
31
|
+
- `@endora-commerce/contracts`
|
|
32
|
+
- `@endora-commerce/platform`
|
|
33
|
+
|
|
34
|
+
**Third-party**
|
|
35
|
+
|
|
36
|
+
- `@mikro-orm/core` ^6
|
|
37
|
+
- `@mikro-orm/migrations` ^6
|
|
38
|
+
- `@mikro-orm/postgresql` ^6
|
|
39
|
+
- `fastify` ^5
|
|
40
|
+
- `ioredis` ^5
|
|
41
|
+
- `lucide-react` ^1 — *optional*
|
|
42
|
+
- `react` ^19 — *optional*
|
|
43
|
+
- `react-router-dom` ^7 — *optional*
|
|
44
|
+
|
|
45
|
+
## What the tarball carries
|
|
46
|
+
|
|
47
|
+
- `dist/` — the compiled JavaScript and its type declarations
|
|
48
|
+
- `i18n/` — the translation bundles this package ships (`en`, `pl`), loaded by the platform
|
|
49
|
+
- `tailwind.css` — the Tailwind source declaration
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm add @endora-commerce/mod-custom-fields
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Licence
|
|
58
|
+
|
|
59
|
+
MIT — the text is in `LICENSE`, beside this file.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CreateCustomFieldDefinitionRequest, CustomFieldDefinitionDto, CustomFieldEntityTypeInfo, SupportedEntityType, UpdateCustomFieldDefinitionRequest } from '@endora-commerce/contracts';
|
|
2
|
+
export declare const customFieldsClient: {
|
|
3
|
+
/** Supported host entity types + host-managed metadata (feature 061). */
|
|
4
|
+
listEntityTypes: () => Promise<CustomFieldEntityTypeInfo[]>;
|
|
5
|
+
list: (entityType?: SupportedEntityType) => Promise<CustomFieldDefinitionDto[]>;
|
|
6
|
+
create: (body: CreateCustomFieldDefinitionRequest) => Promise<CustomFieldDefinitionDto>;
|
|
7
|
+
update: (id: string, patch: UpdateCustomFieldDefinitionRequest) => Promise<CustomFieldDefinitionDto>;
|
|
8
|
+
remove: (id: string) => Promise<unknown>;
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=custom-fields-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-fields-client.d.ts","sourceRoot":"","sources":["../../../src/admin/api/custom-fields-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,kCAAkC,EAClC,wBAAwB,EACxB,yBAAyB,EACzB,mBAAmB,EACnB,kCAAkC,EACnC,MAAM,4BAA4B,CAAC;AAMpC,eAAO,MAAM,kBAAkB;IAC7B,yEAAyE;2BACpD,OAAO,CAAC,yBAAyB,EAAE,CAAC;wBAIrC,mBAAmB,KAAG,OAAO,CAAC,wBAAwB,EAAE,CAAC;mBAM9D,kCAAkC,KAAG,OAAO,CAAC,wBAAwB,CAAC;iBAExE,MAAM,SAAS,kCAAkC,KAAG,OAAO,CAAC,wBAAwB,CAAC;iBAErF,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;CAEvC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { apiClient } from '@endora-commerce/admin-kit/lib';
|
|
2
|
+
const unwrap = (p) => p.then((r) => r.data);
|
|
3
|
+
export const customFieldsClient = {
|
|
4
|
+
/** Supported host entity types + host-managed metadata (feature 061). */
|
|
5
|
+
listEntityTypes: () => unwrap(apiClient.get('/api/v1/admin/custom-fields/entity-types')),
|
|
6
|
+
list: (entityType) => unwrap(apiClient.get(`/api/v1/admin/custom-fields/definitions${entityType ? `?entityType=${entityType}` : ''}`)),
|
|
7
|
+
create: (body) => unwrap(apiClient.post('/api/v1/admin/custom-fields/definitions', body)),
|
|
8
|
+
update: (id, patch) => unwrap(apiClient.patch(`/api/v1/admin/custom-fields/definitions/${id}`, patch)),
|
|
9
|
+
remove: (id) => apiClient.delete(`/api/v1/admin/custom-fields/definitions/${id}`),
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=custom-fields-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-fields-client.js","sourceRoot":"","sources":["../../../src/admin/api/custom-fields-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAW3D,MAAM,MAAM,GAAG,CAAI,CAAmB,EAAc,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,yEAAyE;IACzE,eAAe,EAAE,GAAyC,EAAE,CAC1D,MAAM,CACJ,SAAS,CAAC,GAAG,CAAoC,0CAA0C,CAAC,CAC7F;IACH,IAAI,EAAE,CAAC,UAAgC,EAAuC,EAAE,CAC9E,MAAM,CACJ,SAAS,CAAC,GAAG,CACX,0CAA0C,UAAU,CAAC,CAAC,CAAC,eAAe,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1F,CACF;IACH,MAAM,EAAE,CAAC,IAAwC,EAAqC,EAAE,CACtF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAiC,yCAAyC,EAAE,IAAI,CAAC,CAAC;IACzG,MAAM,EAAE,CAAC,EAAU,EAAE,KAAyC,EAAqC,EAAE,CACnG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAiC,2CAA2C,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;IACjH,MAAM,EAAE,CAAC,EAAU,EAAoB,EAAE,CACvC,SAAS,CAAC,MAAM,CAAC,2CAA2C,EAAE,EAAE,CAAC;CACpE,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `custom_fields`' admin surface — one route and one sidebar entry
|
|
3
|
+
* (feature 091, Phase 4, batch 9;
|
|
4
|
+
* `specs/091-module-owned-admin-surfaces/contracts/admin-contribution.md`).
|
|
5
|
+
*
|
|
6
|
+
* **Its drain was four incoming reaches and it is zero**, and the reason is
|
|
7
|
+
* worth more than the count. `admin-registrations.ts` filed those four under
|
|
8
|
+
* *"reached as components by screens other modules own, which is FR-007's
|
|
9
|
+
* contribution-zone question"*; `admin-component-contribution.md` §9.1 read
|
|
10
|
+
* them instead of counting them and measured the opposite.
|
|
11
|
+
* `CustomFieldValuesPanel`'s props are `(entityType, values, save)` — data in,
|
|
12
|
+
* edited data back — so all four call sites hand it the **host's** own stored
|
|
13
|
+
* bag and the host's own writer, and this module's admin API serves definitions
|
|
14
|
+
* and no values at all. Nothing of the owner's crosses that seam, so it was
|
|
15
|
+
* never a zone contribution; P4e published it into
|
|
16
|
+
* `@endora-commerce/admin-kit/components` and the four consumers name the
|
|
17
|
+
* subpath directly.
|
|
18
|
+
*
|
|
19
|
+
* **The re-export shim P4e left at `admin/src/modules/custom_fields/` goes with
|
|
20
|
+
* this move.** Its own doc block kept it *"for the owner's own screens"*, and
|
|
21
|
+
* after this batch the owner has no screen under `admin/src` — a package screen
|
|
22
|
+
* cannot resolve a `@/` specifier at all. No file in the tree named it; the
|
|
23
|
+
* `admin-kit-identity.test.ts` case that proved it forwarded rather than copied
|
|
24
|
+
* is removed with the file it compared.
|
|
25
|
+
*
|
|
26
|
+
* **The palette action was already declared** (`open-custom-fields` in
|
|
27
|
+
* `manifest.ts`), which is what makes this module's advertisement the one
|
|
28
|
+
* surface the server's effective enabled-set already filtered. This batch adds
|
|
29
|
+
* none and changes none; the off-state test drives it.
|
|
30
|
+
*
|
|
31
|
+
* **`nonDeactivatable`, so the operator axis is the platform's** (`plan.md`
|
|
32
|
+
* Ruling 2). It is Principle XIV's extensibility mechanism — the answer the
|
|
33
|
+
* platform gives to "add a field" instead of a bespoke column — and switching
|
|
34
|
+
* it off would make the values already stored against every host entity
|
|
35
|
+
* unreachable. The permission axis is what this module still has and is what
|
|
36
|
+
* the off-state test drives; the missing axis is read off the manifest rather
|
|
37
|
+
* than skipped.
|
|
38
|
+
*
|
|
39
|
+
* **This entry exports data and nothing else** (R2); the component is a
|
|
40
|
+
* dynamic-import factory (R6).
|
|
41
|
+
*/
|
|
42
|
+
import type { AdminContributions } from '@endora-commerce/admin-kit/contributions';
|
|
43
|
+
export declare const contributions: AdminContributions;
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAiBnF,eAAO,MAAM,aAAa,EAAE,kBAoC3B,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** The module's only route: definitions per entity type, list and create. */
|
|
2
|
+
const ROUTE_PATH = '/custom-fields';
|
|
3
|
+
/**
|
|
4
|
+
* The code the definition reads enforce, and the one the manifest's
|
|
5
|
+
* `open-custom-fields` action already declares — so the palette entry, the
|
|
6
|
+
* sidebar entry and the route agree by construction rather than by three
|
|
7
|
+
* authors remembering.
|
|
8
|
+
*
|
|
9
|
+
* `custom_fields:write` gates the create and delete calls the same screen
|
|
10
|
+
* makes; it is deliberately not the surface gate, because an operator holding
|
|
11
|
+
* only the read code opens the screen and sees the write affordances refuse.
|
|
12
|
+
*/
|
|
13
|
+
const CUSTOM_FIELDS_READ_PERMISSION = 'custom_fields:read';
|
|
14
|
+
export const contributions = {
|
|
15
|
+
routes: [
|
|
16
|
+
{
|
|
17
|
+
path: ROUTE_PATH,
|
|
18
|
+
component: () => import('./pages/CustomFieldsPage.js'),
|
|
19
|
+
requiredPermission: CUSTOM_FIELDS_READ_PERMISSION,
|
|
20
|
+
index: true,
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
nav: [
|
|
24
|
+
{
|
|
25
|
+
to: ROUTE_PATH,
|
|
26
|
+
// Module-relative (R8), resolved in this module's own namespace out of
|
|
27
|
+
// `packages/modules/custom_fields/i18n/`. It was
|
|
28
|
+
// `appShell.nav.customFields` in the shared `_i18n` bundle, one of the
|
|
29
|
+
// four shared files a module author had to edit.
|
|
30
|
+
labelKey: 'nav.customFields.label',
|
|
31
|
+
// The glyph `AppShell.tsx` rendered by hand, and already on
|
|
32
|
+
// `KnownIconNameSchema` — this module's own palette action names it.
|
|
33
|
+
icon: 'Layers',
|
|
34
|
+
// Custom fields extend Organizations, Orders, Customers, Categories and
|
|
35
|
+
// more, so the entry belongs to *System* rather than to any one domain —
|
|
36
|
+
// the placement the host comment carried and this declaration keeps.
|
|
37
|
+
section: 'system',
|
|
38
|
+
// The hand-written position in *System* times a hundred, which is batch
|
|
39
|
+
// four's convention: `/custom-fields` was the fifth row of that section
|
|
40
|
+
// before the drain began, between `/catalog/bulk-operations` and
|
|
41
|
+
// `/audit-log`. The weights already declared there — `admin_users` 200,
|
|
42
|
+
// `admin_roles` 300, `audit_logs` 600, `api_keys` 700, `webhooks` 800 —
|
|
43
|
+
// leave 500 exactly where this row sat among them. *System* still holds
|
|
44
|
+
// host-declared entries, so `composeNav` appends every registry row after
|
|
45
|
+
// all of them whatever the weight says.
|
|
46
|
+
weight: 500,
|
|
47
|
+
requiredPermission: CUSTOM_FIELDS_READ_PERMISSION,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/admin/index.ts"],"names":[],"mappings":"AA2CA,6EAA6E;AAC7E,MAAM,UAAU,GAAG,gBAAgB,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAE3D,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,MAAM,EAAE;QACN;YACE,IAAI,EAAE,UAAU;YAChB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,6BAA6B,CAAC;YACtD,kBAAkB,EAAE,6BAA6B;YACjD,KAAK,EAAE,IAAI;SACZ;KACF;IACD,GAAG,EAAE;QACH;YACE,EAAE,EAAE,UAAU;YACd,uEAAuE;YACvE,iDAAiD;YACjD,uEAAuE;YACvE,iDAAiD;YACjD,QAAQ,EAAE,wBAAwB;YAClC,4DAA4D;YAC5D,qEAAqE;YACrE,IAAI,EAAE,QAAQ;YACd,wEAAwE;YACxE,yEAAyE;YACzE,qEAAqE;YACrE,OAAO,EAAE,QAAQ;YACjB,wEAAwE;YACxE,wEAAwE;YACxE,iEAAiE;YACjE,wEAAwE;YACxE,wEAAwE;YACxE,wEAAwE;YACxE,0EAA0E;YAC1E,wCAAwC;YACxC,MAAM,EAAE,GAAG;YACX,kBAAkB,EAAE,6BAA6B;SAClD;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/** Custom-field definition management (feature 055). List + create per entity type. */
|
|
3
|
+
export declare function CustomFieldsPage(): ReactNode;
|
|
4
|
+
/**
|
|
5
|
+
* The registry loads a route component through a dynamic-import factory and
|
|
6
|
+
* reads its default export (feature 091, FR-013). The named export stays: it is
|
|
7
|
+
* the spelling this module's own code and its tests use.
|
|
8
|
+
*/
|
|
9
|
+
export default CustomFieldsPage;
|
|
10
|
+
//# sourceMappingURL=CustomFieldsPage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldsPage.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/CustomFieldsPage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA+BzE,uFAAuF;AACvF,wBAAgB,gBAAgB,IAAI,SAAS,CA+O5C;AAED;;;;GAIG;AACH,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { Link } from 'react-router-dom';
|
|
4
|
+
import { Trash2 } from 'lucide-react';
|
|
5
|
+
import { ApiError } from '@endora-commerce/admin-kit/lib';
|
|
6
|
+
import { customFieldsClient } from '../api/custom-fields-client.js';
|
|
7
|
+
import { Alert, AlertDescription, Badge, Button, Card, CardContent, CardHeader, CardTitle, Label, PageHeader, Select, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@endora-commerce/admin-kit/ui';
|
|
8
|
+
import { useTranslation } from '@endora-commerce/admin-kit/i18n';
|
|
9
|
+
/** Pre-API fallback so the page renders before the entity-types fetch resolves. */
|
|
10
|
+
const FALLBACK_ENTITY_TYPES = ['category', 'order', 'organization', 'customer', 'quote_request'].map((entityType) => ({
|
|
11
|
+
entityType,
|
|
12
|
+
labelKey: `customFields.entity.${entityType === 'quote_request' ? 'quoteRequest' : entityType}`,
|
|
13
|
+
}));
|
|
14
|
+
const VALUE_TYPES = [
|
|
15
|
+
'text',
|
|
16
|
+
'number',
|
|
17
|
+
'boolean',
|
|
18
|
+
'date',
|
|
19
|
+
'select',
|
|
20
|
+
'multiselect',
|
|
21
|
+
];
|
|
22
|
+
const SELECT_TYPES = new Set(['select', 'multiselect']);
|
|
23
|
+
/** Custom-field definition management (feature 055). List + create per entity type. */
|
|
24
|
+
export function CustomFieldsPage() {
|
|
25
|
+
// One namespace, this module's own (feature 091, R8). The screen used to
|
|
26
|
+
// read `customFields.title` and `customFields.description` out of the shared
|
|
27
|
+
// `core` bundle beside four keys of its own; both were already written in
|
|
28
|
+
// this module's `i18n/{en,pl}.json`, so the `core` reader was a second copy
|
|
29
|
+
// of two strings and nothing else. `customFields.title` and
|
|
30
|
+
// `customFields.save` stay in `_i18n` because
|
|
31
|
+
// `@endora-commerce/admin-kit`'s `CustomFieldValuesPanel` renders them, and a
|
|
32
|
+
// kit component reads the shared namespace by construction.
|
|
33
|
+
const t = useTranslation('custom_fields');
|
|
34
|
+
const [entityTypes, setEntityTypes] = useState(FALLBACK_ENTITY_TYPES);
|
|
35
|
+
const [entityType, setEntityType] = useState('organization');
|
|
36
|
+
const [rows, setRows] = useState([]);
|
|
37
|
+
const [error, setError] = useState(null);
|
|
38
|
+
// Host-managed marker for the selected type (feature 061): definitions are
|
|
39
|
+
// rendered read-only with a link to the owning module's surface.
|
|
40
|
+
const managedBy = entityTypes.find((i) => i.entityType === entityType)?.managedBy;
|
|
41
|
+
// New-definition form state.
|
|
42
|
+
const [key, setKey] = useState('');
|
|
43
|
+
const [label, setLabel] = useState('');
|
|
44
|
+
const [valueType, setValueType] = useState('text');
|
|
45
|
+
const [required, setRequired] = useState(false);
|
|
46
|
+
const [options, setOptions] = useState('');
|
|
47
|
+
const refresh = useCallback(async () => {
|
|
48
|
+
setError(null);
|
|
49
|
+
try {
|
|
50
|
+
setRows(await customFieldsClient.list(entityType));
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
setError(err instanceof ApiError ? err.envelope.error.message : 'Failed to load.');
|
|
54
|
+
}
|
|
55
|
+
}, [entityType]);
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
void refresh();
|
|
58
|
+
}, [refresh]);
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
void (async () => {
|
|
61
|
+
try {
|
|
62
|
+
setEntityTypes(await customFieldsClient.listEntityTypes());
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Keep the static fallback; the definitions list still works.
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
}, []);
|
|
69
|
+
const run = useCallback(async (fn) => {
|
|
70
|
+
setError(null);
|
|
71
|
+
try {
|
|
72
|
+
await fn();
|
|
73
|
+
await refresh();
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
setError(err instanceof ApiError ? err.envelope.error.message : 'Action failed.');
|
|
77
|
+
}
|
|
78
|
+
}, [refresh]);
|
|
79
|
+
const submit = () => {
|
|
80
|
+
const opts = SELECT_TYPES.has(valueType)
|
|
81
|
+
? options
|
|
82
|
+
.split(',')
|
|
83
|
+
.map((s) => s.trim())
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.map((value, i) => ({
|
|
86
|
+
value,
|
|
87
|
+
label: {},
|
|
88
|
+
labelDefault: value,
|
|
89
|
+
isDefault: false,
|
|
90
|
+
sortOrder: i,
|
|
91
|
+
}))
|
|
92
|
+
: [];
|
|
93
|
+
void run(async () => {
|
|
94
|
+
await customFieldsClient.create({
|
|
95
|
+
entityType,
|
|
96
|
+
key: key.trim(),
|
|
97
|
+
label: label.trim() ? { en: label.trim() } : {},
|
|
98
|
+
labelDefault: label.trim() || key.trim(),
|
|
99
|
+
valueType,
|
|
100
|
+
required,
|
|
101
|
+
sortOrder: rows.length,
|
|
102
|
+
config: {},
|
|
103
|
+
options: opts,
|
|
104
|
+
});
|
|
105
|
+
setKey('');
|
|
106
|
+
setLabel('');
|
|
107
|
+
setOptions('');
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(PageHeader, { title: t('customFields.title'), description: t('customFields.description') }), error && (_jsx(Alert, { variant: "destructive", children: _jsx(AlertDescription, { children: error }) })), _jsx("div", { className: "flex items-end gap-2", children: _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "entity", children: "Entity" }), _jsx(Select, { id: "entity", value: entityType, onChange: (e) => setEntityType(e.target.value), children: entityTypes.map((info) => (_jsx("option", { value: info.entityType, children: t(info.labelKey) }, info.entityType))) })] }) }), managedBy && (_jsx(Alert, { children: _jsxs(AlertDescription, { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { children: t('customFields.managedBy.notice') }), _jsx(Link, { to: managedBy.route, className: "font-medium underline underline-offset-4", children: t(managedBy.labelKey) })] }) })), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Fields" }) }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Key" }), _jsx(TableHead, { children: "Label" }), _jsx(TableHead, { children: "Type" }), _jsx(TableHead, { children: "Required" }), _jsx(TableHead, { children: "Options" }), _jsx(TableHead, {})] }) }), _jsx(TableBody, { children: rows.map((d) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-mono text-xs", children: d.key }), _jsx(TableCell, { children: d.label['en'] ?? d.labelDefault }), _jsx(TableCell, { children: d.valueType }), _jsx(TableCell, { children: _jsx(Badge, { variant: d.required ? 'default' : 'secondary', children: d.required ? 'required' : 'optional' }) }), _jsx(TableCell, { className: "text-xs", children: d.options.map((o) => o.value).join(', ') || '—' }), _jsx(TableCell, { children: !managedBy && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => void run(() => customFieldsClient.remove(d.id)), children: _jsx(Trash2, { className: "size-4" }) })) })] }, d.id))) })] }), !managedBy && (_jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "key", children: "Key" }), _jsx("input", { id: "key", className: "h-9 rounded-md border border-input bg-background px-3 text-sm", value: key, onChange: (e) => setKey(e.target.value), placeholder: "po_number" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "label", children: "Label (EN)" }), _jsx("input", { id: "label", className: "h-9 rounded-md border border-input bg-background px-3 text-sm", value: label, onChange: (e) => setLabel(e.target.value) })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "vt", children: "Type" }), _jsx(Select, { id: "vt", value: valueType, onChange: (e) => setValueType(e.target.value), children: VALUE_TYPES.map((vt) => (_jsx("option", { value: vt, children: vt }, vt))) })] }), SELECT_TYPES.has(valueType) && (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "opts", children: "Options (comma-separated)" }), _jsx("input", { id: "opts", className: "h-9 rounded-md border border-input bg-background px-3 text-sm", value: options, onChange: (e) => setOptions(e.target.value), placeholder: "gold, silver" })] })), _jsxs("label", { className: "flex items-center gap-1 text-sm", children: [_jsx("input", { type: "checkbox", checked: required, onChange: (e) => setRequired(e.target.checked) }), "Required"] }), _jsx(Button, { size: "sm", disabled: !key.trim(), onClick: submit, children: "Add field" })] }))] })] })] }));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The registry loads a route component through a dynamic-import factory and
|
|
114
|
+
* reads its default export (feature 091, FR-013). The named export stays: it is
|
|
115
|
+
* the spelling this module's own code and its tests use.
|
|
116
|
+
*/
|
|
117
|
+
export default CustomFieldsPage;
|
|
118
|
+
//# sourceMappingURL=CustomFieldsPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomFieldsPage.js","sourceRoot":"","sources":["../../../src/admin/pages/CustomFieldsPage.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CreateCustomFieldDefinitionRequest, CustomFieldOptionDto, UpdateCustomFieldDefinitionRequest } from '@endora-commerce/contracts';
|
|
2
|
+
import type { Command } from '@endora-commerce/platform/commands';
|
|
3
|
+
import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
|
|
4
|
+
import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
|
|
5
|
+
import { type DefinitionChangeProbes } from '../services/custom-field-definition-apply.js';
|
|
6
|
+
/** Create a definition (+ its options for select types). */
|
|
7
|
+
export declare function createDefinitionCommand(input: CreateCustomFieldDefinitionRequest): Command<CustomFieldDefinition>;
|
|
8
|
+
/** Patch a definition's scalar/label/config fields. Options are managed via option sub-commands. */
|
|
9
|
+
export declare function updateDefinitionCommand(id: string, patch: UpdateCustomFieldDefinitionRequest, probes?: DefinitionChangeProbes): Command<CustomFieldDefinition>;
|
|
10
|
+
/** Delete a definition. Options cascade; host values are retained dormant (FR-010). */
|
|
11
|
+
export declare function deleteDefinitionCommand(id: string): Command<{
|
|
12
|
+
id: string;
|
|
13
|
+
}>;
|
|
14
|
+
/** Add one option to a select/multiselect definition. */
|
|
15
|
+
export declare function createOptionCommand(definitionId: string, input: CustomFieldOptionDto): Command<CustomFieldOption>;
|
|
16
|
+
/** Patch one option's label/default/ordering. */
|
|
17
|
+
export declare function updateOptionCommand(definitionId: string, optionId: string, patch: Partial<Pick<CustomFieldOptionDto, 'label' | 'labelDefault' | 'isDefault' | 'sortOrder'>>): Command<CustomFieldOption>;
|
|
18
|
+
/** Delete one option (the in-use guard also runs in the service before dispatch). */
|
|
19
|
+
export declare function deleteOptionCommand(definitionId: string, optionId: string, probes?: DefinitionChangeProbes): Command<{
|
|
20
|
+
id: string;
|
|
21
|
+
}>;
|
|
22
|
+
//# sourceMappingURL=definition-commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition-commands.d.ts","sourceRoot":"","sources":["../../../src/backend/commands/definition-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kCAAkC,EAClC,oBAAoB,EACpB,kCAAkC,EACnC,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAOL,KAAK,sBAAsB,EAC5B,MAAM,8CAA8C,CAAC;AA2BtD,4DAA4D;AAC5D,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,kCAAkC,GACxC,OAAO,CAAC,qBAAqB,CAAC,CAUhC;AAED,oGAAoG;AACpG,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,kCAAkC,EACzC,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAchC;AAED,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAc3E;AAED,yDAAyD;AACzD,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAU5B;AAED,iDAAiD;AACjD,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,cAAc,GAAG,WAAW,GAAG,WAAW,CAAC,CAAC,GAC/F,OAAO,CAAC,iBAAiB,CAAC,CAc5B;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAczB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { CustomFieldDefinition } from '../entities/custom-field-definition.entity.js';
|
|
2
|
+
import { CustomFieldOption } from '../entities/custom-field-option.entity.js';
|
|
3
|
+
import { applyCreateDefinition, applyCreateOption, applyDeleteDefinition, applyDeleteOption, applyUpdateDefinition, applyUpdateOption, } from '../services/custom-field-definition-apply.js';
|
|
4
|
+
/**
|
|
5
|
+
* Command factories for custom-field definition/option mutations (feature 055,
|
|
6
|
+
* Principle XIII). Each returns a named {@link Command} the {@link CommandBus}
|
|
7
|
+
* runs co-transactionally — the bus writes exactly one audit entry, so the
|
|
8
|
+
* module never calls the audit writer by hand.
|
|
9
|
+
*
|
|
10
|
+
* The command bodies delegate the entity writes + invariants to the shared
|
|
11
|
+
* apply functions (feature 061) so the public CRUD and the host apply seam are
|
|
12
|
+
* a single write path.
|
|
13
|
+
*/
|
|
14
|
+
function snapshotDefinition(def) {
|
|
15
|
+
return {
|
|
16
|
+
id: def.id,
|
|
17
|
+
entityType: def.entityType,
|
|
18
|
+
key: def.key,
|
|
19
|
+
label: def.label,
|
|
20
|
+
labelDefault: def.labelDefault,
|
|
21
|
+
valueType: def.valueType,
|
|
22
|
+
required: def.required,
|
|
23
|
+
sortOrder: def.sortOrder,
|
|
24
|
+
config: def.config,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Create a definition (+ its options for select types). */
|
|
28
|
+
export function createDefinitionCommand(input) {
|
|
29
|
+
return {
|
|
30
|
+
action: 'custom_fields.definition.created',
|
|
31
|
+
objectType: 'custom_field_definition',
|
|
32
|
+
objectId: 'new',
|
|
33
|
+
run: async ({ em }) => {
|
|
34
|
+
const def = await applyCreateDefinition(em, input);
|
|
35
|
+
return { result: def, after: snapshotDefinition(def) };
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Patch a definition's scalar/label/config fields. Options are managed via option sub-commands. */
|
|
40
|
+
export function updateDefinitionCommand(id, patch, probes) {
|
|
41
|
+
return {
|
|
42
|
+
action: 'custom_fields.definition.updated',
|
|
43
|
+
objectType: 'custom_field_definition',
|
|
44
|
+
objectId: id,
|
|
45
|
+
capture: async ({ em }) => {
|
|
46
|
+
const def = await em.findOne(CustomFieldDefinition, { id });
|
|
47
|
+
return def ? snapshotDefinition(def) : null;
|
|
48
|
+
},
|
|
49
|
+
run: async ({ em }) => {
|
|
50
|
+
const def = await applyUpdateDefinition(em, id, patch, probes);
|
|
51
|
+
return { result: def, after: snapshotDefinition(def) };
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Delete a definition. Options cascade; host values are retained dormant (FR-010). */
|
|
56
|
+
export function deleteDefinitionCommand(id) {
|
|
57
|
+
return {
|
|
58
|
+
action: 'custom_fields.definition.deleted',
|
|
59
|
+
objectType: 'custom_field_definition',
|
|
60
|
+
objectId: id,
|
|
61
|
+
capture: async ({ em }) => {
|
|
62
|
+
const def = await em.findOne(CustomFieldDefinition, { id });
|
|
63
|
+
return def ? snapshotDefinition(def) : null;
|
|
64
|
+
},
|
|
65
|
+
run: async ({ em }) => {
|
|
66
|
+
await applyDeleteDefinition(em, id);
|
|
67
|
+
return { result: { id }, after: null };
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Add one option to a select/multiselect definition. */
|
|
72
|
+
export function createOptionCommand(definitionId, input) {
|
|
73
|
+
return {
|
|
74
|
+
action: 'custom_fields.option.created',
|
|
75
|
+
objectType: 'custom_field_option',
|
|
76
|
+
objectId: 'new',
|
|
77
|
+
run: async ({ em }) => {
|
|
78
|
+
const opt = await applyCreateOption(em, definitionId, input);
|
|
79
|
+
return { result: opt, after: { id: opt.id, definitionId, value: opt.value } };
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Patch one option's label/default/ordering. */
|
|
84
|
+
export function updateOptionCommand(definitionId, optionId, patch) {
|
|
85
|
+
return {
|
|
86
|
+
action: 'custom_fields.option.updated',
|
|
87
|
+
objectType: 'custom_field_option',
|
|
88
|
+
objectId: optionId,
|
|
89
|
+
capture: async ({ em }) => {
|
|
90
|
+
const o = await em.findOne(CustomFieldOption, { id: optionId });
|
|
91
|
+
return o ? { id: o.id, value: o.value, labelDefault: o.labelDefault } : null;
|
|
92
|
+
},
|
|
93
|
+
run: async ({ em }) => {
|
|
94
|
+
const o = await applyUpdateOption(em, definitionId, optionId, patch);
|
|
95
|
+
return { result: o, after: { id: o.id, value: o.value, labelDefault: o.labelDefault } };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Delete one option (the in-use guard also runs in the service before dispatch). */
|
|
100
|
+
export function deleteOptionCommand(definitionId, optionId, probes) {
|
|
101
|
+
return {
|
|
102
|
+
action: 'custom_fields.option.deleted',
|
|
103
|
+
objectType: 'custom_field_option',
|
|
104
|
+
objectId: optionId,
|
|
105
|
+
capture: async ({ em }) => {
|
|
106
|
+
const o = await em.findOne(CustomFieldOption, { id: optionId });
|
|
107
|
+
return o ? { id: o.id, value: o.value } : null;
|
|
108
|
+
},
|
|
109
|
+
run: async ({ em }) => {
|
|
110
|
+
await applyDeleteOption(em, definitionId, optionId, probes);
|
|
111
|
+
return { result: { id: optionId }, after: null };
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=definition-commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definition-commands.js","sourceRoot":"","sources":["../../../src/backend/commands/definition-commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OptionalProps } from '@mikro-orm/core';
|
|
2
|
+
import type { CustomFieldValueType, SupportedEntityType } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* CustomFieldDefinition — an operator-defined field for one host entity type
|
|
5
|
+
* (feature 055, FR-001/FR-002).
|
|
6
|
+
*
|
|
7
|
+
* Entity-agnostic by construction (FR-006): it carries a `key`, a per-locale
|
|
8
|
+
* `label` + `labelDefault`, a `valueType`, a `required` flag, ordering, and an
|
|
9
|
+
* opaque `config` JSONB for host-capability opt-in that the generic core stores
|
|
10
|
+
* but never interprets. It deliberately omits the catalog-only capability flags
|
|
11
|
+
* that made the product-attribute registry un-reusable — those stay behind host
|
|
12
|
+
* extension points. Platform-global (`@GlobalEntity`), like the product-attribute
|
|
13
|
+
* registry; the VALUES live on the host row's JSONB column and inherit that
|
|
14
|
+
* host's tenant scope.
|
|
15
|
+
*
|
|
16
|
+
* Unique on `(entity_type, key)` so a key is a clean identifier within an
|
|
17
|
+
* entity type. Options for `select` / `multiselect` live on
|
|
18
|
+
* `custom_field_options` (FK ON DELETE CASCADE).
|
|
19
|
+
*/
|
|
20
|
+
export declare class CustomFieldDefinition {
|
|
21
|
+
[OptionalProps]?: 'id' | 'createdAt' | 'updatedAt' | 'required' | 'sortOrder' | 'label' | 'config';
|
|
22
|
+
id: string;
|
|
23
|
+
entityType: SupportedEntityType;
|
|
24
|
+
key: string;
|
|
25
|
+
/** Per-locale label map (BCP-47-keyed). Empty `{}` is allowed when only `labelDefault` is filled. */
|
|
26
|
+
label: Record<string, string>;
|
|
27
|
+
/** Fallback label used when the active locale is missing from `label` (US1 scenario 4). */
|
|
28
|
+
labelDefault: string;
|
|
29
|
+
valueType: CustomFieldValueType;
|
|
30
|
+
required: boolean;
|
|
31
|
+
sortOrder: number;
|
|
32
|
+
/** Opaque host-capability flags (FR-006). The core stores but never reads this for meaning. */
|
|
33
|
+
config: Record<string, unknown>;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
updatedAt: Date;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=custom-field-definition.entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-field-definition.entity.d.ts","sourceRoot":"","sources":["../../../src/backend/entities/custom-field-definition.entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,aAAa,EAAgC,MAAM,iBAAiB,CAAC;AAG7F,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAE5F;;;;;;;;;;;;;;;;GAgBG;AACH,qBAGa,qBAAqB;IAChC,CAAC,aAAa,CAAC,CAAC,EACZ,IAAI,GACJ,WAAW,GACX,WAAW,GACX,UAAU,GACV,WAAW,GACX,OAAO,GACP,QAAQ,CAAC;IAGb,EAAE,EAAE,MAAM,CAAgB;IAI1B,UAAU,EAAG,mBAAmB,CAAC;IAGjC,GAAG,EAAG,MAAM,CAAC;IAEb,qGAAqG;IAErG,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IAEnC,2FAA2F;IAE3F,YAAY,EAAG,MAAM,CAAC;IAGtB,SAAS,EAAG,oBAAoB,CAAC;IAGjC,QAAQ,EAAE,OAAO,CAAS;IAG1B,SAAS,EAAE,MAAM,CAAK;IAEtB,+FAA+F;IAE/F,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAGrC,SAAS,EAAE,IAAI,CAAc;IAG7B,SAAS,EAAE,IAAI,CAAc;CAC9B"}
|