Skip to content

Commit

Permalink
feat/13: components for setting schedule on a task
Browse files Browse the repository at this point in the history
  • Loading branch information
duckbytes committed Dec 2, 2024
1 parent d91919a commit 3bf92c2
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 0 deletions.
120 changes: 120 additions & 0 deletions src/scenes/sharedTaskComponents/PickUpAndDeliverSchedule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as React from "react";
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Paper,
Stack,
Typography,
} from "@mui/material";
import TimeRelationPicker from "./TimeRelationPicker";
import ScheduledDatePicker from "./ScheduledDatePicker";

export enum ScheduledDatePickerOption {
TODAY = "Today",
TOMORROW = "Tomorrow",
CUSTOM = "Custom",
}

const PickUpAndDeliverSchedule = () => {
const [dialogOpen, setDialogOpen] = React.useState(false);

const [selectionPickUpState, setSelectionPickUpState] =
React.useState<ScheduledDatePickerOption | null>(null);
const [customPickUpDate, setCustomPickUpDate] = React.useState<Date | null>(
null
);

const handleSetPickUpState = (selection: ScheduledDatePickerOption) => {
if (selectionPickUpState === selection) setSelectionPickUpState(null);
else setSelectionPickUpState(selection);
if (
selection === ScheduledDatePickerOption.CUSTOM &&
customPickUpDate === null
)
setCustomPickUpDate(new Date());
};

const [selectionDropOffState, setSelectionDropOffState] =
React.useState<ScheduledDatePickerOption | null>(null);
const [customDropOffDate, setCustomDropOffDate] =
React.useState<Date | null>(null);

const handleSetDropOffState = (selection: ScheduledDatePickerOption) => {
if (selectionDropOffState === selection) setSelectionDropOffState(null);
else setSelectionDropOffState(selection);
if (
selection === ScheduledDatePickerOption.CUSTOM &&
customDropOffDate === null
)
setCustomDropOffDate(new Date());
};

return (
<>
<Stack
direction="row"
sx={{ width: "100%" }}
justifyContent="space-between"
alignItems="center"
>
<Typography>No scheduled set.</Typography>
<Button onClick={() => setDialogOpen(true)}>
Add schedule
</Button>
</Stack>
<Dialog open={dialogOpen}>
<DialogTitle>Add schedule</DialogTitle>
<DialogContent>
<Stack spacing={2}>
<Paper
sx={{
display: "flex",
gap: 1,
flexDirection: "column",
padding: 2,
borderRadius: "1em",
}}
>
<Typography variant="h6">Pick up</Typography>
<ScheduledDatePicker
onSelectOption={handleSetPickUpState}
option={selectionPickUpState}
onSelectCustomDate={setCustomPickUpDate}
customDate={customPickUpDate}
/>
{selectionPickUpState && <TimeRelationPicker />}
</Paper>
<Paper
sx={{
display: "flex",
gap: 1,
flexDirection: "column",
padding: 2,
borderRadius: "1em",
}}
>
<Typography variant="h6">Drop off</Typography>
<ScheduledDatePicker
onSelectOption={handleSetDropOffState}
option={selectionDropOffState}
onSelectCustomDate={setCustomDropOffDate}
customDate={customDropOffDate}
/>
{selectionDropOffState && <TimeRelationPicker />}
</Paper>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={() => setDialogOpen(false)}>Cancel</Button>
<Button onClick={() => setDialogOpen(false)}>OK</Button>
</DialogActions>
</Dialog>
</>
);
};

export default PickUpAndDeliverSchedule;
69 changes: 69 additions & 0 deletions src/scenes/sharedTaskComponents/ScheduledDatePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from "react";
import { Chip, Box, TextField, Stack } from "@mui/material";
import { DatePicker } from "@mui/lab";
import { ScheduledDatePickerOption } from "./PickUpAndDeliverSchedule";

type ScheduledDatePickerProps = {
onSelectOption: (selection: ScheduledDatePickerOption) => void;
option: ScheduledDatePickerOption | null;
onSelectCustomDate: (date: Date | null) => void;
customDate?: Date | null;
};

const ScheduledDatePicker: React.FC<ScheduledDatePickerProps> = ({
onSelectOption,
option,
onSelectCustomDate,
customDate,
}) => {
const isToday = option === ScheduledDatePickerOption.TODAY;
const isTomorrow = option === ScheduledDatePickerOption.TOMORROW;
const isCustom = option === ScheduledDatePickerOption.CUSTOM;

const handleSetToday = () => {
onSelectOption(ScheduledDatePickerOption.TODAY);
};

const handleSetTomorrow = () => {
onSelectOption(ScheduledDatePickerOption.TOMORROW);
};

const handleSetCustom = () => {
onSelectOption(ScheduledDatePickerOption.CUSTOM);
};

return (
<Stack spacing={1}>
<Box sx={{ display: "flex", direction: "row", gap: 1 }}>
<Chip
variant={isToday ? "filled" : "outlined"}
label="Today"
onClick={handleSetToday}
color={isToday ? "primary" : "default"}
/>
<Chip
variant={isTomorrow ? "filled" : "outlined"}
label="Tomorrow"
onClick={handleSetTomorrow}
color={isTomorrow ? "primary" : "default"}
/>
<Chip
variant={isCustom ? "filled" : "outlined"}
color={isCustom ? "primary" : "default"}
label="Custom"
onClick={handleSetCustom}
/>
</Box>
{isCustom && (
<DatePicker
inputFormat={"dd/MM/yyyy"}
value={customDate}
onChange={onSelectCustomDate}
renderInput={(params) => <TextField {...params} />}
/>
)}
</Stack>
);
};

export default ScheduledDatePicker;
67 changes: 67 additions & 0 deletions src/scenes/sharedTaskComponents/TimePickerBasic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { useState } from "react";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";

type TimePickerBasicProps = {
value: string;
onChange: (value: string) => void;
isValid: boolean;
};

const TimePickerBasic: React.FC<TimePickerBasicProps> = ({
value,
onChange,
isValid,
}) => {
// Generate time options (same as before)
const timeOptions = generateTimeOptions();

const handleInputChange = (_: any, newInputValue: string) => {
// Regular expression to allow only numbers and colons, with a maximum length of 5
const validInput = newInputValue.replace(/[^0-9:]/g, "").slice(0, 5);
onChange(validInput);
};

return (
<Autocomplete
fullWidth
freeSolo
id="time-picker-basic"
options={timeOptions}
getOptionLabel={(option) => option}
value={value}
inputValue={value} // Make sure inputValue is controlled
onInputChange={handleInputChange}
onChange={(_, newValue) => {
if (typeof newValue === "string") {
onChange(newValue);
}
}}
renderInput={(params) => (
<TextField
{...params}
helperText={!isValid ? "Invalid time" : ""}
error={!isValid}
label="Time"
variant="outlined"
/>
)}
/>
);
};

// Helper function to generate time options
function generateTimeOptions() {
const options = [];
for (let hour = 0; hour < 24; hour++) {
for (let minute = 0; minute < 60; minute += 30) {
const timeString = `${hour.toString().padStart(2, "0")}:${minute
.toString()
.padStart(2, "0")}`;
options.push(timeString); // Store time strings directly in the array
}
}
return options;
}

export default TimePickerBasic;
52 changes: 52 additions & 0 deletions src/scenes/sharedTaskComponents/TimeRelationPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as React from "react";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import * as models from "../../models";
import { Stack } from "@mui/material";
import TimePickerBasic from "./TimePickerBasic";

const TimeRelationPicker = () => {
const [relation, setRelation] = React.useState<models.TimeRelation>(
models.TimeRelation.ANYTIME
);
const [time, setTime] = React.useState("10:00");

const handleChange = (event: SelectChangeEvent) => {
setRelation(event.target.value as models.TimeRelation);
};

const isValidTime = (time: string) => {
const [hours, minutes] = time
.split(":")
.map((value) => parseInt(value));
return hours >= 0 && hours < 24 && minutes >= 0 && minutes < 60;
};

const isValid = isValidTime(time);

const handleChangeTime = (value: string) => {
setTime(value);
};

return (
<Stack spacing={1} direction="row" sx={{ minWidth: 340 }}>
<FormControl fullWidth>
<Select value={relation} onChange={handleChange}>
{Object.values(models.TimeRelation).map((timeRelation) => (
<MenuItem value={timeRelation}>{timeRelation}</MenuItem>
))}
</Select>
</FormControl>
{relation !== models.TimeRelation.ANYTIME && (
<TimePickerBasic
isValid={isValid}
onChange={handleChangeTime}
value={time}
/>
)}
</Stack>
);
};

export default TimeRelationPicker;

0 comments on commit 3bf92c2

Please sign in to comment.