one step closer in fixing timezone issue

This commit is contained in:
2026-05-27 18:44:56 +02:00
parent fb709b4a2c
commit f56492fb70
4 changed files with 33 additions and 240 deletions
+33
View File
@@ -0,0 +1,33 @@
# Frontend Systematic Audit & Cleanup Plan
## Goal
Perform a comprehensive audit and cleanup of the `frontend/` directory to remove shadowing, duplicates, corruption (appending), and dead code.
## Phase 1: Discovery (READ-ONLY)
I will conduct a comprehensive scan of the `frontend/` directory to build an accurate inventory.
* **File Shadowing Audit**: Scan for files with overlapping names but different extensions (e.g., `.js` vs `.ts`, `.jsx` vs `.tsx`) that cause build-time shadowing.
* **Implementation Duplicate Audit**: Use content searching to find identical or near-identical function/class definitions across different files.
* **Corruption Check**: Scan files for duplicate symbol declarations (identifying where code was incorrectly appended rather than replaced).
* **Dependency/Import Audit**: Cross-reference `package.json` with actual imports to identify unused packages and orphaned imports in code.
* **Slop Identification**: Locate commented-out blocks, unused variables, and dead functions.
## Phase 2: Categorization (PLANNING)
I will create a structured todo list (`FRONTEND_CLEANUP_TODO.md`) with the following categories:
* **`[DELETE]`**: Conflicting or redundant files (e.g., `client.js`).
* **`[REPAIR]`**: Corrupted files that must be completely overwritten to remove duplicates.
* **`[CLEAN]`**: Files requiring removal of unused imports, variables, or dead code.
* **`[REFACTOR]`**: Files requiring naming or pattern consistency fixes.
## Phase 3: Systematic Execution (IMPLEMENTATION)
I will execute the cleanup in this order to maintain stability:
1. **File Purge**: Remove all files in the `[DELETE]` category first to resolve shadowing.
2. **File Repair**: Use full-file rewrites for files in the `[REPAIR]` category.
3. **Slop Removal**: Systematic pass through files in the `[CLEAN]` category.
4. **Verification**:
- Run `npm install` to ensure dependency integrity.
- Run all existing tests (`npm test`) and the reproduction test to confirm functionality.
- Check for build errors/linting issues.
## Notes
- This plan will be updated and tracked via `FRONTEND_CLEANUP_TODO.md`.
- No changes will be made until the Phase 2 Todo list is reviewed and approved.
-72
View File
@@ -1,72 +0,0 @@
const BASE_URL = import.meta.env.VITE_API_BASE_URL || '';
export function localToUTC(localValue) {
return new Date(localValue).toISOString();
}
async function fetchStats(endpoint, from_date, to_date, extraParams = {}) {
const fromDT = new Date(`${from_date}T00:00:00`).toISOString();
const toDT = new Date(`${to_date}T23:59:59`).toISOString();
const params = new URLSearchParams({ from_date: fromDT, to_date: toDT, ...extraParams });
const response = await fetch(`${BASE_URL}/api/stats/${endpoint}?${params.toString()}`);
if (!response.ok)
throw new Error(`Failed to fetch ${endpoint}`);
return response.json();
}
export async function fetchDailySummary(from_date, to_date, day_start, day_end) {
return fetchStats('daily-summary', from_date, to_date, { day_start, day_end });
}
export async function fetchTimetable(from_date, to_date, day_start, day_end) {
return fetchStats('timetable', from_date, to_date, { day_start, day_end });
}
export async function fetchWakeSleepTimes(from_date, to_date, wakeup_ref, bedtime_ref) {
return fetchStats('wake-sleep-times', from_date, to_date, { wakeup_ref, bedtime_ref });
}
// Session API functions
export async function fetchActiveSession() {
const response = await fetch(`${BASE_URL}/api/sessions/active`);
if (!response.ok)
return null;
return response.json();
}
export async function fetchSessions() {
const response = await fetch(`${BASE_URL}/api/sessions`);
if (!response.ok)
throw new Error('Failed to fetch sessions');
return response.json();
}
export async function createSession(payload) {
const response = await fetch(`${BASE_URL}/api/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok)
throw new Error('Failed to create session');
return response.json();
}
export async function endSession(sessionId, payload) {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}/end`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok)
throw new Error('Failed to end session');
return response.json();
}
export async function deleteSession(sessionId) {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, {
method: 'DELETE',
});
if (!response.ok)
throw new Error('Failed to delete session');
}
export async function updateSession(sessionId, payload) {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok)
throw new Error('Failed to update session');
return response.json();
}
-79
View File
@@ -95,82 +95,3 @@ export async function updateSession(sessionId: number, payload: SleepSessionUpda
if (!response.ok) throw new Error('Failed to update session'); if (!response.ok) throw new Error('Failed to update session');
return response.json(); return response.json();
} }
export function localToUTC(localValue: string): string {
return new Date(localValue).toISOString();
}
async function fetchStats(endpoint: string, from_date: string, to_date: string, extraParams: Record<string, string> = {}): Promise<any> {
const fromDT = new Date(`${from_date}T00:00:00`).toISOString();
const toDT = new Date(`${to_date}T23:59:59`).toISOString();
const params = new URLSearchParams({ from_date: fromDT, to_date: toDT, ...extraParams });
const response = await fetch(`${BASE_URL}/api/stats/${endpoint}?${params.toString()}`);
if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`);
return response.json();
}
export async function fetchDailySummary(from_date: string, to_date: string, day_start: string, day_end: string): Promise<any> {
return fetchStats('daily-summary', from_date, to_date, { day_start, day_end });
}
export async function fetchTimetable(from_date: string, to_date: string, day_start: string, day_end: string): Promise<any> {
return fetchStats('timetable', from_date, to_date, { day_start, day_end });
}
export async function fetchWakeSleepTimes(from_date: string, to_date: string, wakeup_ref: string, bedtime_ref: string): Promise<any> {
return fetchStats('wake-sleep-times', from_date, to_date, {
wakeup_ref: localHHMMToUTCHHMM(wakeup_ref),
bedtime_ref: localHHMMToUTCHHMM(bedtime_ref)
});
}
// Session API functions
export async function fetchActiveSession(): Promise<SleepSessionRead | null> {
const response = await fetch(`${BASE_URL}/api/sessions/active`);
if (!response.ok) return null;
return response.json();
}
export async function fetchSessions(): Promise<SessionListResponse> {
const response = await fetch(`${BASE_URL}/api/sessions`);
if (!response.ok) throw new Error('Failed to fetch sessions');
return response.json();
}
export async function createSession(payload: SleepSessionCreate): Promise<SleepSessionRead> {
const response = await fetch(`${BASE_URL}/api/sessions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('Failed to create session');
return response.json();
}
export async function endSession(sessionId: number, payload: SleepSessionUpdate): Promise<SleepSessionRead> {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}/end`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('Failed to end session');
return response.json();
}
export async function deleteSession(sessionId: number): Promise<void> {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, {
method: 'DELETE',
});
if (!response.ok) throw new Error('Failed to delete session');
}
export async function updateSession(sessionId: number, payload: SleepSessionUpdate): Promise<SleepSessionRead> {
const response = await fetch(`${BASE_URL}/api/sessions/${sessionId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('Failed to update session');
return response.json();
}
-89
View File
@@ -1,89 +0,0 @@
import datetime
def solve():
# --- CONSTANTS FROM DATABASE/USER ---
# Actual wakeup time (UTC) from DB for session 1
wakeup_utc_str = "2026-05-27 03:17:00"
wakeup_utc = datetime.datetime.strptime(wakeup_utc_str, "%Y-%m-%d %H:%M:%S")
wakeup_utc_mins = wakeup_utc.hour * 60 + wakeup_utc.minute
# User's local reference time (Stockholm, UTC+2)
local_ref_str = "04:00"
local_ref_mins = 4 * 60
# Stockholm offset (UTC+2) -> getTimezoneOffset() returns -120
offset_in_minutes = -120
print(f"DATABASE WAKEUP (UTC): {wakeup_utc_str} ({wakeup_utc_mins} mins from midnight)")
print(f"LOCAL REFERENCE: {local_ref_str} ({local_ref_mins} mins from midnight)")
print(f"STOCKHOLM OFFSET: {offset_in_minutes} (getTimezoneOffset style)")
# --- SCENARIO 1: THE BUGGY WAY (Current implementation) ---
# Backend receives local "04:00" but treats it as UTC.
# Backend calculates delta = (UTC_wakeup - Ref_as_if_UTC)
ref_as_utc = local_ref_mins # 240
buggy_delta = wakeup_utc_mins - ref_as_utc # 197 - 240 = -43
print(f"\n[BUGGY SCENARIO]")
print(f"Backend returns (wrong) delta: {buggy_delta}")
# Frontend receives -43 and tries to adjust it.
# offsetFromRef = (received_delta - offset) - ref_mins_local
# wait, the logic was: localMins = (received + abs(offset)).
# Let's follow the code exactly:
# adjustForTimezone(delta) = delta - offset_in_minutes
# (Wait, if the component thinks 'delta' is absolute minutes from midnight?)
# The user said: "Render the Wakeup Times given -43 as value ... results to 01:17"
# Let's see how to get 01:17 from -43 and ref 04:00.
# 240 (ref) - 163 = 77? No.
# If offsetFromRef is -163: formatMinutesAfter(-163, "04:00") -> 240 - 163 = 77 (01:17).
# How do we get -163 from -43?
# -43 - 120 = -163. (Ah! It's subtracting the absolute offset instead of handling sign).
# Or: -43 + (-120) = -163.
# This happens if the code does: delta + offset_in_minutes (where offset is -120).
# Wait, if offset is -120: -43 + (-120) = -163.
# So the component is doing: localMins = delta + offset_in_minutes.
# Then offsetFromRef = localMins - ref_mins = (-163) - 240 = -403? No.
# Let's check component:
# offsetFromRef = getLocalOffsetFromReference(day.wakeup_minutes, wakeup_ref);
# which is: (wakeup_minutes - offsetInMinutes) - ref_mins.
# If wakeup_minutes = -43 and offsetInMinutes = 120 (if sign is flipped):
# (-43 - 120) - 240 = -163 - 240 = -403.
# If offsetInMinutes = -120:
# (-43 - (-120)) - 240 = (77) - 240 = -163.
# YES! That's exactly it.
# (-43 - (-120)) - 240 = -163.
# formatMinutesAfter(-163, "04:00") -> 240 + (-163) = 77.
# 77 minutes is 01:17. Correct!
def buggy_frontend(delta, ref_mins, offset):
local_m = delta - offset
return local_m - ref_mins
buggy_offset_from_ref = buggy_frontend(buggy_delta, local_ref_mins, offset_in_minutes)
print(f"BUGGY Frontend offsetFromRef: {buggy_offset_from_ref}")
print(f"BUGGY Frontend Display: {local_ref_mins + buggy_offset_from_ref} (Minutes from midnight)")
# --- SCENARIO 2: THE FIXED WAY (My proposed plan) ---
# 1. Frontend converts local ref "04:00" to UTC "02:00".
# 2. Backend receives ref=120. Calculates delta = 197 - 120 = 77.
# 3. Frontend receives delta=77 and uses it DIRECTLY as offsetFromRef.
correct_delta = wakeup_utc_mins - (local_ref_mins + offset_in_minutes)
print(f"\n[FIXED SCENARIO]")
print(f"Backend returns (correct) delta: {correct_delta}")
# Frontend uses it directly.
fixed_offset_from_ref = correct_delta
print(f"FIXED Frontend offsetFromRef: {fixed_offset_from_ref}")
print(f"FIXED Frontend Display: {local_ref_mins + fixed_offset_from_ref} (Minutes from midnight)")
# ASSERT
expected_mins = 5 * 60 + 17 # 317
actual_mins = local_ref_mins + fixed_offset_from_ref
print(f"\nASSERTION: Expected {expected_mins}, Actual {actual_mins}")
if actual_mins == expected_mins:
print("SUCCESS!")
else:
print("FAILURE!")
solve()