@flusys/ng-task-manager 9.1.0 → 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.
@@ -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