@epistola.app/valtimo-plugin 0.0.1 → 0.1.1

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 (54) hide show
  1. package/package.json +9 -13
  2. package/ng-package.json +0 -17
  3. package/src/lib/assets/epistola-logo.ts +0 -4
  4. package/src/lib/assets/index.ts +0 -1
  5. package/src/lib/components/check-job-status-configuration/check-job-status-configuration.component.html +0 -51
  6. package/src/lib/components/check-job-status-configuration/check-job-status-configuration.component.scss +0 -1
  7. package/src/lib/components/check-job-status-configuration/check-job-status-configuration.component.ts +0 -71
  8. package/src/lib/components/data-mapping-tree/data-mapping-tree.component.html +0 -23
  9. package/src/lib/components/data-mapping-tree/data-mapping-tree.component.scss +0 -38
  10. package/src/lib/components/data-mapping-tree/data-mapping-tree.component.ts +0 -124
  11. package/src/lib/components/download-document-configuration/download-document-configuration.component.html +0 -29
  12. package/src/lib/components/download-document-configuration/download-document-configuration.component.scss +0 -1
  13. package/src/lib/components/download-document-configuration/download-document-configuration.component.ts +0 -71
  14. package/src/lib/components/epistola-configuration/epistola-configuration.component.html +0 -74
  15. package/src/lib/components/epistola-configuration/epistola-configuration.component.scss +0 -1
  16. package/src/lib/components/epistola-configuration/epistola-configuration.component.ts +0 -96
  17. package/src/lib/components/epistola-download/epistola-download.component.ts +0 -79
  18. package/src/lib/components/epistola-download/epistola-download.formio.ts +0 -19
  19. package/src/lib/components/field-tree/field-tree.component.html +0 -192
  20. package/src/lib/components/field-tree/field-tree.component.scss +0 -255
  21. package/src/lib/components/field-tree/field-tree.component.ts +0 -321
  22. package/src/lib/components/generate-document-configuration/generate-document-configuration.component.html +0 -182
  23. package/src/lib/components/generate-document-configuration/generate-document-configuration.component.scss +0 -150
  24. package/src/lib/components/generate-document-configuration/generate-document-configuration.component.ts +0 -422
  25. package/src/lib/epistola.module.ts +0 -50
  26. package/src/lib/epistola.specification.ts +0 -208
  27. package/src/lib/models/config.ts +0 -53
  28. package/src/lib/models/index.ts +0 -2
  29. package/src/lib/models/template.ts +0 -70
  30. package/src/lib/services/epistola-plugin.service.ts +0 -82
  31. package/src/lib/services/index.ts +0 -1
  32. package/src/public_api.ts +0 -16
  33. package/tsconfig.lib.json +0 -21
  34. /package/{dist/fesm2022 → fesm2022}/epistola.app-valtimo-plugin.mjs +0 -0
  35. /package/{dist/fesm2022 → fesm2022}/epistola.app-valtimo-plugin.mjs.map +0 -0
  36. /package/{dist/index.d.ts → index.d.ts} +0 -0
  37. /package/{dist/lib → lib}/assets/epistola-logo.d.ts +0 -0
  38. /package/{dist/lib → lib}/assets/index.d.ts +0 -0
  39. /package/{dist/lib → lib}/components/check-job-status-configuration/check-job-status-configuration.component.d.ts +0 -0
  40. /package/{dist/lib → lib}/components/data-mapping-tree/data-mapping-tree.component.d.ts +0 -0
  41. /package/{dist/lib → lib}/components/download-document-configuration/download-document-configuration.component.d.ts +0 -0
  42. /package/{dist/lib → lib}/components/epistola-configuration/epistola-configuration.component.d.ts +0 -0
  43. /package/{dist/lib → lib}/components/epistola-download/epistola-download.component.d.ts +0 -0
  44. /package/{dist/lib → lib}/components/epistola-download/epistola-download.formio.d.ts +0 -0
  45. /package/{dist/lib → lib}/components/field-tree/field-tree.component.d.ts +0 -0
  46. /package/{dist/lib → lib}/components/generate-document-configuration/generate-document-configuration.component.d.ts +0 -0
  47. /package/{dist/lib → lib}/epistola.module.d.ts +0 -0
  48. /package/{dist/lib → lib}/epistola.specification.d.ts +0 -0
  49. /package/{dist/lib → lib}/models/config.d.ts +0 -0
  50. /package/{dist/lib → lib}/models/index.d.ts +0 -0
  51. /package/{dist/lib → lib}/models/template.d.ts +0 -0
  52. /package/{dist/lib → lib}/services/epistola-plugin.service.d.ts +0 -0
  53. /package/{dist/lib → lib}/services/index.d.ts +0 -0
  54. /package/{dist/public_api.d.ts → public_api.d.ts} +0 -0
@@ -1,321 +0,0 @@
1
- import {Component, EventEmitter, Input, OnChanges, Output, SimpleChanges} from '@angular/core';
2
- import {CommonModule} from '@angular/common';
3
- import {FormsModule} from '@angular/forms';
4
- import {PluginTranslatePipeModule} from '@valtimo/plugin';
5
- import {InputModule, ValuePathSelectorComponent, ValuePathSelectorPrefix} from '@valtimo/components';
6
- import {TemplateField} from '../../models';
7
-
8
- export type InputMode = 'browse' | 'pv' | 'expression';
9
-
10
- /**
11
- * Recursive component that renders a single TemplateField node as part of a tree form.
12
- *
13
- * - SCALAR fields render as a label + input row with a 3-mode selector (browse / pv / expression)
14
- * - OBJECT fields render as a collapsible section with children indented inside
15
- * - ARRAY fields render as a collapsible section with source collection input and optional per-item field mappings
16
- *
17
- * Input modes:
18
- * - Browse (⊞): ValuePathSelector for doc:/case: paths
19
- * - PV (pv): Dropdown of discovered process variables (text fallback when none found)
20
- * - Expression (fx): Free-text input for manual expressions
21
- */
22
- @Component({
23
- selector: 'epistola-field-tree',
24
- templateUrl: './field-tree.component.html',
25
- styleUrls: ['./field-tree.component.scss'],
26
- standalone: true,
27
- imports: [
28
- CommonModule,
29
- FormsModule,
30
- PluginTranslatePipeModule,
31
- InputModule,
32
- ValuePathSelectorComponent
33
- ]
34
- })
35
- export class FieldTreeComponent implements OnChanges {
36
- @Input() field!: TemplateField;
37
- @Input() value: any = undefined;
38
- @Input() pluginId!: string;
39
- @Input() caseDefinitionKey: string | null = null;
40
- @Input() processVariables: string[] = [];
41
- @Input() disabled = false;
42
-
43
- @Output() valueChange = new EventEmitter<any>();
44
-
45
- readonly ValuePathSelectorPrefix = ValuePathSelectorPrefix;
46
-
47
- inputMode: InputMode = 'browse';
48
- expanded = false;
49
-
50
- /** For ARRAY fields: whether to show per-item field mapping */
51
- arrayPerFieldMode = false;
52
-
53
- /** Completeness badge for collapsed object/array sections */
54
- mappedCount = 0;
55
- totalRequired = 0;
56
-
57
- ngOnChanges(changes: SimpleChanges): void {
58
- if (changes['value'] || changes['field']) {
59
- this.updateCompleteness();
60
- // Auto-expand if there are unmapped required children
61
- if (!this.expanded && this.totalRequired > 0 && this.mappedCount < this.totalRequired) {
62
- this.expanded = true;
63
- }
64
- }
65
- // Detect input mode from prefill value
66
- if (changes['value'] && this.value != null) {
67
- if (this.field?.fieldType === 'SCALAR') {
68
- this.inputMode = this.detectInputMode(this.value);
69
- } else if (this.field?.fieldType === 'ARRAY') {
70
- const sourceValue = this.getSourceValue();
71
- if (sourceValue) {
72
- this.inputMode = this.detectInputMode(sourceValue);
73
- }
74
- // Detect per-field mode from value shape
75
- if (typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
76
- this.arrayPerFieldMode = true;
77
- }
78
- }
79
- }
80
- }
81
-
82
- toggleExpanded(): void {
83
- this.expanded = !this.expanded;
84
- }
85
-
86
- setInputMode(mode: InputMode): void {
87
- this.inputMode = mode;
88
- }
89
-
90
- /** Handle value change from ValuePathSelector (browse mode) */
91
- onBrowseValueChange(newValue: string): void {
92
- this.emitScalarValue(newValue);
93
- }
94
-
95
- /** Handle value change from PV dropdown */
96
- onPvChange(newValue: string): void {
97
- if (newValue) {
98
- this.emitScalarValue('pv:' + newValue);
99
- } else {
100
- this.emitScalarValue('');
101
- }
102
- }
103
-
104
- /** Handle value change from text input (expression mode) */
105
- onExpressionValueChange(newValue: string): void {
106
- this.emitScalarValue(newValue);
107
- }
108
-
109
- /** Handle child value change for OBJECT fields */
110
- onChildChange(childName: string, childValue: any): void {
111
- const current = (typeof this.value === 'object' && this.value !== null) ? {...this.value} : {};
112
- if (childValue === undefined || childValue === null || childValue === '') {
113
- delete current[childName];
114
- } else {
115
- current[childName] = childValue;
116
- }
117
- this.valueChange.emit(Object.keys(current).length > 0 ? current : undefined);
118
- }
119
-
120
- /** Get the current value for a child field within an OBJECT */
121
- getChildValue(childName: string): any {
122
- if (typeof this.value === 'object' && this.value !== null) {
123
- return this.value[childName];
124
- }
125
- return undefined;
126
- }
127
-
128
- /** Get the string value for display (for SCALAR leaf inputs and direct array mode) */
129
- getStringValue(): string {
130
- return typeof this.value === 'string' ? this.value : '';
131
- }
132
-
133
- /** Get the PV name from a pv: prefixed value (for PV dropdown default selection) */
134
- getPvName(): string {
135
- const str = this.getStringValue();
136
- return str.startsWith('pv:') ? str.substring(3) : '';
137
- }
138
-
139
- // --- ARRAY-specific methods ---
140
-
141
- /** Get the source collection value (works for both direct string and _source format) */
142
- getSourceValue(): string {
143
- if (typeof this.value === 'string') {
144
- return this.value;
145
- }
146
- if (typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
147
- return this.value['_source'] || '';
148
- }
149
- return '';
150
- }
151
-
152
- /** Get the PV name from the source value */
153
- getSourcePvName(): string {
154
- const source = this.getSourceValue();
155
- return source.startsWith('pv:') ? source.substring(3) : '';
156
- }
157
-
158
- toggleArrayPerFieldMode(): void {
159
- this.arrayPerFieldMode = !this.arrayPerFieldMode;
160
-
161
- if (this.arrayPerFieldMode) {
162
- // Switch from direct to per-field: convert string value to _source object
163
- const currentSource = this.getSourceValue();
164
- const obj: Record<string, any> = {_source: currentSource};
165
- this.valueChange.emit(obj);
166
- } else {
167
- // Switch from per-field to direct: extract _source as string value
168
- const source = this.getSourceValue();
169
- this.valueChange.emit(source || undefined);
170
- }
171
- }
172
-
173
- /** Handle source collection value change (used in ARRAY mode) */
174
- onSourceBrowseChange(newValue: string): void {
175
- this.updateSourceValue(newValue);
176
- }
177
-
178
- onSourcePvChange(newValue: string): void {
179
- this.updateSourceValue(newValue ? 'pv:' + newValue : '');
180
- }
181
-
182
- onSourceExpressionChange(newValue: string): void {
183
- this.updateSourceValue(newValue);
184
- }
185
-
186
- /** Handle per-item field mapping change */
187
- onItemFieldChange(childName: string, sourceFieldName: string): void {
188
- const current = (typeof this.value === 'object' && this.value !== null) ? {...this.value} : {_source: ''};
189
- if (sourceFieldName && sourceFieldName.trim().length > 0) {
190
- current[childName] = sourceFieldName;
191
- } else {
192
- delete current[childName];
193
- }
194
- this.valueChange.emit(current);
195
- }
196
-
197
- /** Get the current source field name mapping for a child */
198
- getItemFieldValue(childName: string): string {
199
- if (typeof this.value === 'object' && this.value !== null) {
200
- return this.value[childName] || '';
201
- }
202
- return '';
203
- }
204
-
205
- /** Check if the array has any children that can be mapped per-item */
206
- hasArrayChildren(): boolean {
207
- return !!(this.field?.children && this.field.children.length > 0);
208
- }
209
-
210
- private emitScalarValue(newValue: string): void {
211
- this.valueChange.emit(newValue || undefined);
212
- }
213
-
214
- private updateSourceValue(newValue: string): void {
215
- if (this.arrayPerFieldMode) {
216
- const current = (typeof this.value === 'object' && this.value !== null) ? {...this.value} : {};
217
- current['_source'] = newValue || '';
218
- this.valueChange.emit(current);
219
- } else {
220
- this.valueChange.emit(newValue || undefined);
221
- }
222
- }
223
-
224
- private updateCompleteness(): void {
225
- if (this.field?.fieldType === 'OBJECT' && this.field.children) {
226
- const stats = this.countRequiredMapped(this.field.children, this.value || {});
227
- this.mappedCount = stats.mapped;
228
- this.totalRequired = stats.total;
229
- } else if (this.field?.fieldType === 'ARRAY') {
230
- this.updateArrayCompleteness();
231
- }
232
- }
233
-
234
- private updateArrayCompleteness(): void {
235
- if (!this.field?.children || this.field.children.length === 0) {
236
- // No children: just check if source is set
237
- this.totalRequired = this.field?.required ? 1 : 0;
238
- this.mappedCount = this.getSourceValue() ? (this.field?.required ? 1 : 0) : 0;
239
- return;
240
- }
241
-
242
- if (typeof this.value === 'object' && this.value !== null && '_source' in this.value) {
243
- // Per-field mode: count source + required children
244
- let total = 0;
245
- let mapped = 0;
246
-
247
- // Source counts as 1 required
248
- if (this.field?.required) {
249
- total++;
250
- if (this.value['_source'] && this.value['_source'].trim().length > 0) {
251
- mapped++;
252
- }
253
- }
254
-
255
- // Count required children
256
- for (const child of this.field.children) {
257
- if (child.required) {
258
- total++;
259
- const val = this.value[child.name];
260
- if (typeof val === 'string' && val.trim().length > 0) {
261
- mapped++;
262
- }
263
- }
264
- }
265
-
266
- this.mappedCount = mapped;
267
- this.totalRequired = total;
268
- } else {
269
- // Direct mode: just check if source is set
270
- this.totalRequired = this.field?.required ? 1 : 0;
271
- this.mappedCount = this.getSourceValue() ? (this.field?.required ? 1 : 0) : 0;
272
- }
273
- }
274
-
275
- private countRequiredMapped(
276
- fields: TemplateField[],
277
- mapping: Record<string, any>
278
- ): {mapped: number; total: number} {
279
- let mapped = 0;
280
- let total = 0;
281
- for (const field of fields) {
282
- if (field.fieldType === 'SCALAR' && field.required) {
283
- total++;
284
- const val = mapping[field.name];
285
- if (typeof val === 'string' && val.trim().length > 0) {
286
- mapped++;
287
- }
288
- } else if (field.fieldType === 'ARRAY' && field.required) {
289
- total++;
290
- const val = mapping[field.name];
291
- if (typeof val === 'string' && val.trim().length > 0) {
292
- mapped++;
293
- } else if (typeof val === 'object' && val !== null && '_source' in val) {
294
- if (typeof val['_source'] === 'string' && val['_source'].trim().length > 0) {
295
- mapped++;
296
- }
297
- }
298
- } else if (field.fieldType === 'OBJECT' && field.children) {
299
- const nested = (typeof mapping[field.name] === 'object' && mapping[field.name] !== null)
300
- ? mapping[field.name]
301
- : {};
302
- const childStats = this.countRequiredMapped(field.children, nested);
303
- mapped += childStats.mapped;
304
- total += childStats.total;
305
- }
306
- }
307
- return {mapped, total};
308
- }
309
-
310
- private detectInputMode(value: any): InputMode {
311
- if (typeof value !== 'string') return 'browse';
312
- if (value.startsWith('doc:') || value.startsWith('case:')) return 'browse';
313
- if (value.startsWith('pv:')) return 'pv';
314
- if (value.length > 0) return 'expression';
315
- return 'browse';
316
- }
317
-
318
- private isResolvableValue(value: string): boolean {
319
- return value.startsWith('doc:') || value.startsWith('case:') || value.startsWith('pv:') || value.startsWith('template:');
320
- }
321
- }
@@ -1,182 +0,0 @@
1
- <v-form
2
- (valueChange)="formValueChange($event)"
3
- *ngIf="{
4
- disabled: disabled$ | async,
5
- prefill: prefillConfiguration$ ? (prefillConfiguration$ | async) : null,
6
- templateOptions: templateOptions$ | async,
7
- templatesLoading: templatesLoading$ | async,
8
- variantOptions: variantOptions$ | async,
9
- variantsLoading: variantsLoading$ | async,
10
- environmentOptions: environmentOptions$ | async,
11
- environmentsLoading: environmentsLoading$ | async,
12
- templateFieldsLoading: templateFieldsLoading$ | async,
13
- selectedTemplateId: selectedTemplateId$ | async
14
- } as obs"
15
- >
16
- <v-select
17
- name="templateId"
18
- [title]="'templateId' | pluginTranslate: pluginId | async"
19
- [tooltip]="'templateIdTooltip' | pluginTranslate: pluginId | async"
20
- [margin]="true"
21
- [items]="obs.templateOptions"
22
- [defaultSelectionId]="obs.prefill?.templateId"
23
- [disabled]="obs.disabled || obs.templatesLoading"
24
- [required]="true"
25
- [loading]="obs.templatesLoading"
26
- >
27
- </v-select>
28
-
29
- <!-- Variant selection mode toggle -->
30
- <div class="variant-mode-toggle" *ngIf="obs.selectedTemplateId">
31
- <label class="variant-mode-label">{{ 'variantSelectionMode' | pluginTranslate: pluginId | async }}</label>
32
- <div class="variant-mode-buttons">
33
- <button
34
- type="button"
35
- class="variant-mode-btn"
36
- [class.active]="variantSelectionMode === 'explicit'"
37
- (click)="onVariantSelectionModeChange('explicit')"
38
- [disabled]="obs.disabled"
39
- >{{ 'selectByVariant' | pluginTranslate: pluginId | async }}</button>
40
- <button
41
- type="button"
42
- class="variant-mode-btn"
43
- [class.active]="variantSelectionMode === 'attributes'"
44
- (click)="onVariantSelectionModeChange('attributes')"
45
- [disabled]="obs.disabled"
46
- >{{ 'selectByAttributes' | pluginTranslate: pluginId | async }}</button>
47
- </div>
48
- </div>
49
-
50
- <!-- Explicit variant selection (dropdown) -->
51
- <v-select
52
- *ngIf="variantSelectionMode === 'explicit'"
53
- name="variantId"
54
- [title]="'variantId' | pluginTranslate: pluginId | async"
55
- [tooltip]="'variantIdTooltip' | pluginTranslate: pluginId | async"
56
- [margin]="true"
57
- [items]="obs.variantOptions"
58
- [defaultSelectionId]="obs.prefill?.variantId"
59
- [disabled]="obs.disabled || obs.variantsLoading || !obs.selectedTemplateId"
60
- [required]="false"
61
- [loading]="obs.variantsLoading"
62
- >
63
- </v-select>
64
-
65
- <!-- Attribute-based variant selection -->
66
- <div *ngIf="variantSelectionMode === 'attributes' && obs.selectedTemplateId" class="variant-attributes-section">
67
- <label class="variant-attributes-label">{{ 'variantAttributes' | pluginTranslate: pluginId | async }}</label>
68
- <div class="variant-attributes-list">
69
- <div *ngFor="let entry of variantAttributeEntries; let i = index" class="variant-attribute-row">
70
- <input
71
- type="text"
72
- class="variant-attribute-input"
73
- [placeholder]="'attributeKey' | pluginTranslate: pluginId | async"
74
- [(ngModel)]="entry.key"
75
- (ngModelChange)="onAttributeEntryChange()"
76
- [disabled]="obs.disabled"
77
- />
78
- <input
79
- type="text"
80
- class="variant-attribute-input"
81
- [placeholder]="'attributeValue' | pluginTranslate: pluginId | async"
82
- [(ngModel)]="entry.value"
83
- (ngModelChange)="onAttributeEntryChange()"
84
- [disabled]="obs.disabled"
85
- />
86
- <button
87
- type="button"
88
- class="variant-attribute-remove-btn"
89
- (click)="removeAttributeEntry(i)"
90
- [disabled]="obs.disabled"
91
- title="{{ 'removeAttribute' | pluginTranslate: pluginId | async }}"
92
- >&times;</button>
93
- </div>
94
- </div>
95
- <button
96
- type="button"
97
- class="variant-attribute-add-btn"
98
- (click)="addAttributeEntry()"
99
- [disabled]="obs.disabled"
100
- >+ {{ 'addAttribute' | pluginTranslate: pluginId | async }}</button>
101
- </div>
102
-
103
- <v-select
104
- name="environmentId"
105
- [title]="'environmentId' | pluginTranslate: pluginId | async"
106
- [tooltip]="'environmentIdTooltip' | pluginTranslate: pluginId | async"
107
- [margin]="true"
108
- [items]="obs.environmentOptions"
109
- [defaultSelectionId]="obs.prefill?.environmentId"
110
- [disabled]="obs.disabled || obs.environmentsLoading"
111
- [required]="false"
112
- [loading]="obs.environmentsLoading"
113
- >
114
- </v-select>
115
-
116
- <v-select
117
- name="outputFormat"
118
- [title]="'outputFormat' | pluginTranslate: pluginId | async"
119
- [tooltip]="'outputFormatTooltip' | pluginTranslate: pluginId | async"
120
- [margin]="true"
121
- [items]="outputFormatOptions"
122
- [defaultSelectionId]="obs.prefill?.outputFormat || 'PDF'"
123
- [disabled]="obs.disabled"
124
- [required]="true"
125
- >
126
- </v-select>
127
-
128
- <v-input
129
- name="filename"
130
- [title]="'filename' | pluginTranslate: pluginId | async"
131
- [tooltip]="'filenameTooltip' | pluginTranslate: pluginId | async"
132
- [margin]="true"
133
- [defaultValue]="obs.prefill?.filename"
134
- [disabled]="obs.disabled"
135
- [required]="true"
136
- >
137
- </v-input>
138
-
139
- <v-input
140
- name="correlationId"
141
- [title]="'correlationId' | pluginTranslate: pluginId | async"
142
- [tooltip]="'correlationIdTooltip' | pluginTranslate: pluginId | async"
143
- [margin]="true"
144
- [defaultValue]="obs.prefill?.correlationId"
145
- [disabled]="obs.disabled"
146
- [required]="false"
147
- >
148
- </v-input>
149
-
150
- <v-input
151
- name="resultProcessVariable"
152
- [title]="'resultProcessVariable' | pluginTranslate: pluginId | async"
153
- [tooltip]="'resultProcessVariableTooltip' | pluginTranslate: pluginId | async"
154
- [margin]="true"
155
- [defaultValue]="obs.prefill?.resultProcessVariable"
156
- [disabled]="obs.disabled"
157
- [required]="true"
158
- >
159
- </v-input>
160
- </v-form>
161
-
162
- <epistola-data-mapping-tree
163
- *ngIf="(selectedTemplateId$ | async)"
164
- [pluginId]="pluginId"
165
- [templateFields$]="templateFields$"
166
- [disabled$]="disabled$"
167
- [prefillMapping$]="prefillDataMapping$"
168
- [caseDefinitionKey]="caseDefinitionKey"
169
- [processVariables]="processVariables"
170
- (mappingChange)="onDataMappingChange($event)"
171
- (requiredFieldsStatus)="onRequiredFieldsStatusChange($event)"
172
- ></epistola-data-mapping-tree>
173
-
174
- <div class="validation-summary" *ngIf="(selectedTemplateId$ | async) && requiredFieldsStatus.total > 0">
175
- <span *ngIf="requiredFieldsStatus.mapped === requiredFieldsStatus.total" class="validation-complete">
176
- {{ 'requiredFieldsComplete' | pluginTranslate: pluginId | async }}
177
- </span>
178
- <span *ngIf="requiredFieldsStatus.mapped < requiredFieldsStatus.total" class="validation-incomplete">
179
- {{ requiredFieldsStatus.mapped }} / {{ requiredFieldsStatus.total }}
180
- {{ 'validationSummary' | pluginTranslate: pluginId | async }}
181
- </span>
182
- </div>
@@ -1,150 +0,0 @@
1
- .validation-summary {
2
- margin-top: 0.5rem;
3
- padding: 0.5rem 0.75rem;
4
- border-radius: 4px;
5
- font-size: 0.875rem;
6
-
7
- .validation-complete {
8
- color: #198754;
9
- }
10
-
11
- .validation-incomplete {
12
- color: #dc3545;
13
- font-weight: 500;
14
- }
15
- }
16
-
17
- .variant-mode-toggle {
18
- margin-bottom: 1rem;
19
- padding: 0 0.75rem;
20
- }
21
-
22
- .variant-mode-label {
23
- display: block;
24
- font-size: 0.875rem;
25
- font-weight: 500;
26
- margin-bottom: 0.375rem;
27
- }
28
-
29
- .variant-mode-buttons {
30
- display: flex;
31
- gap: 0;
32
- border: 1px solid #d1d5db;
33
- border-radius: 4px;
34
- overflow: hidden;
35
- width: fit-content;
36
- }
37
-
38
- .variant-mode-btn {
39
- padding: 0.375rem 0.75rem;
40
- font-size: 0.8125rem;
41
- background: #fff;
42
- border: none;
43
- border-right: 1px solid #d1d5db;
44
- cursor: pointer;
45
- color: #374151;
46
- transition: background-color 0.15s, color 0.15s;
47
-
48
- &:last-child {
49
- border-right: none;
50
- }
51
-
52
- &:hover:not([disabled]) {
53
- background: #f3f4f6;
54
- }
55
-
56
- &.active {
57
- background: #2563eb;
58
- color: #fff;
59
- }
60
-
61
- &[disabled] {
62
- opacity: 0.5;
63
- cursor: not-allowed;
64
- }
65
- }
66
-
67
- .variant-attributes-section {
68
- margin-bottom: 1rem;
69
- padding: 0 0.75rem;
70
- }
71
-
72
- .variant-attributes-label {
73
- display: block;
74
- font-size: 0.875rem;
75
- font-weight: 500;
76
- margin-bottom: 0.375rem;
77
- }
78
-
79
- .variant-attributes-list {
80
- display: flex;
81
- flex-direction: column;
82
- gap: 0.375rem;
83
- }
84
-
85
- .variant-attribute-row {
86
- display: flex;
87
- gap: 0.375rem;
88
- align-items: center;
89
- }
90
-
91
- .variant-attribute-input {
92
- flex: 1;
93
- padding: 0.375rem 0.5rem;
94
- font-size: 0.8125rem;
95
- border: 1px solid #d1d5db;
96
- border-radius: 4px;
97
- outline: none;
98
-
99
- &:focus {
100
- border-color: #2563eb;
101
- box-shadow: 0 0 0 1px #2563eb;
102
- }
103
-
104
- &[disabled] {
105
- opacity: 0.5;
106
- background: #f9fafb;
107
- }
108
- }
109
-
110
- .variant-attribute-remove-btn {
111
- padding: 0.25rem 0.5rem;
112
- font-size: 1rem;
113
- line-height: 1;
114
- background: none;
115
- border: 1px solid #d1d5db;
116
- border-radius: 4px;
117
- cursor: pointer;
118
- color: #6b7280;
119
-
120
- &:hover:not([disabled]) {
121
- color: #dc3545;
122
- border-color: #dc3545;
123
- }
124
-
125
- &[disabled] {
126
- opacity: 0.5;
127
- cursor: not-allowed;
128
- }
129
- }
130
-
131
- .variant-attribute-add-btn {
132
- margin-top: 0.375rem;
133
- padding: 0.25rem 0.5rem;
134
- font-size: 0.8125rem;
135
- background: none;
136
- border: 1px dashed #d1d5db;
137
- border-radius: 4px;
138
- cursor: pointer;
139
- color: #6b7280;
140
-
141
- &:hover:not([disabled]) {
142
- color: #2563eb;
143
- border-color: #2563eb;
144
- }
145
-
146
- &[disabled] {
147
- opacity: 0.5;
148
- cursor: not-allowed;
149
- }
150
- }