@flusys/ng-storage 1.0.0-beta → 1.0.0-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +209 -5
  2. package/fesm2022/flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs +485 -0
  3. package/fesm2022/flusys-ng-storage-file-manager-form.component-CMS_ddbs.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs +305 -0
  5. package/fesm2022/flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs.map +1 -0
  6. package/fesm2022/flusys-ng-storage-folder-form.component-ClYATZkh.mjs +157 -0
  7. package/fesm2022/flusys-ng-storage-folder-form.component-ClYATZkh.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-storage-folder-list.component-CzONANHt.mjs +200 -0
  9. package/fesm2022/flusys-ng-storage-folder-list.component-CzONANHt.mjs.map +1 -0
  10. package/fesm2022/{flusys-ng-storage-storage-config-form.component-Bc95TVD0.mjs → flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs} +209 -236
  11. package/fesm2022/flusys-ng-storage-storage-config-form.component-CODLLCtg.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs +213 -0
  13. package/fesm2022/flusys-ng-storage-storage-config-list.component-BbBx0iSL.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-storage.mjs +384 -96
  15. package/fesm2022/flusys-ng-storage.mjs.map +1 -1
  16. package/package.json +5 -5
  17. package/types/flusys-ng-storage.d.ts +187 -8
  18. package/fesm2022/flusys-ng-storage-file-manager-form.component-DVZsFYrj.mjs +0 -715
  19. package/fesm2022/flusys-ng-storage-file-manager-form.component-DVZsFYrj.mjs.map +0 -1
  20. package/fesm2022/flusys-ng-storage-file-manager-list.component-CwnyNoYb.mjs +0 -475
  21. package/fesm2022/flusys-ng-storage-file-manager-list.component-CwnyNoYb.mjs.map +0 -1
  22. package/fesm2022/flusys-ng-storage-folder-form.component-DH-KCz21.mjs +0 -253
  23. package/fesm2022/flusys-ng-storage-folder-form.component-DH-KCz21.mjs.map +0 -1
  24. package/fesm2022/flusys-ng-storage-folder-list.component-iIys4NvL.mjs +0 -267
  25. package/fesm2022/flusys-ng-storage-folder-list.component-iIys4NvL.mjs.map +0 -1
  26. package/fesm2022/flusys-ng-storage-storage-config-form.component-Bc95TVD0.mjs.map +0 -1
  27. package/fesm2022/flusys-ng-storage-storage-config-list.component-Bfomqyr3.mjs +0 -328
  28. package/fesm2022/flusys-ng-storage-storage-config-list.component-Bfomqyr3.mjs.map +0 -1
@@ -0,0 +1,305 @@
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
+ standalone: true,
211
+ changeDetection: ChangeDetectionStrategy.OnPush,
212
+ imports: [AngularModule, PrimeModule, HasPermissionDirective],
213
+ template: `
214
+ <div class="card">
215
+ <div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-3 mb-4">
216
+ <div>
217
+ <h3 class="text-lg sm:text-xl font-semibold">File Manager</h3>
218
+ @if (showCompanyInfo()) {
219
+ <p class="text-sm text-muted-color mt-1">
220
+ Company: {{ currentCompanyName() }}
221
+ </p>
222
+ }
223
+ </div>
224
+ <p-button *hasPermission="FILE_PERMISSIONS.CREATE" label="Upload File" icon="pi pi-upload" (onClick)="onUpload()" styleClass="w-full sm:w-auto" />
225
+ </div>
226
+
227
+ <div class="overflow-x-auto">
228
+ <p-table
229
+ [value]="files()"
230
+ [loading]="isLoading()"
231
+ [paginator]="totalRecords() > 0"
232
+ [rows]="pageSize()"
233
+ [totalRecords]="totalRecords()"
234
+ [lazy]="true"
235
+ (onLazyLoad)="onPageChange($event)"
236
+ [rowsPerPageOptions]="[10, 25, 50]"
237
+ [scrollable]="true"
238
+ styleClass="p-datatable-sm"
239
+ >
240
+ <ng-template #header>
241
+ <tr>
242
+ <th>Name</th>
243
+ <th class="hidden lg:table-cell">Type</th>
244
+ <th>Size</th>
245
+ <th>Location</th>
246
+ <th class="hidden xl:table-cell">Config</th>
247
+ <th>Private</th>
248
+ <th class="hidden md:table-cell">Created</th>
249
+ <th class="w-[120px]">Actions</th>
250
+ </tr>
251
+ </ng-template>
252
+ <ng-template #body let-file>
253
+ <tr>
254
+ <td>
255
+ <div class="flex items-center gap-2">
256
+ <i [class]="getFileIcon(file.contentType)"></i>
257
+ <span class="truncate max-w-32 sm:max-w-none">{{ file.name }}</span>
258
+ </div>
259
+ </td>
260
+ <td class="hidden lg:table-cell">
261
+ <span class="text-sm text-muted-color">{{ file.contentType }}</span>
262
+ </td>
263
+ <td>{{ file.size }} KB</td>
264
+ <td>
265
+ <p-tag [value]="getProviderLabel(file.location)" [severity]="getProviderSeverity(file.location)" />
266
+ </td>
267
+ <td class="hidden xl:table-cell">{{ file.providerName || '-' }}</td>
268
+ <td>
269
+ @if (file.isPrivate) {
270
+ <p-tag value="Private" severity="warn" />
271
+ } @else {
272
+ <p-tag value="Public" severity="success" />
273
+ }
274
+ </td>
275
+ <td class="hidden md:table-cell">{{ file.createdAt | date: 'short' }}</td>
276
+ <td>
277
+ <div class="flex gap-1">
278
+ <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" />
279
+ <p-button *hasPermission="FILE_PERMISSIONS.UPDATE" icon="pi pi-pencil" [text]="true" severity="secondary" size="small" (onClick)="onEdit(file.id)" pTooltip="Edit" tooltipPosition="top" />
280
+ <p-button *hasPermission="FILE_PERMISSIONS.DELETE" icon="pi pi-trash" [text]="true" severity="warn" size="small" (onClick)="onSoftDelete(file)" pTooltip="Trash" tooltipPosition="top" />
281
+ <p-button *hasPermission="FILE_PERMISSIONS.DELETE" icon="pi pi-times-circle" [text]="true" severity="danger" size="small" (onClick)="onPermanentDelete(file)" pTooltip="Delete" tooltipPosition="top" />
282
+ </div>
283
+ </td>
284
+ </tr>
285
+ </ng-template>
286
+ <ng-template #emptymessage>
287
+ <tr>
288
+ <td colspan="8" class="text-center py-4 text-muted-color">
289
+ @if (showCompanyInfo()) {
290
+ No files found in current company
291
+ } @else {
292
+ No files found
293
+ }
294
+ </td>
295
+ </tr>
296
+ </ng-template>
297
+ </p-table>
298
+ </div>
299
+ </div>
300
+ `,
301
+ }]
302
+ }] });
303
+
304
+ export { FileManagerListComponent };
305
+ //# sourceMappingURL=flusys-ng-storage-file-manager-list.component-DDOFAPRD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-storage-file-manager-list.component-DDOFAPRD.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 standalone: true,\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,157 @@
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
+ const createDto = { name: formValue.name };
81
+ await this.folderService.insertAsync(createDto);
82
+ this.showSuccess('Folder created successfully.');
83
+ }
84
+ this.navigateTo(['/storage/folders']);
85
+ }
86
+ catch {
87
+ this.showError(`Failed to ${this.isEditMode() ? 'update' : 'create'} folder.`);
88
+ }
89
+ finally {
90
+ this.isLoading.set(false);
91
+ }
92
+ }
93
+ onCancel() {
94
+ this.navigateTo(['/storage/folders']);
95
+ }
96
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
97
+ 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: `
98
+ <div class="card">
99
+ <div class="mb-4">
100
+ <h3 class="text-lg sm:text-xl font-semibold">{{ isEditMode() ? 'Edit Folder' : 'New Folder' }}</h3>
101
+ @if (showCompanyInfo()) {
102
+ <p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
103
+ }
104
+ </div>
105
+
106
+ <form (ngSubmit)="onSubmit()">
107
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
108
+ <div class="field">
109
+ <label for="name" class="block font-medium mb-2 text-sm sm:text-base">Folder Name *</label>
110
+ <input pInputText id="name" [formField]="folderForm.name" placeholder="Enter folder name" class="w-full" />
111
+ </div>
112
+ </div>
113
+
114
+ <div class="flex justify-end gap-2 mt-4">
115
+ <p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
116
+ <p-button type="submit" [label]="isEditMode() ? 'Update' : 'Create'" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
117
+ </div>
118
+ </form>
119
+ </div>
120
+ `, 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 });
121
+ }
122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: FolderFormComponent, decorators: [{
123
+ type: Component,
124
+ args: [{
125
+ selector: 'lib-folder-form',
126
+ standalone: true,
127
+ changeDetection: ChangeDetectionStrategy.OnPush,
128
+ imports: [AngularModule, PrimeModule, FormField],
129
+ template: `
130
+ <div class="card">
131
+ <div class="mb-4">
132
+ <h3 class="text-lg sm:text-xl font-semibold">{{ isEditMode() ? 'Edit Folder' : 'New Folder' }}</h3>
133
+ @if (showCompanyInfo()) {
134
+ <p class="text-sm text-muted-color mt-1">Company: {{ currentCompanyName() }}</p>
135
+ }
136
+ </div>
137
+
138
+ <form (ngSubmit)="onSubmit()">
139
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
140
+ <div class="field">
141
+ <label for="name" class="block font-medium mb-2 text-sm sm:text-base">Folder Name *</label>
142
+ <input pInputText id="name" [formField]="folderForm.name" placeholder="Enter folder name" class="w-full" />
143
+ </div>
144
+ </div>
145
+
146
+ <div class="flex justify-end gap-2 mt-4">
147
+ <p-button label="Cancel" severity="secondary" [outlined]="true" (onClick)="onCancel()" />
148
+ <p-button type="submit" [label]="isEditMode() ? 'Update' : 'Create'" [loading]="isLoading()" [disabled]="!isFormValid() || isLoading()" />
149
+ </div>
150
+ </form>
151
+ </div>
152
+ `,
153
+ }]
154
+ }], ctorParameters: () => [] });
155
+
156
+ export { FolderFormComponent };
157
+ //# sourceMappingURL=flusys-ng-storage-folder-form.component-ClYATZkh.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-storage-folder-form.component-ClYATZkh.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 { ICreateFolderDto, 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 standalone: true,\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 const createDto: ICreateFolderDto = { name: formValue.name };\n await this.folderService.insertAsync(createDto as ICreateFolderDto & { id: string });\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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"}