pickup time: intial commit
This commit is contained in:
340
src/pages/checkout/components/pickupEstimate/Content.tsx
Normal file
340
src/pages/checkout/components/pickupEstimate/Content.tsx
Normal file
@@ -0,0 +1,340 @@
|
||||
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 = 1; i < 48; 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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user