Проект разделен на два репозитория

This commit is contained in:
mi
2026-08-14 15:42:45 +03:00
parent e06a77ee1d
commit bbef7a30c9
521 changed files with 2597 additions and 2302 deletions
+242
View File
@@ -0,0 +1,242 @@
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>
);
}