Добавлены новые зависимости, обновлены стили и улучшена структура проекта. Обновлен README с описанием функционала и технологий. Реализована анимация и адаптивный дизайн. Настроена авторизация с использованием Apollo Client.
This commit is contained in:
360
src/components/auth/confirmation-step.tsx
Normal file
360
src/components/auth/confirmation-step.tsx
Normal file
@ -0,0 +1,360 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { AuthLayout } from "./auth-layout"
|
||||
import { Package, UserCheck, Phone, FileText, Key, ArrowLeft, Check, Zap, Truck, Building2 } from "lucide-react"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
|
||||
interface OrganizationData {
|
||||
name?: string
|
||||
fullName?: string
|
||||
address?: string
|
||||
isActive?: boolean
|
||||
}
|
||||
|
||||
interface ApiKeyValidation {
|
||||
sellerId?: string
|
||||
sellerName?: string
|
||||
isValid?: boolean
|
||||
}
|
||||
|
||||
interface ConfirmationStepProps {
|
||||
data: {
|
||||
phone: string
|
||||
cabinetType: 'fulfillment' | 'seller' | 'logist' | 'wholesale'
|
||||
inn?: string
|
||||
organizationData?: OrganizationData
|
||||
wbApiKey?: string
|
||||
wbApiValidation?: ApiKeyValidation
|
||||
ozonApiKey?: string
|
||||
ozonApiValidation?: ApiKeyValidation
|
||||
}
|
||||
onConfirm: () => void
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
export function ConfirmationStep({ data, onConfirm, onBack }: ConfirmationStepProps) {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
const { registerFulfillmentOrganization, registerSellerOrganization } = useAuth()
|
||||
|
||||
const formatPhone = (phone: string) => {
|
||||
return phone || "+7 (___) ___-__-__"
|
||||
}
|
||||
|
||||
const formatApiKey = (key?: string) => {
|
||||
if (!key) return ""
|
||||
return key.substring(0, 4) + "•".repeat(key.length - 8) + key.substring(key.length - 4)
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
|
||||
try {
|
||||
let result
|
||||
|
||||
if ((data.cabinetType === 'fulfillment' || data.cabinetType === 'logist' || data.cabinetType === 'wholesale') && data.inn) {
|
||||
result = await registerFulfillmentOrganization(
|
||||
data.phone.replace(/\D/g, ''),
|
||||
data.inn
|
||||
)
|
||||
} else if (data.cabinetType === 'seller') {
|
||||
result = await registerSellerOrganization({
|
||||
phone: data.phone.replace(/\D/g, ''),
|
||||
wbApiKey: data.wbApiKey,
|
||||
ozonApiKey: data.ozonApiKey
|
||||
})
|
||||
}
|
||||
|
||||
if (result?.success) {
|
||||
onConfirm()
|
||||
} else {
|
||||
setError(result?.message || 'Ошибка при регистрации организации')
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
console.error('Registration error:', error)
|
||||
setError('Произошла ошибка при регистрации. Попробуйте еще раз.')
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthLayout
|
||||
title="Подтверждение данных"
|
||||
description="Проверьте введенные данные перед завершением"
|
||||
currentStep={5}
|
||||
totalSteps={5}
|
||||
stepName="Подтверждение"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{/* Объединенная карточка с данными */}
|
||||
<div className="glass-card p-4 space-y-3">
|
||||
{/* Телефон */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Phone className="h-4 w-4 text-white" />
|
||||
<span className="text-white text-sm">Телефон:</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white/70 text-sm">{formatPhone(data.phone)}</span>
|
||||
<Badge variant="outline" className="glass-secondary text-green-300 border-green-400/30 text-xs flex items-center gap-1">
|
||||
<Check className="h-3 w-3" />
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Тип кабинета */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{data.cabinetType === 'fulfillment' ? (
|
||||
<Package className="h-4 w-4 text-white" />
|
||||
) : data.cabinetType === 'logist' ? (
|
||||
<Truck className="h-4 w-4 text-white" />
|
||||
) : data.cabinetType === 'wholesale' ? (
|
||||
<Building2 className="h-4 w-4 text-white" />
|
||||
) : (
|
||||
<UserCheck className="h-4 w-4 text-white" />
|
||||
)}
|
||||
<span className="text-white text-sm">Кабинет:</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white/70 text-sm">
|
||||
{data.cabinetType === 'fulfillment' ? 'Фулфилмент' :
|
||||
data.cabinetType === 'logist' ? 'Логистика' :
|
||||
data.cabinetType === 'wholesale' ? 'Оптовик' :
|
||||
'Селлер'}
|
||||
</span>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={`glass-secondary text-xs flex items-center gap-1 ${
|
||||
data.cabinetType === 'fulfillment'
|
||||
? "text-blue-300 border-blue-400/30"
|
||||
: data.cabinetType === 'logist'
|
||||
? "text-green-300 border-green-400/30"
|
||||
: data.cabinetType === 'wholesale'
|
||||
? "text-orange-300 border-orange-400/30"
|
||||
: "text-purple-300 border-purple-400/30"
|
||||
}`}
|
||||
>
|
||||
{data.cabinetType === 'fulfillment' ? (
|
||||
<Package className="h-3 w-3" />
|
||||
) : data.cabinetType === 'logist' ? (
|
||||
<Truck className="h-3 w-3" />
|
||||
) : data.cabinetType === 'wholesale' ? (
|
||||
<Building2 className="h-3 w-3" />
|
||||
) : (
|
||||
<UserCheck className="h-3 w-3" />
|
||||
)}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Данные организации */}
|
||||
{(data.cabinetType === 'fulfillment' || data.cabinetType === 'logist' || data.cabinetType === 'wholesale') && data.inn && (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-white" />
|
||||
<span className="text-white text-sm">ИНН:</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white/70 text-sm font-mono">{data.inn}</span>
|
||||
<Badge variant="outline" className="glass-secondary text-green-300 border-green-400/30 text-xs flex items-center gap-1">
|
||||
<Check className="h-3 w-3" />
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Данные организации из DaData */}
|
||||
{data.organizationData && (
|
||||
<>
|
||||
{data.organizationData.name && (
|
||||
<div className="flex items-center justify-between pl-6">
|
||||
<span className="text-white/60 text-sm">Название:</span>
|
||||
<span className="text-white/90 text-sm max-w-[240px] text-right truncate">
|
||||
{data.organizationData.name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.organizationData.fullName && data.organizationData.fullName !== data.organizationData.name && (
|
||||
<div className="flex items-center justify-between pl-6">
|
||||
<span className="text-white/60 text-sm">Полное название:</span>
|
||||
<span className="text-white/70 text-xs max-w-[200px] text-right truncate">
|
||||
{data.organizationData.fullName}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.organizationData.address && (
|
||||
<div className="flex items-center justify-between pl-6">
|
||||
<span className="text-white/60 text-sm">Адрес:</span>
|
||||
<span className="text-white/70 text-xs max-w-[200px] text-right truncate">
|
||||
{data.organizationData.address}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between pl-6">
|
||||
<span className="text-white/60 text-sm">Статус:</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`text-xs flex items-center gap-1 ${
|
||||
data.organizationData.isActive
|
||||
? "glass-secondary text-green-300 border-green-400/30"
|
||||
: "glass-secondary text-red-300 border-red-400/30"
|
||||
}`}
|
||||
>
|
||||
{data.organizationData.isActive ? (
|
||||
<>
|
||||
<Check className="h-3 w-3" />
|
||||
Активна
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FileText className="h-3 w-3" />
|
||||
Неактивна
|
||||
</>
|
||||
)}
|
||||
</Badge>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* API ключи для селлера */}
|
||||
{data.cabinetType === 'seller' && (data.wbApiKey || data.ozonApiKey) && (
|
||||
<>
|
||||
<div className="flex items-center gap-2 pt-1">
|
||||
<Key className="h-4 w-4 text-white" />
|
||||
<span className="text-white text-sm">API ключи:</span>
|
||||
<Badge variant="outline" className="glass-secondary text-yellow-300 border-yellow-400/30 text-xs ml-auto flex items-center gap-1">
|
||||
<Zap className="h-3 w-3" />
|
||||
Активны
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{data.wbApiKey && (
|
||||
<div className="space-y-2 pl-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white/60 text-sm">Wildberries</span>
|
||||
<Badge variant="outline" className="glass-secondary text-purple-300 border-purple-400/30 text-xs">
|
||||
WB
|
||||
</Badge>
|
||||
</div>
|
||||
{data.wbApiValidation?.sellerName ? (
|
||||
<span className="text-white/70 text-xs max-w-[120px] text-right truncate">
|
||||
{data.wbApiValidation.sellerName}
|
||||
</span>
|
||||
) : (
|
||||
<Badge variant="outline" className="glass-secondary text-green-300 border-green-400/30 text-xs flex items-center gap-1">
|
||||
<Check className="h-3 w-3" />
|
||||
Подключен
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{data.wbApiValidation && (
|
||||
<>
|
||||
{data.wbApiValidation.sellerName && (
|
||||
<div className="flex items-center justify-between pl-4">
|
||||
<span className="text-white/50 text-xs">Магазин:</span>
|
||||
<span className="text-white/70 text-xs max-w-[160px] text-right truncate">
|
||||
{data.wbApiValidation.sellerName}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{data.wbApiValidation.sellerId && (
|
||||
<div className="flex items-center justify-between pl-4">
|
||||
<span className="text-white/50 text-xs">ID продавца:</span>
|
||||
<span className="text-white/70 text-xs font-mono">
|
||||
{data.wbApiValidation.sellerId}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.ozonApiKey && (
|
||||
<div className="space-y-2 pl-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-white/60 text-sm">Ozon</span>
|
||||
<Badge variant="outline" className="glass-secondary text-blue-300 border-blue-400/30 text-xs">
|
||||
OZ
|
||||
</Badge>
|
||||
</div>
|
||||
<Badge variant="outline" className="glass-secondary text-green-300 border-green-400/30 text-xs flex items-center gap-1">
|
||||
<Check className="h-3 w-3" />
|
||||
Подключен
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
{data.ozonApiValidation && (
|
||||
<>
|
||||
{data.ozonApiValidation.sellerName && (
|
||||
<div className="flex items-center justify-between pl-4">
|
||||
<span className="text-white/50 text-xs">Магазин:</span>
|
||||
<span className="text-white/70 text-xs max-w-[160px] text-right truncate">
|
||||
{data.ozonApiValidation.sellerName}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{data.ozonApiValidation.sellerId && (
|
||||
<div className="flex items-center justify-between pl-4">
|
||||
<span className="text-white/50 text-xs">ID продавца:</span>
|
||||
<span className="text-white/70 text-xs font-mono">
|
||||
{data.ozonApiValidation.sellerId}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="glass-card p-3 border-red-400/30">
|
||||
<p className="text-red-400 text-sm text-center">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
onClick={handleConfirm}
|
||||
variant="glass"
|
||||
size="lg"
|
||||
className="w-full h-12 flex items-center gap-2"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
{isLoading ? "Создание организации..." : "Подтвердить и завершить"}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="glass-secondary"
|
||||
onClick={onBack}
|
||||
className="w-full flex items-center gap-2"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
Назад
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user