refactor: encapsulate instrument chart data
This commit is contained in:
@@ -7,17 +7,13 @@ type Candles = NonNullable<
|
||||
paths["/api/v1/stocks/{uid}/candles"]["get"]["responses"][200]["content"]["application/json"]["data"]
|
||||
>;
|
||||
|
||||
type ChartRow = Candles["candles"][number] & { date: string; price: number };
|
||||
type InstrumentChartProps = { priceUnit?: "percent"; vm: InstrumentChartVm };
|
||||
|
||||
type InstrumentChartVm = {
|
||||
averageLine: null | number;
|
||||
candles?: Candles;
|
||||
candlesState: SectionState;
|
||||
chartData: ChartRow[];
|
||||
hasIncompleteCandle: boolean;
|
||||
period: string;
|
||||
position?: Position;
|
||||
priceRows: ChartRow[];
|
||||
setPeriod: (value: string) => void;
|
||||
};
|
||||
|
||||
@@ -41,4 +37,4 @@ type Position = NonNullable<
|
||||
paths["/api/v1/stocks/{uid}/position"]["get"]["responses"][200]["content"]["application/json"]["data"]
|
||||
>;
|
||||
|
||||
export type { InstrumentChartVm, InstrumentEventsVm, InstrumentPositionVm };
|
||||
export type { InstrumentChartProps, InstrumentEventsVm, InstrumentPositionVm };
|
||||
|
||||
@@ -10,20 +10,19 @@ import {
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
|
||||
import { formatMoney } from "@/shared/lib";
|
||||
import { formatChartValue, formatMoney } from "@/shared/lib";
|
||||
|
||||
import type { InstrumentChartVm } from "../../model/types";
|
||||
import type { InstrumentChartProps } from "../../model/types";
|
||||
|
||||
import classes from "../InstrumentDetails.module.css";
|
||||
import { InstrumentSection } from "../InstrumentSection.view";
|
||||
import { useInstrumentChartVm } from "./InstrumentChart.vm";
|
||||
|
||||
type InstrumentChartProps = { priceUnit?: "percent"; vm: InstrumentChartVm };
|
||||
|
||||
const InstrumentChart: FC<InstrumentChartProps> = (props) => {
|
||||
const {
|
||||
chartData,
|
||||
chartLabel,
|
||||
formatChartValue,
|
||||
hasIncompleteCandle,
|
||||
periodOptions,
|
||||
priceRows,
|
||||
referenceLines,
|
||||
@@ -34,14 +33,14 @@ const InstrumentChart: FC<InstrumentChartProps> = (props) => {
|
||||
let chartContent;
|
||||
|
||||
if (vm.candles) {
|
||||
if (vm.chartData.length > 0) {
|
||||
if (chartData.length > 0) {
|
||||
chartContent = (
|
||||
<>
|
||||
<AreaChart
|
||||
areaProps={{ isAnimationActive: false }}
|
||||
aria-label={chartLabel}
|
||||
curveType="linear"
|
||||
data={vm.chartData}
|
||||
data={chartData}
|
||||
dataKey="date"
|
||||
gridAxis="y"
|
||||
h={310}
|
||||
@@ -52,7 +51,7 @@ const InstrumentChart: FC<InstrumentChartProps> = (props) => {
|
||||
tickLine="none"
|
||||
tooltipAnimationDuration={0}
|
||||
valueFormatter={formatChartValue}
|
||||
withDots={vm.chartData.length === 1}
|
||||
withDots={chartData.length === 1}
|
||||
withGradient
|
||||
yAxisProps={{ domain: ["auto", "auto"], width: 64 }}
|
||||
/>
|
||||
@@ -65,13 +64,13 @@ const InstrumentChart: FC<InstrumentChartProps> = (props) => {
|
||||
)}
|
||||
</Text>
|
||||
|
||||
{!!vm.hasIncompleteCandle && (
|
||||
{!!hasIncompleteCandle && (
|
||||
<Text c="dimmed" size="xs">
|
||||
{t("stock.incomplete")}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{vm.averageLine !== null && (
|
||||
{referenceLines.length > 0 && (
|
||||
<Text mt="xs" size="sm">
|
||||
{t("stock.averageLine")}:{" "}
|
||||
{formatMoney(vm.position?.averagePurchasePrice)}
|
||||
@@ -88,7 +87,7 @@ const InstrumentChart: FC<InstrumentChartProps> = (props) => {
|
||||
<Text fw={600}>{t("stock.priceTable")}</Text>
|
||||
|
||||
<Text c="dimmed" size="xs">
|
||||
{t("stock.priceTableHint", { count: vm.chartData.length })}
|
||||
{t("stock.priceTableHint", { count: chartData.length })}
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { formatChartValue, formatDecimal, formatMoney } from "@/shared/lib";
|
||||
import { formatDate, formatDecimal, formatMoney } from "@/shared/lib";
|
||||
|
||||
import type { InstrumentChartVm } from "../../model/types";
|
||||
|
||||
type InstrumentChartProps = { priceUnit?: "percent"; vm: InstrumentChartVm };
|
||||
import type { InstrumentChartProps } from "../../model/types";
|
||||
|
||||
function useInstrumentChartVm(props: InstrumentChartProps) {
|
||||
const { vm } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const priceRows = vm.priceRows.map((candle) => {
|
||||
const chartData = vm.candles?.candles.map(candle => ({
|
||||
...candle,
|
||||
date: formatDate(candle.time),
|
||||
price: Number(candle.close),
|
||||
})) ?? [];
|
||||
|
||||
const average = vm.position?.averagePurchasePrice;
|
||||
|
||||
const averageLine = average && props.priceUnit !== "percent"
|
||||
&& average.currency === vm.candles?.currency
|
||||
? Number(average.value)
|
||||
: null;
|
||||
|
||||
const priceRows = chartData.toReversed().map((candle) => {
|
||||
const currency = vm.candles?.currency;
|
||||
const value = candle.close;
|
||||
|
||||
@@ -39,20 +50,21 @@ function useInstrumentChartVm(props: InstrumentChartProps) {
|
||||
);
|
||||
|
||||
const referenceLines
|
||||
= vm.averageLine === null
|
||||
= averageLine === null || !Number.isFinite(averageLine)
|
||||
? []
|
||||
: [
|
||||
{
|
||||
color: "orange.7",
|
||||
ifOverflow: "extendDomain" as const,
|
||||
label: t("stock.averageLine"),
|
||||
y: vm.averageLine,
|
||||
y: averageLine,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
chartData,
|
||||
chartLabel,
|
||||
formatChartValue,
|
||||
hasIncompleteCandle: chartData.some(candle => !candle.isComplete),
|
||||
periodOptions,
|
||||
priceRows,
|
||||
referenceLines,
|
||||
|
||||
Reference in New Issue
Block a user