refactor: enforce semantic values and numeric lint rules
This commit is contained in:
@@ -30,6 +30,8 @@ type CreateExpenseCategory =
|
||||
type UpdateExpenseCategory =
|
||||
paths["/api/v1/bank-cards/categories/{id}"]["patch"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
const OPERATIONS_PAGE_SIZE = 100;
|
||||
|
||||
function useBankCards(includeArchived = false) {
|
||||
return useAuthenticatedQuery(
|
||||
apiQueryClient.queryOptions("get", "/api/v1/bank-cards", {
|
||||
@@ -71,7 +73,7 @@ function useBankOperations(
|
||||
query: {
|
||||
...query,
|
||||
page: all ? 1 : query.page,
|
||||
limit: query.limit === "all" ? 100 : query.limit,
|
||||
limit: query.limit === "all" ? OPERATIONS_PAGE_SIZE : query.limit,
|
||||
},
|
||||
};
|
||||
const options = apiQueryClient.queryOptions(
|
||||
@@ -87,7 +89,7 @@ function useBankOperations(
|
||||
) => {
|
||||
const previous = previousQuery?.queryKey[2]?.params;
|
||||
return !all &&
|
||||
previousQuery?.queryKey.length === 3 &&
|
||||
previousQuery?.queryKey.length === options.queryKey.length &&
|
||||
previous?.path?.id === id &&
|
||||
previous?.query?.month === query.month &&
|
||||
previous?.query?.from === query.from &&
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { z } from "zod";
|
||||
import { validationMessage } from "@/shared/i18n";
|
||||
|
||||
const ISO_MONTH_LENGTH = 7;
|
||||
const MAX_CARD_NAME_LENGTH = 100;
|
||||
|
||||
function bankToday() {
|
||||
return new Intl.DateTimeFormat("en-CA", {
|
||||
timeZone: "Europe/Moscow",
|
||||
@@ -15,7 +18,7 @@ function bankDateInputValue(value: string) {
|
||||
function bankMonth(value: string | null) {
|
||||
return value && /^\d{4}-(0[1-9]|1[0-2])$/.test(value)
|
||||
? value
|
||||
: bankToday().slice(0, 7);
|
||||
: bankToday().slice(0, ISO_MONTH_LENGTH);
|
||||
}
|
||||
function bankMoneyValue(value: string) {
|
||||
return value
|
||||
@@ -32,12 +35,15 @@ function moneyCents(value: string): bigint | null {
|
||||
(normalized.startsWith("-") ? -1n : 1n)
|
||||
);
|
||||
}
|
||||
const MIN_BALANCE_KOPECKS = -9_223_372_036_854_775_808n;
|
||||
const MAX_BALANCE_KOPECKS = 9_223_372_036_854_775_807n;
|
||||
|
||||
const moneySchema = z.string().refine((value) => {
|
||||
const cents = moneyCents(value);
|
||||
return (
|
||||
cents !== null &&
|
||||
cents >= -9223372036854775808n &&
|
||||
cents <= 9223372036854775807n
|
||||
cents >= MIN_BALANCE_KOPECKS &&
|
||||
cents <= MAX_BALANCE_KOPECKS
|
||||
);
|
||||
}, validationMessage("bank.validation.money"));
|
||||
const positiveMoneySchema = moneySchema.refine((value) => {
|
||||
@@ -60,11 +66,17 @@ const bankCardSchema = z.object({
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, validationMessage("bank.validation.name")(undefined))
|
||||
.max(100, validationMessage("bank.validation.name")(undefined)),
|
||||
.max(
|
||||
MAX_CARD_NAME_LENGTH,
|
||||
validationMessage("bank.validation.name")(undefined),
|
||||
),
|
||||
bankName: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(100, validationMessage("bank.validation.bankName")(undefined)),
|
||||
.max(
|
||||
MAX_CARD_NAME_LENGTH,
|
||||
validationMessage("bank.validation.bankName")(undefined),
|
||||
),
|
||||
lastFour: z
|
||||
.string()
|
||||
.refine(
|
||||
|
||||
@@ -25,6 +25,8 @@ import classes from "./BankForms.module.css";
|
||||
import { BankOperationIcon } from "./BankOperationIcon";
|
||||
import { useBankFormFocus } from "./useBankFormFocus";
|
||||
|
||||
const MAX_CATEGORY_NAME_LENGTH = 80;
|
||||
|
||||
type CategoryManagerProps = {
|
||||
onClose: () => void;
|
||||
onCreated?: (id: string) => void;
|
||||
@@ -67,7 +69,8 @@ const CategoryManager: FC<CategoryManagerProps> = (props) => {
|
||||
.string()
|
||||
.trim()
|
||||
.refine(
|
||||
(value) => value.length > 0 && value.length <= 80,
|
||||
(value) =>
|
||||
value.length > 0 && value.length <= MAX_CATEGORY_NAME_LENGTH,
|
||||
validationMessage("bank.validation.category"),
|
||||
)
|
||||
.refine(
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { apiQueryClient, useAuthenticatedQuery } from "@/shared/api";
|
||||
import {
|
||||
apiQueryClient,
|
||||
type paths,
|
||||
useAuthenticatedQuery,
|
||||
} from "@/shared/api";
|
||||
|
||||
type BondPeriod = "month" | "halfYear" | "year" | "all";
|
||||
type BondPeriod = NonNullable<
|
||||
NonNullable<
|
||||
paths["/api/v1/bonds/{uid}/candles"]["get"]["parameters"]["query"]
|
||||
>["period"]
|
||||
>;
|
||||
|
||||
const BOND_STALE_TIME_MS = 30_000;
|
||||
|
||||
function useBondSearch(query: string) {
|
||||
return useAuthenticatedQuery({
|
||||
@@ -8,7 +18,7 @@ function useBondSearch(query: string) {
|
||||
params: { query: { query } },
|
||||
}),
|
||||
enabled: query.length >= 2,
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function bondQueryOptions(uid: string) {
|
||||
@@ -16,7 +26,7 @@ function bondQueryOptions(uid: string) {
|
||||
...apiQueryClient.queryOptions("get", "/api/v1/bonds/{uid}", {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +45,7 @@ function useBond(identifier: string) {
|
||||
params: { path: { ticker: identifier } },
|
||||
}),
|
||||
enabled: Boolean(identifier) && !isUid,
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
return isUid ? byUid : byTicker;
|
||||
}
|
||||
@@ -45,7 +55,7 @@ function useBondMetrics(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useBondQuote(uid: string) {
|
||||
@@ -54,7 +64,7 @@ function useBondQuote(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useBondCandles(uid: string, period: BondPeriod) {
|
||||
@@ -63,7 +73,7 @@ function useBondCandles(uid: string, period: BondPeriod) {
|
||||
params: { path: { uid }, query: { period } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useBondPosition(uid: string) {
|
||||
@@ -72,7 +82,7 @@ function useBondPosition(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useBondCoupons(uid: string) {
|
||||
@@ -81,7 +91,7 @@ function useBondCoupons(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: BOND_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ function formatShare(
|
||||
if (!numerator || !denominator) return null;
|
||||
const total = Number(denominator.value);
|
||||
if (!Number.isFinite(total) || total <= 0) return null;
|
||||
// biome-ignore lint/style/noMagicNumbers: obvious conversion of a ratio to percent.
|
||||
const percent = (Number(numerator.value) / total) * 100;
|
||||
if (!Number.isFinite(percent)) return null;
|
||||
const rounded = new Intl.NumberFormat("ru-RU", {
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import {
|
||||
apiQueryClient,
|
||||
type paths,
|
||||
useAuthenticatedQueries,
|
||||
useAuthenticatedQuery,
|
||||
} from "@/shared/api";
|
||||
|
||||
type StockPeriod = "month" | "halfYear" | "year" | "all";
|
||||
type StockPeriod = NonNullable<
|
||||
NonNullable<
|
||||
paths["/api/v1/stocks/{uid}/candles"]["get"]["parameters"]["query"]
|
||||
>["period"]
|
||||
>;
|
||||
|
||||
const STOCK_STALE_TIME_MS = 30_000;
|
||||
|
||||
function useStockSearch(query: string) {
|
||||
return useAuthenticatedQuery({
|
||||
@@ -12,7 +19,7 @@ function useStockSearch(query: string) {
|
||||
params: { query: { query } },
|
||||
}),
|
||||
enabled: query.length >= 2,
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function stockQueryOptions(uid: string) {
|
||||
@@ -20,7 +27,7 @@ function stockQueryOptions(uid: string) {
|
||||
...apiQueryClient.queryOptions("get", "/api/v1/stocks/{uid}", {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ function useStock(identifier: string) {
|
||||
params: { path: { ticker: identifier } },
|
||||
}),
|
||||
enabled: Boolean(identifier) && !isUid,
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
return isUid ? byUid : byTicker;
|
||||
}
|
||||
@@ -57,7 +64,7 @@ function useStockQuote(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useStockCandles(uid: string, period: StockPeriod) {
|
||||
@@ -66,7 +73,7 @@ function useStockCandles(uid: string, period: StockPeriod) {
|
||||
params: { path: { uid }, query: { period } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useStockPosition(uid: string) {
|
||||
@@ -75,7 +82,7 @@ function useStockPosition(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useStockCompany(uid: string) {
|
||||
@@ -84,7 +91,7 @@ function useStockCompany(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
function useStockDividends(uid: string) {
|
||||
@@ -93,7 +100,7 @@ function useStockDividends(uid: string) {
|
||||
params: { path: { uid } },
|
||||
}),
|
||||
enabled: Boolean(uid),
|
||||
staleTime: 30_000,
|
||||
staleTime: STOCK_STALE_TIME_MS,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user