gendesign/frontend/src/components/analytics/QuartirographyChart.tsx
lekss361 8d3a0874ef add interactive analytics dashboard for Sverdlovsk market and PRINZIP
3 pages (market, PRINZIP drilldown, developers leaderboard) on top of
existing v_developer_full_metrics + domrf_realization views. ECharts on
the frontend, FastAPI router /api/v1/analytics on the backend.
2026-04-27 16:55:30 +03:00

71 lines
2.1 KiB
TypeScript

"use client";
import { useMemo } from "react";
import {
useQuartirographyDeals,
useQuartirographyPortfolio,
} from "@/lib/analytics-api";
import { ChartShell } from "./ChartShell";
export function QuartirographyChart() {
const portfolio = useQuartirographyPortfolio();
const deals = useQuartirographyDeals();
const option = useMemo(() => {
const portfolioRows = portfolio.data ?? [];
const dealsRows = deals.data ?? [];
const buckets = [
"Студии 15-30",
"1-к 30-45",
"2-к 45-60",
"3-к 60-80",
"80+ м²",
];
const portfolioMap: Record<string, number> = {
"1-к 30-45": portfolioRows.find((r) => r.bucket === "1-к")?.percent ?? 0,
"2-к 45-60": portfolioRows.find((r) => r.bucket === "2-к")?.percent ?? 0,
"3-к 60-80": portfolioRows.find((r) => r.bucket === "3-к")?.percent ?? 0,
"80+ м²": portfolioRows.find((r) => r.bucket === "4+")?.percent ?? 0,
};
const dealsPercents = buckets.map(
(b) => dealsRows.find((r) => r.bucket === b)?.percent ?? 0,
);
const portfolioPercents = buckets.map((b) => portfolioMap[b] ?? 0);
return {
tooltip: {
trigger: "axis",
axisPointer: { type: "shadow" },
valueFormatter: (v: number) => `${v}%`,
},
legend: { data: ["Что строится (портфель)", "Что покупают (ДДУ)"] },
grid: { left: 110, right: 32, top: 40, bottom: 28 },
xAxis: { type: "value", axisLabel: { formatter: "{value}%" } },
yAxis: { type: "category", data: buckets, inverse: true },
series: [
{
name: "Что строится (портфель)",
type: "bar",
data: portfolioPercents,
itemStyle: { color: "#94a3b8" },
},
{
name: "Что покупают (ДДУ)",
type: "bar",
data: dealsPercents,
itemStyle: { color: "#0a7a3a" },
},
],
};
}, [portfolio.data, deals.data]);
return (
<ChartShell
option={option}
loading={portfolio.isLoading || deals.isLoading}
height={320}
/>
);
}