feat(home): sort countdown chips by days remaining, fade in after hydration
Build and Push Docker Images / Build Backend (FastAPI) (push) Successful in 12s
Build and Push Docker Images / Build Frontend (Next.js) (push) Successful in 50s
Build and Push Docker Images / Build Pipeline (Meltano + dbt + Airflow) (push) Successful in 12s
Build and Push Docker Images / Trigger Portainer Update (push) Successful in 1s

Chips now sort soonest-first at hydration time so the most urgent
deadline always appears first. The rail is hidden (opacity 0) until
the useEffect populates and sorts the chips, then fades in — avoiding
any visible layout shift from the reorder.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tudor Sitaru
2026-04-16 16:06:21 +01:00
parent 9e0b004d93
commit c39256b1a0
+15 -6
View File
@@ -77,7 +77,9 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp
const mapParamsRef = useRef<string>('');
const [geoState, setGeoState] = useState<'idle' | 'requesting' | 'error'>('idle');
const [geoError, setGeoError] = useState<string | null>(null);
const [chipDays, setChipDays] = useState<(number | null)[]>(ADMISSIONS_CHIPS.map(() => null));
const [sortedChips, setSortedChips] = useState<Array<{ chip: CountdownChipData; days: number | null }>>(
ADMISSIONS_CHIPS.map(c => ({ chip: c, days: null }))
);
const hasSearch = searchParams.get('search') || searchParams.get('postcode');
const isLocationSearch = !!searchParams.get('postcode');
@@ -140,9 +142,11 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp
.catch(() => {});
}, []);
// Compute admissions countdown days client-side to avoid SSR mismatch
// Compute admissions countdown days client-side and sort soonest-first to avoid SSR mismatch
useEffect(() => {
setChipDays(ADMISSIONS_CHIPS.map(c => daysUntil(c.month, c.day)));
const withDays = ADMISSIONS_CHIPS.map(c => ({ chip: c, days: daysUntil(c.month, c.day) }));
withDays.sort((a, b) => (a.days ?? Infinity) - (b.days ?? Infinity));
setSortedChips(withDays);
}, []);
const handleLoadMore = async () => {
@@ -292,9 +296,14 @@ export function HomeView({ initialSchools, filters, totalSchools }: HomeViewProp
<span className={styles.stripLabel}>Key admissions deadlines</span>
<a href="/admissions" className={styles.stripCta}>Full admissions guide </a>
</div>
<div className={styles.countdownRail}>
{ADMISSIONS_CHIPS.map((chip, i) => {
const days = chipDays[i];
<div
className={styles.countdownRail}
style={{
opacity: sortedChips[0]?.days !== null ? 1 : 0,
transition: 'opacity 0.2s ease',
}}
>
{sortedChips.map(({ chip, days }) => {
const isUrgent = days !== null && days <= 14;
const chipClass = [
styles.countdownChip,