diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/.env.sample b/Next-JS-Projects/Advanced/Attendance-Tracking-App/.env.sample new file mode 100644 index 00000000..72c91047 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/.env.sample @@ -0,0 +1,6 @@ +NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain +NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id +NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/.eslintrc.json b/Next-JS-Projects/Advanced/Attendance-Tracking-App/.eslintrc.json new file mode 100644 index 00000000..bffb357a --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/README.md b/Next-JS-Projects/Advanced/Attendance-Tracking-App/README.md new file mode 100644 index 00000000..54edb976 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/README.md @@ -0,0 +1,88 @@ +

đŸ’Ĩ Attendance Tracker đŸ’Ĩ

+ + + +

Tech Stack Used 🎮

+ + +
+ +![HTML5](https://img.shields.io/badge/html5-%23E34F26.svg?style=for-the-badge&logo=tailwind&logoColor=white) +![CSS3](https://img.shields.io/badge/css3-%231572B6.svg?style=for-the-badge&logo=css3&logoColor=white) +![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E) +![TailwindCSS](https://img.shields.io/badge/tailwindcss-%2338B2AC.svg?style=for-the-badge&logo=tailwind-css&logoColor=white) +![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) +![Nextjs](https://img.shields.io/badge/Nextjs-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) +![Firebase](https://img.shields.io/badge/firebase-a08021?style=for-the-badge&logo=firebase&logoColor=ffcd34) + +
+ +![Line](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/114330097/4b78510f-a941-45f8-a9d5-80ed0705e847) + + + +## :zap: Description 📃 + +
+ +

+ The attendance tracker is a web application that allows users to keep track of attendance for various events or activities. It is built using a combination of HTML, CSS, and JavaScript for the frontend. The UI is designed using Tailwind CSS, which provides a responsive and modern look to the application. React and Next.js are used for building the frontend components and managing the application's state. Firebase is used as the backend service for storing and retrieving attendance data. + +Feel free to explore the screenshots section to get a visual representation of the application. If you have any questions or need further assistance, please don't hesitate to reach out. + +

+
+ + + + +## :zap: How to run it? 🕹ī¸ + + + +Steps to run this website in your local machine is as follows : + +1. Fork this repository. +2. Clone the repository to your local machine. +3. Open a terminal and navigate to the project directory. +4. Create a .env file & fill the following .env template with your firebase credentials + +``` +NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain +NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id +NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id +``` + +5. Run the command `npm install` to install the required dependencies. +6. Run the command `npm run dev` to start the development server. +7. Open a web browser and visit `localhost:3000` to access the application. +8. Make sure you have Node.js installed on your machine before running the above commands. + + + +## :zap: Screenshots 📸 + + + + + +![Line](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/114330097/4b78510f-a941-45f8-a9d5-80ed0705e847) + + + +

Developed By Anirudh Sharma đŸ‘Ļ

+

+ + + + + + +

+ +

Happy Coding 🧑‍đŸ’ģ

+ +

Show some  â¤ī¸  by  đŸŒŸ  this repository!

diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/(home)/page.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/(home)/page.tsx new file mode 100644 index 00000000..fc7027cb --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/(home)/page.tsx @@ -0,0 +1,63 @@ +"use client"; +import React, { useState, useEffect } from "react"; +import { auth, provider, db } from "../../../firebase/config"; +import { + signInWithPopup, + signOut, + onAuthStateChanged, + User, +} from "firebase/auth"; +import { doc, setDoc, getDoc } from "firebase/firestore"; +import Home from "@/Components/Home/Home"; +import Landing from "@/Components/Landing"; +interface UserData { + attended: number; + total: number; + Sub_name: string; + target_percentage: number; +} + +const Page: React.FC = () => { + const [user, setUser] = useState(null); + const [userData, setUserData] = useState(null); + + useEffect(() => { + const unsubscribe = onAuthStateChanged(auth, async (currentUser) => { + setUser(currentUser); + if (currentUser) { + const userDoc = doc(db, "users", currentUser.uid); + const userDocSnap = await getDoc(userDoc); + if (userDocSnap.exists()) { + setUserData(userDocSnap.data() as UserData); + } else { + console.log("No such document!"); + } + } + }); + + return () => unsubscribe(); + }, []); + + const handleLogin = async () => { + try { + await signInWithPopup(auth, provider); + } catch (error) { + console.error("Error signing in with Google: ", error); + } + }; + + const handleLogout = async () => { + try { + await signOut(auth); + } catch (error) { + console.error("Error signing out: ", error); + } + }; + return !user ? ( + + ) : ( + + ); +}; + +export default Page; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/calendar/page.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/calendar/page.tsx new file mode 100644 index 00000000..a1d58cb3 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/calendar/page.tsx @@ -0,0 +1,16 @@ +import CalendarComponent from "@/Components/Calendar/Calendar"; +import React from "react"; +import Footer from "@//Components/Footer"; +import Navbar from "@/Components/Navbar"; + +const page = () => { + return ( +
+ + +
+
+ ); +}; + +export default page; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/layout.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/layout.tsx new file mode 100644 index 00000000..ced41e0a --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/layout.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const Layout = ({ children }: { children: React.ReactNode }) => { + return
{children}
; +}; + +export default Layout; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/dsa/page.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/dsa/page.tsx new file mode 100644 index 00000000..c63a140d --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/dsa/page.tsx @@ -0,0 +1,47 @@ +import Stamps from "@/Components/Settings/Stamps"; +import React from "react"; + +const page = () => { + return ( +
+ {/*
*/} + +
+
+

DSA Report

+ + + {" "} + + + + +
+
+

Date

+

Time

+

Status

+
+ + + + + + +
+
+ ); +}; + +export default page; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/layout.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/layout.tsx new file mode 100644 index 00000000..e3c380d0 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/layout.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +import Footer from "@//Components/Footer"; +import Navbar from "@/Components/Navbar"; + +const Layout = ({ children }: { children: React.ReactNode }) => { + return ( +
+ + {children} +
+
+ ); +}; + +export default Layout; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/page.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/page.tsx new file mode 100644 index 00000000..fc3baefc --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/(root)/settings/page.tsx @@ -0,0 +1,7 @@ +import Settings from "@/Components/Settings/Settings"; +import React from "react"; +const page = () => { + return ; +}; + +export default page; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/favicon.ico b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/favicon.ico new file mode 100644 index 00000000..c46d70ce Binary files /dev/null and b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/favicon.ico differ diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/globals.css b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/globals.css new file mode 100644 index 00000000..d58625cb --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/globals.css @@ -0,0 +1,84 @@ +@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"); +@tailwind base; +@tailwind components; +@tailwind utilities; + +.page-height-class { + /* height: calc(100vh - 120px); */ + margin-bottom: 69px; +} +.gradient-profile-text { + background: linear-gradient(90deg, #0097a1 0%, #ffffff 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.bg-black-gradient { + background: linear-gradient( + 144.39deg, + #ffffff -278.56%, + #6d6d6d -78.47%, + #11101d 91.61% + ); +} +.animated-slide-top { + -webkit-animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + z-index: 1000; +} + +@-webkit-keyframes slide-top { + 0% { + -webkit-transform: translateY(100px); + transform: translateY(100px); + } + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +@keyframes slide-top { + 0% { + -webkit-transform: translateY(100px); + transform: translateY(100px); + } + 100% { + -webkit-transform: translateY(0); + transform: translateY(0); + } +} + +.custom-calendar { + width: 400px; + min-width: 400px; + height: auto; + font-size: 1.2rem; + /* background-color: #000000; */ + /* background: #000000; */ + border-radius: 15px; + padding: 20px; + color: black; +} + +.custom-calendar .react-calendar__tile { + display: flex; + align-items: center; + justify-content: center; + /* background-color: #27272a; */ + /* color: white; */ +} + +.custom-calendar .react-calendar__month-view__weekdays__weekday { + font-weight: bold; + color: blue; + text-align: center; + padding: 10px; +} +.custom-calendar .react-calendar__tile--now { + background-color: rgb(255, 255, 64); + color: red; +} +.custom-calendar .react-calendar__month-view__days__day:nth-child(7n-1), /* Sunday */ +.custom-calendar .react-calendar__month-view__days__day:nth-child(7n) { + color: #ff0000; /* Change to desired text color */ +} diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/layout.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/layout.tsx new file mode 100644 index 00000000..03f11080 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/app/layout.tsx @@ -0,0 +1,25 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + {/*
*/} + + + ); +} diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/firebase/config.ts b/Next-JS-Projects/Advanced/Attendance-Tracking-App/firebase/config.ts new file mode 100644 index 00000000..52284588 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/firebase/config.ts @@ -0,0 +1,23 @@ +import { initializeApp } from "firebase/app"; +import { getAuth, GoogleAuthProvider } from "firebase/auth"; +import { getFirestore } from "firebase/firestore"; +import dotenv from "dotenv"; + +dotenv.config(); // Load environment variables from .env file + +const firebaseConfig = { + apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, + authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, + projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, + storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, +}; + +// Initialize Firebase +const app = initializeApp(firebaseConfig); +const auth = getAuth(app); +const provider = new GoogleAuthProvider(); +const db = getFirestore(app); + +export { auth, provider, db }; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/next.config.mjs b/Next-JS-Projects/Advanced/Attendance-Tracking-App/next.config.mjs new file mode 100644 index 00000000..4678774e --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/next.config.mjs @@ -0,0 +1,4 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/package.json b/Next-JS-Projects/Advanced/Attendance-Tracking-App/package.json new file mode 100644 index 00000000..eee9e9fb --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/package.json @@ -0,0 +1,31 @@ +{ + "name": "attendance-tracker", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "lint": "next lint" + }, + "dependencies": { + "@nextui-org/react": "^2.4.6", + "dotenv": "^16.4.5", + "firebase": "^10.12.5", + "lucide-react": "^0.414.0", + "next": "14.2.4", + "react": "^18", + "react-calendar": "^5.0.0", + "react-dom": "^18", + "react-toastify": "^10.0.5" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "eslint": "^8", + "eslint-config-next": "14.2.4", + "postcss": "^8", + "tailwindcss": "^3.4.1", + "typescript": "^5" + } +} diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/postcss.config.mjs b/Next-JS-Projects/Advanced/Attendance-Tracking-App/postcss.config.mjs new file mode 100644 index 00000000..1a69fd2a --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/postcss.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + }, +}; + +export default config; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/bottom-float.png b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/bottom-float.png new file mode 100644 index 00000000..b899376e Binary files /dev/null and b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/bottom-float.png differ diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/logo.png b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/logo.png new file mode 100644 index 00000000..d9732b20 Binary files /dev/null and b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/logo.png differ diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/profile.png b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/profile.png new file mode 100644 index 00000000..861061c3 Binary files /dev/null and b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/profile.png differ diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/top-float.png b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/top-float.png new file mode 100644 index 00000000..7a5ccb28 Binary files /dev/null and b/Next-JS-Projects/Advanced/Attendance-Tracking-App/public/assets/images/top-float.png differ diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/screenshot.webp b/Next-JS-Projects/Advanced/Attendance-Tracking-App/screenshot.webp new file mode 100644 index 00000000..112b7ecd Binary files /dev/null and b/Next-JS-Projects/Advanced/Attendance-Tracking-App/screenshot.webp differ diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Calendar/Calendar.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Calendar/Calendar.tsx new file mode 100644 index 00000000..82e2f774 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Calendar/Calendar.tsx @@ -0,0 +1,25 @@ +"use client"; +import React, { useState } from "react"; +import Calendar from "react-calendar"; +import "react-calendar/dist/Calendar.css"; + +type ValuePiece = Date | null; + +type Value = ValuePiece | [ValuePiece, ValuePiece]; + +const CalendarComponent = () => { + const [value, onChange] = useState(new Date()); + return ( +
+
+ +
+
+ ); +}; + +export default CalendarComponent; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Footer.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Footer.tsx new file mode 100644 index 00000000..61bd6921 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Footer.tsx @@ -0,0 +1,56 @@ +"use client"; +import React, { useEffect } from "react"; +import HomeSvg from "../svg-components/HomeSvg"; +import { usePathname } from "next/navigation"; + +const Footer = () => { + const [active_class, setActiveClass] = React.useState(2); + const pathname = usePathname(); + const getActiveClass = () => { + if (pathname.includes("calendar")) { + return 1; + } else if (pathname.includes("settings")) { + return 3; + } else { + return 2; + } + }; + useEffect(() => { + setActiveClass(getActiveClass()); + }, []); + + return ( + + ); +}; + +export default Footer; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/EditModal.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/EditModal.tsx new file mode 100644 index 00000000..4281ac8c --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/EditModal.tsx @@ -0,0 +1,122 @@ +import React from "react"; +import { useState } from "react"; +interface EditModalProps { + isOpen: boolean; + setOpen: React.Dispatch>; + subject: { + attended: number; + total: number; + Sub_name: string; + setPresent: (present: number) => void; + setTotal: (total: number) => void; + changeName: (newName: string) => void; + }; +} + +const EditModal: React.FC = ({ + isOpen, + setOpen, + subject: { attended, total, Sub_name, setPresent, setTotal, changeName }, +}) => { + const [details, setDetails] = useState({ + attended: attended, + total: total, + Sub_name: Sub_name, + }); + const saveEdit = () => { + if (!details.attended) { + details.attended = 0; + } + if (!details.total) { + details.total = details.attended; + } + if (details.attended > details.total) { + details.total = details.attended; + } + setTotal(details.total); + setPresent(details.attended); + changeName(details.Sub_name); + setOpen(false); + }; + if (!isOpen) return null; + + return ( +
+
+

Edit Subject

+
+ + + setDetails({ ...details, Sub_name: e.target.value }) + } + className="mt-1 p-2 border border-border rounded w-full text-black" + /> +
+
+ + + setDetails({ + ...details, + total: Math.max(0, parseInt(e.target.value)), + }) + } + value={details.total} + className="mt-1 p-2 border border-border rounded w-full text-black" + /> +
+
+ + { + setDetails({ + ...details, + attended: Math.max( + 0, + Math.min(details.total, parseInt(e.target.value)) + ), + }); + }} + className="mt-1 p-2 border border-border rounded w-full text-black" + /> +
+ +
+ + +
+
+
+ ); +}; + +export default EditModal; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/Home.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/Home.tsx new file mode 100644 index 00000000..70f88fd5 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/Home.tsx @@ -0,0 +1,292 @@ +"use client"; +import SubCard from "./SubCard"; +import React, { useState, useEffect } from "react"; +import Modal from "./Modal"; +import Navbar from "@/Components/Navbar"; +import Footer from "@/Components/Footer"; +import { collection, doc, setDoc } from "firebase/firestore"; +import { db } from "../../../firebase/config"; +import { ToastContainer, toast } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; + +interface SubjectDetail { + attended: number; + total: number; + Sub_name: string; + target_percentage: number; +} +const initialSubjectDetails: SubjectDetail[] = [ + // { + // attended: 4, + // total: 6, + // Sub_name: "DSA", + // target_percentage: 75, + // }, + // { + // attended: 9, + // total: 9, + // Sub_name: "DASA", + // target_percentage: 75, + // }, +]; + +interface HomeProps { + logout: () => void; + user: any; + userData: any; +} + +const Home: React.FC = ({ logout, user, userData }) => { + const [toggle, setToggle] = useState(false); + const [hasChanges, setHasChanges] = useState(false); + + const [isOpen, setIsOpen] = useState(false); + const [isData, setIsData] = useState(false); + const [subjectDetails, setSubjectDetails] = useState(initialSubjectDetails); + useEffect(() => { + if (userData) { + setSubjectDetails(userData.Attendance || initialSubjectDetails); + } + }, [userData]); + + useEffect(() => { + if (subjectDetails.length === 0) { + setIsData(false); + } else { + setIsData(true); + } + }, [subjectDetails]); + + const updateUserAttendance = async () => { + try { + const userDocRef = doc(db, "users", user.uid); + await setDoc(userDocRef, { Attendance: subjectDetails }, { merge: true }); + toast.success("Attendance updated!", { + position: "top-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + setHasChanges(false); + } catch (error) { + toast.error("Failed to update details!", { + position: "top-center", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + } + }; + + const markPresent = (index: number) => { + const newDetails = [...subjectDetails]; + newDetails[index].attended += 1; + newDetails[index].total += 1; + setSubjectDetails(newDetails); + setHasChanges(true); + }; + + const markAbsent = (index: number) => { + const newDetails = [...subjectDetails]; + newDetails[index].total += 1; + setSubjectDetails(newDetails); + setHasChanges(true); + }; + const changeName = (index: number, newName: string) => { + const newDetails = [...subjectDetails]; + newDetails[index].Sub_name = newName; + setSubjectDetails(newDetails); + setHasChanges(true); + }; + const setTotal = (index: number, total: number) => { + const newDetails = [...subjectDetails]; + newDetails[index].total = total; + setSubjectDetails(newDetails); + setHasChanges(true); + }; + const setPresents = (index: number, present: number) => { + const newDetails = [...subjectDetails]; + newDetails[index].attended = present; + setSubjectDetails(newDetails); + setHasChanges(true); + }; + const addSubjectDetail = (newDetail: { + Sub_name: string; + target_percentage: number; + attended: number; + total: number; + }) => { + const newDetails = [...subjectDetails, newDetail]; + setSubjectDetails(newDetails); + setHasChanges(true); + }; + const deleteSubject = (index: number) => { + const newDetails = [...subjectDetails]; + newDetails.splice(index, 1); + setSubjectDetails(newDetails); + setHasChanges(true); + }; + return ( +
+ + +
+ {hasChanges && ( + + )} + addSubjectDetail(newDetail)} + /> + {!isData ? ( +
+

No records to display

+
+ +

+ Click on Add button to add a new subject +

+
+
+ ) : ( +
+ {subjectDetails.map((subject: any, index: any) => ( + markPresent(index)} + markAbsent={() => markAbsent(index)} + changeName={(newName: string) => changeName(index, newName)} + setTotal={(total: number) => setTotal(index, total)} + setPresent={(present: number) => setPresents(index, present)} + deleteSubject={() => deleteSubject(index)} + /> + ))} + +
+ +
+
+ )} +
+
+
+ ); +}; + +export default Home; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/Modal.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/Modal.tsx new file mode 100644 index 00000000..3b343ad5 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/Modal.tsx @@ -0,0 +1,117 @@ +import React from "react"; +import { useState } from "react"; +interface ModalProps { + isOpen: boolean; + setOpen: React.Dispatch>; + addSubject: (newDetail: { + Sub_name: string; + target_percentage: number; + attended: number; + total: number; + }) => void; +} + +const Modal: React.FC = ({ isOpen, setOpen, addSubject }) => { + const initialDetails = { + attended: 0, + total: 0, + Sub_name: "", + target_percentage: 75, + }; + const [details, setDetails] = useState(initialDetails); + const addSub = () => { + if (!details.attended) { + details.attended = 0; + } + if (!details.total) { + details.total = details.attended; + } + if (details.attended > details.total) { + details.total = details.attended; + } + addSubject(details); + setDetails(initialDetails); + setOpen(false); + }; + if (!isOpen) return null; + + return ( +
+
+

Add Subject

+
+ + + setDetails({ ...details, Sub_name: e.target.value }) + } + placeholder="Eg. Data structures" + className="mt-1 p-2 border border-black rounded w-full text-black" + /> +
+
+ + + setDetails({ + ...details, + total: Math.max(0, parseInt(e.target.value)), + }) + } + className="mt-1 p-2 border border-black rounded w-full text-black" + /> +
+
+ + { + setDetails({ + ...details, + attended: Math.max( + 0, + Math.min(details.total, parseInt(e.target.value)) + ), + }); + }} + className="mt-1 p-2 border border-black rounded w-full text-black" + /> +
+ +
+ + +
+
+
+ ); +}; + +export default Modal; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/SubCard.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/SubCard.tsx new file mode 100644 index 00000000..27f712f4 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/SubCard.tsx @@ -0,0 +1,164 @@ +"use client"; +import { useState, useEffect } from "react"; +import React from "react"; +import "./percentages.css"; +import EditModal from "./EditModal"; + +interface SubCardProps { + Sub_name: string; + attended: number; + total: number; + target_percentage: number; + markPresent: () => void; + markAbsent: () => void; + changeName: (newName: string) => void; + setPresent: (present: number) => void; + setTotal: (total: number) => void; + deleteSubject: () => void; +} +interface CustomStyle extends React.CSSProperties { + "--bg-color"?: string; + "--circle-percentage"?: string; +} + +const SubCard: React.FC = ({ + attended, + total, + Sub_name, + target_percentage, + markPresent, + markAbsent, + changeName, + setTotal, + setPresent, + deleteSubject, +}) => { + const [isOpen, setIsOpen] = useState(false); + let percentage = + total > 0 ? Number(((attended / total) * 100).toFixed(1)) : 100; + let Status = + target_percentage == percentage + ? "On the Edge!" + : target_percentage > percentage + ? "Low Attendance!!" + : "On track"; + let card_color = + target_percentage == percentage + ? "#006D90" + : target_percentage > percentage + ? "#892B2B" + : "#1A5F18"; + let degs = (percentage / 100) * 360; + const customStyle: CustomStyle = { + "--bg-color": card_color, + "--circle-percentage": `${degs}deg`, + }; + + const editModal = () => { + setIsOpen(true); + }; + + return ( +
+ + + +
+
+
+

{Sub_name}

+
+
+

+ Attendance{" "} + + {attended}/{total} + +

+

Status: {Status}

+
+
+
+
+ {/*
+ 100.0% +
*/} +

+ {percentage}% +

+
+
+ + + +
+
+
+ ); +}; + +export default SubCard; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/percentages.css b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/percentages.css new file mode 100644 index 00000000..394fb279 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Home/percentages.css @@ -0,0 +1,22 @@ +:root { + --bg-color: rgb(188, 0, 0); + --circle-percentage: 100deg; +} +.circular-progress { + border-radius: 50%; + background: conic-gradient(#0093a7 var(--circle-percentage), #171721 0deg); + display: flex; + align-items: center; + justify-content: center; + transition: all 0.5s ease; +} + +.circular-progress::before { + content: ""; + position: absolute; + height: 85%; + width: 85%; + border-radius: 50%; + background-color: var(--bg-color); + transition: all 0.5s ease; +} diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Landing.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Landing.tsx new file mode 100644 index 00000000..cb5c6193 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Landing.tsx @@ -0,0 +1,104 @@ +import React from "react"; +// import Logo from "../svg-components/logo"; +import Image from "next/image"; + +interface LandingProps { + login: () => void; +} + +const Landing: React.FC = ({ login }) => { + return ( +
+
+ logo +
+ top-float +
+
Attendance Tracker
+

+ Never Miss an Important Class, Keep Track of your Schedule +

+
+ + + bottom-float +
+ ); +}; + +export default Landing; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Navbar.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Navbar.tsx new file mode 100644 index 00000000..25580d98 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Navbar.tsx @@ -0,0 +1,46 @@ +"use client"; +import React from "react"; +import { useState } from "react"; + +const Navbar = () => { + const [toggle, setToggle] = useState(false); + return ( + + ); +}; + +export default Navbar; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/ActivityCard.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/ActivityCard.tsx new file mode 100644 index 00000000..412f9495 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/ActivityCard.tsx @@ -0,0 +1,52 @@ +import React from "react"; + +interface ActivityCardProps { + attended: number; + total: number; + sub_name: string; +} + +const ActivityCard: React.FC = ({ + attended, + total, + sub_name, +}) => { + return ( +
+
+
+
+ {sub_name} +
+
+
+ Attendance {attended}/{total} +
+
+ + + + + + +
+
+ ); +}; + +export default ActivityCard; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/ReportCard.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/ReportCard.tsx new file mode 100644 index 00000000..2b5a827f --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/ReportCard.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const ReportCard = () => { + return
ReportCard
; +}; + +export default ReportCard; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/Settings.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/Settings.tsx new file mode 100644 index 00000000..34b3e3d3 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/Settings.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import ActivityCard from "./ActivityCard"; +const Settings = () => { + return ( +
+ profile +

+ User Profile +

+
+

Your Activity

+ + + {/* */} + + +
+ Log Out? +
+
+
+ ); +}; + +export default Settings; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/Stamps.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/Stamps.tsx new file mode 100644 index 00000000..4a485f43 --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/Components/Settings/Stamps.tsx @@ -0,0 +1,23 @@ +import React from "react"; + +interface StampsProps { + color: string; +} + +const Stamps: React.FC = ({ color }) => { + return ( +
+

14/06/2024

+

20:40:01

+

+ Present + +

+
+ ); +}; + +export default Stamps; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/svg-components/HomeSvg.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/svg-components/HomeSvg.tsx new file mode 100644 index 00000000..22c126fd --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/svg-components/HomeSvg.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +const HomeSvg = () => { + return ( + + + + ); +}; + +export default HomeSvg; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/svg-components/Logo.tsx b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/svg-components/Logo.tsx new file mode 100644 index 00000000..a21448fe --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/src/svg-components/Logo.tsx @@ -0,0 +1,64 @@ + +import React from "react"; +const Logo = () => { + return ( +
+ + + + + + + + + + + + + + +
+ );}; +export default Logo; \ No newline at end of file diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/tailwind.config.ts b/Next-JS-Projects/Advanced/Attendance-Tracking-App/tailwind.config.ts new file mode 100644 index 00000000..165abe2d --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/tailwind.config.ts @@ -0,0 +1,25 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/Components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/svg-components/**/*.{js,ts,jsx,tsx,mdx}", + "./app/**/*.{js,ts,jsx,tsx,mdx}", + "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + backgroundImage: { + "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", + "gradient-conic": + "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", + }, + fontFamily: { + poppins: ["Poppins", "sans-serif"], + }, + }, + }, + plugins: [], +}; +export default config; diff --git a/Next-JS-Projects/Advanced/Attendance-Tracking-App/tsconfig.json b/Next-JS-Projects/Advanced/Attendance-Tracking-App/tsconfig.json new file mode 100644 index 00000000..a00f3b0e --- /dev/null +++ b/Next-JS-Projects/Advanced/Attendance-Tracking-App/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/page.js"], + "exclude": ["node_modules"] +} diff --git a/Next-JS-Projects/README.md b/Next-JS-Projects/README.md index 95e86213..b3493ac8 100644 --- a/Next-JS-Projects/README.md +++ b/Next-JS-Projects/README.md @@ -28,6 +28,7 @@ | 1 | [Weather-Website](./Basic/Weather-Website) | ![Basic](https://img.shields.io/badge/Basic-00FF00?style=for-the-badge) | | 2 | [Quiz-App](./Intermediate/Quiz-App) | ![Intermediate](https://img.shields.io/badge/Intermediate-FFD700?style=for-the-badge) | | 3 | [Breaking-News-App](./Advanced/Breaking-News-App/) | ![Advanced](https://img.shields.io/badge/Advanced-FF0000?style=for-the-badge) | +| 4 | [Attendance-Tracking-App](./Advanced/Attendance-Tracking-App/) | ![Advanced](https://img.shields.io/badge/Advanced-FF0000?style=for-the-badge) | @@ -37,4 +38,4 @@

Show some  â¤ī¸  by  đŸŒŸ  this repository!

- + \ No newline at end of file