243 lines
8.4 KiB
TypeScript
243 lines
8.4 KiB
TypeScript
import { ArrowLeft, CheckCircle, Clock, Info, Calendar as CalendarIcon, MapPin, Briefcase, FileText, Globe, Home } from 'lucide-react';
|
||
import { useNavigate } from 'react-router';
|
||
import * as Tooltip from '@radix-ui/react-tooltip';
|
||
|
||
interface ClientData {
|
||
icon: React.ReactNode;
|
||
label: string;
|
||
value: string;
|
||
}
|
||
|
||
interface Obligation {
|
||
id: string;
|
||
title: string;
|
||
status: 'completed' | 'deadline' | 'pending' | 'info';
|
||
statusText?: string;
|
||
tooltip?: string;
|
||
}
|
||
|
||
const clientData: ClientData[] = [
|
||
{
|
||
icon: <Globe className="w-4 h-4" />,
|
||
label: 'Гражданство',
|
||
value: 'Узбекистан'
|
||
},
|
||
{
|
||
icon: <Briefcase className="w-4 h-4" />,
|
||
label: 'Цель визита',
|
||
value: 'Работа'
|
||
},
|
||
{
|
||
icon: <FileText className="w-4 h-4" />,
|
||
label: 'Основание пребывания',
|
||
value: 'Миграционная карта'
|
||
},
|
||
{
|
||
icon: <CalendarIcon className="w-4 h-4" />,
|
||
label: 'Дата въезда в РФ',
|
||
value: '15.03.2026'
|
||
},
|
||
{
|
||
icon: <MapPin className="w-4 h-4" />,
|
||
label: 'Регион РФ',
|
||
value: 'Москва'
|
||
},
|
||
];
|
||
|
||
const currentObligations: Obligation[] = [
|
||
{
|
||
id: '1',
|
||
title: 'Уведомление о прибытии',
|
||
status: 'completed',
|
||
statusText: 'Выполнено'
|
||
},
|
||
{
|
||
id: '2',
|
||
title: 'Заявление на патент',
|
||
status: 'deadline',
|
||
statusText: 'Осталось 5 дней'
|
||
},
|
||
{
|
||
id: '3',
|
||
title: 'Уведомление о работе',
|
||
status: 'pending',
|
||
statusText: 'После получения патента'
|
||
},
|
||
{
|
||
id: '4',
|
||
title: 'Оплатить НДФЛ',
|
||
status: 'deadline',
|
||
statusText: 'Осталось 3 дня'
|
||
},
|
||
];
|
||
|
||
const situationalObligations: Obligation[] = [
|
||
{
|
||
id: '1',
|
||
title: 'Смена места жительства',
|
||
status: 'info',
|
||
tooltip: 'При смене места жительства необходимо уведомить МВД в течение пяти рабочих дней'
|
||
},
|
||
];
|
||
|
||
function ObligationItem({ obligation }: { obligation: Obligation }) {
|
||
const getStatusStyles = () => {
|
||
switch (obligation.status) {
|
||
case 'completed':
|
||
return {
|
||
icon: <CheckCircle className="w-5 h-5" />,
|
||
bgColor: 'bg-green-500/10',
|
||
textColor: 'text-green-600',
|
||
badgeColor: 'bg-green-500/10 text-green-600'
|
||
};
|
||
case 'deadline':
|
||
return {
|
||
icon: <Clock className="w-5 h-5" />,
|
||
bgColor: 'bg-red-500/10',
|
||
textColor: 'text-red-600',
|
||
badgeColor: 'bg-red-500/10 text-red-600'
|
||
};
|
||
case 'pending':
|
||
return {
|
||
icon: <Clock className="w-5 h-5" />,
|
||
bgColor: 'bg-yellow-500/10',
|
||
textColor: 'text-yellow-600',
|
||
badgeColor: 'bg-yellow-500/10 text-yellow-600'
|
||
};
|
||
case 'info':
|
||
return {
|
||
icon: <Info className="w-5 h-5" />,
|
||
bgColor: 'bg-blue-500/10',
|
||
textColor: 'text-blue-600',
|
||
badgeColor: 'bg-blue-500/10 text-blue-600'
|
||
};
|
||
default:
|
||
return {
|
||
icon: <Clock className="w-5 h-5" />,
|
||
bgColor: 'bg-muted',
|
||
textColor: 'text-muted-foreground',
|
||
badgeColor: 'bg-muted text-muted-foreground'
|
||
};
|
||
}
|
||
};
|
||
|
||
const styles = getStatusStyles();
|
||
|
||
return (
|
||
<div className="p-3 flex items-center gap-3 border-b border-border last:border-b-0">
|
||
<div className={`flex items-center justify-center w-9 h-9 rounded-full flex-shrink-0 ${styles.bgColor} ${styles.textColor}`}>
|
||
{styles.icon}
|
||
</div>
|
||
<div className="flex-1 min-w-0">
|
||
<p className="text-sm font-medium">{obligation.title}</p>
|
||
{obligation.statusText && (
|
||
<p className="text-xs text-muted-foreground mt-0.5">{obligation.statusText}</p>
|
||
)}
|
||
</div>
|
||
{obligation.tooltip ? (
|
||
<Tooltip.Provider delayDuration={200}>
|
||
<Tooltip.Root>
|
||
<Tooltip.Trigger asChild>
|
||
<button className={`flex items-center justify-center w-8 h-8 rounded-full ${styles.bgColor} ${styles.textColor}`}>
|
||
<Info className="w-4 h-4" />
|
||
</button>
|
||
</Tooltip.Trigger>
|
||
<Tooltip.Portal>
|
||
<Tooltip.Content
|
||
className="max-w-[280px] bg-popover text-popover-foreground border border-border rounded-lg px-3 py-2 text-sm shadow-lg z-50"
|
||
sideOffset={5}
|
||
>
|
||
{obligation.tooltip}
|
||
<Tooltip.Arrow className="fill-border" />
|
||
</Tooltip.Content>
|
||
</Tooltip.Portal>
|
||
</Tooltip.Root>
|
||
</Tooltip.Provider>
|
||
) : obligation.statusText ? (
|
||
<span className={`text-xs font-medium px-2 py-1 rounded-full ${styles.badgeColor}`}>
|
||
{obligation.statusText}
|
||
</span>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function Calendar() {
|
||
const navigate = useNavigate();
|
||
|
||
return (
|
||
<div className="flex-1 overflow-y-auto">
|
||
<div className="px-4 py-4">
|
||
{/* Заголовок */}
|
||
<div className="flex items-center gap-3 mb-6">
|
||
<button
|
||
onClick={() => navigate('/profile')}
|
||
className="flex items-center justify-center w-9 h-9 rounded-full hover:bg-muted transition-colors"
|
||
aria-label="Назад"
|
||
>
|
||
<ArrowLeft className="w-5 h-5" />
|
||
</button>
|
||
<div>
|
||
<h1 className="text-xl font-medium">Календарь обязательств</h1>
|
||
<p className="text-sm text-muted-foreground">Важные даты и действия</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Блок 1: Данные о клиенте */}
|
||
<div className="mb-4">
|
||
<h2 className="text-xs font-medium text-muted-foreground mb-2 px-1">Данные о пребывании</h2>
|
||
<div className="bg-muted/30 border border-border/50 rounded-lg p-3">
|
||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||
{clientData.map((item, index) => (
|
||
<div key={index} className="flex items-center gap-1.5">
|
||
<div className="text-muted-foreground flex-shrink-0">
|
||
{item.icon}
|
||
</div>
|
||
<div className="flex-1 min-w-0">
|
||
<p className="text-muted-foreground truncate">{item.label}:</p>
|
||
<p className="font-medium truncate">{item.value}</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Блок 2: Текущие обязательства */}
|
||
<div className="mb-6">
|
||
<h2 className="text-sm font-medium text-muted-foreground mb-3 px-1">Текущие обязательства</h2>
|
||
<div className="bg-card border border-border rounded-lg">
|
||
{currentObligations.map((obligation) => (
|
||
<ObligationItem key={obligation.id} obligation={obligation} />
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Блок 3: Ситуационные обязательства */}
|
||
<div className="mb-6">
|
||
<h2 className="text-sm font-medium text-muted-foreground mb-3 px-1">Ситуационные обязательства</h2>
|
||
<div className="bg-card border border-border rounded-lg">
|
||
{situationalObligations.map((obligation) => (
|
||
<ObligationItem key={obligation.id} obligation={obligation} />
|
||
))}
|
||
</div>
|
||
<p className="text-xs text-muted-foreground mt-2 px-1">
|
||
Нажмите на значок <Info className="w-3 h-3 inline" /> для получения подробной информации
|
||
</p>
|
||
</div>
|
||
|
||
{/* Информационное сообщение */}
|
||
<div className="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4 flex gap-3">
|
||
<Info className="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" />
|
||
<div className="flex-1">
|
||
<p className="text-sm font-medium text-blue-900 mb-1">Важная информация</p>
|
||
<p className="text-xs text-blue-800">
|
||
Календарь обязательств помогает следить за важными сроками. Мы автоматически напомним вам о приближающихся датах.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|