├── client ├── src │ ├── App.css │ ├── vite-env.d.ts │ ├── lib │ │ └── utils.ts │ ├── components │ │ ├── ScrollToTop.tsx │ │ ├── ErrorPage.tsx │ │ ├── ui │ │ │ ├── label.tsx │ │ │ ├── input.tsx │ │ │ ├── sonner.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── button.tsx │ │ │ ├── card.tsx │ │ │ ├── table.tsx │ │ │ ├── dialog.tsx │ │ │ ├── sheet.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── select.tsx │ │ │ └── dropdown-menu.tsx │ │ ├── CheckboxDemo.tsx │ │ ├── ModeToggle.tsx │ │ ├── TeachersAbsent.tsx │ │ ├── Login.tsx │ │ ├── VacantRooms.tsx │ │ ├── Footer.tsx │ │ ├── TeachersAbsentAdmin.tsx │ │ ├── CardWithForm.tsx │ │ ├── Timetable.tsx │ │ ├── BookRoom.tsx │ │ ├── TeacherRegister.tsx │ │ └── AddRoom.tsx │ ├── main.tsx │ ├── App.tsx │ ├── assets │ │ ├── logo.svg │ │ └── 404.svg │ ├── context │ │ ├── ThemeProvider.tsx │ │ ├── TimetableProvider.tsx │ │ └── RoomProvider.tsx │ └── index.css ├── .env.production ├── postcss.config.js ├── .env.sample ├── tsconfig.node.json ├── vite.config.ts ├── components.json ├── index.html ├── public │ ├── twitter.svg │ └── logo.svg ├── .eslintrc.cjs ├── tsconfig.json ├── package.json └── tailwind.config.js ├── image.png ├── .env.sample ├── server ├── index.ts ├── src │ ├── utils │ │ ├── asyncHandler.ts │ │ ├── ApiResponse.ts │ │ └── ApiError.ts │ ├── routes │ │ ├── teachersabsent.route.ts │ │ ├── timetable.route.ts │ │ ├── user.route.ts │ │ └── room.route.ts │ ├── db │ │ └── index.ts │ ├── models │ │ ├── room.model.ts │ │ ├── absentteacher.model.ts │ │ ├── timetable.model.ts │ │ ├── user.model.ts │ │ └── occupiedroom.model.ts │ ├── middlewares │ │ ├── error.middleware.ts │ │ └── auth.middleware.ts │ └── controllers │ │ ├── teachersabsent.controller.ts │ │ ├── bookroom.controller.ts │ │ ├── timetable.controller.ts │ │ ├── user.controller.ts │ │ └── room.controller.ts └── app.ts ├── .gitignore ├── LICENSE ├── package.json ├── pull-request.md ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── documentation_update.yml │ └── feature_request.yml ├── README.md ├── contributing.md ├── CODE_OF_CONDUCT.md └── Learn.md /client/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Salvelop07/Campuspace/HEAD/image.png -------------------------------------------------------------------------------- /client/.env.production: -------------------------------------------------------------------------------- 1 | VITE_COLLEGE_NAME='ARSD College' 2 | VITE_TWITTER= 3 | VITE_GITHUB= 4 | VITE_LINKEDIN= 5 | VITE_LOGO= -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/.env.sample: -------------------------------------------------------------------------------- 1 | VITE_COLLEGE_NAME=YOUR_COLLEGE_NAME 2 | VITE_TWITTER=YOUR_TWITTER_LINK 3 | VITE_GITHUB=YOUR_GITHUB_LINK 4 | VITE_LINKEDIN=YOUR_LINKEDIN_LINK 5 | VITE_LOGO=YOUR_LOGO -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | MONGODB_URI="YOUR_MONGO_URI" #"mongodb://127.0.0.1:27017/" 2 | PORT=PORT_NO 3 | ACCESS_TOKEN_SECRET="YOUR_SECRET_TOKEN" 4 | NODE_ENV="production" # Expected value "production". 5 | -------------------------------------------------------------------------------- /client/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /client/src/components/ScrollToTop.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | 4 | export default function ScrollToTop() { 5 | const { pathname } = useLocation(); 6 | 7 | useEffect(() => { 8 | window.scrollTo(0, 0); 9 | }, [pathname]); 10 | 11 | return null; 12 | } -------------------------------------------------------------------------------- /server/index.ts: -------------------------------------------------------------------------------- 1 | import connectDB from "./src/db"; 2 | import app from "./app"; 3 | 4 | const port = process.env.PORT || 5000; 5 | 6 | connectDB() 7 | .then(() => 8 | app.listen(port, () => console.log(`Server is running on port ${port}`)) 9 | ) 10 | .catch((err) => console.log("MONGODB_CONNECTION_ERROR !!!!", err)); 11 | -------------------------------------------------------------------------------- /server/src/utils/asyncHandler.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | 3 | const asyncHandler = (requestHandler: Function) => { 4 | return (req: Request, res: Response, next: NextFunction) => { 5 | Promise.resolve(requestHandler(req, res, next)).catch((err) => next(err)); 6 | }; 7 | }; 8 | 9 | export { asyncHandler }; 10 | -------------------------------------------------------------------------------- /client/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import path from 'path' 4 | 5 | export default defineConfig({ 6 | plugins: [react()], 7 | resolve: { 8 | alias: { 9 | "@": path.resolve(__dirname, "./src"), 10 | }, 11 | }, 12 | server: { 13 | proxy: { 14 | // "/api/v1": "http://localhost:3000" 15 | } 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /client/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Campuspace 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /server/src/routes/teachersabsent.route.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { 3 | addTeachersAbsent, 4 | getTeachersAbsent, 5 | } from "../controllers/teachersabsent.controller"; 6 | import verifyJWT from "../middlewares/auth.middleware"; 7 | 8 | const router = Router(); 9 | 10 | router.route("/").get(getTeachersAbsent); 11 | 12 | router.route("/new").post(verifyJWT, addTeachersAbsent); 13 | 14 | export default router; 15 | -------------------------------------------------------------------------------- /server/src/utils/ApiResponse.ts: -------------------------------------------------------------------------------- 1 | interface ApiResponse { 2 | statusCode: number; 3 | data: object; 4 | message: String; 5 | success: Boolean; 6 | } 7 | 8 | class ApiResponse { 9 | constructor(statusCode: number, data: object, message = "Success") { 10 | this.statusCode = statusCode; 11 | this.data = data; 12 | this.message = message; 13 | this.success = this.statusCode < 400; 14 | } 15 | } 16 | 17 | export { ApiResponse }; 18 | -------------------------------------------------------------------------------- /client/public/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /server/src/db/index.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const connectDB = async () => { 4 | try { 5 | const connectionInstance = await mongoose.connect( 6 | `${process.env.MONGODB_URI as string}/${ 7 | process.env.COLLEGE_NAME?.replace(/\s/g, "") ?? "Campuspace" 8 | }` 9 | ); 10 | console.log("MongoDb Connected:", connectionInstance.connection.host); 11 | } catch (err) { 12 | console.log("MONGODB_CONNECTION_ERROR !!!!", err); 13 | process.exit(1); 14 | } 15 | }; 16 | 17 | export default connectDB; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .env.local 4 | nodemon.json 5 | dist 6 | 7 | # Logs 8 | client/logs 9 | client/*.log 10 | client/npm-debug.log* 11 | client/yarn-debug.log* 12 | client/yarn-error.log* 13 | client/pnpm-debug.log* 14 | client/lerna-debug.log* 15 | 16 | client/node_modules 17 | client/dist 18 | client/dist-ssr 19 | client/*.local 20 | 21 | # Editor directories and files 22 | client/.vscode/* 23 | client/!.vscode/extensions.json 24 | client/.idea 25 | client/.DS_Store 26 | client/*.suo 27 | client/*.ntvs* 28 | client/*.njsproj 29 | client/*.sln 30 | client/*.sw? 31 | -------------------------------------------------------------------------------- /client/src/components/ErrorPage.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | import errorImage from "../assets/404.svg"; 3 | import { Button } from "./ui/button"; 4 | 5 | function ErrorPage() { 6 | return ( 7 |
8 | 9 | 10 | 13 | 14 |
15 | ); 16 | } 17 | 18 | export default ErrorPage; 19 | -------------------------------------------------------------------------------- /server/src/routes/timetable.route.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import verifyJWT from "../middlewares/auth.middleware"; 3 | import { 4 | addTimetable, 5 | deleteTimetable, 6 | getAllTimetables, 7 | getCourses, 8 | getTimetable, 9 | } from "../controllers/timetable.controller"; 10 | 11 | const router = Router(); 12 | 13 | router.route("/courses").get(getCourses); 14 | 15 | router.route("/").get(getTimetable); 16 | 17 | router.route("/new").post(verifyJWT, addTimetable); 18 | 19 | router.route("/delete/:timetableId").delete(verifyJWT, deleteTimetable); 20 | 21 | router.route("/all").get(verifyJWT, getAllTimetables); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /server/src/models/room.model.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | interface RoomInterface extends mongoose.Document { 4 | roomNumber: string; 5 | capacity: number; 6 | location: number; 7 | } 8 | 9 | const roomSchema = new mongoose.Schema( 10 | { 11 | roomNumber: { 12 | type: String, 13 | required: true, 14 | unique: true, 15 | }, 16 | capacity: { 17 | type: Number, 18 | required: true, 19 | }, 20 | location: { 21 | type: String, 22 | required: true, 23 | }, 24 | }, 25 | { 26 | timestamps: true, 27 | } 28 | ); 29 | 30 | const Room = mongoose.model("room", roomSchema); 31 | 32 | export { Room, RoomInterface }; 33 | -------------------------------------------------------------------------------- /server/src/routes/user.route.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { 3 | changeAdmin, 4 | deleteTeacher, 5 | getCurrentUser, 6 | getTeachers, 7 | loginUser, 8 | registerUser, 9 | } from "../controllers/user.controller"; 10 | import verifyJWT from "../middlewares/auth.middleware"; 11 | 12 | const router = Router(); 13 | 14 | router.route("/").get(getTeachers); 15 | 16 | router.route("/login").post(loginUser); 17 | 18 | router.route("/get").get(verifyJWT, getCurrentUser) 19 | 20 | router.route("/register").post(verifyJWT, registerUser); 21 | 22 | router.route("/admin/:teacherId").patch(verifyJWT, changeAdmin); 23 | 24 | router.route("/delete/:teacherId").delete(verifyJWT, deleteTeacher); 25 | 26 | export default router; -------------------------------------------------------------------------------- /server/src/utils/ApiError.ts: -------------------------------------------------------------------------------- 1 | interface ApiError { 2 | statusCode: number; 3 | message: string; 4 | data: null; 5 | success: boolean; 6 | errors: string[]; 7 | stack?: string; 8 | } 9 | 10 | class ApiError extends Error { 11 | constructor( 12 | statusCode: number, 13 | message = "Something went wrong!", 14 | errors: string[] = [], 15 | stack = "" 16 | ) { 17 | super(message); 18 | this.statusCode = statusCode; 19 | this.message = message; 20 | this.success = false; 21 | this.data = null; 22 | this.errors = errors; 23 | if (stack) { 24 | this.stack = stack; 25 | } else { 26 | Error.captureStackTrace(this, this.constructor); 27 | } 28 | } 29 | } 30 | 31 | export { ApiError }; 32 | -------------------------------------------------------------------------------- /server/src/models/absentteacher.model.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | interface TeacherAbsentInterface extends mongoose.Document { 4 | teacher: string; 5 | day: string; 6 | } 7 | 8 | const teacherAbsentSchema = new mongoose.Schema( 9 | { 10 | teacher: { 11 | type: mongoose.Schema.Types.ObjectId, 12 | ref: "user", 13 | required: true 14 | }, 15 | day: { 16 | type: String, 17 | required: true, 18 | trim: true, 19 | }, 20 | createdAt: { 21 | type: Date, 22 | expires: 86400, 23 | default: Date.now 24 | } 25 | } 26 | ); 27 | 28 | const TeacherAbsent = mongoose.model( 29 | "absentteacher", 30 | teacherAbsentSchema 31 | ); 32 | 33 | export { TeacherAbsentInterface, TeacherAbsent }; 34 | -------------------------------------------------------------------------------- /client/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as LabelPrimitive from "@radix-ui/react-label" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const labelVariants = cva( 8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 9 | ) 10 | 11 | const Label = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef & 14 | VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 21 | )) 22 | Label.displayName = LabelPrimitive.Root.displayName 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "baseUrl": ".", 9 | "paths": { 10 | "@/*": [ 11 | "./src/*" 12 | ] 13 | }, 14 | 15 | /* Bundler mode */ 16 | "moduleResolution": "bundler", 17 | "allowImportingTsExtensions": true, 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx", 22 | 23 | /* Linting */ 24 | "strict": true, 25 | "noUnusedLocals": true, 26 | "noUnusedParameters": true, 27 | "noFallthroughCasesInSwitch": true 28 | }, 29 | "include": ["src"], 30 | "references": [{ "path": "./tsconfig.node.json" }] 31 | } 32 | -------------------------------------------------------------------------------- /server/src/routes/room.route.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { addRooms, deleteRoom, getRooms, getVacantRooms, updateRoom } from "../controllers/room.controller"; 3 | import verifyJWT from "../middlewares/auth.middleware"; 4 | import { bookRoom, getBookedRooms, unbookRoom } from "../controllers/bookroom.controller"; 5 | 6 | const router = Router(); 7 | 8 | router.route("/").get(getRooms); 9 | 10 | router.route("/vacant").get(getVacantRooms) 11 | 12 | router.use(verifyJWT); 13 | 14 | router.route("/new").post(addRooms); 15 | 16 | router.route("/book").post(bookRoom); 17 | 18 | router.route("/unbook/:bookingId").delete(unbookRoom); 19 | 20 | router.route("/getbooked").get(getBookedRooms); 21 | 22 | router.route("/delete/:roomId").delete(deleteRoom); 23 | 24 | router.route("/update").put(updateRoom); 25 | 26 | export default router; 27 | -------------------------------------------------------------------------------- /client/src/components/CheckboxDemo.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox } from "@/components/ui/checkbox"; 2 | 3 | export function CheckboxDemo({ 4 | text, 5 | value, 6 | name, 7 | handleChange, 8 | title, 9 | }: { 10 | text: string; 11 | value: string; 12 | name: string; 13 | title?: string; 14 | handleChange: (e: boolean, value: string) => void; 15 | }) { 16 | return ( 17 |
18 | handleChange(e, value)} 22 | name={name} 23 | /> 24 | 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /client/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /client/src/main.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom/client"; 2 | import App from "./App.tsx"; 3 | import "./index.css"; 4 | import { BrowserRouter } from "react-router-dom"; 5 | import { ThemeProvider } from "@/context/ThemeProvider.tsx"; 6 | import ScrollToTop from "./components/ScrollToTop.tsx"; 7 | import { UserProvider } from "./context/UserProvider.tsx"; 8 | import { TimetableProvider } from "./context/TimetableProvider.tsx"; 9 | import { RoomProvider } from "./context/RoomProvider.tsx"; 10 | 11 | ReactDOM.createRoot(document.getElementById("root")!).render( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /client/src/components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from "next-themes" 2 | import { Toaster as Sonner } from "sonner" 3 | 4 | type ToasterProps = React.ComponentProps 5 | 6 | const Toaster = ({ ...props }: ToasterProps) => { 7 | const { theme = "system" } = useTheme() 8 | 9 | return ( 10 | 26 | ) 27 | } 28 | 29 | export { Toaster } 30 | -------------------------------------------------------------------------------- /server/src/middlewares/error.middleware.ts: -------------------------------------------------------------------------------- 1 | import { ApiError } from "../utils/ApiError"; 2 | import { Error } from "mongoose"; 3 | import { Request, Response, NextFunction } from "express"; 4 | 5 | interface ApiErrorInterface { 6 | statusCode: number; 7 | message: string; 8 | data: null; 9 | success: boolean; 10 | errors: string[]; 11 | stack?: string; 12 | } 13 | 14 | const errorHandler = ( 15 | err: ApiErrorInterface, 16 | _: Request, 17 | res: Response, 18 | next: NextFunction 19 | ) => { 20 | let error = err; 21 | 22 | if (!(error instanceof ApiError)) { 23 | const statusCode = error.statusCode || Error ? 400 : 500; 24 | const message = error.message || "Something went wrong!"; 25 | 26 | error = new ApiError(statusCode, message, error?.errors || [], err?.stack); 27 | } 28 | 29 | const response = { 30 | ...error, 31 | message: error.message, 32 | ...(process.env.NODE_ENV === "development" ? { stack: error.stack } : {}), 33 | }; 34 | 35 | return res.status(error.statusCode).json(response); 36 | }; 37 | 38 | export default errorHandler; 39 | -------------------------------------------------------------------------------- /server/src/middlewares/auth.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from "express"; 2 | import jwt, { JwtPayload } from "jsonwebtoken"; 3 | import { ApiError } from "../utils/ApiError"; 4 | import { User } from "../models/user.model"; 5 | 6 | const verifyJWT = async (req: Request, _: Response, next: NextFunction) => { 7 | try { 8 | const token = req.header("Authorization")?.replace("Bearer ", ""); 9 | 10 | if (!token) { 11 | throw new ApiError(400, "Token is required"); 12 | } 13 | 14 | const decodedToken = (await jwt.verify( 15 | token, 16 | process.env.ACCESS_TOKEN_SECRET as string 17 | )) as JwtPayload; 18 | 19 | if (!decodedToken?._id) { 20 | throw new ApiError(401, "Invalid token"); 21 | } 22 | 23 | const user = await User.findById(decodedToken._id).select("-password"); 24 | 25 | if (!user) { 26 | throw new ApiError(400, "User not found"); 27 | } 28 | 29 | req.user = user; 30 | 31 | next(); 32 | } catch (error) { 33 | next(error); 34 | } 35 | }; 36 | 37 | export default verifyJWT; 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Shivam Soni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /client/src/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox" 3 | import { CheckIcon } from "@radix-ui/react-icons" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const Checkbox = React.forwardRef< 8 | React.ElementRef, 9 | React.ComponentPropsWithoutRef 10 | >(({ className, ...props }, ref) => ( 11 | 19 | 22 | 23 | 24 | 25 | )) 26 | Checkbox.displayName = CheckboxPrimitive.Root.displayName 27 | 28 | export { Checkbox } 29 | -------------------------------------------------------------------------------- /server/src/models/timetable.model.ts: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | interface TimeTableInterface extends mongoose.Document { 4 | course: string; 5 | semester: number; 6 | stream: string; 7 | classes: { 8 | allotedRoom: string; 9 | allotedTime: string; 10 | teacher: string; 11 | paperId: number; 12 | subject: string; 13 | day: string; 14 | }[]; 15 | createdAt: string; 16 | updatedAt: string; 17 | } 18 | 19 | const timeTableSchema = new mongoose.Schema({ 20 | course: { 21 | type: String, 22 | required: true, 23 | }, 24 | stream: { 25 | type: String, 26 | required: true, 27 | }, 28 | semester: { 29 | type: Number, 30 | required: true, 31 | }, 32 | classes: [ 33 | { 34 | allotedRoom: { 35 | type: mongoose.Schema.Types.ObjectId, 36 | ref: "room", 37 | }, 38 | allotedTime: String, 39 | teacher: { 40 | type: mongoose.Schema.Types.ObjectId, 41 | ref: "user", 42 | }, 43 | paperId: Number, 44 | subject: String, 45 | day: String, 46 | }, 47 | ] 48 | }); 49 | 50 | const TimeTable = mongoose.model("TimeTable", timeTableSchema); 51 | 52 | export { TimeTable, TimeTableInterface }; 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "college-project", 3 | "version": "1.0.0", 4 | "description": "Campuspace is a MERN stack project with TypeScript & Tailwind CSS. Admins manage timetables, teacher absences, & rooms. Students & teachers view timetables, vacant rooms, & teacher absences. Teachers can book rooms for classes. Efficient college resource management.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npm i --include=dev && npx tsc && cd client && npm i --include=dev && npm run build", 8 | "dev": "nodemon server/index.ts", 9 | "start": "node --env-file=.env dist/index.js" 10 | }, 11 | "keywords": [ 12 | "express", 13 | "typescript", 14 | "mongoose", 15 | "axios", 16 | "bcrypt", 17 | "mern", 18 | "jsonwebtoken", 19 | "tailwindcss", 20 | "shadcn-ui" 21 | ], 22 | "author": "", 23 | "license": "ISC", 24 | "dependencies": { 25 | "bcrypt": "^5.1.1", 26 | "express": "^4.18.3", 27 | "jsonwebtoken": "^9.0.2", 28 | "mongoose": "^8.2.1" 29 | }, 30 | "devDependencies": { 31 | "@types/bcrypt": "^5.0.2", 32 | "@types/express": "^4.17.21", 33 | "@types/jsonwebtoken": "^9.0.6", 34 | "@types/node": "^20.11.24", 35 | "@types/nodemon": "^1.19.6", 36 | "nodemon": "^3.1.0", 37 | "ts-node": "^10.9.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /client/src/components/ModeToggle.tsx: -------------------------------------------------------------------------------- 1 | import { Moon, Sun } from "lucide-react" 2 | 3 | import { Button } from "@/components/ui/button" 4 | import { 5 | DropdownMenu, 6 | DropdownMenuContent, 7 | DropdownMenuItem, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu" 10 | import { useTheme } from "@/context/ThemeProvider" 11 | 12 | export function ModeToggle() { 13 | const { setTheme } = useTheme() 14 | 15 | return ( 16 | 17 | 18 | 23 | 24 | 25 | setTheme("light")}> 26 | Light 27 | 28 | setTheme("dark")}> 29 | Dark 30 | 31 | setTheme("system")}> 32 | System 33 | 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /server/app.ts: -------------------------------------------------------------------------------- 1 | import express, { Request, Response } from "express"; 2 | import errorHandler from "./src/middlewares/error.middleware"; 3 | import { UserInterface } from "./src/models/user.model"; 4 | import path from "path"; 5 | 6 | const app = express(); 7 | 8 | declare module "express" { 9 | interface Request { 10 | user?: UserInterface; 11 | } 12 | } 13 | 14 | app.use(express.json({ limit: "5mb" })); 15 | app.use(express.urlencoded({ extended: true, limit: "5mb" })); 16 | app.use(express.static("public")); 17 | 18 | import userRouter from "./src/routes/user.route"; 19 | import timtableRouter from "./src/routes/timetable.route"; 20 | import teachersAbsentRouter from "./src/routes/teachersabsent.route"; 21 | import roomRouter from "./src/routes/room.route"; 22 | 23 | app.use("/api/v1/users", userRouter); 24 | app.use("/api/v1/timetable", timtableRouter); 25 | app.use("/api/v1/teachersabsent", teachersAbsentRouter); 26 | app.use("/api/v1/room", roomRouter); 27 | 28 | const __dirname1 = path.resolve(); 29 | 30 | if (process.env.NODE_ENV === "production") { 31 | app.use(express.static(path.join(__dirname1, "client", "dist"))); 32 | app.get("*", (_: Request, res: Response) => { 33 | res.sendFile(path.resolve(__dirname1, "client", "dist", "index.html")); 34 | }); 35 | } else { 36 | app.get("/", (_: Request, res: Response) => { 37 | res.send("App is under development!"); 38 | }); 39 | } 40 | 41 | app.use(errorHandler); 42 | 43 | export default app; -------------------------------------------------------------------------------- /pull-request.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | 4 | 5 | Fixes #(issue) 6 | 7 | ### Type of Change 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Documentation update 13 | 14 | ### How Has This Been Tested? 15 | 16 | 17 | 18 | - [ ] Test A 19 | - [ ] Test B 20 | 21 | **Test Configuration**: 22 | * Firmware version: 23 | * Hardware: 24 | * Toolchain: 25 | * SDK: 26 | 27 | ### Checklist: 28 | 29 | - [ ] My code follows the style guidelines of this project 30 | - [ ] I have performed a self-review of my own code 31 | - [ ] I have commented my code, particularly in hard-to-understand areas 32 | - [ ] I have made corresponding changes to the documentation 33 | - [ ] My changes generate no new warnings 34 | - [ ] I have added tests that prove my fix is effective or that my feature works 35 | - [ ] New and existing unit tests pass locally with my changes 36 | - [ ] Any dependent changes have been merged and published in downstream modules 37 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@radix-ui/react-alert-dialog": "^1.0.5", 14 | "@radix-ui/react-checkbox": "^1.0.4", 15 | "@radix-ui/react-dialog": "^1.0.5", 16 | "@radix-ui/react-dropdown-menu": "^2.0.6", 17 | "@radix-ui/react-icons": "^1.3.0", 18 | "@radix-ui/react-label": "^2.0.2", 19 | "@radix-ui/react-navigation-menu": "^1.1.4", 20 | "@radix-ui/react-select": "^2.0.0", 21 | "@radix-ui/react-slot": "^1.0.2", 22 | "axios": "^1.6.8", 23 | "class-variance-authority": "^0.7.0", 24 | "clsx": "^2.1.0", 25 | "lucide-react": "^0.350.0", 26 | "next-themes": "^0.3.0", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0", 29 | "react-router-dom": "^6.22.3", 30 | "sonner": "^1.4.3", 31 | "tailwind-merge": "^2.2.1", 32 | "tailwindcss-animate": "^1.0.7" 33 | }, 34 | "devDependencies": { 35 | "@types/react": "^18.2.56", 36 | "@types/react-dom": "^18.2.19", 37 | "@typescript-eslint/eslint-plugin": "^7.0.2", 38 | "@typescript-eslint/parser": "^7.0.2", 39 | "@vitejs/plugin-react": "^4.2.1", 40 | "autoprefixer": "^10.4.18", 41 | "eslint": "^8.56.0", 42 | "eslint-plugin-react-hooks": "^4.6.0", 43 | "eslint-plugin-react-refresh": "^0.4.5", 44 | "postcss": "^8.4.35", 45 | "tailwindcss": "^3.4.1", 46 | "typescript": "^5.2.2", 47 | "vite": "^5.1.4" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /client/src/App.tsx: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import { Routes, Route } from "react-router-dom"; 3 | import TimetableAdmin from "./components/TimetableAdmin"; 4 | import Navbar from "./components/Navbar"; 5 | import { VacantRooms } from "./components/VacantRooms"; 6 | import TeachersAbsentAdmin from "./components/TeachersAbsentAdmin"; 7 | import TeachersAbsent from "./components/TeachersAbsent"; 8 | import Login from "./components/Login"; 9 | import ErrorPage from "./components/ErrorPage"; 10 | import Timetable from "./components/Timetable"; 11 | import TeacherRegister from "./components/TeacherRegister"; 12 | import Footer from "./components/Footer"; 13 | import AddRoom from "./components/AddRoom"; 14 | import { Toaster } from "./components/ui/sonner"; 15 | import { useTheme } from "./context/ThemeProvider"; 16 | import { BookRoom } from "./components/BookRoom"; 17 | 18 | function App() { 19 | const { theme } = useTheme(); 20 | return ( 21 | <> 22 | 23 | 24 | 25 | } path="/login" /> 26 | } path="/admin/timetable" /> 27 | } path="/admin/teachersabsent" /> 28 | } path="/admin/register" /> 29 | } path="/admin/addroom" /> 30 | } path="/bookroom" /> 31 | 32 | } path="/timetable" /> 33 | } path="/" /> 34 | } path="/teachersabsent" /> 35 | 36 | } path="/*" /> 37 | 38 |