Skip to content

Commit c7c97d7

Browse files
committed
user-payment-statistic, payment-statistic
1 parent 7478f47 commit c7c97d7

File tree

15 files changed

+1245
-190
lines changed

15 files changed

+1245
-190
lines changed

src/app/core/models/statistics.model.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,13 @@ export type SummaryStatisticsAdmin = {
2424
totalSubmissions: number;
2525
totalPassedSubmissions: number;
2626
};
27+
export type PaymentStatisticsAdmin = {
28+
day: string;
29+
totalAmount: number;
30+
};
31+
export type PaymentStatisticsUser = {
32+
day: string;
33+
depositAmount: number;
34+
purchaseAmount: number;
35+
walletBalance: number;
36+
};

src/app/core/router-manager/vetical-menu-dynamic/statistics-vetical-menu.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,19 @@ export function sidebarStatisticsRouter(roles: string[]): SidebarItem[] {
1818
icon: 'fa-solid fa-chart-pie',
1919
isVisible: !roles.includes(auth_lv2[0]),
2020
},
21+
{
22+
id: 'chart-payment-statistics',
23+
path: '/codecampus-statistics/admin-payment-statistics',
24+
label: 'Thống kê doanh thu',
25+
icon: 'fa-solid fa-file-invoice-dollar',
26+
isVisible: !roles.includes(auth_lv2[0]),
27+
},
28+
{
29+
id: 'chart-user-payment-statistics',
30+
path: '/codecampus-statistics/user-payment-statistics',
31+
label: 'Thống kê nạp & mua',
32+
icon: 'fa-solid fa-credit-card',
33+
isVisible: !roles.includes(auth_lv2[0]),
34+
},
2135
];
2236
}

src/app/core/services/api-service/statistics.service.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { ApiMethod } from '../config-service/api.methods';
33
import { ApiResponse, IPaginationResponse } from '../../models/api-response';
44
import {
55
ExerciseStatisticsResponse,
6+
PaymentStatisticsAdmin,
7+
PaymentStatisticsUser,
68
SummaryStatisticsAdmin,
79
} from '../../models/statistics.model';
810
import { API_CONFIG } from '../config-service/api.enpoints';
@@ -24,4 +26,14 @@ export class StatisticsService {
2426
API_CONFIG.ENDPOINTS.GET.GET_SUMMARY_STATISTICS_ADMIN
2527
);
2628
}
29+
getAdminPaymentStats(year: number, month: number) {
30+
return this.api.get<ApiResponse<PaymentStatisticsAdmin[]>>(
31+
API_CONFIG.ENDPOINTS.GET.GET_PAYMENT_STATISTICS_ADMIN(year, month)
32+
);
33+
}
34+
getUserPaymentStats(year: number, month: number) {
35+
return this.api.get<ApiResponse<PaymentStatisticsUser[]>>(
36+
API_CONFIG.ENDPOINTS.GET.GET_USER_PAYMENT_STATISTICS_ADMIN(year, month)
37+
);
38+
}
2739
}

src/app/core/services/config-service/api.enpoints.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ export const API_CONFIG = {
217217

218218
GET_EXERCISE_STATISTICS_ADMIN: (page: number, size: number) =>
219219
`/submission/stats/admin/exercises?page=${page}&size=${size}`,
220+
GET_PAYMENT_STATISTICS_ADMIN: (year: number, month: number) =>
221+
`/payment/payment-statistics/daily-deposit?year=${year}&month=${month}`,
222+
GET_USER_PAYMENT_STATISTICS_ADMIN: (year: number, month: number) =>
223+
`/payment/payment-statistics/daily-statistic?year=${year}&month=${month}`,
220224
GET_SUMMARY_STATISTICS_ADMIN: '/submission/stats/admin/summary',
221225
},
222226
POST: {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<div class="payment-container">
2+
<header class="payment-header">
3+
<h1>Thống kê tiền nạp</h1>
4+
<p>Các chỉ số chính về tiền nạp trên hệ thống.</p>
5+
</header>
6+
7+
<div class="filter-bar">
8+
<label for="month">Trong tháng:</label>
9+
<select id="month" [(ngModel)]="selectedMonth" (change)="onFilterChange()">
10+
<option *ngFor="let month of months" [value]="month">{{ month }}</option>
11+
</select>
12+
<label for="year">của năm:</label>
13+
<select id="year" [(ngModel)]="selectedYear" (change)="onFilterChange()">
14+
<option *ngFor="let year of years" [value]="year">{{ year }}</option>
15+
</select>
16+
</div>
17+
18+
@if (isLoading) {
19+
<div class="loading-overlay">
20+
<div class="spinner"></div>
21+
<p>Đang tải dữ liệu...</p>
22+
</div>
23+
} @else if (error && !isLoading) {
24+
<div class="error-message">
25+
<p>{{ error }}</p>
26+
<button class="btn-retry" (click)="loadPayment()">Thử lại</button>
27+
</div>
28+
} @else if (paymentData && !isLoading && !error) {
29+
<div class="payment-content">
30+
<div class="payment-cards-grid">
31+
<div class="payment-card">
32+
<div class="card-label">Tổng số</div>
33+
<div class="card-value">{{ totalAmount | number }}</div>
34+
</div>
35+
<div class="payment-card">
36+
<div class="card-label">Trung bình</div>
37+
<div class="card-value">{{ averageAmount | number }}</div>
38+
</div>
39+
<div class="payment-card">
40+
<div class="card-label">Cao nhất</div>
41+
<div class="card-value">{{ maxAmount | number }}</div>
42+
</div>
43+
<div class="payment-card">
44+
<div class="card-label">Thấp nhất</div>
45+
<div class="card-value">{{ minAmount | number }}</div>
46+
</div>
47+
</div>
48+
49+
<div class="charts-grid">
50+
<div class="chart-wrapper">
51+
<fx-line-chart
52+
[categories]="chartCategories"
53+
[series]="chartSeries"
54+
[chartTitle]="
55+
'Thống kê tiền nạp theo ngày trong tháng ' +
56+
selectedMonth +
57+
'/' +
58+
selectedYear
59+
"
60+
></fx-line-chart>
61+
</div>
62+
</div>
63+
<!-- Bảng chi tiết -->
64+
<div class="table-wrapper">
65+
<app-table
66+
[headers]="tableHeaders"
67+
[data]="tableData"
68+
[needNo]="true"
69+
[amountDataPerPage]="paymentData.length"
70+
>
71+
</app-table>
72+
</div>
73+
</div>
74+
}
75+
</div>
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
:host {
2+
display: block;
3+
}
4+
5+
.payment-container {
6+
padding: 24px;
7+
background-color: var(--background-color);
8+
color: var(--text-color);
9+
height: calc(100vh - 180px);
10+
overflow: auto;
11+
transition: background-color 0.3s, color 0.3s;
12+
}
13+
14+
.payment-header {
15+
margin-bottom: 12px;
16+
h1 {
17+
color: var(--title-text);
18+
font-size: 1.2rem;
19+
font-weight: 700;
20+
margin-top: 0;
21+
}
22+
p {
23+
color: var(--text-muted-color);
24+
font-size: 1rem;
25+
}
26+
}
27+
// ---- Thanh lọc ----
28+
.filter-bar {
29+
display: flex;
30+
align-items: center;
31+
gap: 16px;
32+
margin-bottom: 24px;
33+
// background-color: var(--surface-color);
34+
// border: 1px solid var(--border-color);
35+
// border-radius: 8px;
36+
// box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
37+
38+
label {
39+
font-size: 0.95rem;
40+
font-weight: 500;
41+
color: var(--text-muted-color);
42+
margin-right: 4px;
43+
}
44+
45+
select {
46+
padding: 8px 12px;
47+
border-radius: 6px;
48+
border: 1px solid var(--border-color);
49+
background-color: var(--background-color);
50+
color: var(--text-color);
51+
font-size: 0.95rem;
52+
transition: border-color 0.2s, box-shadow 0.2s;
53+
54+
&:focus {
55+
outline: none;
56+
border-color: var(--primary-color);
57+
box-shadow: 0 0 0 2px rgba(var(--primary-rgb), 0.2);
58+
}
59+
}
60+
}
61+
62+
// ---- Grid cho các thẻ ----
63+
.payment-cards-grid {
64+
display: grid;
65+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
66+
gap: 24px;
67+
}
68+
69+
.payment-card {
70+
background-color: var(--surface-color);
71+
padding: 24px;
72+
border-radius: 12px;
73+
border: 1px solid var(--border-color);
74+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
75+
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
76+
77+
&:hover {
78+
transform: translateY(-5px);
79+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
80+
}
81+
82+
.card-label {
83+
font-size: 0.9rem;
84+
color: var(--text-muted-color);
85+
margin-bottom: 8px;
86+
}
87+
88+
.card-value {
89+
font-size: 2.5rem;
90+
font-weight: 700;
91+
color: var(--primary-color);
92+
line-height: 1.1;
93+
}
94+
95+
.card-sub-value {
96+
font-size: 0.85rem;
97+
color: var(--accent-color);
98+
margin-top: 12px;
99+
}
100+
}
101+
102+
// ---- Grid cho các biểu đồ ----
103+
.charts-grid {
104+
margin-top: 48px;
105+
display: flex;
106+
flex-wrap: wrap;
107+
justify-content: center;
108+
gap: 30px;
109+
}
110+
111+
.chart-wrapper {
112+
background-color: var(--surface-color);
113+
padding: 20px;
114+
border-radius: 12px;
115+
border: 1px solid var(--border-color);
116+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
117+
// Đảm bảo chart component được căn giữa
118+
display: flex;
119+
justify-content: center;
120+
align-items: center;
121+
}
122+
.table-wrapper {
123+
margin: 32px auto; // thay vì margin-top
124+
width: 80%;
125+
border-radius: 12px;
126+
padding: 20px;
127+
}
128+
129+
.table-wrapper table th:first-child,
130+
.table-wrapper table td:first-child {
131+
width: 60px; // nhỏ hơn các cột khác
132+
text-align: center; // căn giữa số
133+
}
134+
135+
// ---- Các style cho Loading, Error (có thể dùng chung) ----
136+
.loading-overlay,
137+
.error-message {
138+
display: flex;
139+
flex-direction: column;
140+
justify-content: center;
141+
align-items: center;
142+
padding: 60px 20px;
143+
border-radius: 8px;
144+
background-color: var(--background-color-secondary);
145+
}
146+
147+
.spinner {
148+
width: 50px;
149+
height: 50px;
150+
border: 5px solid var(--primary-color-lighter);
151+
border-top-color: var(--primary-color);
152+
border-radius: 50%;
153+
animation: spin 1s linear infinite;
154+
}
155+
156+
@keyframes spin {
157+
to {
158+
transform: rotate(360deg);
159+
}
160+
}
161+
162+
.error-message p {
163+
color: var(--error-color);
164+
}
165+
166+
.btn-retry {
167+
margin-top: 16px;
168+
padding: 10px 20px;
169+
border: 1px solid var(--button-color);
170+
color: var(--button-color);
171+
background-color: transparent;
172+
border-radius: 5px;
173+
cursor: pointer;
174+
&:hover {
175+
background-color: var(--button-color-hover);
176+
color: var(--reverse-color-text);
177+
}
178+
}
179+
// Responsive
180+
@media (max-width: 600px) {
181+
.filter-bar {
182+
flex-direction: column;
183+
align-items: flex-start;
184+
gap: 12px;
185+
186+
label {
187+
margin-bottom: 4px;
188+
}
189+
190+
select {
191+
width: 100%;
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)