admin page
This commit is contained in:
@@ -3,15 +3,36 @@ import { tokenStore } from '@shared/api/tokenStore'
|
||||
|
||||
const USERS_API_URL = 'https://app.users.elcsa.ru'
|
||||
|
||||
export type AccountType = 'individual' | 'legal_entity'
|
||||
|
||||
// Nested organization payload — present only on legal_entity accounts.
|
||||
export interface LegalEntityInfo {
|
||||
id: string
|
||||
name: string
|
||||
inn: string
|
||||
status: string
|
||||
short_name: string | null
|
||||
ogrn: string | null
|
||||
kpp: string | null
|
||||
legal_address: string | null
|
||||
actual_address: string | null
|
||||
bank_details: Record<string, unknown> | null
|
||||
contact_person: string | null
|
||||
contact_phone: string | null
|
||||
kyc_verified: boolean
|
||||
kyc_verified_at: string | null
|
||||
}
|
||||
|
||||
export interface MeResponse {
|
||||
id: string
|
||||
email: string
|
||||
first_name: string
|
||||
middle_name: string
|
||||
last_name: string
|
||||
birth_date: string
|
||||
// Person fields are null on legal_entity accounts.
|
||||
first_name: string | null
|
||||
middle_name: string | null
|
||||
last_name: string | null
|
||||
birth_date: string | null
|
||||
encrypted_mnemonic: string | null
|
||||
phone: string
|
||||
phone: string | null
|
||||
passport_data: string | null
|
||||
inn: string | null
|
||||
erc20: string | null
|
||||
@@ -21,7 +42,11 @@ export interface MeResponse {
|
||||
created_at: string
|
||||
updated_at: string
|
||||
kyc_verified_at: string | null
|
||||
webp_size_bytes: number
|
||||
webp_size_bytes?: number
|
||||
// "individual" -> физлицо, "legal_entity" -> аккаунт юр.лица.
|
||||
account_type: AccountType
|
||||
// Populated only for legal_entity accounts.
|
||||
legal_entity?: LegalEntityInfo | null
|
||||
}
|
||||
|
||||
export interface UploadAvatarPayload {
|
||||
|
||||
34
src/pages/profile/ui/IndividualFields.tsx
Normal file
34
src/pages/profile/ui/IndividualFields.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { MeResponse } from '@features/auth'
|
||||
import { FormField } from '@shared/ui'
|
||||
import { ProfileSection } from '@widgets/profile'
|
||||
import styles from './ProfilePage.module.css'
|
||||
|
||||
interface Props {
|
||||
data: MeResponse
|
||||
fullName: string
|
||||
phone: string
|
||||
onPhoneChange: (value: string) => void
|
||||
onPhoneBlur: () => void
|
||||
}
|
||||
|
||||
export function IndividualFields({ data, fullName, phone, onPhoneChange, onPhoneBlur }: Props) {
|
||||
return (
|
||||
<>
|
||||
<ProfileSection title="Личные данные">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Полное ФИО" value={fullName} placeholder="Например: Иванов Иван Иванович" readOnly />
|
||||
<FormField label="Адрес электронной почты" value={data.email ?? ''} type="email" icon="check" placeholder="example@mail.ru" readOnly />
|
||||
<FormField label="Серия и номер паспорта" value={data.passport_data ?? ''} placeholder="0000 000000" readOnly />
|
||||
<FormField label="Номер телефона" value={phone} onChange={onPhoneChange} onBlur={onPhoneBlur} type="tel" placeholder="+7 (999) 000-00-00" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Верификация">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="ИНН" value={data.inn ?? ''} readOnly icon="lock" placeholder="000000000000" />
|
||||
<FormField label="ID аккаунта" value={data.id ?? ''} readOnly icon="lock" placeholder="ECSA-00000000" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
47
src/pages/profile/ui/LegalEntityFields.tsx
Normal file
47
src/pages/profile/ui/LegalEntityFields.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { MeResponse } from '@features/auth'
|
||||
import { FormField } from '@shared/ui'
|
||||
import { ProfileSection } from '@widgets/profile'
|
||||
import styles from './ProfilePage.module.css'
|
||||
|
||||
interface Props {
|
||||
data: MeResponse
|
||||
}
|
||||
|
||||
// Legal-account fields. Organization data lives in the nested `legal_entity`
|
||||
// object; person-level fields are null on these accounts.
|
||||
// All read-only — organization data is managed admin-side, not by the user.
|
||||
export function LegalEntityFields({ data }: Props) {
|
||||
const le = data.legal_entity
|
||||
if (!le) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProfileSection title="Данные организации">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Наименование" value={le.name ?? ''} placeholder="ООО «Ромашка»" readOnly />
|
||||
<FormField label="Краткое наименование" value={le.short_name ?? ''} placeholder="Ромашка" readOnly />
|
||||
<FormField label="ИНН" value={le.inn ?? ''} readOnly icon="lock" placeholder="000000000000" />
|
||||
<FormField label="ОГРН" value={le.ogrn ?? ''} placeholder="1027700132195" readOnly />
|
||||
<FormField label="КПП" value={le.kpp ?? ''} placeholder="770801001" readOnly />
|
||||
<FormField label="Адрес электронной почты" value={data.email ?? ''} type="email" icon="check" placeholder="org@mail.ru" readOnly />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Адреса">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Юридический адрес" value={le.legal_address ?? ''} placeholder="г. Москва, ул. Тверская, д. 1" readOnly />
|
||||
<FormField label="Фактический адрес" value={le.actual_address ?? ''} placeholder="г. Москва, ул. Тверская, д. 1" readOnly />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Контакты и верификация">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Контактное лицо" value={le.contact_person ?? ''} placeholder="Иванов Иван Иванович" readOnly />
|
||||
<FormField label="Контактный телефон" value={le.contact_phone ?? ''} type="tel" placeholder="+7 (999) 000-00-00" readOnly />
|
||||
<FormField label="Статус" value={le.status ?? ''} placeholder="active" readOnly />
|
||||
<FormField label="ID аккаунта" value={data.id ?? ''} readOnly icon="lock" placeholder="ECSA-00000000" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import { ROUTES } from '@shared/config/routes'
|
||||
import { Button, FormField, Notification } from '@shared/ui'
|
||||
import { WalletHeader } from '@widgets/wallet-header'
|
||||
import { ProfileAvatar, ProfileSection } from '@widgets/profile'
|
||||
import { IndividualFields } from './IndividualFields'
|
||||
import { LegalEntityFields } from './LegalEntityFields'
|
||||
import styles from './ProfilePage.module.css'
|
||||
|
||||
export function ProfilePage() {
|
||||
@@ -44,11 +46,14 @@ export function ProfilePage() {
|
||||
})
|
||||
}
|
||||
|
||||
const capitalize = (s: string) => (s ? s[0].toUpperCase() + s.slice(1).toLowerCase() : '')
|
||||
const capitalize = (s: string | null) => (s ? s[0].toUpperCase() + s.slice(1).toLowerCase() : '')
|
||||
const fullName = data
|
||||
? [data.last_name, data.first_name, data.middle_name].filter(Boolean).map(capitalize).join(' ')
|
||||
: ''
|
||||
|
||||
const isLegal = !!data && data.account_type !== 'individual'
|
||||
const displayName = isLegal ? (data?.legal_entity?.name ?? '') : fullName
|
||||
|
||||
const userBalance =
|
||||
isPortfolioLoading || !portfolio || portfolio.totalUsd == null
|
||||
? '$—'
|
||||
@@ -66,28 +71,24 @@ export function ProfilePage() {
|
||||
<div className={styles.profileTop}>
|
||||
<ProfileAvatar />
|
||||
<div className={styles.userInfo}>
|
||||
<span className={styles.userName}>{fullName}</span>
|
||||
<span className={styles.userName}>{displayName}</span>
|
||||
<span className={styles.userBalance}>{userBalance}</span>
|
||||
{/* <span className={styles.userBalanceRub}>≈ 22 340,50 ₽</span> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.sections}>
|
||||
<ProfileSection title="Личные данные">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Полное ФИО" value={fullName} placeholder="Например: Иванов Иван Иванович" readOnly />
|
||||
<FormField label="Адрес электронной почты" value={data?.email ?? ''} type="email" icon="check" placeholder="example@mail.ru" readOnly />
|
||||
<FormField label="Серия и номер паспорта" value={data?.passport_data ?? ''} placeholder="0000 000000" readOnly />
|
||||
<FormField label="Номер телефона" value={phone} onChange={handlePhoneChange} onBlur={handlePhoneBlur} type="tel" placeholder="+7 (999) 000-00-00" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Верификация">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="ИНН" value={data?.inn ?? ''} readOnly icon="lock" placeholder="000000000000" />
|
||||
<FormField label="ID аккаунта" value={data?.id ?? ''} readOnly icon="lock" placeholder="ECSA-00000000" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
{data && (isLegal ? (
|
||||
<LegalEntityFields data={data} />
|
||||
) : (
|
||||
<IndividualFields
|
||||
data={data}
|
||||
fullName={fullName}
|
||||
phone={phone}
|
||||
onPhoneChange={handlePhoneChange}
|
||||
onPhoneBlur={handlePhoneBlur}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ProfileSection
|
||||
title="Безопасность"
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Notification } from '@shared/ui'
|
||||
|
||||
const TICKER_SYMBOLS = ['BTC', 'ETH', 'SOL']
|
||||
|
||||
const capitalize = (s: string) => (s ? s[0].toUpperCase() + s.slice(1).toLowerCase() : '')
|
||||
const capitalize = (s: string | null) => (s ? s[0].toUpperCase() + s.slice(1).toLowerCase() : '')
|
||||
|
||||
function formatPrice(value: number | null | undefined): string {
|
||||
if (value == null) return '$—'
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user