Refactor App and EmbeddedActiveEquipmentPage for theme support
- Renamed the main component from `App` to `ToirAdmin` for clarity. - Integrated theme support using `useEmbeddedParentTheme` to dynamically adjust the UI based on the parent theme. - Updated `EmbeddedActiveEquipmentPage` to utilize the new theme and improved styling for better user experience. - Added a new utility hook `useEmbeddedParentTheme` to manage theme changes via postMessage from parent origins. - Enhanced the `vite-env.d.ts` file to include environment variable definitions for parent origins.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Box from '@mui/material/Box';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Link from '@mui/material/Link';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
@@ -9,9 +11,12 @@ import TableContainer from '@mui/material/TableContainer';
|
||||
import TableHead from '@mui/material/TableHead';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useEmbeddedParentTheme } from '../embed/useEmbeddedParentTheme';
|
||||
import { env } from '../config/env';
|
||||
import { ensureFreshToken, getAccessToken } from '../auth/keycloak';
|
||||
import { downloadEquipmentAttachmentFile } from '../resources/equipment/attachmentDownload';
|
||||
|
||||
type EquipmentRecord = {
|
||||
id: string;
|
||||
@@ -19,11 +24,15 @@ type EquipmentRecord = {
|
||||
serialNumber: string;
|
||||
dateOfInspection: string | null;
|
||||
commissionedAt: string | null;
|
||||
attachment?: {
|
||||
objectKey?: string;
|
||||
originalFileName?: string | null;
|
||||
} | null;
|
||||
};
|
||||
|
||||
function formatDate(value: string | null) {
|
||||
if (!value) {
|
||||
return '-';
|
||||
return '—';
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
@@ -35,6 +44,24 @@ function formatDate(value: string | null) {
|
||||
}
|
||||
|
||||
export function EmbeddedActiveEquipmentPage() {
|
||||
const paletteMode = useEmbeddedParentTheme();
|
||||
const muiTheme = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
palette: { mode: paletteMode },
|
||||
components: {
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
backgroundImage: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
[paletteMode],
|
||||
);
|
||||
|
||||
const [data, setData] = useState<EquipmentRecord[]>([]);
|
||||
const [total, setTotal] = useState<number | null>(null);
|
||||
const [isPending, setIsPending] = useState(true);
|
||||
@@ -105,52 +132,98 @@ export function EmbeddedActiveEquipmentPage() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{ minHeight: '100vh', boxSizing: 'border-box', p: { xs: 2, md: 3 }, bgcolor: '#f3f6fa' }}>
|
||||
<Paper elevation={0} sx={{ overflow: 'hidden', borderRadius: 3, border: '1px solid #d7e0ea' }}>
|
||||
<Box sx={{ px: 3, py: 2, borderBottom: '1px solid #d7e0ea', bgcolor: '#fff' }}>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: '#10233a' }}>
|
||||
Оборудование в эксплуатации
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mt: 0.5, color: '#5b7087' }}>
|
||||
Отображаются записи со статусом `Active`
|
||||
{typeof total === 'number' ? `: ${total}` : ''}
|
||||
</Typography>
|
||||
</Box>
|
||||
const pageBg =
|
||||
paletteMode === 'dark' ? 'linear-gradient(180deg, #0d1117 0%, #0a0c10 100%)' : '#f3f6fa';
|
||||
const headerBg = paletteMode === 'dark' ? '#161b22' : '#fff';
|
||||
const headerBorder = paletteMode === 'dark' ? '1px solid #30363d' : '1px solid #d7e0ea';
|
||||
const titleColor = paletteMode === 'dark' ? '#e6edf3' : '#10233a';
|
||||
const subtitleColor = paletteMode === 'dark' ? '#8b949e' : '#5b7087';
|
||||
|
||||
{isPending ? (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
|
||||
<CircularProgress />
|
||||
return (
|
||||
<ThemeProvider theme={muiTheme}>
|
||||
<CssBaseline />
|
||||
<Box
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
boxSizing: 'border-box',
|
||||
p: { xs: 2, md: 3 },
|
||||
bgcolor: pageBg,
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
elevation={0}
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
borderRadius: 3,
|
||||
border: headerBorder,
|
||||
bgcolor: paletteMode === 'dark' ? '#161b22' : '#fff',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ px: 3, py: 2, borderBottom: headerBorder, bgcolor: headerBg }}>
|
||||
<Typography variant="h5" sx={{ fontWeight: 700, color: titleColor }}>
|
||||
Оборудование в эксплуатации
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mt: 0.5, color: subtitleColor }}>
|
||||
Отображаются записи со статусом 'Active'
|
||||
{typeof total === 'number' ? `: ${total}` : ''}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : error ? (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Alert severity="error">Не удалось загрузить активное оборудование.</Alert>
|
||||
</Box>
|
||||
) : (
|
||||
<TableContainer>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Наименование</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Заводской номер</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Дата изготовления</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Дата поверки</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{(data ?? []).map((item) => (
|
||||
<TableRow key={item.id} hover>
|
||||
<TableCell>{item.name}</TableCell>
|
||||
<TableCell>{item.serialNumber}</TableCell>
|
||||
<TableCell>{formatDate(item.dateOfInspection)}</TableCell>
|
||||
<TableCell>{formatDate(item.commissionedAt)}</TableCell>
|
||||
|
||||
{isPending ? (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 8 }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
) : error ? (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Alert severity="error">Не удалось загрузить активное оборудование.</Alert>
|
||||
</Box>
|
||||
) : (
|
||||
<TableContainer>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Наименование</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Заводской номер</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Дата изготовления</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Дата поверки</TableCell>
|
||||
<TableCell sx={{ fontWeight: 700 }}>Файл</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{(data ?? []).map((item) => (
|
||||
<TableRow key={item.id} hover>
|
||||
<TableCell>{item.name}</TableCell>
|
||||
<TableCell>{item.serialNumber}</TableCell>
|
||||
<TableCell>{formatDate(item.commissionedAt)}</TableCell>
|
||||
<TableCell>{formatDate(item.dateOfInspection)}</TableCell>
|
||||
<TableCell>
|
||||
{item?.attachment?.objectKey ? (
|
||||
<Link
|
||||
component="button"
|
||||
type="button"
|
||||
underline="hover"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const label = item.attachment?.originalFileName?.trim() || 'файл';
|
||||
void downloadEquipmentAttachmentFile(item.id, label).catch(() => {});
|
||||
}}
|
||||
sx={{ cursor: 'pointer', verticalAlign: 'inherit' }}
|
||||
>
|
||||
{item.attachment?.originalFileName?.trim() || 'Скачать'}
|
||||
</Link>
|
||||
) : (
|
||||
'—'
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user