feat: replace TextField with MUI DatePicker and TimePicker in DateTimeFields

- Replace native HTML date/time inputs with MUI X Date Pickers components
- Add DatePicker for start/end dates
- Add TimePicker for start/end times
This commit is contained in:
lenhanphung
2025-10-27 16:33:47 +07:00
committed by Benoit TELLIER
parent 01f79553c0
commit 3c75a7e82e
@@ -1,5 +1,10 @@
import React from "react";
import { Box, TextField, Typography } from "@mui/material";
import { Box, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { TimePicker } from "@mui/x-date-pickers/TimePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
import moment, { Moment } from "moment";
/**
* Props for DateTimeFields component
@@ -40,104 +45,130 @@ export const DateTimeFields: React.FC<DateTimeFieldsProps> = ({
onEndTimeChange,
}) => {
return (
<Box display="flex" gap={1} flexDirection="column">
{/* First row: 4 fields */}
<Box display="flex" gap={1} flexDirection="row" alignItems="flex-start">
{/* Start Date - 30% */}
<Box sx={{ flexGrow: 0.3, flexBasis: "30%" }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
Start Date
</Typography>
)}
<TextField
fullWidth
label={!showMore ? "Start Date" : ""}
type="date"
value={startDate}
onChange={(e) => onStartDateChange(e.target.value)}
size="small"
margin="dense"
InputLabelProps={{ shrink: true }}
/>
</Box>
<LocalizationProvider dateAdapter={AdapterMoment} adapterLocale="en">
<Box display="flex" gap={1} flexDirection="column">
{/* First row: 4 fields */}
<Box display="flex" gap={1} flexDirection="row" alignItems="flex-start">
{/* Start Date - 30% */}
<Box sx={{ flexGrow: 0.3, flexBasis: "25%", minWidth: 0 }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
Start Date
</Typography>
)}
<DatePicker
label={!showMore ? "Start Date" : ""}
value={startDate ? moment(startDate) : null}
onChange={(newValue: Moment | null) => {
onStartDateChange(newValue?.format("YYYY-MM-DD") || "");
}}
slotProps={{
textField: {
size: "small",
margin: "dense" as const,
fullWidth: true,
InputLabelProps: { shrink: true },
sx: { width: "100%" },
},
}}
/>
</Box>
{/* Start Time - 20% */}
<Box sx={{ flexGrow: 0.2, flexBasis: "20%" }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
Start Time
</Typography>
)}
<TextField
fullWidth
label={!showMore ? "Start Time" : ""}
type="time"
value={startTime}
onChange={(e) => onStartTimeChange(e.target.value)}
disabled={allday}
size="small"
margin="dense"
InputLabelProps={{ shrink: true }}
/>
</Box>
{/* Start Time - 20% */}
<Box sx={{ flexGrow: 0.2, flexBasis: "25%", minWidth: 0 }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
Start Time
</Typography>
)}
<TimePicker
label={!showMore ? "Start Time" : ""}
value={startTime ? moment(startTime, "HH:mm") : null}
onChange={(newValue: Moment | null) => {
onStartTimeChange(newValue?.format("HH:mm") || "");
}}
disabled={allday}
slotProps={{
textField: {
size: "small",
margin: "dense" as const,
fullWidth: true,
InputLabelProps: { shrink: true },
sx: { width: "100%" },
},
}}
/>
</Box>
{/* End Time - 20% */}
<Box sx={{ flexGrow: 0.2, flexBasis: "20%" }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
End Time
</Typography>
)}
<TextField
fullWidth
label={!showMore ? "End Time" : ""}
type="time"
value={endTime}
onChange={(e) => onEndTimeChange(e.target.value)}
disabled={allday}
error={!!validation.errors.dateTime}
size="small"
margin="dense"
InputLabelProps={{ shrink: true }}
/>
</Box>
{/* End Time - 20% */}
<Box sx={{ flexGrow: 0.2, flexBasis: "25%", minWidth: 0 }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
End Time
</Typography>
)}
<TimePicker
label={!showMore ? "End Time" : ""}
value={endTime ? moment(endTime, "HH:mm") : null}
onChange={(newValue: Moment | null) => {
onEndTimeChange(newValue?.format("HH:mm") || "");
}}
disabled={allday}
slotProps={{
textField: {
size: "small",
margin: "dense" as const,
fullWidth: true,
InputLabelProps: { shrink: true },
error: !!validation.errors.dateTime,
sx: { width: "100%" },
},
}}
/>
</Box>
{/* End Date - 30% */}
<Box sx={{ flexGrow: 0.3, flexBasis: "30%" }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
End Date
</Typography>
)}
<TextField
fullWidth
label={!showMore ? "End Date" : ""}
type="date"
value={endDate}
onChange={(e) => onEndDateChange(e.target.value)}
error={!!validation.errors.dateTime}
size="small"
margin="dense"
InputLabelProps={{ shrink: true }}
/>
</Box>
</Box>
{/* Second row: Error message - 2 columns 50% each */}
{validation.errors.dateTime && (
<Box display="flex" gap={1} flexDirection="row">
{/* Empty left column - 50% */}
<Box sx={{ flexGrow: 0.5, flexBasis: "50%" }} />
{/* Error message right column - 50% */}
<Box sx={{ flexGrow: 0.5, flexBasis: "50%" }}>
<Typography variant="caption" color="error">
{validation.errors.dateTime}
</Typography>
{/* End Date - 30% */}
<Box sx={{ flexGrow: 0.3, flexBasis: "25%", minWidth: 0 }}>
{showMore && (
<Typography variant="caption" display="block" mb={0.5}>
End Date
</Typography>
)}
<DatePicker
label={!showMore ? "End Date" : ""}
value={endDate ? moment(endDate) : null}
onChange={(newValue: Moment | null) => {
onEndDateChange(newValue?.format("YYYY-MM-DD") || "");
}}
slotProps={{
textField: {
size: "small",
margin: "dense" as const,
fullWidth: true,
InputLabelProps: { shrink: true },
error: !!validation.errors.dateTime,
sx: { width: "100%" },
},
}}
/>
</Box>
</Box>
)}
</Box>
{/* Second row: Error message - 2 columns 50% each */}
{validation.errors.dateTime && (
<Box display="flex" gap={1} flexDirection="row">
{/* Empty left column - 50% */}
<Box sx={{ flexGrow: 0.5, flexBasis: "50%" }} />
{/* Error message right column - 50% */}
<Box sx={{ flexGrow: 0.5, flexBasis: "50%" }}>
<Typography variant="caption" sx={{ color: "error.main" }}>
{validation.errors.dateTime}
</Typography>
</Box>
</Box>
)}
</Box>
</LocalizationProvider>
);
};