chore: finish frontend ESLint migration #18

Enable every plugin recommended preset and replace Biome with ESLint formatting.

Preserve API contracts and JSX whitespace; verify source, tests, builds and tooling.
This commit is contained in:
2026-09-28 16:36:40 +03:00
committed by ksv741
parent 57630102e4
commit 72d7835227
253 changed files with 10059 additions and 8864 deletions
@@ -1,3 +1,5 @@
import type { FC } from "react";
import { AreaChart } from "@mantine/charts";
import {
Accordion,
@@ -7,185 +9,187 @@ import {
Table,
Text,
} from "@mantine/core";
import type { FC } from "react";
import { useTranslation } from "react-i18next";
import { formatDecimal, formatMoney } from "@/shared/lib";
import type { InstrumentChartVm } from "../model/types";
import classes from "./InstrumentDetails.module.css";
import { InstrumentSection } from "./InstrumentSection.view";
type InstrumentChartProps = { vm: InstrumentChartVm; priceUnit?: "percent" };
type InstrumentChartProps = { priceUnit?: "percent"; vm: InstrumentChartVm };
const InstrumentChart: FC<InstrumentChartProps> = (props) => {
const { vm } = props;
const { t } = useTranslation();
const priceLabel = (value: string) => {
if (props.priceUnit === "percent") return `${formatDecimal(value)} %`;
const currency = vm.candles?.currency;
return formatMoney(currency ? { currency, value } : undefined);
};
return (
<InstrumentSection
title={t("stock.chart")}
state={vm.candlesState}
large
controls={
controls={(
<div className={classes.controls}>
<SegmentedControl
aria-label={t("stock.period")}
value={vm.period}
onChange={vm.setPeriod}
data={["month", "halfYear", "year", "all"].map((value) => ({
value,
data={["month", "halfYear", "year", "all"].map(value => ({
label: t(`stock.periods.${value}`),
value,
}))}
onChange={vm.setPeriod}
value={vm.period}
/>
</div>
}
)}
large
state={vm.candlesState}
title={t("stock.chart")}
>
{vm.candles ? (
vm.chartData.length ? (
<>
<AreaChart
h={310}
data={vm.chartData}
dataKey="date"
series={[
{ name: "price", color: "blue.6", label: t("stock.price") },
]}
curveType="linear"
withDots={vm.chartData.length === 1}
withGradient
gridAxis="y"
tickLine="none"
tooltipAnimationDuration={0}
areaProps={{ isAnimationActive: false }}
yAxisProps={{ domain: ["auto", "auto"], width: 64 }}
referenceLines={
vm.averageLine === null
? []
: [
{
y: vm.averageLine,
ifOverflow: "extendDomain",
color: "orange.7",
label: t("stock.averageLine"),
},
]
}
valueFormatter={(value) =>
new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 4,
}).format(value)
}
aria-label={t(
props.priceUnit === "percent"
? "bond.chartLabel"
: "stock.chartLabel",
{
currency:
{!!(vm.candles) && ((
vm.chartData.length > 0
? (
<>
<AreaChart
areaProps={{ isAnimationActive: false }}
aria-label={t(
props.priceUnit === "percent"
? "bond.chartLabel"
: "stock.chartLabel",
{
currency:
props.priceUnit === "percent"
? t("bond.percentOfNominal")
: (vm.candles.currency ?? ""),
},
)}
/>
<Text size="xs" c="dimmed" mt="sm">
{t(
props.priceUnit === "percent"
? "bond.chartNote"
: "stock.chartNote",
)}
</Text>
{vm.hasIncompleteCandle ? (
<Text size="xs" c="dimmed">
{t("stock.incomplete")}
</Text>
) : null}
{vm.averageLine !== null ? (
<Text size="sm" mt="xs">
{t("stock.averageLine")}:{" "}
{formatMoney(vm.position?.averagePurchasePrice ?? null)}
</Text>
) : null}
<Accordion
className={classes.priceTable}
variant="contained"
radius="md"
>
<Accordion.Item value="prices">
<Accordion.Control>
<Text fw={600}>{t("stock.priceTable")}</Text>
<Text size="xs" c="dimmed">
{t("stock.priceTableHint", { count: vm.chartData.length })}
},
)}
curveType="linear"
data={vm.chartData}
dataKey="date"
gridAxis="y"
h={310}
referenceLines={
vm.averageLine === null
? []
: [
{
color: "orange.7",
ifOverflow: "extendDomain",
label: t("stock.averageLine"),
y: vm.averageLine,
},
]
}
series={[
{ color: "blue.6", label: t("stock.price"), name: "price" },
]}
tickLine="none"
tooltipAnimationDuration={0}
valueFormatter={value =>
new Intl.NumberFormat("ru-RU", {
maximumFractionDigits: 4,
}).format(value)}
withDots={vm.chartData.length === 1}
withGradient
yAxisProps={{ domain: ["auto", "auto"], width: 64 }}
/>
<Text c="dimmed" mt="sm" size="xs">
{t(
props.priceUnit === "percent"
? "bond.chartNote"
: "stock.chartNote",
)}
</Text>
{!!(vm.hasIncompleteCandle) && ((
<Text c="dimmed" size="xs">
{t("stock.incomplete")}
</Text>
</Accordion.Control>
<Accordion.Panel>
<ScrollArea.Autosize
mah={360}
type="auto"
viewportProps={{
tabIndex: 0,
role: "region",
"aria-label": t("stock.priceTable"),
}}
>
<Table
striped
highlightOnHover
stickyHeader
verticalSpacing="sm"
horizontalSpacing="md"
>
<Table.Thead>
<Table.Tr>
<Table.Th>{t("stock.date")}</Table.Th>
<Table.Th>{t("stock.price")}</Table.Th>
<Table.Th>{t("stock.candle")}</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{vm.priceRows.map((candle) => (
<Table.Tr key={candle.time}>
<Table.Td>{candle.date}</Table.Td>
<Table.Td>
{props.priceUnit === "percent"
? `${formatDecimal(candle.close)} %`
: formatMoney(
vm.candles?.currency
? {
value: candle.close,
currency: vm.candles.currency,
}
: null,
)}
</Table.Td>
<Table.Td>
<Badge
variant="light"
color={candle.isComplete ? "gray" : "blue"}
size="sm"
>
{t(
candle.isComplete
? "stock.complete"
: "stock.forming",
)}
</Badge>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</ScrollArea.Autosize>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
</>
) : (
<Text c="dimmed">
{t(
vm.candlesState.partial
? "investment.partial"
: "stock.noCandles",
)}
</Text>
)
) : null}
))}
{vm.averageLine !== null && ((
<Text mt="xs" size="sm">
{t("stock.averageLine")}
:
{" "}
{formatMoney(vm.position?.averagePurchasePrice)}
</Text>
))}
<Accordion
className={classes.priceTable}
radius="md"
variant="contained"
>
<Accordion.Item value="prices">
<Accordion.Control>
<Text fw={600}>{t("stock.priceTable")}</Text>
<Text c="dimmed" size="xs">
{t("stock.priceTableHint", { count: vm.chartData.length })}
</Text>
</Accordion.Control>
<Accordion.Panel>
<ScrollArea.Autosize
mah={360}
type="auto"
viewportProps={{
"aria-label": t("stock.priceTable"),
"role": "region",
"tabIndex": 0,
}}
>
<Table
highlightOnHover
horizontalSpacing="md"
stickyHeader
striped
verticalSpacing="sm"
>
<Table.Thead>
<Table.Tr>
<Table.Th>{t("stock.date")}</Table.Th>
<Table.Th>{t("stock.price")}</Table.Th>
<Table.Th>{t("stock.candle")}</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{vm.priceRows.map(candle => (
<Table.Tr key={candle.time}>
<Table.Td>{candle.date}</Table.Td>
<Table.Td>
{priceLabel(candle.close)}
</Table.Td>
<Table.Td>
<Badge
color={candle.isComplete ? "gray" : "blue"}
size="sm"
variant="light"
>
{t(
candle.isComplete
? "stock.complete"
: "stock.forming",
)}
</Badge>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</ScrollArea.Autosize>
</Accordion.Panel>
</Accordion.Item>
</Accordion>
</>
)
: (
<Text c="dimmed">
{t(
vm.candlesState.partial
? "investment.partial"
: "stock.noCandles",
)}
</Text>
)
))}
</InstrumentSection>
);
};