Job Summary : 'use client';
import { useRouter } from 'next / navigation';
/ / Loading dialog while AI re-analyzes
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@ / components / ui / dialog';
interface AnalysisLoadingDialogProps {
function AnalysisLoadingDialog({ open } : AnalysisLoadingDialogProps) {
return (
Analyzing documents
Our AI is updating the proposal analysis. This may take a moment.
interface AttachedDocumentsProps {
readonly onRefresh? : () =>
export function AttachedDocuments({
docs,
setDocs,
proposalId,
onRefresh,
readOnly,
} : AttachedDocumentsProps) {
const [isAnalyzing, setIsAnalyzing] = useState(false);
const getFileIcon = (filename : string) =>
if (['doc', 'docx', 'pdf', 'txt', 'rtf'].includes(ext)) {
if (['js', 'ts', 'tsx', 'json', 'py', 'java', 'c', 'cpp', 'cs'].includes(ext)) {
};
const handleFileAdd = async (event : React.ChangeEvent) =>
if (!files || files.length === 0) return;
Array.from(files).forEach(file =>
formData.append('files', file));
try {
setIsAnalyzing(true);
const res = await fetch(`${apiUrl} / api / proposals / ${proposalId} / files`, {
method : 'POST',
body : formData,
});
if (!res.ok) throw new Error(data.error ?? 'Failed to upload files');
/ / Handle duplicate
if (data.status === 'duplicate' || data.skippedFiles?.length) {
toast({
title : 'Duplicate file(s) skipped',
description : `${skippedList} already exist based on first 100 words.`,
variant : 'destructive',
/ / Normal upload success
if (newFiles && newFiles.length >
0) {
setDocs(prev =>
document.dispatchEvent(new CustomEvent('analysisUpdated', { detail : analysis }));
toast({ title : `${newFiles.length} document(s) added and analysis updated.` });
/ / Refresh proposal data dynamically instead of full reload
} else {
toast({
title : 'No new documents added',
description : 'File Already Exists.',
variant : 'destructive',
} catch (err : any) {
toast({
variant : 'destructive',
title : 'Upload Failed',
description : err.message ?? 'Could not upload files',
} finally {
};
const handleFileDelete = async (docId : string) =>
try {
setIsAnalyzing(true);
const res = await fetch(
`${process.env.NEXT_PUBLIC_API_URL} / api / proposals / ${proposalId} / files / ${encodeURIComponent(docId)}`,
{ method : 'DELETE' }
);
if (!res.ok) throw new Error(data.error ?? 'Failed to delete file');
/ / Update local state
const newDocs = docs.filter(d =>
setDocs(newDocs);
/ / If last file deleted delete proposal redirect dashboard
if (newDocs.length === 0) {
toast({ title : 'All files removed', description : 'Deleting proposal...' });
const delRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL} / api / proposals / ${proposalId}`, {
method : 'DELETE',
});
if (!delRes.ok) {
toast({
title : 'Proposal deleted',
description : 'Redirecting to dashboard...',
});
/ / Wait a bit, then redirect and reload
setTimeout(() =>
/ / Notify analysis update
toast({ title : 'File deleted', description : 'Analysis will update shortly.' });
/ /
Registered Nurse • Potsdam, NY, United States