From 6c4f9a97a690409d54f32b4a7bde6c20ff36dde4 Mon Sep 17 00:00:00 2001 From: rassadin11 Date: Wed, 10 Jun 2026 17:24:36 +0300 Subject: [PATCH] remove admin --- src/app/providers/RouterProvider.tsx | 6 - src/features/admin/api/adminApi.ts | 219 ------------------ src/features/admin/hooks/useAdminAuth.ts | 19 -- src/features/admin/hooks/useAdminLogin.ts | 22 -- src/features/admin/hooks/useAdminLogout.ts | 14 -- .../admin/hooks/useCreateOrganization.ts | 13 -- .../hooks/useCreateOrganizationWallets.ts | 18 -- src/features/admin/hooks/useDocuments.ts | 12 - src/features/admin/hooks/useOrganization.ts | 12 - .../admin/hooks/useOrganizationWallets.ts | 12 - src/features/admin/hooks/useOrganizations.ts | 11 - .../admin/hooks/usePurchaseRequests.ts | 15 -- .../admin/hooks/useUpdateOrganization.ts | 16 -- src/features/admin/hooks/useUploadDocument.ts | 20 -- src/features/admin/index.ts | 43 ---- src/features/admin/model/types.ts | 151 ------------ src/pages/admin-organization/index.ts | 1 - .../model/useOrganizationForm.ts | 90 ------- .../ui/AdminOrganizationPage.module.css | 154 ------------ .../ui/AdminOrganizationPage.tsx | 150 ------------ src/pages/admin/index.ts | 1 - src/pages/admin/ui/AdminPage.module.css | 84 ------- src/pages/admin/ui/AdminPage.tsx | 77 ------ src/shared/config/routes.ts | 5 - src/widgets/add-legal-entity-modal/index.ts | 1 - .../model/useAddLegalEntityForm.ts | 86 ------- .../ui/AddLegalEntityModal.module.css | 101 -------- .../ui/AddLegalEntityModal.tsx | 80 ------- src/widgets/admin-login-form/index.ts | 1 - .../model/useAdminLoginForm.ts | 35 --- .../ui/AdminLoginForm.module.css | 49 ---- .../admin-login-form/ui/AdminLoginForm.tsx | 40 ---- src/widgets/legal-entities-table/index.ts | 1 - .../ui/LegalEntitiesTable.module.css | 93 -------- .../ui/LegalEntitiesTable.tsx | 88 ------- src/widgets/organization-documents/index.ts | 1 - .../ui/OrganizationDocuments.module.css | 124 ---------- .../ui/OrganizationDocuments.tsx | 148 ------------ .../organization-purchase-requests/index.ts | 1 - .../OrganizationPurchaseRequests.module.css | 50 ---- .../ui/OrganizationPurchaseRequests.tsx | 63 ----- src/widgets/organization-wallets/index.ts | 1 - .../ui/OrganizationWallets.module.css | 52 ----- .../ui/OrganizationWallets.tsx | 53 ----- tsconfig.tsbuildinfo | 2 +- 45 files changed, 1 insertion(+), 2234 deletions(-) delete mode 100644 src/features/admin/api/adminApi.ts delete mode 100644 src/features/admin/hooks/useAdminAuth.ts delete mode 100644 src/features/admin/hooks/useAdminLogin.ts delete mode 100644 src/features/admin/hooks/useAdminLogout.ts delete mode 100644 src/features/admin/hooks/useCreateOrganization.ts delete mode 100644 src/features/admin/hooks/useCreateOrganizationWallets.ts delete mode 100644 src/features/admin/hooks/useDocuments.ts delete mode 100644 src/features/admin/hooks/useOrganization.ts delete mode 100644 src/features/admin/hooks/useOrganizationWallets.ts delete mode 100644 src/features/admin/hooks/useOrganizations.ts delete mode 100644 src/features/admin/hooks/usePurchaseRequests.ts delete mode 100644 src/features/admin/hooks/useUpdateOrganization.ts delete mode 100644 src/features/admin/hooks/useUploadDocument.ts delete mode 100644 src/features/admin/index.ts delete mode 100644 src/features/admin/model/types.ts delete mode 100644 src/pages/admin-organization/index.ts delete mode 100644 src/pages/admin-organization/model/useOrganizationForm.ts delete mode 100644 src/pages/admin-organization/ui/AdminOrganizationPage.module.css delete mode 100644 src/pages/admin-organization/ui/AdminOrganizationPage.tsx delete mode 100644 src/pages/admin/index.ts delete mode 100644 src/pages/admin/ui/AdminPage.module.css delete mode 100644 src/pages/admin/ui/AdminPage.tsx delete mode 100644 src/widgets/add-legal-entity-modal/index.ts delete mode 100644 src/widgets/add-legal-entity-modal/model/useAddLegalEntityForm.ts delete mode 100644 src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.module.css delete mode 100644 src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.tsx delete mode 100644 src/widgets/admin-login-form/index.ts delete mode 100644 src/widgets/admin-login-form/model/useAdminLoginForm.ts delete mode 100644 src/widgets/admin-login-form/ui/AdminLoginForm.module.css delete mode 100644 src/widgets/admin-login-form/ui/AdminLoginForm.tsx delete mode 100644 src/widgets/legal-entities-table/index.ts delete mode 100644 src/widgets/legal-entities-table/ui/LegalEntitiesTable.module.css delete mode 100644 src/widgets/legal-entities-table/ui/LegalEntitiesTable.tsx delete mode 100644 src/widgets/organization-documents/index.ts delete mode 100644 src/widgets/organization-documents/ui/OrganizationDocuments.module.css delete mode 100644 src/widgets/organization-documents/ui/OrganizationDocuments.tsx delete mode 100644 src/widgets/organization-purchase-requests/index.ts delete mode 100644 src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.module.css delete mode 100644 src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.tsx delete mode 100644 src/widgets/organization-wallets/index.ts delete mode 100644 src/widgets/organization-wallets/ui/OrganizationWallets.module.css delete mode 100644 src/widgets/organization-wallets/ui/OrganizationWallets.tsx diff --git a/src/app/providers/RouterProvider.tsx b/src/app/providers/RouterProvider.tsx index 7b004f7..a02f25f 100644 --- a/src/app/providers/RouterProvider.tsx +++ b/src/app/providers/RouterProvider.tsx @@ -18,8 +18,6 @@ import { PolitikaCookiePage } from '@pages/politika-cookie' import { SoglasiePage } from '@pages/soglasie-personalnyh-dannyh' import { ReestryPage } from '@pages/reestr-pd-rkn' import { TransactionsPage } from '@pages/transactions' -import { AdminPage } from '@pages/admin' -import { AdminOrganizationPage } from '@pages/admin-organization' import { WalletLayout } from '@widgets/wallet-layout' import { ROUTES } from '@shared/config/routes' import { ScrollToTop } from './ScrollToTop' @@ -40,10 +38,6 @@ export function RouterProvider() { } /> } /> - {/* Admin panel — own auth gate, independent of the user auth system */} - } /> - } /> - }> } /> } /> diff --git a/src/features/admin/api/adminApi.ts b/src/features/admin/api/adminApi.ts deleted file mode 100644 index 2c89c78..0000000 --- a/src/features/admin/api/adminApi.ts +++ /dev/null @@ -1,219 +0,0 @@ -import type { - AdminLoginRequest, - AdminLoginResponse, - AdminMeResponse, - AdminRefreshResponse, - CreateOrganizationRequest, - CreateWalletsResponse, - WalletResponse, - DocumentResponse, - DocumentTypeSlug, - Organization, - OrganizationListResponse, - PurchaseRequestListResponse, - UpdateOrganizationRequest, -} from '../model/types' - -const ADMIN_API_URL = 'https://app.admin.elcsa.ru' - -// In-memory admin access token — deliberately separate from the user `tokenStore` -// so the two independent auth systems never collide. No CSRF on the admin API. -let adminToken: string | null = null - -export const adminTokenStore = { - get: () => adminToken, - set: (token: string) => { adminToken = token }, - clear: () => { adminToken = null }, -} - -// The refresh token is body-based (sent in the `/v1/auth/refresh` request body), -// so it must persist across reloads to restore a session. localStorage-backed. -const ADMIN_REFRESH_KEY = 'admin_refresh_token' - -export const adminRefreshStore = { - get: (): string | null => { - try { return localStorage.getItem(ADMIN_REFRESH_KEY) } catch { return null } - }, - set: (token: string) => { - try { localStorage.setItem(ADMIN_REFRESH_KEY, token) } catch { /* ignore */ } - }, - clear: () => { - try { localStorage.removeItem(ADMIN_REFRESH_KEY) } catch { /* ignore */ } - }, -} - -async function doAdminRequest( - path: string, - options: RequestInit, - allowRetry: boolean, -): Promise { - const bearer = adminTokenStore.get() - // For multipart uploads we must NOT set Content-Type — the browser adds the - // boundary itself. Detect FormData bodies and skip the JSON header. - const isFormData = options.body instanceof FormData - - const res = await fetch(`${ADMIN_API_URL}${path}`, { - ...options, - credentials: 'include', - headers: { - ...(isFormData ? {} : { 'Content-Type': 'application/json' }), - ...(bearer ? { Authorization: `Bearer ${bearer}` } : {}), - ...options.headers, - }, - }) - - if (res.status === 401 && allowRetry) { - try { - await refreshAdminToken() - return doAdminRequest(path, options, false) - } catch { - adminTokenStore.clear() - throw new Error('Unauthorized') - } - } - - const data = await res.json().catch(() => null) - if (!res.ok) throw data - return data as T -} - -// Body-based refresh: the API expects the refresh token in the request body and -// returns a fresh access + refresh token pair (the refresh token rotates). -export async function refreshAdminToken(): Promise { - const refreshToken = adminRefreshStore.get() - if (!refreshToken) throw new Error('Unauthorized') - - const res = await fetch(`${ADMIN_API_URL}/v1/auth/refresh`, { - method: 'POST', - credentials: 'include', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ refresh_token: refreshToken }), - }) - if (!res.ok) { - adminRefreshStore.clear() - adminTokenStore.clear() - throw new Error('Unauthorized') - } - const data = (await res.json()) as AdminRefreshResponse - if (data.access_token) adminTokenStore.set(data.access_token) - if (data.refresh_token) adminRefreshStore.set(data.refresh_token) - return data.access_token -} - -export async function adminLogin(payload: AdminLoginRequest): Promise { - const data = await doAdminRequest( - '/v1/auth/login', - { method: 'POST', body: JSON.stringify(payload) }, - false, - ) - if (data.access_token) adminTokenStore.set(data.access_token) - if (data.refresh_token) adminRefreshStore.set(data.refresh_token) - return data -} - -export function getAdminMe(): Promise { - return doAdminRequest('/v1/auth/me', {}, true) -} - -export async function adminLogout(): Promise { - try { - await doAdminRequest('/v1/auth/logout', { method: 'POST' }, false) - } finally { - adminTokenStore.clear() - adminRefreshStore.clear() - } -} - -export function getOrganizations(limit = 50, offset = 0): Promise { - return doAdminRequest( - `/v1/organizations?limit=${limit}&offset=${offset}`, - {}, - true, - ) -} - -export function createOrganization(payload: CreateOrganizationRequest): Promise { - return doAdminRequest( - '/v1/organizations', - { method: 'POST', body: JSON.stringify(payload) }, - true, - ) -} - -export function getOrganization(id: string): Promise { - return doAdminRequest(`/v1/organizations/${id}`, {}, true) -} - -export function createOrganizationWallets(id: string): Promise { - return doAdminRequest( - `/v1/organizations/${id}/wallets/create`, - { method: 'POST', body: JSON.stringify({ id }) }, - true, - ) -} - -export function getOrganizationWallets(id: string): Promise { - return doAdminRequest( - `/v1/organizations/${id}/wallets`, - {}, - true, - ) -} - -export function getDocuments(orgId: string): Promise { - return doAdminRequest( - `/v1/organizations/${orgId}/documents`, - {}, - true, - ) -} - -// Upload/replace a single typed document. The type is part of the path and -// the body carries only the file — PUT acts as an upsert for that slot. -export function uploadDocument( - orgId: string, - type: DocumentTypeSlug, - file: File, -): Promise { - const body = new FormData() - body.append('file', file) - - return doAdminRequest( - `/v1/organizations/${orgId}/documents/${type}`, - { method: 'PUT', body }, - true, - ) -} - -export async function getPurchaseRequests(params: { - organizationId?: string - status?: string - limit?: number - offset?: number -}): Promise { - const query = new URLSearchParams() - if (params.organizationId) query.set('organization_id', params.organizationId) - if (params.status) query.set('status', params.status) - query.set('limit', String(params.limit ?? 50)) - query.set('offset', String(params.offset ?? 0)) - - const data = await doAdminRequest( - `/v1/purchase-requests?${query.toString()}`, - {}, - true, - ) - // TEMP: inspect real backend shape — especially which `status` values appear. - console.log('[purchase-requests] list response:', data) - return data -} - -export function updateOrganization( - id: string, - payload: UpdateOrganizationRequest, -): Promise { - return doAdminRequest( - `/v1/organizations/${id}`, - { method: 'PATCH', body: JSON.stringify(payload) }, - true, - ) -} diff --git a/src/features/admin/hooks/useAdminAuth.ts b/src/features/admin/hooks/useAdminAuth.ts deleted file mode 100644 index 254ad6d..0000000 --- a/src/features/admin/hooks/useAdminAuth.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getAdminMe } from '../api/adminApi' - -export const ADMIN_AUTH_QUERY_KEY = ['admin-auth'] - -export function useAdminAuth(): { isAuthenticated: boolean; isLoading: boolean } { - // `getAdminMe` is the real "is the session valid" check. It runs through - // `doAdminRequest` with retry, so a 401 transparently triggers a body-based - // refresh (using the persisted refresh token) and replays the request. - const { data, isLoading, isError } = useQuery({ - queryKey: ADMIN_AUTH_QUERY_KEY, - queryFn: getAdminMe, - retry: false, - staleTime: Infinity, - gcTime: Infinity, - refetchOnWindowFocus: false, - }) - return { isAuthenticated: !!data && !isError, isLoading } -} diff --git a/src/features/admin/hooks/useAdminLogin.ts b/src/features/admin/hooks/useAdminLogin.ts deleted file mode 100644 index 405bc8d..0000000 --- a/src/features/admin/hooks/useAdminLogin.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { adminLogin } from '../api/adminApi' -import { ADMIN_AUTH_QUERY_KEY } from './useAdminAuth' - -export function useAdminLogin() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: adminLogin, - onSuccess: (data) => { - // Tokens are already stored by adminLogin. Seed the gate's cache with the - // AdminMeResponse-shaped profile (login response carries all these fields) - // so we flip to "authenticated" without an extra /auth/me round-trip. - queryClient.setQueryData(ADMIN_AUTH_QUERY_KEY, { - id: data.id, - login: data.login, - first_name: data.first_name, - last_name: data.last_name, - role: data.role, - }) - }, - }) -} diff --git a/src/features/admin/hooks/useAdminLogout.ts b/src/features/admin/hooks/useAdminLogout.ts deleted file mode 100644 index 40372b7..0000000 --- a/src/features/admin/hooks/useAdminLogout.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { adminLogout } from '../api/adminApi' -import { ADMIN_AUTH_QUERY_KEY } from './useAdminAuth' - -export function useAdminLogout() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: adminLogout, - onSuccess: () => { - // Flip the gate back to "not authenticated" without triggering a /refresh refetch. - queryClient.setQueryData(ADMIN_AUTH_QUERY_KEY, null) - }, - }) -} diff --git a/src/features/admin/hooks/useCreateOrganization.ts b/src/features/admin/hooks/useCreateOrganization.ts deleted file mode 100644 index b35dedb..0000000 --- a/src/features/admin/hooks/useCreateOrganization.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { createOrganization } from '../api/adminApi' -import { ORGANIZATIONS_QUERY_KEY } from './useOrganizations' - -export function useCreateOrganization() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: createOrganization, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ORGANIZATIONS_QUERY_KEY }) - }, - }) -} diff --git a/src/features/admin/hooks/useCreateOrganizationWallets.ts b/src/features/admin/hooks/useCreateOrganizationWallets.ts deleted file mode 100644 index 8b339f7..0000000 --- a/src/features/admin/hooks/useCreateOrganizationWallets.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { createOrganizationWallets } from '../api/adminApi' -import { ORGANIZATIONS_QUERY_KEY } from './useOrganizations' -import { ORGANIZATION_QUERY_KEY } from './useOrganization' -import { WALLETS_QUERY_KEY } from './useOrganizationWallets' - -export function useCreateOrganizationWallets() { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (organizationId: string) => createOrganizationWallets(organizationId), - onSuccess: (_result, organizationId) => { - // `has_wallets` flips to true server-side — refresh list and detail view. - queryClient.invalidateQueries({ queryKey: ORGANIZATIONS_QUERY_KEY }) - queryClient.invalidateQueries({ queryKey: ORGANIZATION_QUERY_KEY(organizationId) }) - queryClient.invalidateQueries({ queryKey: WALLETS_QUERY_KEY(organizationId) }) - }, - }) -} diff --git a/src/features/admin/hooks/useDocuments.ts b/src/features/admin/hooks/useDocuments.ts deleted file mode 100644 index 6e76234..0000000 --- a/src/features/admin/hooks/useDocuments.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getDocuments } from '../api/adminApi' - -export const DOCUMENTS_QUERY_KEY = (orgId: string) => ['admin-documents', orgId] - -export function useDocuments(orgId: string | undefined) { - return useQuery({ - queryKey: DOCUMENTS_QUERY_KEY(orgId ?? ''), - queryFn: () => getDocuments(orgId as string), - enabled: !!orgId, - }) -} diff --git a/src/features/admin/hooks/useOrganization.ts b/src/features/admin/hooks/useOrganization.ts deleted file mode 100644 index 9bed771..0000000 --- a/src/features/admin/hooks/useOrganization.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getOrganization } from '../api/adminApi' - -export const ORGANIZATION_QUERY_KEY = (id: string) => ['admin-organization', id] - -export function useOrganization(id: string | undefined) { - return useQuery({ - queryKey: ORGANIZATION_QUERY_KEY(id ?? ''), - queryFn: () => getOrganization(id as string), - enabled: !!id, - }) -} diff --git a/src/features/admin/hooks/useOrganizationWallets.ts b/src/features/admin/hooks/useOrganizationWallets.ts deleted file mode 100644 index f5753a9..0000000 --- a/src/features/admin/hooks/useOrganizationWallets.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getOrganizationWallets } from '../api/adminApi' - -export const WALLETS_QUERY_KEY = (orgId: string) => ['admin-wallets', orgId] - -export function useOrganizationWallets(orgId: string | undefined) { - return useQuery({ - queryKey: WALLETS_QUERY_KEY(orgId ?? ''), - queryFn: () => getOrganizationWallets(orgId as string), - enabled: !!orgId, - }) -} diff --git a/src/features/admin/hooks/useOrganizations.ts b/src/features/admin/hooks/useOrganizations.ts deleted file mode 100644 index f8584c6..0000000 --- a/src/features/admin/hooks/useOrganizations.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getOrganizations } from '../api/adminApi' - -export const ORGANIZATIONS_QUERY_KEY = ['admin-organizations'] - -export function useOrganizations() { - return useQuery({ - queryKey: ORGANIZATIONS_QUERY_KEY, - queryFn: () => getOrganizations(), - }) -} diff --git a/src/features/admin/hooks/usePurchaseRequests.ts b/src/features/admin/hooks/usePurchaseRequests.ts deleted file mode 100644 index a76da36..0000000 --- a/src/features/admin/hooks/usePurchaseRequests.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { getPurchaseRequests } from '../api/adminApi' - -export const PURCHASE_REQUESTS_QUERY_KEY = (orgId: string) => [ - 'admin-purchase-requests', - orgId, -] - -export function usePurchaseRequests(orgId: string | undefined) { - return useQuery({ - queryKey: PURCHASE_REQUESTS_QUERY_KEY(orgId ?? ''), - queryFn: () => getPurchaseRequests({ organizationId: orgId }), - enabled: !!orgId, - }) -} diff --git a/src/features/admin/hooks/useUpdateOrganization.ts b/src/features/admin/hooks/useUpdateOrganization.ts deleted file mode 100644 index 2181547..0000000 --- a/src/features/admin/hooks/useUpdateOrganization.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { updateOrganization } from '../api/adminApi' -import type { UpdateOrganizationRequest } from '../model/types' -import { ORGANIZATIONS_QUERY_KEY } from './useOrganizations' -import { ORGANIZATION_QUERY_KEY } from './useOrganization' - -export function useUpdateOrganization(id: string) { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: (payload: UpdateOrganizationRequest) => updateOrganization(id, payload), - onSuccess: (data) => { - queryClient.setQueryData(ORGANIZATION_QUERY_KEY(id), data) - queryClient.invalidateQueries({ queryKey: ORGANIZATIONS_QUERY_KEY }) - }, - }) -} diff --git a/src/features/admin/hooks/useUploadDocument.ts b/src/features/admin/hooks/useUploadDocument.ts deleted file mode 100644 index 1be5437..0000000 --- a/src/features/admin/hooks/useUploadDocument.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query' -import { uploadDocument } from '../api/adminApi' -import type { DocumentTypeSlug } from '../model/types' -import { DOCUMENTS_QUERY_KEY } from './useDocuments' - -interface UploadArgs { - type: DocumentTypeSlug - file: File -} - -export function useUploadDocument(orgId: string) { - const queryClient = useQueryClient() - return useMutation({ - mutationFn: ({ type, file }: UploadArgs) => - uploadDocument(orgId, type, file), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: DOCUMENTS_QUERY_KEY(orgId) }) - }, - }) -} diff --git a/src/features/admin/index.ts b/src/features/admin/index.ts deleted file mode 100644 index 603c344..0000000 --- a/src/features/admin/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -export { - adminLogin, - adminLogout, - getAdminMe, - getOrganizations, - getOrganization, - createOrganization, - createOrganizationWallets, - getOrganizationWallets, - updateOrganization, - getDocuments, - uploadDocument, - getPurchaseRequests, - refreshAdminToken, - adminTokenStore, -} from './api/adminApi' -export type { - AdminLoginRequest, - AdminLoginResponse, - AdminMeResponse, - Organization, - OrganizationListResponse, - CreateOrganizationRequest, - UpdateOrganizationRequest, - WalletResponse, - DocumentResponse, - DocumentTypeSlug, - PurchaseRequestResponse, - PurchaseRequestListResponse, - BankDetails, -} from './model/types' -export { useAdminAuth, ADMIN_AUTH_QUERY_KEY } from './hooks/useAdminAuth' -export { useAdminLogin } from './hooks/useAdminLogin' -export { useAdminLogout } from './hooks/useAdminLogout' -export { useOrganizations, ORGANIZATIONS_QUERY_KEY } from './hooks/useOrganizations' -export { useOrganization, ORGANIZATION_QUERY_KEY } from './hooks/useOrganization' -export { useCreateOrganization } from './hooks/useCreateOrganization' -export { useCreateOrganizationWallets } from './hooks/useCreateOrganizationWallets' -export { useUpdateOrganization } from './hooks/useUpdateOrganization' -export { useOrganizationWallets, WALLETS_QUERY_KEY } from './hooks/useOrganizationWallets' -export { useDocuments, DOCUMENTS_QUERY_KEY } from './hooks/useDocuments' -export { useUploadDocument } from './hooks/useUploadDocument' -export { usePurchaseRequests, PURCHASE_REQUESTS_QUERY_KEY } from './hooks/usePurchaseRequests' diff --git a/src/features/admin/model/types.ts b/src/features/admin/model/types.ts deleted file mode 100644 index 0c8be2c..0000000 --- a/src/features/admin/model/types.ts +++ /dev/null @@ -1,151 +0,0 @@ -export interface AdminLoginRequest { - login: string - password: string -} - -export interface AdminLoginResponse { - access_token: string - refresh_token: string - token_type: string - id: string - login: string - first_name: string | null - last_name: string | null - role: string -} - -export interface AdminRefreshResponse { - access_token: string - refresh_token: string - token_type?: string -} - -export interface AdminMeResponse { - id: string - login: string - first_name: string | null - last_name: string | null - role: string -} - -export type BankDetails = Record - -export interface Organization { - id: string - user_id: string - name: string - short_name: string | null - inn: string - ogrn: string | null - kpp: string | null - legal_address: string | null - actual_address: string | null - bank_details: BankDetails | null - contact_person: string | null - contact_phone: string | null - status: string - kyc_verified: boolean - kyc_verified_at: string | null - has_wallets: boolean - created_by: string | null - created_at: string | null - updated_at: string | null -} - -export interface OrganizationListResponse { - items: Organization[] - total: number -} - -export interface WalletResponse { - id: string - chain: string - address: string - derivation_path: string - created_at: string | null -} - -export interface CreateWalletsRequest { - id: string -} - -export interface CreateWalletsResponse { - wallets: WalletResponse[] - mnemonic: string -} - -// Fixed document type slugs — match the path segments of the per-type -// PUT/GET endpoints (`/documents/{slug}`). The API no longer accepts a -// free-text document type. -export type DocumentTypeSlug = - | 'charter' - | 'inn-certificate' - | 'ogrn-certificate' - | 'bank-details' - | 'kyc-representative' - | 'power-of-attorney' - | 'other' - -export interface DocumentResponse { - organization_id: string - document_type: string - s3_key: string | null - file_name: string | null - content_type: string | null - file_size_bytes: number | null - download_url: string | null -} - -// Monetary fields are strings to preserve decimal precision — do not coerce to number. -export interface PurchaseRequestResponse { - id: string - organization_id: string - status: string - usdt_amount: string - rub_amount: string | null - exchange_rate: string | null - service_fee_percent: string | null - comment: string | null - admin_comment: string | null - target_wallet_chain: string | null - target_wallet_address: string | null - tx_hash: string | null - assigned_to: string | null - created_at: string | null - updated_at: string | null - completed_at: string | null -} - -export interface PurchaseRequestListResponse { - items: PurchaseRequestResponse[] - total: number -} - -export interface UpdateOrganizationRequest { - name?: string | null - short_name?: string | null - ogrn?: string | null - kpp?: string | null - legal_address?: string | null - actual_address?: string | null - bank_details?: BankDetails | null - contact_person?: string | null - contact_phone?: string | null - status?: string | null -} - -export interface CreateOrganizationRequest { - email: string - password: string - name: string - inn: string - short_name?: string | null - ogrn?: string | null - kpp?: string | null - legal_address?: string | null - actual_address?: string | null - bank_details?: BankDetails | null - contact_person?: string | null - contact_phone?: string | null - status?: string -} diff --git a/src/pages/admin-organization/index.ts b/src/pages/admin-organization/index.ts deleted file mode 100644 index 5a455a3..0000000 --- a/src/pages/admin-organization/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AdminOrganizationPage } from './ui/AdminOrganizationPage' diff --git a/src/pages/admin-organization/model/useOrganizationForm.ts b/src/pages/admin-organization/model/useOrganizationForm.ts deleted file mode 100644 index a484aa1..0000000 --- a/src/pages/admin-organization/model/useOrganizationForm.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { useEffect, useState } from 'react' -import { useUpdateOrganization } from '@features/admin' -import type { Organization, UpdateOrganizationRequest } from '@features/admin' - -interface FormState { - name: string - short_name: string - ogrn: string - kpp: string - legal_address: string - actual_address: string - contact_person: string - contact_phone: string - status: string -} - -function toForm(org: Organization): FormState { - return { - name: org.name ?? '', - short_name: org.short_name ?? '', - ogrn: org.ogrn ?? '', - kpp: org.kpp ?? '', - legal_address: org.legal_address ?? '', - actual_address: org.actual_address ?? '', - contact_person: org.contact_person ?? '', - contact_phone: org.contact_phone ?? '', - status: org.status ?? '', - } -} - -function extractErrorMessage(error: unknown): string { - const e = error as { detail?: unknown } - if (typeof e?.detail === 'string') return e.detail - if (Array.isArray(e?.detail) && (e.detail[0] as { msg?: string })?.msg) { - return (e.detail[0] as { msg: string }).msg - } - return 'Не удалось сохранить изменения' -} - -export function useOrganizationForm( - org: Organization | undefined, - id: string, - onSaved?: () => void, -) { - const [form, setForm] = useState(() => - org ? toForm(org) : { - name: '', short_name: '', ogrn: '', kpp: '', legal_address: '', - actual_address: '', contact_person: '', contact_phone: '', status: '', - }, - ) - const mutation = useUpdateOrganization(id) - - // Sync local form state once the organization loads / changes. - useEffect(() => { - if (org) setForm(toForm(org)) - }, [org]) - - const setField = (key: keyof FormState) => (value: string) => - setForm((prev) => ({ ...prev, [key]: value })) - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - - const trimmedOrNull = (v: string) => (v.trim() ? v.trim() : null) - - const payload: UpdateOrganizationRequest = { - name: form.name.trim(), - short_name: trimmedOrNull(form.short_name), - ogrn: trimmedOrNull(form.ogrn), - kpp: trimmedOrNull(form.kpp), - legal_address: trimmedOrNull(form.legal_address), - actual_address: trimmedOrNull(form.actual_address), - contact_person: trimmedOrNull(form.contact_person), - contact_phone: trimmedOrNull(form.contact_phone), - status: trimmedOrNull(form.status), - } - - mutation.mutate(payload, { onSuccess: () => onSaved?.() }) - } - - const error = mutation.isError ? extractErrorMessage(mutation.error) : null - - return { - form, - setField, - handleSubmit, - isSaving: mutation.isPending, - error, - } -} diff --git a/src/pages/admin-organization/ui/AdminOrganizationPage.module.css b/src/pages/admin-organization/ui/AdminOrganizationPage.module.css deleted file mode 100644 index af102a9..0000000 --- a/src/pages/admin-organization/ui/AdminOrganizationPage.module.css +++ /dev/null @@ -1,154 +0,0 @@ -.page { - min-height: 100vh; - background: var(--bg-deep); - padding: 40px 48px; -} - -.header { - max-width: 900px; - margin: 0 auto 28px; -} - -.back { - background: none; - border: none; - color: var(--text-secondary, rgba(255, 255, 255, 0.6)); - font-size: 14px; - cursor: pointer; - padding: 0; - margin-bottom: 14px; - transition: color 0.2s; -} - -.back:hover { - color: var(--text-primary, #fff); -} - -.title { - font-size: clamp(24px, 3.5vw, 34px); - font-weight: 700; - color: var(--text-primary, #fff); - margin: 0; -} - -.form { - max-width: 900px; - margin: 0 auto; - display: flex; - flex-direction: column; - gap: 24px; -} - -.tabs { - max-width: 900px; - margin: 0 auto 24px; - display: flex; - gap: 8px; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); -} - -.tab { - background: none; - border: none; - border-bottom: 2px solid transparent; - color: var(--text-secondary, rgba(255, 255, 255, 0.6)); - font-size: 15px; - font-weight: 600; - padding: 12px 16px; - cursor: pointer; - transition: color 0.2s, border-color 0.2s; -} - -.tab:hover { - color: var(--text-primary, #fff); -} - -.tabActive { - color: var(--text-primary, #fff); - border-bottom-color: var(--interactive, #4a6dff); -} - -.tabPanel { - max-width: 900px; - margin: 0 auto; -} - -.section { - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 20px; - padding: 24px; -} - -.sectionTitle { - font-size: 14px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary, rgba(255, 255, 255, 0.5)); - font-weight: 600; - margin: 0 0 18px; -} - -.grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 18px; -} - -.bankLabel { - display: block; - font-size: 12px; - color: var(--text-secondary, rgba(255, 255, 255, 0.5)); - font-weight: 600; - margin-bottom: 8px; -} - -.textarea { - width: 100%; - background: var(--glass-bg, rgba(255, 255, 255, 0.06)); - border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.1)); - border-radius: 10px; - color: var(--text-primary, #fff); - font-family: var(--font-mono, monospace); - font-size: 13px; - padding: 12px 14px; - resize: vertical; - outline: none; - transition: border-color 0.2s, box-shadow 0.2s; -} - -.textarea:focus { - border-color: var(--interactive, #4a6dff); - box-shadow: 0 0 0 3px rgba(74, 109, 255, 0.15); -} - -.state { - max-width: 900px; - margin: 0 auto; - padding: 40px 16px; - text-align: center; - color: var(--text-secondary, rgba(255, 255, 255, 0.6)); -} - -.error { - color: #ff5a5a; - font-size: 13px; - margin: 0; - text-align: center; -} - -.actions { - max-width: 320px; - margin: 0 auto; - width: 100%; -} - -@media (max-width: 768px) { - .page { - padding: 28px 20px; - } - - .grid { - grid-template-columns: 1fr; - } -} diff --git a/src/pages/admin-organization/ui/AdminOrganizationPage.tsx b/src/pages/admin-organization/ui/AdminOrganizationPage.tsx deleted file mode 100644 index b5a2760..0000000 --- a/src/pages/admin-organization/ui/AdminOrganizationPage.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { useState } from 'react' -import { useNavigate, useParams } from 'react-router-dom' -import { useAdminAuth, useOrganization } from '@features/admin' -import { ROUTES } from '@shared/config/routes' -import { FormField, Notification, PrimaryButton } from '@shared/ui' -import { AdminLoginForm } from '@widgets/admin-login-form' -import { OrganizationDocuments } from '@widgets/organization-documents' -import { OrganizationPurchaseRequests } from '@widgets/organization-purchase-requests' -import { OrganizationWallets } from '@widgets/organization-wallets' -import { useOrganizationForm } from '../model/useOrganizationForm' -import styles from './AdminOrganizationPage.module.css' - -type Tab = 'info' | 'wallets' | 'documents' | 'requests' - -const TABS: { id: Tab; label: string }[] = [ - { id: 'info', label: 'Общая информация' }, - { id: 'wallets', label: 'Кошельки' }, - { id: 'documents', label: 'Документы' }, - { id: 'requests', label: 'Заявки' }, -] - -function formatDateTime(value: string | null): string { - if (!value) return '—' - const d = new Date(value) - if (Number.isNaN(d.getTime())) return '—' - return d.toLocaleString('ru-RU') -} - -export function AdminOrganizationPage() { - const { isAuthenticated, isLoading: isAuthLoading } = useAdminAuth() - const { organizationId } = useParams<{ organizationId: string }>() - const navigate = useNavigate() - const { data: org, isLoading, isError } = useOrganization(organizationId) - const [notice, setNotice] = useState(false) - const [activeTab, setActiveTab] = useState('info') - const { form, setField, handleSubmit, isSaving, error } = useOrganizationForm( - org, - organizationId ?? '', - () => setNotice(true), - ) - - if (isAuthLoading) return null - if (!isAuthenticated) return - - return ( -
-
- -

{org ? org.name : 'Юридическое лицо'}

-
- - {isLoading &&
Загрузка...
} - {isError &&
Не удалось загрузить организацию
} - - {org && ( -
- {TABS.map((tab) => ( - - ))} -
- )} - - {org && activeTab === 'info' && ( -
-
-

Реквизиты

-
- - - - - - -
-
- -
-

Адреса

-
- - -
-
- -
-

Контакты

-
- - -
-
- -
-

Системная информация

-
- - - - - - - -
-
- - {error &&

{error}

} - -
- -
-
- )} - - {org && activeTab === 'wallets' && ( -
- -
- )} - - {org && activeTab === 'documents' && ( -
- -
- )} - - {org && activeTab === 'requests' && ( -
- -
- )} - - {notice && ( - setNotice(false)} - /> - )} -
- ) -} diff --git a/src/pages/admin/index.ts b/src/pages/admin/index.ts deleted file mode 100644 index 7f56083..0000000 --- a/src/pages/admin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AdminPage } from './ui/AdminPage' diff --git a/src/pages/admin/ui/AdminPage.module.css b/src/pages/admin/ui/AdminPage.module.css deleted file mode 100644 index bcf01c8..0000000 --- a/src/pages/admin/ui/AdminPage.module.css +++ /dev/null @@ -1,84 +0,0 @@ -.page { - min-height: 100vh; - background: var(--bg-deep); - padding: 40px 48px; -} - -.header { - display: flex; - align-items: center; - justify-content: space-between; - max-width: 1200px; - margin: 0 auto 36px; -} - -.greeting { - font-size: clamp(28px, 4vw, 40px); - font-weight: 700; - color: var(--text-primary, #fff); - margin: 0; -} - -.logout { - background: rgba(255, 255, 255, 0.06); - border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.1)); - color: var(--text-secondary, rgba(255, 255, 255, 0.7)); - border-radius: 10px; - height: 40px; - padding: 0 18px; - font-size: 13px; - font-weight: 600; - cursor: pointer; - transition: background 0.2s, color 0.2s, border-color 0.2s; -} - -.logout:hover { - background: rgba(255, 90, 90, 0.12); - border-color: rgba(255, 90, 90, 0.3); - color: #ff5a5a; -} - -.content { - max-width: 1200px; - margin: 0 auto; -} - -.toolbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - margin-bottom: 20px; - flex-wrap: wrap; -} - -.sectionTitle { - font-size: 20px; - font-weight: 700; - color: var(--text-primary, #fff); - margin: 0; -} - -.addBtn { - background: linear-gradient(135deg, var(--grad-edge, #4a6dff), var(--grad-center, #6f4aff)); - border: none; - color: #fff; - border-radius: 12px; - height: 44px; - padding: 0 20px; - font-size: 14px; - font-weight: 700; - cursor: pointer; - transition: filter 0.2s, box-shadow 0.2s; -} - -.addBtn:hover { - filter: brightness(1.12); - box-shadow: 0 6px 20px rgba(74, 109, 255, 0.35); -} - -@media (max-width: 768px) { - .page { - padding: 28px 20px; - } -} diff --git a/src/pages/admin/ui/AdminPage.tsx b/src/pages/admin/ui/AdminPage.tsx deleted file mode 100644 index 74ca8dc..0000000 --- a/src/pages/admin/ui/AdminPage.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useState } from 'react' -import { useAdminAuth, useAdminLogout, useCreateOrganizationWallets } from '@features/admin' -import type { Organization } from '@features/admin' -import { Notification } from '@shared/ui' -import { AdminLoginForm } from '@widgets/admin-login-form' -import { LegalEntitiesTable } from '@widgets/legal-entities-table' -import { AddLegalEntityModal } from '@widgets/add-legal-entity-modal' -import styles from './AdminPage.module.css' - -type NotificationState = { message: string; status: 'success' | 'error' | 'warning' } - -export function AdminPage() { - const { isAuthenticated, isLoading } = useAdminAuth() - const logout = useAdminLogout() - const createWallets = useCreateOrganizationWallets() - const [modalOpen, setModalOpen] = useState(false) - const [notification, setNotification] = useState(null) - - // After a legal entity is created we immediately provision its wallets. - // The page stays mounted (unlike the modal), so these mutate callbacks fire reliably. - function handleCreated(organization: Organization) { - setNotification({ status: 'success', message: 'Юридическое лицо добавлено' }) - createWallets.mutate(organization.id, { - onSuccess: (result) => { - setNotification({ - status: 'success', - message: `Кошельки созданы (${result.wallets.length})`, - }) - }, - onError: () => { - setNotification({ - status: 'warning', - message: 'Юридическое лицо создано, но кошельки создать не удалось', - }) - }, - }) - } - - if (isLoading) return null - if (!isAuthenticated) return - - return ( -
-
-

Привет, Марк!

- -
- -
-
-

Юридические лица

- -
- - -
- - setModalOpen(false)} - onCreated={handleCreated} - /> - - {notification && ( - setNotification(null)} - /> - )} -
- ) -} diff --git a/src/shared/config/routes.ts b/src/shared/config/routes.ts index 7fba8cb..5a51a86 100644 --- a/src/shared/config/routes.ts +++ b/src/shared/config/routes.ts @@ -19,9 +19,4 @@ export const ROUTES = { SOGLASIE_PERSONALNYH_DANNYH: '/soglasie-personalnyh-dannyh', REESTR_PD_RKN: '/reestr-pd-rkn', TRANSACTIONS: '/transactions', - ADMIN: '/sys-c7f29a4e-d81b-4630-ops-console', - ADMIN_ORGANIZATION: '/sys-c7f29a4e-d81b-4630-ops-console/organizations/:organizationId', } as const - -export const adminOrganizationPath = (id: string) => - `/sys-c7f29a4e-d81b-4630-ops-console/organizations/${id}` diff --git a/src/widgets/add-legal-entity-modal/index.ts b/src/widgets/add-legal-entity-modal/index.ts deleted file mode 100644 index 0f639e1..0000000 --- a/src/widgets/add-legal-entity-modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AddLegalEntityModal } from './ui/AddLegalEntityModal' diff --git a/src/widgets/add-legal-entity-modal/model/useAddLegalEntityForm.ts b/src/widgets/add-legal-entity-modal/model/useAddLegalEntityForm.ts deleted file mode 100644 index 495a6b2..0000000 --- a/src/widgets/add-legal-entity-modal/model/useAddLegalEntityForm.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { useState } from 'react' -import { useCreateOrganization } from '@features/admin' -import type { CreateOrganizationRequest, Organization } from '@features/admin' - -const INITIAL = { - email: '', - password: '', - name: '', - inn: '', - short_name: '', - ogrn: '', - kpp: '', - legal_address: '', - actual_address: '', - contact_person: '', - contact_phone: '', - status: 'active', - bank_name: '', - bik: '', - account: '', - corr_account: '', -} - -type FormState = typeof INITIAL - -function extractErrorMessage(error: unknown): string { - const e = error as { detail?: unknown } - if (typeof e?.detail === 'string') return e.detail - if (Array.isArray(e?.detail) && (e.detail[0] as { msg?: string })?.msg) { - return (e.detail[0] as { msg: string }).msg - } - return 'Не удалось добавить юридическое лицо' -} - -export function useAddLegalEntityForm(onSuccess: (organization: Organization) => void) { - const [form, setForm] = useState(INITIAL) - const mutation = useCreateOrganization() - - const setField = (key: keyof FormState) => (value: string) => - setForm((prev) => ({ ...prev, [key]: value })) - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - - const trimmedOrNull = (v: string) => (v.trim() ? v.trim() : null) - - const bankEntries: Record = {} - if (form.bank_name.trim()) bankEntries.bank_name = form.bank_name.trim() - if (form.bik.trim()) bankEntries.bik = form.bik.trim() - if (form.account.trim()) bankEntries.account = form.account.trim() - if (form.corr_account.trim()) bankEntries.corr_account = form.corr_account.trim() - - const payload: CreateOrganizationRequest = { - email: form.email.trim(), - password: form.password, - name: form.name.trim(), - inn: form.inn.trim(), - short_name: trimmedOrNull(form.short_name), - ogrn: trimmedOrNull(form.ogrn), - kpp: trimmedOrNull(form.kpp), - legal_address: trimmedOrNull(form.legal_address), - actual_address: trimmedOrNull(form.actual_address), - contact_person: trimmedOrNull(form.contact_person), - contact_phone: trimmedOrNull(form.contact_phone), - bank_details: Object.keys(bankEntries).length ? bankEntries : null, - status: form.status.trim() || 'active', - } - - mutation.mutate(payload, { - onSuccess: (organization) => { - setForm(INITIAL) - onSuccess(organization) - }, - }) - } - - const error = mutation.isError ? extractErrorMessage(mutation.error) : null - - return { - form, - setField, - handleSubmit, - isLoading: mutation.isPending, - error, - } -} diff --git a/src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.module.css b/src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.module.css deleted file mode 100644 index 62d29e1..0000000 --- a/src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.module.css +++ /dev/null @@ -1,101 +0,0 @@ -@keyframes dialogIn { - from { opacity: 0; transform: scale(0.96); } - to { opacity: 1; transform: scale(1); } -} - -.overlay { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(4px); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - padding: 16px; -} - -.dialog { - background: var(--bg-mid, #151520); - border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.1)); - border-radius: 20px; - width: 100%; - max-width: 720px; - max-height: 90vh; - display: flex; - flex-direction: column; - animation: dialogIn 0.18s ease; -} - -.header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 20px 24px 0; - flex-shrink: 0; -} - -.title { - font-size: 17px; - font-weight: 700; - color: var(--text-primary, #fff); -} - -.closeBtn { - background: none; - border: none; - color: var(--text-secondary, rgba(255, 255, 255, 0.4)); - font-size: 16px; - cursor: pointer; - padding: 4px; - line-height: 1; - transition: color 0.15s; -} - -.closeBtn:hover { - color: var(--text-primary, #fff); -} - -.body { - padding: 16px 24px 24px; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 8px; -} - -.groupLabel { - font-size: 12px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary, rgba(255, 255, 255, 0.5)); - font-weight: 600; - margin: 16px 0 4px; -} - -.groupLabel:first-child { - margin-top: 4px; -} - -.grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; -} - -.error { - color: #ff5a5a; - font-size: 13px; - margin: 12px 0 0; - text-align: center; -} - -.actions { - margin-top: 20px; -} - -@media (max-width: 560px) { - .grid { - grid-template-columns: 1fr; - } -} diff --git a/src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.tsx b/src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.tsx deleted file mode 100644 index edc0e13..0000000 --- a/src/widgets/add-legal-entity-modal/ui/AddLegalEntityModal.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useEffect } from 'react' -import type { Organization } from '@features/admin' -import { FormField, PrimaryButton } from '@shared/ui' -import { useAddLegalEntityForm } from '../model/useAddLegalEntityForm' -import styles from './AddLegalEntityModal.module.css' - -interface Props { - open: boolean - onClose: () => void - onCreated: (organization: Organization) => void -} - -export function AddLegalEntityModal({ open, onClose, onCreated }: Props) { - const { form, setField, handleSubmit, isLoading, error } = useAddLegalEntityForm((organization) => { - onCreated(organization) - onClose() - }) - - useEffect(() => { - if (!open) return - function onKey(e: KeyboardEvent) { - if (e.key === 'Escape') onClose() - } - window.addEventListener('keydown', onKey) - return () => window.removeEventListener('keydown', onKey) - }, [open, onClose]) - - if (!open) return null - - function handleOverlay(e: React.MouseEvent) { - if (e.target === e.currentTarget) onClose() - } - - return ( -
-
-
- Добавить юридическое лицо - -
- -
-

Обязательные поля

-
- - - - -
- -

Дополнительные поля

-
- - - - - - - - -
- -

Банковские реквизиты

-
- - - - -
- - {error &&

{error}

} - -
- -
-
-
-
- ) -} diff --git a/src/widgets/admin-login-form/index.ts b/src/widgets/admin-login-form/index.ts deleted file mode 100644 index b417604..0000000 --- a/src/widgets/admin-login-form/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { AdminLoginForm } from './ui/AdminLoginForm' diff --git a/src/widgets/admin-login-form/model/useAdminLoginForm.ts b/src/widgets/admin-login-form/model/useAdminLoginForm.ts deleted file mode 100644 index 6f430ab..0000000 --- a/src/widgets/admin-login-form/model/useAdminLoginForm.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useState } from 'react' -import { useAdminLogin } from '@features/admin' - -function extractErrorMessage(error: unknown): string { - const e = error as { detail?: unknown } - if (typeof e?.detail === 'string') return e.detail - if (Array.isArray(e?.detail) && (e.detail[0] as { msg?: string })?.msg) { - return (e.detail[0] as { msg: string }).msg - } - return 'Неверный логин или пароль' -} - -export function useAdminLoginForm() { - const [login, setLogin] = useState('') - const [password, setPassword] = useState('') - const loginMutation = useAdminLogin() - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - if (!login || !password) return - loginMutation.mutate({ login, password }) - } - - const error = loginMutation.isError ? extractErrorMessage(loginMutation.error) : null - - return { - login, - setLogin, - password, - setPassword, - isLoading: loginMutation.isPending, - error, - handleSubmit, - } -} diff --git a/src/widgets/admin-login-form/ui/AdminLoginForm.module.css b/src/widgets/admin-login-form/ui/AdminLoginForm.module.css deleted file mode 100644 index 4d9c196..0000000 --- a/src/widgets/admin-login-form/ui/AdminLoginForm.module.css +++ /dev/null @@ -1,49 +0,0 @@ -.wrap { - min-height: 100vh; - display: flex; - align-items: center; - justify-content: center; - padding: 24px; - background: var(--bg-deep); -} - -.card { - background: var(--glass-bg, rgba(255, 255, 255, 0.06)); - border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.1)); - border-radius: 24px; - padding: 36px 32px; - width: 100%; - max-width: 420px; -} - -.title { - text-align: center; - font-size: 24px; - font-weight: 700; - color: var(--text-primary, #fff); - margin: 0 0 4px; -} - -.subtitle { - text-align: center; - font-size: 14px; - color: var(--text-secondary, rgba(255, 255, 255, 0.5)); - margin: 0 0 28px; -} - -.fields { - display: flex; - flex-direction: column; - gap: 18px; -} - -.error { - color: #ff5a5a; - font-size: 13px; - margin: 14px 0 0; - text-align: center; -} - -.submit { - margin-top: 26px; -} diff --git a/src/widgets/admin-login-form/ui/AdminLoginForm.tsx b/src/widgets/admin-login-form/ui/AdminLoginForm.tsx deleted file mode 100644 index 948342e..0000000 --- a/src/widgets/admin-login-form/ui/AdminLoginForm.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { FormField, PrimaryButton } from '@shared/ui' -import { useAdminLoginForm } from '../model/useAdminLoginForm' -import styles from './AdminLoginForm.module.css' - -export function AdminLoginForm() { - const { login, setLogin, password, setPassword, isLoading, error, handleSubmit } = useAdminLoginForm() - - return ( -
-
-

Панель администратора

-

Войдите, чтобы продолжить

- -
- - -
- - {error &&

{error}

} - -
- -
-
-
- ) -} diff --git a/src/widgets/legal-entities-table/index.ts b/src/widgets/legal-entities-table/index.ts deleted file mode 100644 index 828de42..0000000 --- a/src/widgets/legal-entities-table/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { LegalEntitiesTable } from './ui/LegalEntitiesTable' diff --git a/src/widgets/legal-entities-table/ui/LegalEntitiesTable.module.css b/src/widgets/legal-entities-table/ui/LegalEntitiesTable.module.css deleted file mode 100644 index 6c7284c..0000000 --- a/src/widgets/legal-entities-table/ui/LegalEntitiesTable.module.css +++ /dev/null @@ -1,93 +0,0 @@ -.tableWrap { - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 24px; - padding: 24px; - overflow-x: auto; -} - -.table { - width: 100%; - border-collapse: collapse; -} - -.table th { - text-align: left; - font-size: 12px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary); - font-weight: 500; - padding: 0 16px 18px; - white-space: nowrap; -} - -.table td { - padding: 16px; - border-top: 1px solid rgba(255, 255, 255, 0.06); - vertical-align: middle; - transition: background 0.2s ease; - font-size: 14px; - color: var(--text-primary); -} - -.table tr:hover td { - background: rgba(255, 255, 255, 0.04); -} - -.row { - cursor: pointer; -} - -.name { - font-weight: 600; - display: block; -} - -.subname { - display: block; - font-size: 12px; - color: var(--text-secondary); - margin-top: 2px; -} - -.mono { - font-family: var(--font-mono, monospace); - font-size: 13px; -} - -.status { - display: inline-block; - padding: 4px 10px; - border-radius: 999px; - font-size: 12px; - font-weight: 600; - background: rgba(74, 109, 255, 0.12); - color: #7c95ff; - white-space: nowrap; -} - -.kyc { - display: inline-block; - padding: 4px 10px; - border-radius: 999px; - font-size: 12px; - font-weight: 600; -} - -.kycOk { - background: rgba(0, 196, 140, 0.14); - color: #00c48c; -} - -.kycNo { - background: rgba(255, 90, 90, 0.14); - color: #ff5a5a; -} - -.state { - padding: 40px 16px; - text-align: center; - color: var(--text-secondary); - font-size: 14px; -} diff --git a/src/widgets/legal-entities-table/ui/LegalEntitiesTable.tsx b/src/widgets/legal-entities-table/ui/LegalEntitiesTable.tsx deleted file mode 100644 index f88e16f..0000000 --- a/src/widgets/legal-entities-table/ui/LegalEntitiesTable.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { useNavigate } from 'react-router-dom' -import { useOrganizations } from '@features/admin' -import { adminOrganizationPath } from '@shared/config/routes' -import styles from './LegalEntitiesTable.module.css' - -const STATUS_LABELS: Record = { - active: 'Активно', - blocked: 'Заблокировано', - inactive: 'Неактивно', -} - -function formatDate(value: string | null): string { - if (!value) return '—' - const d = new Date(value) - if (Number.isNaN(d.getTime())) return '—' - return d.toLocaleDateString('ru-RU') -} - -export function LegalEntitiesTable() { - const { data, isLoading, isError } = useOrganizations() - const navigate = useNavigate() - - if (isLoading) { - return
Загрузка...
- } - - if (isError) { - return ( -
-
Не удалось загрузить список юридических лиц
-
- ) - } - - if (!data || data.items.length === 0) { - return ( -
-
Юридические лица ещё не добавлены
-
- ) - } - - return ( -
- - - - - - - - - - - - - - - {data.items.map((org) => ( - navigate(adminOrganizationPath(org.id))} - > - - - - - - - - - - ))} - -
НазваниеИННКППКонтактное лицоТелефонСтатусKYCСоздано
- {org.name} - {org.short_name && {org.short_name}} - {org.inn}{org.kpp ?? '—'}{org.contact_person ?? '—'}{org.contact_phone ?? '—'} - {STATUS_LABELS[org.status] ?? org.status} - - - {org.kyc_verified ? 'Да' : 'Нет'} - - {formatDate(org.created_at)}
-
- ) -} diff --git a/src/widgets/organization-documents/index.ts b/src/widgets/organization-documents/index.ts deleted file mode 100644 index fd0ebcc..0000000 --- a/src/widgets/organization-documents/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OrganizationDocuments } from './ui/OrganizationDocuments' diff --git a/src/widgets/organization-documents/ui/OrganizationDocuments.module.css b/src/widgets/organization-documents/ui/OrganizationDocuments.module.css deleted file mode 100644 index 430ad97..0000000 --- a/src/widgets/organization-documents/ui/OrganizationDocuments.module.css +++ /dev/null @@ -1,124 +0,0 @@ -.section { - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 20px; - padding: 24px; -} - -.sectionTitle { - font-size: 14px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary, rgba(255, 255, 255, 0.5)); - font-weight: 600; - margin: 0 0 18px; -} - -.slots { - list-style: none; - margin: 0; - padding: 0; - display: flex; - flex-direction: column; -} - -.slot { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - flex-wrap: wrap; - padding: 16px 0; - border-top: 1px solid rgba(255, 255, 255, 0.06); -} - -.slot:first-child { - border-top: none; -} - -.slotInfo { - display: flex; - flex-direction: column; - gap: 4px; - min-width: 200px; -} - -.slotLabel { - font-size: 14px; - font-weight: 600; - color: var(--text-primary, #fff); -} - -.slotFile { - font-size: 13px; - color: var(--text-secondary, rgba(255, 255, 255, 0.7)); -} - -.slotActions { - display: flex; - align-items: center; - gap: 10px; -} - -.hiddenInput { - display: none; -} - -.uploadBtn { - background: var(--interactive, #4a6dff); - border: none; - border-radius: 10px; - color: #fff; - font-size: 14px; - font-weight: 600; - padding: 11px 20px; - cursor: pointer; - transition: opacity 0.2s; - white-space: nowrap; -} - -.uploadBtn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.mono { - font-family: var(--font-mono, monospace); - font-size: 13px; -} - -.downloadBtn { - display: inline-block; - background: rgba(74, 109, 255, 0.12); - border: 1px solid rgba(74, 109, 255, 0.3); - border-radius: 8px; - color: #7c95ff; - font-size: 13px; - font-weight: 600; - padding: 7px 14px; - cursor: pointer; - text-decoration: none; - transition: background 0.2s; - white-space: nowrap; -} - -.downloadBtn:hover { - background: rgba(74, 109, 255, 0.22); -} - -.muted { - color: var(--text-secondary, rgba(255, 255, 255, 0.4)); -} - -.state { - padding: 32px 16px; - text-align: center; - color: var(--text-secondary, rgba(255, 255, 255, 0.6)); - font-size: 14px; -} - -.error { - color: #ff5a5a; - font-size: 13px; - margin: 0 0 12px; -} diff --git a/src/widgets/organization-documents/ui/OrganizationDocuments.tsx b/src/widgets/organization-documents/ui/OrganizationDocuments.tsx deleted file mode 100644 index bee7800..0000000 --- a/src/widgets/organization-documents/ui/OrganizationDocuments.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { useRef, useState } from 'react' -import { useDocuments, useUploadDocument } from '@features/admin' -import type { DocumentResponse, DocumentTypeSlug } from '@features/admin' -import styles from './OrganizationDocuments.module.css' - -interface Props { - orgId: string -} - -const DOCUMENT_SLOTS: { type: DocumentTypeSlug; label: string }[] = [ - { type: 'charter', label: 'Устав' }, - { type: 'inn-certificate', label: 'Свидетельство ИНН' }, - { type: 'ogrn-certificate', label: 'Свидетельство ОГРН' }, - { type: 'bank-details', label: 'Банковские реквизиты' }, - { type: 'kyc-representative', label: 'Документ представителя (KYC)' }, - { type: 'power-of-attorney', label: 'Доверенность' }, - { type: 'other', label: 'Прочее' }, -] - -function formatSize(bytes: number | null): string { - if (bytes == null) return '—' - if (bytes < 1024) return `${bytes} Б` - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} КБ` - return `${(bytes / (1024 * 1024)).toFixed(1)} МБ` -} - -function extractErrorMessage(error: unknown): string { - const e = error as { detail?: unknown; message?: unknown } - if (typeof e?.detail === 'string') return e.detail - if (Array.isArray(e?.detail) && (e.detail[0] as { msg?: string })?.msg) { - return (e.detail[0] as { msg: string }).msg - } - if (typeof e?.message === 'string') return e.message - return 'Не удалось выполнить операцию' -} - -export function OrganizationDocuments({ orgId }: Props) { - const { data: documents, isLoading, isError } = useDocuments(orgId) - const upload = useUploadDocument(orgId) - - // Which slot's file picker is currently being uploaded — drives the per-row - // "Загрузка..." state and disables only the active row. - const [activeType, setActiveType] = useState(null) - - // Map list items onto fixed slots. `document_type` may come back in - // underscore form (e.g. `inn_certificate`) while our slugs use hyphens. - const byType = new Map() - for (const doc of documents ?? []) { - byType.set(doc.document_type.replace(/_/g, '-'), doc) - } - - function handleSelect(type: DocumentTypeSlug, input: HTMLInputElement) { - const file = input.files?.[0] - // Reset so picking the same file again still fires onChange. - input.value = '' - if (!file) return - setActiveType(type) - upload.mutate( - { type, file }, - { onSettled: () => setActiveType(null) }, - ) - } - - const uploadError = upload.isError ? extractErrorMessage(upload.error) : null - - return ( -
-

Документы

- - {uploadError &&

{uploadError}

} - - {isLoading &&
Загрузка...
} - {isError &&
Не удалось загрузить документы
} - - {!isLoading && !isError && ( -
    - {DOCUMENT_SLOTS.map(({ type, label }) => { - const doc = byType.get(type) - const isUploading = upload.isPending && activeType === type - return ( -
  • -
    - {label} - {doc ? ( - - {doc.file_name ?? '—'} - · {formatSize(doc.file_size_bytes)} - - ) : ( - Не загружен - )} -
    - -
    - {doc?.download_url && ( - - Скачать - - )} - handleSelect(type, input)} - /> -
    -
  • - ) - })} -
- )} -
- ) -} - -interface UploadButtonProps { - label: string - busy: boolean - disabled: boolean - onSelect: (input: HTMLInputElement) => void -} - -function UploadButton({ label, busy, disabled, onSelect }: UploadButtonProps) { - const inputRef = useRef(null) - return ( - <> - - onSelect(e.currentTarget)} - /> - - ) -} diff --git a/src/widgets/organization-purchase-requests/index.ts b/src/widgets/organization-purchase-requests/index.ts deleted file mode 100644 index 29b1895..0000000 --- a/src/widgets/organization-purchase-requests/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OrganizationPurchaseRequests } from './ui/OrganizationPurchaseRequests' diff --git a/src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.module.css b/src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.module.css deleted file mode 100644 index 67d19cd..0000000 --- a/src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.module.css +++ /dev/null @@ -1,50 +0,0 @@ -.tableWrap { - overflow-x: auto; -} - -.table { - width: 100%; - border-collapse: collapse; -} - -.table th { - text-align: left; - font-size: 12px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary); - font-weight: 500; - padding: 0 16px 14px; - white-space: nowrap; -} - -.table td { - padding: 14px 16px; - border-top: 1px solid rgba(255, 255, 255, 0.06); - vertical-align: middle; - font-size: 14px; - color: var(--text-primary); -} - -.mono { - font-family: var(--font-mono, monospace); - font-size: 13px; -} - -.status { - display: inline-block; - padding: 4px 10px; - border-radius: 999px; - font-size: 12px; - font-weight: 600; - background: rgba(74, 109, 255, 0.12); - color: #7c95ff; - white-space: nowrap; -} - -.state { - padding: 32px 16px; - text-align: center; - color: var(--text-secondary, rgba(255, 255, 255, 0.6)); - font-size: 14px; -} diff --git a/src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.tsx b/src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.tsx deleted file mode 100644 index 257a663..0000000 --- a/src/widgets/organization-purchase-requests/ui/OrganizationPurchaseRequests.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { usePurchaseRequests } from '@features/admin' -import styles from './OrganizationPurchaseRequests.module.css' - -interface Props { - orgId: string -} - -function formatAmount(value: string | null, suffix: string): string { - if (!value) return '—' - return `${value} ${suffix}` -} - -function formatDate(value: string | null): string { - if (!value) return '—' - const d = new Date(value) - if (Number.isNaN(d.getTime())) return '—' - return d.toLocaleString('ru-RU') -} - -export function OrganizationPurchaseRequests({ orgId }: Props) { - const { data, isLoading, isError } = usePurchaseRequests(orgId) - - if (isLoading) { - return
Загрузка...
- } - - if (isError) { - return
Не удалось загрузить заявки
- } - - if (!data || data.items.length === 0) { - return
Заявок пока нет
- } - - return ( -
- - - - - - - - - - - - {data.items.map((req) => ( - - - - - - - - ))} - -
USDTСумма ₽КурсСтатусСоздана
{formatAmount(req.usdt_amount, 'USDT')}{formatAmount(req.rub_amount, '₽')}{req.exchange_rate ?? '—'} - {req.status} - {formatDate(req.created_at)}
-
- ) -} diff --git a/src/widgets/organization-wallets/index.ts b/src/widgets/organization-wallets/index.ts deleted file mode 100644 index b431a2a..0000000 --- a/src/widgets/organization-wallets/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OrganizationWallets } from './ui/OrganizationWallets' diff --git a/src/widgets/organization-wallets/ui/OrganizationWallets.module.css b/src/widgets/organization-wallets/ui/OrganizationWallets.module.css deleted file mode 100644 index e7c63b5..0000000 --- a/src/widgets/organization-wallets/ui/OrganizationWallets.module.css +++ /dev/null @@ -1,52 +0,0 @@ -.section { - background: rgba(255, 255, 255, 0.04); - border: 1px solid rgba(255, 255, 255, 0.08); - border-radius: 20px; - padding: 24px; -} - -.sectionTitle { - font-size: 14px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary, rgba(255, 255, 255, 0.5)); - font-weight: 600; - margin: 0 0 18px; -} - -.table { - width: 100%; - border-collapse: collapse; -} - -.table th { - text-align: left; - font-size: 12px; - letter-spacing: 1.5px; - text-transform: uppercase; - color: var(--text-secondary); - font-weight: 500; - padding: 0 16px 14px; - white-space: nowrap; -} - -.table td { - padding: 14px 16px; - border-top: 1px solid rgba(255, 255, 255, 0.06); - vertical-align: middle; - font-size: 14px; - color: var(--text-primary); -} - -.mono { - font-family: var(--font-mono, monospace); - font-size: 13px; - word-break: break-all; -} - -.state { - padding: 32px 16px; - text-align: center; - color: var(--text-secondary, rgba(255, 255, 255, 0.6)); - font-size: 14px; -} diff --git a/src/widgets/organization-wallets/ui/OrganizationWallets.tsx b/src/widgets/organization-wallets/ui/OrganizationWallets.tsx deleted file mode 100644 index 0c3ded3..0000000 --- a/src/widgets/organization-wallets/ui/OrganizationWallets.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useOrganizationWallets } from '@features/admin' -import styles from './OrganizationWallets.module.css' - -interface Props { - orgId: string -} - -function formatDate(value: string | null): string { - if (!value) return '—' - const d = new Date(value) - if (Number.isNaN(d.getTime())) return '—' - return d.toLocaleString('ru-RU') -} - -export function OrganizationWallets({ orgId }: Props) { - const { data: wallets, isLoading, isError } = useOrganizationWallets(orgId) - - return ( -
-

Кошельки

- - {isLoading &&
Загрузка...
} - {isError &&
Не удалось загрузить кошельки
} - - {wallets && wallets.length === 0 && ( -
Кошельки ещё не созданы
- )} - - {wallets && wallets.length > 0 && ( - - - - - - - - - - - {wallets.map((wallet) => ( - - - - - - - ))} - -
СетьАдресDerivation pathСоздано
{wallet.chain}{wallet.address}{wallet.derivation_path}{formatDate(wallet.created_at)}
- )} -
- ) -} diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index 786d980..8227628 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/app/app.tsx","./src/app/providers/guestroute.tsx","./src/app/providers/protectedroute.tsx","./src/app/providers/queryprovider.tsx","./src/app/providers/routerprovider.tsx","./src/app/providers/scrolltotop.tsx","./src/app/providers/index.ts","./src/entities/commission/index.ts","./src/entities/commission/model/tiers.ts","./src/entities/commission/ui/commissiontable.tsx","./src/features/admin/index.ts","./src/features/admin/api/adminapi.ts","./src/features/admin/hooks/useadminauth.ts","./src/features/admin/hooks/useadminlogin.ts","./src/features/admin/hooks/useadminlogout.ts","./src/features/admin/hooks/usecreateorganization.ts","./src/features/admin/hooks/usecreateorganizationwallets.ts","./src/features/admin/hooks/usedocuments.ts","./src/features/admin/hooks/useorganization.ts","./src/features/admin/hooks/useorganizationwallets.ts","./src/features/admin/hooks/useorganizations.ts","./src/features/admin/hooks/usepurchaserequests.ts","./src/features/admin/hooks/useupdateorganization.ts","./src/features/admin/hooks/useuploaddocument.ts","./src/features/admin/model/types.ts","./src/features/auth/index.ts","./src/features/auth/api/profileapi.ts","./src/features/auth/api/registrationapi.ts","./src/features/auth/hooks/useauth.ts","./src/features/auth/hooks/useisauthenticated.ts","./src/features/auth/hooks/useme.ts","./src/features/auth/hooks/useupdatephone.ts","./src/features/auth/hooks/useuploadavatar.ts","./src/features/b2b/index.ts","./src/features/b2b/api/b2bapi.ts","./src/features/b2b/hooks/usecreatepurchaserequest.ts","./src/features/b2b/hooks/usemypurchaserequests.ts","./src/features/kyc/api/kycapi.ts","./src/features/payment/index.ts","./src/features/payment/api/paymentapi.ts","./src/features/payment/hooks/usecreateorder.ts","./src/features/payment/hooks/useorders.ts","./src/features/payment/hooks/usepaymentconfig.ts","./src/features/payment/hooks/usepaymentquote.ts","./src/features/payment/hooks/usepaymentquotebyrub.ts","./src/features/payment/model/usecurrencyconversion.ts","./src/features/wallet/index.ts","./src/features/wallet/api/walletapi.ts","./src/features/wallet/model/usewalletdata.ts","./src/pages/admin/index.ts","./src/pages/admin/ui/adminpage.tsx","./src/pages/admin-organization/index.ts","./src/pages/admin-organization/model/useorganizationform.ts","./src/pages/admin-organization/ui/adminorganizationpage.tsx","./src/pages/bridge/index.ts","./src/pages/bridge/ui/bridgepage.tsx","./src/pages/converter/index.ts","./src/pages/converter/ui/converterpage.tsx","./src/pages/converter/ui/legalconverterpage.tsx","./src/pages/converter-test/index.ts","./src/pages/converter-test/ui/convertertestpage.tsx","./src/pages/home/index.ts","./src/pages/home/ui/homepage.tsx","./src/pages/kyc/index.ts","./src/pages/kyc/ui/kycpage.tsx","./src/pages/login/index.ts","./src/pages/login/ui/loginpage.tsx","./src/pages/politika-cookie/index.ts","./src/pages/politika-cookie/ui/politikacookiepage.tsx","./src/pages/politika-personalnyh-dannyh/index.ts","./src/pages/politika-personalnyh-dannyh/ui/politikapage.tsx","./src/pages/profile/index.ts","./src/pages/profile/ui/individualfields.tsx","./src/pages/profile/ui/legalentityfields.tsx","./src/pages/profile/ui/profilepage.tsx","./src/pages/publichnaya-oferta/index.ts","./src/pages/publichnaya-oferta/ui/publichnayaofertapage.tsx","./src/pages/reestr-pd-rkn/index.ts","./src/pages/reestr-pd-rkn/ui/reestrypage.tsx","./src/pages/register/index.ts","./src/pages/register/ui/registerpage.tsx","./src/pages/register-test/index.ts","./src/pages/register-test/ui/registertestpage.tsx","./src/pages/restore-password/index.ts","./src/pages/restore-password/ui/restorepasswordpage.tsx","./src/pages/seed-phrase/index.ts","./src/pages/seed-phrase/ui/seedphrasepage.tsx","./src/pages/soglasie-personalnyh-dannyh/index.ts","./src/pages/soglasie-personalnyh-dannyh/ui/soglasiepage.tsx","./src/pages/swap/index.ts","./src/pages/swap/ui/swappage.tsx","./src/pages/transactions/index.ts","./src/pages/transactions/ui/transactionspage.tsx","./src/pages/wallet/index.ts","./src/pages/wallet/ui/walletpage.tsx","./src/shared/api/base.ts","./src/shared/api/csrf.ts","./src/shared/api/tokenstore.ts","./src/shared/api/types.ts","./src/shared/assets/coins/index.ts","./src/shared/config/constants.ts","./src/shared/config/env.ts","./src/shared/config/routes.ts","./src/shared/lib/hooks/usedebounce.ts","./src/shared/lib/hooks/uselocalstorage.ts","./src/shared/lib/utils/baseunits.ts","./src/shared/lib/utils/cn.ts","./src/shared/lib/utils/truncatedecimals.ts","./src/shared/types/index.ts","./src/shared/ui/index.ts","./src/shared/ui/button/button.tsx","./src/shared/ui/button/index.ts","./src/shared/ui/convertfield/convertfield.tsx","./src/shared/ui/convertfield/index.ts","./src/shared/ui/directionswapbutton/directionswapbutton.tsx","./src/shared/ui/directionswapbutton/index.ts","./src/shared/ui/formfield/formfield.tsx","./src/shared/ui/formfield/index.ts","./src/shared/ui/notification/notification.tsx","./src/shared/ui/notification/index.ts","./src/shared/ui/pill/pill.tsx","./src/shared/ui/pill/index.ts","./src/shared/ui/primarybutton/primarybutton.tsx","./src/shared/ui/primarybutton/index.ts","./src/shared/ui/select/select.tsx","./src/shared/ui/select/index.ts","./src/shared/ui/spinner/spinner.tsx","./src/shared/ui/spinner/index.ts","./src/shared/ui/title/title.tsx","./src/shared/ui/tokenicon/tokenicon.tsx","./src/shared/ui/tokenicon/index.ts","./src/widgets/about/index.ts","./src/widgets/about/ui/about.tsx","./src/widgets/add-legal-entity-modal/index.ts","./src/widgets/add-legal-entity-modal/model/useaddlegalentityform.ts","./src/widgets/add-legal-entity-modal/ui/addlegalentitymodal.tsx","./src/widgets/admin-login-form/index.ts","./src/widgets/admin-login-form/model/useadminloginform.ts","./src/widgets/admin-login-form/ui/adminloginform.tsx","./src/widgets/balance-card/index.ts","./src/widgets/balance-card/ui/balancecard.tsx","./src/widgets/bridge-form/index.ts","./src/widgets/bridge-form/ui/bridgeconfirmmodal.tsx","./src/widgets/bridge-form/ui/bridgeform.tsx","./src/widgets/bridge-form/ui/networkselect.tsx","./src/widgets/converter-page/index.ts","./src/widgets/converter-page/model/useconvertersection.ts","./src/widgets/converter-page/ui/agreementcheck.tsx","./src/widgets/converter-page/ui/convertersection.tsx","./src/widgets/currency-converter/index.ts","./src/widgets/currency-converter/ui/agreementcheckbox.tsx","./src/widgets/currency-converter/ui/converter.tsx","./src/widgets/footer/index.ts","./src/widgets/footer/ui/footer.tsx","./src/widgets/header/index.ts","./src/widgets/header/ui/header.tsx","./src/widgets/hero/index.ts","./src/widgets/hero/lib/usecountdown.ts","./src/widgets/hero/ui/conversionflow.tsx","./src/widgets/hero/ui/countdown.tsx","./src/widgets/hero/ui/exchangecard.tsx","./src/widgets/hero/ui/hero.tsx","./src/widgets/kyc-verification/index.ts","./src/widgets/kyc-verification/model/usekyc.ts","./src/widgets/kyc-verification/ui/kycmodal.tsx","./src/widgets/kyc-verification/ui/kycwidget.tsx","./src/widgets/legal-entities-table/index.ts","./src/widgets/legal-entities-table/ui/legalentitiestable.tsx","./src/widgets/login-form/index.ts","./src/widgets/login-form/model/useloginform.ts","./src/widgets/login-form/ui/loginform.tsx","./src/widgets/networks-table/index.ts","./src/widgets/networks-table/model/networks.ts","./src/widgets/networks-table/ui/networkstable.tsx","./src/widgets/organization-documents/index.ts","./src/widgets/organization-documents/ui/organizationdocuments.tsx","./src/widgets/organization-purchase-requests/index.ts","./src/widgets/organization-purchase-requests/ui/organizationpurchaserequests.tsx","./src/widgets/organization-wallets/index.ts","./src/widgets/organization-wallets/ui/organizationwallets.tsx","./src/widgets/profile/index.ts","./src/widgets/profile/ui/avatarcropmodal.tsx","./src/widgets/profile/ui/profileavatar.tsx","./src/widgets/profile/ui/profilesection.tsx","./src/widgets/profile/ui/getcroppedimg.ts","./src/widgets/purchase-requests-list/index.ts","./src/widgets/purchase-requests-list/ui/purchaserequestslist.tsx","./src/widgets/receive-modal/index.ts","./src/widgets/receive-modal/ui/receivemodal.tsx","./src/widgets/register-form/index.ts","./src/widgets/register-form/model/useregisterform.ts","./src/widgets/register-form/ui/individualform.tsx","./src/widgets/register-form/ui/legalregisterinfo.tsx","./src/widgets/register-form/ui/registerform.tsx","./src/widgets/restore-password-form/index.ts","./src/widgets/restore-password-form/ui/restorepasswordform.tsx","./src/widgets/seed-phrase/index.ts","./src/widgets/seed-phrase/model/useseedphrase.ts","./src/widgets/seed-phrase/ui/seedphrasewidget.tsx","./src/widgets/send-modal/index.ts","./src/widgets/send-modal/model/sendtypes.ts","./src/widgets/send-modal/ui/sendmodal.tsx","./src/widgets/swap-bridge-tabs/index.ts","./src/widgets/swap-bridge-tabs/ui/swapbridgetabs.tsx","./src/widgets/swap-form/index.ts","./src/widgets/swap-form/model/useswapform.ts","./src/widgets/swap-form/ui/raterow.tsx","./src/widgets/swap-form/ui/swapcard.tsx","./src/widgets/swap-form/ui/swapconfirmmodal.tsx","./src/widgets/swap-form/ui/swapdirectionbutton.tsx","./src/widgets/swap-form/ui/swapform.tsx","./src/widgets/swap-form/ui/swapinfopanel.tsx","./src/widgets/swap-form/ui/tokenselect.tsx","./src/widgets/swap-form/ui/trxconfirmmodal.tsx","./src/widgets/token-table/index.ts","./src/widgets/token-table/model/tokens.ts","./src/widgets/token-table/model/usechaintokenrows.ts","./src/widgets/token-table/ui/tokentable.tsx","./src/widgets/transactions-list/index.ts","./src/widgets/transactions-list/model/format.ts","./src/widgets/transactions-list/model/paymentstatuslabels.ts","./src/widgets/transactions-list/ui/copybutton.tsx","./src/widgets/transactions-list/ui/orderaccordion.tsx","./src/widgets/transactions-list/ui/statusbadge.tsx","./src/widgets/transactions-list/ui/transactionslist.tsx","./src/widgets/wallet-chain-tabs/index.ts","./src/widgets/wallet-chain-tabs/ui/walletchaintabs.tsx","./src/widgets/wallet-header/index.ts","./src/widgets/wallet-header/ui/walletheader.tsx","./src/widgets/wallet-layout/index.ts","./src/widgets/wallet-layout/ui/walletlayout.tsx"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/main.tsx","./src/vite-env.d.ts","./src/app/app.tsx","./src/app/providers/guestroute.tsx","./src/app/providers/protectedroute.tsx","./src/app/providers/queryprovider.tsx","./src/app/providers/routerprovider.tsx","./src/app/providers/scrolltotop.tsx","./src/app/providers/index.ts","./src/entities/commission/index.ts","./src/entities/commission/model/tiers.ts","./src/entities/commission/ui/commissiontable.tsx","./src/features/auth/index.ts","./src/features/auth/api/profileapi.ts","./src/features/auth/api/registrationapi.ts","./src/features/auth/hooks/useauth.ts","./src/features/auth/hooks/useisauthenticated.ts","./src/features/auth/hooks/useme.ts","./src/features/auth/hooks/useupdatephone.ts","./src/features/auth/hooks/useuploadavatar.ts","./src/features/b2b/index.ts","./src/features/b2b/api/b2bapi.ts","./src/features/b2b/hooks/usecreatepurchaserequest.ts","./src/features/b2b/hooks/usemypurchaserequests.ts","./src/features/kyc/api/kycapi.ts","./src/features/payment/index.ts","./src/features/payment/api/paymentapi.ts","./src/features/payment/hooks/usecreateorder.ts","./src/features/payment/hooks/useorders.ts","./src/features/payment/hooks/usepaymentconfig.ts","./src/features/payment/hooks/usepaymentquote.ts","./src/features/payment/hooks/usepaymentquotebyrub.ts","./src/features/payment/model/usecurrencyconversion.ts","./src/features/wallet/index.ts","./src/features/wallet/api/walletapi.ts","./src/features/wallet/model/usewalletdata.ts","./src/pages/bridge/index.ts","./src/pages/bridge/ui/bridgepage.tsx","./src/pages/converter/index.ts","./src/pages/converter/ui/converterpage.tsx","./src/pages/converter/ui/legalconverterpage.tsx","./src/pages/converter-test/index.ts","./src/pages/converter-test/ui/convertertestpage.tsx","./src/pages/home/index.ts","./src/pages/home/ui/homepage.tsx","./src/pages/kyc/index.ts","./src/pages/kyc/ui/kycpage.tsx","./src/pages/login/index.ts","./src/pages/login/ui/loginpage.tsx","./src/pages/politika-cookie/index.ts","./src/pages/politika-cookie/ui/politikacookiepage.tsx","./src/pages/politika-personalnyh-dannyh/index.ts","./src/pages/politika-personalnyh-dannyh/ui/politikapage.tsx","./src/pages/profile/index.ts","./src/pages/profile/ui/individualfields.tsx","./src/pages/profile/ui/legalentityfields.tsx","./src/pages/profile/ui/profilepage.tsx","./src/pages/publichnaya-oferta/index.ts","./src/pages/publichnaya-oferta/ui/publichnayaofertapage.tsx","./src/pages/reestr-pd-rkn/index.ts","./src/pages/reestr-pd-rkn/ui/reestrypage.tsx","./src/pages/register/index.ts","./src/pages/register/ui/registerpage.tsx","./src/pages/register-test/index.ts","./src/pages/register-test/ui/registertestpage.tsx","./src/pages/restore-password/index.ts","./src/pages/restore-password/ui/restorepasswordpage.tsx","./src/pages/seed-phrase/index.ts","./src/pages/seed-phrase/ui/seedphrasepage.tsx","./src/pages/soglasie-personalnyh-dannyh/index.ts","./src/pages/soglasie-personalnyh-dannyh/ui/soglasiepage.tsx","./src/pages/swap/index.ts","./src/pages/swap/ui/swappage.tsx","./src/pages/transactions/index.ts","./src/pages/transactions/ui/transactionspage.tsx","./src/pages/wallet/index.ts","./src/pages/wallet/ui/walletpage.tsx","./src/shared/api/base.ts","./src/shared/api/csrf.ts","./src/shared/api/tokenstore.ts","./src/shared/api/types.ts","./src/shared/assets/coins/index.ts","./src/shared/config/constants.ts","./src/shared/config/env.ts","./src/shared/config/routes.ts","./src/shared/lib/hooks/usedebounce.ts","./src/shared/lib/hooks/uselocalstorage.ts","./src/shared/lib/utils/baseunits.ts","./src/shared/lib/utils/cn.ts","./src/shared/lib/utils/truncatedecimals.ts","./src/shared/types/index.ts","./src/shared/ui/index.ts","./src/shared/ui/button/button.tsx","./src/shared/ui/button/index.ts","./src/shared/ui/convertfield/convertfield.tsx","./src/shared/ui/convertfield/index.ts","./src/shared/ui/directionswapbutton/directionswapbutton.tsx","./src/shared/ui/directionswapbutton/index.ts","./src/shared/ui/formfield/formfield.tsx","./src/shared/ui/formfield/index.ts","./src/shared/ui/notification/notification.tsx","./src/shared/ui/notification/index.ts","./src/shared/ui/pill/pill.tsx","./src/shared/ui/pill/index.ts","./src/shared/ui/primarybutton/primarybutton.tsx","./src/shared/ui/primarybutton/index.ts","./src/shared/ui/select/select.tsx","./src/shared/ui/select/index.ts","./src/shared/ui/spinner/spinner.tsx","./src/shared/ui/spinner/index.ts","./src/shared/ui/title/title.tsx","./src/shared/ui/tokenicon/tokenicon.tsx","./src/shared/ui/tokenicon/index.ts","./src/widgets/about/index.ts","./src/widgets/about/ui/about.tsx","./src/widgets/balance-card/index.ts","./src/widgets/balance-card/ui/balancecard.tsx","./src/widgets/bridge-form/index.ts","./src/widgets/bridge-form/ui/bridgeconfirmmodal.tsx","./src/widgets/bridge-form/ui/bridgeform.tsx","./src/widgets/bridge-form/ui/networkselect.tsx","./src/widgets/converter-page/index.ts","./src/widgets/converter-page/model/useconvertersection.ts","./src/widgets/converter-page/ui/agreementcheck.tsx","./src/widgets/converter-page/ui/convertersection.tsx","./src/widgets/currency-converter/index.ts","./src/widgets/currency-converter/ui/agreementcheckbox.tsx","./src/widgets/currency-converter/ui/converter.tsx","./src/widgets/footer/index.ts","./src/widgets/footer/ui/footer.tsx","./src/widgets/header/index.ts","./src/widgets/header/ui/header.tsx","./src/widgets/hero/index.ts","./src/widgets/hero/lib/usecountdown.ts","./src/widgets/hero/ui/conversionflow.tsx","./src/widgets/hero/ui/countdown.tsx","./src/widgets/hero/ui/exchangecard.tsx","./src/widgets/hero/ui/hero.tsx","./src/widgets/kyc-verification/index.ts","./src/widgets/kyc-verification/model/usekyc.ts","./src/widgets/kyc-verification/ui/kycmodal.tsx","./src/widgets/kyc-verification/ui/kycwidget.tsx","./src/widgets/login-form/index.ts","./src/widgets/login-form/model/useloginform.ts","./src/widgets/login-form/ui/loginform.tsx","./src/widgets/networks-table/index.ts","./src/widgets/networks-table/model/networks.ts","./src/widgets/networks-table/ui/networkstable.tsx","./src/widgets/profile/index.ts","./src/widgets/profile/ui/avatarcropmodal.tsx","./src/widgets/profile/ui/profileavatar.tsx","./src/widgets/profile/ui/profilesection.tsx","./src/widgets/profile/ui/getcroppedimg.ts","./src/widgets/purchase-requests-list/index.ts","./src/widgets/purchase-requests-list/ui/purchaserequestslist.tsx","./src/widgets/receive-modal/index.ts","./src/widgets/receive-modal/ui/receivemodal.tsx","./src/widgets/register-form/index.ts","./src/widgets/register-form/model/useregisterform.ts","./src/widgets/register-form/ui/individualform.tsx","./src/widgets/register-form/ui/legalregisterinfo.tsx","./src/widgets/register-form/ui/registerform.tsx","./src/widgets/restore-password-form/index.ts","./src/widgets/restore-password-form/ui/restorepasswordform.tsx","./src/widgets/seed-phrase/index.ts","./src/widgets/seed-phrase/model/useseedphrase.ts","./src/widgets/seed-phrase/ui/seedphrasewidget.tsx","./src/widgets/send-modal/index.ts","./src/widgets/send-modal/model/sendtypes.ts","./src/widgets/send-modal/ui/sendmodal.tsx","./src/widgets/swap-bridge-tabs/index.ts","./src/widgets/swap-bridge-tabs/ui/swapbridgetabs.tsx","./src/widgets/swap-form/index.ts","./src/widgets/swap-form/model/useswapform.ts","./src/widgets/swap-form/ui/raterow.tsx","./src/widgets/swap-form/ui/swapcard.tsx","./src/widgets/swap-form/ui/swapconfirmmodal.tsx","./src/widgets/swap-form/ui/swapdirectionbutton.tsx","./src/widgets/swap-form/ui/swapform.tsx","./src/widgets/swap-form/ui/swapinfopanel.tsx","./src/widgets/swap-form/ui/tokenselect.tsx","./src/widgets/swap-form/ui/trxconfirmmodal.tsx","./src/widgets/token-table/index.ts","./src/widgets/token-table/model/tokens.ts","./src/widgets/token-table/model/usechaintokenrows.ts","./src/widgets/token-table/ui/tokentable.tsx","./src/widgets/transactions-list/index.ts","./src/widgets/transactions-list/model/format.ts","./src/widgets/transactions-list/model/paymentstatuslabels.ts","./src/widgets/transactions-list/ui/copybutton.tsx","./src/widgets/transactions-list/ui/orderaccordion.tsx","./src/widgets/transactions-list/ui/statusbadge.tsx","./src/widgets/transactions-list/ui/transactionslist.tsx","./src/widgets/wallet-chain-tabs/index.ts","./src/widgets/wallet-chain-tabs/ui/walletchaintabs.tsx","./src/widgets/wallet-header/index.ts","./src/widgets/wallet-header/ui/walletheader.tsx","./src/widgets/wallet-layout/index.ts","./src/widgets/wallet-layout/ui/walletlayout.tsx"],"version":"5.6.3"} \ No newline at end of file