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.
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
|
|
import { useTopDevelopers } from "@/lib/analytics-api";
|
|
|
|
import { ChartShell } from "./ChartShell";
|
|
|
|
export function VelocityScatter() {
|
|
const { data, isLoading } = useTopDevelopers(30);
|
|
|
|
const option = useMemo(() => {
|
|
const rows = data ?? [];
|
|
return {
|
|
tooltip: {
|
|
trigger: "item",
|
|
formatter: (info: { data: [number, number, string, number] }) => {
|
|
const [x, y, name, sold] = info.data;
|
|
return `<b>${name}</b><br/>Объём: ${x} тыс м²<br/>Δ sold: ${y > 0 ? "+" : ""}${y} пп<br/>текущий sold%: ${sold}%`;
|
|
},
|
|
},
|
|
grid: { left: 56, right: 32, top: 32, bottom: 56 },
|
|
xAxis: {
|
|
name: "Объём, тыс м²",
|
|
nameLocation: "middle",
|
|
nameGap: 32,
|
|
type: "value",
|
|
},
|
|
yAxis: {
|
|
name: "Δ sold % (пп)",
|
|
nameLocation: "middle",
|
|
nameGap: 36,
|
|
type: "value",
|
|
axisLine: { show: true },
|
|
},
|
|
series: [
|
|
{
|
|
type: "scatter",
|
|
symbolSize: 14,
|
|
data: rows
|
|
.filter((r) => r.sold_delta_pp != null && r.sverdl_sqm_th != null)
|
|
.map((r) => [
|
|
r.sverdl_sqm_th,
|
|
r.sold_delta_pp,
|
|
r.developer_name,
|
|
r.sold_pct ?? 0,
|
|
]),
|
|
itemStyle: {
|
|
color: (p: { data: [number, number, string, number] }) =>
|
|
p.data[1] > 5
|
|
? "#0a7a3a"
|
|
: p.data[1] < -5
|
|
? "#b3261e"
|
|
: "#5b6066",
|
|
},
|
|
label: {
|
|
show: true,
|
|
position: "right",
|
|
formatter: (p: { data: [number, number, string, number] }) =>
|
|
p.data[2],
|
|
fontSize: 11,
|
|
color: "#374151",
|
|
},
|
|
markLine: {
|
|
silent: true,
|
|
symbol: "none",
|
|
lineStyle: { color: "#cbd5e1", type: "dashed" },
|
|
data: [{ yAxis: 0 }],
|
|
},
|
|
},
|
|
],
|
|
};
|
|
}, [data]);
|
|
|
|
return <ChartShell option={option} loading={isLoading} height={420} />;
|
|
}
|