| | |
| | | import React, { useState, useRef, useEffect, useMemo } from "react"; |
| | | import React, { useMemo } from "react"; |
| | | import { |
| | | Edit, |
| | | SimpleForm, |
| | | FormDataConsumer, |
| | | useTranslate, |
| | | TextInput, |
| | | NumberInput, |
| | | BooleanInput, |
| | | DateInput, |
| | | SelectInput, |
| | | ReferenceInput, |
| | | ReferenceArrayInput, |
| | | AutocompleteInput, |
| | | SaveButton, |
| | | Toolbar, |
| | | Labeled, |
| | | NumberField, |
| | | required, |
| | | useRecordContext, |
| | | DeleteButton, |
| | | } from 'react-admin'; |
| | | import { useWatch, useFormContext } from "react-hook-form"; |
| | | import { Stack, Grid, Box, Typography } from '@mui/material'; |
| | | import * as Common from '@/utils/common'; |
| | | import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting'; |
| | | import { Stack, Grid, Box, Typography, Paper, Divider } from '@mui/material'; |
| | | import { EDIT_MODE } from '@/config/setting'; |
| | | import EditBaseAside from "../components/EditBaseAside"; |
| | | import CustomerTopToolBar from "../components/EditTopToolBar"; |
| | | import MemoInput from "../components/MemoInput"; |
| | | import StatusSelectInput from "../components/StatusSelectInput"; |
| | | |
| | | const FormToolbar = () => { |
| | | const { getValues } = useFormContext(); |
| | | |
| | | return ( |
| | | <Toolbar sx={{ justifyContent: 'space-between' }}> |
| | | <SaveButton /> |
| | |
| | | } |
| | | |
| | | const LaneEdit = () => { |
| | | const translate = useTranslate(); |
| | | |
| | | return ( |
| | | <Edit |
| | | redirect="list" |
| | |
| | | defaultValues={{}} |
| | | // validate={(values) => { }} |
| | | > |
| | | <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}> |
| | | <Grid item xs={12} md={8}> |
| | | <Typography variant="h6" gutterBottom> |
| | | {translate('common.edit.title.main')} |
| | | </Typography> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.lane.uuid" |
| | | source="uuid" |
| | | parse={v => v} |
| | | autoFocus |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <ReferenceInput |
| | | source="zoneId" |
| | | reference="zone" |
| | | perPage={REFERENCE_INPUT_PAGESIZE} |
| | | > |
| | | <AutocompleteInput |
| | | label="table.field.lane.zoneId" |
| | | optionText="name" |
| | | filterToQuery={(val) => ({ name: val })} |
| | | /> |
| | | </ReferenceInput> |
| | | </Stack> |
| | | {/* <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.lane.name" |
| | | source="name" |
| | | parse={v => v} |
| | | /> |
| | | </Stack> */} |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.lane.hashCode" |
| | | source="hashCode" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | | label="table.field.lane.codes" |
| | | source="codes" |
| | | parse={v => v} |
| | | validate={required()} |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.lane.entryAngle" |
| | | source="entryAngle" |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.lane.maximum" |
| | | source="maximum" |
| | | /> |
| | | </Stack> |
| | | |
| | | </Grid> |
| | | <Grid item xs={12} md={4}> |
| | | <Typography variant="h6" gutterBottom> |
| | | {translate('common.edit.title.common')} |
| | | </Typography> |
| | | <StatusSelectInput /> |
| | | <Box mt="2em" /> |
| | | <MemoInput /> |
| | | </Grid> |
| | | </Grid> |
| | | <LaneEditContent /> |
| | | </SimpleForm> |
| | | </Edit > |
| | | ) |
| | | } |
| | | |
| | | const LaneEditContent = () => { |
| | | const translate = useTranslate(); |
| | | const record = useRecordContext(); |
| | | const codes = useMemo(() => { |
| | | const source = record?.codes; |
| | | if (!source) return []; |
| | | |
| | | if (Array.isArray(source)) { |
| | | return source.filter(Boolean); |
| | | } |
| | | |
| | | if (typeof source === 'string') { |
| | | try { |
| | | const parsed = JSON.parse(source); |
| | | if (Array.isArray(parsed)) { |
| | | return parsed.filter(Boolean); |
| | | } |
| | | } catch (error) { |
| | | // fallback to comma split |
| | | } |
| | | |
| | | return source |
| | | .split(',') |
| | | .map((item) => item.replace(/[\[\]"]/g, '').trim()) |
| | | .filter(Boolean); |
| | | } |
| | | |
| | | return []; |
| | | }, [record?.codes]); |
| | | const codesCount = codes.length; |
| | | |
| | | if (!record) return null; |
| | | |
| | | return ( |
| | | <Box |
| | | sx={{ |
| | | width: '100%', |
| | | maxWidth: { xs: '100%', md: 800 }, |
| | | mx: { xs: 'auto', md: 0 }, |
| | | mr: { md: 'auto' }, |
| | | display: 'flex', |
| | | flexDirection: 'column', |
| | | gap: 3, |
| | | }} |
| | | > |
| | | <Paper |
| | | elevation={0} |
| | | sx={{ |
| | | p: 3, |
| | | borderRadius: 3, |
| | | border: '1px solid', |
| | | borderColor: 'divider', |
| | | background: (theme) => |
| | | theme.palette.mode === 'dark' |
| | | ? 'rgba(255,255,255,0.02)' |
| | | : 'rgba(15,23,42,0.02)', |
| | | }} |
| | | > |
| | | <Stack spacing={1}> |
| | | <Typography variant="overline" color="text.secondary" sx={{ letterSpacing: 2 }}> |
| | | {translate('menu.lane')} |
| | | </Typography> |
| | | <Typography variant="h5" sx={{ fontWeight: 600 }}> |
| | | {record.name || record.uuid} |
| | | </Typography> |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('table.field.lane.hashCode')}: {record.hashCode || '-'} |
| | | </Typography> |
| | | <Stack direction="row" spacing={2} flexWrap="wrap" pt={2}> |
| | | {/* <Typography variant="body2" color="text.secondary"> |
| | | ID: {record.id} |
| | | </Typography> */} |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('common.field.count')}: {codesCount} |
| | | </Typography> |
| | | </Stack> |
| | | <Stack spacing={1.5} mt={5}> |
| | | {/* <Typography variant="subtitle2" color="text.secondary"> |
| | | {translate('table.field.lane.codes')} |
| | | </Typography> */} |
| | | <Box |
| | | sx={{ |
| | | display: 'flex', |
| | | flexWrap: 'wrap', |
| | | gap: 1, |
| | | maxHeight: 160, |
| | | overflowY: 'auto', |
| | | pt: 1, |
| | | }} |
| | | > |
| | | {codes.length ? ( |
| | | codes.map((code, idx) => ( |
| | | <Box |
| | | key={`${code}-${idx}`} |
| | | sx={{ |
| | | px: 1.5, |
| | | py: 0.5, |
| | | borderRadius: 2, |
| | | border: '1px solid', |
| | | borderColor: 'divider', |
| | | fontFamily: `'JetBrains Mono','Fira Code','SFMono-Regular','Roboto Mono',monospace`, |
| | | fontSize: '.9rem', |
| | | backgroundColor: 'background.paper', |
| | | }} |
| | | > |
| | | {code} |
| | | </Box> |
| | | )) |
| | | ) : ( |
| | | <Typography variant="body2" color="text.secondary"> |
| | | {translate('ra.navigation.no_results')} |
| | | </Typography> |
| | | )} |
| | | </Box> |
| | | </Stack> |
| | | </Stack> |
| | | </Paper> |
| | | |
| | | <Paper |
| | | elevation={0} |
| | | sx={{ |
| | | p: { xs: 3, md: 4 }, |
| | | borderRadius: 3, |
| | | border: '1px solid', |
| | | borderColor: 'divider', |
| | | backgroundColor: 'background.paper', |
| | | boxShadow: (theme) => theme.shadows[2], |
| | | }} |
| | | > |
| | | <Stack spacing={2}> |
| | | <Box display="flex" justifyContent="space-between" alignItems="center"> |
| | | <Typography variant="h6" sx={{ fontWeight: 600 }}> |
| | | {translate('common.edit.title.main')} |
| | | </Typography> |
| | | </Box> |
| | | <Divider /> |
| | | <Grid container spacing={3}> |
| | | <Grid item xs={12} md={6}> |
| | | <Stack spacing={1}> |
| | | {/* <Typography variant="subtitle2" color="text.secondary"> |
| | | {translate('table.field.lane.entryAngle')} |
| | | </Typography> */} |
| | | <NumberInput |
| | | label={"table.field.lane.entryAngle"} |
| | | source="entryAngle" |
| | | fullWidth |
| | | helperText={translate('page.lane.helper.entryAngle')} |
| | | /> |
| | | </Stack> |
| | | </Grid> |
| | | <Grid item xs={12} md={6}> |
| | | <Stack spacing={1}> |
| | | {/* <Typography variant="subtitle2" color="text.secondary"> |
| | | {translate('table.field.lane.maximum')} |
| | | </Typography> */} |
| | | <NumberInput |
| | | label={"table.field.lane.maximum"} |
| | | source="maximum" |
| | | fullWidth |
| | | helperText={translate('page.lane.helper.maximum')} |
| | | /> |
| | | </Stack> |
| | | </Grid> |
| | | </Grid> |
| | | </Stack> |
| | | </Paper> |
| | | </Box> |
| | | ); |
| | | }; |
| | | |
| | | export default LaneEdit; |