Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | 2x 9x 9x 9x 9x 9x 9x 6x 18x 3x 1x 2x 2x | // src/components/documents/ProvidedDocumentsTab.tsx
//
// "From FlowState" tab — documents an admin has provided to the investor
// (FSC-57). Read-only: the investor views and downloads.
import {type ReactElement, useState} from 'react';
import {Alert, AlertTitle, Box, Button, Paper, Skeleton, Snackbar, Stack, Typography} from '@mui/material';
import {
Description as FileIcon,
Download as DownloadIcon,
ErrorOutlined as ErrorIcon
} from '@mui/icons-material';
import {
type ProvidedDocument,
useDownloadMyProvidedDocumentMutation,
useListMyProvidedDocumentsQuery
} from '../../services/providedDocumentsApi';
import {formatBytes} from '../../services/uploadsApi';
import {downloadBlob} from '../../utils/downloadBlob';
interface SnackbarState {
open: boolean;
message: string;
severity: 'success' | 'error';
}
function formatDate(dateStr: string): string {
return new Date(dateStr).toLocaleDateString('en-US', {year: 'numeric', month: 'short', day: 'numeric'});
}
export default function ProvidedDocumentsTab(): ReactElement {
const {data, isLoading, isError} = useListMyProvidedDocumentsQuery();
const [downloadDocument, {isLoading: isDownloading}] = useDownloadMyProvidedDocumentMutation();
const [snackbar, setSnackbar] = useState<SnackbarState>({open: false, message: '', severity: 'success'});
const documents = data?.documents ?? [];
const handleDownload = (doc: ProvidedDocument): void => {
downloadDocument(doc.providedDocumentId)
.unwrap()
.then((blob) => {
downloadBlob(blob, doc.originalFilename);
})
.catch(() => {
setSnackbar({open: true, message: 'Failed to download document.', severity: 'error'});
});
};
if (isLoading) {
return (
<Box>
{[1, 2, 3].map((i) => <Skeleton key={i} variant="rounded" height={84} sx={{mb: 1.5}}/>)}
</Box>
);
}
if (isError) {
return (
<Alert severity="error" icon={<ErrorIcon/>}>
<AlertTitle>Unable to load documents</AlertTitle>
There was a problem loading documents provided to you. Please refresh the page.
</Alert>
);
}
return (
<Box>
<Typography variant="body2" sx={{color: 'text.secondary', mb: 2}}>
Documents FlowState has provided to you.
</Typography>
{documents.length === 0 ? (
<Paper variant="outlined" sx={{p: 4, borderRadius: 2, textAlign: 'center', borderStyle: 'dashed'}}>
<Typography variant="body1" gutterBottom sx={{fontWeight: 600}}>
Nothing here yet
</Typography>
<Typography variant="body2" sx={{color: 'text.secondary'}}>
When FlowState provides a document for you, it will appear here.
</Typography>
</Paper>
) : (
<Stack spacing={1.5}>
{documents.map((doc) => (
<Paper key={doc.providedDocumentId} variant="outlined" sx={{p: 2.5, borderRadius: 2}}>
<Stack direction={{xs: 'column', sm: 'row'}} spacing={2} sx={{alignItems: {sm: 'center'}}}>
<Box sx={{
width: 44,
height: 44,
borderRadius: 2,
backgroundColor: 'primary.light',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: 0
}}>
<FileIcon sx={{color: 'primary.main'}}/>
</Box>
<Box sx={{flexGrow: 1, minWidth: 0}}>
<Typography variant="body1" sx={{fontWeight: 600}}>{doc.title}</Typography>
{doc.description !== null && doc.description !== '' && (
<Typography variant="body2" sx={{color: 'text.secondary'}}>
{doc.description}
</Typography>
)}
<Stack direction="row" spacing={3} sx={{flexWrap: 'wrap', color: 'text.secondary', mt: 0.5}}>
<Typography variant="caption">{doc.originalFilename}</Typography>
<Typography variant="caption">{formatBytes(doc.sizeBytes)}</Typography>
<Typography variant="caption">Provided: {formatDate(doc.createdAt)}</Typography>
</Stack>
</Box>
<Button
variant="outlined"
size="small"
startIcon={<DownloadIcon/>}
disabled={isDownloading}
onClick={() => {
handleDownload(doc);
}}
>
Download
</Button>
</Stack>
</Paper>
))}
</Stack>
)}
<Snackbar
open={snackbar.open}
autoHideDuration={6000}
onClose={() => setSnackbar({...snackbar, open: false})}
>
<Alert
onClose={() => setSnackbar({...snackbar, open: false})}
severity={snackbar.severity}
sx={{width: '100%'}}
>
{snackbar.message}
</Alert>
</Snackbar>
</Box>
);
}
|