diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md
index 1cd06b3..e60d973 100644
--- a/docs/CONVENTIONS.md
+++ b/docs/CONVENTIONS.md
@@ -9,6 +9,8 @@
- Публичный импорт слайса идёт через его `index.ts`.
- Тесты лежат рядом с исходником как `*.test.tsx`.
- Зависимости используют exact versions; не добавлять semver ranges.
+- Главный `*.view.tsx` страницы не содержит несколько state-компонентов:
+ каждый state/section — отдельный `*.view.tsx` в `components/` (см. File roles).
- Полный локальный gate: `npm run verify`. Pre-commit запускает staged formatting, OpenAPI check и FSD dependency checks; commit message должен следовать Conventional Commits.
## React component style
@@ -37,6 +39,11 @@ Mandatory review conventions:
- `Component.types.ts` - props и локальные типы.
- `Component.module.css` - локальные стили.
-Большие view files дробить по ответственности. Если section/state можно
-самостоятельно назвать, вынести его в соседний presentation component вместо
-накопления смысловой нагрузки в одном файле.
+State-компоненты страницы не колоцируются в одном `*.view.tsx`. Каждый
+top-level state (loading, error, empty, loaded) и каждый именуемый section
+выносится в отдельный `*.view.tsx` в подпапку `components/`. Имя файла
+отражает state/section: `AccountOperationsPageLoaded.view.tsx`,
+`AccountOperationsPageLoading.view.tsx`, `AccountOperationsPageError.view.tsx`.
+Контейнер (`AccountOperationsPage.tsx`) импортирует их из `./components` и
+выбирает нужный по `viewModel.state`. Нельзя держать несколько state components
+внутри одного файла view — это mandatory review convention.
diff --git a/docs/UI-PATTERN.md b/docs/UI-PATTERN.md
index af140f6..43634c8 100644
--- a/docs/UI-PATTERN.md
+++ b/docs/UI-PATTERN.md
@@ -25,10 +25,12 @@ image state. Простое форматирование одного перед
существующий formatter остаётся presentation concern; вычисления из всей
коллекции остаются во VM.
-View files дробятся по смысловой нагрузке, а не только по количеству строк. Если
-section или state имеет самостоятельный смысл и может быть назван, его нужно
-выносить в nearby presentation component вместо накопления внутри одного
-большого `*.view.tsx`.
+View files дробятся по смысловой нагрузке, а не только по количеству строк.
+Каждый top-level state и каждый именуемый section страницы — отдельный
+`*.view.tsx` в `components/`, а не накопление внутри одного большого
+`*.view.tsx`. Контейнер импортирует state views из `./components` и выбирает
+нужный по `viewModel.state`. Держать loading/error/empty/loaded components в
+одном файле view нельзя.
Known API errors, retry и stale snapshot остаются local views, а непойманные
route errors обрабатывает общий route-keyed ErrorBoundary.
diff --git a/src/pages/account-operations/ui/AccountOperationsPage.tsx b/src/pages/account-operations/ui/AccountOperationsPage.tsx
index 6e1cc29..4df8ed0 100644
--- a/src/pages/account-operations/ui/AccountOperationsPage.tsx
+++ b/src/pages/account-operations/ui/AccountOperationsPage.tsx
@@ -1,18 +1,21 @@
import type { FC } from "react";
import { LedgerEntryRow } from "@/entities/broker-operation";
-import {
- AccountOperationsPageError,
- AccountOperationsPageLoaded,
- AccountOperationsPageLoading,
-} from "./AccountOperationsPage.view";
import { useAccountOperationsPageVm } from "./AccountOperationsPage.vm";
+import { AccountOperationsPageError } from "./components/AccountOperationsPageError.view";
+import { AccountOperationsPageLoaded } from "./components/AccountOperationsPageLoaded.view";
+import { AccountOperationsPageLoading } from "./components/AccountOperationsPageLoading.view";
const AccountOperationsPage: FC = () => {
const viewModel = useAccountOperationsPageVm();
- if (viewModel.state === "loading") return ;
- if (viewModel.state === "error")
+ if (viewModel.state === "loading") {
+ return ;
+ }
+
+ if (viewModel.state === "error") {
return ;
+ }
+
return ;
};
diff --git a/src/pages/account-operations/ui/components/AccountOperationsPageError.view.tsx b/src/pages/account-operations/ui/components/AccountOperationsPageError.view.tsx
new file mode 100644
index 0000000..c5987d6
--- /dev/null
+++ b/src/pages/account-operations/ui/components/AccountOperationsPageError.view.tsx
@@ -0,0 +1,28 @@
+import { Button } from "@mantine/core";
+import type { FC } from "react";
+import { useTranslation } from "react-i18next";
+import { StateView } from "@/shared/ui";
+import type { AccountOperationsPageViewModel } from "../AccountOperationsPage.vm";
+
+type AccountOperationsPageErrorProps = Extract<
+ AccountOperationsPageViewModel,
+ { state: "error" }
+>;
+
+const AccountOperationsPageError: FC = (
+ props,
+) => {
+ const { retry } = props;
+ const { t } = useTranslation();
+
+ return (
+ {t("ledger.retry")}}
+ />
+ );
+};
+
+export { AccountOperationsPageError };
diff --git a/src/pages/account-operations/ui/AccountOperationsPage.view.tsx b/src/pages/account-operations/ui/components/AccountOperationsPageLoaded.view.tsx
similarity index 71%
rename from src/pages/account-operations/ui/AccountOperationsPage.view.tsx
rename to src/pages/account-operations/ui/components/AccountOperationsPageLoaded.view.tsx
index 3800cc0..f22ef1c 100644
--- a/src/pages/account-operations/ui/AccountOperationsPage.view.tsx
+++ b/src/pages/account-operations/ui/components/AccountOperationsPageLoaded.view.tsx
@@ -14,46 +14,13 @@ import {
operationCategories,
} from "@/entities/broker-operation";
import { StateView } from "@/shared/ui";
-import type { AccountOperationsPageViewModel } from "./AccountOperationsPage.vm";
+import type { AccountOperationsPageViewModel } from "../AccountOperationsPage.vm";
type AccountOperationsPageLoadedProps = Extract<
AccountOperationsPageViewModel,
{ state: "loaded" }
>;
-type AccountOperationsPageErrorProps = Extract<
- AccountOperationsPageViewModel,
- { state: "error" }
->;
-
-const AccountOperationsPageLoading: FC = () => {
- const { t } = useTranslation();
-
- return (
-
- );
-};
-
-const AccountOperationsPageError: FC = (
- props,
-) => {
- const { retry } = props;
- const { t } = useTranslation();
-
- return (
- {t("ledger.retry")}}
- />
- );
-};
-
const AccountOperationsPageLoaded: FC = (
props,
) => {
@@ -128,8 +95,4 @@ const AccountOperationsPageLoaded: FC = (
);
};
-export {
- AccountOperationsPageError,
- AccountOperationsPageLoaded,
- AccountOperationsPageLoading,
-};
+export { AccountOperationsPageLoaded };
diff --git a/src/pages/account-operations/ui/components/AccountOperationsPageLoading.view.tsx b/src/pages/account-operations/ui/components/AccountOperationsPageLoading.view.tsx
new file mode 100644
index 0000000..7fb6c1c
--- /dev/null
+++ b/src/pages/account-operations/ui/components/AccountOperationsPageLoading.view.tsx
@@ -0,0 +1,17 @@
+import type { FC } from "react";
+import { useTranslation } from "react-i18next";
+import { StateView } from "@/shared/ui";
+
+const AccountOperationsPageLoading: FC = () => {
+ const { t } = useTranslation();
+
+ return (
+
+ );
+};
+
+export { AccountOperationsPageLoading };