feat: Add Sonner toast notifications to all CRUD operations

- Install sonner package and create Toaster component
- Add toast notifications to all admin dashboard operations:
  * AdminPlans: create, update, delete, reorder, toggle visibility
  * AdminPaymentMethods: create, update, delete, reorder, toggle active
  * AdminUsers: suspend, unsuspend, grant pro access
  * AdminPayments: verify, reject
  * AdminSettings: save settings
- Add toast notifications to all member dashboard operations:
  * Wallets: create, update, delete
  * Transactions: create, update, delete
  * Profile: update name, avatar, phone, password, delete account
  * OTP: enable/disable email, WhatsApp, authenticator
- Replace all alert() calls with toast.success/error/warning
- Add proper success/error messages in Bahasa Indonesia
- Implement smart plan deletion (permanent if no subscriptions, soft delete if has subscriptions)
- Fix admin redirect after login (admin goes to /admin, users to /)
- Exclude admin accounts from subscription distribution chart
- Show inactive plans with visual indicators
- Add real revenue data to admin dashboard charts
- Use formatLargeNumber for consistent number formatting
This commit is contained in:
dwindown
2025-10-12 08:43:50 +07:00
parent 258d326909
commit c0df4a7c2a
37 changed files with 2744 additions and 628 deletions

View File

@@ -107,6 +107,43 @@ let AdminPaymentsService = class AdminPaymentsService {
where: { status: 'pending' },
});
}
async getMonthlyRevenue() {
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
const payments = await this.prisma.payment.findMany({
where: {
status: 'paid',
paidAt: {
gte: sixMonthsAgo,
},
},
select: {
amount: true,
paidAt: true,
},
});
const monthlyData = {};
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
payments.forEach((payment) => {
if (payment.paidAt) {
const date = new Date(payment.paidAt);
const monthKey = `${months[date.getMonth()]} ${date.getFullYear()}`;
if (!monthlyData[monthKey]) {
monthlyData[monthKey] = { revenue: 0, count: 0 };
}
monthlyData[monthKey].revenue += Number(payment.amount);
monthlyData[monthKey].count += 1;
}
});
const result = Object.entries(monthlyData)
.map(([month, data]) => ({
month: month.split(' ')[0],
revenue: data.revenue,
users: data.count,
}))
.slice(-6);
return result;
}
};
exports.AdminPaymentsService = AdminPaymentsService;
exports.AdminPaymentsService = AdminPaymentsService = __decorate([