@dignite/ng.site 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,409 @@
1
+ import * as i4 from '@abp/ng.core';
2
+ import { CoreModule, PermissionDirective } from '@abp/ng.core';
3
+ import * as i2 from '@abp/ng.theme.shared';
4
+ import { ConfirmationService, ToasterService, Confirmation, ThemeSharedModule } from '@abp/ng.theme.shared';
5
+ import * as i0 from '@angular/core';
6
+ import { inject, signal, computed, ViewChild, ChangeDetectionStrategy, Component } from '@angular/core';
7
+ import * as i1 from '@angular/forms';
8
+ import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
9
+ import * as i5 from 'ng-zorro-antd/auto-complete';
10
+ import { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete';
11
+ import { switchMap, forkJoin, map } from 'rxjs';
12
+ import { FieldTypeResolver, FlexFieldConfigComponent } from '@dignite/ng.flex-fields';
13
+ import { g as FieldAdminService, S as SiteReferenceDataService, e as eSitePolicyNames } from './dignite-ng.site-dignite-ng.site-CsKxqjY9.mjs';
14
+ import * as i3 from '@ngx-validate/core';
15
+
16
+ const MAX_FIELD_NAME_LENGTH = 64;
17
+ const MAX_FIELD_DISPLAY_NAME_LENGTH = 128;
18
+ const MAX_GROUP_NAME_LENGTH = 64;
19
+ /** Mirrors `IdentifierName.Pattern` (`src/Dignite.Site.Domain.Shared/IdentifierName.cs`). */
20
+ const NAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
21
+ /**
22
+ * localStorage key for per-group collapse state the user has explicitly chosen, keyed by group name
23
+ * (`''` for Ungrouped - a real group name is never empty after {@link FieldsComponent.sections}
24
+ * trims it). Groups absent here fall back to the built-in default.
25
+ */
26
+ const GROUP_COLLAPSE_STATE_STORAGE_KEY = 'Dignite.Site.Fields.GroupCollapseState';
27
+ /**
28
+ * The tenant's field library, listed in one column with a heading per group.
29
+ *
30
+ * **Grouping is a label on the field, not an entity.** `Field.GroupName` is a plain string, so an
31
+ * editor files a field under a new group by typing it - there is no "create the group first" step, and
32
+ * a group stops existing when the last field leaves it.
33
+ *
34
+ * **No Required/Searchable here.** Unlike the flex-fields demo, whose `ProductFieldDto` carries those
35
+ * flags on the definition, Site keeps them per *usage* on `ContentTypeFieldDto` - the same field can be
36
+ * pulled into several content types with different settings (总体设计 §2.3). They are set in the
37
+ * content type's field arrangement.
38
+ */
39
+ class FieldsComponent {
40
+ /**
41
+ * Backs the group `nz-autocomplete`. Filters `knownGroupNames` client-side against the form's current
42
+ * text rather than a server round trip - the list is already loaded, and there is no separate id: a
43
+ * group's key IS its name.
44
+ */
45
+ get filteredGroupNames() {
46
+ const term = (this.form?.get('groupName')?.value ?? '').trim().toLowerCase();
47
+ const names = this.knownGroupNames();
48
+ return term ? names.filter(name => name.toLowerCase().includes(term)) : names;
49
+ }
50
+ constructor() {
51
+ this.fieldService = inject(FieldAdminService);
52
+ this.referenceData = inject(SiteReferenceDataService);
53
+ this.fieldTypeResolver = inject(FieldTypeResolver);
54
+ this.fb = inject(FormBuilder);
55
+ this.confirmation = inject(ConfirmationService);
56
+ this.toaster = inject(ToasterService);
57
+ this.policies = eSitePolicyNames;
58
+ this.fieldTypes = this.fieldTypeResolver.getAll();
59
+ /**
60
+ * The whole field library, unpaged - grouped display has no notion of "current page".
61
+ *
62
+ * State that changes in an HTTP callback (this, `knownGroupNames` and the modal flags below) is a
63
+ * signal, not a plain field: the host layout's ancestors (`abp-router-outlet`, the LeptonX layouts)
64
+ * are OnPush, and a plain-field write in a response callback never marks them for check, so the list
65
+ * would stay empty until some unrelated event happened to.
66
+ */
67
+ this.data = signal({ items: [] }, /* @ts-ignore */
68
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
69
+ this.filters = {};
70
+ /**
71
+ * The whole field library, bucketed by group for rendering. Ungrouped goes last: it is the catch-all,
72
+ * and leading with it would bury the named groups under whatever has not been filed yet.
73
+ */
74
+ this.sections = computed(() => {
75
+ const buckets = new Map();
76
+ const ungrouped = [];
77
+ for (const field of this.data().items ?? []) {
78
+ const groupName = field.groupName?.trim();
79
+ if (!groupName) {
80
+ ungrouped.push(field);
81
+ continue;
82
+ }
83
+ const bucket = buckets.get(groupName);
84
+ if (bucket) {
85
+ bucket.push(field);
86
+ }
87
+ else {
88
+ buckets.set(groupName, [field]);
89
+ }
90
+ }
91
+ const sections = [...buckets.entries()]
92
+ .sort(([a], [b]) => a.localeCompare(b))
93
+ .map(([groupName, fields]) => ({ groupName, fields }));
94
+ if (ungrouped.length > 0) {
95
+ sections.push({ groupName: null, fields: ungrouped });
96
+ }
97
+ return sections;
98
+ }, /* @ts-ignore */
99
+ ...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
100
+ /** Every group name in use, for the editor's suggestion list. */
101
+ this.knownGroupNames = signal([], /* @ts-ignore */
102
+ ...(ngDevMode ? [{ debugName: "knownGroupNames" }] : /* istanbul ignore next */ []));
103
+ /**
104
+ * Per-group collapse state the user has explicitly toggled, keyed by group name (`''` for
105
+ * Ungrouped). Client-side only, persisted in localStorage. A group absent here uses the built-in
106
+ * default: Ungrouped starts open (it is the catch-all, worth seeing at a glance), named groups
107
+ * start closed.
108
+ */
109
+ this.groupCollapseOverrides = new Map(this.loadGroupCollapseOverrides());
110
+ this.isModalOpen = signal(false, /* @ts-ignore */
111
+ ...(ngDevMode ? [{ debugName: "isModalOpen" }] : /* istanbul ignore next */ []));
112
+ this.isModalBusy = signal(false, /* @ts-ignore */
113
+ ...(ngDevMode ? [{ debugName: "isModalBusy" }] : /* istanbul ignore next */ []));
114
+ this.isRenameModalOpen = signal(false, /* @ts-ignore */
115
+ ...(ngDevMode ? [{ debugName: "isRenameModalOpen" }] : /* istanbul ignore next */ []));
116
+ this.isRenameModalBusy = signal(false, /* @ts-ignore */
117
+ ...(ngDevMode ? [{ debugName: "isRenameModalBusy" }] : /* istanbul ignore next */ []));
118
+ this.isGroupModalOpen = signal(false, /* @ts-ignore */
119
+ ...(ngDevMode ? [{ debugName: "isGroupModalOpen" }] : /* istanbul ignore next */ []));
120
+ this.isGroupModalBusy = signal(false, /* @ts-ignore */
121
+ ...(ngDevMode ? [{ debugName: "isGroupModalBusy" }] : /* istanbul ignore next */ []));
122
+ this.search();
123
+ this.loadKnownGroupNames();
124
+ }
125
+ /** Reloads the whole (unpaged) field library; {@link sections} re-buckets it by group. */
126
+ search() {
127
+ this.fieldService.getList({ filter: this.filters.filter }).subscribe(result => {
128
+ this.data.set(result);
129
+ });
130
+ }
131
+ toggleGroup(groupName) {
132
+ this.groupCollapseOverrides.set(groupName ?? '', !this.isGroupCollapsed(groupName));
133
+ this.saveGroupCollapseOverrides();
134
+ }
135
+ isGroupCollapsed(groupName) {
136
+ return this.groupCollapseOverrides.get(groupName ?? '') ?? groupName !== null;
137
+ }
138
+ loadGroupCollapseOverrides() {
139
+ try {
140
+ const raw = localStorage.getItem(GROUP_COLLAPSE_STATE_STORAGE_KEY);
141
+ return raw ? Object.entries(JSON.parse(raw)) : [];
142
+ }
143
+ catch {
144
+ return [];
145
+ }
146
+ }
147
+ saveGroupCollapseOverrides() {
148
+ localStorage.setItem(GROUP_COLLAPSE_STATE_STORAGE_KEY, JSON.stringify(Object.fromEntries(this.groupCollapseOverrides)));
149
+ }
150
+ /**
151
+ * Drawn from the shared reference cache rather than `data().items` directly, so it stays in sync with
152
+ * whatever else on the page (or another component) last invalidated it.
153
+ */
154
+ loadKnownGroupNames() {
155
+ this.referenceData.getFieldsById().subscribe(fields => {
156
+ this.knownGroupNames.set([
157
+ ...new Set([...fields.values()]
158
+ .map(field => field.groupName?.trim())
159
+ .filter((name) => !!name)),
160
+ ].sort((a, b) => a.localeCompare(b)));
161
+ });
162
+ }
163
+ refreshLibrary() {
164
+ this.referenceData.invalidateFields();
165
+ this.loadKnownGroupNames();
166
+ this.search();
167
+ }
168
+ /** Localization key for a field type's display name, for use with `| abpLocalization`. */
169
+ displayNameKeyOf(fieldTypeName) {
170
+ return (this.fieldTypes.find(type => type.name === fieldTypeName)?.displayNameKey ??
171
+ fieldTypeName ??
172
+ '');
173
+ }
174
+ openCreate() {
175
+ this.editingField = undefined;
176
+ this.form = this.buildForm();
177
+ this.isModalOpen.set(true);
178
+ }
179
+ openEdit(field) {
180
+ this.editingField = field;
181
+ this.form = this.buildForm(field);
182
+ this.isModalOpen.set(true);
183
+ }
184
+ closeModal() {
185
+ this.isModalOpen.set(false);
186
+ this.form = undefined;
187
+ this.editingField = undefined;
188
+ }
189
+ save() {
190
+ if (!this.form || this.form.invalid || this.isModalBusy()) {
191
+ return;
192
+ }
193
+ this.isModalBusy.set(true);
194
+ const value = this.form.getRawValue();
195
+ const configuration = this.form.get('configuration')?.value ?? {};
196
+ const groupName = value.groupName?.trim() || null;
197
+ const request$ = this.editingField
198
+ ? this.fieldService.update(this.editingField.id, {
199
+ displayName: value.displayName,
200
+ fieldTypeName: value.fieldTypeName,
201
+ description: value.description,
202
+ configuration,
203
+ groupName,
204
+ })
205
+ : this.fieldService.create({
206
+ name: value.name,
207
+ displayName: value.displayName,
208
+ fieldTypeName: value.fieldTypeName,
209
+ description: value.description,
210
+ configuration,
211
+ groupName,
212
+ });
213
+ request$.subscribe({
214
+ next: () => {
215
+ this.isModalBusy.set(false);
216
+ this.closeModal();
217
+ this.refreshLibrary();
218
+ this.toaster.success('AbpUi::SavedSuccessfully');
219
+ },
220
+ error: () => this.isModalBusy.set(false),
221
+ });
222
+ }
223
+ delete(field) {
224
+ this.confirmation
225
+ .warn('AbpUi::ItemWillBeDeletedMessage', 'AbpUi::AreYouSure', {
226
+ messageLocalizationParams: [field.displayName ?? ''],
227
+ })
228
+ .subscribe((status) => {
229
+ if (status === Confirmation.Status.confirm) {
230
+ // A platform preset answers Site:030002 here; the error handler surfaces the server's own
231
+ // message, which already explains to remove it from an arrangement instead.
232
+ this.fieldService.delete(field.id).subscribe(() => {
233
+ this.refreshLibrary();
234
+ this.toaster.success('AbpUi::SuccessfullyDeleted');
235
+ });
236
+ }
237
+ });
238
+ }
239
+ /**
240
+ * Renaming is deliberately its own operation, not part of the edit form: `UpdateFieldDto` carries no
241
+ * name, the server guards it with its own permission (`SiteAdmin.Fields.Rename`), and it rewrites the
242
+ * stored value of every content using the field.
243
+ */
244
+ openRename(field) {
245
+ this.renamingField = field;
246
+ this.renameForm = this.fb.group({
247
+ newName: [
248
+ field.name ?? '',
249
+ [Validators.required, Validators.maxLength(MAX_FIELD_NAME_LENGTH), Validators.pattern(NAME_PATTERN)],
250
+ ],
251
+ });
252
+ this.isRenameModalOpen.set(true);
253
+ }
254
+ closeRenameModal() {
255
+ this.isRenameModalOpen.set(false);
256
+ this.renameForm = undefined;
257
+ this.renamingField = undefined;
258
+ }
259
+ saveRename() {
260
+ if (!this.renameForm || this.renameForm.invalid || this.isRenameModalBusy()) {
261
+ return;
262
+ }
263
+ this.isRenameModalBusy.set(true);
264
+ this.fieldService.rename(this.renamingField.id, this.renameForm.getRawValue()).subscribe({
265
+ next: () => {
266
+ this.isRenameModalBusy.set(false);
267
+ this.closeRenameModal();
268
+ this.refreshLibrary();
269
+ this.toaster.success('AbpUi::SavedSuccessfully');
270
+ },
271
+ error: () => this.isRenameModalBusy.set(false),
272
+ });
273
+ }
274
+ /**
275
+ * Group is a label, not an entity ({@link FieldGroupSection}), so "editing" one is a bulk rename of
276
+ * `groupName` across every field that carries it - scoped to the whole library via a fresh fetch in
277
+ * `fieldsInGroup`, not just `section.fields` (which reflects whatever the search box currently
278
+ * filters `data().items` to).
279
+ */
280
+ openEditGroup(groupName) {
281
+ this.renamingGroupName = groupName;
282
+ this.groupForm = this.fb.group({
283
+ groupName: [groupName, [Validators.required, Validators.maxLength(MAX_GROUP_NAME_LENGTH)]],
284
+ });
285
+ this.isGroupModalOpen.set(true);
286
+ }
287
+ closeGroupModal() {
288
+ this.isGroupModalOpen.set(false);
289
+ this.groupForm = undefined;
290
+ this.renamingGroupName = undefined;
291
+ }
292
+ saveGroupRename() {
293
+ if (!this.groupForm || this.groupForm.invalid || this.isGroupModalBusy() || !this.renamingGroupName) {
294
+ return;
295
+ }
296
+ const oldGroupName = this.renamingGroupName;
297
+ const newGroupName = this.groupForm.getRawValue().groupName.trim();
298
+ if (newGroupName === oldGroupName) {
299
+ this.closeGroupModal();
300
+ return;
301
+ }
302
+ this.isGroupModalBusy.set(true);
303
+ this.fieldsInGroup(oldGroupName)
304
+ .pipe(switchMap(fields => forkJoin(fields.map(field => this.updateFieldGroupName(field, newGroupName)))))
305
+ .subscribe({
306
+ next: () => {
307
+ this.isGroupModalBusy.set(false);
308
+ this.closeGroupModal();
309
+ this.refreshLibrary();
310
+ this.toaster.success('AbpUi::SavedSuccessfully');
311
+ },
312
+ error: () => this.isGroupModalBusy.set(false),
313
+ });
314
+ }
315
+ /** Ungroups every field in the group - the group itself is only ever the fields that carry its name. */
316
+ deleteGroup(groupName) {
317
+ this.fieldsInGroup(groupName).subscribe(fields => {
318
+ this.confirmation
319
+ .warn('Site::UngroupConfirmMessage', 'AbpUi::AreYouSure', {
320
+ messageLocalizationParams: [String(fields.length), groupName],
321
+ })
322
+ .subscribe((status) => {
323
+ if (status === Confirmation.Status.confirm) {
324
+ forkJoin(fields.map(field => this.updateFieldGroupName(field, null))).subscribe(() => {
325
+ this.refreshLibrary();
326
+ this.toaster.success('AbpUi::SuccessfullyDeleted');
327
+ });
328
+ }
329
+ });
330
+ });
331
+ }
332
+ /**
333
+ * The group's full membership, library-wide - a fresh, unfiltered fetch so a rename/ungroup is never
334
+ * scoped down by whatever the search box currently filters `data().items` to.
335
+ */
336
+ fieldsInGroup(groupName) {
337
+ return this.fieldService
338
+ .getList({})
339
+ .pipe(map(result => (result.items ?? []).filter(field => field.groupName === groupName)));
340
+ }
341
+ updateFieldGroupName(field, groupName) {
342
+ return this.fieldService.update(field.id, {
343
+ displayName: field.displayName,
344
+ fieldTypeName: field.fieldTypeName,
345
+ description: field.description,
346
+ configuration: field.configuration,
347
+ groupName,
348
+ });
349
+ }
350
+ buildForm(field) {
351
+ this.selectedFieldData = field
352
+ ? {
353
+ id: field.id,
354
+ name: field.name,
355
+ displayName: field.displayName,
356
+ description: field.description ?? undefined,
357
+ fieldTypeName: field.fieldTypeName,
358
+ configuration: (field.configuration ?? {}),
359
+ }
360
+ : undefined;
361
+ return this.fb.group({
362
+ // Fixed after creation - renaming goes through `openRename`. Kept in the form (disabled) so the
363
+ // modal can show what it is.
364
+ name: [
365
+ { value: field?.name ?? '', disabled: !!field },
366
+ [Validators.required, Validators.maxLength(MAX_FIELD_NAME_LENGTH), Validators.pattern(NAME_PATTERN)],
367
+ ],
368
+ displayName: [
369
+ field?.displayName ?? '',
370
+ [Validators.required, Validators.maxLength(MAX_FIELD_DISPLAY_NAME_LENGTH)],
371
+ ],
372
+ description: [field?.description ?? ''],
373
+ // Free text with a suggestion list rather than a closed dropdown: filing a field under a new
374
+ // group is just typing its name.
375
+ groupName: [field?.groupName ?? '', [Validators.maxLength(MAX_GROUP_NAME_LENGTH)]],
376
+ // Also fixed after creation: changing the type would orphan every stored value. Still bound so
377
+ // `ff-flex-field-config` can pick which configuration editor to render.
378
+ fieldTypeName: [
379
+ { value: field?.fieldTypeName ?? this.fieldTypes[0]?.name ?? '', disabled: !!field },
380
+ Validators.required,
381
+ ],
382
+ });
383
+ }
384
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.2", ngImport: i0, type: FieldsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
385
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.2", type: FieldsComponent, isStandalone: true, selector: "site-fields", viewQueries: [{ propertyName: "submitButton", first: true, predicate: ["submitButton"], descendants: true }, { propertyName: "renameSubmitButton", first: true, predicate: ["renameSubmitButton"], descendants: true }, { propertyName: "groupSubmitButton", first: true, predicate: ["groupSubmitButton"], descendants: true }], ngImport: i0, template: "<div class=\"card\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h5 class=\"mb-0\">{{ 'Site::Fields' | abpLocalization }}</h5>\n <button\n *abpPermission=\"policies.FieldsCreate\"\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"openCreate()\"\n >\n <i class=\"fa fa-plus me-1\"></i>{{ 'Site::NewField' | abpLocalization }}\n </button>\n </div>\n\n <div class=\"row mb-3\">\n <div class=\"col-md-4\">\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"filters.filter\"\n (keyup.enter)=\"search()\"\n [placeholder]=\"'AbpUi::Search' | abpLocalization\"\n />\n </div>\n </div>\n\n <div class=\"table-responsive\">\n <table class=\"table align-middle mb-0\">\n <thead>\n <tr>\n <th>{{ 'Site::DisplayName:Field.DisplayName' | abpLocalization }}</th>\n <th>{{ 'Site::DisplayName:Field.Name' | abpLocalization }}</th>\n <th>{{ 'Site::DisplayName:Field.FieldTypeName' | abpLocalization }}</th>\n <th>{{ 'AbpUi::Actions' | abpLocalization }}</th>\n </tr>\n </thead>\n <tbody>\n @for (section of sections(); track section.groupName) {\n <tr class=\"table-light\">\n <th colspan=\"4\" class=\"fw-semibold\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <span style=\"cursor: pointer\" (click)=\"toggleGroup(section.groupName)\">\n <i\n class=\"fa me-2 text-muted\"\n [class.fa-chevron-down]=\"!isGroupCollapsed(section.groupName)\"\n [class.fa-chevron-right]=\"isGroupCollapsed(section.groupName)\"\n ></i>\n @if (section.groupName) {\n <i class=\"fa fa-folder me-1 text-muted\"></i>{{ section.groupName }}\n } @else {\n <i class=\"fa fa-folder-open me-1 text-muted\"></i>\n <span class=\"text-muted\">{{ 'Site::Ungrouped' | abpLocalization }}</span>\n }\n </span>\n @if (section.groupName) {\n <span>\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openEditGroup(section.groupName!)\"\n [title]=\"'AbpUi::Edit' | abpLocalization\"\n >\n <i class=\"fa fa-pencil\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light\"\n (click)=\"deleteGroup(section.groupName!)\"\n [title]=\"'AbpUi::Delete' | abpLocalization\"\n >\n <i class=\"fa fa-trash text-danger\"></i>\n </button>\n </span>\n }\n </div>\n </th>\n </tr>\n @if (!isGroupCollapsed(section.groupName)) {\n @for (field of section.fields; track field.id) {\n <tr>\n <td>\n <div>{{ field.displayName }}</div>\n @if (field.description) {\n <small class=\"text-muted\">{{ field.description }}</small>\n }\n </td>\n <td>{{ field.name }}</td>\n <td>{{ displayNameKeyOf(field.fieldTypeName) | abpLocalization }}</td>\n <td class=\"text-nowrap text-end\">\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openEdit(field)\"\n [title]=\"'AbpUi::Edit' | abpLocalization\"\n >\n <i class=\"fa fa-pencil\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsRename\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openRename(field)\"\n [title]=\"'Site::RenameField' | abpLocalization\"\n >\n <i class=\"fa fa-i-cursor\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsDelete\"\n type=\"button\"\n class=\"btn btn-sm btn-light\"\n (click)=\"delete(field)\"\n [title]=\"'AbpUi::Delete' | abpLocalization\"\n >\n <i class=\"fa fa-trash text-danger\"></i>\n </button>\n </td>\n </tr>\n }\n }\n } @empty {\n <tr>\n <td colspan=\"4\" class=\"text-muted\">\n {{ 'AbpUi::NoDataAvailableInDatatable' | abpLocalization }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n</div>\n\n<abp-modal\n [(visible)]=\"isModalOpen\"\n [busy]=\"isModalBusy()\"\n [options]=\"{ size: 'xl' }\"\n (disappear)=\"closeModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ (editingField ? 'Site::EditField' : 'Site::NewField') | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (form) {\n <form [formGroup]=\"form\" (submit)=\"save()\">\n <button type=\"submit\" #submitButton style=\"display: none\"></button>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.DisplayName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"displayName\" />\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.Name' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"name\" />\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Name' | abpLocalization }}\n </small>\n </div>\n\n <!-- Description sits high on purpose: it is product copy aimed at AI, which an MCP client\n reads to fill the field correctly (\u603B\u4F53\u8BBE\u8BA1 \u00A76.2.6), not an optional footnote. -->\n <div class=\"mb-3\">\n <label class=\"form-label fw-semibold\">\n {{ 'Site::DisplayName:Field.Description' | abpLocalization }}\n </label>\n <textarea class=\"form-control\" formControlName=\"description\" rows=\"3\"></textarea>\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Description' | abpLocalization }}\n </small>\n </div>\n\n <!-- Above the field type, and free text: filing under a new group is just typing its name. -->\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.Group' | abpLocalization }}</label>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"groupName\"\n [nzAutocomplete]=\"groupNameAuto\"\n />\n <nz-autocomplete #groupNameAuto [nzDataSource]=\"filteredGroupNames\"></nz-autocomplete>\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Group' | abpLocalization }}\n </small>\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.FieldTypeName' | abpLocalization }}</label>\n <select class=\"form-select\" formControlName=\"fieldTypeName\">\n @for (fieldType of fieldTypes; track fieldType.name) {\n <option [value]=\"fieldType.name\">{{ fieldType.displayNameKey | abpLocalization }}</option>\n }\n </select>\n @if (editingField) {\n <small class=\"form-text text-muted\">\n {{ 'Site::FieldTypeFixedAfterCreation' | abpLocalization }}\n </small>\n }\n </div>\n\n <hr />\n <h6>{{ 'Site::DisplayName:Field.Configuration' | abpLocalization }}</h6>\n\n <ff-flex-field-config\n [type]=\"form.getRawValue().fieldTypeName\"\n [selected]=\"selectedFieldData\"\n [form]=\"form\"\n ></ff-flex-field-config>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!form?.valid\"\n [loading]=\"isModalBusy()\"\n (click)=\"submitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n\n<abp-modal\n [(visible)]=\"isRenameModalOpen\"\n [busy]=\"isRenameModalBusy()\"\n (disappear)=\"closeRenameModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ 'Site::RenameField' | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (renameForm) {\n <form [formGroup]=\"renameForm\" (submit)=\"saveRename()\">\n <button type=\"submit\" #renameSubmitButton style=\"display: none\"></button>\n\n <div class=\"alert alert-warning\">\n {{ 'Site::RenameFieldWarning' | abpLocalization }}\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.NewName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"newName\" />\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.NewName' | abpLocalization }}\n </small>\n </div>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!renameForm?.valid\"\n [loading]=\"isRenameModalBusy()\"\n (click)=\"renameSubmitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n\n<abp-modal\n [(visible)]=\"isGroupModalOpen\"\n [busy]=\"isGroupModalBusy()\"\n (disappear)=\"closeGroupModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ 'Site::RenameGroup' | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (groupForm) {\n <form [formGroup]=\"groupForm\" (submit)=\"saveGroupRename()\">\n <button type=\"submit\" #groupSubmitButton style=\"display: none\"></button>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Group.NewName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"groupName\" />\n </div>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!groupForm?.valid\"\n [loading]=\"isGroupModalBusy()\"\n (click)=\"groupSubmitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3.ValidationGroupDirective, selector: "[formGroup],[formGroupName]", exportAs: ["validationGroup"] }, { kind: "directive", type: i3.ValidationDirective, selector: "[formControl],[formControlName]", exportAs: ["validationDirective"] }, { kind: "directive", type: i4.PermissionDirective, selector: "[abpPermission]", inputs: ["abpPermission", "abpPermissionRunChangeDetection"] }, { kind: "ngmodule", type: ThemeSharedModule }, { kind: "component", type: i2.ButtonComponent, selector: "abp-button", inputs: ["buttonId", "buttonClass", "buttonType", "formName", "iconClass", "loading", "disabled", "attributes"], outputs: ["click", "focus", "blur", "abpClick", "abpFocus", "abpBlur"] }, { kind: "component", type: i2.ModalComponent, selector: "abp-modal", inputs: ["visible", "busy", "options", "suppressUnsavedChangesWarning"], outputs: ["visibleChange", "init", "appear", "disappear"] }, { kind: "directive", type: i2.ModalCloseDirective, selector: "[abpClose]" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: FlexFieldConfigComponent, selector: "ff-flex-field-config", inputs: ["type", "selected", "form"] }, { kind: "ngmodule", type: NzAutocompleteModule }, { kind: "component", type: i5.NzAutocompleteComponent, selector: "nz-autocomplete", inputs: ["nzWidth", "nzOverlayClassName", "nzOverlayStyle", "nzDefaultActiveFirstOption", "nzBackfill", "compareWith", "nzDataSource"], outputs: ["selectionChange"], exportAs: ["nzAutocomplete"] }, { kind: "directive", type: i5.NzAutocompleteTriggerDirective, selector: "input[nzAutocomplete], textarea[nzAutocomplete]", inputs: ["nzAutocomplete"], exportAs: ["nzAutocompleteTrigger"] }, { kind: "pipe", type: i4.LocalizationPipe, name: "abpLocalization" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
386
+ }
387
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.2", ngImport: i0, type: FieldsComponent, decorators: [{
388
+ type: Component,
389
+ args: [{ selector: 'site-fields', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
390
+ CoreModule,
391
+ ThemeSharedModule,
392
+ ReactiveFormsModule,
393
+ PermissionDirective,
394
+ FlexFieldConfigComponent,
395
+ NzAutocompleteModule,
396
+ ], template: "<div class=\"card\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h5 class=\"mb-0\">{{ 'Site::Fields' | abpLocalization }}</h5>\n <button\n *abpPermission=\"policies.FieldsCreate\"\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"openCreate()\"\n >\n <i class=\"fa fa-plus me-1\"></i>{{ 'Site::NewField' | abpLocalization }}\n </button>\n </div>\n\n <div class=\"row mb-3\">\n <div class=\"col-md-4\">\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"filters.filter\"\n (keyup.enter)=\"search()\"\n [placeholder]=\"'AbpUi::Search' | abpLocalization\"\n />\n </div>\n </div>\n\n <div class=\"table-responsive\">\n <table class=\"table align-middle mb-0\">\n <thead>\n <tr>\n <th>{{ 'Site::DisplayName:Field.DisplayName' | abpLocalization }}</th>\n <th>{{ 'Site::DisplayName:Field.Name' | abpLocalization }}</th>\n <th>{{ 'Site::DisplayName:Field.FieldTypeName' | abpLocalization }}</th>\n <th>{{ 'AbpUi::Actions' | abpLocalization }}</th>\n </tr>\n </thead>\n <tbody>\n @for (section of sections(); track section.groupName) {\n <tr class=\"table-light\">\n <th colspan=\"4\" class=\"fw-semibold\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <span style=\"cursor: pointer\" (click)=\"toggleGroup(section.groupName)\">\n <i\n class=\"fa me-2 text-muted\"\n [class.fa-chevron-down]=\"!isGroupCollapsed(section.groupName)\"\n [class.fa-chevron-right]=\"isGroupCollapsed(section.groupName)\"\n ></i>\n @if (section.groupName) {\n <i class=\"fa fa-folder me-1 text-muted\"></i>{{ section.groupName }}\n } @else {\n <i class=\"fa fa-folder-open me-1 text-muted\"></i>\n <span class=\"text-muted\">{{ 'Site::Ungrouped' | abpLocalization }}</span>\n }\n </span>\n @if (section.groupName) {\n <span>\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openEditGroup(section.groupName!)\"\n [title]=\"'AbpUi::Edit' | abpLocalization\"\n >\n <i class=\"fa fa-pencil\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light\"\n (click)=\"deleteGroup(section.groupName!)\"\n [title]=\"'AbpUi::Delete' | abpLocalization\"\n >\n <i class=\"fa fa-trash text-danger\"></i>\n </button>\n </span>\n }\n </div>\n </th>\n </tr>\n @if (!isGroupCollapsed(section.groupName)) {\n @for (field of section.fields; track field.id) {\n <tr>\n <td>\n <div>{{ field.displayName }}</div>\n @if (field.description) {\n <small class=\"text-muted\">{{ field.description }}</small>\n }\n </td>\n <td>{{ field.name }}</td>\n <td>{{ displayNameKeyOf(field.fieldTypeName) | abpLocalization }}</td>\n <td class=\"text-nowrap text-end\">\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openEdit(field)\"\n [title]=\"'AbpUi::Edit' | abpLocalization\"\n >\n <i class=\"fa fa-pencil\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsRename\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openRename(field)\"\n [title]=\"'Site::RenameField' | abpLocalization\"\n >\n <i class=\"fa fa-i-cursor\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsDelete\"\n type=\"button\"\n class=\"btn btn-sm btn-light\"\n (click)=\"delete(field)\"\n [title]=\"'AbpUi::Delete' | abpLocalization\"\n >\n <i class=\"fa fa-trash text-danger\"></i>\n </button>\n </td>\n </tr>\n }\n }\n } @empty {\n <tr>\n <td colspan=\"4\" class=\"text-muted\">\n {{ 'AbpUi::NoDataAvailableInDatatable' | abpLocalization }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n</div>\n\n<abp-modal\n [(visible)]=\"isModalOpen\"\n [busy]=\"isModalBusy()\"\n [options]=\"{ size: 'xl' }\"\n (disappear)=\"closeModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ (editingField ? 'Site::EditField' : 'Site::NewField') | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (form) {\n <form [formGroup]=\"form\" (submit)=\"save()\">\n <button type=\"submit\" #submitButton style=\"display: none\"></button>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.DisplayName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"displayName\" />\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.Name' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"name\" />\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Name' | abpLocalization }}\n </small>\n </div>\n\n <!-- Description sits high on purpose: it is product copy aimed at AI, which an MCP client\n reads to fill the field correctly (\u603B\u4F53\u8BBE\u8BA1 \u00A76.2.6), not an optional footnote. -->\n <div class=\"mb-3\">\n <label class=\"form-label fw-semibold\">\n {{ 'Site::DisplayName:Field.Description' | abpLocalization }}\n </label>\n <textarea class=\"form-control\" formControlName=\"description\" rows=\"3\"></textarea>\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Description' | abpLocalization }}\n </small>\n </div>\n\n <!-- Above the field type, and free text: filing under a new group is just typing its name. -->\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.Group' | abpLocalization }}</label>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"groupName\"\n [nzAutocomplete]=\"groupNameAuto\"\n />\n <nz-autocomplete #groupNameAuto [nzDataSource]=\"filteredGroupNames\"></nz-autocomplete>\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Group' | abpLocalization }}\n </small>\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.FieldTypeName' | abpLocalization }}</label>\n <select class=\"form-select\" formControlName=\"fieldTypeName\">\n @for (fieldType of fieldTypes; track fieldType.name) {\n <option [value]=\"fieldType.name\">{{ fieldType.displayNameKey | abpLocalization }}</option>\n }\n </select>\n @if (editingField) {\n <small class=\"form-text text-muted\">\n {{ 'Site::FieldTypeFixedAfterCreation' | abpLocalization }}\n </small>\n }\n </div>\n\n <hr />\n <h6>{{ 'Site::DisplayName:Field.Configuration' | abpLocalization }}</h6>\n\n <ff-flex-field-config\n [type]=\"form.getRawValue().fieldTypeName\"\n [selected]=\"selectedFieldData\"\n [form]=\"form\"\n ></ff-flex-field-config>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!form?.valid\"\n [loading]=\"isModalBusy()\"\n (click)=\"submitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n\n<abp-modal\n [(visible)]=\"isRenameModalOpen\"\n [busy]=\"isRenameModalBusy()\"\n (disappear)=\"closeRenameModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ 'Site::RenameField' | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (renameForm) {\n <form [formGroup]=\"renameForm\" (submit)=\"saveRename()\">\n <button type=\"submit\" #renameSubmitButton style=\"display: none\"></button>\n\n <div class=\"alert alert-warning\">\n {{ 'Site::RenameFieldWarning' | abpLocalization }}\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.NewName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"newName\" />\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.NewName' | abpLocalization }}\n </small>\n </div>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!renameForm?.valid\"\n [loading]=\"isRenameModalBusy()\"\n (click)=\"renameSubmitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n\n<abp-modal\n [(visible)]=\"isGroupModalOpen\"\n [busy]=\"isGroupModalBusy()\"\n (disappear)=\"closeGroupModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ 'Site::RenameGroup' | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (groupForm) {\n <form [formGroup]=\"groupForm\" (submit)=\"saveGroupRename()\">\n <button type=\"submit\" #groupSubmitButton style=\"display: none\"></button>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Group.NewName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"groupName\" />\n </div>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!groupForm?.valid\"\n [loading]=\"isGroupModalBusy()\"\n (click)=\"groupSubmitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n" }]
397
+ }], ctorParameters: () => [], propDecorators: { submitButton: [{
398
+ type: ViewChild,
399
+ args: ['submitButton']
400
+ }], renameSubmitButton: [{
401
+ type: ViewChild,
402
+ args: ['renameSubmitButton']
403
+ }], groupSubmitButton: [{
404
+ type: ViewChild,
405
+ args: ['groupSubmitButton']
406
+ }] } });
407
+
408
+ export { FieldsComponent };
409
+ //# sourceMappingURL=dignite-ng.site-fields.component-By_YyfIx.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dignite-ng.site-fields.component-By_YyfIx.mjs","sources":["../../../projects/site/src/lib/components/fields/fields.component.ts","../../../projects/site/src/lib/components/fields/fields.component.html"],"sourcesContent":["import { CoreModule, ListResultDto, PermissionDirective } from '@abp/ng.core';\nimport {\n Confirmation,\n ConfirmationService,\n ThemeSharedModule,\n ToasterService,\n} from '@abp/ng.theme.shared';\nimport {\n Component,\n ElementRef,\n ViewChild,\n computed,\n inject,\n signal,\n ChangeDetectionStrategy,\n} from '@angular/core';\nimport { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';\nimport { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete';\nimport { Observable, forkJoin, map, switchMap } from 'rxjs';\nimport {\n FieldTypeDefinition,\n FieldTypeResolver,\n FlexFieldConfigComponent,\n} from '@dignite/ng.flex-fields';\nimport type { FlexFieldData } from '@dignite/ng.flex-fields';\nimport { FieldAdminService } from '../../proxy/dignite/site/admin/fields/field-admin.service';\nimport type { GetFieldListInput } from '../../proxy/dignite/site/admin/fields/models';\nimport type { FieldDto } from '../../proxy/dignite/site/fields/models';\nimport { eSitePolicyNames } from '../../enums/policy-names';\nimport { SiteReferenceDataService } from '../../services/site-reference-data.service';\n\nconst MAX_FIELD_NAME_LENGTH = 64;\nconst MAX_FIELD_DISPLAY_NAME_LENGTH = 128;\nconst MAX_GROUP_NAME_LENGTH = 64;\n\n/** Mirrors `IdentifierName.Pattern` (`src/Dignite.Site.Domain.Shared/IdentifierName.cs`). */\nconst NAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;\n\n/**\n * localStorage key for per-group collapse state the user has explicitly chosen, keyed by group name\n * (`''` for Ungrouped - a real group name is never empty after {@link FieldsComponent.sections}\n * trims it). Groups absent here fall back to the built-in default.\n */\nconst GROUP_COLLAPSE_STATE_STORAGE_KEY = 'Dignite.Site.Fields.GroupCollapseState';\n\n/** One group heading plus the fields filed under it. */\ninterface FieldGroupSection {\n /** Null for the ungrouped fields, which sort last. */\n groupName: string | null;\n fields: FieldDto[];\n}\n\n/**\n * The tenant's field library, listed in one column with a heading per group.\n *\n * **Grouping is a label on the field, not an entity.** `Field.GroupName` is a plain string, so an\n * editor files a field under a new group by typing it - there is no \"create the group first\" step, and\n * a group stops existing when the last field leaves it.\n *\n * **No Required/Searchable here.** Unlike the flex-fields demo, whose `ProductFieldDto` carries those\n * flags on the definition, Site keeps them per *usage* on `ContentTypeFieldDto` - the same field can be\n * pulled into several content types with different settings (总体设计 §2.3). They are set in the\n * content type's field arrangement.\n */\n@Component({\n selector: 'site-fields',\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './fields.component.html',\n imports: [\n CoreModule,\n ThemeSharedModule,\n ReactiveFormsModule,\n PermissionDirective,\n FlexFieldConfigComponent,\n NzAutocompleteModule,\n ],\n})\nexport class FieldsComponent {\n private readonly fieldService = inject(FieldAdminService);\n private readonly referenceData = inject(SiteReferenceDataService);\n private readonly fieldTypeResolver = inject(FieldTypeResolver);\n private readonly fb = inject(FormBuilder);\n private readonly confirmation = inject(ConfirmationService);\n private readonly toaster = inject(ToasterService);\n\n readonly policies = eSitePolicyNames;\n readonly fieldTypes: readonly FieldTypeDefinition[] = this.fieldTypeResolver.getAll();\n\n /**\n * The whole field library, unpaged - grouped display has no notion of \"current page\".\n *\n * State that changes in an HTTP callback (this, `knownGroupNames` and the modal flags below) is a\n * signal, not a plain field: the host layout's ancestors (`abp-router-outlet`, the LeptonX layouts)\n * are OnPush, and a plain-field write in a response callback never marks them for check, so the list\n * would stay empty until some unrelated event happened to.\n */\n readonly data = signal<ListResultDto<FieldDto>>({ items: [] });\n filters = {} as GetFieldListInput;\n\n /**\n * The whole field library, bucketed by group for rendering. Ungrouped goes last: it is the catch-all,\n * and leading with it would bury the named groups under whatever has not been filed yet.\n */\n readonly sections = computed<FieldGroupSection[]>(() => {\n const buckets = new Map<string, FieldDto[]>();\n const ungrouped: FieldDto[] = [];\n\n for (const field of this.data().items ?? []) {\n const groupName = field.groupName?.trim();\n if (!groupName) {\n ungrouped.push(field);\n continue;\n }\n const bucket = buckets.get(groupName);\n if (bucket) {\n bucket.push(field);\n } else {\n buckets.set(groupName, [field]);\n }\n }\n\n const sections: FieldGroupSection[] = [...buckets.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([groupName, fields]) => ({ groupName, fields }));\n\n if (ungrouped.length > 0) {\n sections.push({ groupName: null, fields: ungrouped });\n }\n\n return sections;\n });\n\n /** Every group name in use, for the editor's suggestion list. */\n readonly knownGroupNames = signal<string[]>([]);\n\n /**\n * Per-group collapse state the user has explicitly toggled, keyed by group name (`''` for\n * Ungrouped). Client-side only, persisted in localStorage. A group absent here uses the built-in\n * default: Ungrouped starts open (it is the catch-all, worth seeing at a glance), named groups\n * start closed.\n */\n private readonly groupCollapseOverrides = new Map<string, boolean>(this.loadGroupCollapseOverrides());\n\n /**\n * Backs the group `nz-autocomplete`. Filters `knownGroupNames` client-side against the form's current\n * text rather than a server round trip - the list is already loaded, and there is no separate id: a\n * group's key IS its name.\n */\n get filteredGroupNames(): string[] {\n const term = ((this.form?.get('groupName')?.value as string) ?? '').trim().toLowerCase();\n const names = this.knownGroupNames();\n return term ? names.filter(name => name.toLowerCase().includes(term)) : names;\n }\n\n readonly isModalOpen = signal(false);\n readonly isModalBusy = signal(false);\n editingField?: FieldDto;\n form?: FormGroup;\n selectedFieldData?: FlexFieldData;\n\n readonly isRenameModalOpen = signal(false);\n readonly isRenameModalBusy = signal(false);\n renamingField?: FieldDto;\n renameForm?: FormGroup;\n\n readonly isGroupModalOpen = signal(false);\n readonly isGroupModalBusy = signal(false);\n renamingGroupName?: string;\n groupForm?: FormGroup;\n\n @ViewChild('submitButton') submitButton?: ElementRef<HTMLButtonElement>;\n @ViewChild('renameSubmitButton') renameSubmitButton?: ElementRef<HTMLButtonElement>;\n @ViewChild('groupSubmitButton') groupSubmitButton?: ElementRef<HTMLButtonElement>;\n\n constructor() {\n this.search();\n this.loadKnownGroupNames();\n }\n\n /** Reloads the whole (unpaged) field library; {@link sections} re-buckets it by group. */\n search(): void {\n this.fieldService.getList({ filter: this.filters.filter }).subscribe(result => {\n this.data.set(result);\n });\n }\n\n toggleGroup(groupName: string | null): void {\n this.groupCollapseOverrides.set(groupName ?? '', !this.isGroupCollapsed(groupName));\n this.saveGroupCollapseOverrides();\n }\n\n isGroupCollapsed(groupName: string | null): boolean {\n return this.groupCollapseOverrides.get(groupName ?? '') ?? groupName !== null;\n }\n\n private loadGroupCollapseOverrides(): [string, boolean][] {\n try {\n const raw = localStorage.getItem(GROUP_COLLAPSE_STATE_STORAGE_KEY);\n return raw ? Object.entries(JSON.parse(raw) as Record<string, boolean>) : [];\n } catch {\n return [];\n }\n }\n\n private saveGroupCollapseOverrides(): void {\n localStorage.setItem(\n GROUP_COLLAPSE_STATE_STORAGE_KEY,\n JSON.stringify(Object.fromEntries(this.groupCollapseOverrides)),\n );\n }\n\n /**\n * Drawn from the shared reference cache rather than `data().items` directly, so it stays in sync with\n * whatever else on the page (or another component) last invalidated it.\n */\n private loadKnownGroupNames(): void {\n this.referenceData.getFieldsById().subscribe(fields => {\n this.knownGroupNames.set(\n [\n ...new Set(\n [...fields.values()]\n .map(field => field.groupName?.trim())\n .filter((name): name is string => !!name),\n ),\n ].sort((a, b) => a.localeCompare(b)),\n );\n });\n }\n\n private refreshLibrary(): void {\n this.referenceData.invalidateFields();\n this.loadKnownGroupNames();\n this.search();\n }\n\n /** Localization key for a field type's display name, for use with `| abpLocalization`. */\n displayNameKeyOf(fieldTypeName?: string): string {\n return (\n this.fieldTypes.find(type => type.name === fieldTypeName)?.displayNameKey ??\n fieldTypeName ??\n ''\n );\n }\n\n openCreate(): void {\n this.editingField = undefined;\n this.form = this.buildForm();\n this.isModalOpen.set(true);\n }\n\n openEdit(field: FieldDto): void {\n this.editingField = field;\n this.form = this.buildForm(field);\n this.isModalOpen.set(true);\n }\n\n closeModal(): void {\n this.isModalOpen.set(false);\n this.form = undefined;\n this.editingField = undefined;\n }\n\n save(): void {\n if (!this.form || this.form.invalid || this.isModalBusy()) {\n return;\n }\n\n this.isModalBusy.set(true);\n const value = this.form.getRawValue();\n const configuration = this.form.get('configuration')?.value ?? {};\n const groupName = (value.groupName as string)?.trim() || null;\n\n const request$ = this.editingField\n ? this.fieldService.update(this.editingField.id!, {\n displayName: value.displayName,\n fieldTypeName: value.fieldTypeName,\n description: value.description,\n configuration,\n groupName,\n })\n : this.fieldService.create({\n name: value.name,\n displayName: value.displayName,\n fieldTypeName: value.fieldTypeName,\n description: value.description,\n configuration,\n groupName,\n });\n\n request$.subscribe({\n next: () => {\n this.isModalBusy.set(false);\n this.closeModal();\n this.refreshLibrary();\n this.toaster.success('AbpUi::SavedSuccessfully');\n },\n error: () => this.isModalBusy.set(false),\n });\n }\n\n delete(field: FieldDto): void {\n this.confirmation\n .warn('AbpUi::ItemWillBeDeletedMessage', 'AbpUi::AreYouSure', {\n messageLocalizationParams: [field.displayName ?? ''],\n })\n .subscribe((status: Confirmation.Status) => {\n if (status === Confirmation.Status.confirm) {\n // A platform preset answers Site:030002 here; the error handler surfaces the server's own\n // message, which already explains to remove it from an arrangement instead.\n this.fieldService.delete(field.id!).subscribe(() => {\n this.refreshLibrary();\n this.toaster.success('AbpUi::SuccessfullyDeleted');\n });\n }\n });\n }\n\n /**\n * Renaming is deliberately its own operation, not part of the edit form: `UpdateFieldDto` carries no\n * name, the server guards it with its own permission (`SiteAdmin.Fields.Rename`), and it rewrites the\n * stored value of every content using the field.\n */\n openRename(field: FieldDto): void {\n this.renamingField = field;\n this.renameForm = this.fb.group({\n newName: [\n field.name ?? '',\n [Validators.required, Validators.maxLength(MAX_FIELD_NAME_LENGTH), Validators.pattern(NAME_PATTERN)],\n ],\n });\n this.isRenameModalOpen.set(true);\n }\n\n closeRenameModal(): void {\n this.isRenameModalOpen.set(false);\n this.renameForm = undefined;\n this.renamingField = undefined;\n }\n\n saveRename(): void {\n if (!this.renameForm || this.renameForm.invalid || this.isRenameModalBusy()) {\n return;\n }\n\n this.isRenameModalBusy.set(true);\n\n this.fieldService.rename(this.renamingField!.id!, this.renameForm.getRawValue()).subscribe({\n next: () => {\n this.isRenameModalBusy.set(false);\n this.closeRenameModal();\n this.refreshLibrary();\n this.toaster.success('AbpUi::SavedSuccessfully');\n },\n error: () => this.isRenameModalBusy.set(false),\n });\n }\n\n /**\n * Group is a label, not an entity ({@link FieldGroupSection}), so \"editing\" one is a bulk rename of\n * `groupName` across every field that carries it - scoped to the whole library via a fresh fetch in\n * `fieldsInGroup`, not just `section.fields` (which reflects whatever the search box currently\n * filters `data().items` to).\n */\n openEditGroup(groupName: string): void {\n this.renamingGroupName = groupName;\n this.groupForm = this.fb.group({\n groupName: [groupName, [Validators.required, Validators.maxLength(MAX_GROUP_NAME_LENGTH)]],\n });\n this.isGroupModalOpen.set(true);\n }\n\n closeGroupModal(): void {\n this.isGroupModalOpen.set(false);\n this.groupForm = undefined;\n this.renamingGroupName = undefined;\n }\n\n saveGroupRename(): void {\n if (!this.groupForm || this.groupForm.invalid || this.isGroupModalBusy() || !this.renamingGroupName) {\n return;\n }\n\n const oldGroupName = this.renamingGroupName;\n const newGroupName = (this.groupForm.getRawValue().groupName as string).trim();\n\n if (newGroupName === oldGroupName) {\n this.closeGroupModal();\n return;\n }\n\n this.isGroupModalBusy.set(true);\n this.fieldsInGroup(oldGroupName)\n .pipe(\n switchMap(fields => forkJoin(fields.map(field => this.updateFieldGroupName(field, newGroupName)))),\n )\n .subscribe({\n next: () => {\n this.isGroupModalBusy.set(false);\n this.closeGroupModal();\n this.refreshLibrary();\n this.toaster.success('AbpUi::SavedSuccessfully');\n },\n error: () => this.isGroupModalBusy.set(false),\n });\n }\n\n /** Ungroups every field in the group - the group itself is only ever the fields that carry its name. */\n deleteGroup(groupName: string): void {\n this.fieldsInGroup(groupName).subscribe(fields => {\n this.confirmation\n .warn('Site::UngroupConfirmMessage', 'AbpUi::AreYouSure', {\n messageLocalizationParams: [String(fields.length), groupName],\n })\n .subscribe((status: Confirmation.Status) => {\n if (status === Confirmation.Status.confirm) {\n forkJoin(fields.map(field => this.updateFieldGroupName(field, null))).subscribe(() => {\n this.refreshLibrary();\n this.toaster.success('AbpUi::SuccessfullyDeleted');\n });\n }\n });\n });\n }\n\n /**\n * The group's full membership, library-wide - a fresh, unfiltered fetch so a rename/ungroup is never\n * scoped down by whatever the search box currently filters `data().items` to.\n */\n private fieldsInGroup(groupName: string): Observable<FieldDto[]> {\n return this.fieldService\n .getList({})\n .pipe(map(result => (result.items ?? []).filter(field => field.groupName === groupName)));\n }\n\n private updateFieldGroupName(field: FieldDto, groupName: string | null) {\n return this.fieldService.update(field.id!, {\n displayName: field.displayName!,\n fieldTypeName: field.fieldTypeName!,\n description: field.description,\n configuration: field.configuration,\n groupName,\n });\n }\n\n private buildForm(field?: FieldDto): FormGroup {\n this.selectedFieldData = field\n ? {\n id: field.id!,\n name: field.name!,\n displayName: field.displayName!,\n description: field.description ?? undefined,\n fieldTypeName: field.fieldTypeName!,\n configuration: (field.configuration ?? {}) as Record<string, unknown>,\n }\n : undefined;\n\n return this.fb.group({\n // Fixed after creation - renaming goes through `openRename`. Kept in the form (disabled) so the\n // modal can show what it is.\n name: [\n { value: field?.name ?? '', disabled: !!field },\n [Validators.required, Validators.maxLength(MAX_FIELD_NAME_LENGTH), Validators.pattern(NAME_PATTERN)],\n ],\n displayName: [\n field?.displayName ?? '',\n [Validators.required, Validators.maxLength(MAX_FIELD_DISPLAY_NAME_LENGTH)],\n ],\n description: [field?.description ?? ''],\n // Free text with a suggestion list rather than a closed dropdown: filing a field under a new\n // group is just typing its name.\n groupName: [field?.groupName ?? '', [Validators.maxLength(MAX_GROUP_NAME_LENGTH)]],\n // Also fixed after creation: changing the type would orphan every stored value. Still bound so\n // `ff-flex-field-config` can pick which configuration editor to render.\n fieldTypeName: [\n { value: field?.fieldTypeName ?? this.fieldTypes[0]?.name ?? '', disabled: !!field },\n Validators.required,\n ],\n });\n }\n}\n","<div class=\"card\">\n <div class=\"card-body\">\n <div class=\"d-flex justify-content-between align-items-center mb-3\">\n <h5 class=\"mb-0\">{{ 'Site::Fields' | abpLocalization }}</h5>\n <button\n *abpPermission=\"policies.FieldsCreate\"\n type=\"button\"\n class=\"btn btn-primary\"\n (click)=\"openCreate()\"\n >\n <i class=\"fa fa-plus me-1\"></i>{{ 'Site::NewField' | abpLocalization }}\n </button>\n </div>\n\n <div class=\"row mb-3\">\n <div class=\"col-md-4\">\n <input\n type=\"text\"\n class=\"form-control\"\n [(ngModel)]=\"filters.filter\"\n (keyup.enter)=\"search()\"\n [placeholder]=\"'AbpUi::Search' | abpLocalization\"\n />\n </div>\n </div>\n\n <div class=\"table-responsive\">\n <table class=\"table align-middle mb-0\">\n <thead>\n <tr>\n <th>{{ 'Site::DisplayName:Field.DisplayName' | abpLocalization }}</th>\n <th>{{ 'Site::DisplayName:Field.Name' | abpLocalization }}</th>\n <th>{{ 'Site::DisplayName:Field.FieldTypeName' | abpLocalization }}</th>\n <th>{{ 'AbpUi::Actions' | abpLocalization }}</th>\n </tr>\n </thead>\n <tbody>\n @for (section of sections(); track section.groupName) {\n <tr class=\"table-light\">\n <th colspan=\"4\" class=\"fw-semibold\">\n <div class=\"d-flex justify-content-between align-items-center\">\n <span style=\"cursor: pointer\" (click)=\"toggleGroup(section.groupName)\">\n <i\n class=\"fa me-2 text-muted\"\n [class.fa-chevron-down]=\"!isGroupCollapsed(section.groupName)\"\n [class.fa-chevron-right]=\"isGroupCollapsed(section.groupName)\"\n ></i>\n @if (section.groupName) {\n <i class=\"fa fa-folder me-1 text-muted\"></i>{{ section.groupName }}\n } @else {\n <i class=\"fa fa-folder-open me-1 text-muted\"></i>\n <span class=\"text-muted\">{{ 'Site::Ungrouped' | abpLocalization }}</span>\n }\n </span>\n @if (section.groupName) {\n <span>\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openEditGroup(section.groupName!)\"\n [title]=\"'AbpUi::Edit' | abpLocalization\"\n >\n <i class=\"fa fa-pencil\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light\"\n (click)=\"deleteGroup(section.groupName!)\"\n [title]=\"'AbpUi::Delete' | abpLocalization\"\n >\n <i class=\"fa fa-trash text-danger\"></i>\n </button>\n </span>\n }\n </div>\n </th>\n </tr>\n @if (!isGroupCollapsed(section.groupName)) {\n @for (field of section.fields; track field.id) {\n <tr>\n <td>\n <div>{{ field.displayName }}</div>\n @if (field.description) {\n <small class=\"text-muted\">{{ field.description }}</small>\n }\n </td>\n <td>{{ field.name }}</td>\n <td>{{ displayNameKeyOf(field.fieldTypeName) | abpLocalization }}</td>\n <td class=\"text-nowrap text-end\">\n <button\n *abpPermission=\"policies.FieldsUpdate\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openEdit(field)\"\n [title]=\"'AbpUi::Edit' | abpLocalization\"\n >\n <i class=\"fa fa-pencil\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsRename\"\n type=\"button\"\n class=\"btn btn-sm btn-light me-1\"\n (click)=\"openRename(field)\"\n [title]=\"'Site::RenameField' | abpLocalization\"\n >\n <i class=\"fa fa-i-cursor\"></i>\n </button>\n <button\n *abpPermission=\"policies.FieldsDelete\"\n type=\"button\"\n class=\"btn btn-sm btn-light\"\n (click)=\"delete(field)\"\n [title]=\"'AbpUi::Delete' | abpLocalization\"\n >\n <i class=\"fa fa-trash text-danger\"></i>\n </button>\n </td>\n </tr>\n }\n }\n } @empty {\n <tr>\n <td colspan=\"4\" class=\"text-muted\">\n {{ 'AbpUi::NoDataAvailableInDatatable' | abpLocalization }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n</div>\n\n<abp-modal\n [(visible)]=\"isModalOpen\"\n [busy]=\"isModalBusy()\"\n [options]=\"{ size: 'xl' }\"\n (disappear)=\"closeModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ (editingField ? 'Site::EditField' : 'Site::NewField') | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (form) {\n <form [formGroup]=\"form\" (submit)=\"save()\">\n <button type=\"submit\" #submitButton style=\"display: none\"></button>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.DisplayName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"displayName\" />\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.Name' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"name\" />\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Name' | abpLocalization }}\n </small>\n </div>\n\n <!-- Description sits high on purpose: it is product copy aimed at AI, which an MCP client\n reads to fill the field correctly (总体设计 §6.2.6), not an optional footnote. -->\n <div class=\"mb-3\">\n <label class=\"form-label fw-semibold\">\n {{ 'Site::DisplayName:Field.Description' | abpLocalization }}\n </label>\n <textarea class=\"form-control\" formControlName=\"description\" rows=\"3\"></textarea>\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Description' | abpLocalization }}\n </small>\n </div>\n\n <!-- Above the field type, and free text: filing under a new group is just typing its name. -->\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.Group' | abpLocalization }}</label>\n <input\n type=\"text\"\n class=\"form-control\"\n formControlName=\"groupName\"\n [nzAutocomplete]=\"groupNameAuto\"\n />\n <nz-autocomplete #groupNameAuto [nzDataSource]=\"filteredGroupNames\"></nz-autocomplete>\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.Group' | abpLocalization }}\n </small>\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.FieldTypeName' | abpLocalization }}</label>\n <select class=\"form-select\" formControlName=\"fieldTypeName\">\n @for (fieldType of fieldTypes; track fieldType.name) {\n <option [value]=\"fieldType.name\">{{ fieldType.displayNameKey | abpLocalization }}</option>\n }\n </select>\n @if (editingField) {\n <small class=\"form-text text-muted\">\n {{ 'Site::FieldTypeFixedAfterCreation' | abpLocalization }}\n </small>\n }\n </div>\n\n <hr />\n <h6>{{ 'Site::DisplayName:Field.Configuration' | abpLocalization }}</h6>\n\n <ff-flex-field-config\n [type]=\"form.getRawValue().fieldTypeName\"\n [selected]=\"selectedFieldData\"\n [form]=\"form\"\n ></ff-flex-field-config>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!form?.valid\"\n [loading]=\"isModalBusy()\"\n (click)=\"submitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n\n<abp-modal\n [(visible)]=\"isRenameModalOpen\"\n [busy]=\"isRenameModalBusy()\"\n (disappear)=\"closeRenameModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ 'Site::RenameField' | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (renameForm) {\n <form [formGroup]=\"renameForm\" (submit)=\"saveRename()\">\n <button type=\"submit\" #renameSubmitButton style=\"display: none\"></button>\n\n <div class=\"alert alert-warning\">\n {{ 'Site::RenameFieldWarning' | abpLocalization }}\n </div>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Field.NewName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"newName\" />\n <small class=\"form-text text-muted\">\n {{ 'Site::Description:Field.NewName' | abpLocalization }}\n </small>\n </div>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!renameForm?.valid\"\n [loading]=\"isRenameModalBusy()\"\n (click)=\"renameSubmitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n\n<abp-modal\n [(visible)]=\"isGroupModalOpen\"\n [busy]=\"isGroupModalBusy()\"\n (disappear)=\"closeGroupModal()\"\n>\n <ng-template #abpHeader>\n <h3>{{ 'Site::RenameGroup' | abpLocalization }}</h3>\n </ng-template>\n <ng-template #abpBody>\n @if (groupForm) {\n <form [formGroup]=\"groupForm\" (submit)=\"saveGroupRename()\">\n <button type=\"submit\" #groupSubmitButton style=\"display: none\"></button>\n\n <div class=\"mb-3\">\n <label class=\"form-label\">{{ 'Site::DisplayName:Group.NewName' | abpLocalization }}</label>\n <input type=\"text\" class=\"form-control\" formControlName=\"groupName\" />\n </div>\n </form>\n }\n </ng-template>\n <ng-template #abpFooter>\n <button type=\"button\" class=\"btn btn-secondary\" abpClose>\n {{ 'AbpUi::Cancel' | abpLocalization }}\n </button>\n <abp-button\n iconClass=\"fa fa-check\"\n [disabled]=\"!groupForm?.valid\"\n [loading]=\"isGroupModalBusy()\"\n (click)=\"groupSubmitButton?.nativeElement?.click()\"\n >\n {{ 'AbpUi::Save' | abpLocalization }}\n </abp-button>\n </ng-template>\n</abp-modal>\n"],"names":["i2","i3","i4"],"mappings":"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"}