- Add Cloud for Sovereignty landing zone architecture and deployment - Implement complete legal document management system - Reorganize documentation with improved navigation - Add infrastructure improvements (Dockerfiles, K8s, monitoring) - Add operational improvements (graceful shutdown, rate limiting, caching) - Create comprehensive project structure documentation - Add Azure deployment automation scripts - Improve repository navigation and organization
287 lines
8.9 KiB
TypeScript
287 lines
8.9 KiB
TypeScript
/**
|
|
* Document Management Component
|
|
* Main UI for document management in MCP Legal app
|
|
*/
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import {
|
|
Box,
|
|
Button,
|
|
Card,
|
|
CardContent,
|
|
CardHeader,
|
|
Chip,
|
|
Dialog,
|
|
DialogActions,
|
|
DialogContent,
|
|
DialogTitle,
|
|
FormControl,
|
|
InputLabel,
|
|
MenuItem,
|
|
Select,
|
|
TextField,
|
|
Typography,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
IconButton,
|
|
Tooltip,
|
|
} from '@mui/material';
|
|
import {
|
|
Add as AddIcon,
|
|
Edit as EditIcon,
|
|
Delete as DeleteIcon,
|
|
Visibility as ViewIcon,
|
|
History as HistoryIcon,
|
|
Download as DownloadIcon,
|
|
Share as ShareIcon,
|
|
} from '@mui/icons-material';
|
|
|
|
interface Document {
|
|
id: string;
|
|
title: string;
|
|
type: string;
|
|
status: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
interface DocumentManagementProps {
|
|
matterId?: string;
|
|
}
|
|
|
|
export function DocumentManagement({ matterId }: DocumentManagementProps) {
|
|
const [selectedDocument, setSelectedDocument] = useState<Document | null>(null);
|
|
const [createDialogOpen, setCreateDialogOpen] = useState(false);
|
|
const [viewDialogOpen, setViewDialogOpen] = useState(false);
|
|
const [filterType, setFilterType] = useState<string>('all');
|
|
const queryClient = useQueryClient();
|
|
|
|
// Fetch documents
|
|
const { data: documents, isLoading } = useQuery<Document[]>({
|
|
queryKey: ['documents', matterId, filterType],
|
|
queryFn: async () => {
|
|
const params = new URLSearchParams();
|
|
if (matterId) params.append('matter_id', matterId);
|
|
if (filterType !== 'all') params.append('type', filterType);
|
|
const response = await fetch(`/api/documents?${params}`);
|
|
if (!response.ok) throw new Error('Failed to fetch documents');
|
|
const data = await response.json();
|
|
return data.documents || [];
|
|
},
|
|
});
|
|
|
|
// Create document mutation
|
|
const createDocument = useMutation({
|
|
mutationFn: async (doc: Partial<Document>) => {
|
|
const response = await fetch('/api/documents', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(doc),
|
|
});
|
|
if (!response.ok) throw new Error('Failed to create document');
|
|
return response.json();
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['documents'] });
|
|
setCreateDialogOpen(false);
|
|
},
|
|
});
|
|
|
|
// Delete document mutation
|
|
const deleteDocument = useMutation({
|
|
mutationFn: async (id: string) => {
|
|
const response = await fetch(`/api/documents/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
if (!response.ok) throw new Error('Failed to delete document');
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['documents'] });
|
|
},
|
|
});
|
|
|
|
const handleCreateDocument = (formData: FormData) => {
|
|
createDocument.mutate({
|
|
title: formData.get('title') as string,
|
|
type: formData.get('type') as string,
|
|
content: formData.get('content') as string,
|
|
matter_id: matterId,
|
|
});
|
|
};
|
|
|
|
const handleViewDocument = (doc: Document) => {
|
|
setSelectedDocument(doc);
|
|
setViewDialogOpen(true);
|
|
};
|
|
|
|
const handleDeleteDocument = (id: string) => {
|
|
if (confirm('Are you sure you want to delete this document?')) {
|
|
deleteDocument.mutate(id);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center" mb={3}>
|
|
<Typography variant="h4">Documents</Typography>
|
|
<Box>
|
|
<FormControl size="small" sx={{ minWidth: 120, mr: 2 }}>
|
|
<InputLabel>Filter</InputLabel>
|
|
<Select
|
|
value={filterType}
|
|
label="Filter"
|
|
onChange={(e) => setFilterType(e.target.value)}
|
|
>
|
|
<MenuItem value="all">All</MenuItem>
|
|
<MenuItem value="legal">Legal</MenuItem>
|
|
<MenuItem value="treaty">Treaty</MenuItem>
|
|
<MenuItem value="finance">Finance</MenuItem>
|
|
<MenuItem value="history">History</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
<Button
|
|
variant="contained"
|
|
startIcon={<AddIcon />}
|
|
onClick={() => setCreateDialogOpen(true)}
|
|
>
|
|
New Document
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
|
|
{isLoading ? (
|
|
<Typography>Loading...</Typography>
|
|
) : (
|
|
<Card>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>Title</TableCell>
|
|
<TableCell>Type</TableCell>
|
|
<TableCell>Status</TableCell>
|
|
<TableCell>Created</TableCell>
|
|
<TableCell>Actions</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{documents?.map((doc) => (
|
|
<TableRow key={doc.id}>
|
|
<TableCell>{doc.title}</TableCell>
|
|
<TableCell>
|
|
<Chip label={doc.type} size="small" />
|
|
</TableCell>
|
|
<TableCell>
|
|
<Chip label={doc.status} size="small" color="primary" />
|
|
</TableCell>
|
|
<TableCell>{new Date(doc.created_at).toLocaleDateString()}</TableCell>
|
|
<TableCell>
|
|
<Tooltip title="View">
|
|
<IconButton size="small" onClick={() => handleViewDocument(doc)}>
|
|
<ViewIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
<Tooltip title="History">
|
|
<IconButton size="small" href={`/documents/${doc.id}/versions`}>
|
|
<HistoryIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
<Tooltip title="Download">
|
|
<IconButton size="small">
|
|
<DownloadIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
<Tooltip title="Delete">
|
|
<IconButton
|
|
size="small"
|
|
color="error"
|
|
onClick={() => handleDeleteDocument(doc.id)}
|
|
>
|
|
<DeleteIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Create Document Dialog */}
|
|
<Dialog open={createDialogOpen} onClose={() => setCreateDialogOpen(false)} maxWidth="md" fullWidth>
|
|
<form
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
handleCreateDocument(new FormData(e.currentTarget));
|
|
}}
|
|
>
|
|
<DialogTitle>Create New Document</DialogTitle>
|
|
<DialogContent>
|
|
<TextField
|
|
autoFocus
|
|
margin="dense"
|
|
name="title"
|
|
label="Title"
|
|
fullWidth
|
|
required
|
|
sx={{ mb: 2 }}
|
|
/>
|
|
<FormControl fullWidth margin="dense">
|
|
<InputLabel>Type</InputLabel>
|
|
<Select name="type" label="Type" required>
|
|
<MenuItem value="legal">Legal</MenuItem>
|
|
<MenuItem value="treaty">Treaty</MenuItem>
|
|
<MenuItem value="finance">Finance</MenuItem>
|
|
<MenuItem value="history">History</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
<TextField
|
|
margin="dense"
|
|
name="content"
|
|
label="Content"
|
|
fullWidth
|
|
multiline
|
|
rows={10}
|
|
sx={{ mt: 2 }}
|
|
/>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={() => setCreateDialogOpen(false)}>Cancel</Button>
|
|
<Button type="submit" variant="contained">
|
|
Create
|
|
</Button>
|
|
</DialogActions>
|
|
</form>
|
|
</Dialog>
|
|
|
|
{/* View Document Dialog */}
|
|
<Dialog open={viewDialogOpen} onClose={() => setViewDialogOpen(false)} maxWidth="lg" fullWidth>
|
|
<DialogTitle>{selectedDocument?.title}</DialogTitle>
|
|
<DialogContent>
|
|
{selectedDocument && (
|
|
<Box>
|
|
<Typography variant="body2" color="text.secondary">
|
|
Type: {selectedDocument.type} | Status: {selectedDocument.status}
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
|
|
Created: {new Date(selectedDocument.created_at).toLocaleString()}
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={() => setViewDialogOpen(false)}>Close</Button>
|
|
<Button variant="contained" href={`/documents/${selectedDocument?.id}`}>
|
|
Open Full View
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</Box>
|
|
);
|
|
}
|
|
|