feat: add all-accounts portfolio pages

Reuse account sections and group securities with per-account holdings.

Use Mantine Select to preserve filters and reset pagination on scope changes.

Document component reuse guardrails and cover routes, grouping and scope switching.
This commit is contained in:
2026-09-09 20:36:49 +03:00
parent c906dfa2e2
commit daf1dca666
36 changed files with 1962 additions and 117 deletions
+417
View File
@@ -116,6 +116,22 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/all/operations": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["BrokerageOperationsController_all"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/{accountId}/operations": {
parameters: {
query?: never;
@@ -132,6 +148,22 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/all/portfolio": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["PortfolioController_all"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/{accountId}/portfolio": {
parameters: {
query?: never;
@@ -148,6 +180,22 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/all/events": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["UpcomingEventsController_all"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/{accountId}/events": {
parameters: {
query?: never;
@@ -164,6 +212,38 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/all/income-summary": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["IncomeAnalyticsController_allSummary"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/all/income-analytics": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: operations["IncomeAnalyticsController_allAnalytics"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/brokerage-accounts/{accountId}/income-summary": {
parameters: {
query?: never;
@@ -253,7 +333,12 @@ export interface components {
openedOn: string | null;
portfolioSummary: components["schemas"]["PortfolioSummaryDto"] | null;
};
AccountReferenceDto: {
id: string;
name: string;
};
BrokerageOperationDto: {
account?: components["schemas"]["AccountReferenceDto"];
id: string;
/** Format: date-time */
occurredAt: string;
@@ -281,6 +366,7 @@ export interface components {
observedAt: string;
};
PortfolioPositionDto: {
account?: components["schemas"]["AccountReferenceDto"];
figi: string | null;
displayName: string;
/** @enum {string} */
@@ -302,6 +388,7 @@ export interface components {
cashBalances: components["schemas"]["MoneyDto"][];
};
UpcomingEventDto: {
account?: components["schemas"]["AccountReferenceDto"];
/** @enum {string} */
type: "coupon" | "dividend" | "maturity" | "offer";
/** Format: date */
@@ -369,6 +456,7 @@ export interface components {
receiptCount: number;
};
IncomeAnalyticsEntryDto: {
account?: components["schemas"]["AccountReferenceDto"];
id: string;
/** Format: date-time */
occurredAt: string;
@@ -995,6 +1083,76 @@ export interface operations {
};
};
};
BrokerageOperationsController_all: {
parameters: {
query?: {
from?: string;
to?: string;
categories?: ("trade" | "income" | "tax" | "fee" | "transfer" | "other")[];
cursor?: string;
limit?: number;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["BrokerageOperationsPageDto"];
};
};
/** @description Invalid request */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Authentication required */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Resource not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Upstream service unavailable */
503: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
};
};
BrokerageOperationsController_get: {
parameters: {
query?: {
@@ -1067,6 +1225,70 @@ export interface operations {
};
};
};
PortfolioController_all: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["PortfolioDto"];
};
};
/** @description Invalid request */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Authentication required */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Resource not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Upstream service unavailable */
503: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
};
};
PortfolioController_get: {
parameters: {
query?: never;
@@ -1133,6 +1355,70 @@ export interface operations {
};
};
};
UpcomingEventsController_all: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["UpcomingEventsDto"];
};
};
/** @description Invalid request */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Authentication required */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Resource not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Upstream service unavailable */
503: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
};
};
UpcomingEventsController_get: {
parameters: {
query?: never;
@@ -1199,6 +1485,137 @@ export interface operations {
};
};
};
IncomeAnalyticsController_allSummary: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["IncomeSummaryDto"];
};
};
/** @description Invalid request */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Authentication required */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Resource not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Upstream service unavailable */
503: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
};
};
IncomeAnalyticsController_allAnalytics: {
parameters: {
query?: {
from?: string;
to?: string;
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["IncomeAnalyticsDto"];
};
};
/** @description Invalid request */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Authentication required */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Resource not found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
/** @description Upstream service unavailable */
503: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDetailsDto"];
};
};
};
};
IncomeAnalyticsController_summary: {
parameters: {
query?: never;