refactor: update SL departure and clock components to respect AlwaysOn mode
This commit is contained in:
parent
28c41577ec
commit
f78b058303
4 changed files with 41 additions and 51 deletions
|
|
@ -1,17 +1,5 @@
|
|||
export const LineBadge = ({ designation, mode }: { designation: string; mode: string }) => {
|
||||
const getColor = (mode: string) => {
|
||||
switch (mode) {
|
||||
case 'METRO': return 'bg-sl-blue';
|
||||
case 'BUS': return 'bg-sl-red';
|
||||
case 'TRAM': return 'bg-sl-green';
|
||||
case 'TRAIN': return 'bg-sl-orange';
|
||||
default: return 'bg-gray-600';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<span className={`${getColor(mode)} text-white px-3 py-1 rounded font-bold min-w-[60px] text-center text-xl inline-block`}>
|
||||
export const LineBadge = ({ designation }: { designation: string; mode?: string; groupOfLines?: string }) => (
|
||||
<span className="border border-white text-white px-2 py-0.5 font-bold min-w-[56px] text-center text-xl inline-block">
|
||||
{designation}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,30 +1,40 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useAlwaysOn } from '../../contexts/AlwaysOnContext';
|
||||
|
||||
export const Clock = () => {
|
||||
const alwaysOn = useAlwaysOn();
|
||||
const [time, setTime] = useState(new Date());
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => setTime(new Date()), 1000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
if (!alwaysOn) {
|
||||
const id = setInterval(() => setTime(new Date()), 1000);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
|
||||
const timeStr = time.toLocaleTimeString('en-GB', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
});
|
||||
// Sync to the next full minute, then tick every 60 s
|
||||
let interval: ReturnType<typeof setInterval>;
|
||||
const now = new Date();
|
||||
const msUntilNext = (60 - now.getSeconds()) * 1000 - now.getMilliseconds();
|
||||
const timeout = setTimeout(() => {
|
||||
setTime(new Date());
|
||||
interval = setInterval(() => setTime(new Date()), 60_000);
|
||||
}, msUntilNext);
|
||||
|
||||
return () => { clearTimeout(timeout); clearInterval(interval); };
|
||||
}, [alwaysOn]);
|
||||
|
||||
const timeStr = alwaysOn
|
||||
? time.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false })
|
||||
: time.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false });
|
||||
|
||||
const dateStr = time.toLocaleDateString('en-GB', {
|
||||
weekday: 'long',
|
||||
day: 'numeric',
|
||||
month: 'long'
|
||||
weekday: 'long', day: 'numeric', month: 'long',
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<div className="text-8xl font-bold tracking-tight">{timeStr}</div>
|
||||
<div className="text-2xl text-gray-400 mt-2 font-medium">{dateStr}</div>
|
||||
<div className="flex items-baseline gap-6">
|
||||
<div className={`text-7xl ${alwaysOn ? 'font-light' : 'font-bold'}`}>{timeStr}</div>
|
||||
<div className="text-2xl text-gray-400">{dateStr}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import { LineBadge } from '../atoms/LineBadge';
|
||||
import { Departure } from '../../types';
|
||||
import { useAlwaysOn } from '../../contexts/AlwaysOnContext';
|
||||
|
||||
export const DepartureItem = ({ departure }: { departure: Departure }) => {
|
||||
const alwaysOn = useAlwaysOn();
|
||||
const depTime = new Date(departure.scheduled);
|
||||
const diffMin = Math.round((depTime.getTime() - Date.now()) / 60000);
|
||||
const timeText = diffMin <= 0 ? 'Now' : `${diffMin} min`;
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center px-5 py-4 bg-surface rounded-xl text-3xl font-medium transition hover:bg-zinc-800">
|
||||
<div className="flex items-center gap-6">
|
||||
<LineBadge designation={departure.line.designation} mode={departure.line.transport_mode} />
|
||||
<span className="truncate max-w-[45vw]">{departure.destination}</span>
|
||||
<div className={`flex justify-between items-center py-3 border-b border-zinc-700 text-2xl ${alwaysOn ? 'font-light' : ''}`}>
|
||||
<div className="flex items-center gap-4">
|
||||
<LineBadge designation={departure.line.designation} mode={departure.line.transport_mode} groupOfLines={departure.line.group_of_lines} />
|
||||
<span>{departure.destination}</span>
|
||||
</div>
|
||||
<span className="font-bold text-sl-green whitespace-nowrap ml-6">
|
||||
{timeText}
|
||||
</span>
|
||||
<span className={alwaysOn ? 'font-normal' : 'font-bold'}>{timeText}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,19 +2,11 @@ import { DepartureItem } from '../molecules/DepartureItem';
|
|||
import { Departure } from '../../types';
|
||||
|
||||
export const DepartureList = ({ departures, loading }: { departures: Departure[]; loading: boolean }) => {
|
||||
if (loading && departures.length === 0) return <p className="text-gray-500 text-xl mt-4">Loading...</p>;
|
||||
if (!loading && departures.length === 0) return <p className="text-gray-500 text-xl mt-4">No departures.</p>;
|
||||
return (
|
||||
<div className="flex flex-col gap-4 mt-8">
|
||||
{loading && departures.length === 0 && (
|
||||
<div className="flex justify-center p-12 text-gray-500 text-2xl animate-pulse">
|
||||
Fetching departures...
|
||||
</div>
|
||||
)}
|
||||
{!loading && departures.length === 0 && (
|
||||
<div className="flex justify-center p-12 text-gray-400 text-2xl bg-surface rounded-xl">
|
||||
No upcoming departures found.
|
||||
</div>
|
||||
)}
|
||||
{departures.slice(0, 5).map((dep, i) => (
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{departures.map((dep, i) => (
|
||||
<DepartureItem key={`${dep.line.designation}-${i}`} departure={dep} />
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue