timetable-project / index.html
theharby's picture
Add 3 files
d00d7d7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Timetable Creator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.timetable-cell {
min-height: 80px;
transition: all 0.2s ease;
}
.timetable-cell:hover {
background-color: #f3f4f6;
}
.draggable-item {
cursor: grab;
user-select: none;
}
.draggable-item:active {
cursor: grabbing;
}
.dropzone {
transition: background-color 0.2s ease;
}
.dropzone.drag-over {
background-color: #e5e7eb;
}
.modal {
transition: opacity 0.2s ease, transform 0.2s ease;
}
.modal-hidden {
opacity: 0;
transform: translateY(20px);
pointer-events: none;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="mb-8">
<h1 class="text-3xl font-bold text-indigo-700 mb-2">Timetable Creator</h1>
<p class="text-gray-600">Drag and drop subjects to create your perfect schedule</p>
</header>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
<!-- Subjects Panel -->
<div class="bg-white rounded-lg shadow p-4 lg:col-span-1">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold text-gray-800">Subjects</h2>
<button id="addSubjectBtn" class="bg-indigo-100 text-indigo-700 p-2 rounded-full hover:bg-indigo-200 transition">
<i class="fas fa-plus"></i>
</button>
</div>
<div id="subjectsList" class="space-y-2">
<!-- Subjects will be added here -->
</div>
</div>
<!-- Timetable -->
<div class="bg-white rounded-lg shadow p-4 lg:col-span-3">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold text-gray-800">Weekly Schedule</h2>
<div class="flex space-x-2">
<button id="saveTimetableBtn" class="bg-green-100 text-green-700 px-3 py-1 rounded hover:bg-green-200 transition">
<i class="fas fa-save mr-1"></i> Save
</button>
<button id="clearTimetableBtn" class="bg-red-100 text-red-700 px-3 py-1 rounded hover:bg-red-200 transition">
<i class="fas fa-trash mr-1"></i> Clear
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr class="bg-gray-100">
<th class="p-2 w-24 font-medium text-gray-600">Time</th>
<th class="p-2 font-medium text-gray-600">Monday</th>
<th class="p-2 font-medium text-gray-600">Tuesday</th>
<th class="p-2 font-medium text-gray-600">Wednesday</th>
<th class="p-2 font-medium text-gray-600">Thursday</th>
<th class="p-2 font-medium text-gray-600">Friday</th>
</tr>
</thead>
<tbody id="timetableBody">
<!-- Timetable rows will be generated here -->
</tbody>
</table>
</div>
</div>
</div>
<!-- Saved Timetables Section -->
<div class="mt-8 bg-white rounded-lg shadow p-4">
<h2 class="text-lg font-semibold text-gray-800 mb-4">Saved Timetables</h2>
<div id="savedTimetables" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Saved timetables will appear here -->
</div>
</div>
</div>
<!-- Add Subject Modal -->
<div id="subjectModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 modal modal-hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-800 mb-4">Add New Subject</h3>
<div class="mb-4">
<label for="subjectName" class="block text-sm font-medium text-gray-700 mb-1">Subject Name</label>
<input type="text" id="subjectName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
</div>
<div class="mb-4">
<label for="subjectColor" class="block text-sm font-medium text-gray-700 mb-1">Color</label>
<select id="subjectColor" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="bg-red-100 text-red-800">Red</option>
<option value="bg-blue-100 text-blue-800">Blue</option>
<option value="bg-green-100 text-green-800">Green</option>
<option value="bg-yellow-100 text-yellow-800">Yellow</option>
<option value="bg-purple-100 text-purple-800">Purple</option>
<option value="bg-pink-100 text-pink-800">Pink</option>
<option value="bg-indigo-100 text-indigo-800">Indigo</option>
</select>
</div>
<div class="flex justify-end space-x-3">
<button id="cancelSubjectBtn" class="px-4 py-2 text-gray-600 hover:text-gray-800 transition">Cancel</button>
<button id="saveSubjectBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Add Subject</button>
</div>
</div>
</div>
</div>
<!-- Save Timetable Modal -->
<div id="saveModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50 modal modal-hidden">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-800 mb-4">Save Timetable</h3>
<div class="mb-4">
<label for="timetableName" class="block text-sm font-medium text-gray-700 mb-1">Timetable Name</label>
<input type="text" id="timetableName" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="e.g. Fall Semester 2023">
</div>
<div class="flex justify-end space-x-3">
<button id="cancelSaveBtn" class="px-4 py-2 text-gray-600 hover:text-gray-800 transition">Cancel</button>
<button id="confirmSaveBtn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Save</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const subjectsList = document.getElementById('subjectsList');
const timetableBody = document.getElementById('timetableBody');
const savedTimetables = document.getElementById('savedTimetables');
const addSubjectBtn = document.getElementById('addSubjectBtn');
const saveTimetableBtn = document.getElementById('saveTimetableBtn');
const clearTimetableBtn = document.getElementById('clearTimetableBtn');
const subjectModal = document.getElementById('subjectModal');
const saveModal = document.getElementById('saveModal');
const cancelSubjectBtn = document.getElementById('cancelSubjectBtn');
const saveSubjectBtn = document.getElementById('saveSubjectBtn');
const cancelSaveBtn = document.getElementById('cancelSaveBtn');
const confirmSaveBtn = document.getElementById('confirmSaveBtn');
const subjectNameInput = document.getElementById('subjectName');
const subjectColorSelect = document.getElementById('subjectColor');
const timetableNameInput = document.getElementById('timetableName');
// State
let subjects = [];
let timetable = {};
let draggedItem = null;
let currentCell = null;
// Initialize timetable
initTimetable();
// Event Listeners
addSubjectBtn.addEventListener('click', showSubjectModal);
cancelSubjectBtn.addEventListener('click', hideSubjectModal);
saveSubjectBtn.addEventListener('click', addSubject);
saveTimetableBtn.addEventListener('click', showSaveModal);
cancelSaveBtn.addEventListener('click', hideSaveModal);
confirmSaveBtn.addEventListener('click', saveTimetable);
clearTimetableBtn.addEventListener('click', clearTimetable);
// Initialize with some sample subjects
addSampleSubjects();
// Functions
function initTimetable() {
const timeSlots = [
'8:00 - 9:00', '9:00 - 10:00', '10:00 - 11:00',
'11:00 - 12:00', '12:00 - 13:00', '13:00 - 14:00',
'14:00 - 15:00', '15:00 - 16:00', '16:00 - 17:00'
];
timetableBody.innerHTML = '';
timeSlots.forEach(time => {
const row = document.createElement('tr');
// Time cell
const timeCell = document.createElement('td');
timeCell.className = 'p-2 border border-gray-200 bg-gray-50 text-center text-sm text-gray-600';
timeCell.textContent = time;
row.appendChild(timeCell);
// Day cells
for (let i = 0; i < 5; i++) {
const dayCell = document.createElement('td');
dayCell.className = 'p-1 border border-gray-200 timetable-cell dropzone';
dayCell.dataset.day = i;
dayCell.dataset.time = time;
// Drag and drop events
dayCell.addEventListener('dragover', handleDragOver);
dayCell.addEventListener('dragleave', handleDragLeave);
dayCell.addEventListener('drop', handleDrop);
dayCell.addEventListener('dragenter', handleDragEnter);
row.appendChild(dayCell);
}
timetableBody.appendChild(row);
});
}
function addSampleSubjects() {
const sampleSubjects = [
{ name: 'Mathematics', color: 'bg-blue-100 text-blue-800' },
{ name: 'Physics', color: 'bg-red-100 text-red-800' },
{ name: 'Chemistry', color: 'bg-green-100 text-green-800' },
{ name: 'Biology', color: 'bg-purple-100 text-purple-800' },
{ name: 'History', color: 'bg-yellow-100 text-yellow-800' },
{ name: 'English', color: 'bg-pink-100 text-pink-800' }
];
sampleSubjects.forEach(subject => {
addSubjectToDOM(subject);
subjects.push(subject);
});
}
function showSubjectModal() {
subjectNameInput.value = '';
subjectModal.classList.remove('modal-hidden');
}
function hideSubjectModal() {
subjectModal.classList.add('modal-hidden');
}
function showSaveModal() {
timetableNameInput.value = '';
saveModal.classList.remove('modal-hidden');
}
function hideSaveModal() {
saveModal.classList.add('modal-hidden');
}
function addSubject() {
const name = subjectNameInput.value.trim();
const color = subjectColorSelect.value;
if (name) {
const subject = { name, color };
addSubjectToDOM(subject);
subjects.push(subject);
hideSubjectModal();
}
}
function addSubjectToDOM(subject) {
const subjectElement = document.createElement('div');
subjectElement.className = `p-3 rounded-md draggable-item ${subject.color} flex justify-between items-center`;
subjectElement.textContent = subject.name;
subjectElement.draggable = true;
subjectElement.dataset.subject = subject.name;
// Add drag icon
const dragIcon = document.createElement('span');
dragIcon.className = 'text-gray-500 ml-2';
dragIcon.innerHTML = '<i class="fas fa-grip-vertical"></i>';
subjectElement.appendChild(dragIcon);
// Drag events
subjectElement.addEventListener('dragstart', handleDragStart);
subjectElement.addEventListener('dragend', handleDragEnd);
subjectsList.appendChild(subjectElement);
}
function handleDragStart(e) {
draggedItem = e.target;
e.target.classList.add('opacity-50');
e.dataTransfer.setData('text/plain', e.target.dataset.subject);
}
function handleDragEnd(e) {
e.target.classList.remove('opacity-50');
}
function handleDragOver(e) {
e.preventDefault();
currentCell = e.target;
e.target.classList.add('drag-over');
}
function handleDragEnter(e) {
e.preventDefault();
}
function handleDragLeave(e) {
e.target.classList.remove('drag-over');
}
function handleDrop(e) {
e.preventDefault();
e.target.classList.remove('drag-over');
const subjectName = e.dataTransfer.getData('text/plain');
const subject = subjects.find(s => s.name === subjectName);
if (subject && currentCell) {
// Clear cell first
currentCell.innerHTML = '';
// Add subject to cell
const subjectElement = document.createElement('div');
subjectElement.className = `p-2 rounded ${subject.color} text-sm font-medium`;
subjectElement.textContent = subject.name;
// Add remove button
const removeBtn = document.createElement('button');
removeBtn.className = 'float-right text-gray-500 hover:text-gray-700 ml-1';
removeBtn.innerHTML = '<i class="fas fa-times"></i>';
removeBtn.addEventListener('click', function() {
currentCell.innerHTML = '';
});
subjectElement.appendChild(removeBtn);
currentCell.appendChild(subjectElement);
// Save to timetable object
const day = currentCell.dataset.day;
const time = currentCell.dataset.time;
if (!timetable[day]) timetable[day] = {};
timetable[day][time] = subject.name;
}
}
function saveTimetable() {
const name = timetableNameInput.value.trim();
if (name) {
const timetableData = {
name,
data: timetable,
createdAt: new Date().toISOString()
};
// Save to localStorage
let saved = JSON.parse(localStorage.getItem('timetables') || '[]');
saved.push(timetableData);
localStorage.setItem('timetables', JSON.stringify(saved));
// Update UI
displaySavedTimetables();
hideSaveModal();
// Show success message
alert('Timetable saved successfully!');
}
}
function displaySavedTimetables() {
const saved = JSON.parse(localStorage.getItem('timetables') || '[]');
savedTimetables.innerHTML = '';
if (saved.length === 0) {
savedTimetables.innerHTML = '<p class="text-gray-500">No saved timetables yet.</p>';
return;
}
saved.forEach((timetable, index) => {
const card = document.createElement('div');
card.className = 'bg-white border border-gray-200 rounded-lg p-4 hover:shadow-md transition';
const name = document.createElement('h3');
name.className = 'font-medium text-lg text-gray-800 mb-2';
name.textContent = timetable.name;
const date = document.createElement('p');
date.className = 'text-sm text-gray-500 mb-3';
date.textContent = new Date(timetable.createdAt).toLocaleString();
const actions = document.createElement('div');
actions.className = 'flex justify-between';
const loadBtn = document.createElement('button');
loadBtn.className = 'bg-indigo-100 text-indigo-700 px-3 py-1 rounded text-sm hover:bg-indigo-200 transition';
loadBtn.innerHTML = '<i class="fas fa-upload mr-1"></i> Load';
loadBtn.addEventListener('click', () => loadTimetable(index));
const deleteBtn = document.createElement('button');
deleteBtn.className = 'bg-red-100 text-red-700 px-3 py-1 rounded text-sm hover:bg-red-200 transition';
deleteBtn.innerHTML = '<i class="fas fa-trash mr-1"></i> Delete';
deleteBtn.addEventListener('click', () => deleteTimetable(index));
actions.appendChild(loadBtn);
actions.appendChild(deleteBtn);
card.appendChild(name);
card.appendChild(date);
card.appendChild(actions);
savedTimetables.appendChild(card);
});
}
function loadTimetable(index) {
const saved = JSON.parse(localStorage.getItem('timetables') || '[]');
if (saved[index]) {
// Clear current timetable
clearTimetable();
// Load the saved timetable
const loadedTimetable = saved[index].data;
timetable = loadedTimetable;
// Update the UI
for (const day in loadedTimetable) {
for (const time in loadedTimetable[day]) {
const subjectName = loadedTimetable[day][time];
const subject = subjects.find(s => s.name === subjectName);
if (subject) {
const cells = document.querySelectorAll(`[data-day="${day}"][data-time="${time}"]`);
if (cells.length > 0) {
const cell = cells[0];
cell.innerHTML = '';
const subjectElement = document.createElement('div');
subjectElement.className = `p-2 rounded ${subject.color} text-sm font-medium`;
subjectElement.textContent = subject.name;
// Add remove button
const removeBtn = document.createElement('button');
removeBtn.className = 'float-right text-gray-500 hover:text-gray-700 ml-1';
removeBtn.innerHTML = '<i class="fas fa-times"></i>';
removeBtn.addEventListener('click', function() {
cell.innerHTML = '';
delete timetable[day][time];
});
subjectElement.appendChild(removeBtn);
cell.appendChild(subjectElement);
}
}
}
}
alert('Timetable loaded successfully!');
}
}
function deleteTimetable(index) {
if (confirm('Are you sure you want to delete this timetable?')) {
const saved = JSON.parse(localStorage.getItem('timetables') || '[]');
saved.splice(index, 1);
localStorage.setItem('timetables', JSON.stringify(saved));
displaySavedTimetables();
}
}
function clearTimetable() {
if (confirm('Are you sure you want to clear the current timetable?')) {
// Clear UI
const cells = document.querySelectorAll('.timetable-cell');
cells.forEach(cell => {
cell.innerHTML = '';
});
// Clear data
timetable = {};
}
}
// Initial display of saved timetables
displaySavedTimetables();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=theharby/timetable-project" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>