| | |
| | | import React, { useState, useRef, useEffect, useMemo } from "react"; |
| | | import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material'; |
| | | import React, { useMemo } from "react"; |
| | | import { Box, Card, CardContent, Stack, Typography, Divider } from '@mui/material'; |
| | | import { |
| | | useTranslate, |
| | | useRecordContext, |
| | | } from 'react-admin'; |
| | | import PanelTypography from "../components/PanelTypography"; |
| | | import * as Common from '@/utils/common' |
| | | |
| | | const LanePanel = () => { |
| | | const record = useRecordContext(); |
| | | if (!record) return null; |
| | | const translate = useTranslate(); |
| | | return ( |
| | | <> |
| | | <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}> |
| | | <CardContent> |
| | | <Grid container spacing={2}> |
| | | <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}> |
| | | <Typography variant="h6" gutterBottom align="left" sx={{ |
| | | maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' }, |
| | | whiteSpace: 'nowrap', |
| | | overflow: 'hidden', |
| | | textOverflow: 'ellipsis', |
| | | }}> |
| | | {Common.camelToPascalWithSpaces(translate('table.field.lane.uuid'))}: {record.uuid} |
| | | </Typography> |
| | | {/* inherit, primary, secondary, textPrimary, textSecondary, error */} |
| | | <Typography variant="h6" gutterBottom align="right" > |
| | | ID: {record.id} |
| | | </Typography> |
| | | </Grid> |
| | | </Grid> |
| | | <Grid container spacing={2}> |
| | | <Grid item xs={12} container alignContent="flex-end"> |
| | | <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}> |
| | | {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo} |
| | | </Typography> |
| | | </Grid> |
| | | </Grid> |
| | | <Box height={20}> </Box> |
| | | <Grid container spacing={2}> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.uuid" |
| | | property={record.uuid} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.zoneId" |
| | | property={record.zoneId$} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.name" |
| | | property={record.name} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.hashCode" |
| | | property={record.hashCode} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.codes" |
| | | property={record.codes} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.entryAngle" |
| | | property={record.entryAngle} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6}> |
| | | <PanelTypography |
| | | title="table.field.lane.maximum" |
| | | property={record.maximum} |
| | | /> |
| | | </Grid> |
| | | const rawCodes = record.codes; |
| | | const codeFontFamily = `'JetBrains Mono','Fira Code','SFMono-Regular','Roboto Mono',monospace`; |
| | | |
| | | </Grid> |
| | | </CardContent> |
| | | </Card > |
| | | </> |
| | | const codes = useMemo(() => { |
| | | if (!rawCodes) return []; |
| | | |
| | | if (Array.isArray(rawCodes)) { |
| | | return rawCodes.filter((item) => !!item); |
| | | } |
| | | |
| | | if (typeof rawCodes === 'string') { |
| | | try { |
| | | const parsed = JSON.parse(rawCodes); |
| | | if (Array.isArray(parsed)) { |
| | | return parsed.filter((item) => !!item); |
| | | } |
| | | } catch (error) { |
| | | // ignore json parse error and fallback below |
| | | } |
| | | |
| | | return rawCodes |
| | | .split(',') |
| | | .map((item) => item.replace(/[\[\]"]/g, '').trim()) |
| | | .filter((item) => !!item); |
| | | } |
| | | |
| | | return []; |
| | | }, [rawCodes]); |
| | | |
| | | return ( |
| | | <Card |
| | | sx={{ |
| | | width: 'min(960px, 92vw)', |
| | | mx: 'auto', |
| | | my: 0.5, |
| | | borderRadius: 3, |
| | | boxShadow: (theme) => theme.shadows[6], |
| | | border: '1px solid', |
| | | borderColor: 'divider', |
| | | background: (theme) => |
| | | theme.palette.mode === 'dark' |
| | | ? 'linear-gradient(135deg, rgba(255,255,255,0.04), rgba(255,255,255,0.01))' |
| | | : 'linear-gradient(135deg, rgba(15,23,42,0.02), rgba(15,23,42,0.06))', |
| | | }} |
| | | > |
| | | <CardContent sx={{ p: { xs: 3, md: 4 } }}> |
| | | <Stack spacing={3}> |
| | | <Box |
| | | display="flex" |
| | | justifyContent="space-between" |
| | | alignItems={{ xs: 'flex-start', sm: 'center' }} |
| | | flexDirection={{ xs: 'column', sm: 'row' }} |
| | | gap={1.5} |
| | | > |
| | | <Stack spacing={0.5}> |
| | | <Typography |
| | | variant="overline" |
| | | color="text.secondary" |
| | | sx={{ letterSpacing: 2, fontWeight: 600 }} |
| | | > |
| | | {translate('table.field.lane.codes')} |
| | | </Typography> |
| | | <Typography variant="body2" sx={{ fontWeight: 700, letterSpacing: 0.5 }}> |
| | | {translate('table.field.lane.hashCode')} · {record.hashCode} |
| | | </Typography> |
| | | </Stack> |
| | | <Box |
| | | sx={{ |
| | | px: 2, |
| | | py: 0.75, |
| | | borderRadius: 999, |
| | | fontWeight: 600, |
| | | letterSpacing: 1, |
| | | bgcolor: 'primary.main', |
| | | color: 'primary.contrastText', |
| | | textTransform: 'uppercase', |
| | | boxShadow: (theme) => theme.shadows[3], |
| | | }} |
| | | > |
| | | {translate('common.field.count')}: {codes.length} |
| | | </Box> |
| | | </Box> |
| | | <Divider flexItem /> |
| | | {codes.length > 0 ? ( |
| | | <Box |
| | | sx={{ |
| | | display: 'grid', |
| | | gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))', |
| | | gap: 1.5, |
| | | maxHeight: 420, |
| | | overflowY: 'auto', |
| | | p: 3, |
| | | }} |
| | | > |
| | | {codes.map((code, idx) => ( |
| | | <Box |
| | | key={`${code}-${idx}`} |
| | | sx={{ |
| | | px: 2.25, |
| | | py: 1.25, |
| | | borderRadius: 2, |
| | | border: '1px solid', |
| | | borderColor: 'grey.200', |
| | | background: |
| | | 'linear-gradient(120deg, rgba(148,163,184,0.18), rgba(148,163,184,0.05))', |
| | | fontFamily: codeFontFamily, |
| | | // fontWeight: 600, |
| | | letterSpacing: 0.8, |
| | | fontSize: '0.95rem', |
| | | textAlign: 'center', |
| | | color: 'text.primary', |
| | | transition: 'all .25s ease', |
| | | boxShadow: (theme) => theme.shadows[1], |
| | | '&:hover': { |
| | | transform: 'translateY(-4px) scale(1.01)', |
| | | boxShadow: (theme) => theme.shadows[4], |
| | | borderColor: 'primary.main', |
| | | background: |
| | | 'linear-gradient(135deg, rgba(59,130,246,0.18), rgba(59,130,246,0.05))', |
| | | }, |
| | | }} |
| | | > |
| | | {code} |
| | | </Box> |
| | | ))} |
| | | </Box> |
| | | ) : ( |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('ra.navigation.no_results')} |
| | | </Typography> |
| | | )} |
| | | </Stack> |
| | | </CardContent> |
| | | </Card> |
| | | ); |
| | | }; |
| | | |