@masterteam/customization 0.0.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.
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, Injectable, computed, input, signal, effect, Component } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
|
+
import { FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
5
|
+
import { TranslocoDirective } from '@jsverse/transloco';
|
|
6
|
+
import { Card } from '@masterteam/components/card';
|
|
7
|
+
import { TextField } from '@masterteam/components/text-field';
|
|
8
|
+
import { ToggleField } from '@masterteam/components/toggle-field';
|
|
9
|
+
import { Button } from '@masterteam/components/button';
|
|
10
|
+
import { Tabs } from '@masterteam/components/tabs';
|
|
11
|
+
import { Icon } from '@masterteam/icons';
|
|
12
|
+
import { EntitiesManage } from '@masterteam/components/entities';
|
|
13
|
+
import { ModalService } from '@masterteam/components/modal';
|
|
14
|
+
import { Skeleton } from 'primeng/skeleton';
|
|
15
|
+
import { HttpClient } from '@angular/common/http';
|
|
16
|
+
import { Action, Selector, State, Store, select } from '@ngxs/store';
|
|
17
|
+
import { CrudStateBase, handleApiRequest } from '@masterteam/components';
|
|
18
|
+
import { ModalRef } from '@masterteam/components/dialog';
|
|
19
|
+
|
|
20
|
+
/** ViewTypes that support the 'showBorder' toggle */
|
|
21
|
+
const BORDER_VIEW_TYPES = [
|
|
22
|
+
'Text',
|
|
23
|
+
'Currency',
|
|
24
|
+
'Date',
|
|
25
|
+
'DateTime',
|
|
26
|
+
];
|
|
27
|
+
/** ViewTypes that support user-specific toggles */
|
|
28
|
+
const USER_VIEW_TYPES = ['User'];
|
|
29
|
+
/**
|
|
30
|
+
* ViewTypes that have editable configuration fields in the drawer.
|
|
31
|
+
* If a viewType is NOT in this list, the edit button is hidden
|
|
32
|
+
* (since size is now managed via drag-resize only).
|
|
33
|
+
*/
|
|
34
|
+
const EDITABLE_VIEW_TYPES = [
|
|
35
|
+
...BORDER_VIEW_TYPES,
|
|
36
|
+
...USER_VIEW_TYPES,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
class GetCustomization {
|
|
40
|
+
params;
|
|
41
|
+
static type = '[Customization] Get Properties';
|
|
42
|
+
constructor(params) {
|
|
43
|
+
this.params = params;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
class GetManagePreviewAreas {
|
|
47
|
+
static type = '[Customization] Get Areas';
|
|
48
|
+
}
|
|
49
|
+
class GetManagePreviewConfigurations {
|
|
50
|
+
areaKeys;
|
|
51
|
+
static type = '[Customization] Get Configurations';
|
|
52
|
+
constructor(areaKeys) {
|
|
53
|
+
this.areaKeys = areaKeys;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
class SetManagePreviewModuleInfo {
|
|
57
|
+
moduleType;
|
|
58
|
+
moduleId;
|
|
59
|
+
parentModuleType;
|
|
60
|
+
parentModuleId;
|
|
61
|
+
parentPath;
|
|
62
|
+
static type = '[Customization] Set Module Info';
|
|
63
|
+
constructor(moduleType, moduleId, parentModuleType, parentModuleId, parentPath) {
|
|
64
|
+
this.moduleType = moduleType;
|
|
65
|
+
this.moduleId = moduleId;
|
|
66
|
+
this.parentModuleType = parentModuleType;
|
|
67
|
+
this.parentModuleId = parentModuleId;
|
|
68
|
+
this.parentPath = parentPath;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
class BulkReplaceConfigurations {
|
|
72
|
+
items;
|
|
73
|
+
static type = '[Customization] Bulk Replace Configurations';
|
|
74
|
+
constructor(items) {
|
|
75
|
+
this.items = items;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
var CustomizationActionKey;
|
|
80
|
+
(function (CustomizationActionKey) {
|
|
81
|
+
CustomizationActionKey["GetProperties"] = "getProperties";
|
|
82
|
+
CustomizationActionKey["GetAreas"] = "getAreas";
|
|
83
|
+
CustomizationActionKey["GetConfigurations"] = "getConfigurations";
|
|
84
|
+
CustomizationActionKey["BulkReplaceConfigurations"] = "bulkReplaceConfigurations";
|
|
85
|
+
})(CustomizationActionKey || (CustomizationActionKey = {}));
|
|
86
|
+
|
|
87
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
88
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
89
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
90
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
91
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
92
|
+
};
|
|
93
|
+
const DEFAULT_STATE = {
|
|
94
|
+
properties: [],
|
|
95
|
+
areas: [],
|
|
96
|
+
configurations: [],
|
|
97
|
+
loadingActive: [],
|
|
98
|
+
errors: {},
|
|
99
|
+
moduleType: null,
|
|
100
|
+
moduleId: null,
|
|
101
|
+
parentModuleType: null,
|
|
102
|
+
parentModuleId: null,
|
|
103
|
+
parentPath: '',
|
|
104
|
+
};
|
|
105
|
+
let CustomizationState = class CustomizationState extends CrudStateBase {
|
|
106
|
+
http = inject(HttpClient);
|
|
107
|
+
baseUrl = 'Properties';
|
|
108
|
+
displayConfigUrl = 'display-configurations';
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Data Selectors - Individual for fine-grained reactivity
|
|
111
|
+
// ============================================================================
|
|
112
|
+
static getProperties(state) {
|
|
113
|
+
return state.properties;
|
|
114
|
+
}
|
|
115
|
+
static getAreas(state) {
|
|
116
|
+
return state.areas;
|
|
117
|
+
}
|
|
118
|
+
static getConfigurations(state) {
|
|
119
|
+
return state.configurations;
|
|
120
|
+
}
|
|
121
|
+
static getModuleId(state) {
|
|
122
|
+
return state.moduleId;
|
|
123
|
+
}
|
|
124
|
+
static getModuleType(state) {
|
|
125
|
+
return state.moduleType;
|
|
126
|
+
}
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Loading/Error Slice Selectors - REQUIRED for optimal performance
|
|
129
|
+
// ============================================================================
|
|
130
|
+
static getLoadingActive(state) {
|
|
131
|
+
return state.loadingActive;
|
|
132
|
+
}
|
|
133
|
+
static getErrors(state) {
|
|
134
|
+
return state.errors;
|
|
135
|
+
}
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Actions
|
|
138
|
+
// ============================================================================
|
|
139
|
+
getAreas(ctx) {
|
|
140
|
+
const req$ = this.http.get(`${this.displayConfigUrl}/areas`);
|
|
141
|
+
return handleApiRequest({
|
|
142
|
+
ctx,
|
|
143
|
+
key: CustomizationActionKey.GetAreas,
|
|
144
|
+
request$: req$,
|
|
145
|
+
onSuccess: (response) => ({
|
|
146
|
+
areas: response.data ?? [],
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
getProperties(ctx, action) {
|
|
151
|
+
const state = ctx.getState();
|
|
152
|
+
const contextKey = this.buildContextKey(state);
|
|
153
|
+
const params = {
|
|
154
|
+
...action.params,
|
|
155
|
+
contextKey,
|
|
156
|
+
};
|
|
157
|
+
const req$ = this.http.get(`${this.baseUrl}/catalog`, { params });
|
|
158
|
+
return handleApiRequest({
|
|
159
|
+
ctx,
|
|
160
|
+
key: CustomizationActionKey.GetProperties,
|
|
161
|
+
request$: req$,
|
|
162
|
+
onSuccess: (response) => ({
|
|
163
|
+
properties: response.data ?? [],
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
getConfigurations(ctx, action) {
|
|
168
|
+
const state = ctx.getState();
|
|
169
|
+
const contextKey = this.buildContextKey(state);
|
|
170
|
+
const params = { contextKey };
|
|
171
|
+
if (action.areaKeys?.length) {
|
|
172
|
+
params['areaKeys'] = action.areaKeys;
|
|
173
|
+
}
|
|
174
|
+
const req$ = this.http.get(this.displayConfigUrl, { params });
|
|
175
|
+
return handleApiRequest({
|
|
176
|
+
ctx,
|
|
177
|
+
key: CustomizationActionKey.GetConfigurations,
|
|
178
|
+
request$: req$,
|
|
179
|
+
onSuccess: (response) => ({
|
|
180
|
+
configurations: response.data ?? [],
|
|
181
|
+
}),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
setModuleInfo(ctx, action) {
|
|
185
|
+
let parentPath = '';
|
|
186
|
+
if (action.parentModuleType && action.parentModuleId) {
|
|
187
|
+
parentPath = `/${action.parentModuleType}/${action.parentModuleId}`;
|
|
188
|
+
}
|
|
189
|
+
else if (action.parentPath) {
|
|
190
|
+
parentPath = action.parentPath;
|
|
191
|
+
}
|
|
192
|
+
ctx.patchState({
|
|
193
|
+
moduleType: action.moduleType,
|
|
194
|
+
moduleId: action.moduleId,
|
|
195
|
+
parentModuleType: action.parentModuleType ?? null,
|
|
196
|
+
parentModuleId: action.parentModuleId ?? null,
|
|
197
|
+
parentPath: parentPath ?? '',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
bulkReplaceConfigurations(ctx, action) {
|
|
201
|
+
const state = ctx.getState();
|
|
202
|
+
const contextKey = this.buildContextKey(state);
|
|
203
|
+
const req$ = this.http.put(`${this.displayConfigUrl}/bulk-replace`, {
|
|
204
|
+
contextKey,
|
|
205
|
+
items: action.items,
|
|
206
|
+
});
|
|
207
|
+
return handleApiRequest({
|
|
208
|
+
ctx,
|
|
209
|
+
key: CustomizationActionKey.BulkReplaceConfigurations,
|
|
210
|
+
request$: req$,
|
|
211
|
+
onSuccess: () => {
|
|
212
|
+
// Rebuild configurations from the items payload
|
|
213
|
+
const newConfigs = [];
|
|
214
|
+
for (const item of action.items) {
|
|
215
|
+
for (const da of item.displayAreas) {
|
|
216
|
+
newConfigs.push({
|
|
217
|
+
contextKey,
|
|
218
|
+
areaKey: da.areaKey,
|
|
219
|
+
propertyKey: item.propertyKey,
|
|
220
|
+
order: da.order,
|
|
221
|
+
configuration: da.configuration,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { configurations: newConfigs };
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// ============================================================================
|
|
230
|
+
// Private Helpers
|
|
231
|
+
// ============================================================================
|
|
232
|
+
buildContextKey(state) {
|
|
233
|
+
const contextParts = [];
|
|
234
|
+
if (state.parentModuleType && state.parentModuleId) {
|
|
235
|
+
contextParts.push(`${state.parentModuleType}:${state.parentModuleId}`);
|
|
236
|
+
}
|
|
237
|
+
if (state.moduleType && state.moduleId) {
|
|
238
|
+
contextParts.push(`${state.moduleType}:${state.moduleId}`);
|
|
239
|
+
}
|
|
240
|
+
return contextParts.join('/');
|
|
241
|
+
}
|
|
242
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: CustomizationState, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
243
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: CustomizationState });
|
|
244
|
+
};
|
|
245
|
+
__decorate([
|
|
246
|
+
Action(GetManagePreviewAreas)
|
|
247
|
+
], CustomizationState.prototype, "getAreas", null);
|
|
248
|
+
__decorate([
|
|
249
|
+
Action(GetCustomization)
|
|
250
|
+
], CustomizationState.prototype, "getProperties", null);
|
|
251
|
+
__decorate([
|
|
252
|
+
Action(GetManagePreviewConfigurations)
|
|
253
|
+
], CustomizationState.prototype, "getConfigurations", null);
|
|
254
|
+
__decorate([
|
|
255
|
+
Action(SetManagePreviewModuleInfo)
|
|
256
|
+
], CustomizationState.prototype, "setModuleInfo", null);
|
|
257
|
+
__decorate([
|
|
258
|
+
Action(BulkReplaceConfigurations)
|
|
259
|
+
], CustomizationState.prototype, "bulkReplaceConfigurations", null);
|
|
260
|
+
__decorate([
|
|
261
|
+
Selector()
|
|
262
|
+
], CustomizationState, "getProperties", null);
|
|
263
|
+
__decorate([
|
|
264
|
+
Selector()
|
|
265
|
+
], CustomizationState, "getAreas", null);
|
|
266
|
+
__decorate([
|
|
267
|
+
Selector()
|
|
268
|
+
], CustomizationState, "getConfigurations", null);
|
|
269
|
+
__decorate([
|
|
270
|
+
Selector()
|
|
271
|
+
], CustomizationState, "getModuleId", null);
|
|
272
|
+
__decorate([
|
|
273
|
+
Selector()
|
|
274
|
+
], CustomizationState, "getModuleType", null);
|
|
275
|
+
__decorate([
|
|
276
|
+
Selector()
|
|
277
|
+
], CustomizationState, "getLoadingActive", null);
|
|
278
|
+
__decorate([
|
|
279
|
+
Selector()
|
|
280
|
+
], CustomizationState, "getErrors", null);
|
|
281
|
+
CustomizationState = __decorate([
|
|
282
|
+
State({
|
|
283
|
+
name: 'customization',
|
|
284
|
+
defaults: DEFAULT_STATE,
|
|
285
|
+
})
|
|
286
|
+
], CustomizationState);
|
|
287
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: CustomizationState, decorators: [{
|
|
288
|
+
type: Injectable
|
|
289
|
+
}], propDecorators: { getAreas: [], getProperties: [], getConfigurations: [], setModuleInfo: [], bulkReplaceConfigurations: [] } });
|
|
290
|
+
|
|
291
|
+
class CustomizationFacade {
|
|
292
|
+
store = inject(Store);
|
|
293
|
+
// ============================================================================
|
|
294
|
+
// Data Selectors - Memoized by NGXS (fine-grained reactivity)
|
|
295
|
+
// ============================================================================
|
|
296
|
+
properties = select(CustomizationState.getProperties);
|
|
297
|
+
areas = select(CustomizationState.getAreas);
|
|
298
|
+
configurations = select(CustomizationState.getConfigurations);
|
|
299
|
+
moduleId = select(CustomizationState.getModuleId);
|
|
300
|
+
moduleType = select(CustomizationState.getModuleType);
|
|
301
|
+
// ============================================================================
|
|
302
|
+
// Loading/Error Slices - Memoized by NGXS
|
|
303
|
+
// ============================================================================
|
|
304
|
+
loadingActive = select(CustomizationState.getLoadingActive);
|
|
305
|
+
errors = select(CustomizationState.getErrors);
|
|
306
|
+
// ============================================================================
|
|
307
|
+
// Loading Signals - Computed from slice (minimal reactivity)
|
|
308
|
+
// ============================================================================
|
|
309
|
+
isLoadingProperties = computed(() => this.loadingActive().includes(CustomizationActionKey.GetProperties), ...(ngDevMode ? [{ debugName: "isLoadingProperties" }] : []));
|
|
310
|
+
isLoadingAreas = computed(() => this.loadingActive().includes(CustomizationActionKey.GetAreas), ...(ngDevMode ? [{ debugName: "isLoadingAreas" }] : []));
|
|
311
|
+
isLoadingConfigurations = computed(() => this.loadingActive().includes(CustomizationActionKey.GetConfigurations), ...(ngDevMode ? [{ debugName: "isLoadingConfigurations" }] : []));
|
|
312
|
+
isBulkReplacing = computed(() => this.loadingActive().includes(CustomizationActionKey.BulkReplaceConfigurations), ...(ngDevMode ? [{ debugName: "isBulkReplacing" }] : []));
|
|
313
|
+
// ============================================================================
|
|
314
|
+
// Error Signals - Computed from slice (minimal reactivity)
|
|
315
|
+
// ============================================================================
|
|
316
|
+
propertiesError = computed(() => this.errors()[CustomizationActionKey.GetProperties] ?? null, ...(ngDevMode ? [{ debugName: "propertiesError" }] : []));
|
|
317
|
+
areasError = computed(() => this.errors()[CustomizationActionKey.GetAreas] ?? null, ...(ngDevMode ? [{ debugName: "areasError" }] : []));
|
|
318
|
+
configurationsError = computed(() => this.errors()[CustomizationActionKey.GetConfigurations] ?? null, ...(ngDevMode ? [{ debugName: "configurationsError" }] : []));
|
|
319
|
+
bulkReplaceError = computed(() => this.errors()[CustomizationActionKey.BulkReplaceConfigurations] ?? null, ...(ngDevMode ? [{ debugName: "bulkReplaceError" }] : []));
|
|
320
|
+
// ============================================================================
|
|
321
|
+
// Action Dispatchers
|
|
322
|
+
// ============================================================================
|
|
323
|
+
loadAreas() {
|
|
324
|
+
return this.store.dispatch(new GetManagePreviewAreas());
|
|
325
|
+
}
|
|
326
|
+
loadProperties(params) {
|
|
327
|
+
return this.store.dispatch(new GetCustomization(params));
|
|
328
|
+
}
|
|
329
|
+
loadConfigurations(areaKeys) {
|
|
330
|
+
return this.store.dispatch(new GetManagePreviewConfigurations(areaKeys));
|
|
331
|
+
}
|
|
332
|
+
bulkReplaceConfigurations(items) {
|
|
333
|
+
return this.store.dispatch(new BulkReplaceConfigurations(items));
|
|
334
|
+
}
|
|
335
|
+
setModuleInfo(moduleType, moduleId, parentModuleType, parentModuleId, parentPath) {
|
|
336
|
+
return this.store.dispatch(new SetManagePreviewModuleInfo(moduleType, moduleId, parentModuleType, parentModuleId, parentPath));
|
|
337
|
+
}
|
|
338
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: CustomizationFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
339
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: CustomizationFacade, providedIn: 'root' });
|
|
340
|
+
}
|
|
341
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: CustomizationFacade, decorators: [{
|
|
342
|
+
type: Injectable,
|
|
343
|
+
args: [{
|
|
344
|
+
providedIn: 'root',
|
|
345
|
+
}]
|
|
346
|
+
}] });
|
|
347
|
+
|
|
348
|
+
class PropertyConfigDrawer {
|
|
349
|
+
modalService = inject(ModalService);
|
|
350
|
+
ref = inject(ModalRef);
|
|
351
|
+
facade = inject(CustomizationFacade);
|
|
352
|
+
// ─── Inputs (passed via drawer inputValues) ───
|
|
353
|
+
propertyKey = input('', ...(ngDevMode ? [{ debugName: "propertyKey" }] : []));
|
|
354
|
+
propertyName = input('', ...(ngDevMode ? [{ debugName: "propertyName" }] : []));
|
|
355
|
+
viewType = input('Text', ...(ngDevMode ? [{ debugName: "viewType" }] : []));
|
|
356
|
+
areaKey = input('card', ...(ngDevMode ? [{ debugName: "areaKey" }] : []));
|
|
357
|
+
currentOrder = input(1, ...(ngDevMode ? [{ debugName: "currentOrder" }] : []));
|
|
358
|
+
initialConfig = input({
|
|
359
|
+
showBorder: false,
|
|
360
|
+
showDisplayName: false,
|
|
361
|
+
showPhoneNumber: false,
|
|
362
|
+
showEmail: false,
|
|
363
|
+
}, ...(ngDevMode ? [{ debugName: "initialConfig" }] : []));
|
|
364
|
+
// ─── UI state ───
|
|
365
|
+
submitting = signal(false, ...(ngDevMode ? [{ debugName: "submitting" }] : []));
|
|
366
|
+
showBorderControl = new FormControl(false);
|
|
367
|
+
showDisplayNameControl = new FormControl(false);
|
|
368
|
+
showPhoneNumberControl = new FormControl(false);
|
|
369
|
+
showEmailControl = new FormControl(false);
|
|
370
|
+
// ─── Computed visibility flags ───
|
|
371
|
+
isBorderViewType = computed(() => BORDER_VIEW_TYPES.includes(this.viewType()), ...(ngDevMode ? [{ debugName: "isBorderViewType" }] : []));
|
|
372
|
+
isUserViewType = computed(() => USER_VIEW_TYPES.includes(this.viewType()), ...(ngDevMode ? [{ debugName: "isUserViewType" }] : []));
|
|
373
|
+
constructor() {
|
|
374
|
+
// Initialize form from input config
|
|
375
|
+
effect(() => {
|
|
376
|
+
const config = this.initialConfig();
|
|
377
|
+
this.showBorderControl.patchValue(config.showBorder ?? false);
|
|
378
|
+
this.showDisplayNameControl.patchValue(config.showDisplayName ?? false);
|
|
379
|
+
this.showPhoneNumberControl.patchValue(config.showPhoneNumber ?? false);
|
|
380
|
+
this.showEmailControl.patchValue(config.showEmail ?? false);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
onSave() {
|
|
384
|
+
// Preserve existing size from configuration (managed via drag-resize)
|
|
385
|
+
const existingConfig = this.facade
|
|
386
|
+
.configurations()
|
|
387
|
+
.find((c) => c.propertyKey === this.propertyKey() && c.areaKey === this.areaKey());
|
|
388
|
+
const existingSize = existingConfig?.configuration?.['size'];
|
|
389
|
+
const configuration = {};
|
|
390
|
+
// Carry over size so drag-resize value is not lost
|
|
391
|
+
if (existingSize != null) {
|
|
392
|
+
configuration['size'] = existingSize;
|
|
393
|
+
}
|
|
394
|
+
if (this.isBorderViewType()) {
|
|
395
|
+
configuration['showBorder'] = this.showBorderControl.value ?? false;
|
|
396
|
+
}
|
|
397
|
+
if (this.isUserViewType()) {
|
|
398
|
+
configuration['showDisplayName'] =
|
|
399
|
+
this.showDisplayNameControl.value ?? false;
|
|
400
|
+
configuration['showPhoneNumber'] =
|
|
401
|
+
this.showPhoneNumberControl.value ?? false;
|
|
402
|
+
configuration['showEmail'] = this.showEmailControl.value ?? false;
|
|
403
|
+
}
|
|
404
|
+
const newDisplayArea = {
|
|
405
|
+
areaKey: this.areaKey(),
|
|
406
|
+
order: this.currentOrder(),
|
|
407
|
+
configuration,
|
|
408
|
+
};
|
|
409
|
+
// Build bulk items from existing configs, replacing the current property+area
|
|
410
|
+
const configs = this.facade.configurations();
|
|
411
|
+
const itemsMap = new Map();
|
|
412
|
+
for (const c of configs) {
|
|
413
|
+
// Skip the entry being replaced
|
|
414
|
+
if (c.propertyKey === this.propertyKey() &&
|
|
415
|
+
c.areaKey === this.areaKey()) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (!itemsMap.has(c.propertyKey)) {
|
|
419
|
+
itemsMap.set(c.propertyKey, []);
|
|
420
|
+
}
|
|
421
|
+
itemsMap.get(c.propertyKey).push({
|
|
422
|
+
areaKey: c.areaKey,
|
|
423
|
+
order: c.order,
|
|
424
|
+
configuration: c.configuration,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
// Add/replace the current property's area config
|
|
428
|
+
if (!itemsMap.has(this.propertyKey())) {
|
|
429
|
+
itemsMap.set(this.propertyKey(), []);
|
|
430
|
+
}
|
|
431
|
+
itemsMap.get(this.propertyKey()).push(newDisplayArea);
|
|
432
|
+
const items = Array.from(itemsMap.entries()).map(([propertyKey, displayAreas]) => ({ propertyKey, displayAreas }));
|
|
433
|
+
this.submitting.set(true);
|
|
434
|
+
this.facade.bulkReplaceConfigurations(items).subscribe({
|
|
435
|
+
next: () => this.ref.close(true),
|
|
436
|
+
error: () => this.submitting.set(false),
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
onCancel() {
|
|
440
|
+
this.ref.close(null);
|
|
441
|
+
}
|
|
442
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: PropertyConfigDrawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
443
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: PropertyConfigDrawer, isStandalone: true, selector: "mt-property-config-drawer", inputs: { propertyKey: { classPropertyName: "propertyKey", publicName: "propertyKey", isSignal: true, isRequired: false, transformFunction: null }, propertyName: { classPropertyName: "propertyName", publicName: "propertyName", isSignal: true, isRequired: false, transformFunction: null }, viewType: { classPropertyName: "viewType", publicName: "viewType", isSignal: true, isRequired: false, transformFunction: null }, areaKey: { classPropertyName: "areaKey", publicName: "areaKey", isSignal: true, isRequired: false, transformFunction: null }, currentOrder: { classPropertyName: "currentOrder", publicName: "currentOrder", isSignal: true, isRequired: false, transformFunction: null }, initialConfig: { classPropertyName: "initialConfig", publicName: "initialConfig", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'customization'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-2 h-full overflow-y-auto pb-10 flex flex-col gap-5\">\r\n <!-- Show Border Toggle (Text, Currency, Date, DateTime) -->\r\n @if (isBorderViewType()) {\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-border')\"\r\n icon=\"layout.layout-grid-01\"\r\n [formControl]=\"showBorderControl\"\r\n ></mt-toggle-field>\r\n }\r\n\r\n <!-- User-specific Toggles -->\r\n @if (isUserViewType()) {\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-display-name')\"\r\n icon=\"user.user-circle\"\r\n [formControl]=\"showDisplayNameControl\"\r\n ></mt-toggle-field>\r\n\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-phone-number')\"\r\n icon=\"communication.phone\"\r\n [formControl]=\"showPhoneNumberControl\"\r\n ></mt-toggle-field>\r\n\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-email')\"\r\n icon=\"communication.mail-01\"\r\n [formControl]=\"showEmailControl\"\r\n ></mt-toggle-field>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onCancel()\"\r\n ></mt-button>\r\n <mt-button\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n ></mt-button>\r\n </div>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }] });
|
|
444
|
+
}
|
|
445
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: PropertyConfigDrawer, decorators: [{
|
|
446
|
+
type: Component,
|
|
447
|
+
args: [{ selector: 'mt-property-config-drawer', standalone: true, imports: [TranslocoDirective, ReactiveFormsModule, Button, ToggleField], template: "<ng-container *transloco=\"let t; prefix: 'customization'\">\r\n <div [class]=\"[modalService.contentClass, 'p-4', 'overflow-y-hidden!']\">\r\n <div class=\"mt-2 h-full overflow-y-auto pb-10 flex flex-col gap-5\">\r\n <!-- Show Border Toggle (Text, Currency, Date, DateTime) -->\r\n @if (isBorderViewType()) {\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-border')\"\r\n icon=\"layout.layout-grid-01\"\r\n [formControl]=\"showBorderControl\"\r\n ></mt-toggle-field>\r\n }\r\n\r\n <!-- User-specific Toggles -->\r\n @if (isUserViewType()) {\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-display-name')\"\r\n icon=\"user.user-circle\"\r\n [formControl]=\"showDisplayNameControl\"\r\n ></mt-toggle-field>\r\n\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-phone-number')\"\r\n icon=\"communication.phone\"\r\n [formControl]=\"showPhoneNumberControl\"\r\n ></mt-toggle-field>\r\n\r\n <mt-toggle-field\r\n toggleShape=\"card\"\r\n [label]=\"t('show-email')\"\r\n icon=\"communication.mail-01\"\r\n [formControl]=\"showEmailControl\"\r\n ></mt-toggle-field>\r\n }\r\n </div>\r\n </div>\r\n\r\n <div [class]=\"modalService.footerClass\">\r\n <mt-button\r\n [label]=\"t('cancel')\"\r\n severity=\"secondary\"\r\n [disabled]=\"submitting()\"\r\n (onClick)=\"onCancel()\"\r\n ></mt-button>\r\n <mt-button\r\n [label]=\"t('save')\"\r\n severity=\"primary\"\r\n [loading]=\"submitting()\"\r\n (onClick)=\"onSave()\"\r\n ></mt-button>\r\n </div>\r\n</ng-container>\r\n" }]
|
|
448
|
+
}], ctorParameters: () => [], propDecorators: { propertyKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertyKey", required: false }] }], propertyName: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertyName", required: false }] }], viewType: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewType", required: false }] }], areaKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "areaKey", required: false }] }], currentOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentOrder", required: false }] }], initialConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialConfig", required: false }] }] } });
|
|
449
|
+
|
|
450
|
+
class Customization {
|
|
451
|
+
facade = inject(CustomizationFacade);
|
|
452
|
+
modalService = inject(ModalService);
|
|
453
|
+
/** Set of viewTypes that have editable fields in the drawer (beyond size) */
|
|
454
|
+
editableViewTypes = new Set(EDITABLE_VIEW_TYPES);
|
|
455
|
+
searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
|
|
456
|
+
activeArea = signal(null, ...(ngDevMode ? [{ debugName: "activeArea" }] : []));
|
|
457
|
+
// ── Derived signals ──
|
|
458
|
+
properties = this.facade.properties;
|
|
459
|
+
areas = this.facade.areas;
|
|
460
|
+
isLoading = this.facade.isLoadingProperties;
|
|
461
|
+
isLoadingAreas = this.facade.isLoadingAreas;
|
|
462
|
+
isLoadingPreview = computed(() => this.facade.isLoadingConfigurations() ||
|
|
463
|
+
this.facade.isLoadingProperties(), ...(ngDevMode ? [{ debugName: "isLoadingPreview" }] : []));
|
|
464
|
+
areasTabs = computed(() => this.areas().map((area) => ({ label: area.name, value: area.key })), ...(ngDevMode ? [{ debugName: "areasTabs" }] : []));
|
|
465
|
+
/** Properties enriched with `enabled` based on current area configurations */
|
|
466
|
+
propertiesWithEnabled = computed(() => {
|
|
467
|
+
const props = this.properties();
|
|
468
|
+
const configs = this.facade.configurations();
|
|
469
|
+
const area = this.activeArea();
|
|
470
|
+
if (!area)
|
|
471
|
+
return props.map((p) => ({ ...p, enabled: false }));
|
|
472
|
+
return props.map((p) => ({
|
|
473
|
+
...p,
|
|
474
|
+
enabled: configs.some((c) => c.areaKey === area && c.propertyKey === p.normalizedKey),
|
|
475
|
+
}));
|
|
476
|
+
}, ...(ngDevMode ? [{ debugName: "propertiesWithEnabled" }] : []));
|
|
477
|
+
filteredProperties = computed(() => {
|
|
478
|
+
const query = this.searchQuery().toLowerCase().trim();
|
|
479
|
+
const list = this.propertiesWithEnabled();
|
|
480
|
+
if (!query)
|
|
481
|
+
return list;
|
|
482
|
+
return list.filter((prop) => {
|
|
483
|
+
const name = typeof prop.name === 'string'
|
|
484
|
+
? prop.name
|
|
485
|
+
: Object.values(prop.name).join(' ');
|
|
486
|
+
return name.toLowerCase().includes(query);
|
|
487
|
+
});
|
|
488
|
+
}, ...(ngDevMode ? [{ debugName: "filteredProperties" }] : []));
|
|
489
|
+
/** Preview entities built from configurations + properties for the active area */
|
|
490
|
+
previewEntities = signal([], ...(ngDevMode ? [{ debugName: "previewEntities" }] : []));
|
|
491
|
+
constructor() {
|
|
492
|
+
// Set default active area when areas load
|
|
493
|
+
effect(() => {
|
|
494
|
+
const areas = this.areas();
|
|
495
|
+
if (areas.length > 0 && !this.activeArea()) {
|
|
496
|
+
this.activeArea.set(areas[0].key);
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
// Rebuild preview entities when configs, properties, or active area change
|
|
500
|
+
effect(() => {
|
|
501
|
+
const configs = this.facade.configurations();
|
|
502
|
+
const props = this.properties();
|
|
503
|
+
const area = this.activeArea();
|
|
504
|
+
if (!area || !props.length) {
|
|
505
|
+
this.previewEntities.set([]);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const areaConfigs = configs.filter((c) => c.areaKey === area);
|
|
509
|
+
const entities = areaConfigs
|
|
510
|
+
.map((config) => this.buildEntityFromConfig(config, props))
|
|
511
|
+
.filter((e) => e !== null)
|
|
512
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
513
|
+
this.previewEntities.set(entities);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
ngOnInit() {
|
|
517
|
+
// Load display areas
|
|
518
|
+
this.facade.loadAreas();
|
|
519
|
+
this.facade.loadProperties();
|
|
520
|
+
this.facade.loadConfigurations();
|
|
521
|
+
}
|
|
522
|
+
onPropertyToggle(prop, enabled) {
|
|
523
|
+
if (enabled) {
|
|
524
|
+
this.openDrawer(prop);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
// Remove this property and bulk-replace with remaining checked props
|
|
528
|
+
this.bulkSaveWithout(prop.normalizedKey);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
onEditProperty(prop) {
|
|
532
|
+
const area = this.activeArea();
|
|
533
|
+
if (!area)
|
|
534
|
+
return;
|
|
535
|
+
const configs = this.facade.configurations();
|
|
536
|
+
const existingConfig = configs.find((c) => c.areaKey === area && c.propertyKey === prop.normalizedKey);
|
|
537
|
+
this.openDrawer(prop, existingConfig);
|
|
538
|
+
}
|
|
539
|
+
onEntityResized(event) {
|
|
540
|
+
const area = this.activeArea();
|
|
541
|
+
if (!area)
|
|
542
|
+
return;
|
|
543
|
+
const configs = this.facade.configurations();
|
|
544
|
+
// Update the configuration for the resized entity in the current area
|
|
545
|
+
const updatedConfigs = configs.map((c) => c.areaKey === area && c.propertyKey === event.entity.normalizedKey
|
|
546
|
+
? { ...c, configuration: { ...c.configuration, size: event.newSize } }
|
|
547
|
+
: c);
|
|
548
|
+
// Build bulk-replace items from updated configs
|
|
549
|
+
const items = this.groupConfigsByProperty(updatedConfigs);
|
|
550
|
+
this.facade.bulkReplaceConfigurations(items).subscribe();
|
|
551
|
+
}
|
|
552
|
+
onEntitiesReordered(entities) {
|
|
553
|
+
const area = this.activeArea();
|
|
554
|
+
if (!area)
|
|
555
|
+
return;
|
|
556
|
+
const configs = this.facade.configurations();
|
|
557
|
+
const items = entities.map((entity, index) => {
|
|
558
|
+
const existingConfig = configs.find((c) => c.areaKey === area && c.propertyKey === entity.normalizedKey);
|
|
559
|
+
return {
|
|
560
|
+
propertyKey: entity.normalizedKey,
|
|
561
|
+
displayAreas: [
|
|
562
|
+
{
|
|
563
|
+
areaKey: area,
|
|
564
|
+
order: index + 1,
|
|
565
|
+
configuration: existingConfig?.configuration ?? {},
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
};
|
|
569
|
+
});
|
|
570
|
+
// Include configs from other areas that are not being reordered
|
|
571
|
+
const otherAreaConfigs = configs.filter((c) => c.areaKey !== area);
|
|
572
|
+
const otherAreaItems = this.groupConfigsByProperty(otherAreaConfigs);
|
|
573
|
+
// Merge: for properties that exist in both, combine their displayAreas
|
|
574
|
+
const mergedItems = this.mergeItems(items, otherAreaItems);
|
|
575
|
+
this.facade.bulkReplaceConfigurations(mergedItems).subscribe();
|
|
576
|
+
}
|
|
577
|
+
// ── Private helpers ──
|
|
578
|
+
/**
|
|
579
|
+
* Build bulk items from all current configurations, excluding a specific property.
|
|
580
|
+
* Then call bulk-replace so the backend replaces the entire context atomically.
|
|
581
|
+
*/
|
|
582
|
+
bulkSaveWithout(excludePropertyKey) {
|
|
583
|
+
const configs = this.facade.configurations();
|
|
584
|
+
const remaining = configs.filter((c) => c.propertyKey !== excludePropertyKey);
|
|
585
|
+
const items = this.groupConfigsByProperty(remaining);
|
|
586
|
+
this.facade.bulkReplaceConfigurations(items).subscribe();
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Group flat DisplayConfiguration[] into bulk-replace items grouped by propertyKey.
|
|
590
|
+
*/
|
|
591
|
+
groupConfigsByProperty(configs) {
|
|
592
|
+
const map = new Map();
|
|
593
|
+
for (const c of configs) {
|
|
594
|
+
if (!map.has(c.propertyKey)) {
|
|
595
|
+
map.set(c.propertyKey, []);
|
|
596
|
+
}
|
|
597
|
+
map.get(c.propertyKey).push({
|
|
598
|
+
areaKey: c.areaKey,
|
|
599
|
+
order: c.order,
|
|
600
|
+
configuration: c.configuration,
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
return Array.from(map.entries()).map(([propertyKey, displayAreas]) => ({
|
|
604
|
+
propertyKey,
|
|
605
|
+
displayAreas,
|
|
606
|
+
}));
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Merge two sets of bulk items. If a propertyKey exists in both,
|
|
610
|
+
* combine their displayAreas arrays.
|
|
611
|
+
*/
|
|
612
|
+
mergeItems(primary, secondary) {
|
|
613
|
+
const map = new Map();
|
|
614
|
+
for (const item of primary) {
|
|
615
|
+
map.set(item.propertyKey, [...item.displayAreas]);
|
|
616
|
+
}
|
|
617
|
+
for (const item of secondary) {
|
|
618
|
+
const existing = map.get(item.propertyKey);
|
|
619
|
+
if (existing) {
|
|
620
|
+
existing.push(...item.displayAreas);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
map.set(item.propertyKey, [...item.displayAreas]);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return Array.from(map.entries()).map(([propertyKey, displayAreas]) => ({
|
|
627
|
+
propertyKey,
|
|
628
|
+
displayAreas,
|
|
629
|
+
}));
|
|
630
|
+
}
|
|
631
|
+
openDrawer(prop, existingConfig) {
|
|
632
|
+
const area = this.activeArea();
|
|
633
|
+
if (!area)
|
|
634
|
+
return;
|
|
635
|
+
const configs = this.facade.configurations();
|
|
636
|
+
const areaConfigs = configs.filter((c) => c.areaKey === area);
|
|
637
|
+
const nextOrder = existingConfig?.order ?? areaConfigs.length + 1;
|
|
638
|
+
const displayName = this.getPropertyDisplayName(prop);
|
|
639
|
+
const cfg = existingConfig?.configuration;
|
|
640
|
+
const initConfig = {
|
|
641
|
+
showBorder: cfg?.['showBorder'] ?? false,
|
|
642
|
+
showDisplayName: cfg?.['showDisplayName'] ?? false,
|
|
643
|
+
showPhoneNumber: cfg?.['showPhoneNumber'] ?? false,
|
|
644
|
+
showEmail: cfg?.['showEmail'] ?? false,
|
|
645
|
+
};
|
|
646
|
+
this.modalService.openModal(PropertyConfigDrawer, 'drawer', {
|
|
647
|
+
header: displayName,
|
|
648
|
+
height: '25vw',
|
|
649
|
+
styleClass: '!w-[27%] !absolute !shadow-none',
|
|
650
|
+
position: 'end',
|
|
651
|
+
appendTo: '#page-content',
|
|
652
|
+
dismissible: true,
|
|
653
|
+
inputValues: {
|
|
654
|
+
propertyKey: prop.normalizedKey,
|
|
655
|
+
propertyName: displayName,
|
|
656
|
+
viewType: prop.viewType,
|
|
657
|
+
areaKey: area,
|
|
658
|
+
currentOrder: nextOrder,
|
|
659
|
+
initialConfig: initConfig,
|
|
660
|
+
},
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
buildEntityFromConfig(config, props) {
|
|
664
|
+
const prop = props.find((p) => p.normalizedKey === config.propertyKey);
|
|
665
|
+
if (!prop)
|
|
666
|
+
return null;
|
|
667
|
+
const displayName = this.getPropertyDisplayName(prop);
|
|
668
|
+
const viewType = prop.viewType;
|
|
669
|
+
const entity = {
|
|
670
|
+
id: prop.id,
|
|
671
|
+
propertyId: prop.id,
|
|
672
|
+
key: prop.key,
|
|
673
|
+
normalizedKey: prop.normalizedKey,
|
|
674
|
+
name: displayName,
|
|
675
|
+
value: this.getPlaceholderValue(viewType, displayName),
|
|
676
|
+
rawValue: this.getPlaceholderRawValue(viewType, displayName),
|
|
677
|
+
viewType,
|
|
678
|
+
order: config.order,
|
|
679
|
+
configuration: config.configuration,
|
|
680
|
+
};
|
|
681
|
+
// Add user-specific fields for User viewType
|
|
682
|
+
if (viewType === 'User') {
|
|
683
|
+
entity.displayName = 'John Doe';
|
|
684
|
+
entity.photoUrl = '';
|
|
685
|
+
entity.phoneNumber = '+1234567890';
|
|
686
|
+
entity.email = 'user@example.com';
|
|
687
|
+
}
|
|
688
|
+
return entity;
|
|
689
|
+
}
|
|
690
|
+
getPropertyDisplayName(prop) {
|
|
691
|
+
if (typeof prop.name === 'string')
|
|
692
|
+
return prop.name;
|
|
693
|
+
if (prop.name && typeof prop.name === 'object') {
|
|
694
|
+
return (prop.name['display'] ||
|
|
695
|
+
Object.values(prop.name)[0] ||
|
|
696
|
+
prop.key);
|
|
697
|
+
}
|
|
698
|
+
return prop.key;
|
|
699
|
+
}
|
|
700
|
+
getPlaceholderValue(viewType, name) {
|
|
701
|
+
switch (viewType) {
|
|
702
|
+
case 'Text':
|
|
703
|
+
return 'Sample Text';
|
|
704
|
+
case 'Date':
|
|
705
|
+
return '2026-01-15';
|
|
706
|
+
case 'DateTime':
|
|
707
|
+
return '2026-01-15 10:30';
|
|
708
|
+
case 'Percentage':
|
|
709
|
+
return '75%';
|
|
710
|
+
case 'Status':
|
|
711
|
+
return { key: 'sample', display: name, color: '#3B82F6' };
|
|
712
|
+
case 'Currency':
|
|
713
|
+
return '$1,250.00';
|
|
714
|
+
case 'Checkbox':
|
|
715
|
+
return 'true';
|
|
716
|
+
case 'User':
|
|
717
|
+
return 'John Doe';
|
|
718
|
+
default:
|
|
719
|
+
return name;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
getPlaceholderRawValue(viewType, name) {
|
|
723
|
+
switch (viewType) {
|
|
724
|
+
case 'Percentage':
|
|
725
|
+
return '75';
|
|
726
|
+
case 'Checkbox':
|
|
727
|
+
return 'true';
|
|
728
|
+
default:
|
|
729
|
+
return name;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Customization, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
733
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: Customization, isStandalone: true, selector: "mt-customization", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'customization'\">\r\n <div class=\"flex gap-6 h-full w-full overflow-hidden\">\r\n <!-- \u2500\u2500\u2500 Left Sidebar: Attributes Panel \u2500\u2500\u2500 -->\r\n <mt-card class=\"w-1/5 h-full flex flex-col overflow-hidden pb-3\">\r\n <ng-template #headless>\r\n <!-- Header -->\r\n <h3 class=\"text-xl font-semibold px-4 pt-5 pb-3\">\r\n {{ t(\"attributes\") }}\r\n </h3>\r\n\r\n <!-- Properties List -->\r\n <div class=\"flex-1 overflow-y-auto px-4 pb-4 space-y-3\">\r\n <!-- Search Field -->\r\n <div class=\"sticky top-0 bg-surface-0 mb-0 py-3 pb-2 mb-1 z-10\">\r\n <mt-text-field\r\n [placeholder]=\"t('search-properties')\"\r\n [(ngModel)]=\"searchQuery\"\r\n icon=\"general.search-lg\"\r\n />\r\n </div>\r\n @if (isLoading()) {\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <div class=\"flex items-center gap-1 p-1 rounded-lg\">\r\n <p-skeleton height=\"4rem\" />\r\n </div>\r\n }\r\n } @else {\r\n @for (prop of filteredProperties(); track prop.id) {\r\n <div\r\n class=\"flex items-center gap-3 p-3 rounded-lg border border-gray-300 border-dashed hover:bg-emphasis transition-colors\"\r\n >\r\n <!-- Property Name -->\r\n <div class=\"flex-1 min-w-0 py-2\">\r\n <span\r\n class=\"text-sm font-medium truncate block\"\r\n [class.text-primary]=\"prop.enabled\"\r\n >\r\n {{ prop.name?.display }}\r\n </span>\r\n </div>\r\n\r\n <!-- Edit Button (visible when enabled and viewType has configurable fields) -->\r\n @if (prop.enabled && editableViewTypes.has(prop.viewType)) {\r\n <mt-button\r\n icon=\"editor.pencil-01\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n (onClick)=\"onEditProperty(prop)\"\r\n />\r\n }\r\n\r\n <!-- Toggle -->\r\n <mt-toggle-field\r\n [ngModel]=\"prop.enabled\"\r\n (ngModelChange)=\"onPropertyToggle(prop, $event)\"\r\n class=\"shrink-0\"\r\n ></mt-toggle-field>\r\n </div>\r\n }\r\n\r\n @if (filteredProperties().length === 0) {\r\n <div class=\"py-8 text-center text-muted-color\">\r\n <p class=\"text-sm\">{{ t(\"no-properties\") }}</p>\r\n </div>\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n\r\n <!-- \u2500\u2500\u2500 Main Area: Preview \u2500\u2500\u2500 -->\r\n <div class=\"w-3/5 flex flex-col gap-4 h-full overflow-hidden items-center\">\r\n <!-- Area Tabs -->\r\n @if (isLoadingAreas()) {\r\n <div class=\"flex gap-2 py-1\">\r\n @for (i of [1, 2, 3]; track i) {\r\n <p-skeleton width=\"6rem\" height=\"2.25rem\" borderRadius=\"8px\" />\r\n }\r\n </div>\r\n } @else if (areasTabs().length > 0) {\r\n <mt-tabs\r\n [options]=\"areasTabs()\"\r\n [(active)]=\"activeArea\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n />\r\n }\r\n\r\n <!-- Entities Preview -->\r\n <div class=\"h-full\">\r\n <mt-card class=\"h-full p-3 w-150\">\r\n <ng-template #headless>\r\n @if (isLoadingPreview()) {\r\n <div class=\"grid grid-cols-12 gap-x-4 gap-y-10 p-4\">\r\n @for (i of [1, 2, 3]; track i) {\r\n <div class=\"col-span-4 flex flex-col gap-1.5 p-3\">\r\n <p-skeleton width=\"60%\" height=\"2rem\" borderRadius=\"6px\" />\r\n <p-skeleton\r\n width=\"40%\"\r\n height=\"1.5rem\"\r\n borderRadius=\"4px\"\r\n />\r\n </div>\r\n }\r\n <div class=\"col-span-12 flex flex-col gap-1.5 p-3\">\r\n <p-skeleton width=\"20rem\" height=\"4rem\" borderRadius=\"6px\" />\r\n </div>\r\n <div class=\"col-span-6 flex items-center gap-2.5 p-3\">\r\n <p-skeleton size=\"4rem\" shape=\"circle\" />\r\n <div class=\"flex flex-col gap-1 flex-1\">\r\n <p-skeleton\r\n width=\"55%\"\r\n height=\"0.85rem\"\r\n borderRadius=\"4px\"\r\n />\r\n <p-skeleton\r\n width=\"35%\"\r\n height=\"0.65rem\"\r\n borderRadius=\"4px\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n } @else if (previewEntities().length > 0) {\r\n <mt-entities-manage\r\n [(entities)]=\"previewEntities\"\r\n (entitiesReordered)=\"onEntitiesReordered($event)\"\r\n (entityResized)=\"onEntityResized($event)\"\r\n />\r\n } @else {\r\n <div\r\n class=\"flex items-center justify-center h-full text-muted-color\"\r\n >\r\n <p class=\"text-sm\">{{ t(\"no-preview-items\") }}</p>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "icon", "iconPosition"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "size", "fluid", "disabled"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: EntitiesManage, selector: "mt-entities-manage", inputs: ["entities"], outputs: ["entitiesChange", "entitiesReordered"] }] });
|
|
734
|
+
}
|
|
735
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: Customization, decorators: [{
|
|
736
|
+
type: Component,
|
|
737
|
+
args: [{ selector: 'mt-customization', standalone: true, imports: [
|
|
738
|
+
FormsModule,
|
|
739
|
+
TranslocoDirective,
|
|
740
|
+
Card,
|
|
741
|
+
TextField,
|
|
742
|
+
ToggleField,
|
|
743
|
+
Button,
|
|
744
|
+
Tabs,
|
|
745
|
+
Icon,
|
|
746
|
+
Skeleton,
|
|
747
|
+
EntitiesManage,
|
|
748
|
+
], template: "<ng-container *transloco=\"let t; prefix: 'customization'\">\r\n <div class=\"flex gap-6 h-full w-full overflow-hidden\">\r\n <!-- \u2500\u2500\u2500 Left Sidebar: Attributes Panel \u2500\u2500\u2500 -->\r\n <mt-card class=\"w-1/5 h-full flex flex-col overflow-hidden pb-3\">\r\n <ng-template #headless>\r\n <!-- Header -->\r\n <h3 class=\"text-xl font-semibold px-4 pt-5 pb-3\">\r\n {{ t(\"attributes\") }}\r\n </h3>\r\n\r\n <!-- Properties List -->\r\n <div class=\"flex-1 overflow-y-auto px-4 pb-4 space-y-3\">\r\n <!-- Search Field -->\r\n <div class=\"sticky top-0 bg-surface-0 mb-0 py-3 pb-2 mb-1 z-10\">\r\n <mt-text-field\r\n [placeholder]=\"t('search-properties')\"\r\n [(ngModel)]=\"searchQuery\"\r\n icon=\"general.search-lg\"\r\n />\r\n </div>\r\n @if (isLoading()) {\r\n @for (i of [1, 2, 3, 4, 5, 6]; track i) {\r\n <div class=\"flex items-center gap-1 p-1 rounded-lg\">\r\n <p-skeleton height=\"4rem\" />\r\n </div>\r\n }\r\n } @else {\r\n @for (prop of filteredProperties(); track prop.id) {\r\n <div\r\n class=\"flex items-center gap-3 p-3 rounded-lg border border-gray-300 border-dashed hover:bg-emphasis transition-colors\"\r\n >\r\n <!-- Property Name -->\r\n <div class=\"flex-1 min-w-0 py-2\">\r\n <span\r\n class=\"text-sm font-medium truncate block\"\r\n [class.text-primary]=\"prop.enabled\"\r\n >\r\n {{ prop.name?.display }}\r\n </span>\r\n </div>\r\n\r\n <!-- Edit Button (visible when enabled and viewType has configurable fields) -->\r\n @if (prop.enabled && editableViewTypes.has(prop.viewType)) {\r\n <mt-button\r\n icon=\"editor.pencil-01\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n severity=\"primary\"\r\n (onClick)=\"onEditProperty(prop)\"\r\n />\r\n }\r\n\r\n <!-- Toggle -->\r\n <mt-toggle-field\r\n [ngModel]=\"prop.enabled\"\r\n (ngModelChange)=\"onPropertyToggle(prop, $event)\"\r\n class=\"shrink-0\"\r\n ></mt-toggle-field>\r\n </div>\r\n }\r\n\r\n @if (filteredProperties().length === 0) {\r\n <div class=\"py-8 text-center text-muted-color\">\r\n <p class=\"text-sm\">{{ t(\"no-properties\") }}</p>\r\n </div>\r\n }\r\n }\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n\r\n <!-- \u2500\u2500\u2500 Main Area: Preview \u2500\u2500\u2500 -->\r\n <div class=\"w-3/5 flex flex-col gap-4 h-full overflow-hidden items-center\">\r\n <!-- Area Tabs -->\r\n @if (isLoadingAreas()) {\r\n <div class=\"flex gap-2 py-1\">\r\n @for (i of [1, 2, 3]; track i) {\r\n <p-skeleton width=\"6rem\" height=\"2.25rem\" borderRadius=\"8px\" />\r\n }\r\n </div>\r\n } @else if (areasTabs().length > 0) {\r\n <mt-tabs\r\n [options]=\"areasTabs()\"\r\n [(active)]=\"activeArea\"\r\n optionLabel=\"label\"\r\n optionValue=\"value\"\r\n />\r\n }\r\n\r\n <!-- Entities Preview -->\r\n <div class=\"h-full\">\r\n <mt-card class=\"h-full p-3 w-150\">\r\n <ng-template #headless>\r\n @if (isLoadingPreview()) {\r\n <div class=\"grid grid-cols-12 gap-x-4 gap-y-10 p-4\">\r\n @for (i of [1, 2, 3]; track i) {\r\n <div class=\"col-span-4 flex flex-col gap-1.5 p-3\">\r\n <p-skeleton width=\"60%\" height=\"2rem\" borderRadius=\"6px\" />\r\n <p-skeleton\r\n width=\"40%\"\r\n height=\"1.5rem\"\r\n borderRadius=\"4px\"\r\n />\r\n </div>\r\n }\r\n <div class=\"col-span-12 flex flex-col gap-1.5 p-3\">\r\n <p-skeleton width=\"20rem\" height=\"4rem\" borderRadius=\"6px\" />\r\n </div>\r\n <div class=\"col-span-6 flex items-center gap-2.5 p-3\">\r\n <p-skeleton size=\"4rem\" shape=\"circle\" />\r\n <div class=\"flex flex-col gap-1 flex-1\">\r\n <p-skeleton\r\n width=\"55%\"\r\n height=\"0.85rem\"\r\n borderRadius=\"4px\"\r\n />\r\n <p-skeleton\r\n width=\"35%\"\r\n height=\"0.65rem\"\r\n borderRadius=\"4px\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n } @else if (previewEntities().length > 0) {\r\n <mt-entities-manage\r\n [(entities)]=\"previewEntities\"\r\n (entitiesReordered)=\"onEntitiesReordered($event)\"\r\n (entityResized)=\"onEntityResized($event)\"\r\n />\r\n } @else {\r\n <div\r\n class=\"flex items-center justify-center h-full text-muted-color\"\r\n >\r\n <p class=\"text-sm\">{{ t(\"no-preview-items\") }}</p>\r\n </div>\r\n }\r\n </ng-template>\r\n </mt-card>\r\n </div>\r\n </div>\r\n </div>\r\n</ng-container>\r\n", styles: [":host{display:block;height:100%;width:100%}\n"] }]
|
|
749
|
+
}], ctorParameters: () => [] });
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Generated bundle index. Do not edit.
|
|
753
|
+
*/
|
|
754
|
+
|
|
755
|
+
export { BulkReplaceConfigurations, Customization, CustomizationActionKey, CustomizationFacade, CustomizationState, GetCustomization, GetManagePreviewAreas, GetManagePreviewConfigurations, SetManagePreviewModuleInfo };
|
|
756
|
+
//# sourceMappingURL=masterteam-customization.mjs.map
|