-
-
- {/* Flex container for the boxes */}
-
-
+
+
+ {/* Flex container for the two boxes */}
+
+
{/*TODO: need to implement absentee list.*/}
+
-
+
{/*need to implement some sorta graph*/}
+
-
);
diff --git a/src/app/dashboard/dashboard.tsx b/src/app/dashboard/dashboard.tsx
index e0e1630..0174040 100644
--- a/src/app/dashboard/dashboard.tsx
+++ b/src/app/dashboard/dashboard.tsx
@@ -2,26 +2,39 @@
import React, { useState, useEffect } from "react";
import Calendar from "@/components/Calendar";
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/Card";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/Card";
import { ChartLine, Search } from "lucide-react";
import { Bar } from "react-chartjs-2";
-import { useRouter } from 'next/navigation';
+import { useRouter } from "next/navigation";
import {
- Chart as ChartJS,
- CategoryScale,
- LinearScale,
- BarElement,
- Title,
- Tooltip,
- Legend,
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
} from "chart.js";
import { DashboardService } from "@/services/streak-service";
import MemberDetails from "./[memberId]/page";
-ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend);
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend
+);
const Dashboard = () => {
- const router = useRouter();
+ const router = useRouter();
const [lowCountData] = useState<{ name: string; attended: number; missed: number }[]>([
{ name: "Jagadeesh", attended: 2, missed: 5 },
@@ -38,17 +51,17 @@ const Dashboard = () => {
]);
- const [selectedDate, setSelectedDate] = useState(new Date());
- const [attendanceData, setAttendanceData] = useState
([]);
- const [labels, setLabels] = useState([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
- const [selected, setSelected] = useState("attendance");
- const [memberSummary, setMemberSummary] = useState<{
- enrichedData: any[];
- topAttendance: { memberName: string; attendanceRatio: string };
- topStatusUpdate: { memberName: string; statusRatio: string };
- } | null>(null);
+ const [selectedDate, setSelectedDate] = useState(new Date());
+ const [attendanceData, setAttendanceData] = useState([]);
+ const [labels, setLabels] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [selected, setSelected] = useState("attendance");
+ const [memberSummary, setMemberSummary] = useState<{
+ enrichedData: any[];
+ topAttendance: { memberName: string; attendanceRatio: string };
+ topStatusUpdate: { memberName: string; statusRatio: string };
+ } | null>(null);
const fetchAttendanceCount = async () => {
setLoading(true);
@@ -115,159 +128,171 @@ const Dashboard = () => {
};
- const chartOptions = {
- responsive: true,
- plugins: {
- legend: {
- display: false,
- },
- tooltip: {
- enabled: true,
- },
+ const chartOptions = {
+ responsive: true,
+ plugins: {
+ legend: {
+ display: false,
+ },
+ tooltip: {
+ enabled: true,
+ },
+ },
+ scales: {
+ x: {
+ grid: {
+ display: false,
},
- scales: {
- x: {
- grid: {
- display: false,
- },
- ticks: {
- color: "#fff",
- },
- },
- y: {
- grid: {
- display: false,
- },
- ticks: {
- color: "#fff",
- stepSize: 1,
- max: 50,
- },
- beginAtZero: true,
- },
+ ticks: {
+ color: "#fff",
},
- };
-
- const chartData = {
- labels,
- datasets: [
- {
- data: loading ? new Array(7).fill(0) : attendanceData,
- backgroundColor: "rgba(229, 172, 0, 0.6)",
- borderColor: "#FEC108",
- borderWidth: 1,
- },
- ],
- };
+ },
+ y: {
+ grid: {
+ display: false,
+ },
+ ticks: {
+ color: "#fff",
+ stepSize: 1,
+ max: 50,
+ },
+ beginAtZero: true,
+ },
+ },
+ };
+
+ const chartData = {
+ labels,
+ datasets: [
+ {
+ data: loading ? new Array(7).fill(0) : attendanceData,
+ backgroundColor: "rgba(229, 172, 0, 0.6)",
+ borderColor: "#FEC108",
+ borderWidth: 1,
+ },
+ ],
+ };
useEffect(() => {
fetchAttendanceCount();
fetchMemberSummary();
}, [selectedDate]);
- const formatDate = (date: Date): string => {
- const options: Intl.DateTimeFormatOptions = { month: "short", day: "numeric", year: "numeric" };
- return date.toLocaleDateString("en-US", options);
- };
-
- const navigateToMemberDetails = (member: MemberDetails) => {
- router.push(`/dashboard/${member.id}`);
+ const formatDate = (date: Date): string => {
+ const options: Intl.DateTimeFormatOptions = {
+ month: "short",
+ day: "numeric",
+ year: "numeric",
};
+ return date.toLocaleDateString("en-US", options);
+ };
+
+ const navigateToMemberDetails = (member: MemberDetails) => {
+ router.push(`/dashboard/${member.id}`);
+ };
+
+ const getDateRange = (date: Date, range: number) => {
+ const startDate = new Date(date);
+ startDate.setDate(date.getDate() - range);
+ return `${formatDate(startDate)} - ${formatDate(date)}`;
+ };
+
+ const handleDateClick = (date: Date) => {
+ setSelectedDate(date);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ }
+ >
+
+ Attendance Summary
+
+ From {getDateRange(selectedDate, 7)}
+
+
+
+
+ {loading ? (
+
+ ) : error ? (
+ {error}
+ ) : (
+
+ )}
+
+
+
+
+
+
+ Low on count
+
+ From {getDateRange(selectedDate, 30)}
+
+
+
+ {/* Highlighter */}
+
+
+ {/* Attendance Option */}
+
setSelected("attendance")}
+ >
+ Attendance
+
+
+ {/* Status Updates Option */}
+
setSelected("status")}
+ >
+ Status Updates
+
+
+
+
+
+
+
+
Name
+
Attended
+
Missed
+
- const getDateRange = (date: Date, range: number) => {
- const startDate = new Date(date);
- startDate.setDate(date.getDate() - range);
- return `${formatDate(startDate)} - ${formatDate(date)}`;
- };
-
- const handleDateClick = (date: Date) => {
- setSelectedDate(date);
- };
-
- return (
-
-
-
-
-
-
-
- }
- >
-
- Attendance Summary
-
- From {getDateRange(selectedDate, 7)}
-
-
-
-
- {loading ? (
-
- ) : error ? (
- {error}
- ) : (
-
- )}
-
-
-
-
-
-
- Low on count
-
- From {getDateRange(selectedDate, 30)}
-
-
-
- {/* Highlighter */}
-
-
- {/* Attendance Option */}
-
setSelected("attendance")}
- >
- Attendance
-
-
- {/* Status Updates Option */}
-
setSelected("status")}
- >
- Status Updates
-
-
-
-
-
-
-
-
-
Name
-
Attended
-
Missed
-
-
-
+
{/*need to add query for getting people with least numbers*/}
{/*
No data available
*/}
@@ -351,8 +376,7 @@ const Dashboard = () => {
Status Updates
-
-
+
{memberSummary?.enrichedData.map((item, index) => (
{
);
};
-export default Dashboard;
+export default Dashboard;
\ No newline at end of file
diff --git a/src/components/Calendar.tsx b/src/components/Calendar.tsx
index 666716e..fe3822c 100644
--- a/src/components/Calendar.tsx
+++ b/src/components/Calendar.tsx
@@ -2,164 +2,236 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
import React, { useRef, useState, useEffect } from "react";
interface CalendarProps {
- onDateClick: (date: Date) => void;
+ onDateClick: (date: Date) => void;
}
const Calendar: React.FC
= ({ onDateClick }) => {
- const monthMap: { [key: number]: string } = {
- 0: 'January', 1: 'February', 2: 'March', 3: 'April', 4: 'May', 5: 'June',
- 6: 'July', 7: 'August', 8: 'September', 9: 'October', 10: 'November', 11: 'December'
+ const currentYear = new Date().getFullYear();
+ const initialYear = 2024;
+ const years = [];
+ for (let i = currentYear; i >= initialYear; i--) {
+ years.push(i);
+ }
+ const [month, setMonth] = useState(new Date().getMonth());
+ const [year, setYear] = useState(new Date().getFullYear());
+
+ const monthMap: { [key: number]: string } = {
+ 0: "January",
+ 1: "February",
+ 2: "March",
+ 3: "April",
+ 4: "May",
+ 5: "June",
+ 6: "July",
+ 7: "August",
+ 8: "September",
+ 9: "October",
+ 10: "November",
+ 11: "December",
+ };
+ const monthValues = Object.values(monthMap);
+
+ const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+
+ const generateWeekDates = (baseDate: Date, count: number) => {
+ const pastWeekDates = [];
+ for (let i = 0; i < count; i++) {
+ const day = new Date(baseDate);
+ day.setDate(baseDate.getDate() - i);
+ pastWeekDates.push(day);
+ }
+
+ return pastWeekDates.reverse();
+ };
+
+ const [currentDate, setCurrentDate] = useState(new Date()); // Tracks the selected date
+ const [visibleDates, setVisibleDates] = useState(
+ generateWeekDates(new Date(), 10)
+ ); // Initial number of visible dates
+ const [daysToDisplay, setDaysToDisplay] = useState(10); // Default to 10 days
+ const today = new Date();
+
+ // Dynamically set the number of visible days based on screen size
+ const updateDaysToDisplay = () => {
+ const width = window.innerWidth;
+ if (width < 430) {
+ setDaysToDisplay(3);
+ } else if (width < 530) {
+ setDaysToDisplay(4);
+ } else if (width < 600) {
+ setDaysToDisplay(5);
+ } else if (width < 690) {
+ // Small screens (sm)
+ setDaysToDisplay(6); // Show 5 days
+ } else if (width < 1024) {
+ // Medium screens (md)
+ setDaysToDisplay(7); // Show 7 days
+ } else if (width < 1440) {
+ // Large screens (lg)
+ setDaysToDisplay(10); // Show 10 days
+ } else {
+ setDaysToDisplay(13);
+ }
+ };
+
+ // Call the function on initial render and whenever the window is resized
+ useEffect(() => {
+ updateDaysToDisplay();
+ window.addEventListener("resize", updateDaysToDisplay);
+ return () => {
+ window.removeEventListener("resize", updateDaysToDisplay);
};
- const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
-
- const generateWeekDates = (baseDate: Date, count: number) => {
- const pastWeekDates = [];
- for (let i = 0; i < count; i++) {
- const day = new Date(baseDate);
- day.setDate(baseDate.getDate() - i);
- pastWeekDates.push(day);
- }
-
- return pastWeekDates.reverse();
- };
-
- const [currentDate, setCurrentDate] = useState(new Date()); // Tracks the selected date
- const [visibleDates, setVisibleDates] = useState(generateWeekDates(new Date(), 10)); // Initial number of visible dates
- const [daysToDisplay, setDaysToDisplay] = useState(10); // Default to 10 days
- const today = new Date();
-
- // Dynamically set the number of visible days based on screen size
- const updateDaysToDisplay = () => {
- const width = window.innerWidth;
- if (width < 430) {
- setDaysToDisplay(3);
- }
- else if (width < 530) {
- setDaysToDisplay(4);
- }
- else if (width < 600) {
- setDaysToDisplay(5);
- }
- else if (width < 690) { // Small screens (sm)
- setDaysToDisplay(6); // Show 5 days
- } else if (width < 1024) { // Medium screens (md)
- setDaysToDisplay(7); // Show 7 days
- } else { // Large screens (lg)
- setDaysToDisplay(10); // Show 10 days
- }
- };
-
- // Call the function on initial render and whenever the window is resized
- useEffect(() => {
- updateDaysToDisplay();
- window.addEventListener('resize', updateDaysToDisplay);
- return () => {
- window.removeEventListener('resize', updateDaysToDisplay);
- };
- }, []);
-
- useEffect(() => {
- setVisibleDates(generateWeekDates(new Date(), daysToDisplay)); // Update the visible dates
- }, [daysToDisplay]);
-
- const handlePreviousDay = () => {
- setVisibleDates((prevDates) => {
- const newDay = new Date(prevDates[0]);
- newDay.setDate(newDay.getDate() - 1);
- return [newDay, ...prevDates.slice(0, -1)];
- });
-
- setCurrentDate((prevDate) => {
- const newDate = new Date(prevDate);
- newDate.setDate(prevDate.getDate() - 1);
- return newDate;
- });
- };
-
- const handleNextDay = () => {
- setVisibleDates((prevDates) => {
- const newDay = new Date(prevDates[prevDates.length - 1]);
- newDay.setDate(newDay.getDate() + 1);
- return [...prevDates.slice(1), newDay];
- });
-
- setCurrentDate((prevDate) => {
- const newDate = new Date(prevDate);
- newDate.setDate(prevDate.getDate() + 1);
- return newDate;
- });
- };
- const handleDateClick = (date: Date) => {
- onDateClick(date);
- setCurrentDate(date);
- };
-
- const pressTimerRef = useRef(null);
- const [isLongPressing, setIsLongPressing] = useState(false);
- const LONG_PRESS_THRESHOLD = 1000; // 1 second threshold for long press
-
- const handlePressStart = () => {
- pressTimerRef.current = setTimeout(() => {
- setIsLongPressing(true);
- setCurrentDate(new Date()); // Set to today's date
- setVisibleDates(generateWeekDates(new Date(), daysToDisplay)); // Refresh the visible dates
- }, LONG_PRESS_THRESHOLD);
- };
-
- const handlePressEnd = () => {
- if (pressTimerRef.current) {
- clearTimeout(pressTimerRef.current);
- pressTimerRef.current = null;
- }
- setIsLongPressing(false);
- };
-
- const isNextDisabled = visibleDates[visibleDates.length - 1].toDateString() === today.toDateString();
- return (
-
- {/* Calendar Header */}
-
-
- {monthMap[currentDate.getMonth()]} {currentDate.getFullYear()}
-
-
-
-
-
-
-
- {visibleDates.map((dateObj, index) => (
-
handleDateClick(dateObj)}
- >
-
{weekdays[dateObj.getDay()]}
-
{dateObj.getDate()}
- {/* Circle indicator only for today's date */}
- {dateObj.toDateString() === today.toDateString() && (
-
- )}
-
- ))}
-
+ }, []);
+
+ useEffect(() => {
+ setVisibleDates(generateWeekDates(new Date(), daysToDisplay)); // Update the visible dates
+ }, [daysToDisplay]);
+
+ const handlePreviousDay = () => {
+ setVisibleDates((prevDates) => {
+ const newDay = new Date(prevDates[0]);
+ newDay.setDate(newDay.getDate() - 1);
+
+ return [newDay, ...prevDates.slice(0, -1)];
+ });
+
+ setCurrentDate((prevDate) => {
+ const newDate = new Date(prevDate);
+ newDate.setDate(prevDate.getDate() - 1);
+ return newDate;
+ });
+ };
+
+ const handleNextDay = () => {
+ setVisibleDates((prevDates) => {
+ const newDay = new Date(prevDates[prevDates.length - 1]);
+ newDay.setDate(newDay.getDate() + 1);
+ return [...prevDates.slice(1), newDay];
+ });
+
+ setCurrentDate((prevDate) => {
+ const newDate = new Date(prevDate);
+ newDate.setDate(prevDate.getDate() + 1);
+ return newDate;
+ });
+ };
+ const handleDateClick = (date: Date) => {
+ onDateClick(date);
+ setCurrentDate(date);
+ setMonth(date.getMonth());
+ setYear(date.getFullYear());
+ };
+
+ const pressTimerRef = useRef
(null);
+ const [isLongPressing, setIsLongPressing] = useState(false);
+ const LONG_PRESS_THRESHOLD = 1000; // 1 second threshold for long press
+
+ const handlePressStart = () => {
+ pressTimerRef.current = setTimeout(() => {
+ setIsLongPressing(true);
+ setMonth(new Date().getMonth()); // Reset to current month
+ setYear(new Date().getFullYear()); // Reset to current year
+ setCurrentDate(new Date()); // Set to today's date
+ setVisibleDates(generateWeekDates(new Date(), daysToDisplay)); // Refresh the visible dates
+ }, LONG_PRESS_THRESHOLD);
+ };
+
+ const handlePressEnd = () => {
+ if (pressTimerRef.current) {
+ clearTimeout(pressTimerRef.current);
+ pressTimerRef.current = null;
+
+ }
+ setIsLongPressing(false);
+ };
+
+ const handleSelectMonth = (event: React.ChangeEvent) => {
+ setMonth(Number(event?.target?.value));
+ setVisibleDates(generateWeekDates(new Date(year, Number(event?.target?.value), 1), daysToDisplay));
+ };
+
+ const handleSelectYear = (event: React.ChangeEvent) => {
+ setYear(Number(event?.target?.value));
+ setVisibleDates(generateWeekDates(new Date(Number(event?.target?.value), month, 1), daysToDisplay));
+ };
+
+ const isNextDisabled =
+ visibleDates[visibleDates.length - 1].toDateString() ===
+ today.toDateString();
+ return (
+
+ {/* Calendar Header */}
+
+
+
+
+
+
+
- );
+
+
+ {visibleDates.map((dateObj, index) => (
+
handleDateClick(dateObj)}
+ >
+
+ {weekdays[dateObj.getDay()]}
+
+
{dateObj.getDate()}
+ {/* Circle indicator only for today's date */}
+ {dateObj.toDateString() === today.toDateString() && (
+
+ )}
+
+ ))}
+
+
+ );
};
export default Calendar;
diff --git a/src/components/attendance-track/AttendanceDetails.tsx b/src/components/attendance-track/AttendanceDetails.tsx
index ad0b712..83efad8 100644
--- a/src/components/attendance-track/AttendanceDetails.tsx
+++ b/src/components/attendance-track/AttendanceDetails.tsx
@@ -1,12 +1,12 @@
"use client";
import React from "react";
-import { AttendanceDetails } from "@/types/types";
+import { updatedAttendanceDetails } from "@/types/types";
type AttendanceDetailRowProps = {
titles: string[];
loading: boolean;
error: string | null;
- attendanceData: AttendanceDetails[];
+ attendanceData: updatedAttendanceDetails[];
complete: boolean;
};
@@ -29,7 +29,7 @@ export const AttendanceDetailRow: React.FC = ({
{titles.map((title: string, index: number) => (
{title}
@@ -46,28 +46,25 @@ export const AttendanceDetailRow: React.FC = ({
className={`flex min-w-full p-2 text-center items-center font-bold ${attendance.isPresent ? "text-offWhite" : "text-red-500"
}`}
key={index}
- aria-label={`Attendance record for ${attendance.memberName}`}
+ aria-label={`Attendance record for ${attendance.name}`}
>
- {truncate(attendance.memberName, 12)}
+ {truncate(attendance.name, 30)}
-
+
{attendance.year}
{complete && (
<>
-
- {attendance.date || "N/A"}
+
+ {attendance.timeIn?.substring(0, 8) || "N/A"}
-
- {attendance.timein?.substring(0, 8) || "N/A"}
-
-
- {attendance.timeout?.substring(0, 8) || "N/A"}
+
+ {attendance.timeOut?.substring(0, 8) || "N/A"}
>
)}
diff --git a/src/lib/apollo-client.ts b/src/lib/apollo-client.ts
index 318602c..81f7b33 100644
--- a/src/lib/apollo-client.ts
+++ b/src/lib/apollo-client.ts
@@ -2,7 +2,7 @@
import { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
- uri: "https://root.amfoss.in",
+ uri: "http://localhost:8010/proxy",
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
diff --git a/src/services/attendance-service.ts b/src/services/attendance-service.ts
index 5dfbf64..0da942f 100644
--- a/src/services/attendance-service.ts
+++ b/src/services/attendance-service.ts
@@ -4,66 +4,36 @@ import client from "@/lib/apollo-client";
import { gql } from "@apollo/client";
import {
GetAttendanceDetailsQueryResponse,
- AttendanceDetails,
- GetMemberDetailsQueryResponse,
+ updatedAttendanceDetails,
+ updatedGetAttendanceDetailsQueryResponse,
} from "@/types/types";
const GET_ATTENDANCE_DETAILS_QUERY = gql`
-
- query
- {attendanceByDate(date : "2025-01-01"){
- # attendance
+ query GABD($date:NaiveDate!) {
+ attendanceByDate(date: $date ) {
+ memberId
+ name
+ year
timeIn
timeOut
- date
isPresent
- }}
-`;
-
-const GET_MEMBER_DETAILS_QUERY = gql`
-
- query {
- members {
- memberId
- name
- year
- }
+ }
}
`;
export const AttendanceService = {
// Function to get attendance details based on a specific date
- async getAttendanceDetails(date: string): Promise
{
+ async getAttendanceDetails(date: string): Promise {
try {
- const [attendanceResponse, memberResponse] = await Promise.all([
- client.query({
+ const [attendanceResponse] = await Promise.all([
+ client.query({
query: GET_ATTENDANCE_DETAILS_QUERY,
variables: { date },
}),
- client.query({
- query: GET_MEMBER_DETAILS_QUERY,
- }),
]);
- const attendanceDetails = attendanceResponse.data.getAttendance;
- const members = memberResponse.data.getMember;
-
- // Map member IDs to both names and years
- const memberMap = new Map(
- members.map((member) => [
- member.id,
- { name: member.name, year: member.year },
- ])
- );
-
- // Enrich attendance details with member names and years
- const enrichedAttendance = attendanceDetails.map((attendance) => ({
- ...attendance,
- memberName: memberMap.get(attendance.id)?.name || "Unknown", // Get member name or default to 'Unknown'
- year: memberMap.get(attendance.id)?.year || "Unknown", // Get member year or default to 'Unknown'
- }));
-
- return enrichedAttendance;
+ const attendanceDetails = attendanceResponse.data.attendanceByDate;
+ return attendanceDetails;
} catch (error) {
console.error("Error fetching attendance details:", error);
throw new Error("Could not fetch attendance details");
diff --git a/src/types/types.ts b/src/types/types.ts
index 4d0ae38..2cf27d9 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -1,5 +1,7 @@
// Type for the AttendanceDetails object returned by the query
export type AttendanceDetails = {
+ name: any;
+ memberId: any;
id: string;
timein: string;
timeout: string;
@@ -9,6 +11,16 @@ export type AttendanceDetails = {
year: string;
};
+//Saperate type for the same updatedAttendanceDetails
+export type updatedAttendanceDetails = {
+ memberId: string;
+ name: string;
+ year: string;
+ timeIn: any;
+ timeOut: any;
+ isPresent: boolean;
+};
+
// Type for the Member object
export type Member = {
id: string;
@@ -29,6 +41,10 @@ export type GetAttendanceDetailsQueryResponse = {
getAttendance: AttendanceDetails[];
};
+export type updatedGetAttendanceDetailsQueryResponse = {
+ attendanceByDate: updatedAttendanceDetails[];
+};
+
export type GetMemberDetailsQueryResponse = {
getMember: Member[];
};