Add Dark Mode, Password Hashing for better security , Settings Page, Policy PDF in Policy section,UI Changes

This commit is contained in:
arav
2026-01-10 19:39:40 +05:30
parent 933c0741ab
commit 9b605279e6
35 changed files with 1344 additions and 659 deletions

View File

@@ -3,29 +3,42 @@ import React from "react";
const Table = ({ tableData }) => {
return (
<div className="table-responsive">
<table
style={{
width: "100%",
borderCollapse: "collapse",
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
}}
>
<thead>
<tr style={{ backgroundColor: "#f4f4f4" }}>
<th style={{ padding: "10px", border: "1px solid #ddd" }}>ID</th>
<th style={{ padding: "10px", border: "1px solid #ddd" }}>Stream</th>
<th style={{ padding: "10px", border: "1px solid #ddd" }}>Scholarship</th>
<th style={{ padding: "10px", border: "1px solid #ddd" }}>Funds</th>
<div className="overflow-x-auto">
<table className="w-full border-collapse shadow-sm rounded-lg overflow-hidden bg-white dark:bg-[#3c4043] transition-colors duration-200">
<thead className="bg-gray-100 dark:bg-[#303134]">
<tr className="text-left">
<th className="p-3 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-200 font-semibold">
ID
</th>
<th className="p-3 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-200 font-semibold">
Stream
</th>
<th className="p-3 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-200 font-semibold">
Scholarship
</th>
<th className="p-3 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-200 font-semibold">
Funds
</th>
</tr>
</thead>
<tbody>
{tableData?.map((row) => (
<tr key={row.id}>
<td style={{ padding: "10px", border: "1px solid #ddd" }}>{row.id}</td>
<td style={{ padding: "10px", border: "1px solid #ddd" }}>{row.Stream}</td>
<td style={{ padding: "10px", border: "1px solid #ddd" }}>{row.Scholarship}</td>
<td style={{ padding: "10px", border: "1px solid #ddd" }}>{row.Funds}</td>
<tr
key={row.id}
className="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
>
<td className="p-3 border border-gray-200 dark:border-gray-600 text-gray-800 dark:text-gray-300">
{row.id}
</td>
<td className="p-3 border border-gray-200 dark:border-gray-600 text-gray-800 dark:text-gray-300">
{row.Stream}
</td>
<td className="p-3 border border-gray-200 dark:border-gray-600 text-gray-800 dark:text-gray-300">
{row.Scholarship}
</td>
<td className="p-3 border border-gray-200 dark:border-gray-600 text-gray-800 dark:text-gray-300">
{row.Funds}
</td>
</tr>
))}
</tbody>

View File

@@ -15,7 +15,7 @@ import {
Tooltip,
Legend,
} from "chart.js";
import ChartDataLabels from 'chartjs-plugin-datalabels';
import ChartDataLabels from "chartjs-plugin-datalabels";
import Table from "./Table";
import { PDFDownloadLink, PDFViewer } from "@react-pdf/renderer";
import ApprovalVsRejectionTrends from "./map";
@@ -46,19 +46,24 @@ function Charts({ reportData }) {
);
}
const { acceptedApplications, rejectedApplications, pendingApplications } = data;
const { acceptedApplications, rejectedApplications, pendingApplications } =
data;
const tableData = [];
const groupedData = {};
// --- Data Processing for Table --- (Simple aggregation logic)
if (acceptedApplications) {
for (const item of acceptedApplications) {
const { institute, department, formData } = item;
const { totalExpense } = formData;
// Initialize institute object if not exists
if (!groupedData[institute]) {
groupedData[institute] = {};
}
// If filtering by specific institute, we group by Department (e.g. Computer, IT, Mech)
if (query.institute) {
if (!groupedData[institute][department]) {
groupedData[institute][department] = {
@@ -67,11 +72,12 @@ function Charts({ reportData }) {
};
}
// Aggregate the data
// Add expense and increment count
groupedData[institute][department].totalExpense +=
parseFloat(totalExpense); // Summing the expenses
parseFloat(totalExpense);
groupedData[institute][department].applications += 1;
} else {
// If viewing all institutes, we group by Institute (e.g. KJSCE, KJSIM)
if (!groupedData[institute].applications) {
groupedData[institute] = {
totalExpense: 0,
@@ -79,38 +85,40 @@ function Charts({ reportData }) {
};
}
// Aggregate the data
groupedData[institute].totalExpense += parseFloat(totalExpense); // Summing the expenses
// Add expense and increment count
groupedData[institute].totalExpense += parseFloat(totalExpense);
groupedData[institute].applications += 1;
}
}
}
// Step 2: Transform grouped data into desired table format
// --- Transform Groups to Array for Display ---
if (query.institute) {
// Loop through departments for the selected institute
for (const institute in groupedData) {
for (const department in groupedData[institute]) {
const departmentData = groupedData[institute][department];
tableData.push({
id: tableData.length + 1,
Stream: department,
Scholarship: departmentData.applications, // Assuming each application is one scholarship
Purpose_of_Travel: departmentData.purposeOfTravel,
Funds: departmentData.totalExpense.toFixed(2), // Formatting funds to 2 decimal places
Stream: department, // 'Stream' here refers to the Department name
Scholarship: departmentData.applications, // Number of applications
Purpose_of_Travel: departmentData.purposeOfTravel, // (Placeholder)
Funds: departmentData.totalExpense.toFixed(2), // Total money spent
});
}
}
} else {
// Loop through all institutes
for (const institute in groupedData) {
const instituteData = groupedData[institute];
tableData.push({
id: tableData.length + 1,
Stream: institute,
Scholarship: instituteData.applications, // Assuming each application is one scholarship
Stream: institute, // 'Stream' here is the Institute name
Scholarship: instituteData.applications, // Number of applications
Purpose_of_Travel: instituteData.purposeOfTravel,
Funds: instituteData.totalExpense.toFixed(2), // Formatting funds to 2 decimal places
Funds: instituteData.totalExpense.toFixed(2), // Total money spent
});
}
}
@@ -122,6 +130,8 @@ function Charts({ reportData }) {
isLoading: false,
});
// --- Chart Configuration (Preserved for future use) ---
// Line Chart Data and Options
const lineOptions = {
responsive: true,
@@ -281,80 +291,86 @@ function Charts({ reportData }) {
],
};
// const barChartRef = useRef();
// const pieChartRef1 = useRef();
// const pieChartRef2 = useRef();
const barChartRef = useRef();
const pieChartRef1 = useRef();
const pieChartRef2 = useRef();
// const loadChartsInPdf = () => {
// const barChartInstance = barChartRef.current;
// const pieChartInstance1 = pieChartRef1.current;
// const pieChartInstance2 = pieChartRef2.current;
// Note: Chart generation logic for PDF is currently disabled as we focus on the Table view.
// Uncomment this when we are ready to integrate charts into the PDF report.
/*
const loadChartsInPdf = () => {
const barChartInstance = barChartRef.current;
const pieChartInstance1 = pieChartRef1.current;
const pieChartInstance2 = pieChartRef2.current;
// if (barChartInstance) {
// const barBase64Image = barChartInstance.toBase64Image();
// setChartImages((prevImages) => ({
// ...prevImages,
// barChart: barBase64Image,
// }));
// }
if (barChartInstance) {
const barBase64Image = barChartInstance.toBase64Image();
setChartImages((prevImages) => ({
...prevImages,
barChart: barBase64Image,
}));
}
// if (pieChartInstance1) {
// const pieBase64Image = pieChartInstance1.toBase64Image();
// setChartImages((prevImages) => ({
// ...prevImages,
// pieChart1: pieBase64Image,
// }));
// }
if (pieChartInstance1) {
const pieBase64Image = pieChartInstance1.toBase64Image();
setChartImages((prevImages) => ({
...prevImages,
pieChart1: pieBase64Image,
}));
}
// if (pieChartInstance2) {
// const pieBase64Image = pieChartInstance2.toBase64Image();
// setChartImages((prevImages) => ({
// ...prevImages,
// pieChart2: pieBase64Image,
// }));
// }
// };
if (pieChartInstance2) {
const pieBase64Image = pieChartInstance2.toBase64Image();
setChartImages((prevImages) => ({
...prevImages,
pieChart2: pieBase64Image,
}));
}
};
// useEffect(() => {
// setChartImages((prevImages) => ({ ...prevImages, isLoading: true }));
useEffect(() => {
setChartImages((prevImages) => ({ ...prevImages, isLoading: true }));
// const handleRender = () => {
// loadChartsInPdf();
// setChartImages((prevImages) => ({ ...prevImages, isLoading: false }));
// };
const handleRender = () => {
loadChartsInPdf();
setChartImages((prevImages) => ({ ...prevImages, isLoading: false }));
};
// const barChartInstance = barChartRef.current;
// const pieChartInstance1 = pieChartRef1.current;
// const pieChartInstance2 = pieChartRef2.current;
const barChartInstance = barChartRef.current;
const pieChartInstance1 = pieChartRef1.current;
const pieChartInstance2 = pieChartRef2.current;
// if (barChartInstance) {
// barChartInstance.options.animation.onComplete = handleRender;
// }
if (barChartInstance) {
barChartInstance.options.animation.onComplete = handleRender;
}
// if (pieChartInstance1) {
// pieChartInstance1.options.animation.onComplete = handleRender;
// }
if (pieChartInstance1) {
pieChartInstance1.options.animation.onComplete = handleRender;
}
// if (pieChartInstance2) {
// pieChartInstance2.options.animation.onComplete = handleRender;
// }
if (pieChartInstance2) {
pieChartInstance2.options.animation.onComplete = handleRender;
}
// return () => {
// if (barChartInstance) {
// barChartInstance.options.animation.onComplete = null;
// }
// if (pieChartInstance1) {
// pieChartInstance1.options.animation.onComplete = null;
// }
// if (pieChartInstance2) {
// pieChartInstance2.options.animation.onComplete = null;
// }
// };
// }, []);
return () => {
if (barChartInstance) {
barChartInstance.options.animation.onComplete = null;
}
if (pieChartInstance1) {
pieChartInstance1.options.animation.onComplete = null;
}
if (pieChartInstance2) {
pieChartInstance2.options.animation.onComplete = null;
}
};
}, []);
*/
return (
<div className="p-10">
<h1 className="text-3xl mb-6">Travel Policy Report</h1>
<h1 className="text-3xl mb-6 text-gray-800 dark:text-google-text">
Travel Policy Report
</h1>
{/* Container for all three charts */}
{/* <div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-[2fr,1fr] gap-6">
@@ -387,9 +403,11 @@ function Charts({ reportData }) {
<div className="flex flex-col gap-10 items-center justify-center my-10">
<div className="w-full">
{/* Reuse the Table component to show aggregated data */}
<Table tableData={tableData} />
</div>
{/*
{/*
<div>
<Pie options={pie_Options} data={pie_Data} ref={pieChartRef2} />
</div> */}
@@ -428,9 +446,11 @@ function Charts({ reportData }) {
}
</PDFDownloadLink>
<PDFViewer style={{ width: "70vw", height: "100vh" }}>
<ReportPDF tableData={tableData} chartImages={chartImages} />
</PDFViewer>
<div className="mt-8 hidden md:block">
<PDFViewer style={{ width: "70vw", height: "100vh" }}>
<ReportPDF tableData={tableData} chartImages={chartImages} />
</PDFViewer>
</div>
</div>
)}
</div>