Skip to content

Commit c4584e9

Browse files
committed
feat: add project list demo
1 parent be4f0d0 commit c4584e9

5 files changed

Lines changed: 243 additions & 27 deletions

File tree

src/app/core/layout/component/app-menu/app-menu.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export class AppMenuComponent {
2525
items: [
2626
{ label: 'All Projects', icon: 'pi pi-fw pi-list', routerLink: ['/projects'] },
2727
{ label: 'Create Project', icon: 'pi pi-fw pi-plus', routerLink: ['/projects/create'] },
28+
{ label: 'Kanban Board', icon: 'pi pi-fw pi-th-large', routerLink: ['/projects/kanban'] },
2829
{ label: 'Archived Projects', icon: 'pi pi-fw pi-folder', routerLink: ['/projects/archived'] }
2930
]
3031
},
Lines changed: 139 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,140 @@
1-
@if (projects.length > 0) {
2-
@for (project of projects; track project.id) {
3-
<div class="project">
4-
<h2>{{ project.name }}</h2>
5-
<p>{{ project.description }}</p>
6-
7-
@if (project.taskItems && project.taskItems.length > 0) {
8-
<div class="tasks">
9-
@for (task of project.taskItems; track task.id) {
10-
<div class="task">
11-
<span>{{ task.title }} - {{ task.status }}</span>
12-
</div>
13-
}
14-
</div>
15-
} @else {
16-
<p>No tasks for this project</p>
17-
}
1+
<div class="grid">
2+
<div class="col-12">
3+
<p-card>
4+
<div class="card">
5+
<p-table #dt1 [value]="projects" dataKey="id" [rows]="10" [rowsPerPageOptions]="[10, 25, 50]"
6+
[loading]="loading" [paginator]="true" styleClass="p-datatable-gridlines"
7+
[globalFilterFields]="['name', 'country.name', 'representative.name', 'status']">
8+
<ng-template pTemplate="caption">
9+
<div class="flex">
10+
<p-button label="Clear" [outlined]="true" icon="pi pi-filter-slash" (onClick)="clear(dt1)" />
11+
<span class="p-input-icon-left ml-auto">
12+
<i class="pi pi-search"></i>
13+
<input pInputText type="text" [(ngModel)]="searchValue" (input)="onGlobalFilter(dt1, $event)"
14+
placeholder="Search keyword" />
15+
</span>
16+
</div>
17+
</ng-template>
18+
<ng-template pTemplate="header">
19+
<tr>
20+
<th style="min-width:15rem">
21+
<div class="flex align-items-center">
22+
Name
23+
<p-columnFilter type="text" field="name" display="menu" />
24+
</div>
25+
</th>
26+
<th style="min-width:15rem">
27+
<div class="flex align-items-center">
28+
Owner
29+
<p-columnFilter field="representative" matchMode="in" display="menu" [showMatchModes]="false"
30+
[showOperator]="false" [showAddButton]="false">
31+
<ng-template pTemplate="header">
32+
<div class="px-3 pt-3 pb-0">
33+
<span class="font-bold">Owner Picker</span>
34+
</div>
35+
</ng-template>
36+
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
37+
<p-multiSelect [ngModel]="value" [options]="representatives" placeholder="Any"
38+
(onChange)="filter($event.value)" optionLabel="name">
39+
<ng-template let-option pTemplate="item">
40+
<div class="inline-block vertical-align-middle">
41+
<img [alt]="option.label"
42+
src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="24"
43+
class="vertical-align-middle" />
44+
<span class="ml-1 mt-1">{{ option.name }}</span>
45+
</div>
46+
</ng-template>
47+
</p-multiSelect>
48+
</ng-template>
49+
</p-columnFilter>
50+
</div>
51+
</th>
52+
<th style="min-width:10rem">
53+
<div class="flex align-items-center">
54+
Created At
55+
<p-columnFilter type="date" field="createdate" display="menu" />
56+
</div>
57+
</th>
58+
<th style="min-width:10rem">
59+
<div class="flex align-items-center">
60+
Last Modified At
61+
<p-columnFilter type="date" field="modifydate" display="menu" />
62+
</div>
63+
</th>
64+
<th style="min-width:10rem">
65+
<div class="flex align-items-center">
66+
Status
67+
<p-columnFilter field="status" matchMode="equals" display="menu">
68+
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
69+
<p-dropdown [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)"
70+
placeholder="Any">
71+
<ng-template let-option pTemplate="item">
72+
<p-tag [value]="option.value" [severity]="getSeverity(option.label)" />
73+
</ng-template>
74+
</p-dropdown>
75+
</ng-template>
76+
</p-columnFilter>
77+
</div>
78+
</th>
79+
<th style="min-width:10rem">
80+
<div class="flex align-items-center">
81+
Activity
82+
<p-columnFilter field="activity" matchMode="between" display="menu" [showMatchModes]="false"
83+
[showOperator]="false" [showAddButton]="false">
84+
<ng-template pTemplate="filter" let-filter="filterCallback">
85+
<p-slider [(ngModel)]="activityValues" [range]="true" (onSlideEnd)="filter($event.values)"
86+
styleClass="m-3" />
87+
<div class="flex align-items-center px-2">
88+
<span>{{ activityValues[0] }}</span>
89+
<span>{{ activityValues[1] }}</span>
90+
</div>
91+
</ng-template>
92+
</p-columnFilter>
93+
</div>
94+
</th>
95+
<th style="width: 3rem">
96+
<div class="flex align-items-center">
97+
Verified
98+
<p-columnFilter type="boolean" field="verified" display="menu" />
99+
</div>
100+
</th>
101+
</tr>
102+
</ng-template>
103+
<ng-template pTemplate="body" let-project>
104+
<tr>
105+
<td>
106+
{{ project.name }}
107+
</td>
108+
<td>
109+
<img [alt]="project.createdBy" src="https://primefaces.org/cdn/primeng/images/demo/avatar/amyelsner.png"
110+
width="32" style="vertical-align: middle" />
111+
<span class="ml-1 vertical-align-middle">{{ project.createdBy }}</span>
112+
</td>
113+
<td>
114+
{{ project.createdAt | date: 'MM/dd/yyyy' }}
115+
</td>
116+
<td>
117+
{{ project.lastModifiedAt | date: 'MM/dd/yyyy' }}
118+
</td>
119+
<td>
120+
<p-tag [value]="'new'" [severity]="getSeverity('new')" />
121+
</td>
122+
<td>
123+
<p-progressBar [value]="30" [showValue]="false" />
124+
</td>
125+
<td class="text-center">
126+
<i class="pi"
127+
[ngClass]="{ 'text-green-500 pi-check-circle': true, 'text-red-500 pi-times-circle': false }"></i>
128+
</td>
129+
</tr>
130+
</ng-template>
131+
<ng-template pTemplate="emptymessage">
132+
<tr>
133+
<td colspan="7">No projects found.</td>
134+
</tr>
135+
</ng-template>
136+
</p-table>
18137
</div>
19-
}
20-
} @else {
21-
<p>No projects found</p>
22-
}
138+
</p-card>
139+
</div>
140+
</div>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
::ng-deep {
2+
.p-progressbar {
3+
height: .5rem;
4+
background-color: #D8DADC;
5+
6+
.p-progressbar-value {
7+
background-color: #607D8B;
8+
}
9+
}
10+
}
Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,64 @@
11
import { Component, OnInit } from '@angular/core';
22
import { ProjectDto, ProjectService } from '../../services/project.service';
3+
import { CommonModule } from '@angular/common';
4+
import { SharedModule } from '../../../../shared/shared.module';
5+
import { Table } from 'primeng/table';
36

47
@Component({
58
selector: 'app-project-list',
69
standalone: true,
7-
imports: [],
10+
imports: [CommonModule, SharedModule],
811
templateUrl: './project-list.component.html',
912
styleUrl: './project-list.component.scss'
1013
})
1114
export class ProjectListComponent implements OnInit {
1215
projects: ProjectDto[] = [];
1316

14-
constructor(private projectService: ProjectService) {}
17+
representatives: any[] = [];
18+
19+
statuses!: any[];
20+
21+
loading: boolean = true;
22+
23+
activityValues: number[] = [0, 100];
24+
25+
searchValue: string | undefined;
26+
27+
constructor(private projectService: ProjectService) { }
1528

1629
ngOnInit() {
17-
this.projectService.getUserProjects().subscribe(
18-
projects => this.projects = projects
19-
);
30+
this.projectService.getUserProjects().subscribe({
31+
next: (projects) => {
32+
this.projects = projects;
33+
this.loading = false;
34+
},
35+
error: (err) => {
36+
console.error('Error fetching projects:', err);
37+
this.loading = false;
38+
}
39+
});
40+
41+
this.representatives = [
42+
{ name: 'Amy Elsner', image: 'amyelsner.png' },
43+
{ name: 'Anna Fali', image: 'annafali.png' },
44+
{ name: 'Asiya Javayant', image: 'asiyajavayant.png' },
45+
{ name: 'Bernardo Dominic', image: 'bernardodominic.png' },
46+
{ name: 'Elwin Sharvill', image: 'elwinsharvill.png' },
47+
{ name: 'Ioni Bowcher', image: 'ionibowcher.png' },
48+
{ name: 'Ivan Magalhaes', image: 'ivanmagalhaes.png' },
49+
{ name: 'Onyama Limba', image: 'onyamalimba.png' },
50+
{ name: 'Stephen Shaw', image: 'stephenshaw.png' },
51+
{ name: 'Xuxue Feng', image: 'xuxuefeng.png' }
52+
];
53+
54+
this.statuses = [
55+
{ label: 'Unqualified', value: 'unqualified' },
56+
{ label: 'Qualified', value: 'qualified' },
57+
{ label: 'New', value: 'new' },
58+
{ label: 'Negotiation', value: 'negotiation' },
59+
{ label: 'Renewal', value: 'renewal' },
60+
{ label: 'Proposal', value: 'proposal' }
61+
];
2062
}
2163

2264
createProject() {
@@ -29,4 +71,33 @@ export class ProjectListComponent implements OnInit {
2971
createdProject => this.projects.push(createdProject)
3072
);
3173
}
74+
75+
clear(table: Table) {
76+
table.clear();
77+
this.searchValue = ''
78+
}
79+
80+
getSeverity(status: string) {
81+
switch (status.toLowerCase()) {
82+
case 'unqualified':
83+
return 'danger';
84+
85+
case 'qualified':
86+
return 'success';
87+
88+
case 'new':
89+
return 'info';
90+
91+
case 'negotiation':
92+
return 'warning';
93+
94+
case 'renewal':
95+
return undefined;
96+
}
97+
return undefined;
98+
}
99+
100+
onGlobalFilter(table: Table, event: Event) {
101+
table.filterGlobal((event.target as HTMLInputElement).value, 'contains');
102+
}
32103
}

src/app/shared/shared.module.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import { AvatarGroupModule } from 'primeng/avatargroup';
1010
import { IconFieldModule } from 'primeng/iconfield';
1111
import { InputIconModule } from 'primeng/inputicon';
1212
import { InputSwitchModule } from 'primeng/inputswitch';
13+
import { TableModule } from 'primeng/table';
14+
import { DropdownModule } from 'primeng/dropdown';
15+
import { MultiSelectModule } from 'primeng/multiselect';
16+
import { ProgressBarModule } from 'primeng/progressbar';
17+
import { TagModule } from 'primeng/tag';
18+
import { SliderModule } from 'primeng/slider';
1319

1420
@NgModule({
1521
exports: [
@@ -24,7 +30,17 @@ import { InputSwitchModule } from 'primeng/inputswitch';
2430
AvatarGroupModule,
2531
IconFieldModule,
2632
InputIconModule,
27-
InputSwitchModule
33+
InputSwitchModule,
34+
TableModule,
35+
InputTextModule,
36+
TagModule,
37+
DropdownModule,
38+
MultiSelectModule,
39+
ProgressBarModule,
40+
SliderModule,
41+
IconFieldModule,
42+
InputIconModule,
43+
MultiSelectModule
2844
],
2945
})
3046
export class SharedModule {}

0 commit comments

Comments
 (0)