Skip to content

Commit 2baf16f

Browse files
committed
✨: refactor components to use a new Widget wrapper for consistent styling
- Added a new Widget component to standardize the layout of various components. - Updated HumanizeDuration, Plex, RealTimeTrains, TflArrivals, Weather, and YearProgress components to utilize the Widget for improved styling and layout consistency. - Removed unused className props from several components to streamline props. - Enhanced shared store to include isHoliday state management.
1 parent 169c38e commit 2baf16f

16 files changed

Lines changed: 837 additions & 794 deletions

File tree

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { Aqi } from './aqi'
2-
import { BinDays } from './bin-days'
3-
import { Calendar } from './calendar'
4-
import { Clock } from './clock'
5-
import Compliments from './compliments'
6-
import { HumanizeDuration } from './humanize-duration'
7-
import { Plex } from './plex'
8-
import { RealTimeTrains } from './real-time-trains'
9-
import { TflArrivals } from './tfl-arrivals'
10-
import { Weather } from './weather'
11-
import { YearProgress } from './year-progress'
1+
import { Aqi } from '@/components/aqi'
2+
import { BinDays } from '@/components/bin-days'
3+
import { Calendar } from '@/components/calendar'
4+
import { Clock } from '@/components/clock'
5+
import Compliments from '@/components/compliments'
6+
import { HumanizeDuration } from '@/components/humanize-duration'
7+
import { Plex } from '@/components/plex'
8+
import { RealTimeTrains } from '@/components/real-time-trains'
9+
import { TflArrivals } from '@/components/tfl-arrivals'
10+
import { Weather } from '@/components/weather'
11+
import { YearProgress } from '@/components/year-progress'
1212

1313
const App = () => (
1414
<main className="">
@@ -18,13 +18,9 @@ const App = () => (
1818

1919
{/* Row 2, Column 1: Top-left aligned */}
2020
<div className="flex flex-col items-start justify-start gap-4">
21-
<Clock className="w-full xl:w-4/5" />
22-
<YearProgress
23-
className="w-full xl:w-4/5"
24-
config={{ accentColor: 'oklch(68.5% 0.169 237.323)' }}
25-
/>
21+
<Clock />
22+
<YearProgress config={{ accentColor: 'oklch(68.5% 0.169 237.323)' }} />
2623
<HumanizeDuration
27-
className="w-full xl:w-4/5"
2824
config={{
2925
events: [
3026
{
@@ -67,31 +63,27 @@ const App = () => (
6763
/>
6864

6965
<Aqi
70-
className="w-full xl:w-4/5"
7166
config={{
7267
city: 'London',
7368
apiToken: import.meta.env.VITE_WAQI_API_KEY
7469
}}
7570
/>
7671

7772
<TflArrivals
78-
className="w-full xl:w-4/5"
7973
config={{
8074
naptanId: '490011739N',
8175
stopName: 'Stanford Way',
8276
}}
8377
/>
8478

8579
<Plex
86-
className="w-full xl:w-4/5"
8780
config={{
8881
baseUrl: 'http://192.168.1.5:32400',
8982
refreshIntervalSeconds: 30
9083
}}
9184
/>
9285

9386
<BinDays
94-
className="w-full xl:w-4/5"
9587
config={{
9688
collections: [
9789
{
@@ -148,29 +140,25 @@ const App = () => (
148140
{/* Row 2, Column 2: Top-right aligned - WeatherWidget */}
149141
<div className="flex flex-col items-start justify-start gap-4">
150142
<Weather
151-
className="w-full xl:w-4/5"
152143
config={{
153144
latitude: 51.40545725179989,
154145
longitude: -0.14240032490376395
155146
}}
156147
/>
157148

158149
<RealTimeTrains
159-
className="w-full xl:w-4/5"
160150
config={{
161151
originStationCode: 'SRC',
162152
destinationStationCode: 'VIC'
163153
}}
164154
/>
165155

166156
<Calendar
167-
className="w-full xl:w-4/5"
168157
config={{
169158
holidays: [
170159
// 2025
171160
'2025-12-25', // Christmas Day
172161
'2025-12-26', // Boxing Day
173-
'2025-12-04',
174162
// 2026
175163
'2026-01-01', // New Year's Day
176164
'2026-04-03', // Good Friday
@@ -189,7 +177,6 @@ const App = () => (
189177
{/* Row 4: Full width merged column */}
190178
<div className="col-span-1 flex items-center justify-center md:col-span-2">
191179
<Compliments
192-
className="w-full"
193180
config={{ refreshIntervalSeconds: 10 }}
194181
/>
195182
</div>

src/components/aqi/index.tsx

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useQuery } from '@tanstack/react-query'
22
import { useMemo } from 'react'
3+
import { Widget } from '../widget'
34

45
type AqiWidgetConfig = {
56
city: string
@@ -9,7 +10,6 @@ type AqiWidgetConfig = {
910

1011
type AqiWidgetProps = {
1112
config: AqiWidgetConfig
12-
className?: string
1313
}
1414

1515
type WaqiResponse = {
@@ -164,7 +164,7 @@ const useAqiData = (params: {
164164
})
165165
}
166166

167-
export const Aqi = ({ config, className = '' }: AqiWidgetProps) => {
167+
export const Aqi = ({ config }: AqiWidgetProps) => {
168168
const { city, apiToken, refreshIntervalSeconds = 5 * 60 } = config
169169

170170
const { data, isLoading, error } = useAqiData({
@@ -194,20 +194,24 @@ export const Aqi = ({ config, className = '' }: AqiWidgetProps) => {
194194

195195
if (isLoading) {
196196
return (
197-
<div className={`${className}`} id="aqi-widget">
198-
<p className="text-white/70">Loading AQI data...</p>
199-
</div>
197+
<Widget>
198+
<div id="aqi-widget">
199+
<p className="text-white/70">Loading AQI data...</p>
200+
</div>
201+
</Widget>
200202
)
201203
}
202204

203205
if (error || !data || data.status !== 'ok') {
204206
return (
205-
<div className={`${className}`} id="aqi-widget">
206-
<p className="text-red-400">Failed to load AQI data</p>
207-
<p className="text-white/50 text-xs">
208-
{error instanceof Error ? error.message : 'Unknown error'}
209-
</p>
210-
</div>
207+
<Widget>
208+
<div id="aqi-widget">
209+
<p className="text-red-400">Failed to load AQI data</p>
210+
<p className="text-white/50 text-xs">
211+
{error instanceof Error ? error.message : 'Unknown error'}
212+
</p>
213+
</div>
214+
</Widget>
211215
)
212216
}
213217

@@ -217,38 +221,40 @@ export const Aqi = ({ config, className = '' }: AqiWidgetProps) => {
217221
const aqiLabel = getAqiLabel(aqi)
218222

219223
return (
220-
<div className={`flex flex-col gap-2 p-4 ${className}`} id="aqi-widget">
221-
{/* Header with city name and overall AQI */}
222-
<div className="flex items-center justify-between pb-4">
223-
<div className="font-light text-lg text-white md:text-xl">
224-
{cityName}
225-
</div>
226-
<div className="flex flex-col items-center gap-1">
227-
<div className={`font-bold text-xl md:text-2xl ${aqiColor}`}>
228-
{aqiLabel}
224+
<Widget>
225+
<div className="flex flex-col gap-2 p-4" id="aqi-widget">
226+
{/* Header with city name and overall AQI */}
227+
<div className="flex items-center justify-between pb-4">
228+
<div className="font-light text-lg text-white md:text-xl">
229+
{cityName}
230+
</div>
231+
<div className="flex flex-col items-center gap-1">
232+
<div className={`font-bold text-xl md:text-2xl ${aqiColor}`}>
233+
{aqiLabel}
234+
</div>
229235
</div>
230236
</div>
231-
</div>
232237

233-
{/* Individual pollutants */}
234-
<div className="grid grid-cols-6 gap-4">
235-
{sortedPollutants.map((pollutant) => {
236-
const color = getAqiColor(pollutant.value)
237-
return (
238-
<div
239-
className="flex flex-col items-center gap-0"
240-
key={pollutant.key}
241-
>
242-
<span className={`font-medium text-2xl md:text-2xl ${color}`}>
243-
{pollutant.value}
244-
</span>
245-
<span className="text-sm text-white/40">
246-
{getPollutantLabel(pollutant.key, pollutant.label)}
247-
</span>
248-
</div>
249-
)
250-
})}
238+
{/* Individual pollutants */}
239+
<div className="grid grid-cols-6 gap-4">
240+
{sortedPollutants.map((pollutant) => {
241+
const color = getAqiColor(pollutant.value)
242+
return (
243+
<div
244+
className="flex flex-col items-center gap-0"
245+
key={pollutant.key}
246+
>
247+
<span className={`font-medium text-2xl md:text-2xl ${color}`}>
248+
{pollutant.value}
249+
</span>
250+
<span className="text-sm text-white/40">
251+
{getPollutantLabel(pollutant.key, pollutant.label)}
252+
</span>
253+
</div>
254+
)
255+
})}
256+
</div>
251257
</div>
252-
</div>
258+
</Widget>
253259
)
254260
}

src/components/bin-days/index.tsx

Lines changed: 47 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { addWeeks, format, isSameDay, nextDay, parseISO, startOfDay, subDays } from 'date-fns'
22
import { useEffect, useMemo, useState } from 'react'
3+
import { Widget } from '../widget'
34
import type { BinDaysConfig, CollectionType, HolidayException } from './types'
45
import { id } from 'date-fns/locale'
56

67
type BinDaysProps = {
78
config: BinDaysConfig
8-
className?: string
99
}
1010

1111
const applyHolidayException = (date: Date, exceptions: HolidayException[] = []): Date => {
@@ -48,7 +48,7 @@ const calculateNextCollection = (
4848
return applyHolidayException(candidate, holidayExceptions)
4949
}
5050

51-
export const BinDays = ({ config: widgetConfig, className = '' }: BinDaysProps) => {
51+
export const BinDays = ({ config: widgetConfig }: BinDaysProps) => {
5252
const { collections, holidayExceptions, refreshIntervalSeconds = 60 * 60 } = widgetConfig
5353
const [now, setNow] = useState(new Date())
5454

@@ -95,53 +95,55 @@ export const BinDays = ({ config: widgetConfig, className = '' }: BinDaysProps)
9595
const displayDate = nextCollections[0].displayDate
9696

9797
return (
98-
<div className={`flex flex-col items-center justify-center gap-4 p-4 ${className}`} id="bin-days">
99-
{/* Header */}
100-
<div className="font-light text-white/60 text-lg uppercase tracking-wide">
101-
Next bin day
102-
</div>
103-
104-
{/* Date info */}
105-
<div className="flex flex-col items-center gap-1">
106-
<div className="font-light text-white text-2xl">
107-
{format(displayDate, 'EEEE MMM dd')}
98+
<Widget>
99+
<div className="flex flex-col items-center justify-center gap-4 p-4" id="bin-days">
100+
{/* Header */}
101+
<div className="font-light text-white/60 text-lg uppercase tracking-wide">
102+
Next bin day
108103
</div>
109-
<div className="font-light text-white/60 text-xl">
110-
{daysUntil === 0
111-
? 'Tonight'
112-
: daysUntil === 1
113-
? 'Tomorrow'
114-
: `in ${daysUntil} days`}
104+
105+
{/* Date info */}
106+
<div className="flex flex-col items-center gap-1">
107+
<div className="font-light text-white text-2xl">
108+
{format(displayDate, 'EEEE MMM dd')}
109+
</div>
110+
<div className="font-light text-white/60 text-xl">
111+
{daysUntil === 0
112+
? 'Tonight'
113+
: daysUntil === 1
114+
? 'Tomorrow'
115+
: `in ${daysUntil} days`}
116+
</div>
115117
</div>
116-
</div>
117118

118-
{/* Icons row */}
119-
<div className="flex items-center gap-6">
120-
{nextCollections.map((collection) => (
121-
<div key={collection.name} className="flex flex-col items-center gap-2">
122-
<svg
123-
xmlns="http://www.w3.org/2000/svg"
124-
width="48"
125-
height="48"
126-
viewBox="0 0 24 24"
127-
fill="none"
128-
stroke={collection.accent}
129-
strokeWidth="2"
130-
strokeLinecap="round"
131-
strokeLinejoin="round"
132-
>
133-
<path d="M10 11v6" />
134-
<path d="M14 11v6" />
135-
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
136-
<path d="M3 6h18" />
137-
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
138-
</svg>
139-
<div className="font-light text-white/80 text-sm text-center">
140-
{collection.name}
119+
{/* Icons row */}
120+
<div className="flex items-center gap-6">
121+
{nextCollections.map((collection) => (
122+
<div key={collection.name} className="flex flex-col items-center gap-2">
123+
<svg
124+
xmlns="http://www.w3.org/2000/svg"
125+
width="48"
126+
height="48"
127+
viewBox="0 0 24 24"
128+
fill="none"
129+
stroke={collection.accent}
130+
strokeWidth="2"
131+
strokeLinecap="round"
132+
strokeLinejoin="round"
133+
>
134+
<path d="M10 11v6" />
135+
<path d="M14 11v6" />
136+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
137+
<path d="M3 6h18" />
138+
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
139+
</svg>
140+
<div className="font-light text-white/80 text-sm text-center">
141+
{collection.name}
142+
</div>
141143
</div>
142-
</div>
143-
))}
144+
))}
145+
</div>
144146
</div>
145-
</div>
147+
</Widget>
146148
)
147149
}

0 commit comments

Comments
 (0)