GPT : Timezone and reporting improvements

Add timezone-aware timestamp support and reporting improvements
Store timestamps in ISO-8601 format with timezone offsets
Accept client-local timestamps from browser and API
Normalize legacy naive timestamps during parsing
Add migration script for converting old SQLite timestamps
Fix mixed naive/aware datetime comparison errors
Render timestamps in browser-local timezone
Auto-generate default 7-day reports on page load
Expand default dashboard report to include all users
Preserve manual single-user report generation
Add curl-based validation/test script for API and report flows
This commit is contained in:
2026-05-29 10:55:56 -04:00
parent f175321283
commit 4ee4ced6eb
4 changed files with 473 additions and 84 deletions

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<title>Time Clock</title>
</head>
@@ -13,7 +14,13 @@
{% if clocked_in_users %}
<ul>
{% for user in clocked_in_users %}
<li>{{ user.name }}</li>
<li>
{{ user.name }} —
since
<span class="tztime" data-ts="{{ user.since }}">
{{ user.since }}
</span>
</li>
{% endfor %}
</ul>
{% else %}
@@ -21,21 +28,58 @@
{% endif %}
<br/>
{% if all_user_reports %}
<h2>Last 7 Days Report</h2>
<table border="1" cellpadding="5">
<tr>
<th>User</th>
<th>Total Hours Worked</th>
</tr>
{% for report in all_user_reports %}
<tr>
<td>{{ report.name }}</td>
<td>{{ report.hours }}</td>
</tr>
{% endfor %}
</table>
<br>
{% endif %}
<hr/>
<br/>
<form method="POST">
<form method="POST" id="timeclock-form">
<input
type="hidden"
name="client_timestamp"
id="client_timestamp"
>
<table border="1" cellpadding="5">
<tr>
<td>User</td>
<td>
<select name="user_id">
{% for user in users %}
<option value="{{ user.id }}">
{{ user.name }}
<option
value="{{ user.id }}"
{% if selected_user_id == user.id %}
selected
{% endif %}
>
{{ user.name }}
</option>
{% endfor %}
@@ -47,13 +91,25 @@
<td>Clock Actions</td>
<td>
<button type="submit" name="action" value="clock_in">
<button
type="submit"
name="action"
value="clock_in"
onclick="setCurrentTimestamp()"
>
Clock In
</button>
<button type="submit" name="action" value="clock_out">
<button
type="submit"
name="action"
value="clock_out"
onclick="setCurrentTimestamp()"
>
Clock Out
</button>
</td>
</tr>
@@ -64,6 +120,7 @@
<input
type="datetime-local"
name="begin_date"
id="begin_date"
value=""
>
</td>
@@ -76,6 +133,7 @@
<input
type="datetime-local"
name="end_date"
id="end_date"
value=""
>
</td>
@@ -85,7 +143,11 @@
<td>Report</td>
<td>
<button type="submit" name="action" value="report">
<button
type="submit"
name="action"
value="report"
>
Generate Report
</button>
</td>
@@ -99,6 +161,8 @@
{% if report_hours is not none %}
<h2>Custom Report</h2>
<table border="1" cellpadding="5">
<tr>
@@ -113,5 +177,60 @@
{% endif %}
<script>
function localDatetimeValue(date) {
const pad = (v) => String(v).padStart(2, "0");
return (
date.getFullYear() + "-" +
pad(date.getMonth() + 1) + "-" +
pad(date.getDate()) + "T" +
pad(date.getHours()) + ":" +
pad(date.getMinutes())
);
}
function setCurrentTimestamp() {
document.getElementById("client_timestamp").value =
new Date().toISOString();
}
window.addEventListener("load", () => {
//
// Populate default report range
//
const now = new Date();
const weekAgo = new Date();
weekAgo.setDate(now.getDate() - 7);
document.getElementById("begin_date").value =
localDatetimeValue(weekAgo);
document.getElementById("end_date").value =
localDatetimeValue(now);
//
// Convert displayed timestamps to local timezone
//
document.querySelectorAll(".tztime").forEach((el) => {
const ts = el.dataset.ts;
const d = new Date(ts);
el.textContent = d.toLocaleString();
});
});
</script>
</body>
</html>