@flusys/ng-task-manager 9.1.0-rc.1 → 9.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/flusys-ng-task-manager-board-list-page.component-Dmay3IyF.mjs +784 -0
- package/fesm2022/flusys-ng-task-manager-board-list-page.component-Dmay3IyF.mjs.map +1 -0
- package/fesm2022/flusys-ng-task-manager-board-view-page.component-CkC8-UKF.mjs +2345 -0
- package/fesm2022/flusys-ng-task-manager-board-view-page.component-CkC8-UKF.mjs.map +1 -0
- package/fesm2022/flusys-ng-task-manager.mjs +1693 -3338
- package/fesm2022/flusys-ng-task-manager.mjs.map +1 -1
- package/package.json +4 -4
- package/types/flusys-ng-task-manager.d.ts +516 -580
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
import { BOARD_COLOR_PALETTE, TaskBoardApiService, TaskBoardStateService } from "./flusys-ng-task-manager.mjs";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { Component, computed, inject, signal } from "@angular/core";
|
|
4
|
+
import { TranslatePipe } from "@flusys/ng-shared";
|
|
5
|
+
import { DatePipe } from "@angular/common";
|
|
6
|
+
import { DialogComponent, FButtonDirective, IconFieldComponent, InputIconComponent, InputTextDirective, SkeletonComponent, TextareaDirective, TooltipDirective, UiIconComponent } from "@flusys/ng-ui";
|
|
7
|
+
import * as i1 from "@angular/forms";
|
|
8
|
+
import { FormsModule } from "@angular/forms";
|
|
9
|
+
import { Router } from "@angular/router";
|
|
10
|
+
var BoardListPageComponent = class BoardListPageComponent {
|
|
11
|
+
state = inject(TaskBoardStateService);
|
|
12
|
+
boardApi = inject(TaskBoardApiService);
|
|
13
|
+
router = inject(Router);
|
|
14
|
+
showCreateDialog = signal(false, ...ngDevMode ? [{ debugName: "showCreateDialog" }] : /* istanbul ignore next */ []);
|
|
15
|
+
isCreating = signal(false, ...ngDevMode ? [{ debugName: "isCreating" }] : /* istanbul ignore next */ []);
|
|
16
|
+
newBoardName = "";
|
|
17
|
+
newBoardDescription = "";
|
|
18
|
+
newBoardColor = BOARD_COLOR_PALETTE[0];
|
|
19
|
+
boardColors = BOARD_COLOR_PALETTE;
|
|
20
|
+
_searchTerm = signal("", ...ngDevMode ? [{ debugName: "_searchTerm" }] : /* istanbul ignore next */ []);
|
|
21
|
+
searchTerm = this._searchTerm.asReadonly();
|
|
22
|
+
filteredBoards = computed(() => {
|
|
23
|
+
const term = this.searchTerm().toLowerCase().trim();
|
|
24
|
+
const boards = this.state.boards();
|
|
25
|
+
if (!term) return boards;
|
|
26
|
+
return boards.filter((board) => board.name.toLowerCase().includes(term) || (board.description ?? "").toLowerCase().includes(term));
|
|
27
|
+
}, ...ngDevMode ? [{ debugName: "filteredBoards" }] : /* istanbul ignore next */ []);
|
|
28
|
+
constructor() {
|
|
29
|
+
this.state.loadBoards();
|
|
30
|
+
}
|
|
31
|
+
onSearchChange(term) {
|
|
32
|
+
this._searchTerm.set(term);
|
|
33
|
+
}
|
|
34
|
+
openBoard(boardId) {
|
|
35
|
+
this.router.navigate(["/task-manager/board", boardId]);
|
|
36
|
+
}
|
|
37
|
+
closeCreateDialog() {
|
|
38
|
+
this.showCreateDialog.set(false);
|
|
39
|
+
this.newBoardName = "";
|
|
40
|
+
this.newBoardDescription = "";
|
|
41
|
+
this.newBoardColor = BOARD_COLOR_PALETTE[0];
|
|
42
|
+
}
|
|
43
|
+
async createBoard() {
|
|
44
|
+
if (!this.newBoardName.trim()) return;
|
|
45
|
+
this.isCreating.set(true);
|
|
46
|
+
try {
|
|
47
|
+
await this.boardApi.insert({
|
|
48
|
+
name: this.newBoardName,
|
|
49
|
+
description: this.newBoardDescription || void 0,
|
|
50
|
+
color: this.newBoardColor
|
|
51
|
+
});
|
|
52
|
+
await this.state.loadBoards();
|
|
53
|
+
this.closeCreateDialog();
|
|
54
|
+
} finally {
|
|
55
|
+
this.isCreating.set(false);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
59
|
+
minVersion: "12.0.0",
|
|
60
|
+
version: "22.2.1",
|
|
61
|
+
ngImport: i0,
|
|
62
|
+
type: BoardListPageComponent,
|
|
63
|
+
deps: [],
|
|
64
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
65
|
+
});
|
|
66
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
67
|
+
minVersion: "17.0.0",
|
|
68
|
+
version: "22.2.1",
|
|
69
|
+
type: BoardListPageComponent,
|
|
70
|
+
isStandalone: true,
|
|
71
|
+
selector: "tm-board-list-page",
|
|
72
|
+
ngImport: i0,
|
|
73
|
+
template: `
|
|
74
|
+
<div class="p-2 sm:p-4">
|
|
75
|
+
<div
|
|
76
|
+
class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
|
|
77
|
+
>
|
|
78
|
+
<div>
|
|
79
|
+
<h1 class="text-xl sm:text-2xl font-bold m-0">
|
|
80
|
+
{{ 'task.manager.boards.title' | translate }}
|
|
81
|
+
</h1>
|
|
82
|
+
<p class="text-muted-color mt-1 text-sm sm:text-base">
|
|
83
|
+
{{ 'task.manager.boards.subtitle' | translate }}
|
|
84
|
+
</p>
|
|
85
|
+
</div>
|
|
86
|
+
<div
|
|
87
|
+
class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full sm:w-auto"
|
|
88
|
+
>
|
|
89
|
+
<f-iconfield class="w-full sm:w-64">
|
|
90
|
+
<f-inputicon icon="search" />
|
|
91
|
+
<input
|
|
92
|
+
fInputText
|
|
93
|
+
type="text"
|
|
94
|
+
[ngModel]="searchTerm()"
|
|
95
|
+
(ngModelChange)="onSearchChange($event)"
|
|
96
|
+
[ngModelOptions]="{ standalone: true }"
|
|
97
|
+
[placeholder]="'shared.search' | translate"
|
|
98
|
+
class="w-full"
|
|
99
|
+
/>
|
|
100
|
+
</f-iconfield>
|
|
101
|
+
<button
|
|
102
|
+
fButton
|
|
103
|
+
class="w-full sm:w-auto"
|
|
104
|
+
(click)="showCreateDialog.set(true)"
|
|
105
|
+
>
|
|
106
|
+
<ui-icon pButtonIcon name="plus"></ui-icon>
|
|
107
|
+
<span pButtonLabel>{{ 'task.manager.board.new' | translate }}</span>
|
|
108
|
+
</button>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
@if (state.isLoading()) {
|
|
113
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
114
|
+
@for (i of [1, 2, 3, 4, 5, 6]; track i) {
|
|
115
|
+
<div class="card !p-0 overflow-hidden">
|
|
116
|
+
<f-skeleton height="4px" borderRadius="0" />
|
|
117
|
+
<div class="p-5">
|
|
118
|
+
<f-skeleton height="18px" width="60%" class="mb-3" />
|
|
119
|
+
<f-skeleton height="13px" class="mb-1" />
|
|
120
|
+
<f-skeleton height="13px" width="70%" class="mb-5" />
|
|
121
|
+
<div class="flex gap-5 pt-4 border-t border-surface">
|
|
122
|
+
<f-skeleton height="14px" width="55px" />
|
|
123
|
+
<f-skeleton height="14px" width="45px" />
|
|
124
|
+
<f-skeleton height="14px" width="50px" />
|
|
125
|
+
</div>
|
|
126
|
+
<f-skeleton height="11px" width="100px" class="mt-2" />
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
}
|
|
130
|
+
</div>
|
|
131
|
+
} @else if (filteredBoards().length === 0) {
|
|
132
|
+
<div
|
|
133
|
+
class="flex flex-col items-center justify-center py-20 text-muted-color"
|
|
134
|
+
>
|
|
135
|
+
<span
|
|
136
|
+
class="w-16 h-16 rounded-2xl border-2 border-dashed border-surface flex items-center justify-center mb-4"
|
|
137
|
+
>
|
|
138
|
+
<ui-icon name="th-large" class="text-2xl text-muted-color"></ui-icon>
|
|
139
|
+
</span>
|
|
140
|
+
<p class="text-base font-semibold mb-1">
|
|
141
|
+
{{ 'task.manager.board.empty' | translate }}
|
|
142
|
+
</p>
|
|
143
|
+
<p class="text-sm text-muted-color mb-5">
|
|
144
|
+
{{ 'task.manager.board.empty.hint' | translate }}
|
|
145
|
+
</p>
|
|
146
|
+
<button
|
|
147
|
+
fButton
|
|
148
|
+
size="small"
|
|
149
|
+
(click)="showCreateDialog.set(true)"
|
|
150
|
+
>
|
|
151
|
+
<ui-icon pButtonIcon name="plus"></ui-icon>
|
|
152
|
+
<span pButtonLabel>{{ 'task.manager.board.new' | translate }}</span>
|
|
153
|
+
</button>
|
|
154
|
+
</div>
|
|
155
|
+
} @else {
|
|
156
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
157
|
+
@for (board of filteredBoards(); track board.id) {
|
|
158
|
+
<div
|
|
159
|
+
class="card !p-0 cursor-pointer overflow-hidden group hover:shadow-lg transition-all duration-200"
|
|
160
|
+
(click)="openBoard(board.id)"
|
|
161
|
+
>
|
|
162
|
+
<div class="h-1" [style.background-color]="board.color"></div>
|
|
163
|
+
|
|
164
|
+
<div class="p-5">
|
|
165
|
+
<div class="flex items-start justify-between gap-3 mb-2">
|
|
166
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
167
|
+
<h3
|
|
168
|
+
class="font-semibold text-base m-0 truncate group-hover:text-primary-500 transition-colors duration-150"
|
|
169
|
+
>
|
|
170
|
+
{{ board.name }}
|
|
171
|
+
</h3>
|
|
172
|
+
</div>
|
|
173
|
+
<ui-icon name="arrow-right" class="text-xs text-muted-color group-hover:text-primary-500 group-hover:translate-x-0.5 transition-all duration-150 flex-shrink-0 mt-1"></ui-icon>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<p
|
|
177
|
+
class="text-sm text-muted-color line-clamp-2 pl-[18px] mb-0 min-h-[40px]"
|
|
178
|
+
>
|
|
179
|
+
{{ board.description || '' }}
|
|
180
|
+
</p>
|
|
181
|
+
|
|
182
|
+
<div
|
|
183
|
+
class="border-t border-surface mt-4 pt-4 space-y-2.5"
|
|
184
|
+
>
|
|
185
|
+
<div class="flex items-center gap-4">
|
|
186
|
+
<span class="flex items-center gap-1.5">
|
|
187
|
+
<ui-icon name="users" class="text-[11px] text-muted-color"></ui-icon>
|
|
188
|
+
<span class="text-sm font-semibold">{{
|
|
189
|
+
board.totalMembers ?? 0
|
|
190
|
+
}}</span>
|
|
191
|
+
<span class="text-xs text-muted-color">{{
|
|
192
|
+
'task.manager.board.members' | translate
|
|
193
|
+
}}</span>
|
|
194
|
+
</span>
|
|
195
|
+
<span
|
|
196
|
+
class="w-px h-3 bg-surface-200 dark:bg-surface-600"
|
|
197
|
+
></span>
|
|
198
|
+
<span class="flex items-center gap-1.5">
|
|
199
|
+
<ui-icon name="list" class="text-[11px] text-muted-color"></ui-icon>
|
|
200
|
+
<span class="text-sm font-semibold">{{
|
|
201
|
+
board.totalLists ?? 0
|
|
202
|
+
}}</span>
|
|
203
|
+
<span class="text-xs text-muted-color">{{
|
|
204
|
+
'task.manager.board.lists' | translate
|
|
205
|
+
}}</span>
|
|
206
|
+
</span>
|
|
207
|
+
<span
|
|
208
|
+
class="w-px h-3 bg-surface-200 dark:bg-surface-600"
|
|
209
|
+
></span>
|
|
210
|
+
<span class="flex items-center gap-1.5">
|
|
211
|
+
<ui-icon name="check-square" class="text-[11px] text-muted-color"></ui-icon>
|
|
212
|
+
<span class="text-sm font-semibold">{{
|
|
213
|
+
board.totalTasks ?? 0
|
|
214
|
+
}}</span>
|
|
215
|
+
<span class="text-xs text-muted-color">{{
|
|
216
|
+
'task.manager.board.tasks' | translate
|
|
217
|
+
}}</span>
|
|
218
|
+
</span>
|
|
219
|
+
</div>
|
|
220
|
+
<div
|
|
221
|
+
class="flex items-center gap-1.5 text-xs text-muted-color"
|
|
222
|
+
>
|
|
223
|
+
<ui-icon name="clock" class="text-[10px]"></ui-icon>
|
|
224
|
+
<span>{{ board.createdAt | date: 'MMM d, yyyy' }}</span>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
}
|
|
230
|
+
</div>
|
|
231
|
+
}
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<f-dialog
|
|
235
|
+
[header]="'task.manager.board.new' | translate"
|
|
236
|
+
[(visible)]="showCreateDialog"
|
|
237
|
+
[modal]="true"
|
|
238
|
+
[style]="{ width: '440px' }"
|
|
239
|
+
>
|
|
240
|
+
<div class="flex flex-col gap-4 py-2">
|
|
241
|
+
<div class="flex flex-col gap-1">
|
|
242
|
+
<label class="text-sm font-medium"
|
|
243
|
+
>{{ 'shared.name' | translate }}
|
|
244
|
+
<span class="text-red-500">*</span></label
|
|
245
|
+
>
|
|
246
|
+
<input
|
|
247
|
+
fInputText
|
|
248
|
+
[(ngModel)]="newBoardName"
|
|
249
|
+
[placeholder]="'task.manager.board.name.placeholder' | translate"
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="flex flex-col gap-1">
|
|
253
|
+
<label class="text-sm font-medium">{{
|
|
254
|
+
'shared.description' | translate
|
|
255
|
+
}}</label>
|
|
256
|
+
<textarea
|
|
257
|
+
fTextarea
|
|
258
|
+
[(ngModel)]="newBoardDescription"
|
|
259
|
+
rows="2"
|
|
260
|
+
[placeholder]="
|
|
261
|
+
'task.manager.board.description.placeholder' | translate
|
|
262
|
+
"
|
|
263
|
+
></textarea>
|
|
264
|
+
</div>
|
|
265
|
+
<div class="flex flex-col gap-2">
|
|
266
|
+
<label class="text-sm font-medium">{{
|
|
267
|
+
'task.manager.board.color.label' | translate
|
|
268
|
+
}}</label>
|
|
269
|
+
<div class="flex flex-wrap gap-2">
|
|
270
|
+
@for (c of boardColors; track c) {
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
class="w-8 h-8 rounded-full border-2 transition-all hover:scale-110 focus:outline-none flex items-center justify-center"
|
|
274
|
+
[style.background-color]="c"
|
|
275
|
+
[class.ring-2]="newBoardColor === c"
|
|
276
|
+
[class.ring-offset-2]="newBoardColor === c"
|
|
277
|
+
[class.ring-surface-900]="newBoardColor === c"
|
|
278
|
+
[class.dark:ring-surface-0]="newBoardColor === c"
|
|
279
|
+
[fTooltip]="c"
|
|
280
|
+
(click)="newBoardColor = c"
|
|
281
|
+
>
|
|
282
|
+
@if (newBoardColor === c) {
|
|
283
|
+
<ui-icon name="check" class="text-white text-xs"></ui-icon>
|
|
284
|
+
}
|
|
285
|
+
</button>
|
|
286
|
+
}
|
|
287
|
+
</div>
|
|
288
|
+
<div class="flex items-center gap-2 mt-1">
|
|
289
|
+
<span
|
|
290
|
+
class="w-8 h-8 rounded-full border border-surface flex-shrink-0"
|
|
291
|
+
[style.background-color]="newBoardColor"
|
|
292
|
+
></span>
|
|
293
|
+
<span class="text-sm text-muted-color font-mono">{{
|
|
294
|
+
newBoardColor
|
|
295
|
+
}}</span>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
<ng-template #footer>
|
|
301
|
+
<div class="flex justify-end gap-2">
|
|
302
|
+
<button
|
|
303
|
+
fButton
|
|
304
|
+
severity="secondary"
|
|
305
|
+
(click)="closeCreateDialog()"
|
|
306
|
+
>
|
|
307
|
+
<span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
|
|
308
|
+
</button>
|
|
309
|
+
<button
|
|
310
|
+
fButton
|
|
311
|
+
(click)="createBoard()"
|
|
312
|
+
[disabled]="isCreating()"
|
|
313
|
+
>
|
|
314
|
+
@if (isCreating()) {
|
|
315
|
+
<ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
|
|
316
|
+
}
|
|
317
|
+
<span pButtonLabel>{{ 'shared.create' | translate }}</span>
|
|
318
|
+
</button>
|
|
319
|
+
</div>
|
|
320
|
+
</ng-template>
|
|
321
|
+
</f-dialog>
|
|
322
|
+
`,
|
|
323
|
+
isInline: true,
|
|
324
|
+
dependencies: [
|
|
325
|
+
{
|
|
326
|
+
kind: "ngmodule",
|
|
327
|
+
type: FormsModule
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
kind: "directive",
|
|
331
|
+
type: i1.DefaultValueAccessor,
|
|
332
|
+
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]"
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
kind: "directive",
|
|
336
|
+
type: i1.NgControlStatus,
|
|
337
|
+
selector: "[formControlName],[ngModel],[formControl]"
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
kind: "directive",
|
|
341
|
+
type: i1.NgModel,
|
|
342
|
+
selector: "[ngModel]:not([formControlName]):not([formControl])",
|
|
343
|
+
inputs: [
|
|
344
|
+
"name",
|
|
345
|
+
"disabled",
|
|
346
|
+
"ngModel",
|
|
347
|
+
"ngModelOptions"
|
|
348
|
+
],
|
|
349
|
+
outputs: ["ngModelChange"],
|
|
350
|
+
exportAs: ["ngModel"]
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
kind: "component",
|
|
354
|
+
type: DialogComponent,
|
|
355
|
+
selector: "f-dialog",
|
|
356
|
+
inputs: [
|
|
357
|
+
"header",
|
|
358
|
+
"visible",
|
|
359
|
+
"modal",
|
|
360
|
+
"closable",
|
|
361
|
+
"closeOnEscape",
|
|
362
|
+
"dismissableMask",
|
|
363
|
+
"resizable",
|
|
364
|
+
"draggable",
|
|
365
|
+
"maximizable",
|
|
366
|
+
"blockScroll",
|
|
367
|
+
"returnFocusOnClose",
|
|
368
|
+
"position",
|
|
369
|
+
"style",
|
|
370
|
+
"styleClass",
|
|
371
|
+
"breakpoints"
|
|
372
|
+
],
|
|
373
|
+
outputs: [
|
|
374
|
+
"visibleChange",
|
|
375
|
+
"onShow",
|
|
376
|
+
"onHide",
|
|
377
|
+
"onMaximize"
|
|
378
|
+
]
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
kind: "directive",
|
|
382
|
+
type: FButtonDirective,
|
|
383
|
+
selector: "[fButton]",
|
|
384
|
+
inputs: [
|
|
385
|
+
"severity",
|
|
386
|
+
"text",
|
|
387
|
+
"outlined",
|
|
388
|
+
"link",
|
|
389
|
+
"variant",
|
|
390
|
+
"rounded",
|
|
391
|
+
"raised",
|
|
392
|
+
"fluid",
|
|
393
|
+
"disabled",
|
|
394
|
+
"loading",
|
|
395
|
+
"loadingIcon",
|
|
396
|
+
"size",
|
|
397
|
+
"label",
|
|
398
|
+
"icon",
|
|
399
|
+
"iconPos",
|
|
400
|
+
"ariaLabel",
|
|
401
|
+
"plain"
|
|
402
|
+
]
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
kind: "component",
|
|
406
|
+
type: IconFieldComponent,
|
|
407
|
+
selector: "f-iconfield",
|
|
408
|
+
inputs: ["iconPosition", "styleClass"]
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
kind: "component",
|
|
412
|
+
type: InputIconComponent,
|
|
413
|
+
selector: "f-inputicon",
|
|
414
|
+
inputs: [
|
|
415
|
+
"icon",
|
|
416
|
+
"position",
|
|
417
|
+
"styleClass",
|
|
418
|
+
"style"
|
|
419
|
+
]
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
kind: "directive",
|
|
423
|
+
type: InputTextDirective,
|
|
424
|
+
selector: "[fInputText]",
|
|
425
|
+
inputs: [
|
|
426
|
+
"invalid",
|
|
427
|
+
"touched",
|
|
428
|
+
"variant",
|
|
429
|
+
"size",
|
|
430
|
+
"fluid"
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
kind: "component",
|
|
435
|
+
type: SkeletonComponent,
|
|
436
|
+
selector: "f-skeleton",
|
|
437
|
+
inputs: [
|
|
438
|
+
"width",
|
|
439
|
+
"height",
|
|
440
|
+
"size",
|
|
441
|
+
"borderRadius",
|
|
442
|
+
"shape",
|
|
443
|
+
"animation",
|
|
444
|
+
"styleClass"
|
|
445
|
+
]
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
kind: "directive",
|
|
449
|
+
type: TextareaDirective,
|
|
450
|
+
selector: "[fTextarea]",
|
|
451
|
+
inputs: [
|
|
452
|
+
"invalid",
|
|
453
|
+
"touched",
|
|
454
|
+
"variant",
|
|
455
|
+
"fluid",
|
|
456
|
+
"autoResize"
|
|
457
|
+
],
|
|
458
|
+
outputs: ["onResize"]
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
kind: "directive",
|
|
462
|
+
type: TooltipDirective,
|
|
463
|
+
selector: "[fTooltip]",
|
|
464
|
+
inputs: [
|
|
465
|
+
"fTooltip",
|
|
466
|
+
"tooltipPosition",
|
|
467
|
+
"positionStyle",
|
|
468
|
+
"tooltipStyleClass",
|
|
469
|
+
"disabled",
|
|
470
|
+
"tooltipEvent",
|
|
471
|
+
"showDelay",
|
|
472
|
+
"hideDelay",
|
|
473
|
+
"life",
|
|
474
|
+
"autoHide",
|
|
475
|
+
"positionTop",
|
|
476
|
+
"positionLeft",
|
|
477
|
+
"escape"
|
|
478
|
+
]
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
kind: "component",
|
|
482
|
+
type: UiIconComponent,
|
|
483
|
+
selector: "ui-icon",
|
|
484
|
+
inputs: [
|
|
485
|
+
"name",
|
|
486
|
+
"size",
|
|
487
|
+
"spin",
|
|
488
|
+
"fill",
|
|
489
|
+
"severity"
|
|
490
|
+
]
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
kind: "pipe",
|
|
494
|
+
type: DatePipe,
|
|
495
|
+
name: "date"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
kind: "pipe",
|
|
499
|
+
type: TranslatePipe,
|
|
500
|
+
name: "translate"
|
|
501
|
+
}
|
|
502
|
+
]
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
506
|
+
minVersion: "12.0.0",
|
|
507
|
+
version: "22.2.1",
|
|
508
|
+
ngImport: i0,
|
|
509
|
+
type: BoardListPageComponent,
|
|
510
|
+
decorators: [{
|
|
511
|
+
type: Component,
|
|
512
|
+
args: [{
|
|
513
|
+
selector: "tm-board-list-page",
|
|
514
|
+
imports: [
|
|
515
|
+
DatePipe,
|
|
516
|
+
FormsModule,
|
|
517
|
+
DialogComponent,
|
|
518
|
+
FButtonDirective,
|
|
519
|
+
IconFieldComponent,
|
|
520
|
+
InputIconComponent,
|
|
521
|
+
InputTextDirective,
|
|
522
|
+
SkeletonComponent,
|
|
523
|
+
TextareaDirective,
|
|
524
|
+
TooltipDirective,
|
|
525
|
+
UiIconComponent,
|
|
526
|
+
TranslatePipe
|
|
527
|
+
],
|
|
528
|
+
template: `
|
|
529
|
+
<div class="p-2 sm:p-4">
|
|
530
|
+
<div
|
|
531
|
+
class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4"
|
|
532
|
+
>
|
|
533
|
+
<div>
|
|
534
|
+
<h1 class="text-xl sm:text-2xl font-bold m-0">
|
|
535
|
+
{{ 'task.manager.boards.title' | translate }}
|
|
536
|
+
</h1>
|
|
537
|
+
<p class="text-muted-color mt-1 text-sm sm:text-base">
|
|
538
|
+
{{ 'task.manager.boards.subtitle' | translate }}
|
|
539
|
+
</p>
|
|
540
|
+
</div>
|
|
541
|
+
<div
|
|
542
|
+
class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 w-full sm:w-auto"
|
|
543
|
+
>
|
|
544
|
+
<f-iconfield class="w-full sm:w-64">
|
|
545
|
+
<f-inputicon icon="search" />
|
|
546
|
+
<input
|
|
547
|
+
fInputText
|
|
548
|
+
type="text"
|
|
549
|
+
[ngModel]="searchTerm()"
|
|
550
|
+
(ngModelChange)="onSearchChange($event)"
|
|
551
|
+
[ngModelOptions]="{ standalone: true }"
|
|
552
|
+
[placeholder]="'shared.search' | translate"
|
|
553
|
+
class="w-full"
|
|
554
|
+
/>
|
|
555
|
+
</f-iconfield>
|
|
556
|
+
<button
|
|
557
|
+
fButton
|
|
558
|
+
class="w-full sm:w-auto"
|
|
559
|
+
(click)="showCreateDialog.set(true)"
|
|
560
|
+
>
|
|
561
|
+
<ui-icon pButtonIcon name="plus"></ui-icon>
|
|
562
|
+
<span pButtonLabel>{{ 'task.manager.board.new' | translate }}</span>
|
|
563
|
+
</button>
|
|
564
|
+
</div>
|
|
565
|
+
</div>
|
|
566
|
+
|
|
567
|
+
@if (state.isLoading()) {
|
|
568
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
569
|
+
@for (i of [1, 2, 3, 4, 5, 6]; track i) {
|
|
570
|
+
<div class="card !p-0 overflow-hidden">
|
|
571
|
+
<f-skeleton height="4px" borderRadius="0" />
|
|
572
|
+
<div class="p-5">
|
|
573
|
+
<f-skeleton height="18px" width="60%" class="mb-3" />
|
|
574
|
+
<f-skeleton height="13px" class="mb-1" />
|
|
575
|
+
<f-skeleton height="13px" width="70%" class="mb-5" />
|
|
576
|
+
<div class="flex gap-5 pt-4 border-t border-surface">
|
|
577
|
+
<f-skeleton height="14px" width="55px" />
|
|
578
|
+
<f-skeleton height="14px" width="45px" />
|
|
579
|
+
<f-skeleton height="14px" width="50px" />
|
|
580
|
+
</div>
|
|
581
|
+
<f-skeleton height="11px" width="100px" class="mt-2" />
|
|
582
|
+
</div>
|
|
583
|
+
</div>
|
|
584
|
+
}
|
|
585
|
+
</div>
|
|
586
|
+
} @else if (filteredBoards().length === 0) {
|
|
587
|
+
<div
|
|
588
|
+
class="flex flex-col items-center justify-center py-20 text-muted-color"
|
|
589
|
+
>
|
|
590
|
+
<span
|
|
591
|
+
class="w-16 h-16 rounded-2xl border-2 border-dashed border-surface flex items-center justify-center mb-4"
|
|
592
|
+
>
|
|
593
|
+
<ui-icon name="th-large" class="text-2xl text-muted-color"></ui-icon>
|
|
594
|
+
</span>
|
|
595
|
+
<p class="text-base font-semibold mb-1">
|
|
596
|
+
{{ 'task.manager.board.empty' | translate }}
|
|
597
|
+
</p>
|
|
598
|
+
<p class="text-sm text-muted-color mb-5">
|
|
599
|
+
{{ 'task.manager.board.empty.hint' | translate }}
|
|
600
|
+
</p>
|
|
601
|
+
<button
|
|
602
|
+
fButton
|
|
603
|
+
size="small"
|
|
604
|
+
(click)="showCreateDialog.set(true)"
|
|
605
|
+
>
|
|
606
|
+
<ui-icon pButtonIcon name="plus"></ui-icon>
|
|
607
|
+
<span pButtonLabel>{{ 'task.manager.board.new' | translate }}</span>
|
|
608
|
+
</button>
|
|
609
|
+
</div>
|
|
610
|
+
} @else {
|
|
611
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
612
|
+
@for (board of filteredBoards(); track board.id) {
|
|
613
|
+
<div
|
|
614
|
+
class="card !p-0 cursor-pointer overflow-hidden group hover:shadow-lg transition-all duration-200"
|
|
615
|
+
(click)="openBoard(board.id)"
|
|
616
|
+
>
|
|
617
|
+
<div class="h-1" [style.background-color]="board.color"></div>
|
|
618
|
+
|
|
619
|
+
<div class="p-5">
|
|
620
|
+
<div class="flex items-start justify-between gap-3 mb-2">
|
|
621
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
622
|
+
<h3
|
|
623
|
+
class="font-semibold text-base m-0 truncate group-hover:text-primary-500 transition-colors duration-150"
|
|
624
|
+
>
|
|
625
|
+
{{ board.name }}
|
|
626
|
+
</h3>
|
|
627
|
+
</div>
|
|
628
|
+
<ui-icon name="arrow-right" class="text-xs text-muted-color group-hover:text-primary-500 group-hover:translate-x-0.5 transition-all duration-150 flex-shrink-0 mt-1"></ui-icon>
|
|
629
|
+
</div>
|
|
630
|
+
|
|
631
|
+
<p
|
|
632
|
+
class="text-sm text-muted-color line-clamp-2 pl-[18px] mb-0 min-h-[40px]"
|
|
633
|
+
>
|
|
634
|
+
{{ board.description || '' }}
|
|
635
|
+
</p>
|
|
636
|
+
|
|
637
|
+
<div
|
|
638
|
+
class="border-t border-surface mt-4 pt-4 space-y-2.5"
|
|
639
|
+
>
|
|
640
|
+
<div class="flex items-center gap-4">
|
|
641
|
+
<span class="flex items-center gap-1.5">
|
|
642
|
+
<ui-icon name="users" class="text-[11px] text-muted-color"></ui-icon>
|
|
643
|
+
<span class="text-sm font-semibold">{{
|
|
644
|
+
board.totalMembers ?? 0
|
|
645
|
+
}}</span>
|
|
646
|
+
<span class="text-xs text-muted-color">{{
|
|
647
|
+
'task.manager.board.members' | translate
|
|
648
|
+
}}</span>
|
|
649
|
+
</span>
|
|
650
|
+
<span
|
|
651
|
+
class="w-px h-3 bg-surface-200 dark:bg-surface-600"
|
|
652
|
+
></span>
|
|
653
|
+
<span class="flex items-center gap-1.5">
|
|
654
|
+
<ui-icon name="list" class="text-[11px] text-muted-color"></ui-icon>
|
|
655
|
+
<span class="text-sm font-semibold">{{
|
|
656
|
+
board.totalLists ?? 0
|
|
657
|
+
}}</span>
|
|
658
|
+
<span class="text-xs text-muted-color">{{
|
|
659
|
+
'task.manager.board.lists' | translate
|
|
660
|
+
}}</span>
|
|
661
|
+
</span>
|
|
662
|
+
<span
|
|
663
|
+
class="w-px h-3 bg-surface-200 dark:bg-surface-600"
|
|
664
|
+
></span>
|
|
665
|
+
<span class="flex items-center gap-1.5">
|
|
666
|
+
<ui-icon name="check-square" class="text-[11px] text-muted-color"></ui-icon>
|
|
667
|
+
<span class="text-sm font-semibold">{{
|
|
668
|
+
board.totalTasks ?? 0
|
|
669
|
+
}}</span>
|
|
670
|
+
<span class="text-xs text-muted-color">{{
|
|
671
|
+
'task.manager.board.tasks' | translate
|
|
672
|
+
}}</span>
|
|
673
|
+
</span>
|
|
674
|
+
</div>
|
|
675
|
+
<div
|
|
676
|
+
class="flex items-center gap-1.5 text-xs text-muted-color"
|
|
677
|
+
>
|
|
678
|
+
<ui-icon name="clock" class="text-[10px]"></ui-icon>
|
|
679
|
+
<span>{{ board.createdAt | date: 'MMM d, yyyy' }}</span>
|
|
680
|
+
</div>
|
|
681
|
+
</div>
|
|
682
|
+
</div>
|
|
683
|
+
</div>
|
|
684
|
+
}
|
|
685
|
+
</div>
|
|
686
|
+
}
|
|
687
|
+
</div>
|
|
688
|
+
|
|
689
|
+
<f-dialog
|
|
690
|
+
[header]="'task.manager.board.new' | translate"
|
|
691
|
+
[(visible)]="showCreateDialog"
|
|
692
|
+
[modal]="true"
|
|
693
|
+
[style]="{ width: '440px' }"
|
|
694
|
+
>
|
|
695
|
+
<div class="flex flex-col gap-4 py-2">
|
|
696
|
+
<div class="flex flex-col gap-1">
|
|
697
|
+
<label class="text-sm font-medium"
|
|
698
|
+
>{{ 'shared.name' | translate }}
|
|
699
|
+
<span class="text-red-500">*</span></label
|
|
700
|
+
>
|
|
701
|
+
<input
|
|
702
|
+
fInputText
|
|
703
|
+
[(ngModel)]="newBoardName"
|
|
704
|
+
[placeholder]="'task.manager.board.name.placeholder' | translate"
|
|
705
|
+
/>
|
|
706
|
+
</div>
|
|
707
|
+
<div class="flex flex-col gap-1">
|
|
708
|
+
<label class="text-sm font-medium">{{
|
|
709
|
+
'shared.description' | translate
|
|
710
|
+
}}</label>
|
|
711
|
+
<textarea
|
|
712
|
+
fTextarea
|
|
713
|
+
[(ngModel)]="newBoardDescription"
|
|
714
|
+
rows="2"
|
|
715
|
+
[placeholder]="
|
|
716
|
+
'task.manager.board.description.placeholder' | translate
|
|
717
|
+
"
|
|
718
|
+
></textarea>
|
|
719
|
+
</div>
|
|
720
|
+
<div class="flex flex-col gap-2">
|
|
721
|
+
<label class="text-sm font-medium">{{
|
|
722
|
+
'task.manager.board.color.label' | translate
|
|
723
|
+
}}</label>
|
|
724
|
+
<div class="flex flex-wrap gap-2">
|
|
725
|
+
@for (c of boardColors; track c) {
|
|
726
|
+
<button
|
|
727
|
+
type="button"
|
|
728
|
+
class="w-8 h-8 rounded-full border-2 transition-all hover:scale-110 focus:outline-none flex items-center justify-center"
|
|
729
|
+
[style.background-color]="c"
|
|
730
|
+
[class.ring-2]="newBoardColor === c"
|
|
731
|
+
[class.ring-offset-2]="newBoardColor === c"
|
|
732
|
+
[class.ring-surface-900]="newBoardColor === c"
|
|
733
|
+
[class.dark:ring-surface-0]="newBoardColor === c"
|
|
734
|
+
[fTooltip]="c"
|
|
735
|
+
(click)="newBoardColor = c"
|
|
736
|
+
>
|
|
737
|
+
@if (newBoardColor === c) {
|
|
738
|
+
<ui-icon name="check" class="text-white text-xs"></ui-icon>
|
|
739
|
+
}
|
|
740
|
+
</button>
|
|
741
|
+
}
|
|
742
|
+
</div>
|
|
743
|
+
<div class="flex items-center gap-2 mt-1">
|
|
744
|
+
<span
|
|
745
|
+
class="w-8 h-8 rounded-full border border-surface flex-shrink-0"
|
|
746
|
+
[style.background-color]="newBoardColor"
|
|
747
|
+
></span>
|
|
748
|
+
<span class="text-sm text-muted-color font-mono">{{
|
|
749
|
+
newBoardColor
|
|
750
|
+
}}</span>
|
|
751
|
+
</div>
|
|
752
|
+
</div>
|
|
753
|
+
</div>
|
|
754
|
+
|
|
755
|
+
<ng-template #footer>
|
|
756
|
+
<div class="flex justify-end gap-2">
|
|
757
|
+
<button
|
|
758
|
+
fButton
|
|
759
|
+
severity="secondary"
|
|
760
|
+
(click)="closeCreateDialog()"
|
|
761
|
+
>
|
|
762
|
+
<span pButtonLabel>{{ 'shared.cancel' | translate }}</span>
|
|
763
|
+
</button>
|
|
764
|
+
<button
|
|
765
|
+
fButton
|
|
766
|
+
(click)="createBoard()"
|
|
767
|
+
[disabled]="isCreating()"
|
|
768
|
+
>
|
|
769
|
+
@if (isCreating()) {
|
|
770
|
+
<ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
|
|
771
|
+
}
|
|
772
|
+
<span pButtonLabel>{{ 'shared.create' | translate }}</span>
|
|
773
|
+
</button>
|
|
774
|
+
</div>
|
|
775
|
+
</ng-template>
|
|
776
|
+
</f-dialog>
|
|
777
|
+
`
|
|
778
|
+
}]
|
|
779
|
+
}],
|
|
780
|
+
ctorParameters: () => []
|
|
781
|
+
});
|
|
782
|
+
export { BoardListPageComponent };
|
|
783
|
+
|
|
784
|
+
//# sourceMappingURL=flusys-ng-task-manager-board-list-page.component-Dmay3IyF.mjs.map
|