341 lines
9.7 KiB
TypeScript
341 lines
9.7 KiB
TypeScript
import { Button } from "antd";
|
|
import dayjs from "dayjs";
|
|
import { useMemo, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import Picker from "components/WheelPicker";
|
|
import { SERVER_DATE_FORMAT } from "utils/constants.ts";
|
|
|
|
interface PickupEstimateContentProps {
|
|
onSave: (date: string, time: string) => void;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export default function PickupEstimateContent({
|
|
onSave,
|
|
onClose,
|
|
}: PickupEstimateContentProps) {
|
|
const { t } = useTranslation();
|
|
|
|
// Generate day options: Today, Tomorrow, then formatted dates (90 days total for scrolling)
|
|
const dayOptions = useMemo(() => {
|
|
const options = [];
|
|
const today = dayjs();
|
|
|
|
// Today
|
|
options.push({
|
|
value: "today",
|
|
label: "Today",
|
|
date: today.format(SERVER_DATE_FORMAT),
|
|
});
|
|
|
|
// Tomorrow
|
|
options.push({
|
|
value: "tomorrow",
|
|
label: "Tomorrow",
|
|
date: today.add(1, "day").format(SERVER_DATE_FORMAT),
|
|
});
|
|
|
|
// Next days with formatted dates (up to 90 days total for smooth scrolling)
|
|
for (let i = 2; i < 90; i++) {
|
|
const date = today.add(i, "day");
|
|
const formatted = date.toDate().toLocaleDateString("en-US", {
|
|
weekday: "long",
|
|
month: "long",
|
|
day: "numeric",
|
|
});
|
|
options.push({
|
|
value: `day-${i}`,
|
|
label: formatted,
|
|
date: date.format(SERVER_DATE_FORMAT),
|
|
});
|
|
}
|
|
|
|
return options;
|
|
}, []);
|
|
|
|
// Generate time options: Now, then time slots (5 items total)
|
|
const timeOptions = useMemo(() => {
|
|
const options = [];
|
|
const now = dayjs();
|
|
|
|
// Now option
|
|
options.push({
|
|
value: "now",
|
|
label: "Now",
|
|
time: now.format("HH:mm"),
|
|
});
|
|
|
|
// Calculate next 15-minute interval (always the next one, even if we're on a boundary)
|
|
const currentMinute = now.minute();
|
|
let nextMinute = Math.floor(currentMinute / 15) * 15 + 15;
|
|
let nextHour = now.hour();
|
|
|
|
if (nextMinute >= 60) {
|
|
nextMinute = 0;
|
|
nextHour += 1;
|
|
if (nextHour >= 24) {
|
|
nextHour = 0;
|
|
}
|
|
}
|
|
|
|
// Generate next 4 time slots (15-minute intervals)
|
|
let hour = nextHour;
|
|
let minute = nextMinute;
|
|
|
|
for (let i = 0; i < 96; i++) {
|
|
const time = dayjs().hour(hour).minute(minute).second(0);
|
|
const formatted = time.format("h:mm A");
|
|
|
|
options.push({
|
|
value: `time-${i}`,
|
|
label: formatted,
|
|
time: time.format("HH:mm"),
|
|
});
|
|
|
|
// Calculate next interval
|
|
minute += 15;
|
|
if (minute >= 60) {
|
|
minute = 0;
|
|
hour += 1;
|
|
if (hour >= 24) {
|
|
hour = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return options;
|
|
}, []);
|
|
|
|
const [selectedDay, setSelectedDay] = useState<string>("today");
|
|
const [selectedTime, setSelectedTime] = useState<string>("now");
|
|
|
|
const handlePickerChange = (value: { day?: string; time?: string }) => {
|
|
console.log(value);
|
|
if (value.day !== undefined) {
|
|
setSelectedDay(value.day);
|
|
}
|
|
if (value.time !== undefined) {
|
|
setSelectedTime(value.time);
|
|
}
|
|
};
|
|
|
|
const handleSave = () => {
|
|
const selectedDayOption = dayOptions.find(
|
|
(opt) => opt.value === selectedDay,
|
|
);
|
|
const selectedTimeOption = timeOptions.find(
|
|
(opt) => opt.value === selectedTime,
|
|
);
|
|
|
|
if (selectedDayOption && selectedTimeOption) {
|
|
onSave(selectedDayOption.date, selectedTimeOption.time);
|
|
onClose();
|
|
}
|
|
};
|
|
|
|
const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleTouchEnd = (e: React.TouchEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleMouseUp = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const handleWheel = (e: React.WheelEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
style={{
|
|
marginTop: 30,
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
}}
|
|
onTouchStart={handleTouchStart}
|
|
onTouchMove={handleTouchMove}
|
|
onTouchEnd={handleTouchEnd}
|
|
onMouseDown={handleMouseDown}
|
|
onMouseMove={handleMouseMove}
|
|
onMouseUp={handleMouseUp}
|
|
onWheel={handleWheel}
|
|
>
|
|
{/* Day and Time Pickers - Same Row */}
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
flexDirection: "row",
|
|
gap: "8px",
|
|
marginBottom: 30,
|
|
}}
|
|
>
|
|
{/* Day Picker */}
|
|
<div style={{ width: "60%" }}>
|
|
<Picker
|
|
value={{ day: selectedDay }}
|
|
onChange={(value) => handlePickerChange(value)}
|
|
width="100%"
|
|
height={250}
|
|
style={{
|
|
position: "relative",
|
|
display: "flex",
|
|
backgroundColor: "var(--secondary-background)",
|
|
overflow: "hidden",
|
|
width: "100%",
|
|
height: 250,
|
|
border: "none",
|
|
borderRadius: "8px",
|
|
}}
|
|
aria-selected={false}
|
|
>
|
|
<Picker.Column
|
|
name="day"
|
|
style={{
|
|
flex: 1,
|
|
backgroundColor: "var(--secondary-background)",
|
|
}}
|
|
>
|
|
{dayOptions.map((option) => (
|
|
<Picker.Item key={option.value} value={option.value}>
|
|
{({ selected }) => (
|
|
<div
|
|
style={{
|
|
fontWeight: selected ? "600" : "400",
|
|
color: selected
|
|
? "var(--foreground)"
|
|
: "var(--text-color-gray)",
|
|
fontSize: "16px",
|
|
padding: "9px 0",
|
|
textAlign: "center",
|
|
opacity: selected ? 1 : 0.7,
|
|
transition: "all 0.2s ease",
|
|
lineHeight: "1.2",
|
|
height: 36,
|
|
width: "100%",
|
|
backgroundColor: selected
|
|
? "var(--background)"
|
|
: "transparent",
|
|
borderRadius: selected ? "4px" : "0",
|
|
margin: selected ? "2px 4px" : "0",
|
|
}}
|
|
>
|
|
{option.label}
|
|
</div>
|
|
)}
|
|
</Picker.Item>
|
|
))}
|
|
</Picker.Column>
|
|
</Picker>
|
|
</div>
|
|
|
|
{/* Time Picker */}
|
|
<div style={{ width: "40%" }}>
|
|
<Picker
|
|
value={{ time: selectedTime }}
|
|
onChange={(value) => handlePickerChange(value)}
|
|
width="100%"
|
|
height={250}
|
|
style={{
|
|
position: "relative",
|
|
display: "flex",
|
|
backgroundColor: "var(--secondary-background)",
|
|
overflow: "hidden",
|
|
width: "100%",
|
|
height: 250,
|
|
border: "none",
|
|
borderRadius: "8px",
|
|
}}
|
|
aria-selected={false}
|
|
>
|
|
<Picker.Column
|
|
name="time"
|
|
style={{
|
|
flex: 1,
|
|
backgroundColor: "var(--secondary-background)",
|
|
}}
|
|
>
|
|
{timeOptions.map((option) => (
|
|
<Picker.Item key={option.value} value={option.value}>
|
|
{({ selected }) => (
|
|
<div
|
|
style={{
|
|
fontWeight: selected ? "600" : "400",
|
|
color: selected
|
|
? "var(--foreground)"
|
|
: "var(--text-color-gray)",
|
|
fontSize: "16px",
|
|
padding: "9px 0",
|
|
textAlign: "center",
|
|
opacity: selected ? 1 : 0.7,
|
|
transition: "all 0.2s ease",
|
|
lineHeight: "1.2",
|
|
height: 36,
|
|
width: "100%",
|
|
backgroundColor: selected
|
|
? "var(--background)"
|
|
: "transparent",
|
|
borderRadius: selected ? "4px" : "0",
|
|
margin: selected ? "2px 4px" : "0",
|
|
}}
|
|
>
|
|
{option.label}
|
|
</div>
|
|
)}
|
|
</Picker.Item>
|
|
))}
|
|
</Picker.Column>
|
|
</Picker>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Save Button */}
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
<Button
|
|
type="primary"
|
|
onClick={handleSave}
|
|
style={{
|
|
width: "100%",
|
|
height: 48,
|
|
fontSize: 16,
|
|
fontWeight: 600,
|
|
backgroundColor: "var(--primary)",
|
|
border: "none",
|
|
}}
|
|
>
|
|
{t("checkout.scheduled")}
|
|
</Button>
|
|
<Button
|
|
onClick={handleSave}
|
|
style={{
|
|
width: "100%",
|
|
height: 48,
|
|
fontSize: 16,
|
|
fontWeight: 600,
|
|
border: "1px solid ##C0BFC4",
|
|
}}
|
|
>
|
|
{t("checkout.pickupNow")}
|
|
</Button>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|