David Headrick f5b1fe6397 Update mailing and target management features
- Added new methods for creating mailings and testing targets.
- Updated configuration files for JWT settings and connection strings.
- Introduced new DTOs for target column updates and test targets.
- Enhanced MailingStatistic with a new SentDate property.
- Created new components for handling cancelled mailings and target samples.
- Refactored authentication in Login.tsx to use fetch API.
- Updated various services and repositories to support new functionalities.
2025-04-07 12:13:44 -05:00

178 lines
7.5 KiB
TypeScript

import { useState, useRef, useEffect } from 'react';
import { useSetupData, SetupData } from "@/context/SetupDataContext";
import EditIcon from '@mui/icons-material/Edit';
import AddIcon from '@mui/icons-material/Add';
import RefreshIcon from '@mui/icons-material/Refresh';
import { List, Card, CardContent, Typography, Box, useTheme, useMediaQuery, CircularProgress, IconButton } from '@mui/material';
import { DataGrid, GridColDef, GridRenderCellParams, GridRowModel, GridToolbarContainer, GridToolbarQuickFilter, GridToolbarExport, GridToolbarDensitySelector, GridToolbarColumnsButton } from '@mui/x-data-grid';
import Mailing from '@/types/mailing';
//import Template from '@/types/template';
import MailingEdit from "@/components/modals/MailingEdit";
function NewMailings() {
const theme = useTheme();
const setupData: SetupData = useSetupData();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
const gridContainerRef = useRef<HTMLDivElement | null>(null);
const [mailingsLoading, setMailingsLoading] = useState<boolean>(false);
const [mailings, setMailings] = useState<Mailing[]>([]);
const [selectedRow, setSelectedRow] = useState<Mailing | null>(null);
const [open, setOpen] = useState<boolean>(false);
const columns: GridColDef<Mailing>[] = [
{
field: "actions",
headerName: "",
sortable: false,
width: 60,
renderCell: (params: GridRenderCellParams<Mailing>) => (
<IconButton color="primary" onClick={(e) => { e.stopPropagation(); handleEdit(params.row); }}>
<EditIcon />
</IconButton>
),
},
{ field: "id", headerName: "ID", width: 80 },
{ field: "name", headerName: "Name", flex: 1, minWidth: 160 },
{ field: "description", headerName: "Description", flex: 1, minWidth: 200 },
{
field: "templateId",
headerName: "Subject",
flex: 1,
minWidth: 160,
valueGetter: (_: number, row: Mailing) => setupData.templates.find(t => t.id === row.templateId)?.subject || 'Unknown',
},
];
const reloadMailings = async () => {
setMailingsLoading(true);
const mailingsResponse = await fetch("/api/mailings/status/ed");
const mailingsData = await mailingsResponse.json();
if (mailingsData) {
setMailings(mailingsData);
setMailingsLoading(false);
}
else {
console.error("Failed to fetch mailings");
setMailingsLoading(false);
}
}
const handleNew = () => {
setSelectedRow(null);
setOpen(true);
};
const handleEdit = (row: GridRowModel<Mailing>) => {
setSelectedRow(row);
setOpen(true);
};
const handleUpdateRow = (updatedRow: Mailing) => {
if (updatedRow.statusCode.toUpperCase() !== "ED")
removeMailing(updatedRow);
else
updateMailings(updatedRow);
};
useEffect(() => {
reloadMailings();
}, []);
const removeMailing = (mailing: Mailing) => {
setMailings((prevMailings) => {
return prevMailings.filter(el => el.id !== mailing.id);
});
}
const updateMailings = (updatedMailing: Mailing) => {
setMailings((prev) => {
const exists = prev.some((e) => e.id === updatedMailing.id);
return exists
? prev.map((server) => (server.id === updatedMailing.id ? updatedMailing : server))
: [...prev, updatedMailing];
});
};
return (
<Box ref={gridContainerRef} sx={{
position: 'relative', left: 0, right: 0, height: "calc(100vh - 124px)", overflow: "hidden",
transition: theme.transitions.create(['width', 'height'], {
easing: theme.transitions.easing.easeInOut,
duration: theme.transitions.duration.standard,
})
}}>
<Box sx={{ position: 'absolute', inset: 0 }}>
{isMobile ? (
<List>
{mailings.map((row) => (
<Card key={row.id} sx={{ marginBottom: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<CardContent>
<Typography variant="h6">{row.name}</Typography>
<Typography variant="body2">ID: {row.id}</Typography>
<Typography variant="body2">Description: {row.description}</Typography>
<Typography variant="body2">Subject: {setupData.templates.find(t => t.id === row.templateId)?.subject || 'Unknown'}</Typography>
</CardContent>
<IconButton onClick={(e) => { e.stopPropagation(); handleEdit(row); }}>
<EditIcon />
</IconButton>
</Box>
</Card>
))}
</List>
) : (
<DataGrid
rows={mailings}
columns={columns}
autoPageSize
sx={{ minWidth: "600px" }}
slots={{
toolbar: () => (
<GridToolbarContainer sx={{ display: "flex", alignItems: "center" }}>
<IconButton size="small" color="primary" onClick={handleNew} sx={{ marginLeft: 1 }}>
<AddIcon />
</IconButton>
<IconButton size="small" color="primary" onClick={() => reloadMailings()} sx={{ marginLeft: 1 }}>
{mailingsLoading ? <CircularProgress size={24} color="inherit" /> : <RefreshIcon />}
</IconButton>
<GridToolbarColumnsButton />
<GridToolbarDensitySelector />
<GridToolbarExport />
<GridToolbarQuickFilter sx={{ ml: "auto" }} />
</GridToolbarContainer>
),
}}
slotProps={{
toolbar: {
showQuickFilter: true,
},
}}
initialState={{
pagination: {
paginationModel: {
pageSize: 20,
},
},
sorting: {
sortModel: [{ field: 'id', sort: 'asc' }],
},
}}
pageSizeOptions={[10, 20, 50, 100]}
/>
)}
</Box>
{open && (
<MailingEdit
open={open}
mailing={selectedRow}
onClose={(reason) => { if (reason !== 'backdropClick') setOpen(false) }}
onSave={handleUpdateRow}
/>
)}
</Box>
);
}
export default NewMailings;