Files
the_order/apps/mcp-legal/src/components/DocumentManagement.tsx
defiQUG 6a8582e54d feat: comprehensive project structure improvements and Cloud for Sovereignty landing zone
- 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
2025-11-13 09:32:55 -08:00

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>
);
}