@duyanhdev/mvp-ifs-ui-kit 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/.editorconfig +16 -0
  2. package/.gitmodules +3 -0
  3. package/.postcssrc.json +5 -0
  4. package/.prettierignore +14 -0
  5. package/.prettierrc.json +29 -0
  6. package/LICENSE.md +21 -0
  7. package/README.md +59 -0
  8. package/angular.json +98 -0
  9. package/eslint.config.js +89 -0
  10. package/package.json +59 -0
  11. package/public/demo/images/flag/flag_placeholder.png +0 -0
  12. package/public/demo/images/footer-image.gif +0 -0
  13. package/public/demo/images/galleria/galleria1.jpg +0 -0
  14. package/public/demo/images/galleria/galleria10.jpg +0 -0
  15. package/public/demo/images/galleria/galleria10s.jpg +0 -0
  16. package/public/demo/images/galleria/galleria11.jpg +0 -0
  17. package/public/demo/images/galleria/galleria11s.jpg +0 -0
  18. package/public/demo/images/galleria/galleria12.jpg +0 -0
  19. package/public/demo/images/galleria/galleria12s.jpg +0 -0
  20. package/public/demo/images/galleria/galleria13.jpg +0 -0
  21. package/public/demo/images/galleria/galleria13s.jpg +0 -0
  22. package/public/demo/images/galleria/galleria14.jpg +0 -0
  23. package/public/demo/images/galleria/galleria14s.jpg +0 -0
  24. package/public/demo/images/galleria/galleria15.jpg +0 -0
  25. package/public/demo/images/galleria/galleria15s.jpg +0 -0
  26. package/public/demo/images/galleria/galleria1s.jpg +0 -0
  27. package/public/demo/images/galleria/galleria2.jpg +0 -0
  28. package/public/demo/images/galleria/galleria2s.jpg +0 -0
  29. package/public/demo/images/galleria/galleria3.jpg +0 -0
  30. package/public/demo/images/galleria/galleria3s.jpg +0 -0
  31. package/public/demo/images/galleria/galleria4.jpg +0 -0
  32. package/public/demo/images/galleria/galleria4s.jpg +0 -0
  33. package/public/demo/images/galleria/galleria5.jpg +0 -0
  34. package/public/demo/images/galleria/galleria5s.jpg +0 -0
  35. package/public/demo/images/galleria/galleria6.jpg +0 -0
  36. package/public/demo/images/galleria/galleria6s.jpg +0 -0
  37. package/public/demo/images/galleria/galleria7.jpg +0 -0
  38. package/public/demo/images/galleria/galleria7s.jpg +0 -0
  39. package/public/demo/images/galleria/galleria8.jpg +0 -0
  40. package/public/demo/images/galleria/galleria8s.jpg +0 -0
  41. package/public/demo/images/galleria/galleria9.jpg +0 -0
  42. package/public/demo/images/galleria/galleria9s.jpg +0 -0
  43. package/public/demo/images/product/bamboo-watch.jpg +0 -0
  44. package/public/demo/images/product/black-watch.jpg +0 -0
  45. package/public/demo/images/product/blue-band.jpg +0 -0
  46. package/public/demo/images/product/blue-t-shirt.jpg +0 -0
  47. package/public/demo/images/product/bracelet.jpg +0 -0
  48. package/public/demo/images/product/brown-purse.jpg +0 -0
  49. package/public/demo/images/product/chakra-bracelet.jpg +0 -0
  50. package/public/demo/images/product/galaxy-earrings.jpg +0 -0
  51. package/public/demo/images/product/game-controller.jpg +0 -0
  52. package/public/demo/images/product/gaming-set.jpg +0 -0
  53. package/public/demo/images/product/gold-phone-case.jpg +0 -0
  54. package/public/demo/images/product/green-earbuds.jpg +0 -0
  55. package/public/demo/images/product/green-t-shirt.jpg +0 -0
  56. package/public/demo/images/product/grey-t-shirt.jpg +0 -0
  57. package/public/demo/images/product/headphones.jpg +0 -0
  58. package/public/demo/images/product/light-green-t-shirt.jpg +0 -0
  59. package/public/demo/images/product/lime-band.jpg +0 -0
  60. package/public/demo/images/product/mini-speakers.jpg +0 -0
  61. package/public/demo/images/product/painted-phone-case.jpg +0 -0
  62. package/public/demo/images/product/pink-band.jpg +0 -0
  63. package/public/demo/images/product/pink-purse.jpg +0 -0
  64. package/public/demo/images/product/product-placeholder.svg +10 -0
  65. package/public/demo/images/product/purple-band.jpg +0 -0
  66. package/public/demo/images/product/purple-gemstone-necklace.jpg +0 -0
  67. package/public/demo/images/product/purple-t-shirt.jpg +0 -0
  68. package/public/demo/images/product/shoes.jpg +0 -0
  69. package/public/demo/images/product/sneakers.jpg +0 -0
  70. package/public/demo/images/product/teal-t-shirt.jpg +0 -0
  71. package/public/demo/images/product/yellow-earbuds.jpg +0 -0
  72. package/public/demo/images/product/yoga-mat.jpg +0 -0
  73. package/public/demo/images/product/yoga-set.jpg +0 -0
  74. package/src/app/layout/component/configurator/app.configurator.html +48 -0
  75. package/src/app/layout/component/configurator/app.configurator.ts +396 -0
  76. package/src/app/layout/component/floatingconfigurator/app.floatingconfigurator.ts +31 -0
  77. package/src/app/layout/component/footer/app.footer.scss +52 -0
  78. package/src/app/layout/component/footer/app.footer.ts +26 -0
  79. package/src/app/layout/component/layout/app.layout.ts +50 -0
  80. package/src/app/layout/component/menu/app.menu.html +7 -0
  81. package/src/app/layout/component/menu/app.menu.scss +13 -0
  82. package/src/app/layout/component/menu/app.menu.ts +90 -0
  83. package/src/app/layout/component/menuitem/app.menuitem.html +56 -0
  84. package/src/app/layout/component/menuitem/app.menuitem.scss +218 -0
  85. package/src/app/layout/component/menuitem/app.menuitem.ts +126 -0
  86. package/src/app/layout/component/sidebar/app.sidebar.html +3 -0
  87. package/src/app/layout/component/sidebar/app.sidebar.scss +0 -0
  88. package/src/app/layout/component/sidebar/app.sidebar.ts +106 -0
  89. package/src/app/layout/component/topbar/app.topbar.html +190 -0
  90. package/src/app/layout/component/topbar/app.topbar.scss +8 -0
  91. package/src/app/layout/component/topbar/app.topbar.ts +68 -0
  92. package/src/app/layout/service/layout.service.ts +117 -0
  93. package/src/app/pages/auth/access.ts +32 -0
  94. package/src/app/pages/auth/auth.routes.ts +10 -0
  95. package/src/app/pages/auth/error.ts +32 -0
  96. package/src/app/pages/auth/login.ts +71 -0
  97. package/src/app/pages/crud/crud.ts +387 -0
  98. package/src/app/pages/dashboard/dashboard.css +778 -0
  99. package/src/app/pages/dashboard/dashboard.html +191 -0
  100. package/src/app/pages/dashboard/dashboard.ts +348 -0
  101. package/src/app/pages/documentation/documentation.ts +73 -0
  102. package/src/app/pages/empty/empty.ts +11 -0
  103. package/src/app/pages/landing/components/featureswidget.ts +139 -0
  104. package/src/app/pages/landing/components/footerwidget.ts +73 -0
  105. package/src/app/pages/landing/components/herowidget.ts +25 -0
  106. package/src/app/pages/landing/components/highlightswidget.ts +46 -0
  107. package/src/app/pages/landing/components/pricingwidget.ts +119 -0
  108. package/src/app/pages/landing/components/topbarwidget.component.ts +68 -0
  109. package/src/app/pages/landing/landing.ts +31 -0
  110. package/src/app/pages/notfound/notfound.ts +68 -0
  111. package/src/app/pages/pages.routes.ts +17 -0
  112. package/src/app/pages/profile/profile.html +57 -0
  113. package/src/app/pages/profile/profile.scss +145 -0
  114. package/src/app/pages/profile/profile.ts +19 -0
  115. package/src/app/pages/service/country.service.ts +255 -0
  116. package/src/app/pages/service/customer.service.ts +9057 -0
  117. package/src/app/pages/service/icon.service.ts +23 -0
  118. package/src/app/pages/service/node.service.ts +816 -0
  119. package/src/app/pages/service/photo.service.ts +103 -0
  120. package/src/app/pages/service/product.service.ts +1322 -0
  121. package/src/app/pages/tickets/tickets-create/tickets-create.html +140 -0
  122. package/src/app/pages/tickets/tickets-create/tickets-create.scss +617 -0
  123. package/src/app/pages/tickets/tickets-create/tickets-create.ts +104 -0
  124. package/src/app/pages/tickets/tickets-list/ticket-list.html +150 -0
  125. package/src/app/pages/tickets/tickets-list/ticket-list.scss +392 -0
  126. package/src/app/pages/tickets/tickets-list/ticket-list.ts +178 -0
  127. package/src/app/pages/uikit/buttondemo.ts +254 -0
  128. package/src/app/pages/uikit/chartdemo.ts +290 -0
  129. package/src/app/pages/uikit/filedemo.ts +52 -0
  130. package/src/app/pages/uikit/formlayoutdemo.ts +129 -0
  131. package/src/app/pages/uikit/inputdemo.ts +339 -0
  132. package/src/app/pages/uikit/listdemo.ts +217 -0
  133. package/src/app/pages/uikit/mediademo.ts +1021 -0
  134. package/src/app/pages/uikit/menudemo.ts +540 -0
  135. package/src/app/pages/uikit/messagesdemo.ts +101 -0
  136. package/src/app/pages/uikit/miscdemo.ts +192 -0
  137. package/src/app/pages/uikit/overlaydemo.ts +235 -0
  138. package/src/app/pages/uikit/panelsdemo.ts +235 -0
  139. package/src/app/pages/uikit/tabledemo.ts +568 -0
  140. package/src/app/pages/uikit/timelinedemo.ts +141 -0
  141. package/src/app/pages/uikit/treedemo.ts +75 -0
  142. package/src/app/pages/uikit/uikit.routes.ts +35 -0
  143. package/src/app.component.ts +22 -0
  144. package/src/app.config.ts +23 -0
  145. package/src/app.routes.ts +23 -0
  146. package/src/assets/demo/code.scss +17 -0
  147. package/src/assets/demo/demo.scss +2 -0
  148. package/src/assets/demo/flags/flags.css +984 -0
  149. package/src/assets/layout/_core.scss +24 -0
  150. package/src/assets/layout/_footer.scss +8 -0
  151. package/src/assets/layout/_main.scss +21 -0
  152. package/src/assets/layout/_menu.scss +159 -0
  153. package/src/assets/layout/_mixins.scss +15 -0
  154. package/src/assets/layout/_preloading.scss +47 -0
  155. package/src/assets/layout/_responsive.scss +111 -0
  156. package/src/assets/layout/_topbar.scss +201 -0
  157. package/src/assets/layout/_typography.scss +68 -0
  158. package/src/assets/layout/_utils.scss +25 -0
  159. package/src/assets/layout/layout.scss +13 -0
  160. package/src/assets/layout/variables/_common.scss +21 -0
  161. package/src/assets/layout/variables/_dark.scss +5 -0
  162. package/src/assets/layout/variables/_light.scss +5 -0
  163. package/src/assets/styles.scss +4 -0
  164. package/src/assets/tailwind.css +32 -0
  165. package/src/index.html +15 -0
  166. package/src/main.ts +5 -0
  167. package/tsconfig.app.json +15 -0
  168. package/tsconfig.json +33 -0
  169. package/tsconfig.spec.json +15 -0
  170. package/vercel.json +9 -0
@@ -0,0 +1,568 @@
1
+ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
2
+ import { ConfirmationService, MessageService } from 'primeng/api';
3
+ import { InputTextModule } from 'primeng/inputtext';
4
+ import { MultiSelectModule } from 'primeng/multiselect';
5
+ import { SelectModule } from 'primeng/select';
6
+ import { SliderModule } from 'primeng/slider';
7
+ import { Table, TableModule } from 'primeng/table';
8
+ import { ProgressBarModule } from 'primeng/progressbar';
9
+ import { ToggleButtonModule } from 'primeng/togglebutton';
10
+ import { ToastModule } from 'primeng/toast';
11
+ import { CommonModule } from '@angular/common';
12
+ import { FormsModule } from '@angular/forms';
13
+ import { ButtonModule } from 'primeng/button';
14
+ import { RatingModule } from 'primeng/rating';
15
+ import { RippleModule } from 'primeng/ripple';
16
+ import { InputIconModule } from 'primeng/inputicon';
17
+ import { IconFieldModule } from 'primeng/iconfield';
18
+ import { TagModule } from 'primeng/tag';
19
+ import { Customer, CustomerService, Representative } from '@/app/pages/service/customer.service';
20
+ import { Product, ProductService } from '@/app/pages/service/product.service';
21
+ import {ObjectUtils} from "primeng/utils";
22
+
23
+ interface expandedRows {
24
+ [key: string]: boolean;
25
+ }
26
+
27
+ @Component({
28
+ selector: 'app-table-demo',
29
+ standalone: true,
30
+ imports: [
31
+ TableModule,
32
+ MultiSelectModule,
33
+ SelectModule,
34
+ InputIconModule,
35
+ TagModule,
36
+ InputTextModule,
37
+ SliderModule,
38
+ ProgressBarModule,
39
+ ToggleButtonModule,
40
+ ToastModule,
41
+ CommonModule,
42
+ FormsModule,
43
+ ButtonModule,
44
+ RatingModule,
45
+ RippleModule,
46
+ IconFieldModule
47
+ ],
48
+ template: ` <div class="card">
49
+ <div class="font-semibold text-xl mb-4">Filtering</div>
50
+ <p-table
51
+ #dt1
52
+ [value]="customers1"
53
+ dataKey="id"
54
+ [rows]="10"
55
+ [loading]="loading"
56
+ [rowHover]="true"
57
+ [showGridlines]="true"
58
+ [paginator]="true"
59
+ [globalFilterFields]="['name', 'country.name', 'representative.name', 'status']"
60
+ responsiveLayout="scroll"
61
+ >
62
+ <ng-template #caption>
63
+ <div class="flex justify-between items-center flex-column sm:flex-row">
64
+ <button pButton label="Clear" class="p-button-outlined mb-2" icon="pi pi-filter-slash" (click)="clear(dt1)"></button>
65
+ <p-iconfield iconPosition="left" class="ml-auto">
66
+ <p-inputicon>
67
+ <i class="pi pi-search"></i>
68
+ </p-inputicon>
69
+ <input pInputText type="text" (input)="onGlobalFilter(dt1, $event)" placeholder="Search keyword" />
70
+ </p-iconfield>
71
+ </div>
72
+ </ng-template>
73
+ <ng-template #header>
74
+ <tr>
75
+ <th style="min-width: 12rem">
76
+ <div class="flex justify-between items-center">
77
+ Name
78
+ <p-columnFilter type="text" field="name" display="menu" placeholder="Search by name"></p-columnFilter>
79
+ </div>
80
+ </th>
81
+ <th style="min-width: 12rem">
82
+ <div class="flex justify-between items-center">
83
+ Country
84
+ <p-columnFilter type="text" field="country.name" display="menu" placeholder="Search by country"></p-columnFilter>
85
+ </div>
86
+ </th>
87
+ <th style="min-width: 14rem">
88
+ <div class="flex justify-between items-center">
89
+ Agent
90
+ <p-columnFilter field="representative" matchMode="in" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false">
91
+ <ng-template #header>
92
+ <div class="px-3 pt-3 pb-0">
93
+ <span class="font-bold">Agent Picker</span>
94
+ </div>
95
+ </ng-template>
96
+ <ng-template #filter let-value let-filter="filterCallback">
97
+ <p-multiselect [ngModel]="value" [options]="representatives" placeholder="Any" (onChange)="filter($event.value)" optionLabel="name" styleClass="w-full">
98
+ <ng-template let-option #item>
99
+ <div class="flex items-center gap-2 w-44">
100
+ <img [alt]="option.label" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ option.image }}" width="32" />
101
+ <span>{{ option.name }}</span>
102
+ </div>
103
+ </ng-template>
104
+ </p-multiselect>
105
+ </ng-template>
106
+ </p-columnFilter>
107
+ </div>
108
+ </th>
109
+ <th style="min-width: 10rem">
110
+ <div class="flex justify-between items-center">
111
+ Date
112
+ <p-columnFilter type="date" field="date" display="menu" placeholder="mm/dd/yyyy"></p-columnFilter>
113
+ </div>
114
+ </th>
115
+ <th style="min-width: 10rem">
116
+ <div class="flex justify-between items-center">
117
+ Balance
118
+ <p-columnFilter type="numeric" field="balance" display="menu" currency="USD"></p-columnFilter>
119
+ </div>
120
+ </th>
121
+ <th style="min-width: 12rem">
122
+ <div class="flex justify-between items-center">
123
+ Status
124
+ <p-columnFilter field="status" matchMode="equals" display="menu">
125
+ <ng-template #filter let-value let-filter="filterCallback">
126
+ <p-select [ngModel]="value" [options]="statuses" (onChange)="filter($event.value)" placeholder="Any" [style]="{ 'min-width': '12rem' }">
127
+ <ng-template let-option #item>
128
+ <span [class]="'customer-badge status-' + option.value">{{ option.label }}</span>
129
+ </ng-template>
130
+ </p-select>
131
+ </ng-template>
132
+ </p-columnFilter>
133
+ </div>
134
+ </th>
135
+ <th style="min-width: 12rem">
136
+ <div class="flex justify-between items-center">
137
+ Activity
138
+ <p-columnFilter field="activity" matchMode="between" display="menu" [showMatchModes]="false" [showOperator]="false" [showAddButton]="false">
139
+ <ng-template #filter let-filter="filterCallback">
140
+ <p-slider [ngModel]="activityValues" [range]="true" (onSlideEnd)="filter($event.values)" styleClass="m-3" [style]="{ 'min-width': '12rem' }"></p-slider>
141
+ <div class="flex items-center justify-between px-2">
142
+ <span>{{ activityValues[0] }}</span>
143
+ <span>{{ activityValues[1] }}</span>
144
+ </div>
145
+ </ng-template>
146
+ </p-columnFilter>
147
+ </div>
148
+ </th>
149
+ <th style="min-width: 8rem">
150
+ <div class="flex justify-between items-center">
151
+ Verified
152
+ <p-columnFilter type="boolean" field="verified" display="menu"></p-columnFilter>
153
+ </div>
154
+ </th>
155
+ </tr>
156
+ </ng-template>
157
+ <ng-template #body let-customer>
158
+ <tr>
159
+ <td>
160
+ {{ customer.name }}
161
+ </td>
162
+ <td>
163
+ <div class="flex items-center gap-2">
164
+ <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" width="30" />
165
+ <span>{{ customer.country.name }}</span>
166
+ </div>
167
+ </td>
168
+ <td>
169
+ <div class="flex items-center gap-2">
170
+ <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
171
+ <span class="image-text">{{ customer.representative.name }}</span>
172
+ </div>
173
+ </td>
174
+ <td>
175
+ {{ customer.date | date: 'MM/dd/yyyy' }}
176
+ </td>
177
+ <td>
178
+ {{ customer.balance | currency: 'USD' : 'symbol' }}
179
+ </td>
180
+ <td>
181
+ <p-tag [value]="customer.status.toLowerCase()" [severity]="getSeverity(customer.status.toLowerCase())" styleClass="dark:bg-surface-900!" />
182
+ </td>
183
+ <td>
184
+ <p-progressbar [value]="customer.activity" [showValue]="false" [style]="{ height: '0.5rem' }" />
185
+ </td>
186
+ <td class="text-center">
187
+ <p-tag [value]="customer.status.toLowerCase()" [severity]="getSeverity(customer.status.toLowerCase())" styleClass="dark:bg-surface-900!" />
188
+ </td>
189
+ </tr>
190
+ </ng-template>
191
+ <ng-template #emptymessage>
192
+ <tr>
193
+ <td colspan="8">No customers found.</td>
194
+ </tr>
195
+ </ng-template>
196
+ <ng-template #loadingbody>
197
+ <tr>
198
+ <td colspan="8">Loading customers data. Please wait.</td>
199
+ </tr>
200
+ </ng-template>
201
+ </p-table>
202
+ </div>
203
+
204
+ <div class="card">
205
+ <div class="font-semibold text-xl mb-4">Frozen Columns</div>
206
+ <p-togglebutton [(ngModel)]="balanceFrozen" [onIcon]="'pi pi-lock'" offIcon="pi pi-lock-open" [onLabel]="'Balance'" offLabel="Balance" />
207
+
208
+ <p-table [value]="customers2" [scrollable]="true" scrollHeight="400px" styleClass="mt-4">
209
+ <ng-template #header>
210
+ <tr>
211
+ <th style="min-width:200px" pFrozenColumn class="font-bold">Name</th>
212
+ <th style="min-width:100px">Id</th>
213
+ <th style="min-width:200px">Country</th>
214
+ <th style="min-width:200px">Date</th>
215
+ <th style="min-width:200px">Company</th>
216
+ <th style="min-width:200px">Status</th>
217
+ <th style="min-width:200px">Activity</th>
218
+ <th style="min-width:200px">Representative</th>
219
+ <th style="min-width:200px" alignFrozen="right" pFrozenColumn [frozen]="balanceFrozen" [ngClass]="{ 'font-bold': balanceFrozen }">Balance</th>
220
+ </tr>
221
+ </ng-template>
222
+ <ng-template #body let-customer>
223
+ <tr>
224
+ <td pFrozenColumn class="font-bold">{{ customer.name }}</td>
225
+ <td style="min-width:100px">{{ customer.id }}</td>
226
+ <td>{{ customer.country.name }}</td>
227
+ <td>{{ customer.date }}</td>
228
+ <td>{{ customer.company }}</td>
229
+ <td>{{ customer.status }}</td>
230
+ <td>{{ customer.activity }}</td>
231
+ <td>{{ customer.representative.name }}</td>
232
+ <td alignFrozen="right" pFrozenColumn [frozen]="balanceFrozen" [ngClass]="{ 'font-bold': balanceFrozen }">
233
+ {{ formatCurrency(customer.balance) }}
234
+ </td>
235
+ </tr>
236
+ </ng-template>
237
+ </p-table>
238
+ </div>
239
+
240
+ <div class="card">
241
+ <div class="font-semibold text-xl mb-4">Row Expansion</div>
242
+ <p-table [value]="products" dataKey="id" [tableStyle]="{ 'min-width': '60rem' }" [expandedRowKeys]="expandedRows">
243
+ <ng-template #caption>
244
+ <button pButton icon="pi pi-fw {{ isExpanded ? 'pi-minus' : 'pi-plus' }}" label="{{ isExpanded ? 'Collapse All' : 'Expand All' }}" (click)="expandAll()"></button>
245
+ <div class="flex table-header"></div>
246
+ </ng-template>
247
+ <ng-template #header>
248
+ <tr>
249
+ <th style="width: 5rem"></th>
250
+ <th pSortableColumn="name">Name <p-sortIcon field="name" /></th>
251
+ <th>Image</th>
252
+ <th pSortableColumn="price">Price <p-sortIcon field="price" /></th>
253
+ <th pSortableColumn="category">Category <p-sortIcon field="category" /></th>
254
+ <th pSortableColumn="rating">Reviews <p-sortIcon field="rating" /></th>
255
+ <th pSortableColumn="inventoryStatus">Status <p-sortIcon field="inventoryStatus" /></th>
256
+ </tr>
257
+ </ng-template>
258
+ <ng-template #body let-product let-expanded="expanded">
259
+ <tr>
260
+ <td>
261
+ <p-button type="button" pRipple [pRowToggler]="product" [text]="true" [rounded]="true" [plain]="true" [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'" />
262
+ </td>
263
+ <td>{{ product.name }}</td>
264
+ <td>
265
+ <img [src]="'https://primefaces.org/cdn/primeng/images/demo/product/' + product.image" [alt]="product.name" width="50" class="shadow-lg" />
266
+ </td>
267
+ <td>{{ product.price | currency: 'USD' }}</td>
268
+ <td>{{ product.category }}</td>
269
+ <td>
270
+ <p-rating [ngModel]="product.rating" [readonly]="true" />
271
+ </td>
272
+ <td>
273
+ <p-tag [value]="product.inventoryStatus" [severity]="getSeverity(product.inventoryStatus)" />
274
+ </td>
275
+ </tr>
276
+ </ng-template>
277
+ <ng-template #expandedrow let-product>
278
+ <tr>
279
+ <td colspan="7">
280
+ <div class="p-4">
281
+ <h5>Orders for {{ product.name }}</h5>
282
+ <p-table [value]="product.orders" dataKey="id">
283
+ <ng-template #header>
284
+ <tr>
285
+ <th pSortableColumn="id">Id <p-sortIcon field="price" /></th>
286
+ <th pSortableColumn="customer">
287
+ Customer
288
+ <p-sortIcon field="customer" />
289
+ </th>
290
+ <th pSortableColumn="date">Date <p-sortIcon field="date" /></th>
291
+ <th pSortableColumn="amount">
292
+ Amount
293
+ <p-sortIcon field="amount" />
294
+ </th>
295
+ <th pSortableColumn="status">
296
+ Status
297
+ <p-sortIcon field="status" />
298
+ </th>
299
+ <th style="width: 4rem"></th>
300
+ </tr>
301
+ </ng-template>
302
+ <ng-template #body let-order>
303
+ <tr>
304
+ <td>{{ order.id }}</td>
305
+ <td>{{ order.customer }}</td>
306
+ <td>{{ order.date }}</td>
307
+ <td>
308
+ {{ order.amount | currency: 'USD' }}
309
+ </td>
310
+ <td>
311
+ <p-tag [value]="order.status" [severity]="getSeverity(order.status)" />
312
+ </td>
313
+ <td>
314
+ <p-button type="button" icon="pi pi-search" />
315
+ </td>
316
+ </tr>
317
+ </ng-template>
318
+ <ng-template #emptymessage>
319
+ <tr>
320
+ <td colspan="6">There are no order for this product yet.</td>
321
+ </tr>
322
+ </ng-template>
323
+ </p-table>
324
+ </div>
325
+ </td>
326
+ </tr>
327
+ </ng-template>
328
+ </p-table>
329
+ </div>
330
+
331
+ <div class="card">
332
+ <div class="font-semibold text-xl mb-4">Grouping</div>
333
+ <p-table [value]="customers3" sortField="representative.name" sortMode="single" [scrollable]="true" scrollHeight="400px" rowGroupMode="subheader" groupRowsBy="representative.name" [tableStyle]="{ 'min-width': '60rem' }">
334
+ <ng-template #header>
335
+ <tr>
336
+ <th>Name</th>
337
+ <th>Country</th>
338
+ <th>Company</th>
339
+ <th>Status</th>
340
+ <th>Date</th>
341
+ </tr>
342
+ </ng-template>
343
+ <ng-template #groupheader let-customer>
344
+ <tr pRowGroupHeader>
345
+ <td colspan="5">
346
+ <div class="flex items-center gap-2">
347
+ <img [alt]="customer.representative.name" src="https://primefaces.org/cdn/primeng/images/demo/avatar/{{ customer.representative.image }}" width="32" style="vertical-align: middle" />
348
+ <span class="font-bold">{{ customer.representative.name }}</span>
349
+ </div>
350
+ </td>
351
+ </tr>
352
+ </ng-template>
353
+ <ng-template #groupfooter let-customer>
354
+ <tr>
355
+ <td colspan="5" class="text-right font-bold pr-12">Total Customers: {{ calculateCustomerTotal(customer.representative.name) }}</td>
356
+ </tr>
357
+ </ng-template>
358
+ <ng-template #body let-customer let-rowIndex="rowIndex">
359
+ <tr>
360
+ <td>
361
+ {{ customer.name }}
362
+ </td>
363
+ <td>
364
+ <div class="flex items-center gap-2">
365
+ <img src="https://primefaces.org/cdn/primeng/images/demo/flag/flag_placeholder.png" [class]="'flag flag-' + customer.country.code" style="width: 20px" />
366
+ <span>{{ customer.country.name }}</span>
367
+ </div>
368
+ </td>
369
+ <td>
370
+ {{ customer.company }}
371
+ </td>
372
+ <td>
373
+ <p-tag [value]="customer.status" [severity]="getSeverity(customer.status)" />
374
+ </td>
375
+ <td>
376
+ {{ customer.date }}
377
+ </td>
378
+ </tr>
379
+ </ng-template>
380
+ </p-table>
381
+ </div>`,
382
+ styles: `
383
+ .p-datatable-frozen-tbody {
384
+ font-weight: bold;
385
+ }
386
+
387
+ .p-datatable-scrollable .p-frozen-column {
388
+ font-weight: bold;
389
+ }
390
+ `,
391
+ providers: [ConfirmationService, MessageService, CustomerService, ProductService]
392
+ })
393
+ export class TableDemo implements OnInit {
394
+ customers1: Customer[] = [];
395
+
396
+ customers2: Customer[] = [];
397
+
398
+ customers3: Customer[] = [];
399
+
400
+ selectedCustomers1: Customer[] = [];
401
+
402
+ selectedCustomer: Customer = {};
403
+
404
+ representatives: Representative[] = [];
405
+
406
+ statuses: any[] = [];
407
+
408
+ products: Product[] = [];
409
+
410
+ rowGroupMetadata: any;
411
+
412
+ expandedRows: expandedRows = {};
413
+
414
+ activityValues: number[] = [0, 100];
415
+
416
+ isExpanded: boolean = false;
417
+
418
+ balanceFrozen: boolean = false;
419
+
420
+ loading: boolean = true;
421
+
422
+ @ViewChild('filter') filter!: ElementRef;
423
+
424
+ constructor(
425
+ private customerService: CustomerService,
426
+ private productService: ProductService
427
+ ) {}
428
+
429
+ ngOnInit() {
430
+ this.customerService.getCustomersLarge().then((customers) => {
431
+ this.customers1 = customers;
432
+ this.loading = false;
433
+
434
+ // @ts-ignore
435
+ this.customers1.forEach((customer) => (customer.date = new Date(customer.date)));
436
+ });
437
+ this.customerService.getCustomersMedium().then((customers) => (this.customers2 = customers));
438
+ this.customerService.getCustomersLarge().then((customers) => (this.customers3 = customers));
439
+ this.productService.getProductsWithOrdersSmall().then((data) => (this.products = data));
440
+
441
+ this.representatives = [
442
+ { name: 'Amy Elsner', image: 'amyelsner.png' },
443
+ { name: 'Anna Fali', image: 'annafali.png' },
444
+ { name: 'Asiya Javayant', image: 'asiyajavayant.png' },
445
+ { name: 'Bernardo Dominic', image: 'bernardodominic.png' },
446
+ { name: 'Elwin Sharvill', image: 'elwinsharvill.png' },
447
+ { name: 'Ioni Bowcher', image: 'ionibowcher.png' },
448
+ { name: 'Ivan Magalhaes', image: 'ivanmagalhaes.png' },
449
+ { name: 'Onyama Limba', image: 'onyamalimba.png' },
450
+ { name: 'Stephen Shaw', image: 'stephenshaw.png' },
451
+ { name: 'XuXue Feng', image: 'xuxuefeng.png' }
452
+ ];
453
+
454
+ this.statuses = [
455
+ { label: 'Unqualified', value: 'unqualified' },
456
+ { label: 'Qualified', value: 'qualified' },
457
+ { label: 'New', value: 'new' },
458
+ { label: 'Negotiation', value: 'negotiation' },
459
+ { label: 'Renewal', value: 'renewal' },
460
+ { label: 'Proposal', value: 'proposal' }
461
+ ];
462
+ }
463
+
464
+ onSort() {
465
+ this.updateRowGroupMetaData();
466
+ }
467
+
468
+ updateRowGroupMetaData() {
469
+ this.rowGroupMetadata = {};
470
+
471
+ if (this.customers3) {
472
+ for (let i = 0; i < this.customers3.length; i++) {
473
+ const rowData = this.customers3[i];
474
+ const representativeName = rowData?.representative?.name || '';
475
+
476
+ if (i === 0) {
477
+ this.rowGroupMetadata[representativeName] = { index: 0, size: 1 };
478
+ } else {
479
+ const previousRowData = this.customers3[i - 1];
480
+ const previousRowGroup = previousRowData?.representative?.name;
481
+ if (representativeName === previousRowGroup) {
482
+ this.rowGroupMetadata[representativeName].size++;
483
+ } else {
484
+ this.rowGroupMetadata[representativeName] = { index: i, size: 1 };
485
+ }
486
+ }
487
+ }
488
+ }
489
+ }
490
+
491
+ expandAll() {
492
+ if(ObjectUtils.isEmpty(this.expandedRows)) {
493
+ this.expandedRows = this.products.reduce(
494
+ (acc, p) => {
495
+ if (p.id) {
496
+ acc[p.id] = true;
497
+ }
498
+ return acc;
499
+ },
500
+ {} as { [key: string]: boolean }
501
+ );
502
+ this.isExpanded = true;
503
+ } else {
504
+ this.collapseAll()
505
+ }
506
+
507
+ }
508
+
509
+ collapseAll() {
510
+ this.expandedRows = {};
511
+ this.isExpanded = false;
512
+ }
513
+
514
+ formatCurrency(value: number) {
515
+ return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
516
+ }
517
+
518
+ onGlobalFilter(table: Table, event: Event) {
519
+ table.filterGlobal((event.target as HTMLInputElement).value, 'contains');
520
+ }
521
+
522
+ clear(table: Table) {
523
+ table.clear();
524
+ this.filter.nativeElement.value = '';
525
+ }
526
+
527
+ getSeverity(status: string) {
528
+ switch (status) {
529
+ case 'qualified':
530
+ case 'instock':
531
+ case 'INSTOCK':
532
+ case 'DELIVERED':
533
+ case 'delivered':
534
+ return 'success';
535
+
536
+ case 'negotiation':
537
+ case 'lowstock':
538
+ case 'LOWSTOCK':
539
+ case 'PENDING':
540
+ case 'pending':
541
+ return 'warn';
542
+
543
+ case 'unqualified':
544
+ case 'outofstock':
545
+ case 'OUTOFSTOCK':
546
+ case 'CANCELLED':
547
+ case 'cancelled':
548
+ return 'danger';
549
+
550
+ default:
551
+ return 'info';
552
+ }
553
+ }
554
+
555
+ calculateCustomerTotal(name: string) {
556
+ let total = 0;
557
+
558
+ if (this.customers2) {
559
+ for (let customer of this.customers2) {
560
+ if (customer.representative?.name === name) {
561
+ total++;
562
+ }
563
+ }
564
+ }
565
+
566
+ return total;
567
+ }
568
+ }