@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.
- package/.editorconfig +16 -0
- package/.gitmodules +3 -0
- package/.postcssrc.json +5 -0
- package/.prettierignore +14 -0
- package/.prettierrc.json +29 -0
- package/LICENSE.md +21 -0
- package/README.md +59 -0
- package/angular.json +98 -0
- package/eslint.config.js +89 -0
- package/package.json +59 -0
- package/public/demo/images/flag/flag_placeholder.png +0 -0
- package/public/demo/images/footer-image.gif +0 -0
- package/public/demo/images/galleria/galleria1.jpg +0 -0
- package/public/demo/images/galleria/galleria10.jpg +0 -0
- package/public/demo/images/galleria/galleria10s.jpg +0 -0
- package/public/demo/images/galleria/galleria11.jpg +0 -0
- package/public/demo/images/galleria/galleria11s.jpg +0 -0
- package/public/demo/images/galleria/galleria12.jpg +0 -0
- package/public/demo/images/galleria/galleria12s.jpg +0 -0
- package/public/demo/images/galleria/galleria13.jpg +0 -0
- package/public/demo/images/galleria/galleria13s.jpg +0 -0
- package/public/demo/images/galleria/galleria14.jpg +0 -0
- package/public/demo/images/galleria/galleria14s.jpg +0 -0
- package/public/demo/images/galleria/galleria15.jpg +0 -0
- package/public/demo/images/galleria/galleria15s.jpg +0 -0
- package/public/demo/images/galleria/galleria1s.jpg +0 -0
- package/public/demo/images/galleria/galleria2.jpg +0 -0
- package/public/demo/images/galleria/galleria2s.jpg +0 -0
- package/public/demo/images/galleria/galleria3.jpg +0 -0
- package/public/demo/images/galleria/galleria3s.jpg +0 -0
- package/public/demo/images/galleria/galleria4.jpg +0 -0
- package/public/demo/images/galleria/galleria4s.jpg +0 -0
- package/public/demo/images/galleria/galleria5.jpg +0 -0
- package/public/demo/images/galleria/galleria5s.jpg +0 -0
- package/public/demo/images/galleria/galleria6.jpg +0 -0
- package/public/demo/images/galleria/galleria6s.jpg +0 -0
- package/public/demo/images/galleria/galleria7.jpg +0 -0
- package/public/demo/images/galleria/galleria7s.jpg +0 -0
- package/public/demo/images/galleria/galleria8.jpg +0 -0
- package/public/demo/images/galleria/galleria8s.jpg +0 -0
- package/public/demo/images/galleria/galleria9.jpg +0 -0
- package/public/demo/images/galleria/galleria9s.jpg +0 -0
- package/public/demo/images/product/bamboo-watch.jpg +0 -0
- package/public/demo/images/product/black-watch.jpg +0 -0
- package/public/demo/images/product/blue-band.jpg +0 -0
- package/public/demo/images/product/blue-t-shirt.jpg +0 -0
- package/public/demo/images/product/bracelet.jpg +0 -0
- package/public/demo/images/product/brown-purse.jpg +0 -0
- package/public/demo/images/product/chakra-bracelet.jpg +0 -0
- package/public/demo/images/product/galaxy-earrings.jpg +0 -0
- package/public/demo/images/product/game-controller.jpg +0 -0
- package/public/demo/images/product/gaming-set.jpg +0 -0
- package/public/demo/images/product/gold-phone-case.jpg +0 -0
- package/public/demo/images/product/green-earbuds.jpg +0 -0
- package/public/demo/images/product/green-t-shirt.jpg +0 -0
- package/public/demo/images/product/grey-t-shirt.jpg +0 -0
- package/public/demo/images/product/headphones.jpg +0 -0
- package/public/demo/images/product/light-green-t-shirt.jpg +0 -0
- package/public/demo/images/product/lime-band.jpg +0 -0
- package/public/demo/images/product/mini-speakers.jpg +0 -0
- package/public/demo/images/product/painted-phone-case.jpg +0 -0
- package/public/demo/images/product/pink-band.jpg +0 -0
- package/public/demo/images/product/pink-purse.jpg +0 -0
- package/public/demo/images/product/product-placeholder.svg +10 -0
- package/public/demo/images/product/purple-band.jpg +0 -0
- package/public/demo/images/product/purple-gemstone-necklace.jpg +0 -0
- package/public/demo/images/product/purple-t-shirt.jpg +0 -0
- package/public/demo/images/product/shoes.jpg +0 -0
- package/public/demo/images/product/sneakers.jpg +0 -0
- package/public/demo/images/product/teal-t-shirt.jpg +0 -0
- package/public/demo/images/product/yellow-earbuds.jpg +0 -0
- package/public/demo/images/product/yoga-mat.jpg +0 -0
- package/public/demo/images/product/yoga-set.jpg +0 -0
- package/src/app/layout/component/configurator/app.configurator.html +48 -0
- package/src/app/layout/component/configurator/app.configurator.ts +396 -0
- package/src/app/layout/component/floatingconfigurator/app.floatingconfigurator.ts +31 -0
- package/src/app/layout/component/footer/app.footer.scss +52 -0
- package/src/app/layout/component/footer/app.footer.ts +26 -0
- package/src/app/layout/component/layout/app.layout.ts +50 -0
- package/src/app/layout/component/menu/app.menu.html +7 -0
- package/src/app/layout/component/menu/app.menu.scss +13 -0
- package/src/app/layout/component/menu/app.menu.ts +90 -0
- package/src/app/layout/component/menuitem/app.menuitem.html +56 -0
- package/src/app/layout/component/menuitem/app.menuitem.scss +218 -0
- package/src/app/layout/component/menuitem/app.menuitem.ts +126 -0
- package/src/app/layout/component/sidebar/app.sidebar.html +3 -0
- package/src/app/layout/component/sidebar/app.sidebar.scss +0 -0
- package/src/app/layout/component/sidebar/app.sidebar.ts +106 -0
- package/src/app/layout/component/topbar/app.topbar.html +190 -0
- package/src/app/layout/component/topbar/app.topbar.scss +8 -0
- package/src/app/layout/component/topbar/app.topbar.ts +68 -0
- package/src/app/layout/service/layout.service.ts +117 -0
- package/src/app/pages/auth/access.ts +32 -0
- package/src/app/pages/auth/auth.routes.ts +10 -0
- package/src/app/pages/auth/error.ts +32 -0
- package/src/app/pages/auth/login.ts +71 -0
- package/src/app/pages/crud/crud.ts +387 -0
- package/src/app/pages/dashboard/dashboard.css +778 -0
- package/src/app/pages/dashboard/dashboard.html +191 -0
- package/src/app/pages/dashboard/dashboard.ts +348 -0
- package/src/app/pages/documentation/documentation.ts +73 -0
- package/src/app/pages/empty/empty.ts +11 -0
- package/src/app/pages/landing/components/featureswidget.ts +139 -0
- package/src/app/pages/landing/components/footerwidget.ts +73 -0
- package/src/app/pages/landing/components/herowidget.ts +25 -0
- package/src/app/pages/landing/components/highlightswidget.ts +46 -0
- package/src/app/pages/landing/components/pricingwidget.ts +119 -0
- package/src/app/pages/landing/components/topbarwidget.component.ts +68 -0
- package/src/app/pages/landing/landing.ts +31 -0
- package/src/app/pages/notfound/notfound.ts +68 -0
- package/src/app/pages/pages.routes.ts +17 -0
- package/src/app/pages/profile/profile.html +57 -0
- package/src/app/pages/profile/profile.scss +145 -0
- package/src/app/pages/profile/profile.ts +19 -0
- package/src/app/pages/service/country.service.ts +255 -0
- package/src/app/pages/service/customer.service.ts +9057 -0
- package/src/app/pages/service/icon.service.ts +23 -0
- package/src/app/pages/service/node.service.ts +816 -0
- package/src/app/pages/service/photo.service.ts +103 -0
- package/src/app/pages/service/product.service.ts +1322 -0
- package/src/app/pages/tickets/tickets-create/tickets-create.html +140 -0
- package/src/app/pages/tickets/tickets-create/tickets-create.scss +617 -0
- package/src/app/pages/tickets/tickets-create/tickets-create.ts +104 -0
- package/src/app/pages/tickets/tickets-list/ticket-list.html +150 -0
- package/src/app/pages/tickets/tickets-list/ticket-list.scss +392 -0
- package/src/app/pages/tickets/tickets-list/ticket-list.ts +178 -0
- package/src/app/pages/uikit/buttondemo.ts +254 -0
- package/src/app/pages/uikit/chartdemo.ts +290 -0
- package/src/app/pages/uikit/filedemo.ts +52 -0
- package/src/app/pages/uikit/formlayoutdemo.ts +129 -0
- package/src/app/pages/uikit/inputdemo.ts +339 -0
- package/src/app/pages/uikit/listdemo.ts +217 -0
- package/src/app/pages/uikit/mediademo.ts +1021 -0
- package/src/app/pages/uikit/menudemo.ts +540 -0
- package/src/app/pages/uikit/messagesdemo.ts +101 -0
- package/src/app/pages/uikit/miscdemo.ts +192 -0
- package/src/app/pages/uikit/overlaydemo.ts +235 -0
- package/src/app/pages/uikit/panelsdemo.ts +235 -0
- package/src/app/pages/uikit/tabledemo.ts +568 -0
- package/src/app/pages/uikit/timelinedemo.ts +141 -0
- package/src/app/pages/uikit/treedemo.ts +75 -0
- package/src/app/pages/uikit/uikit.routes.ts +35 -0
- package/src/app.component.ts +22 -0
- package/src/app.config.ts +23 -0
- package/src/app.routes.ts +23 -0
- package/src/assets/demo/code.scss +17 -0
- package/src/assets/demo/demo.scss +2 -0
- package/src/assets/demo/flags/flags.css +984 -0
- package/src/assets/layout/_core.scss +24 -0
- package/src/assets/layout/_footer.scss +8 -0
- package/src/assets/layout/_main.scss +21 -0
- package/src/assets/layout/_menu.scss +159 -0
- package/src/assets/layout/_mixins.scss +15 -0
- package/src/assets/layout/_preloading.scss +47 -0
- package/src/assets/layout/_responsive.scss +111 -0
- package/src/assets/layout/_topbar.scss +201 -0
- package/src/assets/layout/_typography.scss +68 -0
- package/src/assets/layout/_utils.scss +25 -0
- package/src/assets/layout/layout.scss +13 -0
- package/src/assets/layout/variables/_common.scss +21 -0
- package/src/assets/layout/variables/_dark.scss +5 -0
- package/src/assets/layout/variables/_light.scss +5 -0
- package/src/assets/styles.scss +4 -0
- package/src/assets/tailwind.css +32 -0
- package/src/index.html +15 -0
- package/src/main.ts +5 -0
- package/tsconfig.app.json +15 -0
- package/tsconfig.json +33 -0
- package/tsconfig.spec.json +15 -0
- package/vercel.json +9 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<div class="dashboard-wrap">
|
|
2
|
+
<!-- ══════════════ HEADER ══════════════ -->
|
|
3
|
+
<div class="dash-header">
|
|
4
|
+
<div class="dash-header-left">
|
|
5
|
+
<div class="dash-title">
|
|
6
|
+
<span class="dash-icon-badge">📊</span>
|
|
7
|
+
Dashboard Tổng Quan
|
|
8
|
+
</div>
|
|
9
|
+
<div class="dash-subtitle">
|
|
10
|
+
<span class="live-dot"></span>
|
|
11
|
+
Cập nhật thời gian thực · MIAT Kakotora IT Department
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<button class="btn-create" (click)="openNewTicket()"><i class="pi pi-plus"></i> Tạo ticket mới</button>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<!-- ══════════════ STAT CARDS ══════════════ -->
|
|
18
|
+
<div class="stat-grid">
|
|
19
|
+
<div class="stat-card stat-blue" (mouseenter)="hoverCard('total')" (mouseleave)="leaveCard()">
|
|
20
|
+
<div class="stat-glow glow-blue"></div>
|
|
21
|
+
<div class="stat-icon-wrap icon-blue">🎫</div>
|
|
22
|
+
<div class="stat-label">Tổng Ticket</div>
|
|
23
|
+
<div class="stat-value">{{ totalTickets }}</div>
|
|
24
|
+
<div class="stat-trend trend-up">↑ +3 hôm nay</div>
|
|
25
|
+
<div class="sparkline">
|
|
26
|
+
<div class="spark-bar" *ngFor="let v of sparkTotal" [style.height.%]="v" [style.background]="'var(--dash-blue)'"></div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="stat-card stat-orange" (mouseenter)="hoverCard('open')" (mouseleave)="leaveCard()">
|
|
31
|
+
<div class="stat-glow glow-orange"></div>
|
|
32
|
+
<div class="stat-icon-wrap icon-orange">🔴</div>
|
|
33
|
+
<div class="stat-label">Đang Mở</div>
|
|
34
|
+
<div class="stat-value">{{ openTickets }}</div>
|
|
35
|
+
<div class="stat-trend trend-warn">⚠ Cần xử lý ngay</div>
|
|
36
|
+
<div class="sparkline">
|
|
37
|
+
<div class="spark-bar" *ngFor="let v of sparkOpen" [style.height.%]="v" [style.background]="'var(--dash-orange)'"></div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="stat-card stat-violet" (mouseenter)="hoverCard('progress')" (mouseleave)="leaveCard()">
|
|
42
|
+
<div class="stat-glow glow-violet"></div>
|
|
43
|
+
<div class="stat-icon-wrap icon-violet">⚡</div>
|
|
44
|
+
<div class="stat-label">Đang Xử Lý</div>
|
|
45
|
+
<div class="stat-value">{{ progressTickets }}</div>
|
|
46
|
+
<div class="stat-trend trend-info">🔵 Đang theo dõi</div>
|
|
47
|
+
<div class="sparkline">
|
|
48
|
+
<div class="spark-bar" *ngFor="let v of sparkProgress" [style.height.%]="v" [style.background]="'var(--dash-violet)'"></div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div class="stat-card stat-emerald" (mouseenter)="hoverCard('done')" (mouseleave)="leaveCard()">
|
|
53
|
+
<div class="stat-glow glow-emerald"></div>
|
|
54
|
+
<div class="stat-icon-wrap icon-emerald">✅</div>
|
|
55
|
+
<div class="stat-label">Hoàn Thành</div>
|
|
56
|
+
<div class="stat-value">{{ doneTickets }}</div>
|
|
57
|
+
<div class="stat-trend trend-up">↑ Tốt hơn tuần trước</div>
|
|
58
|
+
<div class="sparkline">
|
|
59
|
+
<div class="spark-bar" *ngFor="let v of sparkDone" [style.height.%]="v" [style.background]="'var(--dash-emerald)'"></div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<!-- ══════════════ CHARTS ROW ══════════════ -->
|
|
65
|
+
<div class="charts-row">
|
|
66
|
+
<!-- Line Chart -->
|
|
67
|
+
<div class="panel panel-accent-blue chart-panel">
|
|
68
|
+
<div class="panel-head">
|
|
69
|
+
<span class="panel-title">📈 Ticket 7 ngày gần nhất</span>
|
|
70
|
+
<div class="legend-row">
|
|
71
|
+
<span class="legend-dot" style="background: var(--dash-blue)"></span><span class="legend-label">Tạo mới</span> <span class="legend-dot" style="background: var(--dash-emerald)"></span><span class="legend-label">Hoàn thành</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="panel-body chart-body">
|
|
75
|
+
<p-chart type="line" [data]="lineChartData" [options]="lineChartOptions"></p-chart>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- Doughnut Chart -->
|
|
80
|
+
<div class="panel panel-accent-violet chart-panel">
|
|
81
|
+
<div class="panel-head">
|
|
82
|
+
<span class="panel-title">🍩 Phân bố trạng thái</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="panel-body chart-body donut-body">
|
|
85
|
+
<p-chart type="doughnut" [data]="doughnutData" [options]="doughnutOptions"></p-chart>
|
|
86
|
+
<div class="donut-legend">
|
|
87
|
+
<div class="donut-legend-item" *ngFor="let item of doughnutLegend">
|
|
88
|
+
<span class="legend-dot" [style.background]="item.color"></span>
|
|
89
|
+
<span class="legend-label">{{ item.label }}</span>
|
|
90
|
+
<span class="legend-val">{{ item.value }}</span>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- ══════════════ BAR CHART (Full Width) ══════════════ -->
|
|
98
|
+
<div class="panel panel-accent-emerald mb-4">
|
|
99
|
+
<div class="panel-head">
|
|
100
|
+
<span class="panel-title">📊 Thống kê theo loại yêu cầu</span>
|
|
101
|
+
<span class="panel-sub">Tổng số ticket từng danh mục</span>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="panel-body chart-body">
|
|
104
|
+
<p-chart type="bar" [data]="barChartData" [options]="barChartOptions"></p-chart>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<!-- ══════════════ TWO COL PANELS ══════════════ -->
|
|
109
|
+
<div class="two-col">
|
|
110
|
+
<!-- Recent Tickets -->
|
|
111
|
+
<div class="panel panel-accent-blue">
|
|
112
|
+
<div class="panel-head">
|
|
113
|
+
<span class="panel-title">🕐 Ticket Gần Đây</span>
|
|
114
|
+
<button class="btn-ghost" (click)="viewAll()">Xem tất cả →</button>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="panel-body p-0">
|
|
117
|
+
<div class="recent-item" *ngFor="let ticket of recentTickets; let last = last" [class.last]="last">
|
|
118
|
+
<div class="ri-avatar" [style.background]="ticket.avatarBg" [style.color]="ticket.avatarText">{{ getInitials(ticket.requester) }}</div>
|
|
119
|
+
<div class="ri-info">
|
|
120
|
+
<div class="ri-title">{{ ticket.title }}</div>
|
|
121
|
+
<div class="ri-meta">{{ ticket.id }} · {{ ticket.requester }}</div>
|
|
122
|
+
</div>
|
|
123
|
+
<span class="st-badge" [ngClass]="getStatusClass(ticket.status)">
|
|
124
|
+
<span class="st-dot" [ngClass]="getDotClass(ticket.status)"></span>
|
|
125
|
+
{{ getStatusText(ticket.status) }}
|
|
126
|
+
</span>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<!-- Type breakdown -->
|
|
132
|
+
<div class="panel panel-accent-violet">
|
|
133
|
+
<div class="panel-head">
|
|
134
|
+
<span class="panel-title">📦 Theo Loại Yêu Cầu</span>
|
|
135
|
+
</div>
|
|
136
|
+
<div class="panel-body">
|
|
137
|
+
<div class="type-row" *ngFor="let item of typeBreakdown">
|
|
138
|
+
<span class="type-name">{{ item.type }}</span>
|
|
139
|
+
<div class="type-bar-bg">
|
|
140
|
+
<div class="type-bar" [style.width.%]="item.percent" [style.background]="item.color"></div>
|
|
141
|
+
</div>
|
|
142
|
+
<span class="type-count">{{ item.count }}</span>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<!-- ══════════════ PRIORITY TABLE ══════════════ -->
|
|
149
|
+
<div class="panel panel-accent-orange mb-4">
|
|
150
|
+
<div class="panel-head">
|
|
151
|
+
<span class="panel-title">🚨 Ticket Ưu Tiên Cao</span>
|
|
152
|
+
<span class="panel-sub">Cần xử lý trong hôm nay</span>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="panel-body p-0">
|
|
155
|
+
<table class="dash-table">
|
|
156
|
+
<thead>
|
|
157
|
+
<tr>
|
|
158
|
+
<th>ID</th>
|
|
159
|
+
<th>Tiêu đề</th>
|
|
160
|
+
<th>Người yêu cầu</th>
|
|
161
|
+
<th>Phòng ban</th>
|
|
162
|
+
<th>Loại</th>
|
|
163
|
+
<th>Trạng thái</th>
|
|
164
|
+
<th>Ngày tạo</th>
|
|
165
|
+
</tr>
|
|
166
|
+
</thead>
|
|
167
|
+
<tbody>
|
|
168
|
+
<tr *ngFor="let t of highPriorityTickets" class="table-row">
|
|
169
|
+
<td><span class="ticket-id">{{ t.id }}</span></td>
|
|
170
|
+
<td class="ticket-title-cell">{{ t.title }}</td>
|
|
171
|
+
<td>
|
|
172
|
+
<div class="table-avatar-wrap">
|
|
173
|
+
<div class="table-avatar" [style.background]="t.avatarBg" [style.color]="t.avatarText">{{ getInitials(t.requester) }}</div>
|
|
174
|
+
<span>{{ t.requester }}</span>
|
|
175
|
+
</div>
|
|
176
|
+
</td>
|
|
177
|
+
<td><span class="dept-badge">{{ t.dept }}</span></td>
|
|
178
|
+
<td><span class="type-chip" [style.color]="getTypeColor(t.type)">{{ t.type }}</span></td>
|
|
179
|
+
<td>
|
|
180
|
+
<span class="st-badge" [ngClass]="getStatusClass(t.status)">
|
|
181
|
+
<span class="st-dot" [ngClass]="getDotClass(t.status)"></span>
|
|
182
|
+
{{ getStatusText(t.status) }}
|
|
183
|
+
</span>
|
|
184
|
+
</td>
|
|
185
|
+
<td class="date-cell">{{ t.created }}</td>
|
|
186
|
+
</tr>
|
|
187
|
+
</tbody>
|
|
188
|
+
</table>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { ChartModule } from 'primeng/chart';
|
|
4
|
+
|
|
5
|
+
interface Ticket {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
type: string;
|
|
9
|
+
priority: string;
|
|
10
|
+
requester: string;
|
|
11
|
+
dept: string;
|
|
12
|
+
status: string;
|
|
13
|
+
device: string;
|
|
14
|
+
desc: string;
|
|
15
|
+
created: string;
|
|
16
|
+
avatarBg?: string;
|
|
17
|
+
avatarText?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-dashboard',
|
|
22
|
+
standalone: true,
|
|
23
|
+
imports: [CommonModule, ChartModule],
|
|
24
|
+
templateUrl: './dashboard.html',
|
|
25
|
+
styleUrls: ['./dashboard.css']
|
|
26
|
+
})
|
|
27
|
+
export class DashboardComponent implements OnInit, OnDestroy {
|
|
28
|
+
// ─── Sparklines ───────────────────────────────────
|
|
29
|
+
sparkTotal = [40, 60, 45, 70, 55, 80, 100];
|
|
30
|
+
sparkOpen = [80, 65, 70, 50, 60, 75, 90];
|
|
31
|
+
sparkProgress = [30, 50, 60, 45, 70, 55, 65];
|
|
32
|
+
sparkDone = [20, 40, 55, 70, 80, 90, 100];
|
|
33
|
+
|
|
34
|
+
// ─── Raw data ─────────────────────────────────────
|
|
35
|
+
data: Ticket[] = [
|
|
36
|
+
{ id: 'TK-001', title: 'Màn hình không hiển thị', type: 'Hardware', priority: 'high', requester: 'Nguyễn Văn An', dept: 'Kế toán', status: 'open', device: 'Dell P2419H', desc: 'Màn hình mất tín hiệu', created: '2025-07-01' },
|
|
37
|
+
{ id: 'TK-002', title: 'Không kết nối được VPN', type: 'Mạng', priority: 'high', requester: 'Trần Thị Bình', dept: 'Kinh doanh', status: 'progress', device: 'HP EliteBook', desc: 'VPN lỗi authentication', created: '2025-07-02' },
|
|
38
|
+
{ id: 'TK-003', title: 'Cài đặt phần mềm MISA', type: 'Software', priority: 'medium', requester: 'Lê Minh Cường', dept: 'Kế toán', status: 'done', device: 'PC-KT-03', desc: 'Cài MISA SME 2024', created: '2025-07-02' },
|
|
39
|
+
{ id: 'TK-004', title: 'Quên mật khẩu Windows', type: 'Tài khoản', priority: 'medium', requester: 'Phạm Thu Dung', dept: 'Nhân sự', status: 'done', device: 'Lenovo ThinkPad', desc: 'Không đăng nhập được', created: '2025-07-03' },
|
|
40
|
+
{ id: 'TK-005', title: 'Máy in offline', type: 'Máy in', priority: 'low', requester: 'Hoàng Văn Em', dept: 'Vận hành', status: 'open', device: 'HP LaserJet M404', desc: 'Không nhận lệnh in', created: '2025-07-03' },
|
|
41
|
+
{ id: 'TK-006', title: 'Email gửi đi bị spam', type: 'Email', priority: 'medium', requester: 'Vũ Thị Phương', dept: 'Marketing', status: 'progress', device: '', desc: 'Email bị spam filter chặn', created: '2025-07-04' },
|
|
42
|
+
{ id: 'TK-007', title: 'CPU quá nóng 95°C', type: 'Hardware', priority: 'high', requester: 'Đỗ Quang Hưng', dept: 'IT', status: 'open', device: 'PC-IT-07', desc: 'Máy tự tắt sau 30 phút', created: '2025-07-05' },
|
|
43
|
+
{ id: 'TK-008', title: 'Mất file sau khi format', type: 'Software', priority: 'high', requester: 'Ngô Thanh Lan', dept: 'Ban giám đốc', status: 'progress', device: 'Dell XPS 15', desc: 'Cần khôi phục dữ liệu', created: '2025-07-05' },
|
|
44
|
+
{ id: 'TK-009', title: 'WiFi yếu tầng 3', type: 'Mạng', priority: 'low', requester: 'Bùi Văn Minh', dept: 'Vận hành', status: 'done', device: '', desc: 'Tín hiệu không ổn định', created: '2025-07-06' },
|
|
45
|
+
{ id: 'TK-010', title: 'CRM không mở được', type: 'Software', priority: 'medium', requester: 'Lý Ngọc Nam', dept: 'Kinh doanh', status: 'open', device: 'PC-KD-10', desc: 'Lỗi runtime module', created: '2025-07-07' },
|
|
46
|
+
{ id: 'TK-011', title: 'Tạo tài khoản nhân viên mới', type: 'Tài khoản', priority: 'low', requester: 'Trần Văn Oai', dept: 'Nhân sự', status: 'done', device: '', desc: '3 nhân viên onboard mới', created: '2025-07-07' },
|
|
47
|
+
{ id: 'TK-012', title: 'BSOD khi chạy Excel', type: 'Hardware', priority: 'high', requester: 'Đinh Thị Phúc', dept: 'Kế toán', status: 'progress', device: 'PC-KT-12', desc: 'Màn hình xanh liên tục', created: '2025-07-08' },
|
|
48
|
+
{ id: 'TK-013', title: 'Không vào được portal nội bộ', type: 'Mạng', priority: 'high', requester: 'Cao Minh Quân', dept: 'Kinh doanh', status: 'open', device: 'Asus Laptop', desc: 'Sau khi đổi mật khẩu WiFi', created: '2025-07-09' },
|
|
49
|
+
{ id: 'TK-014', title: 'Cài Canva Pro', type: 'Software', priority: 'low', requester: 'Phan Thị Thảo', dept: 'Marketing', status: 'done', device: 'MacBook Air M2', desc: 'License cho team marketing', created: '2025-07-09' },
|
|
50
|
+
{ id: 'TK-015', title: 'Setup backup server tự động', type: 'Khác', priority: 'medium', requester: 'Trịnh Văn Ước', dept: 'IT', status: 'progress', device: 'Dell PowerEdge', desc: 'Backup hàng tuần tối thứ 6', created: '2025-07-10' }
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
tickets: Ticket[] = [];
|
|
54
|
+
|
|
55
|
+
// ─── Stats ────────────────────────────────────────
|
|
56
|
+
get totalTickets() {
|
|
57
|
+
return this.tickets.length;
|
|
58
|
+
}
|
|
59
|
+
get openTickets() {
|
|
60
|
+
return this.tickets.filter((t) => t.status === 'open').length;
|
|
61
|
+
}
|
|
62
|
+
get progressTickets() {
|
|
63
|
+
return this.tickets.filter((t) => t.status === 'progress').length;
|
|
64
|
+
}
|
|
65
|
+
get doneTickets() {
|
|
66
|
+
return this.tickets.filter((t) => t.status === 'done').length;
|
|
67
|
+
}
|
|
68
|
+
get recentTickets() {
|
|
69
|
+
return this.tickets.slice(0, 6);
|
|
70
|
+
}
|
|
71
|
+
get highPriorityTickets() {
|
|
72
|
+
return this.tickets.filter((t) => t.priority === 'high');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
get typeBreakdown() {
|
|
76
|
+
const map = new Map<string, number>();
|
|
77
|
+
for (const t of this.tickets) map.set(t.type, (map.get(t.type) ?? 0) + 1);
|
|
78
|
+
const arr = Array.from(map.entries())
|
|
79
|
+
.map(([type, count]) => ({ type, count, color: this.getTypeColor(type) }))
|
|
80
|
+
.sort((a, b) => b.count - a.count);
|
|
81
|
+
const max = Math.max(...arr.map((x) => x.count));
|
|
82
|
+
return arr.map((x) => ({ ...x, percent: max ? (x.count / max) * 100 : 0 }));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── Charts ───────────────────────────────────────
|
|
86
|
+
lineChartData: any;
|
|
87
|
+
lineChartOptions: any;
|
|
88
|
+
doughnutData: any;
|
|
89
|
+
doughnutOptions: any;
|
|
90
|
+
barChartData: any;
|
|
91
|
+
barChartOptions: any;
|
|
92
|
+
|
|
93
|
+
doughnutLegend = [
|
|
94
|
+
{ label: 'Đang mở', color: '#f97316', value: 0 },
|
|
95
|
+
{ label: 'Đang xử lý', color: '#8b5cf6', value: 0 },
|
|
96
|
+
{ label: 'Hoàn thành', color: '#10b981', value: 0 }
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
isDark = false;
|
|
100
|
+
|
|
101
|
+
// ─── MutationObserver để watch theme changes ──────
|
|
102
|
+
private themeObserver!: MutationObserver;
|
|
103
|
+
|
|
104
|
+
ngOnInit(): void {
|
|
105
|
+
this.tickets = this.data.map((t) => {
|
|
106
|
+
const [bg, color] = this.avatarColor(t.requester);
|
|
107
|
+
return { ...t, avatarBg: bg, avatarText: color };
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this.doughnutLegend[0].value = this.openTickets;
|
|
111
|
+
this.doughnutLegend[1].value = this.progressTickets;
|
|
112
|
+
this.doughnutLegend[2].value = this.doneTickets;
|
|
113
|
+
|
|
114
|
+
this.syncTheme();
|
|
115
|
+
this.buildCharts();
|
|
116
|
+
|
|
117
|
+
// Lắng nghe khi PrimeNG toggle class p-dark trên <html>
|
|
118
|
+
this.themeObserver = new MutationObserver(() => {
|
|
119
|
+
const wasDark = this.isDark;
|
|
120
|
+
this.syncTheme();
|
|
121
|
+
if (wasDark !== this.isDark) {
|
|
122
|
+
this.buildCharts();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
this.themeObserver.observe(document.documentElement, {
|
|
127
|
+
attributes: true,
|
|
128
|
+
attributeFilter: ['class', 'data-theme']
|
|
129
|
+
});
|
|
130
|
+
// Cũng observe <body> phòng khi PrimeNG gắn vào body
|
|
131
|
+
this.themeObserver.observe(document.body, {
|
|
132
|
+
attributes: true,
|
|
133
|
+
attributeFilter: ['class']
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ngOnDestroy(): void {
|
|
138
|
+
this.themeObserver?.disconnect();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Đọc trạng thái dark mode hiện tại từ DOM */
|
|
142
|
+
private syncTheme(): void {
|
|
143
|
+
this.isDark = document.documentElement.classList.contains('p-dark') || document.body.classList.contains('p-dark') || document.documentElement.getAttribute('data-theme') === 'dark';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private buildCharts(): void {
|
|
147
|
+
const textColor = this.isDark ? '#94a3b8' : '#64748b';
|
|
148
|
+
const gridColor = this.isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)';
|
|
149
|
+
const tooltipBg = this.isDark ? '#1e293b' : '#ffffff';
|
|
150
|
+
const tooltipBorder = this.isDark ? '#334155' : '#e2e8f0';
|
|
151
|
+
|
|
152
|
+
// ── Line chart ──────────────────────────────────
|
|
153
|
+
this.lineChartData = {
|
|
154
|
+
labels: ['T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'CN'],
|
|
155
|
+
datasets: [
|
|
156
|
+
{
|
|
157
|
+
label: 'Ticket tạo mới',
|
|
158
|
+
data: [3, 5, 4, 7, 2, 6, 4],
|
|
159
|
+
borderColor: '#3b82f6',
|
|
160
|
+
backgroundColor: 'rgba(59,130,246,0.12)',
|
|
161
|
+
borderWidth: 2.5,
|
|
162
|
+
tension: 0.45,
|
|
163
|
+
pointBackgroundColor: '#3b82f6',
|
|
164
|
+
pointRadius: 4,
|
|
165
|
+
pointHoverRadius: 7,
|
|
166
|
+
fill: true
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
label: 'Hoàn thành',
|
|
170
|
+
data: [2, 3, 4, 5, 2, 4, 5],
|
|
171
|
+
borderColor: '#10b981',
|
|
172
|
+
backgroundColor: 'rgba(16,185,129,0.10)',
|
|
173
|
+
borderWidth: 2.5,
|
|
174
|
+
tension: 0.45,
|
|
175
|
+
pointBackgroundColor: '#10b981',
|
|
176
|
+
pointRadius: 4,
|
|
177
|
+
pointHoverRadius: 7,
|
|
178
|
+
fill: true
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
this.lineChartOptions = {
|
|
184
|
+
responsive: true,
|
|
185
|
+
maintainAspectRatio: false,
|
|
186
|
+
interaction: { mode: 'index', intersect: false },
|
|
187
|
+
plugins: {
|
|
188
|
+
legend: { display: false },
|
|
189
|
+
tooltip: {
|
|
190
|
+
backgroundColor: tooltipBg,
|
|
191
|
+
borderColor: tooltipBorder,
|
|
192
|
+
borderWidth: 1,
|
|
193
|
+
titleColor: textColor,
|
|
194
|
+
bodyColor: textColor,
|
|
195
|
+
padding: 12,
|
|
196
|
+
cornerRadius: 10
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
scales: {
|
|
200
|
+
x: {
|
|
201
|
+
ticks: { color: textColor, font: { size: 12 } },
|
|
202
|
+
grid: { color: gridColor },
|
|
203
|
+
border: { dash: [4, 4] }
|
|
204
|
+
},
|
|
205
|
+
y: {
|
|
206
|
+
ticks: { color: textColor, font: { size: 12 }, stepSize: 1 },
|
|
207
|
+
grid: { color: gridColor },
|
|
208
|
+
border: { dash: [4, 4] },
|
|
209
|
+
min: 0
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// ── Doughnut chart ──────────────────────────────
|
|
215
|
+
this.doughnutData = {
|
|
216
|
+
labels: ['Đang mở', 'Đang xử lý', 'Hoàn thành'],
|
|
217
|
+
datasets: [
|
|
218
|
+
{
|
|
219
|
+
data: [this.openTickets, this.progressTickets, this.doneTickets],
|
|
220
|
+
backgroundColor: ['rgba(249,115,22,0.85)', 'rgba(139,92,246,0.85)', 'rgba(16,185,129,0.85)'],
|
|
221
|
+
borderColor: ['#f97316', '#8b5cf6', '#10b981'],
|
|
222
|
+
borderWidth: 2,
|
|
223
|
+
hoverOffset: 10
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
this.doughnutOptions = {
|
|
229
|
+
responsive: true,
|
|
230
|
+
maintainAspectRatio: false,
|
|
231
|
+
cutout: '72%',
|
|
232
|
+
plugins: {
|
|
233
|
+
legend: { display: false },
|
|
234
|
+
tooltip: {
|
|
235
|
+
backgroundColor: tooltipBg,
|
|
236
|
+
borderColor: tooltipBorder,
|
|
237
|
+
borderWidth: 1,
|
|
238
|
+
titleColor: textColor,
|
|
239
|
+
bodyColor: textColor,
|
|
240
|
+
padding: 12,
|
|
241
|
+
cornerRadius: 10
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// ── Bar chart ───────────────────────────────────
|
|
247
|
+
const typeData = this.typeBreakdown;
|
|
248
|
+
this.barChartData = {
|
|
249
|
+
labels: typeData.map((t) => t.type),
|
|
250
|
+
datasets: [
|
|
251
|
+
{
|
|
252
|
+
label: 'Số ticket',
|
|
253
|
+
data: typeData.map((t) => t.count),
|
|
254
|
+
backgroundColor: typeData.map((t) => t.color + 'bb'),
|
|
255
|
+
borderColor: typeData.map((t) => t.color),
|
|
256
|
+
borderWidth: 2,
|
|
257
|
+
borderRadius: 8,
|
|
258
|
+
borderSkipped: false
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
this.barChartOptions = {
|
|
264
|
+
responsive: true,
|
|
265
|
+
maintainAspectRatio: false,
|
|
266
|
+
plugins: {
|
|
267
|
+
legend: { display: false },
|
|
268
|
+
tooltip: {
|
|
269
|
+
backgroundColor: tooltipBg,
|
|
270
|
+
borderColor: tooltipBorder,
|
|
271
|
+
borderWidth: 1,
|
|
272
|
+
titleColor: textColor,
|
|
273
|
+
bodyColor: textColor,
|
|
274
|
+
padding: 12,
|
|
275
|
+
cornerRadius: 10
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
scales: {
|
|
279
|
+
x: {
|
|
280
|
+
ticks: { color: textColor, font: { size: 12 } },
|
|
281
|
+
grid: { display: false }
|
|
282
|
+
},
|
|
283
|
+
y: {
|
|
284
|
+
ticks: { color: textColor, stepSize: 1 },
|
|
285
|
+
grid: { color: gridColor },
|
|
286
|
+
border: { dash: [4, 4] },
|
|
287
|
+
min: 0
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ─── Helpers ──────────────────────────────────────
|
|
294
|
+
getInitials(name: string): string {
|
|
295
|
+
return name
|
|
296
|
+
?.split(' ')
|
|
297
|
+
.slice(-2)
|
|
298
|
+
.map((w) => w[0])
|
|
299
|
+
.join('')
|
|
300
|
+
.toUpperCase();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
avatarColor(name: string = ''): [string, string] {
|
|
304
|
+
const palette: [string, string][] = [
|
|
305
|
+
['rgba(59,130,246,.22)', '#93c5fd'],
|
|
306
|
+
['rgba(139,92,246,.22)', '#c4b5fd'],
|
|
307
|
+
['rgba(236,72,153,.22)', '#f9a8d4'],
|
|
308
|
+
['rgba(6,182,212,.22)', '#67e8f9'],
|
|
309
|
+
['rgba(16,185,129,.22)', '#6ee7b7'],
|
|
310
|
+
['rgba(245,158,11,.22)', '#fcd34d']
|
|
311
|
+
];
|
|
312
|
+
return palette[(name?.charCodeAt(0) ?? 0) % palette.length];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getTypeColor(type: string): string {
|
|
316
|
+
const map: Record<string, string> = {
|
|
317
|
+
Software: '#a78bfa',
|
|
318
|
+
Hardware: '#60a5fa',
|
|
319
|
+
Mạng: '#22d3ee',
|
|
320
|
+
'Tài khoản': '#f472b6',
|
|
321
|
+
'Máy in': '#fbbf24',
|
|
322
|
+
Email: '#34d399',
|
|
323
|
+
Khác: '#94a3b8'
|
|
324
|
+
};
|
|
325
|
+
return map[type] ?? '#94a3b8';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
getStatusClass(status: string): string {
|
|
329
|
+
return { open: 'st-open', progress: 'st-progress', done: 'st-done' }[status] ?? 'st-done';
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
getDotClass(status: string): string {
|
|
333
|
+
return { open: 'dot-red', progress: 'dot-amber', done: 'dot-green' }[status] ?? 'dot-green';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
getStatusText(status: string): string {
|
|
337
|
+
return { open: 'Mở', progress: 'Đang xử lý', done: 'Hoàn thành' }[status] ?? status;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
openNewTicket() {
|
|
341
|
+
console.log('Open new ticket modal');
|
|
342
|
+
}
|
|
343
|
+
viewAll() {
|
|
344
|
+
console.log('View all tickets');
|
|
345
|
+
}
|
|
346
|
+
hoverCard(_: string) {}
|
|
347
|
+
leaveCard() {}
|
|
348
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'app-documentation',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
template: `
|
|
9
|
+
<div class="card">
|
|
10
|
+
<div class="font-semibold text-2xl mb-4">Documentation</div>
|
|
11
|
+
<div class="font-semibold text-xl mb-4">Get Started</div>
|
|
12
|
+
<p class="text-lg mb-4">Sakai is an application template for Angular and is distributed as a CLI project. Current versions are Angular v21 with PrimeNG v21. In case CLI is not installed already, use the command below to set it up.</p>
|
|
13
|
+
<pre class="app-code">
|
|
14
|
+
<code>npm install -g @angular/cli</code></pre>
|
|
15
|
+
<p class="text-lg mb-4">
|
|
16
|
+
Once CLI is ready in your system, extract the contents of the zip file distribution, cd to the directory, install the libraries from npm and then execute "ng serve" to run the application in your local environment.
|
|
17
|
+
</p>
|
|
18
|
+
<pre class="app-code">
|
|
19
|
+
<code>git clone https://github.com/primefaces/sakai-ng
|
|
20
|
+
npm install
|
|
21
|
+
ng serve</code></pre>
|
|
22
|
+
|
|
23
|
+
<p class="text-lg mb-4">The application should run at <i class="bg-highlight px-2 py-1 rounded-border not-italic text-base">http://localhost:4200/</i> to view the application in your local environment.</p>
|
|
24
|
+
|
|
25
|
+
<div class="font-semibold text-xl mb-4">Structure</div>
|
|
26
|
+
<p class="text-lg mb-4">Templates consists of a couple folders, demos and layout have been separated so that you can easily identify what is necessary for your application.</p>
|
|
27
|
+
<ul class="leading-normal list-disc pl-8 text-lg mb-4">
|
|
28
|
+
<li><span class="text-primary font-medium">src/app/layout</span>: Main layout files, needs to be present.</li>
|
|
29
|
+
<li><span class="text-primary font-medium">src/app/pages</span>: Demo content like Dashboard.</li>
|
|
30
|
+
<li><span class="text-primary font-medium">src/assets/demo</span>: Assets used in demos</li>
|
|
31
|
+
<li><span class="text-primary font-medium">src/assets/layout</span>: SCSS files of the main layout</li>
|
|
32
|
+
</ul>
|
|
33
|
+
|
|
34
|
+
<div class="font-semibold text-xl mb-4">Menu</div>
|
|
35
|
+
<p class="text-lg mb-4">
|
|
36
|
+
Main menu is defined at <span class="bg-highlight px-2 py-1 rounded-border not-italic text-base">src/app/layout/component/app.menu.ts</span> file. Update the
|
|
37
|
+
<i class="bg-highlight px-2 py-1 rounded-border not-italic text-base">model</i> property to define your own menu items.
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<div class="font-semibold text-xl mb-4">Layout Service</div>
|
|
41
|
+
<p class="text-lg mb-4">
|
|
42
|
+
<span class="bg-highlight px-2 py-1 rounded-border not-italic text-base">src/app/layout/service/layout.service.ts</span> is a service that manages layout state changes, including dark mode, PrimeNG theme, menu modes, and states.
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<div class="font-semibold text-xl mb-4">Tailwind CSS</div>
|
|
46
|
+
<p class="text-lg mb-4">The demo pages are developed with Tailwind CSS however the core application shell uses custom CSS.</p>
|
|
47
|
+
|
|
48
|
+
<div class="font-semibold text-xl mb-4">Variables</div>
|
|
49
|
+
<p class="text-lg mb-4">
|
|
50
|
+
CSS variables used in the template are derived from the applied PrimeNG theme. Customize them through the CSS variables in <span class="bg-highlight px-2 py-1 rounded-border not-italic text-base">src/assets/layout/variables</span>.
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
`,
|
|
54
|
+
styles: `
|
|
55
|
+
@media screen and (max-width: 991px) {
|
|
56
|
+
.video-container {
|
|
57
|
+
position: relative;
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 0;
|
|
60
|
+
padding-bottom: 56.25%;
|
|
61
|
+
|
|
62
|
+
iframe {
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 0;
|
|
65
|
+
left: 0;
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: 100%;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
`
|
|
72
|
+
})
|
|
73
|
+
export class Documentation {}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'app-empty',
|
|
5
|
+
standalone: true,
|
|
6
|
+
template: ` <div class="card">
|
|
7
|
+
<div class="font-semibold text-xl mb-4">Empty Page</div>
|
|
8
|
+
<p>Use this page to start from scratch and place your custom content.</p>
|
|
9
|
+
</div>`
|
|
10
|
+
})
|
|
11
|
+
export class Empty {}
|