332 lines
11 KiB
HTML
332 lines
11 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
|
||
|
<head>
|
||
|
<meta charset="UTF-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
<title>Break Hour Processing</title>
|
||
|
<script>
|
||
|
document.addEventListener('DOMContentLoaded', () => {
|
||
|
// const url = 'https://breakhours.fleks.c7a.nl'
|
||
|
const url = 'https://localhost:22000'
|
||
|
|
||
|
const shiftsUrl = `${url}/api/shifts`;
|
||
|
const timeTableUrl = `${url}/api/time-table`;
|
||
|
const postUrl = `${url}/api/shift-update`;
|
||
|
const putUrl = `${url}/api/shifts`;
|
||
|
|
||
|
document.getElementById('refresh-data').addEventListener('click', async () => {
|
||
|
const apiKey = document.getElementById('api-key').value;
|
||
|
const userName = document.getElementById('username').value;
|
||
|
const passWord = document.getElementById('password').value;
|
||
|
|
||
|
if (!userName || !passWord || !apiKey) {
|
||
|
alert('Please enter username, password and apiKey');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
const shiftsData = await fetchData('POST', shiftsUrl, apiKey);
|
||
|
const timeTable = await fetchData('GET', timeTableUrl);
|
||
|
|
||
|
const resultContainer = document.getElementById('result');
|
||
|
resultContainer.innerHTML = '';
|
||
|
|
||
|
const summaryStats = { totalNeedsChange: 0, totalDoesNotNeedChange: 0, totalNoMatch: 0 };
|
||
|
const shiftsTable = createShiftsTable(shiftsData, timeTable, summaryStats);
|
||
|
const statsTable = createStatsTable(summaryStats, shiftsData.results.length);
|
||
|
|
||
|
resultContainer.appendChild(statsTable);
|
||
|
resultContainer.appendChild(shiftsTable);
|
||
|
|
||
|
const selectAllCheckbox = document.getElementById('select-all');
|
||
|
if (selectAllCheckbox) {
|
||
|
selectAllCheckbox.addEventListener('click', () => {
|
||
|
const checkboxes = document.querySelectorAll('.shift-row.needs-change input[type="checkbox"]');
|
||
|
checkboxes.forEach(checkbox => checkbox.checked = true);
|
||
|
});
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error('Failed to fetch data:', error);
|
||
|
alert('Error fetching data. Check the console for more details.');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
document.addEventListener('click', async (event) => {
|
||
|
if (event.target.classList.contains('post-button')) {
|
||
|
const row = event.target.closest('tr');
|
||
|
const shiftUuid = event.target.dataset.uuid;
|
||
|
const newBreakMinutes = parseInt(row.cells[7].textContent.split(' ')[0]); // Assuming new break hours are in the format "XX min"
|
||
|
const newBreakHours = minutesToHoursMinutes(newBreakMinutes); // Convert to h:mm format
|
||
|
|
||
|
const userName = document.getElementById('username').value;
|
||
|
const passWord = document.getElementById('password').value;
|
||
|
const apiKey = document.getElementById('api-key').value;
|
||
|
const limit = document.getElementById('limit').value || 0;
|
||
|
|
||
|
if (!userName || !passWord || !apiKey) {
|
||
|
alert('Please enter username, password and apiKey');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const body = [
|
||
|
{ uuid: shiftUuid, break_hours: newBreakHours, userName, passWord, limit: null }
|
||
|
];
|
||
|
|
||
|
try {
|
||
|
await fetch(putUrl, {
|
||
|
method: 'PUT',
|
||
|
headers: { 'Content-Type': 'application/json' },
|
||
|
body: JSON.stringify(body)
|
||
|
});
|
||
|
alert(`Break hour update for ${shiftUuid} was successful.`);
|
||
|
} catch (error) {
|
||
|
alert(`Break hour update for ${shiftUuid} failed.`);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
document.getElementById('queue-requests').addEventListener('click', async () => {
|
||
|
|
||
|
const userName = document.getElementById('username').value;
|
||
|
const passWord = document.getElementById('password').value;
|
||
|
const apiKey = document.getElementById('api-key').value;
|
||
|
const limit = 0 // document.getElementById('limit').value || 0;
|
||
|
|
||
|
|
||
|
if (!userName || !passWord || !apiKey) {
|
||
|
alert('Please enter username, password and apiKey');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const config = { userName, passWord, apiKey, limit }
|
||
|
|
||
|
const selectedCheckboxes = document.querySelectorAll('.shift-row input[type="checkbox"]:checked');
|
||
|
const shifts = [];
|
||
|
|
||
|
selectedCheckboxes.forEach(checkbox => {
|
||
|
const row = checkbox.closest('tr');
|
||
|
const shiftUuid = checkbox.dataset.uuid;
|
||
|
const newBreakMinutes = parseInt(row.cells[7].textContent.split(' ')[0]);
|
||
|
const newBreakHours = minutesToHoursMinutes(newBreakMinutes);
|
||
|
|
||
|
shifts.push({ uuid: shiftUuid, break_hours: newBreakHours });
|
||
|
});
|
||
|
|
||
|
if (shifts.length === 0) {
|
||
|
alert('No shifts selected.');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
await fetch(putUrl, {
|
||
|
method: 'PUT',
|
||
|
headers: { 'Content-Type': 'application/json' },
|
||
|
body: JSON.stringify({ config, shifts })
|
||
|
});
|
||
|
alert('Break hour updates were successful.');
|
||
|
} catch (error) {
|
||
|
alert('Break hour updates failed.');
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
function minutesToHoursMinutes(minutes) {
|
||
|
const hours = Math.floor(minutes / 60);
|
||
|
const mins = minutes % 60;
|
||
|
return `${hours}:${mins.toString().padStart(2, '0')}`;
|
||
|
}
|
||
|
|
||
|
async function fetchData(method, url, apiKey) {
|
||
|
const options = {
|
||
|
method: method || 'GET',
|
||
|
headers: { 'Content-Type': 'application/json' },
|
||
|
};
|
||
|
|
||
|
if (method === 'POST') {
|
||
|
options.body = JSON.stringify({ apiKey })
|
||
|
}
|
||
|
|
||
|
const response = await fetch(url, options);
|
||
|
if (!response.ok) throw new Error('Network response was not ok.');
|
||
|
return await response.json();
|
||
|
}
|
||
|
|
||
|
function createShiftsTable(shiftsData, timeTable, summaryStats) {
|
||
|
const sortedShifts = shiftsData.results.sort((a, b) => a.project_title.localeCompare(b.project_title));
|
||
|
|
||
|
const table = document.createElement('table');
|
||
|
table.innerHTML = `
|
||
|
<tr>
|
||
|
<th><input type="checkbox" id="select-all"></th>
|
||
|
<th>Shift UUID</th>
|
||
|
<th>Job ID</th>
|
||
|
<th>Project Title</th>
|
||
|
<th>Start Time</th>
|
||
|
<th>End Time</th>
|
||
|
<th>Old Break Hours (Decimal/Minutes)</th>
|
||
|
<th>New Break Hours</th>
|
||
|
<th>Post</th>
|
||
|
</tr>`;
|
||
|
|
||
|
sortedShifts.forEach(shift => {
|
||
|
const row = createShiftRow(shift, timeTable, summaryStats);
|
||
|
table.appendChild(row);
|
||
|
});
|
||
|
|
||
|
return table;
|
||
|
}
|
||
|
|
||
|
function createShiftRow(shift, timeTable, summaryStats) {
|
||
|
const startTime = shift.start_time.slice(0, 5);
|
||
|
const endTime = shift.end_time.slice(0, 5);
|
||
|
const matchedTime = timeTable.find(entry =>
|
||
|
entry.start_time.replace('.', ':') === startTime &&
|
||
|
entry.end_time.replace('.', ':') === endTime
|
||
|
);
|
||
|
|
||
|
const oldBreakDecimal = parseFloat(shift.break_compensation);
|
||
|
const oldBreakMinutes = Math.round(oldBreakDecimal * 60);
|
||
|
const newBreakMinutes = matchedTime ? Math.round(parseFloat(matchedTime.break_hours) * 60) : "N/A";
|
||
|
|
||
|
const statusColor = newBreakMinutes === "N/A" ? 'red' : (oldBreakMinutes === newBreakMinutes ? 'green' : 'orange');
|
||
|
const statusClass = newBreakMinutes === "N/A" ? 'no-match' : (oldBreakMinutes === newBreakMinutes ? 'no-change' : 'needs-change');
|
||
|
updateSummaryStats(newBreakMinutes, oldBreakMinutes, summaryStats);
|
||
|
|
||
|
const tr = document.createElement('tr');
|
||
|
tr.classList.add('shift-row', statusClass);
|
||
|
tr.innerHTML = `
|
||
|
<td><input type="checkbox" data-uuid="${shift.shifts_uuid}"></td>
|
||
|
<td>${shift.shifts_uuid}</td>
|
||
|
<td>${shift.job_id}</td>
|
||
|
<td>${shift.project_title}</td>
|
||
|
<td>${startTime}</td>
|
||
|
<td>${endTime}</td>
|
||
|
<td>${oldBreakDecimal.toFixed(2)} / ${oldBreakMinutes} min</td>
|
||
|
<td style="background-color: ${statusColor}">${newBreakMinutes} min</td>
|
||
|
<td><button class="post-button" data-uuid="${shift.shifts_uuid}">Post</button></td>
|
||
|
`;
|
||
|
return tr;
|
||
|
}
|
||
|
|
||
|
function updateSummaryStats(newBreakMinutes, oldBreakMinutes, summaryStats) {
|
||
|
if (newBreakMinutes === "N/A") summaryStats.totalNoMatch++;
|
||
|
else if (oldBreakMinutes === newBreakMinutes) summaryStats.totalDoesNotNeedChange++;
|
||
|
else summaryStats.totalNeedsChange++;
|
||
|
}
|
||
|
|
||
|
function createStatsTable(summaryStats, totalShifts) {
|
||
|
const table = document.createElement('table');
|
||
|
table.classList.add('stats-table');
|
||
|
table.innerHTML = `<tr><th>Type</th><th>Count</th><th>Show</th></tr>
|
||
|
<tr>
|
||
|
<td>Needs Change</td><td>${summaryStats.totalNeedsChange}</td>
|
||
|
<td><input type="checkbox" checked onclick="toggleVisibility('needs-change', this.checked)"></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Does Not Need Change</td><td>${summaryStats.totalDoesNotNeedChange}</td>
|
||
|
<td><input type="checkbox" checked onclick="toggleVisibility('no-change', this.checked)"></td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>No Match</td><td>${summaryStats.totalNoMatch}</td>
|
||
|
<td><input type="checkbox" checked onclick="toggleVisibility('no-match', this.checked)"></td>
|
||
|
</tr>
|
||
|
<tr><th>Total Shifts</th><th colspan="2">${totalShifts}</th></tr>`;
|
||
|
return table;
|
||
|
}
|
||
|
|
||
|
function toggleVisibility(className, isVisible) {
|
||
|
const rows = document.querySelectorAll(`.${className}`);
|
||
|
rows.forEach(row => {
|
||
|
row.style.display = isVisible ? '' : 'none';
|
||
|
});
|
||
|
}
|
||
|
</script>
|
||
|
<style>
|
||
|
body {
|
||
|
font-family: Arial, sans-serif;
|
||
|
margin: 20px;
|
||
|
}
|
||
|
|
||
|
.container {
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
align-items: flex-start;
|
||
|
gap: 20px;
|
||
|
}
|
||
|
|
||
|
.inputs-container {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
gap: 10px;
|
||
|
}
|
||
|
|
||
|
.inputs-container input {
|
||
|
flex: 1;
|
||
|
padding: 5px;
|
||
|
font-size: 1em;
|
||
|
}
|
||
|
|
||
|
.button-container {
|
||
|
display: flex;
|
||
|
align-items: center;
|
||
|
gap: 10px;
|
||
|
}
|
||
|
|
||
|
.button-container button {
|
||
|
padding: 5px 10px;
|
||
|
font-size: 1em;
|
||
|
width: 150px;
|
||
|
}
|
||
|
|
||
|
table,
|
||
|
th,
|
||
|
td {
|
||
|
border: 1px solid black;
|
||
|
border-collapse: collapse;
|
||
|
margin: 10px 0;
|
||
|
width: 100%;
|
||
|
table-layout: fixed;
|
||
|
}
|
||
|
|
||
|
th,
|
||
|
td {
|
||
|
padding: 10px;
|
||
|
text-align: left;
|
||
|
}
|
||
|
|
||
|
th {
|
||
|
background-color: #f2f2f2;
|
||
|
}
|
||
|
|
||
|
td:nth-child(1),
|
||
|
th:nth-child(1) {
|
||
|
width: 11.11%;
|
||
|
}
|
||
|
|
||
|
.stats-table th:nth-child(1),
|
||
|
.stats-table td:nth-child(1) {
|
||
|
width: 50px;
|
||
|
text-align: center;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
|
||
|
<body>
|
||
|
<div class="container">
|
||
|
<h1>Break Hour Processing</h1>
|
||
|
<div class="inputs-container">
|
||
|
<input type="text" id="username" placeholder="Enter your username" size="30">
|
||
|
<input type="password" id="password" placeholder="Enter your password" size="30">
|
||
|
<input type="text" id="api-key" placeholder="Enter your API key here" size="30">
|
||
|
</div>
|
||
|
|
||
|
<div class="button-container">
|
||
|
<button id="refresh-data">Refresh Data</button>
|
||
|
<button id="queue-requests">Queue Requests</button>
|
||
|
</div>
|
||
|
<div id="result"></div>
|
||
|
</div>
|
||
|
</body>
|
||
|
|
||
|
</html>
|