187 lines
5.5 KiB
JavaScript
187 lines
5.5 KiB
JavaScript
import { DatePicker, Space, TimePicker } from "antd";
|
|
import PropTypes from "prop-types";
|
|
import { useCallback, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { connect } from "react-redux";
|
|
import { createStructuredSelector } from "reselect";
|
|
import { selectBodyshop } from "../../redux/user/user.selectors.js";
|
|
import dayjs from "../../utils/day";
|
|
import { fuzzyMatchDate } from "./formats.js";
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
bodyshop: selectBodyshop
|
|
});
|
|
|
|
const DateTimePicker = ({
|
|
value,
|
|
onChange,
|
|
onBlur,
|
|
id,
|
|
onlyFuture,
|
|
onlyToday,
|
|
isDateOnly = false,
|
|
isSeparatedTime = false,
|
|
bodyshop,
|
|
...restProps
|
|
}) => {
|
|
const [isManualInput, setIsManualInput] = useState(false);
|
|
const { t } = useTranslation();
|
|
|
|
const handleChange = useCallback(
|
|
(newDate) => {
|
|
if (onChange) {
|
|
onChange(bodyshop?.timezone && newDate ? dayjs(newDate).tz(bodyshop.timezone, true) : newDate);
|
|
}
|
|
setIsManualInput(false);
|
|
},
|
|
[onChange, bodyshop?.timezone]
|
|
);
|
|
|
|
const handleBlur = useCallback(
|
|
(e) => {
|
|
// Bail if this is not a manual input
|
|
if (!isManualInput) {
|
|
return;
|
|
}
|
|
// Reset manual input flag
|
|
setIsManualInput(false);
|
|
|
|
const v = e?.target?.value;
|
|
|
|
if (!v) return;
|
|
|
|
let parsedDate = isDateOnly ? fuzzyMatchDate(v)?.startOf("day") : fuzzyMatchDate(v);
|
|
|
|
if (parsedDate && onChange) {
|
|
onChange(parsedDate);
|
|
}
|
|
},
|
|
[isManualInput, isDateOnly, onChange]
|
|
);
|
|
|
|
const handleKeyDown = useCallback(
|
|
(e) => {
|
|
setIsManualInput(true);
|
|
|
|
if (e.key.toLowerCase() === "t" && onChange) {
|
|
e.preventDefault();
|
|
setIsManualInput(false);
|
|
onChange(dayjs());
|
|
} else if (e.key.toLowerCase() === "enter") {
|
|
handleBlur(e);
|
|
}
|
|
},
|
|
[onChange, handleBlur]
|
|
);
|
|
|
|
const handleDisabledDate = useCallback(
|
|
(current) => {
|
|
if (onlyToday) {
|
|
return !dayjs().isSame(current, "day");
|
|
} else if (onlyFuture) {
|
|
return dayjs().subtract(1, "day").isAfter(current);
|
|
}
|
|
return false;
|
|
},
|
|
[onlyToday, onlyFuture]
|
|
);
|
|
|
|
return (
|
|
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
|
|
{isSeparatedTime && (
|
|
<Space direction="vertical" style={{ width: "100%" }}>
|
|
<DatePicker
|
|
showTime={false}
|
|
format="MM/DD/YYYY"
|
|
value={value ? dayjs(value) : null}
|
|
onChange={(dateValue) => {
|
|
if (dateValue) {
|
|
// When date changes, preserve the existing time if it exists
|
|
if (value && dayjs(value).isValid()) {
|
|
const existingTime = dayjs(value);
|
|
const newDateTime = dayjs(dateValue)
|
|
.hour(existingTime.hour())
|
|
.minute(existingTime.minute())
|
|
.second(existingTime.second());
|
|
handleChange(newDateTime);
|
|
} else {
|
|
// If no existing time, just set the date without time
|
|
handleChange(dateValue);
|
|
}
|
|
} else {
|
|
handleChange(dateValue);
|
|
}
|
|
}}
|
|
placeholder={t("general.labels.date")}
|
|
onBlur={handleBlur}
|
|
disabledDate={handleDisabledDate}
|
|
isDateOnly={true}
|
|
{...restProps}
|
|
/>
|
|
{value && (
|
|
<TimePicker
|
|
format="hh:mm a"
|
|
minuteStep={15}
|
|
value={value && dayjs(value).hour() === 0 && dayjs(value).minute() === 0 ? null : dayjs(value)}
|
|
defaultOpenValue={dayjs(value)
|
|
.hour(dayjs().hour())
|
|
.minute(Math.floor(dayjs().minute() / 15) * 15)
|
|
.second(0)}
|
|
onChange={(timeValue) => {
|
|
if (timeValue) {
|
|
// When time changes, combine it with the existing date
|
|
const existingDate = dayjs(value);
|
|
const newDateTime = existingDate
|
|
.hour(timeValue.hour())
|
|
.minute(timeValue.minute())
|
|
.second(0);
|
|
handleChange(newDateTime);
|
|
} else {
|
|
// If time is cleared, just update with null time but keep date
|
|
handleChange(timeValue);
|
|
}
|
|
if (onBlur) onBlur();
|
|
}}
|
|
placeholder={t("general.labels.time")}
|
|
{...restProps}
|
|
/>
|
|
)}
|
|
</Space>
|
|
)}
|
|
{!isSeparatedTime && (
|
|
<DatePicker
|
|
showTime={
|
|
isDateOnly
|
|
? false
|
|
: {
|
|
format: "hh:mm a",
|
|
minuteStep: 15,
|
|
defaultValue: dayjs(dayjs(), "HH:mm:ss")
|
|
}
|
|
}
|
|
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
|
|
value={value ? dayjs(value) : null}
|
|
onChange={handleChange}
|
|
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
|
|
onBlur={onBlur || handleBlur}
|
|
disabledDate={handleDisabledDate}
|
|
{...restProps}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
DateTimePicker.propTypes = {
|
|
value: PropTypes.any,
|
|
onChange: PropTypes.func,
|
|
onBlur: PropTypes.func,
|
|
id: PropTypes.string,
|
|
onlyFuture: PropTypes.bool,
|
|
onlyToday: PropTypes.bool,
|
|
isDateOnly: PropTypes.bool,
|
|
isSeparatedTime: PropTypes.bool
|
|
};
|
|
|
|
export default connect(mapStateToProps, null)(DateTimePicker);
|