Files
IdP/apps/frontend/components/documents/user-documents-dialog.tsx
2026-07-01 22:46:04 +03:00

121 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { FileText } from 'lucide-react';
import { DocumentFormDialog } from '@/components/documents/document-form-dialog';
import { DocumentPhotosInline } from '@/components/documents/document-photo-gallery';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { getDocumentType, indexDocumentsByType, parseDocumentPhotos, parseMetadata, 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;
const photoKeys = parseDocumentPhotos(parseMetadata(document.metadataJson));
return (
<div key={document.id}>
<button
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>
{photoKeys.length > 0 && token ? (
<div className="mt-2 px-1">
<DocumentPhotosInline userId={targetUserId} token={token} storageKeys={photoKeys} />
</div>
) : null}
</div>
);
})}
</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}
</>
);
}