@flusys/ng-storage 1.0.0-beta → 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.
- package/README.md +209 -5
- package/fesm2022/flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs +484 -0
- package/fesm2022/flusys-ng-storage-file-manager-form.component-Bv8ER8J0.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs +304 -0
- package/fesm2022/flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-fRFiMFev.mjs +155 -0
- package/fesm2022/flusys-ng-storage-folder-form.component-fRFiMFev.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-C4xHI8SO.mjs +194 -0
- package/fesm2022/flusys-ng-storage-folder-list.component-C4xHI8SO.mjs.map +1 -0
- package/fesm2022/{flusys-ng-storage-storage-config-form.component-Bc95TVD0.mjs → flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs} +242 -292
- package/fesm2022/flusys-ng-storage-storage-config-form.component-DEMexjmg.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-iJez-rII.mjs +187 -0
- package/fesm2022/flusys-ng-storage-storage-config-list.component-iJez-rII.mjs.map +1 -0
- package/fesm2022/flusys-ng-storage.mjs +363 -199
- package/fesm2022/flusys-ng-storage.mjs.map +1 -1
- package/package.json +9 -9
- package/types/flusys-ng-storage.d.ts +150 -127
- package/fesm2022/flusys-ng-storage-file-manager-form.component-DVZsFYrj.mjs +0 -715
- package/fesm2022/flusys-ng-storage-file-manager-form.component-DVZsFYrj.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-file-manager-list.component-CwnyNoYb.mjs +0 -475
- package/fesm2022/flusys-ng-storage-file-manager-list.component-CwnyNoYb.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-form.component-DH-KCz21.mjs +0 -253
- package/fesm2022/flusys-ng-storage-folder-form.component-DH-KCz21.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-folder-list.component-iIys4NvL.mjs +0 -267
- package/fesm2022/flusys-ng-storage-folder-list.component-iIys4NvL.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-form.component-Bc95TVD0.mjs.map +0 -1
- package/fesm2022/flusys-ng-storage-storage-config-list.component-Bfomqyr3.mjs +0 -328
- package/fesm2022/flusys-ng-storage-storage-config-list.component-Bfomqyr3.mjs.map +0 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { FILE_PERMISSIONS, AngularModule, PrimeModule, HasPermissionDirective } from '@flusys/ng-shared';
|
|
4
|
+
import { firstValueFrom } from 'rxjs';
|
|
5
|
+
import { BaseStorageListPage, FileManagerApiService, getFileIcon, getProviderLabel, getProviderSeverity } from './flusys-ng-storage.mjs';
|
|
6
|
+
import * as i1 from 'primeng/button';
|
|
7
|
+
import * as i2 from 'primeng/table';
|
|
8
|
+
import * as i3 from 'primeng/tag';
|
|
9
|
+
import * as i4 from 'primeng/tooltip';
|
|
10
|
+
import * as i5 from '@angular/common';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* File Manager List Component
|
|
14
|
+
* Displays all files with company filter (if enabled)
|
|
15
|
+
*/
|
|
16
|
+
class FileManagerListComponent extends BaseStorageListPage {
|
|
17
|
+
// Permission constants for template
|
|
18
|
+
FILE_PERMISSIONS = FILE_PERMISSIONS;
|
|
19
|
+
fileService = inject(FileManagerApiService);
|
|
20
|
+
/** Files list alias for template compatibility */
|
|
21
|
+
files = this.items;
|
|
22
|
+
/** ID of file currently being viewed (null when not viewing) */
|
|
23
|
+
viewingFileId = signal(null, ...(ngDevMode ? [{ debugName: "viewingFileId" }] : []));
|
|
24
|
+
/** Utility functions exposed to template */
|
|
25
|
+
getFileIcon = getFileIcon;
|
|
26
|
+
getProviderLabel = getProviderLabel;
|
|
27
|
+
getProviderSeverity = getProviderSeverity;
|
|
28
|
+
getResourceRoute() {
|
|
29
|
+
return '/storage/files';
|
|
30
|
+
}
|
|
31
|
+
getDeleteConfirmMessage(file) {
|
|
32
|
+
return `Are you sure you want to delete "${file.name}"?`;
|
|
33
|
+
}
|
|
34
|
+
async loadData() {
|
|
35
|
+
this.isLoading.set(true);
|
|
36
|
+
try {
|
|
37
|
+
const response = await firstValueFrom(this.fileService.getAll('', {
|
|
38
|
+
pagination: this.getPaginationParams(),
|
|
39
|
+
filter: {},
|
|
40
|
+
select: [],
|
|
41
|
+
sort: {},
|
|
42
|
+
}));
|
|
43
|
+
if (response.success) {
|
|
44
|
+
this.items.set(response.data ?? []);
|
|
45
|
+
this.total.set(response.meta?.total ?? 0);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
this.showError('Failed to load files.');
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
this.isLoading.set(false);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
onUpload() {
|
|
56
|
+
this.navigateTo(['/storage/files/new']);
|
|
57
|
+
}
|
|
58
|
+
async onView(file) {
|
|
59
|
+
this.viewingFileId.set(file.id);
|
|
60
|
+
try {
|
|
61
|
+
const response = await firstValueFrom(this.fileService.getFiles([file.id]));
|
|
62
|
+
// Response type is ISingleResponse<IFilesResponseDto[]>
|
|
63
|
+
if (response.success && response.data?.length && response.data[0].url) {
|
|
64
|
+
window.open(response.data[0].url, '_blank');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.showError('File URL is not available.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
this.showError('Failed to get file URL.');
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
this.viewingFileId.set(null);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
onSoftDelete(file) {
|
|
78
|
+
this.confirmationService.confirm({
|
|
79
|
+
message: `Move "${file.name}" to trash? You can recover it later.`,
|
|
80
|
+
header: 'Move to Trash',
|
|
81
|
+
icon: 'pi pi-trash',
|
|
82
|
+
acceptLabel: 'Move to Trash',
|
|
83
|
+
acceptButtonStyleClass: 'p-button-warning',
|
|
84
|
+
accept: async () => {
|
|
85
|
+
try {
|
|
86
|
+
await this.fileService.deleteAsync({ id: file.id, type: 'delete' });
|
|
87
|
+
this.showSuccess('File moved to trash successfully.');
|
|
88
|
+
this.loadData();
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
this.showError('Failed to move file to trash.');
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
onPermanentDelete(file) {
|
|
97
|
+
this.confirmationService.confirm({
|
|
98
|
+
message: `Permanently delete "${file.name}"? This action CANNOT be undone and will delete the file from storage.`,
|
|
99
|
+
header: 'Permanent Delete',
|
|
100
|
+
icon: 'pi pi-exclamation-triangle',
|
|
101
|
+
acceptLabel: 'Delete Permanently',
|
|
102
|
+
rejectLabel: 'Cancel',
|
|
103
|
+
acceptButtonStyleClass: 'p-button-danger',
|
|
104
|
+
accept: async () => {
|
|
105
|
+
try {
|
|
106
|
+
await this.fileService.deleteAsync({ id: file.id, type: 'permanent' });
|
|
107
|
+
this.showSuccess('File permanently deleted.');
|
|
108
|
+
this.loadData();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
this.showError('Failed to permanently delete file.');
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerListComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
117
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: FileManagerListComponent, isStandalone: true, selector: "lib-file-manager-list", usesInheritance: true, ngImport: i0, template: `
|
|
118
|
+
<div class="card">
|
|
119
|
+
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-4">
|
|
120
|
+
<div>
|
|
121
|
+
<h3 class="text-lg sm:text-xl font-semibold">File Manager</h3>
|
|
122
|
+
@if (showCompanyInfo()) {
|
|
123
|
+
<p class="text-sm text-muted-color mt-1">
|
|
124
|
+
Company: {{ currentCompanyName() }}
|
|
125
|
+
</p>
|
|
126
|
+
}
|
|
127
|
+
</div>
|
|
128
|
+
<p-button *hasPermission="FILE_PERMISSIONS.CREATE" label="Upload File" icon="pi pi-upload" (onClick)="onUpload()" styleClass="w-full sm:w-auto" />
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div class="overflow-x-auto">
|
|
132
|
+
<p-table
|
|
133
|
+
[value]="files()"
|
|
134
|
+
[loading]="isLoading()"
|
|
135
|
+
[paginator]="totalRecords() > 0"
|
|
136
|
+
[rows]="pageSize()"
|
|
137
|
+
[totalRecords]="totalRecords()"
|
|
138
|
+
[lazy]="true"
|
|
139
|
+
(onLazyLoad)="onPageChange($event)"
|
|
140
|
+
[rowsPerPageOptions]="[10, 25, 50]"
|
|
141
|
+
[scrollable]="true"
|
|
142
|
+
styleClass="p-datatable-sm"
|
|
143
|
+
>
|
|
144
|
+
<ng-template #header>
|
|
145
|
+
<tr>
|
|
146
|
+
<th>Name</th>
|
|
147
|
+
<th class="hidden lg:table-cell">Type</th>
|
|
148
|
+
<th>Size</th>
|
|
149
|
+
<th>Location</th>
|
|
150
|
+
<th class="hidden xl:table-cell">Config</th>
|
|
151
|
+
<th>Private</th>
|
|
152
|
+
<th class="hidden md:table-cell">Created</th>
|
|
153
|
+
<th class="w-[120px]">Actions</th>
|
|
154
|
+
</tr>
|
|
155
|
+
</ng-template>
|
|
156
|
+
<ng-template #body let-file>
|
|
157
|
+
<tr>
|
|
158
|
+
<td>
|
|
159
|
+
<div class="flex items-center gap-2">
|
|
160
|
+
<i [class]="getFileIcon(file.contentType)"></i>
|
|
161
|
+
<span class="truncate max-w-32 sm:max-w-none">{{ file.name }}</span>
|
|
162
|
+
</div>
|
|
163
|
+
</td>
|
|
164
|
+
<td class="hidden lg:table-cell">
|
|
165
|
+
<span class="text-sm text-muted-color">{{ file.contentType }}</span>
|
|
166
|
+
</td>
|
|
167
|
+
<td>{{ file.size }} KB</td>
|
|
168
|
+
<td>
|
|
169
|
+
<p-tag [value]="getProviderLabel(file.location)" [severity]="getProviderSeverity(file.location)" />
|
|
170
|
+
</td>
|
|
171
|
+
<td class="hidden xl:table-cell">{{ file.providerName || '-' }}</td>
|
|
172
|
+
<td>
|
|
173
|
+
@if (file.isPrivate) {
|
|
174
|
+
<p-tag value="Private" severity="warn" />
|
|
175
|
+
} @else {
|
|
176
|
+
<p-tag value="Public" severity="success" />
|
|
177
|
+
}
|
|
178
|
+
</td>
|
|
179
|
+
<td class="hidden md:table-cell">{{ file.createdAt | date: 'short' }}</td>
|
|
180
|
+
<td>
|
|
181
|
+
<div class="flex gap-1">
|
|
182
|
+
<p-button *hasPermission="FILE_PERMISSIONS.READ" icon="pi pi-eye" [text]="true" severity="secondary" size="small" [loading]="viewingFileId() === file.id" [disabled]="viewingFileId() === file.id" (onClick)="onView(file)" pTooltip="View" tooltipPosition="top" />
|
|
183
|
+
<p-button *hasPermission="FILE_PERMISSIONS.UPDATE" icon="pi pi-pencil" [text]="true" severity="secondary" size="small" (onClick)="onEdit(file.id)" pTooltip="Edit" tooltipPosition="top" />
|
|
184
|
+
<p-button *hasPermission="FILE_PERMISSIONS.DELETE" icon="pi pi-trash" [text]="true" severity="warn" size="small" (onClick)="onSoftDelete(file)" pTooltip="Trash" tooltipPosition="top" />
|
|
185
|
+
<p-button *hasPermission="FILE_PERMISSIONS.DELETE" icon="pi pi-times-circle" [text]="true" severity="danger" size="small" (onClick)="onPermanentDelete(file)" pTooltip="Delete" tooltipPosition="top" />
|
|
186
|
+
</div>
|
|
187
|
+
</td>
|
|
188
|
+
</tr>
|
|
189
|
+
</ng-template>
|
|
190
|
+
<ng-template #emptymessage>
|
|
191
|
+
<tr>
|
|
192
|
+
<td colspan="8" class="text-center py-4 text-muted-color">
|
|
193
|
+
@if (showCompanyInfo()) {
|
|
194
|
+
No files found in current company
|
|
195
|
+
} @else {
|
|
196
|
+
No files found
|
|
197
|
+
}
|
|
198
|
+
</td>
|
|
199
|
+
</tr>
|
|
200
|
+
</ng-template>
|
|
201
|
+
</p-table>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i2.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i3.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i4.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "pipe", type: i5.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
205
|
+
}
|
|
206
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FileManagerListComponent, decorators: [{
|
|
207
|
+
type: Component,
|
|
208
|
+
args: [{
|
|
209
|
+
selector: 'lib-file-manager-list',
|
|
210
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
211
|
+
imports: [AngularModule, PrimeModule, HasPermissionDirective],
|
|
212
|
+
template: `
|
|
213
|
+
<div class="card">
|
|
214
|
+
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-4">
|
|
215
|
+
<div>
|
|
216
|
+
<h3 class="text-lg sm:text-xl font-semibold">File Manager</h3>
|
|
217
|
+
@if (showCompanyInfo()) {
|
|
218
|
+
<p class="text-sm text-muted-color mt-1">
|
|
219
|
+
Company: {{ currentCompanyName() }}
|
|
220
|
+
</p>
|
|
221
|
+
}
|
|
222
|
+
</div>
|
|
223
|
+
<p-button *hasPermission="FILE_PERMISSIONS.CREATE" label="Upload File" icon="pi pi-upload" (onClick)="onUpload()" styleClass="w-full sm:w-auto" />
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<div class="overflow-x-auto">
|
|
227
|
+
<p-table
|
|
228
|
+
[value]="files()"
|
|
229
|
+
[loading]="isLoading()"
|
|
230
|
+
[paginator]="totalRecords() > 0"
|
|
231
|
+
[rows]="pageSize()"
|
|
232
|
+
[totalRecords]="totalRecords()"
|
|
233
|
+
[lazy]="true"
|
|
234
|
+
(onLazyLoad)="onPageChange($event)"
|
|
235
|
+
[rowsPerPageOptions]="[10, 25, 50]"
|
|
236
|
+
[scrollable]="true"
|
|
237
|
+
styleClass="p-datatable-sm"
|
|
238
|
+
>
|
|
239
|
+
<ng-template #header>
|
|
240
|
+
<tr>
|
|
241
|
+
<th>Name</th>
|
|
242
|
+
<th class="hidden lg:table-cell">Type</th>
|
|
243
|
+
<th>Size</th>
|
|
244
|
+
<th>Location</th>
|
|
245
|
+
<th class="hidden xl:table-cell">Config</th>
|
|
246
|
+
<th>Private</th>
|
|
247
|
+
<th class="hidden md:table-cell">Created</th>
|
|
248
|
+
<th class="w-[120px]">Actions</th>
|
|
249
|
+
</tr>
|
|
250
|
+
</ng-template>
|
|
251
|
+
<ng-template #body let-file>
|
|
252
|
+
<tr>
|
|
253
|
+
<td>
|
|
254
|
+
<div class="flex items-center gap-2">
|
|
255
|
+
<i [class]="getFileIcon(file.contentType)"></i>
|
|
256
|
+
<span class="truncate max-w-32 sm:max-w-none">{{ file.name }}</span>
|
|
257
|
+
</div>
|
|
258
|
+
</td>
|
|
259
|
+
<td class="hidden lg:table-cell">
|
|
260
|
+
<span class="text-sm text-muted-color">{{ file.contentType }}</span>
|
|
261
|
+
</td>
|
|
262
|
+
<td>{{ file.size }} KB</td>
|
|
263
|
+
<td>
|
|
264
|
+
<p-tag [value]="getProviderLabel(file.location)" [severity]="getProviderSeverity(file.location)" />
|
|
265
|
+
</td>
|
|
266
|
+
<td class="hidden xl:table-cell">{{ file.providerName || '-' }}</td>
|
|
267
|
+
<td>
|
|
268
|
+
@if (file.isPrivate) {
|
|
269
|
+
<p-tag value="Private" severity="warn" />
|
|
270
|
+
} @else {
|
|
271
|
+
<p-tag value="Public" severity="success" />
|
|
272
|
+
}
|
|
273
|
+
</td>
|
|
274
|
+
<td class="hidden md:table-cell">{{ file.createdAt | date: 'short' }}</td>
|
|
275
|
+
<td>
|
|
276
|
+
<div class="flex gap-1">
|
|
277
|
+
<p-button *hasPermission="FILE_PERMISSIONS.READ" icon="pi pi-eye" [text]="true" severity="secondary" size="small" [loading]="viewingFileId() === file.id" [disabled]="viewingFileId() === file.id" (onClick)="onView(file)" pTooltip="View" tooltipPosition="top" />
|
|
278
|
+
<p-button *hasPermission="FILE_PERMISSIONS.UPDATE" icon="pi pi-pencil" [text]="true" severity="secondary" size="small" (onClick)="onEdit(file.id)" pTooltip="Edit" tooltipPosition="top" />
|
|
279
|
+
<p-button *hasPermission="FILE_PERMISSIONS.DELETE" icon="pi pi-trash" [text]="true" severity="warn" size="small" (onClick)="onSoftDelete(file)" pTooltip="Trash" tooltipPosition="top" />
|
|
280
|
+
<p-button *hasPermission="FILE_PERMISSIONS.DELETE" icon="pi pi-times-circle" [text]="true" severity="danger" size="small" (onClick)="onPermanentDelete(file)" pTooltip="Delete" tooltipPosition="top" />
|
|
281
|
+
</div>
|
|
282
|
+
</td>
|
|
283
|
+
</tr>
|
|
284
|
+
</ng-template>
|
|
285
|
+
<ng-template #emptymessage>
|
|
286
|
+
<tr>
|
|
287
|
+
<td colspan="8" class="text-center py-4 text-muted-color">
|
|
288
|
+
@if (showCompanyInfo()) {
|
|
289
|
+
No files found in current company
|
|
290
|
+
} @else {
|
|
291
|
+
No files found
|
|
292
|
+
}
|
|
293
|
+
</td>
|
|
294
|
+
</tr>
|
|
295
|
+
</ng-template>
|
|
296
|
+
</p-table>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
`,
|
|
300
|
+
}]
|
|
301
|
+
}] });
|
|
302
|
+
|
|
303
|
+
export { FileManagerListComponent };
|
|
304
|
+
//# sourceMappingURL=flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-storage-file-manager-list.component-McM1zSlE.mjs","sources":["../../../projects/ng-storage/pages/file-manager/file-manager-list.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { AngularModule, FILE_PERMISSIONS, HasPermissionDirective, PrimeModule } from '@flusys/ng-shared';\nimport { firstValueFrom } from 'rxjs';\nimport { IFileManager } from '../../interfaces/file-manager.interface';\nimport { FileManagerApiService } from '../../services/file-manager-api.service';\nimport { BaseStorageListPage } from '../../utils/base-storage-page';\nimport { getFileIcon, getProviderLabel, getProviderSeverity } from '../../utils/storage-utils';\n\n/**\n * File Manager List Component\n * Displays all files with company filter (if enabled)\n */\n@Component({\n selector: 'lib-file-manager-list',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AngularModule, PrimeModule, HasPermissionDirective],\n template: `\n <div class=\"card\">\n <div class=\"flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-4\">\n <div>\n <h3 class=\"text-lg sm:text-xl font-semibold\">File Manager</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">\n Company: {{ currentCompanyName() }}\n </p>\n }\n </div>\n <p-button *hasPermission=\"FILE_PERMISSIONS.CREATE\" label=\"Upload File\" icon=\"pi pi-upload\" (onClick)=\"onUpload()\" styleClass=\"w-full sm:w-auto\" />\n </div>\n\n <div class=\"overflow-x-auto\">\n <p-table\n [value]=\"files()\"\n [loading]=\"isLoading()\"\n [paginator]=\"totalRecords() > 0\"\n [rows]=\"pageSize()\"\n [totalRecords]=\"totalRecords()\"\n [lazy]=\"true\"\n (onLazyLoad)=\"onPageChange($event)\"\n [rowsPerPageOptions]=\"[10, 25, 50]\"\n [scrollable]=\"true\"\n styleClass=\"p-datatable-sm\"\n >\n <ng-template #header>\n <tr>\n <th>Name</th>\n <th class=\"hidden lg:table-cell\">Type</th>\n <th>Size</th>\n <th>Location</th>\n <th class=\"hidden xl:table-cell\">Config</th>\n <th>Private</th>\n <th class=\"hidden md:table-cell\">Created</th>\n <th class=\"w-[120px]\">Actions</th>\n </tr>\n </ng-template>\n <ng-template #body let-file>\n <tr>\n <td>\n <div class=\"flex items-center gap-2\">\n <i [class]=\"getFileIcon(file.contentType)\"></i>\n <span class=\"truncate max-w-32 sm:max-w-none\">{{ file.name }}</span>\n </div>\n </td>\n <td class=\"hidden lg:table-cell\">\n <span class=\"text-sm text-muted-color\">{{ file.contentType }}</span>\n </td>\n <td>{{ file.size }} KB</td>\n <td>\n <p-tag [value]=\"getProviderLabel(file.location)\" [severity]=\"getProviderSeverity(file.location)\" />\n </td>\n <td class=\"hidden xl:table-cell\">{{ file.providerName || '-' }}</td>\n <td>\n @if (file.isPrivate) {\n <p-tag value=\"Private\" severity=\"warn\" />\n } @else {\n <p-tag value=\"Public\" severity=\"success\" />\n }\n </td>\n <td class=\"hidden md:table-cell\">{{ file.createdAt | date: 'short' }}</td>\n <td>\n <div class=\"flex gap-1\">\n <p-button *hasPermission=\"FILE_PERMISSIONS.READ\" icon=\"pi pi-eye\" [text]=\"true\" severity=\"secondary\" size=\"small\" [loading]=\"viewingFileId() === file.id\" [disabled]=\"viewingFileId() === file.id\" (onClick)=\"onView(file)\" pTooltip=\"View\" tooltipPosition=\"top\" />\n <p-button *hasPermission=\"FILE_PERMISSIONS.UPDATE\" icon=\"pi pi-pencil\" [text]=\"true\" severity=\"secondary\" size=\"small\" (onClick)=\"onEdit(file.id)\" pTooltip=\"Edit\" tooltipPosition=\"top\" />\n <p-button *hasPermission=\"FILE_PERMISSIONS.DELETE\" icon=\"pi pi-trash\" [text]=\"true\" severity=\"warn\" size=\"small\" (onClick)=\"onSoftDelete(file)\" pTooltip=\"Trash\" tooltipPosition=\"top\" />\n <p-button *hasPermission=\"FILE_PERMISSIONS.DELETE\" icon=\"pi pi-times-circle\" [text]=\"true\" severity=\"danger\" size=\"small\" (onClick)=\"onPermanentDelete(file)\" pTooltip=\"Delete\" tooltipPosition=\"top\" />\n </div>\n </td>\n </tr>\n </ng-template>\n <ng-template #emptymessage>\n <tr>\n <td colspan=\"8\" class=\"text-center py-4 text-muted-color\">\n @if (showCompanyInfo()) {\n No files found in current company\n } @else {\n No files found\n }\n </td>\n </tr>\n </ng-template>\n </p-table>\n </div>\n </div>\n `,\n})\nexport class FileManagerListComponent extends BaseStorageListPage<IFileManager> {\n // Permission constants for template\n readonly FILE_PERMISSIONS = FILE_PERMISSIONS;\n\n private readonly fileService = inject(FileManagerApiService);\n\n /** Files list alias for template compatibility */\n readonly files = this.items;\n\n /** ID of file currently being viewed (null when not viewing) */\n readonly viewingFileId = signal<string | null>(null);\n\n /** Utility functions exposed to template */\n readonly getFileIcon = getFileIcon;\n readonly getProviderLabel = getProviderLabel;\n readonly getProviderSeverity = getProviderSeverity;\n\n getResourceRoute(): string {\n return '/storage/files';\n }\n\n override getDeleteConfirmMessage(file: IFileManager): string {\n return `Are you sure you want to delete \"${file.name}\"?`;\n }\n\n async loadData(): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await firstValueFrom(\n this.fileService.getAll('', {\n pagination: this.getPaginationParams(),\n filter: {},\n select: [],\n sort: {},\n }),\n );\n\n if (response.success) {\n this.items.set(response.data ?? []);\n this.total.set(response.meta?.total ?? 0);\n }\n } catch {\n this.showError('Failed to load files.');\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onUpload(): void {\n this.navigateTo(['/storage/files/new']);\n }\n\n async onView(file: IFileManager): Promise<void> {\n this.viewingFileId.set(file.id);\n try {\n const response = await firstValueFrom(this.fileService.getFiles([file.id]));\n\n // Response type is ISingleResponse<IFilesResponseDto[]>\n if (response.success && response.data?.length && response.data[0].url) {\n window.open(response.data[0].url, '_blank');\n } else {\n this.showError('File URL is not available.');\n }\n } catch {\n this.showError('Failed to get file URL.');\n } finally {\n this.viewingFileId.set(null);\n }\n }\n\n onSoftDelete(file: IFileManager): void {\n this.confirmationService.confirm({\n message: `Move \"${file.name}\" to trash? You can recover it later.`,\n header: 'Move to Trash',\n icon: 'pi pi-trash',\n acceptLabel: 'Move to Trash',\n acceptButtonStyleClass: 'p-button-warning',\n accept: async () => {\n try {\n await this.fileService.deleteAsync({ id: file.id, type: 'delete' });\n this.showSuccess('File moved to trash successfully.');\n this.loadData();\n } catch {\n this.showError('Failed to move file to trash.');\n }\n },\n });\n }\n\n onPermanentDelete(file: IFileManager): void {\n this.confirmationService.confirm({\n message: `Permanently delete \"${file.name}\"? This action CANNOT be undone and will delete the file from storage.`,\n header: 'Permanent Delete',\n icon: 'pi pi-exclamation-triangle',\n acceptLabel: 'Delete Permanently',\n rejectLabel: 'Cancel',\n acceptButtonStyleClass: 'p-button-danger',\n accept: async () => {\n try {\n await this.fileService.deleteAsync({ id: file.id, type: 'permanent' });\n this.showSuccess('File permanently deleted.');\n this.loadData();\n } catch {\n this.showError('Failed to permanently delete file.');\n }\n },\n });\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, computed, effect, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
+
import { form, required, FormField } from '@angular/forms/signals';
|
|
5
|
+
import { ActivatedRoute } from '@angular/router';
|
|
6
|
+
import { AngularModule, PrimeModule } from '@flusys/ng-shared';
|
|
7
|
+
import { BaseStoragePage, FolderApiService } from './flusys-ng-storage.mjs';
|
|
8
|
+
import * as i1 from '@angular/forms';
|
|
9
|
+
import * as i1$1 from 'primeng/button';
|
|
10
|
+
import * as i3 from 'primeng/inputtext';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Folder Form Component
|
|
14
|
+
* Create and edit folders
|
|
15
|
+
*/
|
|
16
|
+
class FolderFormComponent extends BaseStoragePage {
|
|
17
|
+
route = inject(ActivatedRoute);
|
|
18
|
+
folderService = inject(FolderApiService);
|
|
19
|
+
// Route params as signal
|
|
20
|
+
routeParams = toSignal(this.route.paramMap);
|
|
21
|
+
initialized = false;
|
|
22
|
+
/** Existing folder for edit mode */
|
|
23
|
+
existingFolder = signal(null, ...(ngDevMode ? [{ debugName: "existingFolder" }] : []));
|
|
24
|
+
/** Check if in edit mode */
|
|
25
|
+
isEditMode = computed(() => !!this.existingFolder(), ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
|
|
26
|
+
/** Form model */
|
|
27
|
+
formModel = signal({
|
|
28
|
+
id: '',
|
|
29
|
+
name: '',
|
|
30
|
+
}, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
31
|
+
/** Form with validation */
|
|
32
|
+
folderForm = form(this.formModel, (f) => {
|
|
33
|
+
required(f.name, { message: 'Folder name is required' });
|
|
34
|
+
});
|
|
35
|
+
/** Check if form is valid */
|
|
36
|
+
isFormValid = computed(() => this.formModel().name.trim().length > 0, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
37
|
+
constructor() {
|
|
38
|
+
super();
|
|
39
|
+
// Effect to handle route-based initialization
|
|
40
|
+
effect(() => {
|
|
41
|
+
const params = this.routeParams();
|
|
42
|
+
if (!params || this.initialized)
|
|
43
|
+
return;
|
|
44
|
+
this.initialized = true;
|
|
45
|
+
const folderId = params.get('id');
|
|
46
|
+
if (folderId && folderId !== 'new') {
|
|
47
|
+
this.loadFolder(folderId);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async loadFolder(id) {
|
|
52
|
+
this.isLoading.set(true);
|
|
53
|
+
try {
|
|
54
|
+
const response = await this.folderService.findByIdAsync(id);
|
|
55
|
+
if (response.success && response.data) {
|
|
56
|
+
const folder = response.data;
|
|
57
|
+
this.existingFolder.set(folder);
|
|
58
|
+
this.formModel.set({ id: folder.id, name: folder.name });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
this.showError('Failed to load folder.');
|
|
63
|
+
this.navigateTo(['/storage/folders']);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
this.isLoading.set(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async onSubmit() {
|
|
70
|
+
if (!this.isFormValid())
|
|
71
|
+
return;
|
|
72
|
+
this.isLoading.set(true);
|
|
73
|
+
try {
|
|
74
|
+
const formValue = this.formModel();
|
|
75
|
+
if (this.isEditMode()) {
|
|
76
|
+
await this.folderService.updateAsync({ id: formValue.id, name: formValue.name });
|
|
77
|
+
this.showSuccess('Folder updated successfully.');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
await this.folderService.insertAsync({ name: formValue.name });
|
|
81
|
+
this.showSuccess('Folder created successfully.');
|
|
82
|
+
}
|
|
83
|
+
this.navigateTo(['/storage/folders']);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
this.showError(`Failed to ${this.isEditMode() ? 'update' : 'create'} folder.`);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
this.isLoading.set(false);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
onCancel() {
|
|
93
|
+
this.navigateTo(['/storage/folders']);
|
|
94
|
+
}
|
|
95
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
96
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: FolderFormComponent, isStandalone: true, selector: "lib-folder-form", usesInheritance: true, ngImport: i0, template: `
|
|
97
|
+
<div class="card">
|
|
98
|
+
<div class="mb-4">
|
|
99
|
+
<h3 class="text-lg sm:text-xl font-semibold">{{ isEditMode() ? 'Edit Folder' : 'New Folder' }}</h3>
|
|
100
|
+
@if (showCompanyInfo()) {
|
|
101
|
+
<p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
|
|
102
|
+
}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<form (ngSubmit)="onSubmit()">
|
|
106
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
107
|
+
<div class="field">
|
|
108
|
+
<label for="name" class="block font-medium mb-2 text-sm sm:text-base">Folder Name *</label>
|
|
109
|
+
<input pInputText id="name" [formField]="folderForm.name" placeholder="Enter folder name" class="w-full" />
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="flex justify-end gap-2 mt-4">
|
|
114
|
+
<p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
|
|
115
|
+
<p-button type="submit" [label]="isEditMode() ? 'Update' : 'Create'" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
|
|
116
|
+
</div>
|
|
117
|
+
</form>
|
|
118
|
+
</div>
|
|
119
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i1$1.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i3.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
120
|
+
}
|
|
121
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderFormComponent, decorators: [{
|
|
122
|
+
type: Component,
|
|
123
|
+
args: [{
|
|
124
|
+
selector: 'lib-folder-form',
|
|
125
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
126
|
+
imports: [AngularModule, PrimeModule, FormField],
|
|
127
|
+
template: `
|
|
128
|
+
<div class="card">
|
|
129
|
+
<div class="mb-4">
|
|
130
|
+
<h3 class="text-lg sm:text-xl font-semibold">{{ isEditMode() ? 'Edit Folder' : 'New Folder' }}</h3>
|
|
131
|
+
@if (showCompanyInfo()) {
|
|
132
|
+
<p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
|
|
133
|
+
}
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<form (ngSubmit)="onSubmit()">
|
|
137
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
138
|
+
<div class="field">
|
|
139
|
+
<label for="name" class="block font-medium mb-2 text-sm sm:text-base">Folder Name *</label>
|
|
140
|
+
<input pInputText id="name" [formField]="folderForm.name" placeholder="Enter folder name" class="w-full" />
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<div class="flex justify-end gap-2 mt-4">
|
|
145
|
+
<p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
|
|
146
|
+
<p-button type="submit" [label]="isEditMode() ? 'Update' : 'Create'" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
|
|
147
|
+
</div>
|
|
148
|
+
</form>
|
|
149
|
+
</div>
|
|
150
|
+
`,
|
|
151
|
+
}]
|
|
152
|
+
}], ctorParameters: () => [] });
|
|
153
|
+
|
|
154
|
+
export { FolderFormComponent };
|
|
155
|
+
//# sourceMappingURL=flusys-ng-storage-folder-form.component-fRFiMFev.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flusys-ng-storage-folder-form.component-fRFiMFev.mjs","sources":["../../../projects/ng-storage/pages/folder/folder-form.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, effect, inject, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { form, FormField, required } from '@angular/forms/signals';\nimport { ActivatedRoute } from '@angular/router';\nimport { AngularModule, PrimeModule } from '@flusys/ng-shared';\nimport { IFolder } from '../../interfaces/folder.interface';\nimport { FolderApiService } from '../../services/folder-api.service';\nimport { BaseStoragePage } from '../../utils/base-storage-page';\n\n/** Folder form model interface */\ninterface IFolderFormModel {\n id: string;\n name: string;\n}\n\n/**\n * Folder Form Component\n * Create and edit folders\n */\n@Component({\n selector: 'lib-folder-form',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AngularModule, PrimeModule, FormField],\n template: `\n <div class=\"card\">\n <div class=\"mb-4\">\n <h3 class=\"text-lg sm:text-xl font-semibold\">{{ isEditMode() ? 'Edit Folder' : 'New Folder' }}</h3>\n @if (showCompanyInfo()) {\n <p class=\"text-sm text-muted-color mt-1\">Company: {{ currentCompanyName() }}</p>\n }\n </div>\n\n <form (ngSubmit)=\"onSubmit()\">\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <div class=\"field\">\n <label for=\"name\" class=\"block font-medium mb-2 text-sm sm:text-base\">Folder Name *</label>\n <input pInputText id=\"name\" [formField]=\"folderForm.name\" placeholder=\"Enter folder name\" class=\"w-full\" />\n </div>\n </div>\n\n <div class=\"flex justify-end gap-2 mt-4\">\n <p-button label=\"Cancel\" severity=\"secondary\" [outlined]=\"true\" (onClick)=\"onCancel()\" />\n <p-button type=\"submit\" [label]=\"isEditMode() ? 'Update' : 'Create'\" [loading]=\"isLoading()\" [disabled]=\"!isFormValid() || isLoading()\" />\n </div>\n </form>\n </div>\n `,\n})\nexport class FolderFormComponent extends BaseStoragePage {\n private readonly route = inject(ActivatedRoute);\n private readonly folderService = inject(FolderApiService);\n\n // Route params as signal\n private readonly routeParams = toSignal(this.route.paramMap);\n private initialized = false;\n\n /** Existing folder for edit mode */\n readonly existingFolder = signal<IFolder | null>(null);\n\n /** Check if in edit mode */\n readonly isEditMode = computed(() => !!this.existingFolder());\n\n /** Form model */\n readonly formModel = signal<IFolderFormModel>({\n id: '',\n name: '',\n });\n\n /** Form with validation */\n readonly folderForm = form(this.formModel, (f) => {\n required(f.name, { message: 'Folder name is required' });\n });\n\n /** Check if form is valid */\n readonly isFormValid = computed(() => this.formModel().name.trim().length > 0);\n\n constructor() {\n super();\n // Effect to handle route-based initialization\n effect(() => {\n const params = this.routeParams();\n if (!params || this.initialized) return;\n\n this.initialized = true;\n const folderId = params.get('id');\n if (folderId && folderId !== 'new') {\n this.loadFolder(folderId);\n }\n });\n }\n\n async loadFolder(id: string): Promise<void> {\n this.isLoading.set(true);\n try {\n const response = await this.folderService.findByIdAsync(id);\n if (response.success && response.data) {\n const folder = response.data;\n this.existingFolder.set(folder);\n this.formModel.set({ id: folder.id, name: folder.name });\n }\n } catch {\n this.showError('Failed to load folder.');\n this.navigateTo(['/storage/folders']);\n } finally {\n this.isLoading.set(false);\n }\n }\n\n async onSubmit(): Promise<void> {\n if (!this.isFormValid()) return;\n\n this.isLoading.set(true);\n try {\n const formValue = this.formModel();\n\n if (this.isEditMode()) {\n await this.folderService.updateAsync({ id: formValue.id, name: formValue.name });\n this.showSuccess('Folder updated successfully.');\n } else {\n await this.folderService.insertAsync({ name: formValue.name });\n this.showSuccess('Folder created successfully.');\n }\n\n this.navigateTo(['/storage/folders']);\n } catch {\n this.showError(`Failed to ${this.isEditMode() ? 'update' : 'create'} folder.`);\n } finally {\n this.isLoading.set(false);\n }\n }\n\n onCancel(): void {\n this.navigateTo(['/storage/folders']);\n }\n}\n"],"names":["i2"],"mappings":"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"}
|