feat : admin feature
This commit is contained in:
103
src/app/_components/report/Activeuser_report.tsx
Normal file
103
src/app/_components/report/Activeuser_report.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
"use client";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const generateActiveUsersPDF = (reportData: any, startDate: string, endDate: string) => {
|
||||
const html2pdf = require("html2pdf.js");
|
||||
|
||||
// Build rows from user list
|
||||
const rows = reportData.active_user_list
|
||||
.map(
|
||||
(user: any) => `
|
||||
<tr style="page-break-inside: avoid;">
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">${user.customer_no}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:left;">${user.user_name}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">
|
||||
${new Date(user.created_at).toLocaleString()}
|
||||
</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">
|
||||
${new Date(user.last_login).toLocaleString()}
|
||||
</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;
|
||||
color:${user.status === "active" ? "green" : "red"};">
|
||||
${user.status.toUpperCase()}
|
||||
</td>
|
||||
</tr>`
|
||||
)
|
||||
.join("");
|
||||
|
||||
// HTML content for PDF
|
||||
const content = `
|
||||
<div style="font-family:Arial, sans-serif; font-size:12px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
|
||||
<div style="display:flex;align-items:center;gap:10px;">
|
||||
<img src="/logo.jpg" alt="Bank Logo" style="height:50px;" />
|
||||
<h2 style="margin:0;">The Kangra Central Co Operative Bank</h2>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#555;">
|
||||
Report generated: ${dayjs().format("DD/MM/YYYY HH:mm")}
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<h3 style="text-align:center;margin:10px 0;">Active Users Report</h3>
|
||||
|
||||
<p style="text-align:center; font-size:13px; margin-top:-5px;">
|
||||
<strong>Report from ${startDate} to ${endDate}</strong>
|
||||
</p>
|
||||
<p><strong>Total Users:</strong> ${reportData.total_users}</p>
|
||||
<p><strong>Active Users:</strong> ${reportData.active_users}</p>
|
||||
|
||||
<table style="width:100%;border-collapse:collapse;font-size:12px;margin-top:10px;">
|
||||
<thead style="background:#f0f0f0;">
|
||||
<tr>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Customer No</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:left;">User Name</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Created At</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Last Login</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const opt = {
|
||||
margin: [20, 10, 20, 10],
|
||||
filename: `ActiveUsersReport_${dayjs().format("DDMMYYYY_HHmm")}.pdf`,
|
||||
image: { type: "jpeg", quality: 0.98 },
|
||||
html2canvas: { scale: 2 },
|
||||
jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
|
||||
pagebreak: { mode: ["avoid-all", "css", "legacy"] },
|
||||
};
|
||||
|
||||
// ✅ Properly call .save() INSIDE the .then()
|
||||
html2pdf()
|
||||
.set(opt)
|
||||
.from(content)
|
||||
.toPdf()
|
||||
.get("pdf")
|
||||
.then((pdf: any) => {
|
||||
const totalPages = pdf.internal.getNumberOfPages();
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
|
||||
for (let i = 2; i <= totalPages; i++) {
|
||||
pdf.setPage(i);
|
||||
pdf.setFontSize(10);
|
||||
pdf.setFont("helvetica", "bold");
|
||||
|
||||
pdf.text("Active Users Report", pageWidth / 2, 18, { align: "center" });
|
||||
pdf.setFontSize(9);
|
||||
pdf.text(
|
||||
`Page ${i} of ${totalPages}`,
|
||||
pageWidth - 40,
|
||||
pdf.internal.pageSize.getHeight() - 10
|
||||
);
|
||||
}
|
||||
|
||||
// ✅ Save PDF here
|
||||
pdf.save(`ActiveUsersReport_${dayjs().format("DDMMYYYY_HHmm")}.pdf`);
|
||||
});
|
||||
};
|
||||
105
src/app/_components/report/Inactiveuser_report.tsx
Normal file
105
src/app/_components/report/Inactiveuser_report.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const generateInactiveUsersPDF = (
|
||||
reportData: any,
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
const html2pdf = require("html2pdf.js");
|
||||
|
||||
// ✅ Use correct list (change to 'active_user_list' if needed)
|
||||
const userList = reportData.inactive_user_list || reportData.active_user_list || [];
|
||||
|
||||
// Build table rows
|
||||
const rows = userList
|
||||
.map(
|
||||
(user: any) => `
|
||||
<tr style="page-break-inside: avoid;">
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">${user.customer_no}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:left;">${user.user_name}</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">
|
||||
${new Date(user.created_at).toLocaleString()}
|
||||
</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;">
|
||||
${new Date(user.last_login).toLocaleString()}
|
||||
</td>
|
||||
<td style="border:1px solid #ccc;padding:6px;text-align:center;color:${
|
||||
user.status === "active" ? "green" : "red"
|
||||
};">
|
||||
${user.status.toUpperCase()}
|
||||
</td>
|
||||
</tr>`
|
||||
)
|
||||
.join("");
|
||||
|
||||
// ✅ HTML layout
|
||||
const content = `
|
||||
<div style="font-family:Arial, sans-serif; font-size:12px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
|
||||
<div style="display:flex;align-items:center;gap:10px;">
|
||||
<img src="/logo.jpg" alt="Bank Logo" style="height:50px;" />
|
||||
<h2 style="margin:0;">The Kangra Central Co Operative Bank</h2>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#555;">
|
||||
Report generated: ${dayjs().format("DD/MM/YYYY HH:mm")}
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
<h3 style="text-align:center;margin:10px 0;">Inactive Users Report</h3>
|
||||
|
||||
<p style="text-align:center; font-size:13px; margin-top:-5px;">
|
||||
<strong>Report from ${startDate} to ${endDate}</strong>
|
||||
</p>
|
||||
|
||||
<p><strong>Total Users:</strong> ${reportData.total_users}</p>
|
||||
<p><strong>Inactive Users:</strong> ${reportData.inactive_users}</p>
|
||||
|
||||
<table style="width:100%;border-collapse:collapse;font-size:12px;margin-top:10px;">
|
||||
<thead style="background:#f0f0f0;">
|
||||
<tr>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Customer No</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:left;">User Name</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Created At</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Last Login</th>
|
||||
<th style="border:1px solid #ccc;padding:6px;text-align:center;">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>${rows}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// ✅ Options — fix margin type
|
||||
const opt = {
|
||||
margin: [20, 10, 20, 10] as [number, number, number, number],
|
||||
filename: `InactiveUsersReport_${dayjs().format("DDMMYYYY_HHmm")}.pdf`,
|
||||
image: { type: "jpeg", quality: 0.98 },
|
||||
html2canvas: { scale: 2 },
|
||||
jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
|
||||
pagebreak: { mode: ["avoid-all", "css", "legacy"] },
|
||||
};
|
||||
|
||||
// ✅ Proper html2pdf chain
|
||||
html2pdf()
|
||||
.set(opt)
|
||||
.from(content)
|
||||
.toPdf()
|
||||
.get("pdf")
|
||||
.then((pdf: any) => {
|
||||
const totalPages = pdf.internal.getNumberOfPages();
|
||||
const pageWidth = pdf.internal.pageSize.getWidth();
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pdf.setPage(i);
|
||||
pdf.setFontSize(9);
|
||||
pdf.text(
|
||||
`Page ${i} of ${totalPages}`,
|
||||
pageWidth - 40,
|
||||
pdf.internal.pageSize.getHeight() - 10
|
||||
);
|
||||
}
|
||||
})
|
||||
.save(); // ✅ this triggers the download
|
||||
};
|
||||
@@ -14,8 +14,8 @@ interface SendOtpPayload {
|
||||
}
|
||||
|
||||
function getStoredMobileNumber(): string {
|
||||
const mobileNumber = localStorage.getItem('remitter_mobile_no');
|
||||
// const mobileNumber = "7890544527";
|
||||
//const mobileNumber = localStorage.getItem('remitter_mobile_no');
|
||||
const mobileNumber = "6297421727";
|
||||
if (!mobileNumber) throw new Error('Mobile number not found.');
|
||||
return mobileNumber;
|
||||
}
|
||||
|
||||
299
src/app/administrator/home/ActiveUsersReport.tsx
Normal file
299
src/app/administrator/home/ActiveUsersReport.tsx
Normal file
@@ -0,0 +1,299 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Stack,
|
||||
Modal,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
Divider,
|
||||
Badge,
|
||||
} from "@mantine/core";
|
||||
import Image from "next/image";
|
||||
import img from '@/app/image/logo1.jpg'
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
|
||||
import { IconDownload, IconRefresh } from "@tabler/icons-react";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { generateActiveUsersPDF } from "@/app/_components/report/Activeuser_report";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default function ActiveUsersReport() {
|
||||
const [startDate, setStartDate] = useState<Date | null>(null);
|
||||
const [endDate, setEndDate] = useState<Date | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [reportData, setReportData] = useState<any>(null);
|
||||
|
||||
const handleFetch = async () => {
|
||||
if (!startDate || !endDate) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Missing Dates",
|
||||
message: "Please select both start and end dates.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// const monthDiff =
|
||||
// (endDate.getFullYear() - startDate.getFullYear()) * 12 +
|
||||
// (endDate.getMonth() - startDate.getMonth());
|
||||
const monthDiff = dayjs(endDate).diff(dayjs(startDate), "month", true);
|
||||
|
||||
if (monthDiff > 3) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Invalid Range",
|
||||
message: "You can only fetch reports up to 3 months at a time.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const token = localStorage.getItem("admin_access_token");
|
||||
const response = await fetch(`/api/report/active_users`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Login-Type": "Admin",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
from_date: dayjs(startDate).format("YYYY-MM-DD"),
|
||||
to_date: dayjs(endDate).format("YYYY-MM-DD"),
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json(); // Expected format from your backend
|
||||
console.log(data);
|
||||
if (response.ok) {
|
||||
setReportData(data);
|
||||
setOpened(true);
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "Success",
|
||||
message: "Report fetched successfully",
|
||||
autoClose: 5000,
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Error",
|
||||
message: data?.error || "Failed to fetch report",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
message: "Something went wrong while fetching report",
|
||||
color: "red",
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Download button inside modal
|
||||
const handleDownload = () => {
|
||||
if (!reportData) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "No Data",
|
||||
message: "No report data to download.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Call your external PDF generator
|
||||
generateActiveUsersPDF(
|
||||
reportData,
|
||||
dayjs(startDate).format("YYYY-MM-DD"),
|
||||
dayjs(endDate).format("YYYY-MM-DD")
|
||||
);
|
||||
|
||||
notifications.show({
|
||||
color: "blue",
|
||||
title: "Download",
|
||||
message: "PDF downloaded successfully.",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Reset button
|
||||
const handleReset = () => {
|
||||
setStartDate(null);
|
||||
setEndDate(null);
|
||||
setReportData(null);
|
||||
notifications.show({
|
||||
color: "yellow",
|
||||
title: "Form Reset",
|
||||
message: "Dates cleared successfully.",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<Box
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "0 0 5px rgba(0,0,0,0.1)",
|
||||
maxWidth: 1000,
|
||||
}}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Group grow>
|
||||
<DatePickerInput
|
||||
label="Start Date"
|
||||
placeholder="Select start date"
|
||||
value={startDate}
|
||||
onChange={setStartDate}
|
||||
required
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label="End Date"
|
||||
placeholder="Select end date"
|
||||
value={endDate}
|
||||
onChange={setEndDate}
|
||||
required
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Group justify="flex-end" mt="sm">
|
||||
<Button onClick={handleFetch} loading={loading}>
|
||||
Fetch
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
variant="outline"
|
||||
color="red"
|
||||
title="Reset Form"
|
||||
>
|
||||
<IconRefresh size={18} />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
size="xl"
|
||||
centered
|
||||
withCloseButton
|
||||
>
|
||||
{reportData ? (
|
||||
<Box>
|
||||
|
||||
|
||||
<Box mb="sm">
|
||||
<img
|
||||
src={img.src}
|
||||
alt="KCC Bank Logo"
|
||||
width={60}
|
||||
height={60}
|
||||
style={{
|
||||
display: "block",
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
<Stack gap={4} mb="md">
|
||||
<Title order={4}>Active Users Report</Title>
|
||||
|
||||
<Text>
|
||||
<b>Total Users:</b> {reportData.total_users}
|
||||
</Text>
|
||||
<Text >
|
||||
<b>Active Users:</b> {reportData.active_users}
|
||||
</Text>
|
||||
|
||||
</Stack>
|
||||
|
||||
<Divider my="sm" />
|
||||
|
||||
|
||||
<Group justify="space-between" align="center" mb="xs">
|
||||
<Title order={5}>
|
||||
Active User List From{" "}
|
||||
|
||||
{startDate ? dayjs(startDate).format("YYYY-MM-DD") : "—"} To{" "}
|
||||
{endDate ? dayjs(endDate).format("YYYY-MM-DD") : "—"}
|
||||
</Title>
|
||||
|
||||
<Button
|
||||
onClick={handleDownload}
|
||||
size="xs"
|
||||
leftSection={<IconDownload size={16} />}
|
||||
color="blue"
|
||||
variant="light"
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
<Box style={{ maxHeight: "400px", overflowY: "auto" }}>
|
||||
<Table striped withTableBorder style={{ width: "100%" }}>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Customer No</Table.Th>
|
||||
<Table.Th>User Name</Table.Th>
|
||||
<Table.Th>Created At</Table.Th>
|
||||
<Table.Th>Last Login</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{reportData.active_user_list.map((user: any, index: number) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>{user.customer_no}</Table.Td>
|
||||
<Table.Td>{user.user_name}</Table.Td>
|
||||
<Table.Td>
|
||||
{new Date(user.created_at).toLocaleString()}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{new Date(user.last_login).toLocaleString()}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
color={user.status === "active" ? "green" : "red"}
|
||||
variant="filled"
|
||||
>
|
||||
{user.status.toUpperCase()}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Text ta="center" c="dimmed">
|
||||
|
||||
No data available
|
||||
</Text>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
|
||||
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
300
src/app/administrator/home/InactiveUsersReport.tsx
Normal file
300
src/app/administrator/home/InactiveUsersReport.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Group,
|
||||
Stack,
|
||||
Modal,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
Divider,
|
||||
Badge,
|
||||
} from "@mantine/core";
|
||||
import Image from "next/image";
|
||||
import img from '@/app/image/logo1.jpg'
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
|
||||
import { IconDownload, IconRefresh } from "@tabler/icons-react";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { generateInactiveUsersPDF } from "@/app/_components/report/Inactiveuser_report";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default function InactiveUsersReport() {
|
||||
const [startDate, setStartDate] = useState<Date | null>(null);
|
||||
const [endDate, setEndDate] = useState<Date | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [opened, setOpened] = useState(false);
|
||||
const [reportData, setReportData] = useState<any>(null);
|
||||
|
||||
const handleFetch = async () => {
|
||||
if (!startDate || !endDate) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Missing Dates",
|
||||
message: "Please select both start and end dates.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// const monthDiff =
|
||||
// (endDate.getFullYear() - startDate.getFullYear()) * 12 +
|
||||
// (endDate.getMonth() - startDate.getMonth());
|
||||
const monthDiff = dayjs(endDate).diff(dayjs(startDate), "month", true);
|
||||
|
||||
if (monthDiff > 3) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Invalid Range",
|
||||
message: "You can only fetch reports up to 3 months at a time.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const token = localStorage.getItem("admin_access_token");
|
||||
const response = await fetch(`/api/report/in-active_users`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Login-Type": "Admin",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
from_date: dayjs(startDate).format("YYYY-MM-DD"),
|
||||
to_date: dayjs(endDate).format("YYYY-MM-DD"),
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json(); // Expected format from your backend
|
||||
console.log(data);
|
||||
if (response.ok) {
|
||||
setReportData(data);
|
||||
setOpened(true);
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "green",
|
||||
title: "Success",
|
||||
message: "Report fetched successfully",
|
||||
autoClose: 5000,
|
||||
});
|
||||
} else {
|
||||
notifications.show({
|
||||
withBorder: true,
|
||||
color: "red",
|
||||
title: "Error",
|
||||
message: data?.error || "Failed to fetch report",
|
||||
autoClose: 5000,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notifications.show({
|
||||
title: "Error",
|
||||
message: "Something went wrong while fetching report",
|
||||
color: "red",
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Download button inside modal
|
||||
const handleDownload = () => {
|
||||
if (!reportData) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "No Data",
|
||||
message: "No report data to download.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Call your external PDF generator
|
||||
generateInactiveUsersPDF(
|
||||
reportData,
|
||||
dayjs(startDate).format("YYYY-MM-DD"),
|
||||
dayjs(endDate).format("YYYY-MM-DD")
|
||||
);
|
||||
|
||||
notifications.show({
|
||||
color: "blue",
|
||||
title: "Download",
|
||||
message: "PDF downloaded successfully.",
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Reset button
|
||||
const handleReset = () => {
|
||||
setStartDate(null);
|
||||
setEndDate(null);
|
||||
setReportData(null);
|
||||
notifications.show({
|
||||
color: "yellow",
|
||||
title: "Form Reset",
|
||||
message: "Dates cleared successfully.",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<Box
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "0 0 5px rgba(0,0,0,0.1)",
|
||||
maxWidth: 1000,
|
||||
}}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Group grow>
|
||||
<DatePickerInput
|
||||
label="Start Date"
|
||||
placeholder="Select start date"
|
||||
value={startDate}
|
||||
onChange={setStartDate}
|
||||
required
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label="End Date"
|
||||
placeholder="Select end date"
|
||||
value={endDate}
|
||||
onChange={setEndDate}
|
||||
required
|
||||
maxDate={new Date()}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
<Group justify="flex-end" mt="sm">
|
||||
<Button onClick={handleFetch} loading={loading}>
|
||||
Fetch
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
variant="outline"
|
||||
color="red"
|
||||
title="Reset Form"
|
||||
>
|
||||
<IconRefresh size={18} />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
<Modal
|
||||
opened={opened}
|
||||
onClose={() => setOpened(false)}
|
||||
size="xl"
|
||||
centered
|
||||
withCloseButton
|
||||
>
|
||||
{reportData ? (
|
||||
<Box>
|
||||
|
||||
|
||||
<Box mb="sm">
|
||||
<img
|
||||
src={img.src}
|
||||
alt="KCC Bank Logo"
|
||||
width={60}
|
||||
height={60}
|
||||
style={{
|
||||
display: "block",
|
||||
marginBottom: "10px",
|
||||
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
||||
<Stack gap={4} mb="md">
|
||||
<Title order={4}>Inactive Users Report</Title>
|
||||
|
||||
<Text>
|
||||
<b>Total Users:</b> {reportData.total_users}
|
||||
</Text>
|
||||
<Text >
|
||||
<b>Inactive Users:</b> {reportData.active_users}
|
||||
</Text>
|
||||
|
||||
</Stack>
|
||||
|
||||
<Divider my="sm" />
|
||||
|
||||
|
||||
|
||||
|
||||
<Group justify="space-between" align="center" mb="xs">
|
||||
<Title order={5}>
|
||||
Active User List From{" "}
|
||||
{startDate ? dayjs(startDate).format("YYYY-MM-DD") : "—"} To{" "}
|
||||
{endDate ? dayjs(endDate).format("YYYY-MM-DD") : "—"}
|
||||
</Title>
|
||||
|
||||
<Button
|
||||
onClick={handleDownload}
|
||||
size="xs"
|
||||
leftSection={<IconDownload size={16} />}
|
||||
color="blue"
|
||||
variant="light"
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
|
||||
<Box style={{ maxHeight: "400px", overflowY: "auto" }}>
|
||||
<Table striped withTableBorder style={{ width: "100%" }}>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Customer No</Table.Th>
|
||||
<Table.Th>User Name</Table.Th>
|
||||
<Table.Th>Created At</Table.Th>
|
||||
<Table.Th>Last Login</Table.Th>
|
||||
<Table.Th>Status</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{reportData.inactive_user_list.map((user: any, index: number) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>{user.customer_no}</Table.Td>
|
||||
<Table.Td>{user.user_name}</Table.Td>
|
||||
<Table.Td>
|
||||
{new Date(user.created_at).toLocaleString()}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{new Date(user.last_login).toLocaleString()}
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
color={user.status === "inactive" ? "green" : "red"}
|
||||
variant="filled"
|
||||
>
|
||||
{user.status.toUpperCase()}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<Text ta="center" c="dimmed">
|
||||
No data available
|
||||
</Text>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
|
||||
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
189
src/app/administrator/home/TransactionReport.tsx
Normal file
189
src/app/administrator/home/TransactionReport.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
"use client";
|
||||
import React, { useState } from "react";
|
||||
import { Box, Button, Group, Stack, Select } from "@mantine/core";
|
||||
import { DatePickerInput } from "@mantine/dates";
|
||||
import { IconDownload, IconRefresh } from "@tabler/icons-react";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
|
||||
export default function TransactionReport() {
|
||||
const [selectType, setSelectType] = useState<string | null>(null);
|
||||
const [transactionType, setTransactionType] = useState<string | null>(null);
|
||||
const [fromDate, setFromDate] = useState<Date | null>(null);
|
||||
const [toDate, setToDate] = useState<Date | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 🔹 Fetch logic
|
||||
const handleFetch = async () => {
|
||||
if (!selectType || !transactionType || !fromDate || !toDate) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Missing Fields",
|
||||
message: "Please fill all fields before fetching report.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/reports/transactions?type=${selectType}&txnType=${transactionType}&start=${fromDate.toISOString()}&end=${toDate.toISOString()}`
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to fetch report");
|
||||
const data = await response.json();
|
||||
console.log("Fetched Transaction Report:", data);
|
||||
notifications.show({
|
||||
color: "green",
|
||||
title: "Success",
|
||||
message: "Transaction report fetched successfully!",
|
||||
});
|
||||
} catch (err) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Error",
|
||||
message: "Unable to fetch transaction report. Try again later.",
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Download logic with 3-month validation
|
||||
const handleDownload = () => {
|
||||
if (!selectType || !transactionType || !fromDate || !toDate) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Missing Fields",
|
||||
message: "Please fill all fields before downloading report.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const monthDiff =
|
||||
(toDate.getFullYear() - fromDate.getFullYear()) * 12 +
|
||||
(toDate.getMonth() - fromDate.getMonth());
|
||||
|
||||
if (monthDiff > 3) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
title: "Invalid Range",
|
||||
message: "You can only download reports up to 3 months at a time.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
notifications.show({
|
||||
color: "blue",
|
||||
title: "Download Started",
|
||||
message: "Downloading transaction report...",
|
||||
});
|
||||
|
||||
// Add your actual download API logic here
|
||||
console.log("Downloading report:", {
|
||||
selectType,
|
||||
transactionType,
|
||||
fromDate,
|
||||
toDate,
|
||||
});
|
||||
};
|
||||
|
||||
// Reset logic
|
||||
const handleReset = () => {
|
||||
setSelectType(null);
|
||||
setTransactionType(null);
|
||||
setFromDate(null);
|
||||
setToDate(null);
|
||||
notifications.show({
|
||||
color: "yellow",
|
||||
title: "Form Reset",
|
||||
message: "All fields cleared successfully.",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
p="md"
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
borderRadius: "8px",
|
||||
boxShadow: "0 0 5px rgba(0,0,0,0.1)",
|
||||
maxWidth: 1000,
|
||||
}}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Group gap="md" grow>
|
||||
{/* Select Type */}
|
||||
<Select
|
||||
label="Select Type"
|
||||
placeholder="Choose Type"
|
||||
data={[
|
||||
{ value: "IB", label: "IB" },
|
||||
{ value: "MB", label: "MB" },
|
||||
]}
|
||||
value={selectType}
|
||||
onChange={setSelectType}
|
||||
style={{ width: "200px" }}
|
||||
/>
|
||||
|
||||
{/* Transaction Type */}
|
||||
<Select
|
||||
label="Transaction Type"
|
||||
placeholder="Select Transaction Type"
|
||||
data={[
|
||||
{ value: "IMPS", label: "IMPS" },
|
||||
{ value: "RTGS", label: "RTGS" },
|
||||
{ value: "NEFT", label: "NEFT" },
|
||||
]}
|
||||
value={transactionType}
|
||||
onChange={setTransactionType}
|
||||
style={{ width: "200px" }}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
{/* From and To Date on same line */}
|
||||
<Group grow>
|
||||
<DatePickerInput
|
||||
label="From Date"
|
||||
placeholder="Select from date"
|
||||
value={fromDate}
|
||||
onChange={setFromDate}
|
||||
/>
|
||||
<DatePickerInput
|
||||
label="To Date"
|
||||
placeholder="Select to date"
|
||||
value={toDate}
|
||||
onChange={setToDate}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
{/* Buttons aligned to right */}
|
||||
<Group justify="flex-end" mt="sm">
|
||||
<Button
|
||||
onClick={handleFetch}
|
||||
loading={loading}
|
||||
|
||||
>
|
||||
Fetch
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleDownload}
|
||||
variant="outline"
|
||||
color="blue"
|
||||
title="Download Report"
|
||||
>
|
||||
<IconDownload size={18} />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
variant="outline"
|
||||
color="red"
|
||||
title="Reset Form"
|
||||
>
|
||||
<IconRefresh size={18} />
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -28,6 +28,10 @@ import {
|
||||
import UserConfiguration from "./UserConfiguration";
|
||||
import ViewUserConfiguration from "./ViewUserConfiguration";
|
||||
import UnlockedUsers from "./UnlockedUsers";
|
||||
import ActiveUsersReport from "./ActiveUsersReport";
|
||||
import InactiveUsersReport from "./InactiveUsersReport";
|
||||
|
||||
import TransactionReport from "./TransactionReport";
|
||||
|
||||
export default function Login() {
|
||||
const router = useRouter();
|
||||
@@ -267,6 +271,20 @@ export default function Login() {
|
||||
>
|
||||
• Active Users Report
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
onClick={() => setView("inactiveUsersReport")}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: view === "inactiveUsersReport" ? "#02a355" : "white",
|
||||
backgroundColor: view === "inactiveUsersReport" ? "white" : "transparent",
|
||||
borderRadius: "3px",
|
||||
padding: "3px 6px",
|
||||
transition: "0.2s",
|
||||
}}
|
||||
>
|
||||
• Inactive Users Report
|
||||
</Box>
|
||||
<Box
|
||||
onClick={() => setView("noOfTrans")}
|
||||
style={{
|
||||
@@ -373,29 +391,18 @@ export default function Login() {
|
||||
{view === "userConf" && <UserConfiguration />}
|
||||
{view === "viewUser" && <ViewUserConfiguration />}
|
||||
{view === "unlockUser" && <UnlockedUsers />}
|
||||
{view === "noOfTrans" && (
|
||||
<Text size="sm" c="gray">
|
||||
Reports will be Coming Soon
|
||||
</Text>
|
||||
)}
|
||||
{view === "activeUsersReport" && (
|
||||
<Text size="sm" c="gray">
|
||||
Active users Reports will be Coming Soon
|
||||
</Text>
|
||||
)}
|
||||
{view === "ifscConfig" && (
|
||||
<Text size="sm" c="gray">
|
||||
IFSC Configuration Page Coming Soon
|
||||
</Text>
|
||||
)}
|
||||
{view === "activeUsersReport" && <ActiveUsersReport />}
|
||||
{view === "inactiveUsersReport" && <InactiveUsersReport />}
|
||||
{view === "noOfTrans" && <TransactionReport />}
|
||||
|
||||
{!view && (
|
||||
<>
|
||||
<Text size="xl" c="gray">
|
||||
{/* <Text size="xl" c="gray">
|
||||
Welcome To The Internet Banking Admin Portal
|
||||
</Text>
|
||||
<Text size="sm" c="black">
|
||||
Choose the service from the menu.
|
||||
</Text>
|
||||
</Text> */}
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@@ -45,8 +45,8 @@ export default function Login() {
|
||||
}
|
||||
|
||||
try {
|
||||
await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: mobile });
|
||||
// await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: "7890544527" });
|
||||
// await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: mobile });
|
||||
await sendOtp({ type: 'LOGIN_OTP', username: CIF, mobileNumber: "6297421727" });
|
||||
notifications.show({
|
||||
color: 'orange',
|
||||
title: 'OTP Required',
|
||||
@@ -67,8 +67,9 @@ export default function Login() {
|
||||
async function handleVerifyOtp(mobile?: string) {
|
||||
try {
|
||||
if (mobile) {
|
||||
await verifyLoginOtp(otp, mobile);
|
||||
// await verifyLoginOtp(otp, '7890544527');
|
||||
//await verifyLoginOtp(otp, mobile);
|
||||
await verifyLoginOtp(otp, '6297421727');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user