@api-client/ui 0.6.7 → 0.6.9

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 (25) hide show
  1. package/build/src/modeling/dialogs/CountryTaxonomy.d.ts +24 -0
  2. package/build/src/modeling/dialogs/CountryTaxonomy.d.ts.map +1 -0
  3. package/build/src/modeling/dialogs/CountryTaxonomy.js +174 -0
  4. package/build/src/modeling/dialogs/CountryTaxonomy.js.map +1 -0
  5. package/build/src/modeling/dialogs/PostalCodeTaxonomy.d.ts +24 -0
  6. package/build/src/modeling/dialogs/PostalCodeTaxonomy.d.ts.map +1 -0
  7. package/build/src/modeling/dialogs/PostalCodeTaxonomy.js +167 -0
  8. package/build/src/modeling/dialogs/PostalCodeTaxonomy.js.map +1 -0
  9. package/build/src/modeling/internals/DomainAutoFieldsDialog.d.ts.map +1 -1
  10. package/build/src/modeling/internals/DomainAutoFieldsDialog.js +41 -16
  11. package/build/src/modeling/internals/DomainAutoFieldsDialog.js.map +1 -1
  12. package/build/src/modeling/internals/DomainPropertyEditor.d.ts.map +1 -1
  13. package/build/src/modeling/internals/DomainPropertyEditor.js +8 -0
  14. package/build/src/modeling/internals/DomainPropertyEditor.js.map +1 -1
  15. package/build/src/modeling/internals/DomainTemplateBrowser.d.ts +10 -6
  16. package/build/src/modeling/internals/DomainTemplateBrowser.d.ts.map +1 -1
  17. package/build/src/modeling/internals/DomainTemplateBrowser.js +50 -7
  18. package/build/src/modeling/internals/DomainTemplateBrowser.js.map +1 -1
  19. package/build/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +2 -1
  21. package/src/modeling/dialogs/CountryTaxonomy.ts +176 -0
  22. package/src/modeling/dialogs/PostalCodeTaxonomy.ts +169 -0
  23. package/src/modeling/internals/DomainAutoFieldsDialog.ts +41 -16
  24. package/src/modeling/internals/DomainPropertyEditor.ts +6 -0
  25. package/src/modeling/internals/DomainTemplateBrowser.ts +61 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "Internal UI component library for the API Client ecosystem.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "build/src/index.js",
@@ -195,6 +195,7 @@
195
195
  "dependencies": {
196
196
  "@adonisjs/transmit-client": "^1.0.0",
197
197
  "@api-client/core": "^0.18.0",
198
+ "@api-client/domain-templates": "^0.1.2",
198
199
  "@api-client/graph": "^0.3.6",
199
200
  "@api-client/json": "^0.2.0",
200
201
  "@codemirror/autocomplete": "^6.18.6",
@@ -0,0 +1,176 @@
1
+ import type { DomainProperty } from '@api-client/core/modeling/DomainProperty.js'
2
+ import { type CountryConfig, DEFAULT_COUNTRY_CONFIG } from '@api-client/core/modeling/definitions/Country.js'
3
+ import { bound } from '../../decorators/bound.js'
4
+ import { html, TemplateResult } from 'lit'
5
+ import { SemanticType } from '@api-client/core/modeling/Semantics.js'
6
+ import { renderHelp } from '../../help/render.js'
7
+ import { TaxonomyDialog } from './TaxonomyDialog.js'
8
+
9
+ import '../../md/chip/ui-chip-set.js'
10
+ import '../../md/chip/ui-chip.js'
11
+
12
+ /**
13
+ * A class that generates a dialog that allows the user to configure the taxonomy of a Country annotated association.
14
+ */
15
+ export class CountryTaxonomy extends TaxonomyDialog<CountryConfig> {
16
+ static override title = `Configure Country Taxonomy`
17
+
18
+ constructor(public domainElement: DomainProperty) {
19
+ let config: CountryConfig
20
+ const semantic = domainElement.semantics.find((s) => s.id === SemanticType.Country)
21
+ if (semantic && semantic.config) {
22
+ config = structuredClone(semantic.config)
23
+ } else {
24
+ config = {}
25
+ }
26
+ super(config)
27
+ }
28
+
29
+ protected override formCallback(event: Event): Promise<void> {
30
+ const semantic = this.domainElement.semantics.find((s) => s.id === SemanticType.Country)
31
+ if (semantic) {
32
+ semantic.config = this.config
33
+ } else {
34
+ this.domainElement.semantics.push({
35
+ id: SemanticType.Country,
36
+ config: this.config,
37
+ })
38
+ }
39
+ this.domainElement.domain.notifyChange()
40
+ return super.formCallback(event)
41
+ }
42
+
43
+ protected handleAllowedCountriesChange(event: Event): void {
44
+ const input = event.target as HTMLInputElement
45
+ const value = input.value.trim()
46
+ if (value) {
47
+ const countries = this.config?.allowedCountries ?? DEFAULT_COUNTRY_CONFIG.allowedCountries ?? []
48
+ if (!countries.includes(value)) {
49
+ countries.push(value)
50
+ this.config = { ...this.config, allowedCountries: countries }
51
+ this.requestUpdate()
52
+ }
53
+ input.value = ''
54
+ }
55
+ }
56
+
57
+ protected handleRemoveAllowedCountry(event: Event): void {
58
+ const chip = event.currentTarget as HTMLElement
59
+ const index = parseInt(chip.dataset.index || '-1', 10)
60
+ if (index >= 0 && this.config?.allowedCountries) {
61
+ this.config.allowedCountries.splice(index, 1)
62
+ this.requestUpdate()
63
+ }
64
+ }
65
+
66
+ protected handleDisallowedCountriesChange(event: Event): void {
67
+ const input = event.target as HTMLInputElement
68
+ const value = input.value.trim()
69
+ if (value) {
70
+ const countries = this.config?.disallowedCountries ?? DEFAULT_COUNTRY_CONFIG.disallowedCountries ?? []
71
+ if (!countries.includes(value)) {
72
+ countries.push(value)
73
+ this.config = { ...this.config, disallowedCountries: countries }
74
+ this.requestUpdate()
75
+ }
76
+ input.value = ''
77
+ }
78
+ }
79
+
80
+ protected handleRemoveDisallowedCountry(event: Event): void {
81
+ const chip = event.currentTarget as HTMLElement
82
+ const index = parseInt(chip.dataset.index || '-1', 10)
83
+ if (index >= 0 && this.config?.disallowedCountries) {
84
+ this.config.disallowedCountries.splice(index, 1)
85
+ this.requestUpdate()
86
+ }
87
+ }
88
+
89
+ @bound
90
+ render(): TemplateResult {
91
+ return html`
92
+ <div class="input-group">
93
+ ${this.renderFormatSelect()} ${this.renderAllowedCountries()} ${this.renderDisallowedCountries()}
94
+ </div>
95
+ `
96
+ }
97
+
98
+ protected renderFormatSelect(): TemplateResult {
99
+ const value = this.config?.format ?? DEFAULT_COUNTRY_CONFIG.format
100
+ return html`
101
+ <div class="row">
102
+ <ui-select
103
+ label="Country format"
104
+ class="input"
105
+ name="format"
106
+ .value="${value as string}"
107
+ @change="${this.handleSelectChange}"
108
+ >
109
+ <ui-option value="ISO_3166_Alpha_2">
110
+ <div>ISO 3166 Alpha-2 (Recommended)</div>
111
+ <div slot="supporting-text">Store as ISO 3166 Alpha-2 (e.g., US)</div>
112
+ </ui-option>
113
+ <ui-option value="ISO_3166_Alpha_3">
114
+ <div>ISO 3166 Alpha-3</div>
115
+ <div slot="supporting-text">Store as ISO 3166 Alpha-3 (e.g., USA)</div>
116
+ </ui-option>
117
+ <ui-option value="Name">
118
+ <div>Name</div>
119
+ <div slot="supporting-text">Store as country name (e.g., United States)</div>
120
+ </ui-option>
121
+ </ui-select>
122
+ ${renderHelp('taxonomy', 'country:format')}
123
+ </div>
124
+ `
125
+ }
126
+
127
+ protected renderAllowedCountries(): TemplateResult {
128
+ const value = this.config?.allowedCountries ?? DEFAULT_COUNTRY_CONFIG.allowedCountries
129
+ const chips = (value || []).map(
130
+ (country, index) =>
131
+ html`<ui-chip type="input" removable data-index="${index}" @remove="${this.handleRemoveAllowedCountry}"
132
+ >${country}</ui-chip
133
+ >`
134
+ )
135
+ return html`
136
+ <div class="row input-row">
137
+ <div class="input chips-input">
138
+ <ui-outlined-text-field
139
+ class="input"
140
+ name="allowedCountries"
141
+ label="Allowed Countries"
142
+ @change="${this.handleAllowedCountriesChange}"
143
+ supportingText="Add a country and press Enter to add it to the list. Use the selected format."
144
+ ></ui-outlined-text-field>
145
+ <ui-chip-set>${chips}</ui-chip-set>
146
+ </div>
147
+ ${renderHelp('taxonomy', 'country:allowedCountries')}
148
+ </div>
149
+ `
150
+ }
151
+
152
+ protected renderDisallowedCountries(): TemplateResult {
153
+ const value = this.config?.disallowedCountries ?? DEFAULT_COUNTRY_CONFIG.disallowedCountries
154
+ const chips = (value || []).map(
155
+ (country, index) =>
156
+ html`<ui-chip type="input" removable data-index="${index}" @remove="${this.handleRemoveDisallowedCountry}"
157
+ >${country}</ui-chip
158
+ >`
159
+ )
160
+ return html`
161
+ <div class="row input-row">
162
+ <div class="input chips-input">
163
+ <ui-outlined-text-field
164
+ class="input"
165
+ name="disallowedCountries"
166
+ label="Disallowed Countries"
167
+ @change="${this.handleDisallowedCountriesChange}"
168
+ supportingText="Add a country and press Enter to add it to the list. Use the selected format."
169
+ ></ui-outlined-text-field>
170
+ <ui-chip-set>${chips}</ui-chip-set>
171
+ </div>
172
+ ${renderHelp('taxonomy', 'country:disallowedCountries')}
173
+ </div>
174
+ `
175
+ }
176
+ }
@@ -0,0 +1,169 @@
1
+ import type { DomainProperty } from '@api-client/core/modeling/DomainProperty.js'
2
+ import { type PostalCodeConfig, DEFAULT_POSTAL_CODE_CONFIG } from '@api-client/core/modeling/definitions/PostalCode.js'
3
+ import { bound } from '../../decorators/bound.js'
4
+ import { html, TemplateResult } from 'lit'
5
+ import { SemanticType } from '@api-client/core/modeling/Semantics.js'
6
+ import { renderHelp } from '../../help/render.js'
7
+ import { TaxonomyDialog } from './TaxonomyDialog.js'
8
+
9
+ import '../../md/chip/ui-chip-set.js'
10
+ import '../../md/chip/ui-chip.js'
11
+
12
+ /**
13
+ * A class that generates a dialog that allows the user to configure the taxonomy of a PostalCode annotated association.
14
+ */
15
+ export class PostalCodeTaxonomy extends TaxonomyDialog<PostalCodeConfig> {
16
+ static override title = `Configure Postal Code Taxonomy`
17
+
18
+ constructor(public domainElement: DomainProperty) {
19
+ let config: PostalCodeConfig
20
+ const semantic = domainElement.semantics.find((s) => s.id === SemanticType.PostalCode)
21
+ if (semantic && semantic.config) {
22
+ config = structuredClone(semantic.config)
23
+ } else {
24
+ config = {}
25
+ }
26
+ super(config)
27
+ }
28
+
29
+ protected override formCallback(event: Event): Promise<void> {
30
+ const semantic = this.domainElement.semantics.find((s) => s.id === SemanticType.PostalCode)
31
+ if (semantic) {
32
+ semantic.config = this.config
33
+ } else {
34
+ this.domainElement.semantics.push({
35
+ id: SemanticType.PostalCode,
36
+ config: this.config,
37
+ })
38
+ }
39
+ this.domainElement.domain.notifyChange()
40
+ return super.formCallback(event)
41
+ }
42
+
43
+ protected handleAllowedPostalCodesChange(event: Event): void {
44
+ const input = event.target as HTMLInputElement
45
+ const value = input.value.trim()
46
+ if (value) {
47
+ const postalCodes = this.config?.allowedPostalCodes ?? DEFAULT_POSTAL_CODE_CONFIG.allowedPostalCodes ?? []
48
+ if (!postalCodes.includes(value)) {
49
+ postalCodes.push(value)
50
+ this.config = { ...this.config, allowedPostalCodes: postalCodes }
51
+ this.requestUpdate()
52
+ }
53
+ input.value = ''
54
+ }
55
+ }
56
+
57
+ protected handleRemovePostalCode(event: Event): void {
58
+ const chip = event.currentTarget as HTMLElement
59
+ const index = parseInt(chip.dataset.index || '-1', 10)
60
+ if (index >= 0 && this.config?.allowedPostalCodes) {
61
+ this.config.allowedPostalCodes.splice(index, 1)
62
+ this.requestUpdate()
63
+ }
64
+ }
65
+
66
+ protected handleDisallowedPostalCodesChange(event: Event): void {
67
+ const input = event.target as HTMLInputElement
68
+ const value = input.value.trim()
69
+ if (value) {
70
+ const postalCodes = this.config?.disallowedPostalCodes ?? DEFAULT_POSTAL_CODE_CONFIG.disallowedPostalCodes ?? []
71
+ if (!postalCodes.includes(value)) {
72
+ postalCodes.push(value)
73
+ this.config = { ...this.config, disallowedPostalCodes: postalCodes }
74
+ this.requestUpdate()
75
+ }
76
+ input.value = ''
77
+ }
78
+ }
79
+
80
+ protected handleRemoveDisallowedPostalCode(event: Event): void {
81
+ const chip = event.currentTarget as HTMLElement
82
+ const index = parseInt(chip.dataset.index || '-1', 10)
83
+ if (index >= 0 && this.config?.disallowedPostalCodes) {
84
+ this.config.disallowedPostalCodes.splice(index, 1)
85
+ this.requestUpdate()
86
+ }
87
+ }
88
+
89
+ @bound
90
+ render(): TemplateResult {
91
+ return html`
92
+ <div class="checkbox-group">${this.renderValidate()}</div>
93
+ <div class="input-group">${this.renderAllowedPostalCodes()} ${this.renderDisallowedPostalCodes()}</div>
94
+ `
95
+ }
96
+
97
+ protected renderValidate(): TemplateResult {
98
+ const enabled = this.config?.validate ?? DEFAULT_POSTAL_CODE_CONFIG.validate
99
+ return html`
100
+ <div class="row">
101
+ <div class="input checkbox metadata">
102
+ <ui-checkbox
103
+ id="validate"
104
+ .checked="${enabled}"
105
+ name="validate"
106
+ @change="${this.handleBooleanChange}"
107
+ ></ui-checkbox>
108
+ <div class="label">
109
+ <label for="validate" class="body-medium">Validate</label>
110
+ <span class="body-small meta"
111
+ >Enable strict validation of the postal code. This is an experimental feature.</span
112
+ >
113
+ </div>
114
+ </div>
115
+ ${renderHelp('taxonomy', 'postalCode:validate')}
116
+ </div>
117
+ `
118
+ }
119
+
120
+ protected renderAllowedPostalCodes(): TemplateResult {
121
+ const value = this.config?.allowedPostalCodes ?? DEFAULT_POSTAL_CODE_CONFIG.allowedPostalCodes
122
+ const chips = (value || []).map(
123
+ (postalCode, index) =>
124
+ html`<ui-chip type="input" removable data-index="${index}" @remove="${this.handleRemovePostalCode}"
125
+ >${postalCode}</ui-chip
126
+ >`
127
+ )
128
+ return html`
129
+ <div class="row input-row">
130
+ <div class="input chips-input">
131
+ <ui-outlined-text-field
132
+ class="input"
133
+ name="allowedPostalCodes"
134
+ label="Allowed Postal Codes"
135
+ @change="${this.handleAllowedPostalCodesChange}"
136
+ supportingText="Add a postal code and press Enter to add it to the list."
137
+ ></ui-outlined-text-field>
138
+ <ui-chip-set>${chips}</ui-chip-set>
139
+ </div>
140
+ ${renderHelp('taxonomy', 'postalCode:allowedPostalCodes')}
141
+ </div>
142
+ `
143
+ }
144
+
145
+ protected renderDisallowedPostalCodes(): TemplateResult {
146
+ const value = this.config?.disallowedPostalCodes ?? DEFAULT_POSTAL_CODE_CONFIG.disallowedPostalCodes
147
+ const chips = (value || []).map(
148
+ (postalCode, index) =>
149
+ html`<ui-chip type="input" removable data-index="${index}" @remove="${this.handleRemoveDisallowedPostalCode}"
150
+ >${postalCode}</ui-chip
151
+ >`
152
+ )
153
+ return html`
154
+ <div class="row input-row">
155
+ <div class="input chips-input">
156
+ <ui-outlined-text-field
157
+ class="input"
158
+ name="disallowedPostalCodes"
159
+ label="Disallowed Postal Codes"
160
+ @change="${this.handleDisallowedPostalCodesChange}"
161
+ supportingText="Add a postal code and press Enter to add it to the list."
162
+ ></ui-outlined-text-field>
163
+ <ui-chip-set>${chips}</ui-chip-set>
164
+ </div>
165
+ ${renderHelp('taxonomy', 'postalCode:disallowedPostalCodes')}
166
+ </div>
167
+ `
168
+ }
169
+ }
@@ -34,10 +34,15 @@ export default class DomainAutoFieldsDialog extends LitElement {
34
34
  name: 'Name',
35
35
  description: 'A human-readable name for the record.',
36
36
  },
37
+ {
38
+ id: 'display-name',
39
+ name: 'Display Name',
40
+ description: 'A user-friendly label for UI presentation.',
41
+ },
37
42
  {
38
43
  id: 'description',
39
44
  name: 'Description',
40
- description: 'A detailed description of the record.',
45
+ description: 'A detailed description of the record with support for markdown.',
41
46
  },
42
47
  {
43
48
  id: 'public-unique-name',
@@ -54,6 +59,11 @@ export default class DomainAutoFieldsDialog extends LitElement {
54
59
  name: 'Email Address',
55
60
  description: 'Email address with verification support.',
56
61
  },
62
+ {
63
+ id: 'role',
64
+ name: 'User Role',
65
+ description: 'Defines the access level or permissions for a user.',
66
+ },
57
67
  {
58
68
  id: 'first-name',
59
69
  name: 'First Name',
@@ -76,6 +86,36 @@ export default class DomainAutoFieldsDialog extends LitElement {
76
86
  },
77
87
  ],
78
88
  },
89
+ {
90
+ name: 'Contact & Address',
91
+ fields: [
92
+ {
93
+ id: 'street-address',
94
+ name: 'Street Address',
95
+ description: 'The street address.',
96
+ },
97
+ {
98
+ id: 'city',
99
+ name: 'City',
100
+ description: 'The city.',
101
+ },
102
+ {
103
+ id: 'region',
104
+ name: 'Region',
105
+ description: 'The region/state/province.',
106
+ },
107
+ {
108
+ id: 'postal-code',
109
+ name: 'Postal/Zip Code',
110
+ description: 'The international postal/zip code.',
111
+ },
112
+ {
113
+ id: 'country',
114
+ name: 'Country',
115
+ description: 'The country.',
116
+ },
117
+ ],
118
+ },
79
119
  {
80
120
  name: 'Lifecycle & Status',
81
121
  fields: [
@@ -131,21 +171,6 @@ export default class DomainAutoFieldsDialog extends LitElement {
131
171
  },
132
172
  ],
133
173
  },
134
- {
135
- name: 'Session & Security',
136
- fields: [
137
- {
138
- id: 'session-id',
139
- name: 'Session ID',
140
- description: 'Unique identifier for user sessions.',
141
- },
142
- {
143
- id: 'expires-at',
144
- name: 'Expires At',
145
- description: 'When a session or token expires.',
146
- },
147
- ],
148
- },
149
174
  {
150
175
  name: 'Audit Trail',
151
176
  fields: [
@@ -30,6 +30,8 @@ import { UrlTaxonomy } from '../dialogs/UrlTaxonomy.js'
30
30
  import { PublicUniqueNameTaxonomy } from '../dialogs/PublicUniqueNameTaxonomy.js'
31
31
  import { GeospatialCoordinatesTaxonomy } from '../dialogs/GeospatialCoordinatesTaxonomy.js'
32
32
  import { CalculatedTaxonomy } from '../dialogs/CalculatedTaxonomy.js'
33
+ import { PostalCodeTaxonomy } from '../dialogs/PostalCodeTaxonomy.js'
34
+ import { CountryTaxonomy } from '../dialogs/CountryTaxonomy.js'
33
35
  import type { UiSelectElement } from '../../md/select/ui-select.js'
34
36
 
35
37
  /**
@@ -658,6 +660,10 @@ export default class DomainPropertyEditor extends ModelingElement {
658
660
  dialog = new GeospatialCoordinatesTaxonomy(element)
659
661
  } else if (id === SemanticType.Calculated) {
660
662
  dialog = new CalculatedTaxonomy(element)
663
+ } else if (id === SemanticType.PostalCode) {
664
+ dialog = new PostalCodeTaxonomy(element)
665
+ } else if (id === SemanticType.Country) {
666
+ dialog = new CountryTaxonomy(element)
661
667
  } else {
662
668
  throw new TypeError(`Unsupported taxonomy type: ${id}.`)
663
669
  }
@@ -1,22 +1,50 @@
1
+ /* eslint-disable max-len */
1
2
  import { html, type TemplateResult, LitElement } from 'lit'
2
3
  import { state, property } from 'lit/decorators.js'
3
- import {
4
- TEMPLATE_METADATA_REGISTRY,
5
- TEMPLATE_CATEGORIES,
6
- searchTemplates,
7
- getTemplatesByCategory,
8
- } from '@api-client/core/modeling/templates/meta/index.js'
4
+ import type { IconType } from '../../md/icons/Icons.js'
5
+ // Template metadata imports
6
+ import ecommercePlatformMetadata from '@api-client/domain-templates/meta/ecommerce-platform.json' with { type: 'json' }
7
+ import blogPublishingPlatformMetadata from '@api-client/domain-templates/meta/blog-publishing-platform.json' with { type: 'json' }
8
+ import healthcareManagementPlatformMetadata from '@api-client/domain-templates/meta/healthcare-management-platform.json' with { type: 'json' }
9
+ import financialServicesPlatformMetadata from '@api-client/domain-templates/meta/financial-services-platform.json' with { type: 'json' }
10
+ import educationManagementPlatformMetadata from '@api-client/domain-templates/meta/education-management-platform.json' with { type: 'json' }
11
+ import realEstateManagementPlatformMetadata from '@api-client/domain-templates/meta/real-estate-management-platform.json' with { type: 'json' }
12
+ import manufacturingPlatformMetadata from '@api-client/domain-templates/meta/manufacturing-platform.json' with { type: 'json' }
13
+ import hospitalityPlatformMetadata from '@api-client/domain-templates/meta/hospitality-platform.json' with { type: 'json' }
14
+ import legalServicesPlatformMetadata from '@api-client/domain-templates/meta/legal-services-platform.json' with { type: 'json' }
15
+ import nonProfitPlatformMetadata from '@api-client/domain-templates/meta/non-profit-platform.json' with { type: 'json' }
16
+ import iotSmartHomePlatformMetadata from '@api-client/domain-templates/meta/iot-smart-home-platform.json' with { type: 'json' }
17
+ import gamingPlatformMetadata from '@api-client/domain-templates/meta/gaming-platform.json' with { type: 'json' }
18
+ import { templatesByVertical } from '@api-client/domain-templates'
9
19
  import type {
10
20
  AssociationInfo,
11
21
  DomainTemplate,
22
+ DomainTemplateMetadata,
12
23
  EntityInfo,
13
24
  ModelInfo,
14
25
  NamespaceInfo,
15
26
  PropertyInfo,
16
- } from '@api-client/core/modeling/templates/types.js'
17
- import type { IconType } from '../../md/icons/Icons.js'
27
+ } from '@api-client/domain-templates/types.js'
18
28
 
19
- export const availableCategories = Object.keys(TEMPLATE_CATEGORIES) as (keyof typeof TEMPLATE_CATEGORIES)[]
29
+ /**
30
+ * Registry of all available template metadata
31
+ */
32
+ export const TEMPLATE_METADATA_REGISTRY: Record<string, DomainTemplate> = {
33
+ 'ecommerce-platform': ecommercePlatformMetadata as DomainTemplate,
34
+ 'blog-publishing-platform': blogPublishingPlatformMetadata as DomainTemplate,
35
+ 'healthcare-management-platform': healthcareManagementPlatformMetadata as DomainTemplate,
36
+ 'financial-services-platform': financialServicesPlatformMetadata as DomainTemplate,
37
+ 'education-management-platform': educationManagementPlatformMetadata as DomainTemplate,
38
+ 'real-estate-management-platform': realEstateManagementPlatformMetadata as DomainTemplate,
39
+ 'manufacturing-platform': manufacturingPlatformMetadata as DomainTemplate,
40
+ 'hospitality-platform': hospitalityPlatformMetadata as DomainTemplate,
41
+ 'legal-services-platform': legalServicesPlatformMetadata as DomainTemplate,
42
+ 'non-profit-platform': nonProfitPlatformMetadata as DomainTemplate,
43
+ 'iot-smart-home-platform': iotSmartHomePlatformMetadata as DomainTemplate,
44
+ 'gaming-platform': gamingPlatformMetadata as DomainTemplate,
45
+ } as const
46
+
47
+ export const availableCategories = Object.keys(templatesByVertical) as (keyof typeof templatesByVertical)[]
20
48
 
21
49
  /**
22
50
  * The selection detail object dispatched by the `selectionchange` event.
@@ -54,6 +82,18 @@ export function getPropertyIcon(p: PropertyInfo): IconType {
54
82
  }
55
83
  }
56
84
 
85
+ function searchTemplates(query: string): DomainTemplateMetadata[] {
86
+ const searchTerm = query.toLowerCase()
87
+ return Object.values(templatesByVertical)
88
+ .flat()
89
+ .filter(
90
+ (template) =>
91
+ template.name.toLowerCase().includes(searchTerm) ||
92
+ template.description.toLowerCase().includes(searchTerm) ||
93
+ template.tags.some((tag) => tag.toLowerCase().includes(searchTerm))
94
+ )
95
+ }
96
+
57
97
  /**
58
98
  * A browser component for exploring and selecting domain templates.
59
99
  * It provides the UI for searching, filtering, previewing, and selecting data domain templates.
@@ -76,13 +116,13 @@ export default class DomainTemplateBrowser extends LitElement {
76
116
  * If not set, all categories are included.
77
117
  * @attribute
78
118
  */
79
- @property({ type: String }) accessor category: keyof typeof TEMPLATE_CATEGORIES | undefined
119
+ @property({ type: String }) accessor category: keyof typeof templatesByVertical | undefined
80
120
 
81
121
  /**
82
122
  * The internal list of filtered templates to render in the UI.
83
123
  * This is computed based on `query` and `category`.
84
124
  */
85
- @state() accessor templates: DomainTemplate[] = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
125
+ @state() accessor templates: DomainTemplateMetadata[] = [...Object.values(templatesByVertical)].flat()
86
126
 
87
127
  /**
88
128
  * The currently previewed template. When set, the component renders the preview view instead of the list.
@@ -115,7 +155,7 @@ export default class DomainTemplateBrowser extends LitElement {
115
155
  search(value?: string): void {
116
156
  this.query = value
117
157
  if (!value) {
118
- this.templates = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
158
+ this.templates = [...Object.values(templatesByVertical)].flat()
119
159
  } else {
120
160
  this.templates = searchTemplates(value)
121
161
  }
@@ -130,10 +170,10 @@ export default class DomainTemplateBrowser extends LitElement {
130
170
  }
131
171
  if (value === '') {
132
172
  this.category = undefined
133
- this.templates = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
173
+ this.templates = [...Object.values(templatesByVertical)].flat()
134
174
  } else {
135
- this.category = value as keyof typeof TEMPLATE_CATEGORIES
136
- this.templates = getTemplatesByCategory(this.category)
175
+ this.category = value as keyof typeof templatesByVertical
176
+ this.templates = templatesByVertical[this.category].flat()
137
177
  }
138
178
  }
139
179
 
@@ -157,7 +197,11 @@ export default class DomainTemplateBrowser extends LitElement {
157
197
  if (!templateId) {
158
198
  return
159
199
  }
160
- this.previewTemplate = this.templates.find((t) => t.id === templateId)
200
+ const value = TEMPLATE_METADATA_REGISTRY[templateId]
201
+ if (!value) {
202
+ return
203
+ }
204
+ this.previewTemplate = value
161
205
  }
162
206
 
163
207
  protected handleClosePreview(): void {
@@ -236,7 +280,7 @@ export default class DomainTemplateBrowser extends LitElement {
236
280
  `
237
281
  }
238
282
 
239
- protected renderTemplateCard(template: DomainTemplate): TemplateResult {
283
+ protected renderTemplateCard(template: DomainTemplateMetadata): TemplateResult {
240
284
  // Use available properties from DomainTemplate interface
241
285
  const templateName = template.name || 'Unnamed Template'
242
286
  const templateDescription = template.description || 'No description available'