spb/qwhpi Public
QHPI — Quebec Housing Price Index: quality-adjusted, hierarchically pooled housing price indexes.
Python 63.9%
TypeScript 25.4%
CSS 5.5%
TeX 3.5%
SQL 0.8%
Makefile 0.5%
Dockerfile 0.5%
1/**2 * =============================================================================3 * QWHPI — Quebec Weekly Housing Price Index4 * Author : Simon-Pierre Boucher5 * Contact : contact@spboucher.ai6 * File : web/components/Ticker.tsx7 * Purpose : Bloomberg-style infinite ticker tape — regions & cities YoY.8 * =============================================================================9 */10"use client";1112import { useEffect, useState } from "react";13import Link from "next/link";14import { fetchOverview, type OverviewRow } from "../lib/api";1516export default function Ticker({ ptype = "all" }: { ptype?: string }) {17 const [rows, setRows] = useState<OverviewRow[]>([]);1819 useEffect(() => {20 Promise.all([21 fetchOverview("region", ptype),22 fetchOverview("municipality", ptype),23 ])24 .then(([r, m]) => setRows([...m.rows, ...r.rows]))25 .catch(() => setRows([]));26 }, [ptype]);2728 if (!rows.length) return <div className="ticker-shell" aria-hidden />;2930 const items = rows.map((r) => (31 <Link32 key={r.geography_id}33 className="ticker-item"34 href={`/explore?geography=${r.geography_id}&type=${ptype}`}35 >36 <span className="ticker-name">{r.geography_name}</span>37 <span className="ticker-val">{r.index.toFixed(1)}</span>38 <span className={r.yoy_pct != null && r.yoy_pct < 0 ? "delta down" : "delta up"}>39 {r.yoy_pct == null ? "—"40 : `${r.yoy_pct >= 0 ? "▲" : "▼"}${Math.abs(r.yoy_pct).toFixed(1)}%`}41 </span>42 </Link>43 ));4445 return (46 <div className="ticker-shell" role="marquee"47 aria-label="Latest year-over-year appreciation by geography">48 <div className="ticker-track">49 <div className="ticker-group">{items}</div>50 <div className="ticker-group" aria-hidden>{items}</div>51 </div>52 </div>53 );54}55