refactor: enforce semantic values and numeric lint rules
This commit is contained in:
@@ -3,6 +3,8 @@ import type { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LoadingSkeleton } from "@/shared/ui";
|
||||
|
||||
const skeletonRowIds = ["first", "second", "third", "fourth"];
|
||||
|
||||
const AccountEventsPageLoading: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@@ -17,8 +19,8 @@ const AccountEventsPageLoading: FC = () => {
|
||||
<Skeleton height={36} width={116} />
|
||||
<Skeleton height={36} width={108} />
|
||||
</Group>
|
||||
{[0, 1, 2, 3].map((index) => (
|
||||
<Skeleton key={index} height={82} radius="md" />
|
||||
{skeletonRowIds.map((id) => (
|
||||
<Skeleton key={id} height={82} radius="md" />
|
||||
))}
|
||||
</LoadingSkeleton>
|
||||
);
|
||||
|
||||
@@ -7,6 +7,8 @@ import { type IncomeAnalytics, useIncomeAnalytics } from "@/entities/income";
|
||||
import { validationMessage } from "@/shared/i18n";
|
||||
import { formatDate } from "@/shared/lib";
|
||||
|
||||
const ISO_MONTH_LENGTH = 7;
|
||||
|
||||
type IncomePeriod = { from: string; to: string };
|
||||
type IncomeForm = IncomePeriod;
|
||||
|
||||
@@ -143,7 +145,7 @@ function draftPeriod(period: IncomePeriod): IncomeForm {
|
||||
|
||||
function defaultPeriod(): IncomePeriod {
|
||||
const today = moscowDate(new Date());
|
||||
return { from: `${today.slice(0, 7)}-01`, to: today };
|
||||
return { from: `${today.slice(0, ISO_MONTH_LENGTH)}-01`, to: today };
|
||||
}
|
||||
|
||||
function validPeriod(period: IncomePeriod): period is IncomePeriod {
|
||||
|
||||
@@ -3,6 +3,8 @@ import type { FC } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { LoadingSkeleton } from "@/shared/ui";
|
||||
|
||||
const skeletonRowIds = ["first", "second", "third", "fourth"];
|
||||
|
||||
const AccountOperationsPageLoading: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -19,8 +21,8 @@ const AccountOperationsPageLoading: FC = () => {
|
||||
<Skeleton height={36} width={110} radius="sm" />
|
||||
</Group>
|
||||
<Stack gap="xs">
|
||||
{[0, 1, 2, 3].map((index) => (
|
||||
<Skeleton key={index} height={64} radius="md" />
|
||||
{skeletonRowIds.map((id) => (
|
||||
<Skeleton key={id} height={64} radius="md" />
|
||||
))}
|
||||
</Stack>
|
||||
</LoadingSkeleton>
|
||||
|
||||
@@ -17,6 +17,8 @@ import { useStocks } from "@/entities/stock";
|
||||
import { apiErrorCode } from "@/shared/api";
|
||||
import { formatMoney, instrumentLogoUrl } from "@/shared/lib";
|
||||
|
||||
const RECENT_EVENTS_LIMIT = 5;
|
||||
|
||||
type PositionSectionKind = "stocks" | "bonds" | "other";
|
||||
type AccountPageReturnTone = "positive" | "negative";
|
||||
|
||||
@@ -176,7 +178,7 @@ function toRecentEvents(
|
||||
const to = `/accounts/${accountId}/events`;
|
||||
if (events.isLoading) return { state: "loading", to };
|
||||
if (events.isError) return { state: "error", to };
|
||||
const items = events.data?.events.slice(0, 5) ?? [];
|
||||
const items = events.data?.events.slice(0, RECENT_EVENTS_LIMIT) ?? [];
|
||||
if (!items.length) return { state: "empty", to };
|
||||
return { state: "loaded", to, items };
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { Portfolio } from "@/entities/portfolio";
|
||||
import { formatMoney } from "@/shared/lib";
|
||||
import { PortfolioStructureView } from "./PortfolioStructure.view";
|
||||
|
||||
const MIN_VISIBLE_SHARE_PERCENT = 0.01;
|
||||
|
||||
// A cached snapshot or an older running server can predate this response field.
|
||||
type PortfolioStructureProps = { structure?: Portfolio["structure"] | null };
|
||||
|
||||
@@ -45,7 +47,7 @@ const PortfolioStructure: FC<PortfolioStructureProps> = (props) => {
|
||||
share:
|
||||
percentage === null
|
||||
? t("portfolio.structure.shareUnavailable")
|
||||
: percentage > 0 && percentage < 0.01
|
||||
: percentage > 0 && percentage < MIN_VISIBLE_SHARE_PERCENT
|
||||
? t("portfolio.structure.smallShare")
|
||||
: `${new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 2 }).format(percentage)} %`,
|
||||
value: percentage ?? 0,
|
||||
|
||||
@@ -4,10 +4,12 @@ import { useBondSearch } from "@/entities/bond";
|
||||
import { apiErrorCode } from "@/shared/api";
|
||||
import { instrumentLogoUrl } from "@/shared/lib";
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 300;
|
||||
|
||||
function useBondsPageVm() {
|
||||
const [params, setParams] = useSearchParams();
|
||||
const query = params.get("q") ?? "";
|
||||
const [debounced] = useDebouncedValue(query.trim(), 300);
|
||||
const [debounced] = useDebouncedValue(query.trim(), SEARCH_DEBOUNCE_MS);
|
||||
const result = useBondSearch(debounced);
|
||||
const waiting = query.trim() !== debounced;
|
||||
return {
|
||||
|
||||
@@ -26,6 +26,8 @@ import classes from "./OperationForm.module.css";
|
||||
import type { OperationValues } from "./OperationForm.types";
|
||||
import { OperationFields, RefundFields } from "./OperationForm.view";
|
||||
|
||||
const MAX_DESCRIPTION_LENGTH = 500;
|
||||
|
||||
const operationSchema = z.object({
|
||||
kind: z.enum(["expense", "topup", "transfer", "refund"]),
|
||||
cardId: z.string(),
|
||||
@@ -36,7 +38,10 @@ const operationSchema = z.object({
|
||||
date: bankDateSchema,
|
||||
description: z
|
||||
.string()
|
||||
.max(500, validationMessage("bank.validation.description")(undefined)),
|
||||
.max(
|
||||
MAX_DESCRIPTION_LENGTH,
|
||||
validationMessage("bank.validation.description")(undefined),
|
||||
),
|
||||
});
|
||||
|
||||
type OperationFormProps = {
|
||||
|
||||
@@ -4,10 +4,12 @@ import { useStockSearch } from "@/entities/stock";
|
||||
import { apiErrorCode } from "@/shared/api";
|
||||
import { instrumentLogoUrl } from "@/shared/lib";
|
||||
|
||||
const SEARCH_DEBOUNCE_MS = 300;
|
||||
|
||||
function useStocksPageVm() {
|
||||
const [params, setParams] = useSearchParams();
|
||||
const query = params.get("q") ?? "";
|
||||
const [debounced] = useDebouncedValue(query.trim(), 300);
|
||||
const [debounced] = useDebouncedValue(query.trim(), SEARCH_DEBOUNCE_MS);
|
||||
const result = useStockSearch(debounced);
|
||||
const waiting = query.trim() !== debounced;
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user