Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import Home from './pages/Home';
import NotFound from './pages/NotFound'
import './App.css'
import Schedule from './pages/Schedule';

import Compare from './pages/Compare';
import CompareResult from './pages/CompareResult';

function App() {

Expand All @@ -16,6 +17,8 @@ function App() {
<Route path="/" element={<Home />} />
<Route path ="/schedule/:sub" element = { <Schedule /> } />

<Route path="/compare" element={<Compare />} />
<Route path="/compare/result" element={<CompareResult />} />



Expand Down
58 changes: 58 additions & 0 deletions src/assets/free-grid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
@media (max-width: 768px) {

.grid-row {
grid-template-columns: 65px repeat(5, minmax(40px, 1fr));
}

.grid-cell {
height: 32px;
font-size: 10px;
}

.time {
font-size: 11px;
}

}

.free-grid {
display: flex;
flex-direction: column;
gap: 6px;
margin-top: 20px;
width: 100%;
}

.grid-row {
display: grid;
grid-template-columns: 80px repeat(5, minmax(60px, 1fr));
gap: 6px;
}

.grid-row.header {
font-weight: 600;
}

.time {
font-size: 13px;
padding: 6px;
}

.grid-cell {
height: 40px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}

.grid-cell.free {
background: #d4f8d4;
color: #1c6b1c;
font-weight: 600;
}

.grid-cell.busy {
background: #eee;
}
22 changes: 22 additions & 0 deletions src/components/BatchCountBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import Select from 'react-select';

const BatchCountBox = ({ onChange }) => {
// Transform the options if they are strings
const formattedOptions = Array.from({ length: 6 }, (_, i) => ({
value: i + 1,
label: `${i + 1}`
}));


return (
<Select
options={formattedOptions}
onChange={onChange}
placeholder="Select batches"
// styles={customStyles}
/>
);
};

export default BatchCountBox;
49 changes: 49 additions & 0 deletions src/components/FreeSlotGrid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import '@/assets/free-grid.css'

const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

function FreeSlotGrid({ slots, freeSlots }) {

const freeMap = new Set(
freeSlots.map(s => `${s.day}-${s.time}`)
);

return (
<div className="free-grid">

<div className="grid-row header">
<div></div>
{days.map(day => (
<div key={day}>{day}</div>
))}
</div>

{slots.map(time => (
<div className="grid-row" key={time}>

<div className="time">{time}</div>

{days.map(day => {

const key = `${day}-${time}`;
const isFree = freeMap.has(key);

return (
<div
key={key}
className={`grid-cell ${isFree ? "free" : "busy"}`}
>
{isFree ? "FREE" : ""}
</div>
);
})}

</div>
))}

</div>
);
}

export default FreeSlotGrid;
155 changes: 155 additions & 0 deletions src/pages/Compare.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import '@/assets/home.css';
import InputBox from '../components/InputBox';
import BatchCountBox from '../components/BatchCountBox';
import instagramLogo from '@/assets/logos/instagram.svg';
import githubLogo from '@/assets/logos/github.svg';

function Compare() {

const [batchCount, setBatchCount] = useState(1);
const [selectedBatches, setSelectedBatches] = useState([]);
const navigate = useNavigate();

const handleBatchCountChange = (selected) => {
const count = selected.value;
setBatchCount(count);
setSelectedBatches(Array(count).fill(null));
};

const handleBatchChange = (index, selected) => {
const updated = [...selectedBatches];
updated[index] = selected;
setSelectedBatches(updated);
};

const handleSubmit = (event) => {
event.preventDefault();

if (selectedBatches.some(b => !b)) {
alert("Please select all batches");
return;
}

const batchValues = selectedBatches.map(b => b.value);

navigate("/compare/result", {
state: { batches: batchValues }
});
};

return (

<div className="home-container">

<div className="navbar">
<div className="cont1">
<img
src="https://acm-thapar.github.io/img/logo.png"
alt="Logo"
className="logo"
/>
</div>

<ul className="cont2">
<li>
<a
href="/"
style={{
textDecoration: "none",
color: "#fff",
fontWeight: "500",
padding: "6px 12px",
borderRadius: "6px",
background: "#5aff5a"
}}
>
TimeTable
</a>
</li>
<li>
<a href="https://www.instagram.com/acmthapar/" target="_blank" rel="noopener noreferrer">
<img src={instagramLogo} className="social" alt="Instagram Logo" />
</a>
</li>
<li>
<a href="https://github.com/ACM-Thapar" target="_blank" rel="noopener noreferrer">
<img src={githubLogo} className="social" alt="Github Logo" />
</a>
</li>
</ul>
</div>


<div className="container">
<div className="content">

<div className="tt-header">
Free Slot Finder
</div>

<div className="tt-subtext">
Thapar University
</div>

<form className="form-container" onSubmit={handleSubmit}>

<div className="text">
Current Semester: &emsp;
<span className='bold'>2025-2026 EVEN</span>
</div>


{/* Batch count */}

<div className="text">
Count: &emsp;

<div className="input-box-container">
<BatchCountBox onChange={handleBatchCountChange} />
</div>

</div>


{/* Dynamic subgroup inputs */}

{Array.from({ length: batchCount }).map((_, index) => (

<div className="text" key={index}>
Subgroup {index + 1}: &emsp;

<div className="input-box-container">

<InputBox
onChange={(selected) =>
handleBatchChange(index, selected)
}
/>

</div>

</div>

))}


<div className="note">
*make sure you search for updated group names mentioned in webkiosk in Subject Regtd. Details
</div>

<button className="submit-button" type="submit">
Get free Slots
</button>

</form>

</div>
</div>

</div>
);
}

export default Compare;
87 changes: 87 additions & 0 deletions src/pages/CompareResult.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useRef } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import timetableData from "@/assets/results_updated.json";
import FreeSlotGrid from "../components/FreeSlotGrid";


import useViewPortMetaTag from '@/hooks/useViewPortMetaTag'

const days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"];

const slots = [
"08:00 AM",
"08:50 AM",
"09:40 AM",
"10:30 AM",
"11:20 AM",
"12:10 PM",
"01:00 PM",
"01:50 PM",
"02:40 PM",
"03:30 PM",
"04:20 PM",
];

function CompareResult() {
// useViewPortMetaTag(1200);

const location = useLocation();
const navigate = useNavigate();
const batches = location.state?.batches || [];

const tableRef = useRef(null);

if (!batches.length) {
return (
<div style={{ padding: "40px" }}>
<h2>No batches selected</h2>
<button onClick={() => navigate("/")}>Go Back</button>
</div>
);
}

const freeSlots = [];

days.forEach(day => {
slots.forEach(time => {

const isFree = batches.every(batch => {
return !timetableData?.[batch]?.[day]?.[time];
});

if (isFree) {
freeSlots.push({ day, time });
}

});
});

const scheduleData = {};

days.forEach(day => {
scheduleData[day] = {};
});

freeSlots.forEach(({ day, time }) => {
scheduleData[day][time] = ["FREE", "", "", ""];
});

return (
<div>
<nav className="acm-title">
<div className="title-text">Free Slots</div>
<img src="https://acm-thapar.github.io/img/logo.png" alt="Logo" />
</nav>
<div>
<article className="table" ref={tableRef}>
<FreeSlotGrid
slots={slots}
freeSlots={freeSlots}
/>
</article>
</div>
</div>
);
}

export default CompareResult;
Loading