431 lines
16 KiB
TypeScript
431 lines
16 KiB
TypeScript
"use client"
|
||
|
||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||
import { Card } from '@/components/ui/card'
|
||
import { Badge } from '@/components/ui/badge'
|
||
import { Separator } from '@/components/ui/separator'
|
||
import {
|
||
Building2,
|
||
Phone,
|
||
Mail,
|
||
MapPin,
|
||
Calendar,
|
||
FileText,
|
||
Users,
|
||
CreditCard,
|
||
Hash,
|
||
User,
|
||
Briefcase
|
||
} from 'lucide-react'
|
||
import { OrganizationAvatar } from './organization-avatar'
|
||
|
||
interface User {
|
||
id: string
|
||
avatar?: string | null
|
||
phone: string
|
||
createdAt: string
|
||
}
|
||
|
||
interface ApiKey {
|
||
id: string
|
||
marketplace: string
|
||
isActive: boolean
|
||
createdAt: string
|
||
}
|
||
|
||
interface Organization {
|
||
id: string
|
||
inn: string
|
||
kpp?: string | null
|
||
name?: string | null
|
||
fullName?: string | null
|
||
type: 'FULFILLMENT' | 'SELLER' | 'LOGIST' | 'WHOLESALE'
|
||
address?: string | null
|
||
addressFull?: string | null
|
||
ogrn?: string | null
|
||
ogrnDate?: string | null
|
||
status?: string | null
|
||
actualityDate?: string | null
|
||
registrationDate?: string | null
|
||
liquidationDate?: string | null
|
||
managementName?: string | null
|
||
managementPost?: string | null
|
||
opfCode?: string | null
|
||
opfFull?: string | null
|
||
opfShort?: string | null
|
||
okato?: string | null
|
||
oktmo?: string | null
|
||
okpo?: string | null
|
||
okved?: string | null
|
||
employeeCount?: number | null
|
||
revenue?: string | null
|
||
taxSystem?: string | null
|
||
phones?: Array<{ value: string }> | null
|
||
emails?: Array<{ value: string }> | null
|
||
users?: User[]
|
||
apiKeys?: ApiKey[]
|
||
createdAt: string
|
||
}
|
||
|
||
interface OrganizationDetailsModalProps {
|
||
organization: Organization | null
|
||
open: boolean
|
||
onOpenChange: (open: boolean) => void
|
||
}
|
||
|
||
function formatDate(dateString?: string | null): string {
|
||
if (!dateString) return 'Не указана'
|
||
|
||
try {
|
||
let date: Date
|
||
|
||
// Проверяем, является ли строка числом (Unix timestamp)
|
||
if (/^\d+$/.test(dateString)) {
|
||
// Если это Unix timestamp в миллисекундах
|
||
const timestamp = parseInt(dateString, 10)
|
||
date = new Date(timestamp)
|
||
} else {
|
||
// Обычная строка даты
|
||
date = new Date(dateString)
|
||
}
|
||
|
||
if (isNaN(date.getTime())) {
|
||
return 'Не указана'
|
||
}
|
||
|
||
return date.toLocaleDateString('ru-RU', {
|
||
year: 'numeric',
|
||
month: 'long',
|
||
day: 'numeric'
|
||
})
|
||
} catch (error) {
|
||
return 'Не указана'
|
||
}
|
||
}
|
||
|
||
function getTypeLabel(type: string): string {
|
||
switch (type) {
|
||
case 'FULFILLMENT':
|
||
return 'Фулфилмент'
|
||
case 'SELLER':
|
||
return 'Селлер'
|
||
case 'LOGIST':
|
||
return 'Логистика'
|
||
case 'WHOLESALE':
|
||
return 'Оптовик'
|
||
default:
|
||
return type
|
||
}
|
||
}
|
||
|
||
function getTypeColor(type: string): string {
|
||
switch (type) {
|
||
case 'FULFILLMENT':
|
||
return 'bg-blue-500/20 text-blue-300 border-blue-500/30'
|
||
case 'SELLER':
|
||
return 'bg-green-500/20 text-green-300 border-green-500/30'
|
||
case 'LOGIST':
|
||
return 'bg-orange-500/20 text-orange-300 border-orange-500/30'
|
||
case 'WHOLESALE':
|
||
return 'bg-purple-500/20 text-purple-300 border-purple-500/30'
|
||
default:
|
||
return 'bg-gray-500/20 text-gray-300 border-gray-500/30'
|
||
}
|
||
}
|
||
|
||
export function OrganizationDetailsModal({ organization, open, onOpenChange }: OrganizationDetailsModalProps) {
|
||
if (!organization) return null
|
||
|
||
const displayName = organization.name || organization.fullName || 'Неизвестная организация'
|
||
|
||
return (
|
||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto bg-black/90 backdrop-blur-xl border border-white/20">
|
||
<DialogHeader>
|
||
<DialogTitle className="flex items-center space-x-4 text-white">
|
||
<OrganizationAvatar organization={organization} size="lg" />
|
||
<div>
|
||
<h2 className="text-xl font-semibold">{displayName}</h2>
|
||
<Badge className={getTypeColor(organization.type)}>
|
||
{getTypeLabel(organization.type)}
|
||
</Badge>
|
||
</div>
|
||
</DialogTitle>
|
||
</DialogHeader>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
{/* Основная информация */}
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<Building2 className="h-5 w-5 mr-2 text-blue-400" />
|
||
Основная информация
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">ИНН:</span>
|
||
<span className="text-white font-mono">{organization.inn}</span>
|
||
</div>
|
||
|
||
{organization.kpp && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">КПП:</span>
|
||
<span className="text-white font-mono">{organization.kpp}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.ogrn && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">ОГРН:</span>
|
||
<span className="text-white font-mono">{organization.ogrn}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.status && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Статус:</span>
|
||
<span className="text-white">{organization.status}</span>
|
||
</div>
|
||
)}
|
||
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Дата регистрации:</span>
|
||
<span className="text-white">{formatDate(organization.registrationDate)}</span>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
|
||
{/* Контактная информация */}
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<Phone className="h-5 w-5 mr-2 text-green-400" />
|
||
Контакты
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
{organization.phones && organization.phones.length > 0 && (
|
||
<div>
|
||
<div className="text-white/60 text-sm mb-2">Телефоны:</div>
|
||
{organization.phones.map((phone, index) => (
|
||
<div key={index} className="flex items-center text-white">
|
||
<Phone className="h-3 w-3 mr-2 text-green-400" />
|
||
{phone.value}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{organization.emails && organization.emails.length > 0 && (
|
||
<div>
|
||
<div className="text-white/60 text-sm mb-2">Email:</div>
|
||
{organization.emails.map((email, index) => (
|
||
<div key={index} className="flex items-center text-white">
|
||
<Mail className="h-3 w-3 mr-2 text-blue-400" />
|
||
{email.value}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{organization.address && (
|
||
<div>
|
||
<div className="text-white/60 text-sm mb-2">Адрес:</div>
|
||
<div className="flex items-start text-white">
|
||
<MapPin className="h-3 w-3 mr-2 mt-1 text-orange-400 flex-shrink-0" />
|
||
<span className="text-sm">{organization.addressFull || organization.address}</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
|
||
{/* Руководство */}
|
||
{organization.managementName && (
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<User className="h-5 w-5 mr-2 text-purple-400" />
|
||
Руководство
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Руководитель:</span>
|
||
<span className="text-white">{organization.managementName}</span>
|
||
</div>
|
||
|
||
{organization.managementPost && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Должность:</span>
|
||
<span className="text-white">{organization.managementPost}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
{/* Организационно-правовая форма */}
|
||
{organization.opfFull && (
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<FileText className="h-5 w-5 mr-2 text-yellow-400" />
|
||
ОПФ
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Полное название:</span>
|
||
<span className="text-white">{organization.opfFull}</span>
|
||
</div>
|
||
|
||
{organization.opfShort && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Краткое название:</span>
|
||
<span className="text-white">{organization.opfShort}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.opfCode && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Код ОКОПФ:</span>
|
||
<span className="text-white font-mono">{organization.opfCode}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
{/* Коды статистики */}
|
||
{(organization.okato || organization.oktmo || organization.okpo || organization.okved) && (
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<Hash className="h-5 w-5 mr-2 text-cyan-400" />
|
||
Коды статистики
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
{organization.okato && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">ОКАТО:</span>
|
||
<span className="text-white font-mono">{organization.okato}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.oktmo && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">ОКТМО:</span>
|
||
<span className="text-white font-mono">{organization.oktmo}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.okpo && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">ОКПО:</span>
|
||
<span className="text-white font-mono">{organization.okpo}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.okved && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Основной ОКВЭД:</span>
|
||
<span className="text-white font-mono">{organization.okved}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
{/* Финансовая информация */}
|
||
{(organization.employeeCount || organization.revenue || organization.taxSystem) && (
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<CreditCard className="h-5 w-5 mr-2 text-emerald-400" />
|
||
Финансовая информация
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
{organization.employeeCount && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Сотрудников:</span>
|
||
<span className="text-white">{organization.employeeCount}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.revenue && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Выручка:</span>
|
||
<span className="text-white">{organization.revenue}</span>
|
||
</div>
|
||
)}
|
||
|
||
{organization.taxSystem && (
|
||
<div className="flex justify-between">
|
||
<span className="text-white/60">Налоговая система:</span>
|
||
<span className="text-white">{organization.taxSystem}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
{/* Пользователи */}
|
||
{organization.users && organization.users.length > 0 && (
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<Users className="h-5 w-5 mr-2 text-indigo-400" />
|
||
Пользователи ({organization.users.length})
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
{organization.users.map((user, index) => (
|
||
<div key={user.id} className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-3">
|
||
<OrganizationAvatar
|
||
organization={{
|
||
id: user.id,
|
||
users: [user]
|
||
}}
|
||
size="sm"
|
||
/>
|
||
<span className="text-white">{user.phone}</span>
|
||
</div>
|
||
<span className="text-white/60 text-sm">
|
||
{formatDate(user.createdAt)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</Card>
|
||
)}
|
||
|
||
{/* API ключи */}
|
||
{organization.apiKeys && organization.apiKeys.length > 0 && (
|
||
<Card className="glass-card p-4">
|
||
<h3 className="text-lg font-semibold text-white mb-4 flex items-center">
|
||
<Briefcase className="h-5 w-5 mr-2 text-pink-400" />
|
||
API ключи маркетплейсов
|
||
</h3>
|
||
|
||
<div className="space-y-3">
|
||
{organization.apiKeys.map((apiKey, index) => (
|
||
<div key={apiKey.id} className="flex items-center justify-between">
|
||
<div className="flex items-center space-x-3">
|
||
<Badge className={apiKey.isActive ? 'bg-green-500/20 text-green-300 border-green-500/30' : 'bg-red-500/20 text-red-300 border-red-500/30'}>
|
||
{apiKey.marketplace}
|
||
</Badge>
|
||
<span className="text-white/60 text-sm">
|
||
{apiKey.isActive ? 'Активен' : 'Неактивен'}
|
||
</span>
|
||
</div>
|
||
<span className="text-white/60 text-sm">
|
||
{formatDate(apiKey.createdAt)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</Card>
|
||
)}
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)
|
||
}
|