first commit

This commit is contained in:
lendry
2026-06-24 14:37:15 +03:00
commit 995adeedd4
188 changed files with 28810 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
'use client';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { FileText } from 'lucide-react';
import { DocumentFormDialog } from '@/components/documents/document-form-dialog';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { getDocumentType, indexDocumentsByType, type DocumentTypeCode } from '@/lib/document-catalog';
import { apiFetch, UserDocument } from '@/lib/api';
import { cn } from '@/lib/utils';
export function UserDocumentsDialog({
open,
onOpenChange,
targetUserId,
targetUserName,
token
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
targetUserId: string;
targetUserName: string;
token: string | null;
}) {
const [documents, setDocuments] = useState<UserDocument[]>([]);
const [loading, setLoading] = useState(false);
const [activeType, setActiveType] = useState<DocumentTypeCode | null>(null);
const [selectedDocument, setSelectedDocument] = useState<UserDocument | null>(null);
const loadDocuments = useCallback(async () => {
if (!token || !targetUserId) return;
setLoading(true);
try {
const response = await apiFetch<{ documents?: UserDocument[] }>(`/documents/users/${targetUserId}`, {}, token);
setDocuments(response.documents ?? []);
} finally {
setLoading(false);
}
}, [targetUserId, token]);
useEffect(() => {
if (open) void loadDocuments();
}, [loadDocuments, open]);
const documentsByType = useMemo(() => indexDocumentsByType(documents), [documents]);
function openDocument(type: DocumentTypeCode) {
setSelectedDocument(documentsByType.get(type) ?? null);
setActiveType(type);
}
return (
<>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-h-[90vh] overflow-y-auto rounded-[28px] sm:max-w-[520px]">
<DialogHeader>
<DialogTitle>Документы пользователя</DialogTitle>
<p className="text-sm text-[#667085]">{targetUserName}</p>
</DialogHeader>
{loading ? <p className="py-8 text-center text-sm text-[#667085]">Загрузка документов...</p> : null}
{!loading && documents.length === 0 ? (
<p className="py-8 text-center text-sm text-[#667085]">У пользователя нет сохранённых документов</p>
) : null}
<div className="space-y-2">
{documents.map((document) => {
const config = getDocumentType(document.type);
if (!config) return null;
const Icon = config.icon;
return (
<button
key={document.id}
type="button"
onClick={() => openDocument(document.type as DocumentTypeCode)}
className="flex w-full items-center gap-3 rounded-2xl bg-[#f4f5f8] px-4 py-3 text-left transition hover:bg-[#eceef4]"
>
<div className={cn('flex h-10 w-10 items-center justify-center rounded-xl', config.accent)}>
<Icon className="h-5 w-5" />
</div>
<div className="min-w-0 flex-1">
<div className="font-medium">{config.label}</div>
<div className="truncate text-sm text-[#667085]">{document.number}</div>
</div>
<FileText className="h-4 w-4 text-[#a8adbc]" />
</button>
);
})}
</div>
</DialogContent>
</Dialog>
{activeType ? (
<DocumentFormDialog
open={Boolean(activeType)}
onOpenChange={(nextOpen) => {
if (!nextOpen) {
setActiveType(null);
setSelectedDocument(null);
}
}}
documentType={activeType}
userId={targetUserId}
token={token}
existing={selectedDocument}
onSaved={loadDocuments}
readOnly
/>
) : null}
</>
);
}