Skip to content

Instantly share code, notes, and snippets.

@abel-masila
Created January 11, 2023 22:31
Show Gist options
  • Save abel-masila/f7666d782e3ee85d6004157247bc2f3f to your computer and use it in GitHub Desktop.
Save abel-masila/f7666d782e3ee85d6004157247bc2f3f to your computer and use it in GitHub Desktop.
/* eslint-disable no-unused-expressions */
import React, { useState, useEffect } from "react";
import {
AppBar,
Button,
Card,
Dialog,
DialogActions,
DialogContent,
FormControl,
FormControlLabel,
FormLabel,
IconButton,
InputLabel,
MenuItem,
Radio,
RadioGroup,
Select,
Slide,
Snackbar,
Step,
StepContent,
StepLabel,
Stepper,
TextField,
Toolbar,
Typography,
} from "@mui/material";
// import { DatePicker } from '@mui/x-date-pickers-pro/DatePicker';
// // or
// import { DatePicker } from '@mui/x-date-pickers/DatePicker';
// // or
// import { DatePicker } from '@mui/x-date-pickers-pro';
// // or
import { DatePicker } from "@mui/x-date-pickers";
import { ListSubheader } from "@mui/material";
// import {RadioGroup} from '@mui/material'
// import { RaisedButton } from "@mui/material";
// import { FlatButton } from "@mui/material";
import moment from "moment";
// import { moment } from "@mui/material/moment";
// import { DatePicker, DateTimePicker } from "@mui/x-date-pickers";
// import { Dialog } from "@mui/material";
// import { SelectField } from "@mui/material";
// import { MenuItem } from "@mui/material";
// import { TextField } from "@mui/material";
// import { SnackBar } from "@mui/material";
// import { Card } from "@mui/material";
// import { Button } from "@mui/material";
// import {
// Stepper,
// StepperClassKey,
// StepLabel,
// StepContent,
// } from "@mui/material/Stepper";
// import { RadioGroup } from "@mui/material";
import axios from "axios";
import { Box } from "@mui/system";
// import { DatePickerToolbar } from "@mui/x-date-pickers/DatePicker/DatePickerToolbar";
// import { response } from "../../../api";
const AppointmentApp = () => {
const API_BASE = "http://localhost:8800";
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [email, setEmail] = useState("");
const [phone, setPhone] = useState("");
const [schedule, setSchedule] = useState([]);
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
const [appointmentDateSelected, setAppointmentDateSelected] = useState(false);
const [appointmentMeridiem, setAppointmentMeridiem] = useState("");
const [validEmail, setValidEmail] = useState(true);
const [validPhone, setValidPhone] = useState(true);
const [finished, setFinished] = useState(false);
const [smallScreen, setSmallScreen] = useState(window.innerWidth < 768);
const [stepIndex, setStepIndex] = useState(0);
const [appointmentDate, setAppointmentDate] = useState(Date.now());
const [appointmentSlot, setAppointmentSlot] = useState("");
const [confirmationTextVisible, setConfirmationTextVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [processed, setProcessed] = useState(false);
const [confirmationSnackbarOpen, setConfirmationSnackbarOpen] =
useState(false);
const [confirmationSnackbarMessage, setConfirmationSnackbarMessage] =
useState(" ");
const fetchData = async () => {
const { data } = await axios.get(API_BASE + `/retrieveSlots`);
console.log("response via db: ", data);
handleDBReponse(data);
};
useEffect(() => {
fetchData();
}, []);
// MOVING THE STEPPER TO THE NEXT AND PREVIOUS POSITION
const handleNext = () => {
setStepIndex((prev) => prev + 1);
setFinished((prev) => prev >= 2);
};
const handlePrev = () => {
if (stepIndex > 0) {
setStepIndex((prev) => prev - 1);
}
};
// VALIDATING PHONE NUMBER AND EMAILS
const validateEmail = (email) => {
const regex =
/^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\].,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
return regex.test(email)
? (setEmail(email), setValidEmail(true))
: setValidEmail(false);
};
const validatePhone = (phoneNumber) => {
const regex = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/;
return regex.test(phoneNumber)
? (setPhone(phone), setValidPhone(true))
: this.setState({ validPhone: false });
};
// NEXT, FINISHED AND BACK BUTTONS
const renderStepActions = (step) => {
return (
<div style={{ margin: "12px 0" }}>
<Button
variant="contained"
label={stepIndex === 2 ? "Finish" : "Next"}
disableTouchRipple={true}
disableFocusRipple={true}
primary={true}
onClick={handleNext}
backgroundColor="#00C853 !important"
style={{ marginRight: 12, backgroundColor: "#00C853" }}
>
Continue
</Button>
{step > 0 && (
<Button
label="Back"
disabled={stepIndex === 0}
disableTouchRipple={true}
disableFocusRipple={true}
onClick={handlePrev}
style={{ marginRight: 12, backgroundColor: "#d8C893" }}
>
Back
</Button>
)}
</div>
);
};
// set state of the appointmentDate field
const handleSetAppointmentDate = (date) => {
setAppointmentDate(date);
setConfirmationTextVisible(true);
setValue(date);
};
// set state of the appointment slot visible
const handleSetAppointmentSlot = (event) => {
setAppointmentSlot(event.target.value);
};
// set the state for the appointmentMeridiem field
const handleSetAppointmentMeridiem = (event) => {
setAppointmentMeridiem(event.target.value);
};
// PASS DATES TO THE DATE PICKER COMPONENT
const checkDisableDate = (day) => {
const dateString = moment(day).format("YYYY-DD-MM");
return (
schedule[dateString] === true ||
moment(day).startOf("day").diff(moment().startOf("day")) < 0
);
};
// HANDLE THE APPOINTMENT SLOT DATA FROM THE DATABASE
const handleDBReponse = (response) => {
const appointments = response;
const today = moment().startOf("day"); //start of today 12 am
const initialSchedule = {};
initialSchedule[today.format("YYYY-DD-MM")] = true;
const schedule = !appointments.length
? initialSchedule
: appointments.reduce((currentSchedule, appointment) => {
const { slot_date, slot_time } = appointment;
const dateString = moment(slot_date, "YYYY-DD-MM").format(
"YYYY-DD-MM"
);
!currentSchedule[slot_date]
? (currentSchedule[dateString] = Array(8).fill(false))
: null;
Array.isArray(currentSchedule[dateString])
? (currentSchedule[dateString][slot_time] = true)
: null;
return currentSchedule;
}, initialSchedule);
for (let day in schedule) {
let slots = schedule[day];
slots.length
? slots.every((slot) => slot === true)
? (schedule[day] = true)
: null
: null;
}
setSchedule(schedule);
};
// DISPLAY THE AVAILABLE TIME SLOTS AND DISABLE THE REST
const renderAppointmentTimes = () => {
// if (!isLoading) {
if (true) {
const slotss = [...Array(8).keys()];
// const slots = ["8am - 10am", "10am - 12pm"];
const slots = slotss.filter(function (mov) {
return mov % 2 !== 0;
});
console.log("slotssssssssssssssssss", slotss);
console.log("slotssssssssssssssssss", slots);
return slots.map((slot) => {
// slot = slot + 1;
console.log(
"Monemt appointment date ???????????????????????????",
moment(appointmentDate).format("YYYY-DD-MM")
);
const appointmentDateString =
moment(appointmentDate).format("YYYY-DD-MM");
console.log(
"appointmentDateString>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
appointmentDateString
);
const time1 = moment()
.hour(6)
.minute(0)
.add(slot + 2, "hours");
// const time2 = moment()
// .hour(8)
// .minute(0)
// .add(slot + 2, "hours");
const scheduleDisabled = schedule[appointmentDateString]
? schedule[moment(appointmentDate).format("YYYY-DD-MM")][slot]
: false;
return (
<FormControlLabel
label={time1.format("h:mm a")}
// label={time1.format("h:mm a") + " - " + time2.format("h:mm a")}
key={slot}
value={slot}
control={<Radio />}
style={{
marginBottom: 15,
// display: meridiemDisabled ? "none" : "inherit",
}}
disabled={scheduleDisabled}
/>
);
});
} else {
return null;
}
};
// DISPLAY USER INFORMATION CONFIRMATION BEFORE SAVING TO DATABASE
const renderAppointmentConfirmation = () => {
console.log("firstname>>>>>>>>>>>>>>>>>>>>>", firstName);
const spanStyle = { color: "#00C853" };
return (
<section>
<p>
Name: {firstName}
{/* <span style={spanStyle}>
{firstName} {lastName}
</span> */}
</p>
<p>
Number: <span style={spanStyle}>{phone}</span>
</p>
<p>
Email: <span style={spanStyle}>{email}</span>
</p>
<p>
Appointment:{" "}
<span style={spanStyle}>
{moment(appointmentDate).format("dddd[,] MMMM Do[,] YYYY")}
</span>{" "}
at{" "}
<span style={spanStyle}>
{moment()
.hour(9)
.minute(0)
.add(appointmentSlot, "hours")
.format("h:mm a")}
</span>
</p>
</section>
);
};
// PASS DATA TO DATABASE - EXPRESS APP, DISPLAY SUCCESS/FAIL MESSAGE
const handleSubmit = () => {
setOpen(false);
const newAppointment = {
name: firstName + " " + lastName,
email: email,
phone: phone,
slot_date: moment(appointmentDate).format("YYYY-DD-MM"),
slot_time: appointmentSlot,
};
axios
.post(API_BASE + "/appointmentCreate", newAppointment)
.then((response) => {
console.log(response);
setConfirmationSnackbarMessage("Appointment succesfully added!");
setConfirmationSnackbarOpen(true);
setProcessed(true);
// setIsLoading(true);
})
.catch((err) => {
console.log(err);
// ( setConfirmationSnackbarMessage("Appointment failed to save "),
// setConfirmationSnackbarOpen(true));
});
};
// RENDERING ................................
// const [...data] = useState;
let contactFormFilled;
if (firstName !== "" && lastName !== "" && phone !== "" && email !== "") {
contactFormFilled = true;
} else {
contactFormFilled = false;
}
// && validPhone && validEmail;
const [value, setValue] = useState("");
const [timeOfDay, setTimeOfDay] = useState("");
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const DatePickerExampleSimple = () => (
<div>
{/* <DatePicker
label="Select Date"
mode={smallScreen ? "portrait" : "landscape"}
onChange={(n, date) => handleSetAppointmentDate(date)}
shouldDisableDate={(day) => checkDisableDate(day)}
/> */}
<DatePicker
label="Select Date"
value={value}
// onChange={(newValue) => {
// setValue(newValue);
// }}
onChange={(date) => handleSetAppointmentDate(date)}
shouldDisableDate={(day) => checkDisableDate(day)}
renderInput={(params) => <TextField {...params} />}
/>
</div>
);
const modalActions = [
// FlatButton
<Button label="Cancel" primary={false} onClick={() => setOpen(false)} />,
<Button
label="Confirm"
style={{ backgroundColor: "#00C853 !important" }}
primary={true}
onClick={() => handleSubmit()}
/>,
];
return (
<div>
{/* <AppBar
title="Appointment Scheduler"
iconClassNameRight="muidocs-icon-navigation-expand-more"
/> */}
<AppBar position="static">
<Toolbar variant="dense">
<IconButton
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
>
<h1>Icon</h1>
</IconButton>
<Typography variant="h6" color="inherit" component="div">
Booking
</Typography>
</Toolbar>
</AppBar>
<section
style={{
maxWidth: !smallScreen ? "80%" : "100%",
margin: "auto",
marginTop: !smallScreen ? 20 : 0,
}}
>
<Card
style={{
padding: "12px 12px 25px 12px",
height: smallScreen ? "100vh" : null,
}}
>
<Stepper
activeStep={stepIndex}
// alternativeLabel
orientation="vertical"
// orientation="vertical" linear={false}
>
<Step>
<StepLabel>
Choose an available day for your appointment
</StepLabel>
<StepContent>
{DatePickerExampleSimple()}
{renderStepActions(0)}
</StepContent>
</Step>
{/* check for date disable */}
<Step disabled={!appointmentDate}>
<StepLabel>
Choose an available time for your appointment
</StepLabel>
<StepContent>
{/* <Box sx={{ minWidth: 100 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Time</InputLabel>
<Select
value={appointmentMeridiem}
label="Time"
onChange={handleSetAppointmentMeridiem}
// selectionRenderer={(value) => (value ? "PM" : "AM")}
>
<MenuItem value={1}> AM</MenuItem>
<MenuItem value={2}> PM</MenuItem>
</Select>
</FormControl>
</Box> */}
{/* ///////////////////////////////// */}
<FormControl>
{/* <FormLabel id="demo-radio-buttons-group-label">
Choose Appointment Slot
</FormLabel> */}
<RadioGroup
style={{
marginTop: 15,
marginLeft: 15,
}}
name="appointmentTimes"
// defaultSelected={appointmentSlot}
onChange={handleSetAppointmentSlot}
>
{renderAppointmentTimes()}
</RadioGroup>
</FormControl>
{renderStepActions(1)}
</StepContent>
</Step>
<Step>
<StepLabel>contact information/Booking Details</StepLabel>
<StepContent>
<Box
component="form"
sx={{
"& .MuiTextField-root": { m: 1, width: "55ch" },
}}
noValidate
autoComplete="off"
>
<div>
<TextField
// style={{ display: "block" }}
name="first_name"
label="FirstName"
multiline
maxRows={4}
id="outlined-error"
placeholder="First Name"
onChange={(event) => setFirstName(event.target.value)}
/>
<TextField
// style={{ display: "block" }}
name="last_name"
label="Last Name"
multiline
placeholder="Last Name"
maxRows={4}
onChange={(event) => setLastName(event.target.value)}
/>
</div>
<div>
<TextField
name="email"
placeholder="youraddress@mail.com"
multiline
maxRows={4}
label="Email"
id="outlined-error"
// error
// helperText={
// validEmail ? null : "Enter a valid email address"
// }
onChange={(event) => setEmail(event.target.value)}
/>
<TextField
name="phone"
placeholder="+2547******34/07******34"
label="Phone"
// errorText={
// validPhone ? null : "Enter a valid phone number"
// }
onChange={(event) => setPhone(event.target.value)}
/>
</div>
<Button
style={{ display: "block", backgroundColor: "#00C853" }}
variant="raised"
// variant={
// contactFormFilled
// ? "Schedule"
// : "Fill out your information to schedule"
// }
labelPosition="before"
primary={true}
fullWidth={true}
onClick={() => setOpen(!open)}
disabled={!contactFormFilled || processed}
// style={{ marginTop: 20, maxWidth: 100 }}
>
{contactFormFilled
? "Confirm your Info"
: "Fill out your information to schedule"}
</Button>
</Box>
{renderStepActions(2)}
</StepContent>
</Step>
</Stepper>
</Card>
{/* //////////////////////// */}
<Dialog
TransitionComponent={Transition}
open={open}
keepMounted
onClose={handleClose}
// actions={modalActions}
// title="Confirm your appointment"
aria-describedby="Confirm your appointment"
>
<DialogContent>{renderAppointmentConfirmation()}</DialogContent>
<DialogActions>
<Button
variant="outlined"
label="Cancel"
primary={false}
onClick={() => setOpen(false)}
>
{" "}
Cancel{" "}
</Button>
,
<Button
variant="outlined"
// label="Confirm"
style={{ backgroundColor: "#00C853 !important" }}
// primary={true}
onClick={() => handleSubmit()}
>
{" "}
Confirm{" "}
</Button>
,
</DialogActions>
</Dialog>
<Snackbar
open={confirmationSnackbarOpen || isLoading}
message={
isLoading ? "Loading... " : confirmationSnackbarMessage || ""
}
autoHideDuration={10000}
// onRequestClose={() =>
// this.setState({ confirmationSnackbarOpen: false })
// setConfirmationSnackbarOpen(false)
// }
/>
</section>
</div>
);
};
export default AppointmentApp;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment