Some checks failed
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 36s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Filesystem Scan (push) Failing after 30s
Deploy / Deploy to Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Deploy / Rollback Staging (push) Has been skipped
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 6s
CI / E2E Tests (push) Has been skipped
Deploy / Build API Image (push) Failing after 13s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 4s
CI / AI Services (Python) — Smoke (push) Failing after 5s
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 57s
Deploy / Build Web Image (push) Failing after 6s
Deploy / Build AI Services Image (push) Failing after 6s
E2E Tests / Playwright E2E (push) Failing after 12s
Security Scanning / Trivy Scan — API Image (push) Failing after 51s
Security Scanning / Trivy Scan — Web Image (push) Failing after 35s
- price-area-chart + sparkline: replace non-existent `var(--color-signal-up)` with proper `hsl(var(--signal-up))` (and same for -down + border + muted-foreground). The previous tokens resolved to undefined, leaving the chart line + sparkline invisible against the dark background. - public/page: switch `currentPeriod()` from monthly (YYYY-MM) to quarterly (YYYY-Qn) to match the MarketIndex aggregation period — heatmap and district stats now find rows. - import-market-data: add `2026-Q2` to seeded periods so the current quarter has data on a freshly seeded dev DB. - new scripts/seed-bulk-listings-per-district.ts: top up the dev DB with 12 synthetic listings per district per 7-day window so the movers query (which requires >= 10 listings/district/window) has signal to compute against. - update price-area-chart.spec to match new color tokens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
1.9 KiB
TypeScript
73 lines
1.9 KiB
TypeScript
'use client';
|
|
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import * as React from 'react';
|
|
import { listingsApi } from '@/lib/listings-api';
|
|
|
|
interface SparklineProps {
|
|
listingId: string;
|
|
width?: number;
|
|
height?: number;
|
|
}
|
|
|
|
/**
|
|
* Inline SVG sparkline showing 30-day price history.
|
|
* Fetches data lazily when the component mounts (used in table hover/visible rows).
|
|
*/
|
|
export function Sparkline({ listingId, width = 64, height = 20 }: SparklineProps) {
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: ['listings', 'price-history', listingId],
|
|
queryFn: () => listingsApi.getPriceHistory(listingId),
|
|
staleTime: 5 * 60 * 1000,
|
|
});
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<span
|
|
className="inline-block animate-pulse rounded bg-background-surface"
|
|
style={{ width, height }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (!data || data.length < 2) {
|
|
return <span className="text-[11px] text-foreground-dim">—</span>;
|
|
}
|
|
|
|
const prices = data.map((d) => Number(d.newPrice));
|
|
const min = Math.min(...prices);
|
|
const max = Math.max(...prices);
|
|
const range = max - min || 1;
|
|
|
|
const points = prices
|
|
.map((p, i) => {
|
|
const x = (i / (prices.length - 1)) * width;
|
|
const y = height - ((p - min) / range) * (height - 4) - 2;
|
|
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
})
|
|
.join(' ');
|
|
|
|
// Color based on trend direction
|
|
const trending = prices[prices.length - 1]! >= prices[0]!;
|
|
const strokeColor = trending ? 'hsl(var(--signal-up))' : 'hsl(var(--signal-down))';
|
|
|
|
return (
|
|
<svg
|
|
width={width}
|
|
height={height}
|
|
viewBox={`0 0 ${width} ${height}`}
|
|
className="inline-block"
|
|
aria-label="Biểu đồ giá 30 ngày"
|
|
>
|
|
<polyline
|
|
points={points}
|
|
fill="none"
|
|
stroke={strokeColor}
|
|
strokeWidth="1.5"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
);
|
|
}
|