Compare commits
6
Commits
fe91ddbb91
..
v1.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df86b8450f | ||
|
|
b5ceb77208 | ||
|
|
b009101b7b | ||
|
|
7e6fd95cfa | ||
|
|
2cd6b1e72d | ||
|
|
3af9df0ae2 |
@@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## [1.2.1](https://git.personal.ksv741.ru/WealthLedger/frontend/compare/v1.2.0...v1.2.1) (2026-10-24)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* keep session when refresh fails on network error or 5xx (WealthLedger/frontend[#97](https://git.personal.ksv741.ru/WealthLedger/frontend/issues/97)) ([3af9df0](https://git.personal.ksv741.ru/WealthLedger/frontend/commit/3af9df0ae2f0fb314c276504ef7d50a7472de680))
|
||||
* show snapshot time in browser time zone ([#101](https://git.personal.ksv741.ru/WealthLedger/frontend/issues/101)) ([b009101](https://git.personal.ksv741.ru/WealthLedger/frontend/commit/b009101b7b954e8462672a9da859267ebb71edc4))
|
||||
|
||||
### Maintenance
|
||||
|
||||
* bump Node to 24.22.1 (WealthLedger/backend[#122](https://git.personal.ksv741.ru/WealthLedger/frontend/issues/122)) ([2cd6b1e](https://git.personal.ksv741.ru/WealthLedger/frontend/commit/2cd6b1e72d97117b0c37465aac3d989b0469c1c3))
|
||||
* bump test stack and fix npm audit findings ([#105](https://git.personal.ksv741.ru/WealthLedger/frontend/issues/105)) ([7e6fd95](https://git.personal.ksv741.ru/WealthLedger/frontend/commit/7e6fd95cfaf7d288242fb46ec7091f561e6f3de5))
|
||||
* **deps:** update non-major dependencies [#9](https://git.personal.ksv741.ru/WealthLedger/frontend/issues/9) ([b5ceb77](https://git.personal.ksv741.ru/WealthLedger/frontend/commit/b5ceb77208e35a0e389258f3d3c4b85852980042))
|
||||
* resolve dev dependency audit findings (WealthLedger/frontend[#103](https://git.personal.ksv741.ru/WealthLedger/frontend/issues/103)) ([7b6f34c](https://git.personal.ksv741.ru/WealthLedger/frontend/commit/7b6f34c776fa3517c93b5c3468e3b277aaa6273f))
|
||||
|
||||
## [1.2.0](https://git.personal.ksv741.ru/WealthLedger/frontend/compare/v1.1.0...v1.2.0) (2026-10-18)
|
||||
|
||||
### Features
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
FROM node:24.21.0-bookworm-slim AS dependencies
|
||||
FROM node:24.22.1-bookworm-slim AS dependencies
|
||||
WORKDIR /app
|
||||
ENV HUSKY=0
|
||||
COPY package.json package-lock.json ./
|
||||
@@ -19,7 +19,7 @@ COPY . .
|
||||
ENV VITE_API_BASE_URL=""
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.28.0-alpine AS production
|
||||
FROM nginx:1.31.8-alpine AS production
|
||||
ARG VERSION
|
||||
ARG REVISION
|
||||
LABEL org.opencontainers.image.version=$VERSION \
|
||||
|
||||
Generated
+1003
-1138
File diff suppressed because it is too large
Load Diff
+46
-44
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "wealth-ledger-frontend",
|
||||
"private": true,
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.1",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": "24.21.0"
|
||||
"node": "24.22.1"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "npm run build:packages && vite",
|
||||
@@ -37,88 +37,90 @@
|
||||
"*.{js,mjs,cjs,ts,tsx}": "eslint --fix --max-warnings=0 --no-warn-ignored"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "5.9.1",
|
||||
"@mantine/charts": "9.6.1",
|
||||
"@mantine/core": "9.6.1",
|
||||
"@mantine/dates": "9.6.1",
|
||||
"@mantine/hooks": "9.6.1",
|
||||
"@mantine/notifications": "9.6.1",
|
||||
"@tanstack/react-query": "5.102.8",
|
||||
"@hookform/resolvers": "5.9.2",
|
||||
"@mantine/charts": "9.7.3",
|
||||
"@mantine/core": "9.7.3",
|
||||
"@mantine/dates": "9.7.3",
|
||||
"@mantine/hooks": "9.7.3",
|
||||
"@mantine/notifications": "9.7.3",
|
||||
"@tanstack/react-query": "5.104.5",
|
||||
"@wealthledger/icons": "file:packages/icons",
|
||||
"@wealthledger/tokens": "file:packages/tokens",
|
||||
"@wealthledger/ui": "file:packages/ui",
|
||||
"dayjs": "1.11.23",
|
||||
"i18next": "26.4.2",
|
||||
"motion": "13.2.0",
|
||||
"i18next": "26.5.10",
|
||||
"motion": "13.5.1",
|
||||
"openapi-fetch": "0.17.0",
|
||||
"openapi-react-query": "0.5.4",
|
||||
"react": "19.3.0",
|
||||
"react-dom": "19.3.0",
|
||||
"react-hook-form": "7.88.0",
|
||||
"react-i18next": "17.0.14",
|
||||
"react-is": "19.3.0",
|
||||
"react-router": "8.3.1",
|
||||
"react": "19.3.1",
|
||||
"react-dom": "19.3.1",
|
||||
"react-hook-form": "7.91.0",
|
||||
"react-i18next": "17.1.8",
|
||||
"react-is": "19.3.1",
|
||||
"react-router": "8.5.0",
|
||||
"recharts": "3.10.1",
|
||||
"zod": "4.6.4"
|
||||
"zod": "4.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "8.0.5",
|
||||
"@commitlint/cli": "21.2.2",
|
||||
"@commitlint/config-conventional": "21.2.2",
|
||||
"@eslint-react/eslint-plugin": "5.21.0",
|
||||
"@babel/core": "8.0.9",
|
||||
"@commitlint/cli": "21.2.4",
|
||||
"@commitlint/config-conventional": "21.2.4",
|
||||
"@eslint-react/eslint-plugin": "5.28.8",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@release-it/conventional-changelog": "12.0.0",
|
||||
"@release-it/conventional-changelog": "12.0.2",
|
||||
"@rolldown/plugin-babel": "0.2.4",
|
||||
"@storybook/addon-docs": "10.6.0",
|
||||
"@storybook/react-vite": "10.6.0",
|
||||
"@storybook/addon-docs": "10.6.3",
|
||||
"@storybook/react-vite": "10.6.3",
|
||||
"@stylistic/eslint-plugin": "5.10.0",
|
||||
"@tanstack/eslint-plugin-query": "5.104.0",
|
||||
"@tanstack/eslint-plugin-query": "5.104.5",
|
||||
"@testing-library/jest-dom": "7.0.1",
|
||||
"@testing-library/react": "16.3.3",
|
||||
"@types/node": "24.13.4",
|
||||
"@types/node": "24.19.2",
|
||||
"@types/react": "19.3.0",
|
||||
"@types/react-dom": "19.3.0",
|
||||
"@vitejs/plugin-react": "6.1.1",
|
||||
"@vitejs/plugin-react": "6.1.2",
|
||||
"@vitest/browser": "5.0.5",
|
||||
"@vitest/browser-playwright": "5.0.5",
|
||||
"@vitest/coverage-v8": "5.0.5",
|
||||
"@vitest/eslint-plugin": "1.6.27",
|
||||
"@vitest/ui": "5.0.5",
|
||||
"babel-plugin-react-compiler": "1.0.0",
|
||||
"conventional-changelog-conventionalcommits": "10.4.0",
|
||||
"dependency-cruiser": "18.2.0",
|
||||
"eslint": "10.11.0",
|
||||
"conventional-changelog-conventionalcommits": "10.4.1",
|
||||
"dependency-cruiser": "18.7.0",
|
||||
"eslint": "10.13.0",
|
||||
"eslint-import-resolver-typescript": "4.4.5",
|
||||
"eslint-plugin-import-x": "4.17.1",
|
||||
"eslint-plugin-jest-dom": "5.10.1",
|
||||
"eslint-plugin-no-unsanitized": "4.1.5",
|
||||
"eslint-plugin-perfectionist": "5.12.1",
|
||||
"eslint-plugin-perfectionist": "5.13.0",
|
||||
"eslint-plugin-react-hooks": "7.1.1",
|
||||
"eslint-plugin-react-refresh": "0.5.7",
|
||||
"eslint-plugin-regexp": "3.3.1",
|
||||
"eslint-plugin-security": "4.1.0",
|
||||
"eslint-plugin-sonarjs": "4.2.1",
|
||||
"eslint-plugin-storybook": "10.6.0",
|
||||
"eslint-plugin-security": "4.2.0",
|
||||
"eslint-plugin-sonarjs": "4.2.2",
|
||||
"eslint-plugin-storybook": "10.6.3",
|
||||
"eslint-plugin-testing-library": "7.16.2",
|
||||
"eslint-plugin-unicorn": "76.0.0",
|
||||
"globals": "17.12.0",
|
||||
"globals": "17.13.0",
|
||||
"husky": "9.1.7",
|
||||
"jsdom": "30.0.1",
|
||||
"knip": "6.35.1",
|
||||
"lint-staged": "17.5.1",
|
||||
"jsdom": "30.1.3",
|
||||
"knip": "6.43.0",
|
||||
"lint-staged": "17.6.0",
|
||||
"msw": "2.15.0",
|
||||
"openapi-typescript": "7.13.0",
|
||||
"playwright": "1.63.0",
|
||||
"release-it": "21.0.2",
|
||||
"playwright": "1.64.1",
|
||||
"release-it": "21.1.1",
|
||||
"storybook": "10.6.3",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.70.1",
|
||||
"typescript-eslint": "8.72.0",
|
||||
"vite": "8.3.5",
|
||||
"vite-plugin-pwa": "1.3.0",
|
||||
"vitest": "5.0.5"
|
||||
},
|
||||
"overrides": {
|
||||
"release-it": {
|
||||
"basic-ftp": "6.2.1",
|
||||
"undici": "7.29.1"
|
||||
"basic-ftp": "6.2.3",
|
||||
"undici": "8.12.3"
|
||||
},
|
||||
"@vitest/browser": "5.0.5",
|
||||
"@vitest/ui": "5.0.5",
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
"build": "vite build && tsc -p tsconfig.json"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "19.3.0"
|
||||
"react": "19.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/react-vite": "10.6.0",
|
||||
"@storybook/react-vite": "10.6.3",
|
||||
"@testing-library/react": "16.3.3",
|
||||
"@types/react": "19.3.0",
|
||||
"typescript": "5.9.3",
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
"@wealthledger/tokens": "0.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@mantine/core": "9.6.1",
|
||||
"@mantine/hooks": "9.6.1",
|
||||
"react": "19.3.0",
|
||||
"react-dom": "19.3.0"
|
||||
"@mantine/core": "9.7.3",
|
||||
"@mantine/hooks": "9.7.3",
|
||||
"react": "19.3.1",
|
||||
"react-dom": "19.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/react-vite": "10.6.0",
|
||||
"@storybook/react-vite": "10.6.3",
|
||||
"@testing-library/react": "16.3.3",
|
||||
"@types/react": "19.3.0",
|
||||
"storybook": "10.6.3",
|
||||
|
||||
@@ -10,7 +10,6 @@ import { useAuth } from "@/features/auth";
|
||||
|
||||
type BoundaryProps = PropsWithChildren<{
|
||||
authenticated: boolean;
|
||||
routeKey: string;
|
||||
}>;
|
||||
|
||||
type RouteErrorFallbackProps = {
|
||||
@@ -26,12 +25,6 @@ class Boundary extends Component<BoundaryProps, State> {
|
||||
|
||||
override state: State = { hasError: false };
|
||||
|
||||
override componentDidUpdate(previousProps: BoundaryProps) {
|
||||
if (previousProps.routeKey !== this.props.routeKey && this.state.hasError) {
|
||||
this.setState({ hasError: false });
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
return this.state.hasError
|
||||
? <RouteErrorFallback authenticated={this.props.authenticated} />
|
||||
@@ -70,10 +63,7 @@ const RouteErrorBoundary: FC<PropsWithChildren> = (props) => {
|
||||
const { user } = useAuth();
|
||||
|
||||
return (
|
||||
<Boundary
|
||||
authenticated={Boolean(user)}
|
||||
routeKey={`${pathname}${search}:${key}`}
|
||||
>
|
||||
<Boundary authenticated={Boolean(user)} key={`${pathname}${search}:${key}`}>
|
||||
{children}
|
||||
</Boundary>
|
||||
);
|
||||
|
||||
@@ -72,6 +72,9 @@ function renderEvents(accountId: string, searches: string[]) {
|
||||
}
|
||||
|
||||
describe("Upcoming event date filters", () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
beforeEach(() => {
|
||||
// Pin "today": the test picks today + 10 days, which must be inside the visible calendar grid.
|
||||
// Real dates can push it beyond the grid (e.g. 2026-10-23 -> 2 November).
|
||||
@@ -235,6 +238,10 @@ describe("Upcoming event date filters", () => {
|
||||
it.each(["account-1", "all"])(
|
||||
"applies dates only on submit, restores URL dates, and resets from empty results for %s",
|
||||
async (accountId) => {
|
||||
// Pin "today" mid-month so "today + 10 days" stays in the calendar's
|
||||
// visible month regardless of the real date.
|
||||
vi.useFakeTimers({ toFake: ["Date"] });
|
||||
vi.setSystemTime(new Date("2026-10-15T12:00:00+03:00"));
|
||||
const [defaultFrom, defaultTo] = defaultPeriod();
|
||||
const searches: string[] = [];
|
||||
server.use(
|
||||
|
||||
@@ -413,7 +413,9 @@ describe("stock page", () => {
|
||||
expect(await screen.findByText("Продажа")).toBeVisible();
|
||||
expect(screen.getByText("2 шт.")).toBeVisible();
|
||||
expect(screen.getByText("Цена: 100 ₽")).toBeVisible();
|
||||
expect(screen.getByText(/02.01.2022, 10:00 UTC/)).toBeVisible();
|
||||
const snapshotTime = new Intl.DateTimeFormat("ru-RU", { dateStyle: "short", timeStyle: "short" });
|
||||
const operationTime = snapshotTime.format(new Date("2022-01-02T10:00:00Z"));
|
||||
expect(screen.getByText(operationTime, { exact: false })).toBeVisible();
|
||||
expect(
|
||||
requests.some(
|
||||
url =>
|
||||
|
||||
@@ -93,7 +93,9 @@ describe("stock search", () => {
|
||||
expect(
|
||||
await screen.findByRole("link", { name: /Сбербанк/ }),
|
||||
).toHaveAttribute("href", "/stocks/e6123145-9665-43e0-8413-cd61b8aa9b13");
|
||||
expect(screen.getByText("Список получен 10.10.2026, 10:00")).toBeVisible();
|
||||
const snapshotTime = new Intl.DateTimeFormat("ru-RU", { dateStyle: "short", timeStyle: "short" });
|
||||
const fetchedAt = snapshotTime.format(new Date("2026-10-10T10:00:00Z"));
|
||||
expect(screen.getByText(`Список получен ${fetchedAt}`)).toBeVisible();
|
||||
expect(screen.getByText("Лот: 10 шт.")).toBeVisible();
|
||||
expect(screen.getByText("ISIN: RU0009029540")).toBeVisible();
|
||||
expect(screen.getAllByText("Мосбиржа")[0]).toBeVisible();
|
||||
|
||||
@@ -420,7 +420,7 @@ const ru = {
|
||||
next: "Далее",
|
||||
noInstrument: "Без инструмента",
|
||||
noMoney: "Без движения денег",
|
||||
occurredAt: "{{date}} UTC",
|
||||
occurredAt: "{{date}}",
|
||||
open: "Открыть журнал",
|
||||
operationType: {
|
||||
buy: "Покупка",
|
||||
@@ -632,7 +632,7 @@ const ru = {
|
||||
position: "Моя позиция",
|
||||
previous: "Назад",
|
||||
price: "Цена",
|
||||
priceAt: "Цена на {{date}} UTC",
|
||||
priceAt: "Цена на {{date}}",
|
||||
priceTable: "Цены в таблице",
|
||||
priceTableHint: "Дневные закрытия · записей: {{count}} · сначала новые",
|
||||
priceUnavailable: "Цена недоступна",
|
||||
@@ -721,7 +721,7 @@ const ru = {
|
||||
SHARE_TYPE_GDR: "Депозитарная расписка",
|
||||
SHARE_TYPE_PREFERRED: "Привилегированная акция",
|
||||
},
|
||||
snapshotAt: "Позиция на {{date}} UTC",
|
||||
snapshotAt: "Позиция на {{date}}",
|
||||
trading: {
|
||||
SECURITY_TRADING_STATUS_BREAK_IN_TRADING: "Перерыв в торгах",
|
||||
SECURITY_TRADING_STATUS_CLOSING_AUCTION: "Аукцион закрытия",
|
||||
|
||||
@@ -12,9 +12,16 @@ describe("shared display helpers", () => {
|
||||
expect(formatDate("2027-09-10")).toBe("10.09.2027");
|
||||
expect(formatDate("2024-02-29")).toBe("29.02.2024");
|
||||
expect(formatDate("2027-09-10T00:30:00+03:00")).toBe("09.09.2027");
|
||||
expect(formatSnapshotTime("2027-09-10T00:30:00+03:00")).toBe(
|
||||
"09.09.2027, 21:30",
|
||||
);
|
||||
});
|
||||
it("formats snapshot time in the browser time zone", () => {
|
||||
const instant = "2027-09-10T00:30:00+03:00";
|
||||
|
||||
const expected = new Intl.DateTimeFormat("ru-RU", {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
}).format(new Date(instant));
|
||||
|
||||
expect(formatSnapshotTime(instant)).toBe(expected);
|
||||
});
|
||||
it("formats decimal strings without financial arithmetic", () => {
|
||||
expect(formatMoney({ currency: "RUB", value: "-1234567.89" })).toBe(
|
||||
|
||||
@@ -61,10 +61,10 @@ function formatMoney(
|
||||
return `${formatDecimal(decimal)} ${currency}`;
|
||||
}
|
||||
|
||||
// Без timeZone Intl берёт часовой пояс браузера пользователя.
|
||||
const snapshotFormatter = new Intl.DateTimeFormat("ru-RU", {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
timeZone: "UTC",
|
||||
});
|
||||
|
||||
function formatSnapshotTime(value: string): string {
|
||||
|
||||
Reference in New Issue
Block a user