Реализован интерфейс согласий
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router';
|
||||
import { ArrowRight, Check, ExternalLink, ShieldCheck } from 'lucide-react';
|
||||
|
||||
interface ConsentLink {
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
interface ConsentItem {
|
||||
id: string;
|
||||
required: boolean;
|
||||
text: string;
|
||||
links: ConsentLink[];
|
||||
}
|
||||
|
||||
const consents: ConsentItem[] = [
|
||||
{
|
||||
id: 'pdp',
|
||||
required: true,
|
||||
text: 'Я ознакомлен с Политикой обработки персональных данных ООО «ХАН» и даю своё Согласие на обработку моих персональных данных',
|
||||
links: [
|
||||
{ label: 'Согласие на обработку ПД', href: '#pdp-consent' },
|
||||
{ label: 'Политика обработки ПД', href: '#pdp-policy' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'terms',
|
||||
required: true,
|
||||
text: 'Я прочитал и соглашаюсь с Пользовательским соглашением',
|
||||
links: [{ label: 'Пользовательское соглашение', href: '#terms' }],
|
||||
},
|
||||
{
|
||||
id: 'marketing',
|
||||
required: false,
|
||||
text: 'Я даю своё согласие на получение рекламных и маркетинговых коммуникаций',
|
||||
links: [{ label: 'Условия получения коммуникаций', href: '#marketing' }],
|
||||
},
|
||||
];
|
||||
|
||||
function Checkbox({ checked, onChange }: { checked: boolean; onChange: () => void }) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onChange}
|
||||
className={`w-5 h-5 rounded-md border-2 flex items-center justify-center flex-shrink-0 transition-all ${
|
||||
checked
|
||||
? 'bg-primary border-primary'
|
||||
: 'border-border bg-background hover:border-primary/50'
|
||||
}`}
|
||||
aria-checked={checked}
|
||||
role="checkbox"
|
||||
>
|
||||
{checked && <Check className="w-3 h-3 text-primary-foreground" strokeWidth={3} />}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function AuthConsent() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const returnTo = (location.state as { returnTo?: string })?.returnTo ?? '/auth/phone';
|
||||
|
||||
const [checked, setChecked] = useState<Record<string, boolean>>({
|
||||
pdp: false,
|
||||
terms: false,
|
||||
marketing: false,
|
||||
});
|
||||
|
||||
const toggle = (id: string) => setChecked(prev => ({ ...prev, [id]: !prev[id] }));
|
||||
|
||||
const requiredDone = consents.filter(c => c.required).every(c => checked[c.id]);
|
||||
|
||||
function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (requiredDone) navigate(returnTo);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full bg-background">
|
||||
<div className="flex flex-col h-full px-6 pt-14 pb-8">
|
||||
|
||||
{/* Иконка + заголовок */}
|
||||
<div className="mb-8">
|
||||
<div className="w-12 h-12 rounded-2xl bg-primary/10 flex items-center justify-center mb-5">
|
||||
<ShieldCheck className="w-6 h-6 text-primary" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-semibold text-foreground mb-2">
|
||||
Перед началом работы
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
Для использования приложения ознакомьтесь со следующими документами и предоставьте необходимые согласия
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Форма согласий */}
|
||||
<form onSubmit={handleSubmit} className="flex flex-col flex-1">
|
||||
<div className="space-y-4 flex-1">
|
||||
{consents.map(consent => (
|
||||
<div
|
||||
key={consent.id}
|
||||
className={`rounded-xl border p-4 transition-colors cursor-pointer ${
|
||||
checked[consent.id]
|
||||
? 'border-primary/30 bg-primary/4'
|
||||
: 'border-border bg-card'
|
||||
}`}
|
||||
onClick={() => toggle(consent.id)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="mt-0.5">
|
||||
<Checkbox checked={checked[consent.id]} onChange={() => toggle(consent.id)} />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm text-foreground leading-relaxed mb-2">
|
||||
{consent.text}
|
||||
{consent.required && (
|
||||
<span className="text-destructive ml-1">*</span>
|
||||
)}
|
||||
</p>
|
||||
{/* Ссылки на документы */}
|
||||
<div className="flex flex-wrap gap-x-3 gap-y-1">
|
||||
{consent.links.map(link => (
|
||||
<a
|
||||
key={link.href}
|
||||
href={link.href}
|
||||
onClick={e => e.stopPropagation()}
|
||||
className="inline-flex items-center gap-1 text-xs text-primary underline underline-offset-2 hover:opacity-75 transition-opacity"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<p className="text-xs text-muted-foreground px-1">
|
||||
<span className="text-destructive">*</span> — обязательные согласия
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Кнопка */}
|
||||
<div className="pt-6">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!requiredDone}
|
||||
className="w-full h-14 bg-primary text-primary-foreground rounded-xl font-medium flex items-center justify-center gap-2.5 transition-all disabled:opacity-40 disabled:cursor-not-allowed hover:bg-primary/90 active:scale-[0.98]"
|
||||
>
|
||||
Продолжить
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user