timetracker/frontend/edit.html
hhftechnologies c8f5379289 update
2024-11-25 23:16:17 +05:30

420 lines
No EOL
13 KiB
HTML

<!DOCTYPE html>
<html lang="en" x-data="appData()" x-init="init()">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Time Entry</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<link href="/frontend/dist/styles.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<script src="https://unpkg.com/alpinejs" defer></script>
<style>
/* Default (Light Mode) Styles */
body {
background-color: #f5f5f5;
color: #333;
transition: background-color 0.3s, color 0.3s;
text-align: center;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
overflow: hidden;
}
/* Time Tracker Card */
.time-tracker {
background-color: #fff;
color: #333;
transition: background-color 0.3s, color 0.3s;
max-width: 800px;
z-index: 10;
position: relative;
margin: 0 auto;
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
/* Dark Mode Styles */
body.dark-mode {
background-color: #1a202c;
color: #cbd5e0;
}
.dark-mode .time-tracker {
background-color: #2d3748;
color: #cbd5e0;
}
/* SVG Background */
.svg-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
/* Buttons */
.btn {
display: inline-block;
padding: 0.5em 1.5em;
font-size: 1em;
font-weight: 500;
color: white;
background-color: #4CAF50;
border: none;
border-radius: 0.375em;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.1s ease;
}
.btn:hover {
background-color: #45a049;
transform: scale(1.05);
}
.btn-danger {
background-color: #e53e3e;
}
.btn-danger:hover {
background-color: #c53030;
}
/* Dark Mode for Buttons */
.dark-mode .btn {
background-color: #3182ce;
}
.dark-mode .btn:hover {
background-color: #2b6cb0;
}
/* DateTime Input Styles */
input[type="datetime-local"] {
width: 100%;
padding: 0.5em;
margin: 0.5em 0;
border: 1px solid #ccc;
border-radius: 0.375em;
font-size: 1em;
transition: border-color 0.2s ease;
}
input[type="datetime-local"]:focus {
outline: none;
border-color: #3182ce;
}
.dark-mode input[type="datetime-local"] {
background-color: #2d3748;
color: #cbd5e0;
border-color: #4a5568;
}
.dark-mode input[type="datetime-local"]:focus {
border-color: #63b3ed;
}
/* Modal */
#edit-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
justify-content: center;
align-items: center;
}
#edit-modal .modal-content {
background-color: white;
padding: 20px;
margin: auto;
width: 50%;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
/* Dark Mode for Modal */
.dark-mode #edit-modal .modal-content {
background-color: #2d3748;
color: #cbd5e0;
}
/* Table Styles */
table {
width: 100%;
border-collapse: collapse;
margin-top: 1em;
}
thead th {
background-color: #e2e8f0;
color: #2d3748;
padding: 0.75em;
border-bottom: 2px solid #cbd5e0;
}
tbody tr:nth-child(even) {
background-color: #f7fafc;
}
tbody tr:nth-child(odd) {
background-color: #ffffff;
}
tbody td {
padding: 0.75em;
border-bottom: 1px solid #cbd5e0;
}
body.dark-mode table {
background-color: #2d3748;
color: #cbd5e0;
}
body.dark-mode thead th {
background-color: #4a5568;
color: #edf2f7;
}
body.dark-mode tbody tr:nth-child(even) {
background-color: #2c3440;
}
body.dark-mode tbody tr:nth-child(odd) {
background-color: #1f2733;
}
body.dark-mode tbody td {
border-bottom: 1px solid #4a5568;
}
</style>
</head>
<body :class="{ 'dark-mode': isDarkMode }">
<!-- SVG Wave Animation Background -->
<svg class="svg-background" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100%" height="100%" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMax slice">
<defs>
<linearGradient id="bg">
<stop offset="0%" style="stop-color:rgba(130, 158, 249, 0.06)"></stop>
<stop offset="50%" style="stop-color:rgba(76, 190, 255, 0.6)"></stop>
<stop offset="100%" style="stop-color:rgba(115, 209, 72, 0.2)"></stop>
</linearGradient>
<path id="wave" fill="url(#bg)" d="M-363.852,452.589c0,0,236.988-91.997,505.475,0s371.981,88.998,575.971,0s293.985-89.278,505.474,5.859s493.475,98.368,716.963-4.995v560.106H-363.852V452.589z" />
</defs>
<g>
<use xlink:href='#wave' opacity=".3">
<animateTransform attributeName="transform" attributeType="XML" type="translate" dur="10s" calcMode="spline"
values="270 230; -334 180; 270 230" keyTimes="0; .5; 1" keySplines="0.42, 0, 0.58, 1.0;0.42, 0, 0.58, 1.0" repeatCount="indefinite" />
</use>
<use xlink:href='#wave' opacity=".6">
<animateTransform attributeName="transform" attributeType="XML" type="translate" dur="8s" calcMode="spline"
values="-270 230;243 220;-270 230" keyTimes="0; .6; 1" keySplines="0.42, 0, 0.58, 1.0;0.42, 0, 0.58, 1.0" repeatCount="indefinite" />
</use>
<use xlink:href='#wave' opacity=".9">
<animateTransform attributeName="transform" attributeType="XML" type="translate" dur="6s" calcMode="spline"
values="0 230;-140 200;0 230" keyTimes="0; .4; 1" keySplines="0.42, 0, 0.58, 1.0;0.42, 0, 0.58, 1.0" repeatCount="indefinite" />
</use>
</g>
</svg>
<!-- Main Content -->
<div class="relative max-w-lg w-full time-tracker shadow-2xl rounded-xl p-8 z-10">
<h1 class="text-4xl font-bold mb-6 text-center">Edit Time Entry</h1>
<!-- Go Home Button -->
<div class="text-center mb-4">
<a href="/" class="btn">Go Home</a>
</div>
<!-- Form to select a user -->
<form id="user-form">
<label for="user">Select User:</label>
<select id="user" name="user" required></select>
<button class="btn" type="submit">Fetch Times</button>
</form>
<!-- Table to display clock-in/clock-out times -->
<table id="time-table" class="time-entry-table mt-6" style="display:none;">
<thead>
<tr>
<th>Date</th>
<th>Clock In</th>
<th>Clock Out</th>
<th>Edit</th>
</tr>
</thead>
<tbody></tbody>
</table>
<!-- Edit Form Modal -->
<div id="edit-modal" class="hidden flex">
<div class="modal-content">
<h2>Edit Clock In/Out Times for <span id="edit-date"></span></h2>
<form id="edit-form">
<input type="hidden" id="entry-id">
<label for="clock_in">Clock In:</label>
<input type="datetime-local" id="edit-clock-in" name="clock_in">
<br>
<label for="clock_out">Clock Out:</label>
<input type="datetime-local" id="edit-clock-out" name="clock_out">
<br>
<button class="btn" type="submit">Save Changes</button>
<button class="btn btn-danger" type="button" onclick="closeModal()">Cancel</button>
</form>
</div>
</div>
</div>
<script>
function appData() {
return {
isDarkMode: false,
// Initialize the app
init() {
this.isDarkMode = localStorage.getItem('darkMode') === 'true';
if (this.isDarkMode) {
document.body.classList.add('dark-mode');
} else {
document.body.classList.remove('dark-mode');
}
},
// Toggle between dark mode and light mode
toggleDarkMode() {
this.isDarkMode = !this.isDarkMode;
localStorage.setItem('darkMode', this.isDarkMode);
document.body.classList.toggle('dark-mode', this.isDarkMode);
},
};
}
async function fetchUsers() {
const response = await fetch('/users');
const users = await response.json();
const userSelect = document.getElementById('user');
users.forEach(user => {
const option = document.createElement('option');
option.value = user.name;
option.text = user.name;
userSelect.add(option);
});
}
function formatTime(datetimeString) {
if (!datetimeString) return 'N/A';
const date = new Date(datetimeString);
const options = {
hour: 'numeric',
minute: 'numeric',
hour12: true, // Set to false for 24-hour format
};
return date.toLocaleTimeString([], options);
}
async function fetchTimeEntries(user) {
const response = await fetch(`/time/${user}/recall/month`);
const data = await response.json();
const tableBody = document.getElementById('time-table').getElementsByTagName('tbody')[0];
tableBody.innerHTML = ''; // Clear existing table data
data.entries.forEach(entry => {
const row = tableBody.insertRow();
row.insertCell(0).innerText = entry.date;
row.insertCell(1).innerText = entry.clock_in ? formatTime(entry.clock_in) : 'N/A';
row.insertCell(2).innerText = entry.clock_out ? formatTime(entry.clock_out) : 'N/A';
const editCell = row.insertCell(3);
const editButton = document.createElement('button');
editButton.innerText = 'Edit';
editButton.className = 'btn';
editButton.onclick = () => openEditModal(user, entry.date, entry.clock_in, entry.clock_out);
editCell.appendChild(editButton);
});
document.getElementById('time-table').style.display = 'table'; // Show table
}
function toLocalDatetimeInputValue(date) {
if (!date) return '';
const dt = new Date(date);
const year = dt.getFullYear();
const month = ('0' + (dt.getMonth() + 1)).slice(-2);
const day = ('0' + dt.getDate()).slice(-2);
const hours = ('0' + dt.getHours()).slice(-2);
const minutes = ('0' + dt.getMinutes()).slice(-2);
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
function localDatetimeToUTC(datetimeLocal) {
if (!datetimeLocal) return null;
const localDate = new Date(datetimeLocal);
return localDate.toISOString();
}
function openEditModal(user, date, clockIn, clockOut) {
document.getElementById('edit-date').innerText = date;
document.getElementById('entry-id').value = date;
document.getElementById('edit-clock-in').value = clockIn ? toLocalDatetimeInputValue(clockIn) : '';
document.getElementById('edit-clock-out').value = clockOut ? toLocalDatetimeInputValue(clockOut) : '';
document.getElementById('edit-modal').style.display = 'flex';
}
function closeModal() {
document.getElementById('edit-modal').style.display = 'none';
}
async function submitForm(event) {
event.preventDefault();
const user = document.getElementById('user').value;
const date = document.getElementById('entry-id').value;
const clockIn = document.getElementById('edit-clock-in').value;
const clockOut = document.getElementById('edit-clock-out').value;
const clockInUTC = clockIn ? localDatetimeToUTC(clockIn) : null;
const clockOutUTC = clockOut ? localDatetimeToUTC(clockOut) : null;
const response = await fetch(`/time/${user}/edit`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
date: date,
clock_in_time: clockInUTC,
clock_out_time: clockOutUTC,
}),
});
if (response.ok) {
alert('Times updated successfully');
closeModal();
fetchTimeEntries(user);
} else {
const error = await response.json();
alert('Error updating times: ' + error.message);
}
}
document.getElementById('user-form').addEventListener('submit', function(event) {
event.preventDefault();
const user = document.getElementById('user').value;
fetchTimeEntries(user);
});
document.getElementById('edit-form').addEventListener('submit', submitForm);
fetchUsers();
</script>
</body>
</html>