<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Employee Scheduling App</title>
<style>
body {
font-family: Arial, sans-serif !important;
margin: 0 !important;
padding: 20px !important;
background-color: #f0f4f8 !important;
}
.sch-container {
max-width: 1000px !important;
margin: 0 auto !important;
background-color: white !important;
border-radius: 10px !important;
padding: 20px !important;
box-shadow: 0 0 10px rgba(0,0,0,0.1) !important;
}
h1 {
color: #2d3748 !important;
text-align: center !important;
margin-bottom: 30px !important;
}
#sch-schedule {
display: grid !important;
grid-template-columns: auto repeat(7, 1fr) !important;
gap: 10px !important;
margin-bottom: 20px !important;
overflow-x: auto !important;
}
.sch-day, .sch-shift-type {
font-weight: bold !important;
text-align: center !important;
padding: 10px !important;
background-color: #edf2f7 !important;
border-radius: 5px !important;
position: relative !important;
}
.sch-day .sch-copy-dropdown {
margin-top: 5px !important;
}
.sch-copy-dropdown select {
border: 1px solid #e2e8f0 !important;
background-color: #f0f4f8 !important;
padding: 5px !important;
border-radius: 5px !important;
cursor: pointer !important;
color: black !important;
}
.sch-shift-time {
font-size: 0.8em !important;
}
.sch-shift-slot {
border: 1px solid #e2e8f0 !important;
min-height: 50px !important;
padding: 5px !important;
position: relative !important;
border-radius: 5px !important;
}
#sch-employees {
margin-top: 20px !important;
border: 1px solid #e2e8f0 !important;
padding: 10px !important;
background-color: #edf2f7 !important;
border-radius: 5px !important;
text-align: center !important;
}
#sch-employee-list {
display: flex !important;
flex-wrap: wrap !important;
gap: 10px !important;
margin-top: 10px !important;
justify-content: center !important;
}
.sch-employee {
padding: 10px !important;
cursor: move !important;
border-radius: 5px !important;
width: 120px !important;
text-align: center !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
position: relative !important;
}
.sch-employee .sch-title {
font-size: 0.9em !important;
font-weight: bold !important;
}
.sch-employee .sch-name {
font-size: 0.8em !important;
}
.sch-employee .sch-actions {
position: absolute !important;
top: 2px !important;
right: 2px !important;
display: none !important;
}
.sch-employee:hover .sch-actions {
display: block !important;
}
.sch-employee .sch-actions button {
background: none !important;
border: none !important;
cursor: pointer !important;
font-size: 12px !important;
padding: 2px !important;
}
.sch-assigned-employee {
padding: 5px !important;
margin: 2px !important;
border-radius: 3px !important;
font-size: 0.8em !important;
position: relative !important;
}
.sch-remove-employee {
position: absolute !important;
top: 2px !important;
right: 2px !important;
background-color: #fc8181 !important;
color: white !important;
border: none !important;
border-radius: 50% !important;
width: 16px !important;
height: 16px !important;
font-size: 10px !important;
cursor: pointer !important;
display: none !important;
}
.sch-assigned-employee:hover .sch-remove-employee {
display: block !important;
}
.sch-edit-shift, .sch-delete-shift {
cursor: pointer !important;
margin-left: 5px !important;
color: #4a5568 !important;
}
.sch-delete-shift {
color: #e53e3e !important;
}
button {
background-color: #4299e1 !important;
color: white !important;
border: none !important;
padding: 10px 15px !important;
border-radius: 5px !important;
cursor: pointer !important;
transition: background-color 0.3s !important;
}
button:hover {
background-color: #3182ce !important;
}
input, select {
padding: 8px !important;
border: 1px solid #e2e8f0 !important;
border-radius: 5px !important;
margin-right: 10px !important;
}
.sch-highlight {
border: 2px solid #e53e3e !important;
}
.sch-dropdown {
position: relative !important;
display: inline-block !important;
margin-right: 10px !important;
}
.sch-dropdown-content {
display: none !important;
position: absolute !important;
background-color: #f0f4f8 !important;
box-shadow: 0 0 10px rgba(0,0,0,0.1) !important;
border-radius: 5px !important;
z-index: 10 !important;
right: 0 !important;
color: black !important;
}
.sch-dropdown-content button {
display: block !important;
width: 100% !important;
text-align: left !important;
padding: 10px !important;
background-color: #f0f4f8 !important;
border: none !important;
cursor: pointer !important;
color: black !important;
}
.sch-dropdown-content button:hover {
background-color: #e2e8f0 !important;
}
.sch-dropdown:hover .sch-dropdown-content {
display: block !important;
}
/* Tabs styling */
.tab-container {
display: flex !important;
justify-content: center !important;
margin-bottom: 20px !important;
border-bottom: 2px solid #e2e8f0 !important;
}
.tab {
cursor: pointer !important;
padding: 10px 20px !important;
border: 1px solid #e2e8f0 !important;
border-bottom: none !important;
background-color: #edf2f7 !important;
margin-right: 5px !important;
border-radius: 5px 5px 0 0 !important;
transition: background-color 0.3s !important;
}
.tab.active {
background-color: white !important;
box-shadow: 0 0 10px rgba(0,0,0,0.1) !important;
}
.tab-content {
display: none !important;
padding: 20px !important;
background-color: white !important;
border-radius: 0 5px 5px 5px !important;
box-shadow: 0 0 10px rgba(0,0,0,0.1) !important;
}
.tab-content.active {
display: block !important;
}
/* Controls Section */
#controls {
background-color: #edf2f7 !important;
padding: 20px !important;
border-radius: 10px !important;
margin-top: 20px !important;
box-shadow: 0 0 10px rgba(0,0,0,0.1) !important;
}
#controls h2 {
margin-bottom: 15px !important;
color: #2d3748 !important;
text-align: center !important;
}
#controls .control-group {
margin-bottom: 15px !important;
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
}
#controls .control-group label {
flex: 1 !important;
color: #4a5568 !important;
}
#controls .control-group input {
flex: 2 !important;
max-width: 150px !important;
}
#controls .control-group .sch-dropdown,
#controls .control-group button {
margin-top: 10px !important;
}
/* Enhanced Tables Styling */
table {
width: 100% !important;
border-collapse: collapse !important;
margin-bottom: 20px !important;
}
th, td {
border: 1px solid #e2e8f0 !important;
padding: 10px !important;
text-align: left !important;
}
th {
background-color: #4299e1 !important;
color: white !important;
}
td {
background-color: white !important;
}
td .sch-edit-link, td .sch-delete-link {
cursor: pointer !important;
color: #4299e1 !important;
text-decoration: underline !important;
}
td .sch-delete-link {
color: #e53e3e !important;
}
</style>
</head>
<body>
<div class="sch-container">
<h1>Employee Scheduling App</h1>
<div id="sch-schedule"></div>
<div id="sch-employees">
<h2>Available Employees</h2>
<div id="sch-employee-list"></div>
<div id="sch-employee-insights"></div>
</div>
<!-- Management Area with Tabs -->
<div class="sch-container">
<h2>Management Area</h2>
<div class="tab-container">
<div class="tab active" onclick="showTab('shift-management')">Shift Management</div>
<div class="tab" onclick="showTab('employee-management')">Employee Management</div>
<div class="tab" onclick="showTab('controls')">Controls</div>
</div>
<div id="shift-management" class="tab-content active">
<!-- Shift Management Section -->
<div class="sch-section">
<h2>Manage Shifts</h2>
<div id="sch-add-shift">
<h3>Add Shift</h3>
<input type="text" id="sch-new-shift-name" placeholder="Shift Name">
<input type="time" id="sch-new-shift-start" placeholder="Start Time">
<input type="time" id="sch-new-shift-end" placeholder="End Time">
<select id="sch-shift-position">
<option value="before">Before</option>
<option value="after">After</option>
</select>
<select id="sch-existing-shifts"></select>
<button onclick="addShift()">Add Shift</button>
</div>
<h3>Shift Overview</h3>
<table id="sch-shift-overview">
<thead>
<tr>
<th>Shift Name</th>
<th>Start Time</th>
<th>End Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="sch-shift-overview-body">
<!-- Shift rows will be dynamically populated here -->
</tbody>
</table>
</div>
</div>
<div id="employee-management" class="tab-content">
<!-- Employee Management Section -->
<div class="sch-section">
<h2>Manage Employees</h2>
<div id="sch-add-employee">
<h3>Add Employee</h3>
<input type="text" id="sch-new-employee-name" placeholder="Name">
<input type="text" id="sch-new-employee-title" placeholder="Title">
<input type="text" id="sch-new-employee-phone" placeholder="Phone">
<input type="email" id="sch-new-employee-email" placeholder="Email">
<input type="color" id="sch-new-employee-color" value="#4299e1">
<button onclick="addEmployee()">Add Employee</button>
</div>
<h3>Employee Overview</h3>
<table id="sch-employee-overview">
<thead>
<tr>
<th>Name</th>
<th>Title</th>
<th>Phone</th>
<th>Email</th>
<th>Color</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="sch-employee-overview-body">
<!-- Employee rows will be dynamically populated here -->
</tbody>
</table>
</div>
</div>
<div id="controls" class="tab-content">
<!-- Controls Section -->
<div class="sch-section">
<h2>Controls</h2>
<div class="control-group">
<label for="sch-weekly-hours-limit-input">Set Weekly Hours Limit:</label>
<input type="number" id="sch-weekly-hours-limit-input" placeholder="Weekly Hours Limit">
</div>
<div class="control-group">
<div class="sch-dropdown">
<button onclick="toggleDropdown('sch-save-dropdown')">Save Schedule</button>
<div id="sch-save-dropdown" class="sch-dropdown-content">
<button onclick="exportToCSV()">CSV File</button>
<button onclick="saveDataToCookie()">Browser (Cookie)</button>
<button onclick="saveDataToFile()">Local File (JSON)</button>
</div>
</div>
<div class="sch-dropdown">
<button onclick="toggleDropdown('sch-import-dropdown')">Import Schedule</button>
<div id="sch-import-dropdown" class="sch-dropdown-content">
<button onclick="importFromFileClick()">Local File (JSON)</button>
<button onclick="importDataFromCookie()">Browser (Cookie)</button>
<button onclick="importFromCSV()">CSV File</button>
<input type="file" id="sch-file-input" style="display:none" accept=".json,.csv" onchange="handleFileImport(event)">
</div>
</div>
</div>
<div class="control-group">
<button id="sch-verify-button" onclick="verifySchedule()">Verify Schedule</button>
<button id="sch-clear-button" onclick="clearSchedule()">Clear Schedule</button>
</div>
</div>
</div>
</div>
</div>
<script>
let employees = [
{ name: "John Doe", title: "Manager", phone: "123-456-7890", email: "john@example.com", color: "#4299e1" },
{ name: "Jane Smith", title: "Assistant", phone: "987-654-3210", email: "jane@example.com", color: "#48bb78" },
{ name: "Bob Johnson", title: "Associate", phone: "555-555-5555", email: "bob@example.com", color: "#ed8936" }
];
let shifts = [
{ name: "Morning Shift", start: "08:00", end: "12:00" },
{ name: "Afternoon Shift", start: "12:00", end: "16:00" },
{ name: "Evening Shift", start: "16:00", end: "20:00" }
];
let schedule = {};
function initializeSchedule() {
shifts.forEach(shift => {
schedule[shift.name] = {
monday: [], tuesday: [], wednesday: [], thursday: [], friday: [], saturday: [], sunday: []
};
});
}
function getContrastColor(hexcolor) {
if (hexcolor.slice(0, 1) === '#') {
hexcolor = hexcolor.slice(1);
}
var r = parseInt(hexcolor.substr(0,2),16);
var g = parseInt(hexcolor.substr(2,2),16);
var b = parseInt(hexcolor.substr(4,2),16);
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
return (yiq >= 128) ? 'black' : 'white';
}
function renderSchedule() {
const scheduleElement = document.getElementById('sch-schedule');
scheduleElement.innerHTML = `
<div></div>
${['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].map(day => `
<div class="sch-day">
${day}
<div class="sch-copy-dropdown">
<select onchange="copyShifts('${day.toLowerCase()}', this.value)">
<option value="">Copy to</option>
<option value="all">All</option>
${['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].map((targetDay, index) => `
${targetDay.toLowerCase() !== day.toLowerCase() ? `<option value="${targetDay.toLowerCase()}">${targetDay}</option>` : ''}
`).join('')}
</select>
</div>
</div>
`).join('')}
`;
shifts.forEach((shift, index) => {
scheduleElement.innerHTML += `
<div class="sch-shift-type">
${shift.name}
<div class="sch-shift-time">${shift.start} - ${shift.end}</div>
<span class="sch-edit-shift" onclick="editShift(${index})">✏️</span>
<span class="sch-delete-shift" onclick="deleteShift(${index})">🗑️</span>
</div>
${['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'].map(day => `
<div class="sch-shift-slot" data-day="${day}" data-shift="${shift.name}" ondrop="drop(event)" ondragover="allowDrop(event)">
${schedule[shift.name][day].map((employeeIndex, idx) => {
const employee = employees[employeeIndex];
const textColor = getContrastColor(employee.color);
return `
<div class="sch-assigned-employee" data-employee="${employeeIndex}" style="background-color: ${employee.color}; color: ${textColor}">
<div class="sch-title">${employee.title}</div>
<div class="sch-name">${employee.name}</div>
<button class="sch-remove-employee" onclick="removeEmployeeFromShift('${shift.name}', '${day}', ${idx})">X</button>
</div>
`;
}).join('')}
</div>
`).join('')}
`;
});
addDragAndDropListeners();
updateExistingShiftsDropdown();
updateEmployeeInsights();
renderShiftOverview(); // Render the shift overview table
}
function renderEmployees() {
const employeeList = document.getElementById('sch-employee-list');
employeeList.innerHTML = '';
employees.forEach((employee, index) => {
const textColor = getContrastColor(employee.color);
employeeList.innerHTML += `
<div class="sch-employee" draggable="true" data-employee="${index}" style="background-color: ${employee.color}; color: ${textColor}" onmouseover="highlightEmployee(${index})" onmouseout="unhighlightEmployee(${index})" ondragstart="drag(event)">
<div class="sch-title">${employee.title}</div>
<div class="sch-name">${employee.name}</div>
<div class="sch-actions">
<button onclick="editEmployee(${index})">✏️</button>
<button onclick="deleteEmployee(${index})">🗑️</button>
</div>
</div>
`;
});
renderEmployeeOverview(); // Render the employee overview table
}
function renderEmployeeOverview() {
const employeeOverviewBody = document.getElementById('sch-employee-overview-body');
employeeOverviewBody.innerHTML = '';
employees.forEach((employee, index) => {
employeeOverviewBody.innerHTML += `
<tr>
<td>${employee.name}</td>
<td>${employee.title}</td>
<td>${employee.phone}</td>
<td>${employee.email}</td>
<td style="background-color: ${employee.color}; color: ${getContrastColor(employee.color)}">${employee.color}</td>
<td>
<span class="sch-edit-link" onclick="editEmployee(${index})">Edit</span> |
<span class="sch-delete-link" onclick="deleteEmployee(${index})">Delete</span>
</td>
</tr>
`;
});
}
function renderShiftOverview() {
const shiftOverviewBody = document.getElementById('sch-shift-overview-body');
shiftOverviewBody.innerHTML = '';
shifts.forEach((shift, index) => {
shiftOverviewBody.innerHTML += `
<tr>
<td>${shift.name}</td>
<td>${shift.start}</td>
<td>${shift.end}</td>
<td>
<span class="sch-edit-link" onclick="editShift(${index})">Edit</span> |
<span class="sch-delete-link" onclick="deleteShift(${index})">Delete</span>
</td>
</tr>
`;
});
}
function addDragAndDropListeners() {
const draggables = document.querySelectorAll('.sch-employee');
const dropZones = document.querySelectorAll('.sch-shift-slot');
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', drag);
});
dropZones.forEach(dropZone => {
dropZone.addEventListener('dragover', allowDrop);
dropZone.addEventListener('drop', drop);
});
}
function allowDrop(e) {
e.preventDefault();
}
function drag(e) {
e.dataTransfer.setData('text', e.target.getAttribute('data-employee'));
}
function drop(e) {
e.preventDefault();
const employeeIndex = e.dataTransfer.getData('text');
const targetSlot = e.target.closest('.sch-shift-slot');
if (targetSlot) {
const day = targetSlot.dataset.day;
const shift = targetSlot.dataset.shift;
const weeklyHoursLimit = parseInt(document.getElementById('sch-weekly-hours-limit-input').value) || Infinity;
if (getTotalWeeklyHours(employeeIndex) + getHoursDifference(shifts.find(s => s.name === shift).start, shifts.find(s => s.name === shift).end) <= weeklyHoursLimit) {
if (!schedule[shift][day].includes(parseInt(employeeIndex))) { // Prevent duplicate entries
schedule[shift][day].push(parseInt(employeeIndex));
renderSchedule();
}
} else {
alert("This assignment exceeds the weekly hours limit for this employee.");
}
}
}
function addEmployee() {
const name = document.getElementById('sch-new-employee-name').value;
const title = document.getElementById('sch-new-employee-title').value;
const phone = document.getElementById('sch-new-employee-phone').value;
const email = document.getElementById('sch-new-employee-email').value;
const color = document.getElementById('sch-new-employee-color').value;
if (name && title && phone && email) {
employees.push({ name, title, phone, email, color });
renderEmployees();
renderSchedule();
clearEmployeeInputs();
}
}
function editEmployee(index) {
const employee = employees[index];
const newName = prompt("Enter new name:", employee.name);
const newTitle = prompt("Enter new title:", employee.title);
const newPhone = prompt("Enter new phone number:", employee.phone);
const newEmail = prompt("Enter new email:", employee.email);
const newColor = prompt("Enter new color (hex code):", employee.color);
if (newName !== null && newTitle !== null && newPhone !== null && newEmail !== null && newColor !== null) {
employees[index] = {
name: newName || employee.name,
title: newTitle || employee.title,
phone: newPhone || employee.phone,
email: newEmail || employee.email,
color: newColor || employee.color
};
renderEmployees();
renderSchedule();
}
}
function deleteEmployee(index) {
if (confirm("Are you sure you want to delete this employee?")) {
employees.splice(index, 1);
// Remove the deleted employee from all shifts
Object.keys(schedule).forEach(shift => {
Object.keys(schedule[shift]).forEach(day => {
schedule[shift][day] = schedule[shift][day].filter(empIndex => empIndex !== index);
schedule[shift][day] = schedule[shift][day].map(empIndex => empIndex > index ? empIndex - 1 : empIndex);
});
});
renderEmployees();
renderSchedule();
}
}
function clearEmployeeInputs() {
document.getElementById('sch-new-employee-name').value = '';
document.getElementById('sch-new-employee-title').value = '';
document.getElementById('sch-new-employee-phone').value = '';
document.getElementById('sch-new-employee-email').value = '';
document.getElementById('sch-new-employee-color').value = '#4299e1';
}
function addShift() {
const shiftName = document.getElementById('sch-new-shift-name').value;
const start = document.getElementById('sch-new-shift-start').value;
const end = document.getElementById('sch-new-shift-end').value;
const position = document.getElementById('sch-shift-position').value;
const existingShiftIndex = document.getElementById('sch-existing-shifts').value;
if (shiftName && start && end && existingShiftIndex !== '') {
const index = parseInt(existingShiftIndex);
const newShift = { name: shiftName, start, end };
if (position === 'before') {
shifts.splice(index, 0, newShift);
} else {
shifts.splice(index + 1, 0, newShift);
}
schedule[shiftName] = {
monday: [], tuesday: [], wednesday: [], thursday: [], friday: [], saturday: [], sunday: []
};
renderSchedule();
document.getElementById('sch-new-shift-name').value = '';
document.getElementById('sch-new-shift-start').value = '';
document.getElementById('sch-new-shift-end').value = '';
}
}
function editShift(index) {
const shift = shifts[index];
const newName = prompt("Enter new name for the shift:", shift.name);
const newStart = prompt("Enter new start time (HH:MM):", shift.start);
const newEnd = prompt("Enter new end time (HH:MM):", shift.end);
if (newName !== null && newName.trim() !== "" && newName !== shift.name) {
schedule[newName] = schedule[shift.name];
delete schedule[shift.name];
shifts[index].name = newName.trim();
}
if (newStart !== null && newStart.trim() !== "" && newStart !== shift.start) {
shifts[index].start = newStart.trim();
}
if (newEnd !== null && newEnd.trim() !== "" && newEnd !== shift.end) {
shifts[index].end = newEnd.trim();
}
renderSchedule();
updateEmployeeInsights();
}
function deleteShift(index) {
if (confirm("Are you sure you want to delete this shift?")) {
const shiftToDelete = shifts[index];
shifts.splice(index, 1);
delete schedule[shiftToDelete.name];
renderSchedule();
}
}
function updateExistingShiftsDropdown() {
const dropdown = document.getElementById('sch-existing-shifts');
dropdown.innerHTML = '';
shifts.forEach((shift, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = shift.name;
dropdown.appendChild(option);
});
}
function removeEmployeeFromShift(shift, day, employeeIndex) {
schedule[shift][day].splice(employeeIndex, 1);
renderSchedule();
}
function exportToCSV() {
let csvContent = "data:text/csv;charset=utf-8,";
// Add header row
csvContent += "Shift,Day,Employees\n";
// Add data rows
shifts.forEach(shift => {
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'].forEach(day => {
let employeeNames = schedule[shift.name][day].map(index => employees[index].name).join(', ');
csvContent += `${shift.name},${day},${employeeNames}\n`;
});
});
// Create a download link and trigger the download
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "schedule.csv");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function verifySchedule() {
// Clear previous highlights
document.querySelectorAll('.sch-highlight').forEach(element => {
element.classList.remove('sch-highlight');
});
// Mark shifts with duplicate employees in red and empty shifts in light green
shifts.forEach(shift => {
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'].forEach(day => {
const shiftSlot = document.querySelector(`.sch-shift-slot[data-day="${day}"][data-shift="${shift.name}"]`);
const employeeIndices = schedule[shift.name][day];
const uniqueEmployeeIndices = new Set(employeeIndices);
if (employeeIndices.length !== uniqueEmployeeIndices.size) {
shiftSlot.classList.add('sch-highlight');
shiftSlot.style.backgroundColor = '#ffcccc'; // Light red
} else if (employeeIndices.length === 0) {
shiftSlot.style.backgroundColor = '#ccffcc'; // Light green
} else {
shiftSlot.style.backgroundColor = '';
}
});
});
}
function saveDataToCookie() {
const data = {
employees,
shifts,
schedule
};
const jsonData = JSON.stringify(data);
document.cookie = `scheduleData=${encodeURIComponent(jsonData)}; path=/`;
}
function saveDataToFile() {
const data = {
employees,
shifts,
schedule
};
const jsonData = JSON.stringify(data);
// Create a download link for the JSON file
const blob = new Blob([jsonData], { type: "application/json" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", "scheduleData.json");
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function importDataFromCookie() {
const cookies = document.cookie.split(';');
let scheduleDataCookie = cookies.find(cookie => cookie.trim().startsWith('scheduleData='));
if (scheduleDataCookie) {
const jsonData = decodeURIComponent(scheduleDataCookie.split('=')[1]);
const data = JSON.parse(jsonData);
employees = data.employees;
shifts = data.shifts;
schedule = data.schedule;
renderEmployees();
renderSchedule();
}
}
function importFromFileClick() {
document.getElementById('sch-file-input').click();
}
function handleFileImport(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
if (file.type === 'application/json') {
const data = JSON.parse(content);
employees = data.employees;
shifts = data.shifts;
schedule = data.schedule;
} else if (file.type === 'text/csv') {
importFromCSVContent(content);
}
renderEmployees();
renderSchedule();
};
reader.readAsText(file);
}
}
function importFromCSV() {
document.getElementById('sch-file-input').click();
}
function importFromCSVContent(content) {
const rows = content.split('\n');
rows.forEach(row => {
const [shiftName, day, employeeNames] = row.split(',');
if (shiftName && day && employeeNames) {
const employeeIndices = employeeNames.split(' ').map(name => {
return employees.findIndex(e => e.name === name);
});
schedule[shiftName][day] = employeeIndices;
}
});
}
function toggleDropdown(id) {
const dropdown = document.getElementById(id);
dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
}
function clearSchedule() {
if (confirm("Are you sure you want to clear the schedule?")) {
initializeSchedule();
renderSchedule();
}
}
function highlightEmployee(index) {
document.querySelectorAll('.sch-assigned-employee').forEach(employee => {
if (parseInt(employee.dataset.employee) !== index) {
employee.style.opacity = '0.6';
}
});
}
function unhighlightEmployee() {
document.querySelectorAll('.sch-assigned-employee').forEach(employee => {
employee.style.opacity = '1';
});
}
function updateEmployeeInsights() {
const shiftCounts = {};
const hourCounts = {};
employees.forEach((employee, index) => {
shiftCounts[employee.name] = 0;
hourCounts[employee.name] = 0;
});
shifts.forEach(shift => {
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'].forEach(day => {
schedule[shift.name][day].forEach(employeeIndex => {
const employee = employees[employeeIndex];
shiftCounts[employee.name]++;
hourCounts[employee.name] += getHoursDifference(shift.start, shift.end);
});
});
});
const employeeInsightsElement = document.getElementById('sch-employee-insights');
employeeInsightsElement.innerHTML = '';
for (const [name, count] of Object.entries(shiftCounts)) {
const hours = hourCounts[name];
employeeInsightsElement.innerHTML += `<p>${name}: ${count} shifts, ${hours} hours</p>`;
}
}
function getHoursDifference(startTime, endTime) {
const [startHour, startMinute] = startTime.split(':').map(Number);
const [endHour, endMinute] = endTime.split(':').map(Number);
const startDate = new Date(0, 0, 0, startHour, startMinute, 0);
const endDate = new Date(0, 0, 0, endHour, endMinute, 0);
const diff = endDate - startDate;
const hours = diff / (1000 * 60 * 60);
return hours;
}
function getTotalWeeklyHours(employeeIndex) {
let totalHours = 0;
shifts.forEach(shift => {
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'].forEach(day => {
if (schedule[shift.name][day].includes(employeeIndex)) {
totalHours += getHoursDifference(shift.start, shift.end);
}
});
});
return totalHours;
}
function copyShifts(fromDay, toDay) {
if (toDay === "all") {
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'].forEach(day => {
if (day !== fromDay) {
shifts.forEach(shift => {
schedule[shift.name][day] = [...schedule[shift.name][fromDay]];
});
}
});
} else if (toDay) {
shifts.forEach(shift => {
const dayMapping = {
mon: 'monday',
tue: 'tuesday',
wed: 'wednesday',
thu: 'thursday',
fri: 'friday',
sat: 'saturday',
sun: 'sunday'
};
schedule[shift.name][dayMapping[toDay]] = [...schedule[shift.name][fromDay]];
});
}
renderSchedule();
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function showTab(tabId) {
// Remove active class from all tabs and tab contents
document.querySelectorAll('.tab, .tab-content').forEach(element => {
element.classList.remove('active');
});
// Add active class to the clicked tab and its content
document.querySelector(`.tab[onclick="showTab('${tabId}')"]`).classList.add('active');
document.getElementById(tabId).classList.add('active');
}
// Initial setup
initializeSchedule();
renderSchedule();
renderEmployees();
</script>
</body>
</html>