Updated authentication handling in controllers, added JWT support, and improved error logging. Introduced centralized API calls with customFetch for better token management. Added Grafana's Faro SDK for monitoring and tracing. Refactored project files for improved structure and maintainability.
229 lines
9.6 KiB
TypeScript
229 lines
9.6 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 CancelIcon from '@mui/icons-material/Cancel';
|
|
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";
|
|
import ConfirmationDialog from "@/components/modals/ConfirmationDialog";
|
|
import { useCustomFetch } from "@/utils/customFetch";
|
|
|
|
function NewMailings() {
|
|
const customFetch = useCustomFetch();
|
|
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 [confirmDialogOpen, setConfirmDialogOpen] = useState<boolean>(false);
|
|
const [mailingToCancel, setMailingToCancel] = useState<Mailing | null>(null);
|
|
|
|
const columns: GridColDef<Mailing>[] = [
|
|
{
|
|
field: "actions",
|
|
headerName: "",
|
|
sortable: false,
|
|
width: 100,
|
|
renderCell: (params: GridRenderCellParams<Mailing>) => (
|
|
<>
|
|
<IconButton color="primary" onClick={(e) => { e.stopPropagation(); handleEdit(params.row); }}>
|
|
<EditIcon />
|
|
</IconButton>
|
|
<IconButton color="secondary" onClick={(e) => { e.stopPropagation(); handleCancelClick(params.row); }}>
|
|
<CancelIcon />
|
|
</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 customFetch("/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);
|
|
};
|
|
|
|
const handleCancelClick = (row: Mailing) => {
|
|
setMailingToCancel(row);
|
|
setConfirmDialogOpen(true);
|
|
};
|
|
|
|
const handleCancelConfirm = async () => {
|
|
if (!mailingToCancel) return;
|
|
|
|
try {
|
|
const response = await customFetch(`/api/mailings/${mailingToCancel.id}/cancel`, { method: 'POST' });
|
|
if (response.ok) {
|
|
setMailings((prev) => prev.filter(m => m.id !== mailingToCancel.id));
|
|
} else {
|
|
console.error("Failed to cancel mailing");
|
|
}
|
|
} catch (error) {
|
|
console.error("Error cancelling mailing:", error);
|
|
} finally {
|
|
setConfirmDialogOpen(false);
|
|
setMailingToCancel(null);
|
|
}
|
|
};
|
|
|
|
const handleCancelDialogClose = () => {
|
|
setConfirmDialogOpen(false);
|
|
setMailingToCancel(null);
|
|
};
|
|
|
|
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>
|
|
<IconButton color="secondary" onClick={() => handleCancelClick(row)}>
|
|
<CancelIcon />
|
|
</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}
|
|
/>
|
|
)}
|
|
{confirmDialogOpen && (
|
|
<ConfirmationDialog
|
|
open={confirmDialogOpen}
|
|
title="Cancel Mailing"
|
|
message={`Are you sure you want to cancel the mailing "${mailingToCancel?.name}"? This action cannot be undone.`}
|
|
onConfirm={handleCancelConfirm}
|
|
onCancel={handleCancelDialogClose}
|
|
/>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
export default NewMailings; |