@api-client/ui 0.6.8 → 0.6.10

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 (37) hide show
  1. package/build/src/md/button/internals/group.styles.d.ts.map +1 -1
  2. package/build/src/md/button/internals/group.styles.js +1 -0
  3. package/build/src/md/button/internals/group.styles.js.map +1 -1
  4. package/build/src/modeling/dialogs/CountryTaxonomy.d.ts +24 -0
  5. package/build/src/modeling/dialogs/CountryTaxonomy.d.ts.map +1 -0
  6. package/build/src/modeling/dialogs/CountryTaxonomy.js +174 -0
  7. package/build/src/modeling/dialogs/CountryTaxonomy.js.map +1 -0
  8. package/build/src/modeling/dialogs/PostalCodeTaxonomy.d.ts +24 -0
  9. package/build/src/modeling/dialogs/PostalCodeTaxonomy.d.ts.map +1 -0
  10. package/build/src/modeling/dialogs/PostalCodeTaxonomy.js +167 -0
  11. package/build/src/modeling/dialogs/PostalCodeTaxonomy.js.map +1 -0
  12. package/build/src/modeling/domain-template-browser.d.ts.map +1 -1
  13. package/build/src/modeling/domain-template-browser.js +2 -1
  14. package/build/src/modeling/domain-template-browser.js.map +1 -1
  15. package/build/src/modeling/internals/DomainAutoFieldsDialog.d.ts.map +1 -1
  16. package/build/src/modeling/internals/DomainAutoFieldsDialog.js +30 -0
  17. package/build/src/modeling/internals/DomainAutoFieldsDialog.js.map +1 -1
  18. package/build/src/modeling/internals/DomainPropertyEditor.d.ts.map +1 -1
  19. package/build/src/modeling/internals/DomainPropertyEditor.js +8 -0
  20. package/build/src/modeling/internals/DomainPropertyEditor.js.map +1 -1
  21. package/build/src/modeling/internals/DomainTemplateBrowser.d.ts +10 -6
  22. package/build/src/modeling/internals/DomainTemplateBrowser.d.ts.map +1 -1
  23. package/build/src/modeling/internals/DomainTemplateBrowser.js +75 -8
  24. package/build/src/modeling/internals/DomainTemplateBrowser.js.map +1 -1
  25. package/build/src/modeling/internals/styles/DomainTemplateBrowser.styles.d.ts.map +1 -1
  26. package/build/src/modeling/internals/styles/DomainTemplateBrowser.styles.js +12 -0
  27. package/build/src/modeling/internals/styles/DomainTemplateBrowser.styles.js.map +1 -1
  28. package/build/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +2 -1
  30. package/src/md/button/internals/group.styles.ts +1 -0
  31. package/src/modeling/dialogs/CountryTaxonomy.ts +176 -0
  32. package/src/modeling/dialogs/PostalCodeTaxonomy.ts +169 -0
  33. package/src/modeling/domain-template-browser.ts +2 -1
  34. package/src/modeling/internals/DomainAutoFieldsDialog.ts +30 -0
  35. package/src/modeling/internals/DomainPropertyEditor.ts +6 -0
  36. package/src/modeling/internals/DomainTemplateBrowser.ts +87 -18
  37. package/src/modeling/internals/styles/DomainTemplateBrowser.styles.ts +12 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@api-client/ui",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
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",
@@ -4,6 +4,7 @@ import { css } from 'lit'
4
4
  export default css`
5
5
  :host {
6
6
  display: flex;
7
+ flex-wrap: wrap;
7
8
 
8
9
  --_gap: 8px;
9
10
  --_xs-radius: 16px;
@@ -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
+ }
@@ -1,6 +1,7 @@
1
1
  import { customElement } from 'lit/decorators.js'
2
2
  import Element from './internals/DomainTemplateBrowser.js'
3
3
  import styles from './internals/styles/DomainTemplateBrowser.styles.js'
4
+ import typography from '../styles/m3/typography.module.js'
4
5
 
5
6
  import '../md/button/ui-button.js'
6
7
  import '../md/button/ui-button-group.js'
@@ -9,7 +10,7 @@ import '../md/text-field/ui-outlined-text-field.js'
9
10
 
10
11
  @customElement('domain-template-browser')
11
12
  export class DomainTemplateBrowserElement extends Element {
12
- static override styles = [styles]
13
+ static override styles = [styles, typography]
13
14
  }
14
15
 
15
16
  declare global {
@@ -86,6 +86,36 @@ export default class DomainAutoFieldsDialog extends LitElement {
86
86
  },
87
87
  ],
88
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
+ },
89
119
  {
90
120
  name: 'Lifecycle & Status',
91
121
  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,39 @@ 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
+
97
+ function getVerticalName(id: string): string {
98
+ switch (id) {
99
+ case 'businessServices':
100
+ return 'Business Services'
101
+ case 'educationTraining':
102
+ return 'Education & Training'
103
+ case 'healthcareLifeSciences':
104
+ return 'Healthcare & Life Sciences'
105
+ case 'manufacturingLogistics':
106
+ return 'Manufacturing & Logistics'
107
+ case 'publicSector':
108
+ return 'Public Sector'
109
+ case 'realEstateConstruction':
110
+ return 'Real Estate & Construction'
111
+ case 'technologyMedia':
112
+ return 'Technology & Media'
113
+ default:
114
+ return id
115
+ }
116
+ }
117
+
57
118
  /**
58
119
  * A browser component for exploring and selecting domain templates.
59
120
  * It provides the UI for searching, filtering, previewing, and selecting data domain templates.
@@ -76,13 +137,13 @@ export default class DomainTemplateBrowser extends LitElement {
76
137
  * If not set, all categories are included.
77
138
  * @attribute
78
139
  */
79
- @property({ type: String }) accessor category: keyof typeof TEMPLATE_CATEGORIES | undefined
140
+ @property({ type: String }) accessor category: keyof typeof templatesByVertical | undefined
80
141
 
81
142
  /**
82
143
  * The internal list of filtered templates to render in the UI.
83
144
  * This is computed based on `query` and `category`.
84
145
  */
85
- @state() accessor templates: DomainTemplate[] = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
146
+ @state() accessor templates: DomainTemplateMetadata[] = [...Object.values(templatesByVertical)].flat()
86
147
 
87
148
  /**
88
149
  * The currently previewed template. When set, the component renders the preview view instead of the list.
@@ -115,7 +176,7 @@ export default class DomainTemplateBrowser extends LitElement {
115
176
  search(value?: string): void {
116
177
  this.query = value
117
178
  if (!value) {
118
- this.templates = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
179
+ this.templates = [...Object.values(templatesByVertical)].flat()
119
180
  } else {
120
181
  this.templates = searchTemplates(value)
121
182
  }
@@ -130,10 +191,10 @@ export default class DomainTemplateBrowser extends LitElement {
130
191
  }
131
192
  if (value === '') {
132
193
  this.category = undefined
133
- this.templates = [...Object.values(TEMPLATE_METADATA_REGISTRY)]
194
+ this.templates = [...Object.values(templatesByVertical)].flat()
134
195
  } else {
135
- this.category = value as keyof typeof TEMPLATE_CATEGORIES
136
- this.templates = getTemplatesByCategory(this.category)
196
+ this.category = value as keyof typeof templatesByVertical
197
+ this.templates = templatesByVertical[this.category].flat()
137
198
  }
138
199
  }
139
200
 
@@ -157,7 +218,11 @@ export default class DomainTemplateBrowser extends LitElement {
157
218
  if (!templateId) {
158
219
  return
159
220
  }
160
- this.previewTemplate = this.templates.find((t) => t.id === templateId)
221
+ const value = TEMPLATE_METADATA_REGISTRY[templateId]
222
+ if (!value) {
223
+ return
224
+ }
225
+ this.previewTemplate = value
161
226
  }
162
227
 
163
228
  protected handleClosePreview(): void {
@@ -222,7 +287,7 @@ export default class DomainTemplateBrowser extends LitElement {
222
287
  ?selected="${isSelected}"
223
288
  @click="${this.handleFilterClick}"
224
289
  >
225
- ${category}
290
+ ${getVerticalName(category)}
226
291
  </ui-button>`
227
292
  })}
228
293
  </ui-button-group>
@@ -236,13 +301,17 @@ export default class DomainTemplateBrowser extends LitElement {
236
301
  `
237
302
  }
238
303
 
239
- protected renderTemplateCard(template: DomainTemplate): TemplateResult {
304
+ protected renderTemplateCard(template: DomainTemplateMetadata): TemplateResult {
240
305
  // Use available properties from DomainTemplate interface
241
306
  const templateName = template.name || 'Unnamed Template'
242
307
  const templateDescription = template.description || 'No description available'
243
308
 
244
309
  return html`
245
310
  <div class="template-card">
311
+ <div class="meta">
312
+ <span class="value body-small" title="Updated at">${new Date(template.updatedAt).toLocaleDateString()}</span>
313
+ <span class="value body-small" title="Version">v${template.version}</span>
314
+ </div>
246
315
  <h3 class="template-title display-small">${templateName}</h3>
247
316
  <p class="template-description body-medium">${templateDescription}</p>
248
317
 
@@ -71,6 +71,18 @@ export default css`
71
71
  color: var(--md-sys-color-on-surface);
72
72
  border: 1px solid var(--md-sys-color-outline-variant);
73
73
  border-radius: var(--md-sys-shape-corner-medium);
74
+
75
+ .meta {
76
+ display: flex;
77
+ gap: 8px;
78
+
79
+ .value {
80
+ color: var(--md-sys-color-on-surface-variant);
81
+ border: 1px var(--md-sys-color-outline-variant) solid;
82
+ border-radius: 8px;
83
+ padding: 0 4px;
84
+ }
85
+ }
74
86
  }
75
87
 
76
88
  .template-description {