code base

This commit is contained in:
ANUJ7MADKE
2025-07-13 22:49:55 +05:30
parent d4f21c9a99
commit cd43f0e98e
96 changed files with 17779 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
import React, { useEffect } from "react";
import { Formik } from "formik";
import { useSubmit, useRouteLoaderData, useNavigation } from "react-router-dom";
import { filterDataFormFeilds } from "./FilterDataFormFeilds";
import * as yup from "yup";
import Input from "../../ApplicationForm/Input";
import axios from "axios";
function FilterDataForm({ setReportData, setLoading }) {
const { role, user } = useRouteLoaderData("Validator-Root")?.data;
const navigation = useNavigation();
const isSubmittingNav = navigation.state === "submitting";
const prefilledData =
user?.institute || user?.department
? {
institute: user?.institute,
department: user?.department,
}
: null;
const formFields = prefilledData
? filterDataFormFeilds.map((section) => ({
...section,
fields: section.fields.map((field) => ({
...field,
disabled: prefilledData[field.name],
})),
}))
: filterDataFormFeilds;
const createInitialValuesScheme = (formFields) => {
const schema = {};
formFields?.forEach((section) => {
section?.fields?.forEach((field) => {
if (prefilledData) {
if (field.type === "miniForm" || field.type === "checkbox") {
schema[field.name] = JSON.parse(prefilledData[field.name]);
} else {
schema[field.name] = prefilledData[field.name];
}
} else if (field.type === "checkbox") {
schema[field.name] = false;
} else if (field.type === "miniForm") {
schema[field.name] = [];
} else {
schema[field.name] = "";
}
});
});
return schema;
};
const initialValuesSchema = createInitialValuesScheme(formFields);
const createValidationSchema = (formFields) => {
const schema = {};
formFields?.forEach((section) => {
section.fields?.forEach((field) => {
if (field.validation) {
schema[field.name] = field.validation;
}
});
});
return yup.object().shape(schema);
};
const validationSchema = createValidationSchema(formFields);
const handleSubmit = async (values, { setSubmitting, setErrors }) => {
const { institute, department, year, applicationType } = values;
try {
setLoading(true);
const queryParams = new URLSearchParams();
if (institute) queryParams.append("institute", institute);
if (department) queryParams.append("department", department);
if (year) queryParams.append("year", year);
if (applicationType) queryParams.append("applicationType", applicationType);
const res = await axios.get(
`http://localhost:3000/validator/getReportData?${queryParams.toString()}`,
{
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
}
);
setReportData({data: res.data, query: values});
} catch (error) {
if (error.response && error.response.data) {
setErrors({ submit: error.response.data.message });
} else {
setErrors({ submit: "An unexpected error occurred" });
}
} finally {
setLoading(false);
setSubmitting(false);
}
};
useEffect(() => {
// Trigger form submission on first render
handleSubmit(initialValuesSchema, { setSubmitting: () => {}, setErrors: () => {} });
}, []);
return (
<Formik
initialValues={initialValuesSchema}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
setFieldValue, // Use setFieldValue for file handling
isSubmitting,
}) => (
<form onSubmit={handleSubmit} className="bg-transparent">
<Input
values={values}
errors={errors}
touched={touched}
handleChange={handleChange}
handleBlur={handleBlur}
setFieldValue={setFieldValue} // Pass setFieldValue for file handling
formFeilds={formFields}
/>
<button
type="submit"
disabled={isSubmitting || isSubmittingNav}
className="w-full flex items-center justify-center bg-gradient-to-r from-red-600 to-red-800 hover:from-red-800 hover:to-red-600 text-white font-semibold py-2 px-4 rounded-lg shadow-lg transform transition duration-300 ease-in-out disabled:bg-gray-400"
>
{isSubmitting || isSubmittingNav ? "Gettting Data" : "Get Data"}
</button>
</form>
)}
</Formik>
);
}
export default FilterDataForm;

View File

@@ -0,0 +1,65 @@
import * as yup from "yup";
import {
institutes,
instituteDepartmentMapping,
} from "../../../components/BaseData";
const currentYear = new Date().getFullYear();
const yearOptions = [];
for (let year = 2018; year <= currentYear; year++) {
yearOptions.push({ label: year.toString(), value: year.toString() });
}
const filterDataFormFeilds = [
{
label: "Travel Polciy Report",
fields: [
{
label: "Select Institute",
name: "institute",
type: "dropdown",
options: {
"": institutes,
},
validation: yup
.string()
.notRequired("Department selection is notRequired"),
},
{
depend: "institute",
label: "Select Department",
name: "department",
type: "dropdown",
options: instituteDepartmentMapping,
validation: yup
.string()
.notRequired("Department selection is notRequired"),
},
{
label: "Select Application Type",
name: "applicationType",
type: "dropdown",
options: {
"": [
{ label: "Student Applications", value: "STUDENT" },
{ label: "Faculty Applications", value: "FACULTY" },
],
},
validation: yup
.string()
.notRequired("Department selection is notRequired"),
},
{
label: "Select Year",
name: "year",
type: "dropdown",
options: {
"": yearOptions,
},
validation: yup.string().notRequired("Year is required"),
},
],
},
];
export { filterDataFormFeilds };

View File

@@ -0,0 +1,90 @@
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from "chart.js";
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
function OverTheYearsLine() {
const options = {
responsive: true, // Make the chart responsive to window resizing
maintainAspectRatio: false, // Allow chart size to change dynamically (optional)
// Title configuration
plugins: {
title: {
display: true,
text: "Applications Over the Years", // Set a title for the chart
font: {
size: 16,
},
},
tooltip: {
// Customize tooltips
callbacks: {
label: function (context) {
// Format tooltip labels
return `${context.dataset.label}: ${context.raw} steps`;
},
},
},
legend: {
display: true,
position: "top", // Legend position: 'top', 'left', 'bottom', 'right'
},
},
// Scales configuration (e.g., setting up x and y axes)
scales: {
x: {
// X-axis configuration (labels are auto-set)
title: {
display: true,
text: "Year", // Label for the X-axis
},
},
y: {
// Y-axis configuration
title: {
display: true,
text: "Number of Applications", // Label for the Y-axis
},
// ticks: {
// // Custom tick marks
// beginAtZero: true, // Start Y-axis from 0
// stepSize: 9000, // Tick step size for Y-axis
// },
},
},
};
const data = {
labels: [2020, 2021, 2022, 2023, 2024],
datasets: [
{
label: "Steps",
data: [30, 50, 45, 90, 35],
borderColor: "rgb(75, 192, 192)",
},
],
};
return <Line options={options} data={data} />;
}
export default OverTheYearsLine;

View File

@@ -0,0 +1,65 @@
import { Pie } from "react-chartjs-2";
import {
Chart as ChartJS,
ArcElement,
Tooltip,
Legend,
} from "chart.js";
// Register chart components
ChartJS.register(ArcElement, Tooltip, Legend);
function OverTheYearsPie() {
const options = {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: "Steps Distribution Over Years",
font: {
size: 16,
},
},
tooltip: {
callbacks: {
label: function (context) {
return `${context.label}: ${context.raw} steps`;
},
},
},
legend: {
display: true,
position: "top",
},
},
};
const data = {
labels: ["2020", "2021", "2022", "2023", "2024"], // Labels for the pie slices
datasets: [
{
data: [3000, 5000, 4500, 9000, 12000], // Data for the pie chart
backgroundColor: [
"rgba(75, 192, 192, 0.5)", // Color for the 2020 slice
"rgba(255, 99, 132, 0.5)", // Color for the 2021 slice
"rgba(54, 162, 235, 0.5)", // Color for the 2022 slice
"rgba(153, 102, 255, 0.5)", // Color for the 2023 slice
"rgba(255, 159, 64, 0.5)", // Color for the 2024 slice
],
borderColor: [
"rgb(75, 192, 192)",
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(153, 102, 255)",
"rgb(255, 159, 64)",
],
borderWidth: 1,
},
],
};
return <Pie options={options} data={data} />;
}
export default OverTheYearsPie;

View File

@@ -0,0 +1,166 @@
import React from "react";
import {
Page,
Text,
View,
Document,
StyleSheet,
Image,
} from "@react-pdf/renderer";
// Create styles
const styles = StyleSheet.create({
page: {
flexDirection: "column",
backgroundColor: "white",
padding: 20,
},
sectionTitle: {
textAlign: "center",
fontSize: 16,
fontWeight: "bold",
marginBottom: 10,
},
section: {
margin: 10,
padding: 10,
},
viewer: {
width: "75vw", // Full width
height: "100vh", // Full height
},
cardContainer: {
flexDirection: "row",
justifyContent: "space-between",
marginVertical: 10,
},
card: {
width: "45%",
padding: 10,
backgroundColor: "#f8e7d1",
borderRadius: 5,
textAlign: "center",
fontSize: 14,
fontWeight: "bold",
},
chartContainer: {
marginVertical: 20,
textAlign: "center",
},
table: {
display: "table",
width: "auto",
borderStyle: "solid",
borderWidth: 1,
borderColor: "#bfbfbf",
borderBottomWidth: 0,
borderRightWidth: 0,
},
tableRow: {
flexDirection: "row",
},
tableColHeader: {
width: "25%",
borderStyle: "solid",
borderColor: "#bfbfbf",
borderRightWidth: 1,
borderBottomWidth: 1,
backgroundColor: "#f2f2f2",
padding: 5,
textAlign: "center",
},
tableCol: {
width: "25%",
borderStyle: "solid",
borderColor: "#bfbfbf",
borderRightWidth: 1,
borderBottomWidth: 1,
padding: 5,
textAlign: "center",
},
tableCellHeader: {
margin: 5,
fontSize: 12,
fontWeight: "bold",
},
tableCell: {
margin: 5,
fontSize: 10,
},
image: {
width: 400,
height: 300,
},
});
// Create Document Component
const ReportPDF = ({ tableData, chartImages }) => {
return (
<Document>
<Page size="A4" style={styles.page}>
{/* Title */}
<Text style={styles.sectionTitle}>Travel Policy Report</Text>
{/* Summary Cards */}
{/* <View style={styles.cardContainer}>
<View style={styles.card}>
<Text>Total Funds Deployed</Text>
<Text>12,23,234</Text>
</View>
<View style={styles.card}>
<Text>Enrollment Rate</Text>
<Text>90%</Text>
</View>
</View> */}
{/* Table */}
<View style={styles.table}>
<View style={styles.tableRow}>
<View style={styles.tableColHeader}>
<Text style={styles.tableCellHeader}>ID</Text>
</View>
<View style={styles.tableColHeader}>
<Text style={styles.tableCellHeader}>Stream</Text>
</View>
<View style={styles.tableColHeader}>
<Text style={styles.tableCellHeader}>Scholarship</Text>
</View>
<View style={styles.tableColHeader}>
<Text style={styles.tableCellHeader}>Funds</Text>
</View>
</View>
{tableData?.map((row) => (
<View key={row.id} style={styles.tableRow}>
<View style={styles.tableCol}>
<Text style={styles.tableCell}>{row.id}</Text>
</View>
<View style={styles.tableCol}>
<Text style={styles.tableCell}>{row.Stream}</Text>
</View>
<View style={styles.tableCol}>
<Text style={styles.tableCell}>{row.Scholarship}</Text>
</View>
<View style={styles.tableCol}>
<Text style={styles.tableCell}>{row.Funds}</Text>
</View>
</View>
))}
</View>
{/* Charts */}
{chartImages?.barChart && (
<Image src={chartImages.barChart} style={styles.image} />
)}
{chartImages?.pieChart1 && (
<Image src={chartImages.pieChart1} style={styles.image} />
)}
{chartImages?.pieChart2 && (
<Image src={chartImages.pieChart2} style={styles.image} />
)}
</Page>
</Document>
);
};
export default ReportPDF;

View File

@@ -0,0 +1,37 @@
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>
</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>
))}
</tbody>
</table>
</div>
);
};
export default Table;

View File

@@ -0,0 +1,146 @@
import React, { useState } from "react";
import { Bar } from "react-chartjs-2";
const ChartWithDropdown = () => {
// Chart data options for faculty, students, HOI, and HOD
const chartDataOptions = {
faculty: {
approved: {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"],
datasets: [
{
label: "Approved Applications (Faculty)",
data: [100, 150, 200, 250, 300,400,900,132,920,1000,890 ,100],
backgroundColor: "rgba(75, 192, 192, 0.5)",
borderColor: "rgb(75, 192, 192)",
borderWidth: 1,
},
],
},
rejected: {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"],
datasets: [
{
label: "Rejected Applications (Faculty)",
data: [50, 60, 70, 80, 20,40,90,78,23,29,98,33],
backgroundColor: "rgba(255, 99, 132, 0.5)",
borderColor: "rgb(255, 99, 132)",
borderWidth: 1,
},
],
},
},
HOI: {
approved: {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"],
datasets: [
{
label: "Approved Applications (HOI)",
data: [1200, 1500, 1800, 2200, 2500,2000,1999,3453,2345,5633,2345,5647],
backgroundColor: "rgba(54, 162, 235, 0.5)",
borderColor: "rgb(54, 162, 235)",
borderWidth: 1,
},
],
},
rejected: {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"],
datasets: [
{
label: "Rejected Applications (HOI)",
data: [200, 300, 400, 500, 450, 350, 320, 410, 360, 430, 300, 250],
backgroundColor: "rgba(255, 159, 64, 0.5)",
borderColor: "rgb(255, 159, 64)",
borderWidth: 1,
},
],
},
},
HOD: {
approved: {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"],
datasets: [
{
label: "Approved Applications (HOD)",
data: [300, 400, 500, 450, 400, 350, 300, 250, 200, 150, 100, 50],
backgroundColor: "rgba(153, 102, 255, 0.5)",
borderColor: "rgb(153, 102, 255)",
borderWidth: 1,
},
],
},
rejected: {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"],
datasets: [
{
label: "Rejected Applications (HOD)",
data: [30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140],
backgroundColor: "rgba(255, 206, 86, 0.5)",
borderColor: "rgb(255, 206, 86)",
borderWidth: 1,
},
],
},
},
};
const chartOptions = {
responsive: true,
plugins: {
legend: { display: true },
title: { display: true, text: "Applications Over the Years" },
},
scales: {
x: { title: { display: true, text: "Year" } },
y: { title: { display: true, text: "Number of Applications" }, beginAtZero: true },
},
};
const [category, setCategory] = useState("faculty"); // Faculty, HOI, or HOD
const [applicationType, setApplicationType] = useState("approved"); // Approved or Rejected
// Fetch the data based on the selected category and application type
const data =
chartDataOptions[category]?.[applicationType] ||
chartDataOptions["faculty"]["approved"];
return (
<div style={{ width: "100%", margin: "auto", padding: "20px", flexGrow: 1 }}>
{/* Dropdown for selecting category */}
<div style={{ marginBottom: "20px" }}>
<label htmlFor="category-select" style={{ marginRight: "10px" }}>
Select Category:
</label>
<select
id="category-select"
value={category}
onChange={(e) => setCategory(e.target.value)}
style={{ padding: "5px", fontSize: "16px", marginRight: "20px", borderRadius: "15px", textAlign: "center", border: "2px solid black" }}
>
<option value="faculty">Faculty</option>
<option value="HOI">HOI</option>
<option value="HOD">HOD</option>
</select>
{/* Dropdown for selecting application type */}
<label htmlFor="type-select" style={{ marginRight: "10px" }}>
Select Application Type:
</label>
<select
id="type-select"
value={applicationType}
onChange={(e) => setApplicationType(e.target.value)}
style={{ padding: "5px", fontSize: "16px", borderRadius: "15px", textAlign: "center", border: "2px solid black" }}
>
<option value="approved">Approved Applications</option>
<option value="rejected">Rejected Applications</option>
</select>
</div>
{/* Chart */}
{data && <Bar data={data} options={chartOptions} />}
</div>
);
};
export default ChartWithDropdown;

View File

@@ -0,0 +1,143 @@
import React, { useState } from "react";
import { motion } from "framer-motion";
import "./cards.css";
import { Bar } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
} from "chart.js";
// Register chart.js components
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
);
const Card = (props) => {
const [expanded, setExpanded] = useState(false);
return (
<div className="card">
<motion.div
layout
onClick={() => setExpanded(!expanded)} // Toggle the state
className="motion_card"
>
{!expanded && ( // Render CompactCard when NOT expanded
<motion.div>
<CompactCard param={props} />
</motion.div>
)}
{expanded && ( // Render ExpandedCard when expanded
<motion.div>
<ExpandedCard param={props} />
</motion.div>
)}
</motion.div>
</div>
);
};
// Compact Card Component
function CompactCard({ param }) {
return (
<div className="CompactCard">
<div className="data">
<h1>{param.title}</h1>
</div>
<span>{param.value}</span>
</div>
);
}
// Expanded Card Component
function ExpandedCard({ param }) {
const barOptions = {
responsive: true,
plugins: {
title: {
display: true,
text: "Number of Applications Over the Years ",
},
},
scales: {
x: {
title: {
display: true,
text: "Year",
},
},
y: {
title: {
display: true,
text: "Number of Applications",
},
ticks: {
beginAtZero: true,
},
},
},
};
const chartData = {
labels: ["Jan", "Feb", "Mar", "April", "May","June","July","Aug","Sep","Nov","Dec"], // Example years
datasets: [
{
label: param.series[0].name, // e.g., "Applications"
data: param.series[0].data, // e.g., [100, 150, 200, 250, 300]
backgroundColor: "rgba(75, 192, 192, 0.6)", // Bar color
},
],
};
return (
<motion.div
layout
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="ExpandedCard"
style={{
position: "fixed", // Make it fixed to cover the whole screen
top: 0,
left: 0,
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)", // Semi-transparent background
zIndex: 999, // Ensure it's on top of other elements
}}
>
<div
className="expandedCardContent"
style={{
backgroundColor: "white",
padding: "20px",
borderRadius: "10px",
width: "70%",
height: "70%"
}}
>
<div className="data">
<h1>{param.title}</h1>
</div>
<Bar options={barOptions} data={chartData} />
<span>{param.value}</span>
</div>
</motion.div>
)
}
export default Card;

View File

@@ -0,0 +1,63 @@
.generalInfo{
display: flex;
flex-direction: column;
flex-grow: 1;
width: 30%;
padding: 10px;
}
.cards{
display: flex;
gap: 10px;
padding: 10px;
flex-direction: row;
}
.Cards{
display: flex;
flex-direction: column;
gap: 20px;
margin: 20px;
}
.CompactCard{
display: flex;
flex-direction: row;
padding: 20px;
width: 300px;
height: 200px;
align-items: center;
gap: 20px;
cursor: pointer;
background-color: antiquewhite;
border-width: 5px;
border-color: rgb(85, 85, 85);
border-radius: 5px;
filter: drop-shadow(2px 4px 6px rgb(114, 114, 114));
}
.CompactCard:hover
{
filter: drop-shadow(2px 4px 6px rgb(255, 255, 255));
}
.data>h1
{
font-size: large;
font-weight: 1000;
}
.CompactCard>span
{
display: flex;
flex-direction: row;
align-items: end;
}
.motionCard{
gap: 20px;
}
.h{
display: flex;
flex-direction: row;
margin: 10px;
gap: 10px;
}

View File

@@ -0,0 +1,28 @@
import './cards.css'
import React from 'react'
import Card from './card'
import { CardsData } from '../Data';
const Cards= () =>{
return(
<div className="Cards">
{CardsData.map((card , id)=>{
return(
<div className="parentContainer">
<Card
title={card.title}
color={card.color}
value={card.value}
series={card.series}
/>
</div>
)
})}
</div>
);
}
export default Cards

View File

@@ -0,0 +1,440 @@
import React, { useEffect, useRef, useState } from "react";
import ChartWithDropdown from "./approved";
import Cards from "./cards";
import "./cards.css";
import { Bar, Pie } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
ArcElement,
Title,
Tooltip,
Legend,
} from "chart.js";
import ChartDataLabels from 'chartjs-plugin-datalabels';
import Table from "./Table";
import { PDFDownloadLink, PDFViewer } from "@react-pdf/renderer";
import ApprovalVsRejectionTrends from "./map";
import ReportPDF from "./reportPDF";
// Register chart components for all three types (Line, Bar, Pie)
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
BarElement,
ArcElement,
Title,
Tooltip,
Legend,
ChartDataLabels
);
function Charts({ reportData }) {
const { data, query } = reportData;
if (!data) {
return (
<div className="text-center text-xl text-red-700 py-10">
No Data Found
</div>
);
}
const { acceptedApplications, rejectedApplications, pendingApplications } = data;
const tableData = [];
const groupedData = {};
if (acceptedApplications) {
for (const item of acceptedApplications) {
const { institute, department, formData } = item;
const { totalExpense } = formData;
if (!groupedData[institute]) {
groupedData[institute] = {};
}
if (query.institute) {
if (!groupedData[institute][department]) {
groupedData[institute][department] = {
totalExpense: 0,
applications: 0,
};
}
// Aggregate the data
groupedData[institute][department].totalExpense +=
parseFloat(totalExpense); // Summing the expenses
groupedData[institute][department].applications += 1;
} else {
if (!groupedData[institute].applications) {
groupedData[institute] = {
totalExpense: 0,
applications: 0,
};
}
// Aggregate the data
groupedData[institute].totalExpense += parseFloat(totalExpense); // Summing the expenses
groupedData[institute].applications += 1;
}
}
}
// Step 2: Transform grouped data into desired table format
if (query.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
});
}
}
} else {
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
Purpose_of_Travel: instituteData.purposeOfTravel,
Funds: instituteData.totalExpense.toFixed(2), // Formatting funds to 2 decimal places
});
}
}
const [chartImages, setChartImages] = useState({
barChart: null,
pieChart1: null,
pieChart2: null,
isLoading: false,
});
// Line Chart Data and Options
const lineOptions = {
responsive: true,
plugins: {
title: {
display: true,
text: "Number of Applications Over the Years ",
},
},
scales: {
x: {
title: {
display: true,
text: "Year",
},
},
y: {
title: {
display: true,
text: "Number of Applications",
},
ticks: {
beginAtZero: true,
},
},
},
};
const lineData = {
labels: [2020, 2021, 2022, 2023, 2024],
datasets: [
{
label: "Applications",
data: [1200, 1500, 1800, 2200, 2500], // Updated data for number of applications
borderColor: "rgb(75, 192, 192)",
fill: false,
tension: 0.1,
},
],
};
// Bar Chart Data and Options
const barOptions = {
responsive: true,
plugins: {
title: {
display: true,
text: "Number of Applications Over the Years ",
},
},
scales: {
x: {
title: {
display: true,
text: "Month",
},
},
y: {
title: {
display: true,
text: "Number of Applications",
},
ticks: {
beginAtZero: true,
},
},
},
};
const barData = {
labels: [
"Jan",
"Feb",
"Mar",
"April",
"May",
"June",
"July",
"Aug",
"Sep",
"Nov",
"Dec",
],
datasets: [
{
label: "Applications",
data: [
1200, 1500, 1800, 2200, 200, 800, 1235, 604, 2345, 2523, 3453, 6453,
], // Updated data for number of applications
backgroundColor: "rgba(75, 192, 192, 0.5)",
borderColor: "rgb(75, 192, 192)",
borderWidth: 1,
},
],
};
// Pie Chart Data and Options
const pieOptions = {
responsive: true,
plugins: {
title: {
display: true,
text: "Purpose of Travel",
},
},
};
const pieData = {
labels: ["Academic", "Research", "Personal", "Other"],
datasets: [
{
data: [1200, 1500, 1800, 2200], // Updated data for number of applications
backgroundColor: [
"rgba(75, 192, 192, 0.5)",
"rgba(255, 99, 132, 0.5)",
"rgba(54, 162, 235, 0.5)",
"rgba(153, 102, 255, 0.5)",
],
borderColor: [
"rgb(75, 192, 192)",
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
"rgb(153, 102, 255)",
],
borderWidth: 1,
},
],
};
const pie_Options = {
responsive: true,
plugins: {
title: {
display: true,
text: "Travel",
},
},
};
const pie_Data = {
labels: ["Domestic", "International", "Local"],
datasets: [
{
data: [1200, 1500, 1800], // Updated data for number of applications
backgroundColor: [
"rgba(79, 246, 96, 0.5)",
"rgba(255, 99, 132, 0.5)",
"rgba(54, 162, 235, 0.5)",
],
borderColor: [
"rgb(79, 246, 96)",
"rgb(255, 99, 132)",
"rgb(54, 162, 235)",
],
borderWidth: 1,
},
],
};
// const barChartRef = useRef();
// const pieChartRef1 = useRef();
// const pieChartRef2 = useRef();
// 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 (pieChartInstance1) {
// const pieBase64Image = pieChartInstance1.toBase64Image();
// setChartImages((prevImages) => ({
// ...prevImages,
// pieChart1: pieBase64Image,
// }));
// }
// if (pieChartInstance2) {
// const pieBase64Image = pieChartInstance2.toBase64Image();
// setChartImages((prevImages) => ({
// ...prevImages,
// pieChart2: pieBase64Image,
// }));
// }
// };
// useEffect(() => {
// setChartImages((prevImages) => ({ ...prevImages, isLoading: true }));
// const handleRender = () => {
// loadChartsInPdf();
// setChartImages((prevImages) => ({ ...prevImages, isLoading: false }));
// };
// const barChartInstance = barChartRef.current;
// const pieChartInstance1 = pieChartRef1.current;
// const pieChartInstance2 = pieChartRef2.current;
// if (barChartInstance) {
// barChartInstance.options.animation.onComplete = handleRender;
// }
// if (pieChartInstance1) {
// pieChartInstance1.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 (
<div className="p-10">
<h1 className="text-3xl mb-6">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">
<div className="w-full">
<Bar options={barOptions} data={barData} ref={barChartRef} />
</div>
<div className="w-full">
<Pie options={pieOptions} data={pieData} ref={pieChartRef1} />
</div>
</div> */}
{/* <div className="cards">
<Cards />
<div className="generalInfo">
<div className="card2">
<ChartWithDropdown />
</div>
</div>
</div> */}
{/* <div className="hh">
<ApprovalVsRejectionTrends />
</div> */}
{/* Line Chart */}
{/* <div className="w-full">
<Line options={lineOptions} data={lineData} />*/}
<div className="flex flex-col gap-10 items-center justify-center my-10">
<div className="w-full">
<Table tableData={tableData} />
</div>
{/*
<div>
<Pie options={pie_Options} data={pie_Data} ref={pieChartRef2} />
</div> */}
</div>
{chartImages.isLoading ? (
<div className="text-center text-xl text-red-700 py-10">
Generating PDF Report...
</div>
) : (
<div className="pdfreport">
<PDFDownloadLink
document={
<ReportPDF tableData={tableData} chartImages={chartImages} />
}
fileName={`report_${query.institute || "allInstitutes"}_${
query.department || "allDepartments"
}_${query.year || "allYears"}_${
query.applicationType || "allApplications"
}.pdf`}
>
{({ blob, url, loading, error }) =>
loading ? (
<div className="text-center text-xl text-red-700 py-10">
Getting Your PDF Report Ready...
</div>
) : (
<button
disabled={loading}
className="w-full flex items-center justify-center bg-gradient-to-r from-red-600 to-red-800 hover:from-red-800 hover:to-red-600 text-white font-semibold py-2 px-4 rounded-lg shadow-lg transform transition duration-300 ease-in-out disabled:bg-gray-400"
type="button"
>
Download PDF
</button>
)
}
</PDFDownloadLink>
<PDFViewer style={{ width: "70vw", height: "100vh" }}>
<ReportPDF tableData={tableData} chartImages={chartImages} />
</PDFViewer>
</div>
)}
</div>
);
}
export default Charts;

View File

@@ -0,0 +1,92 @@
import React, { useState } from "react";
import { Line } from "react-chartjs-2";
import { Chart as ChartJS, LineElement, CategoryScale, LinearScale, PointElement, Title, Tooltip, Legend, Filler } from "chart.js";
// Register required Chart.js components
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement, Title, Tooltip, Legend, Filler);
const ApprovalVsRejectionTrends = () => {
// Sample data for Approved and Rejected Applications
const applicationData = {
faculty: {
approved: [100, 150, 200, 250, 300, 400, 500, 450, 600, 550, 700, 650],
rejected: [50, 60, 70, 80, 100, 90, 120, 110, 130, 100, 140, 120],
},
HOI: {
approved: [500, 600, 700, 800, 750, 700, 900, 850, 1000, 950, 1100, 1050],
rejected: [100, 120, 140, 150, 130, 110, 180, 150, 200, 170, 220, 190],
},
HOD: {
approved: [300, 400, 350, 450, 500, 480, 550, 520, 600, 580, 650, 620],
rejected: [80, 90, 100, 110, 120, 100, 140, 130, 150, 140, 160, 150],
},
};
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// State for selected category
const [category, setCategory] = useState("faculty");
// Data for the Line Chart
const lineChartData = {
labels: months,
datasets: [
{
label: "Approved Applications",
data: applicationData[category].approved,
borderColor: "rgb(75, 192, 192)",
backgroundColor: "rgba(75, 192, 192, 0.2)",
tension: 0.4, // For a smooth curve
fill: true,
},
{
label: "Rejected Applications",
data: applicationData[category].rejected,
borderColor: "rgb(255, 99, 132)",
backgroundColor: "rgba(255, 99, 132, 0.2)",
tension: 0.4,
fill: true,
},
],
};
const lineChartOptions = {
responsive: true,
plugins: {
legend: { display: true, position: "top" },
title: { display: true, text: "Approval vs. Rejection Trends" },
},
scales: {
x: { title: { display: true, text: "Months" } },
y: { title: { display: true, text: "Number of Applications" }, beginAtZero: true },
},
};
return (
<div style={{ width: "90%", margin: "auto", padding: "20px" }}>
<h2 style={{ textAlign: "center" }}>Approval vs. Rejection Trends</h2>
{/* Dropdown to select category */}
<div style={{ marginBottom: "20px", textAlign: "center" }}>
<label htmlFor="category-select" style={{ marginRight: "10px" }}>
Select Category:
</label>
<select
id="category-select"
value={category}
onChange={(e) => setCategory(e.target.value)}
style={{ padding: "5px", fontSize: "16px", borderRadius: "8px", border: "1px solid #ccc" }}
>
<option value="faculty">Faculty</option>
<option value="HOI">HOI</option>
<option value="HOD">HOD</option>
</select>
</div>
{/* Line Chart */}
<Line data={lineChartData} options={lineChartOptions} />
</div>
);
};
export default ApprovalVsRejectionTrends;