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.
71 lines
2.1 KiB
TypeScript
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}
|
|
/>
|
|
);
|
|
}
|