53 | Repeat
54 | Service
55 | Welcome Back! Enter in a previous estimateID (we put it in your email confirmation) and chose a date for us to come again..
56 |
57 |
62 | {({values, errors, touched, handleBlur, isValid, handleInputChange}) => (
63 |
98 | )}
99 |
100 |
101 |
102 | );
103 | }
104 |
105 | export default RepeatService;
--------------------------------------------------------------------------------
/src/components/UserDetail.js:
--------------------------------------------------------------------------------
1 |
2 | import React, { useContext, useState, useEffect } from 'react';
3 | import { useNavigate } from 'react-router-dom';
4 | import '../styles/Estimates.css';
5 | import EstimateContext from '../context/estimate';
6 | import LocationCreate from './LocationCreate';
7 | import Calendar from "./Calendar";
8 | import {
9 | Typography, Card, CardContent, ThemeProvider, CssBaseline, Button
10 | } from '@mui/material';
11 | import { RapidCleanTheme } from "../themes/Theme.js";
12 |
13 | const UserDetail = () => {
14 | const { estimate, user, location, findLocationByUserId } = useContext(EstimateContext);
15 | const [bookingConfirmed, setBookingConfirmed] = useState(false); // Track if booking is confirmed
16 | const [bookingId, setBookingId] = useState(null); // Track the booking ID
17 | const navigate = useNavigate();
18 |
19 | const checkLocation = async () => {
20 | if (user.hasOwnProperty("userDetails")) {
21 | try {
22 | const foundLocation = await findLocationByUserId(user.userId);
23 | //console.log('[UserDetail] foundLocation: ', foundLocation);
24 | if (!foundLocation) {
25 | console.log('[UserDetail] did not find location.');
26 | }
27 | } catch (error) {
28 | //console.error('Error checking location:', error);
29 | }
30 | }
31 | };
32 |
33 | // Call checkLocation whenever user state changes
34 | useEffect(() => {
35 | if (user && user.hasOwnProperty('userDetails')) {
36 | checkLocation();
37 | }
38 | }, [user]);
39 |
40 | // Function to handle booking completion from Calendar component
41 | const handleBookingComplete = (bookingId) => {
42 | setBookingId(bookingId);
43 | setBookingConfirmed(true);
44 | };
45 |
46 | const handleGoHome = () => {
47 | //navigate('/'); // Navigate back to home page
48 | // reset brownser with window
49 | window.location.href = '/'; // This will navigate to the home page and refresh the browser
50 | };
51 |
52 | let content =
Loading...
;
53 |
54 | // If user exists but no location, prompt for location
55 | if (user.hasOwnProperty("userDetails") && !location.hasOwnProperty("locationdetails")) {
56 | content = (
57 |
58 | Hi {user.userDetails.firstname}, can you please provide the address for the estimate:
59 |
60 |
61 | );
62 | }
63 | // If user and location exist, allow them to choose a date or show confirmation message
64 | else if (user.hasOwnProperty("userDetails") && location.hasOwnProperty("locationdetails")) {
65 | content = (
66 | <>
67 |
68 | {bookingConfirmed
69 | ? `Congratulations, ${user.userDetails.firstname}!`
70 | : `Thank you ${user.userDetails.firstname}!`}
71 |
72 |
73 |
74 | {bookingConfirmed
75 | ? 'Your booking has been confirmed! Please check your email. Important to note that Gmail may block requests from our calendar unless you approve us a known contact.'
76 | : `Please choose a date for service of your ${estimate.servicedetails.typeofservice} of your ${estimate.servicedetails.numrooms} BR, ${estimate.servicedetails.numbaths} BA ${estimate.servicedetails.construct}.`}
77 |
78 |
79 |
80 |
81 |
82 | {bookingConfirmed
83 | ? `Thank you for booking! Your estimate ID (${estimate.estimateId}) has been saved for future use.`
84 | : `We have anonymously registered you. Don't worry, your data is safe with us.`}
85 |
86 |
87 | {bookingConfirmed && (
88 |
91 | )}
92 | >
93 | );
94 | }
95 |
96 | return (
97 |
98 |
99 |
100 |
101 |
102 | Your
103 |
104 |
105 | Details
106 |
107 | {content}
108 |
109 |
110 |
111 | );
112 | };
113 |
114 | export default UserDetail;
115 |
--------------------------------------------------------------------------------
/src/pages/Contact.js:
--------------------------------------------------------------------------------
1 | import '../styles/Contact.css'
2 | import React from "react";
3 | import {
4 | Card,
5 | Grid,
6 | Box,
7 | Typography, ThemeProvider, CssBaseline, Button, CardContent, Link
8 | } from '@mui/material';
9 | import { RapidCleanTheme } from "../themes/Theme.js";
10 | import {useContext} from "react";
11 | import EstimateContext from "../context/estimate";
12 |
13 |
14 | const Contact = () => {
15 | const {estimate} = useContext(EstimateContext);
16 | //console.log('[Contact] estimate ', estimate);
17 | let content;
18 | // if we have a booking
19 | // if we have an estimate, a user and a location but not a booking
20 | // if we have an estimate and a user but no location
21 | // if we have an estimate only
22 | // if we have nothing
23 |
24 | if (estimate.hasOwnProperty("servicedetails")) {
25 | content =
{estimate.servicedetails.cost.total}
26 | } else {
27 | content =
28 |
34 |
35 | }
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {/**/}
48 | {/* /!*Chat *!/*/}
49 | {/* /!*With Us*!/*/}
50 | {/* Chat With */}
51 | {/* Us*/}
52 | {/* Chat with a Customer Service Assistant 24/7. This is the fastest way to contact us.*/}
53 | {/* {content} */}
54 | {/**/}
55 |
56 | Email
57 | Us
58 | We'd love to hear from you, you can email us anytime.
59 | rapidcleanering@gmail.com.
60 | Text
61 | Us
62 | Text us, if you prefer.
63 | 212-555-1212.
64 | Call
65 | Us
66 | Call us between 9-5 M-F. We will try to return your call as quickly as possible.
67 | 212-555-1212
68 | Follow
69 | Us
70 | Stay up to date with our social to latest services and offers!
71 |
72 | {/*Instagram:*/}
73 |
74 | {/**/}
75 | Follow Us on Instagram!
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | )
92 | }
93 | export default Contact;
--------------------------------------------------------------------------------
/src/components/EstimateItem.js:
--------------------------------------------------------------------------------
1 | import React, {useContext, useState} from 'react'
2 | import '../styles/Estimates.css';
3 | import EstimateContext from '../context/estimate';
4 | import { useNavigate } from 'react-router-dom';
5 | import EstimateChip from './EstimateChip';
6 |
7 | import {
8 | Typography, Grid, Box, Button, Chip,
9 | ThemeProvider, CssBaseline, Card,
10 | CardHeader, CardContent, CardActions,
11 | FormControl, Select, MenuItem, InputLabel,
12 | Checkbox,FormControlLabel, FormGroup,
13 | } from '@mui/material';
14 | import { RapidCleanTheme } from "../themes/Theme.js";
15 |
16 | const EstimateItem = (estimate) => {
17 |
18 | // create a string in US Currency for Chip
19 | const convertEstimateForDisplay = (total) => {
20 | return total.toLocaleString('en-US', {
21 | style: 'currency',
22 | currency: 'USD',
23 | });
24 | } /* $2,500.00 */
25 |
26 |
27 | // content is the markup that is displayed in the browser depending on the state of the estimate
28 | let content =
loading
29 |
30 | // IF we have an estimate with the right data structure but the user as asked to edit it
31 | if (estimate.hasOwnProperty("servicedetails")){
32 | content = <>
33 | {/**/}
34 | {estimate.id}, | {estimate.servicedetails.typeofservice} |
35 | {estimate.servicedetails.numrooms} BR | {estimate.servicedetails.numbaths} BA | {estimate.servicedetails.construct}
36 |
37 | >
38 | // IF we dont have an estimate with the correct data structure we act as if we have nothing at all
39 | } else if (!estimate.hasOwnProperty("servicedetails")) {
40 | //console.log('[EstimateDetail] (else if) estimate.servicedetails', estimate.servicedetails);
41 | content =
42 | <>
43 | There are no details about your service yet
44 | waiting..
45 | >
46 |
47 | }
48 |
49 | // else {
50 | // // Otherwise we assume we have the estimate with the service details so we display it
51 | // // check to make sure we have extras, pro and pet services. for each we need a headline so this gets a bit clunky
52 | // let renderedExtrasHeader = <>>
53 | // let renderedProHeader = <>>
54 | // let renderedPetHeader = <>>
55 | //
56 | //
57 | //
58 | //
59 | // //console.log('[EstimateDetail] (else... showDetails) estimate.servicedetails', estimate.servicedetails);
60 | //
61 | //
62 | //
63 | //
64 | // content = <>
65 | //
66 | // For a {estimate.servicedetails.typeofservice} of
67 | // your {estimate.servicedetails.numrooms} BR, {estimate.servicedetails.numbaths} BA {estimate.servicedetails.construct}
68 | //
69 | //
70 | //
71 | //
Type of Service: {estimate.servicedetails.typeofservice}
72 | //
Zoned: {estimate.servicedetails.construct}
73 | //
Occupants: {estimate.servicedetails.numpeople}
74 | //
Number of Bedrooms: {estimate.servicedetails.numrooms}
75 | //
Number of Bathrooms: {estimate.servicedetails.numbaths}
76 | //
Square Feet: {estimate.servicedetails.sqft}
77 | //
Number of Pets: {estimate.servicedetails.numpets}
78 | //
Clutter: {estimate.servicedetails.cleanfactor}
79 | //
80 | //
81 | //
82 | // {renderedExtrasHeader}
83 | //
{renderedExtras}
84 | // {renderedProHeader}
85 | //
{renderedPro}
86 | // {renderedPetHeader}
87 | //
{renderedPet}
88 | //
89 | //
90 | //
91 | //
92 | // Your Temporary user ID: {estimate.servicedetails.userID}
93 | //
94 | //
95 | // Your Personalized Estimate ID: {estimate.id}
96 | //
97 | //
98 | // This highly tailored, hassle-free estimate is only one part of our stellar service. Click NEXT to Book a
99 | // date.
100 | //
101 | //
102 | //
103 | //
104 | //
105 | // If you need to make any changes to your estimate, please use the edit details button. Do not hit the back arrow or refresh the browser.
106 | //
107 | //
108 | // {/* for the user in future cards */}
109 | // {/* Please note we expect to take about {estimate.servicedetails.data.totalhours} hours to complete the {estimate.servicedetails.typeofservice} */}
110 | // {/**/}
111 | // {/* Your anonymous user name is {estimate.servicedetails.userID} and your estimate number is: {estimate.id}.*/}
112 | // {/**/}
113 | //
114 | // {/*
Data for cleaners
*/}
115 | // {/*
total hours: {estimate.servicedetails.data.totalhours}
*/}
116 | // {/*
total time all rooms: {estimate.servicedetails.data.totaltimerooms}
*/}
117 | // {/*
total time all baths: {estimate.servicedetails.data.totaltimebaths}
39 |
40 |
41 | // IF we have an estimate with the right data structure but not one from the user
42 | // SHOW USER CREATE
43 | if ((estimate.hasOwnProperty("servicedetails") && (!user.hasOwnProperty("userDetails")))) {
44 | // search by userID before we ask for their information
45 | // IF you are coming from Appointments and you wish to enter in a previous appointment
46 | // we will do a quick check of the database to see if the user exists
47 | const estimatetosearch = estimate.servicedetails.userID;
48 | console.log('[Appointments] estimatetosearch: ' + estimatetosearch);
49 | // this sends a function to the provider to find the user and setUser which will change the conditions to show the address field locate4d in the UserDetail
50 | // todo refactor to use a custom useEffect hook with memo and fetch the data
51 | const foundExistingUser = findUserByUserId(estimatetosearch);
52 | if (foundExistingUser) {
53 | // and if the user exists lets check to see if the Location Exists
54 | console.log('[Appointments] foundExistingUser: ' + foundExistingUser);
55 | console.log('[Appointments] USER DETAIL will check for Location and decide what to show')
56 | }
57 | content = <>
58 |
59 | Enter
60 | Information
61 | You are one step closer to enjoying a pristine space with our {estimate.servicedetails.typeofservice} service, featuring expert vacuuming of carpets and floors, precise dusting of every corner, and efficient mopping for a flawless finish..
62 |
63 | In order for us to hold a {estimate.servicedetails.typeofservice} of
64 | your {estimate.servicedetails.numrooms} BR, {estimate.servicedetails.numbaths} BA {estimate.servicedetails.construct} for ${estimate.servicedetails.cost.total} will need some information.
65 |
66 | {/*
67 | Your Anonymous user ID: {estimate.servicedetails.userID}
68 | */}
69 | {/**/}
70 | {/* Your Personalized Estimate ID: {estimate.id} */}
71 | {/**/}
72 |
73 | {/*Select a Date:*/}
74 |
75 |
76 | >
77 | // IF wehave an estimate with the correct data structure AND we have the user data
78 | // SHOW USER DETAIL
79 | // TODO get the views right when you have a user we should see the userdetail screen welcoming them and asking for their address
80 | } else if ((estimate.hasOwnProperty("servicedetails") && (user.hasOwnProperty("userDetails")))) {
81 | content =
82 |
83 | } else {
84 | //console.log('[EstimateDetail] (else if) estimate.servicedetails', estimate.servicedetails);
85 | content =
86 | <>
87 |
88 | New
89 | Service
90 |
91 | First we give you an INSTANT hassle-free estimate, then you choose a date and time.
92 | {/**/}
93 | {/*
83 |
84 |
85 | Please Enter Your Information to preserve this Estimate. You can book anytime after this step.
86 |
87 |
92 | {({values, errors, touched, handleBlur, isValid, handleInputChange}) => (
93 |
170 | )}
171 |
172 |
61 |
62 | // IF we have an estimate with the right data structure but the user as asked to edit it
63 | if ((hasServiceDetails) && (showEdit)){
64 | content = <>
65 | {/**/}
66 | For a {estimate.servicedetails.typeofservice} of
67 | your {estimate.servicedetails.numrooms} BR, {estimate.servicedetails.numbaths} BA {estimate.servicedetails.construct}
68 |
69 |
70 |
71 | >
72 | // IF we dont have an estimate with the correct data structure we act as if we have nothing at all
73 | } else if (!hasServiceDetails) {
74 | //console.log('[EstimateDetail] (else if) estimate.servicedetails', estimate.servicedetails);
75 | content =
76 | <>
77 | There are no details about your service yet
78 | waiting..
79 | >
80 |
81 | } else {
82 | // Otherwise we assume we have the estimate with the service details so we display it
83 | // check to make sure we have extras, pro and pet services. for each we need a headline so this gets a bit clunky
84 | let renderedExtrasHeader = <>>
85 | let renderedProHeader = <>>
86 | let renderedPetHeader = <>>
87 | if (estimate.servicedetails.extraservices.length > 0) {
88 | renderedExtrasHeader =
89 | <>
90 | Extra
91 | Services
92 | >
93 | }
94 |
95 | if (estimate.servicedetails.proservices.length > 0) {
96 | renderedProHeader =
97 | <>
98 | Pro
99 | Services
100 | >
101 | }
102 |
103 | if (estimate.servicedetails.petservices.length > 0) {
104 | renderedPetHeader =
105 | <>
106 | Pet
107 | Services
108 | >
109 | }
110 | //console.log('[EstimateDetail] (else... showDetails) estimate.servicedetails', estimate.servicedetails);
111 | const renderedExtras = estimate.servicedetails.extraservices.map((extra) => {
112 | return
121 | });
122 |
123 | content = <>
124 |
125 | For a {estimate.servicedetails.typeofservice} of
126 | your {estimate.servicedetails.numrooms} BR, {estimate.servicedetails.numbaths} BA {estimate.servicedetails.construct}
127 |
128 |
129 |
130 |
Type of Service: {estimate.servicedetails.typeofservice}
131 |
Zoned: {estimate.servicedetails.construct}
132 |
Occupants: {estimate.servicedetails.numpeople}
133 |
Number of Bedrooms: {estimate.servicedetails.numrooms}
134 |
Number of Bathrooms: {estimate.servicedetails.numbaths}
135 |
Square Feet: {estimate.servicedetails.sqft}
136 |
Number of Pets: {estimate.servicedetails.numpets}
137 |
Clutter: {estimate.servicedetails.cleanfactor}
138 |
139 |
140 |
141 | {renderedExtrasHeader}
142 |
{renderedExtras}
143 | {renderedProHeader}
144 |
{renderedPro}
145 | {renderedPetHeader}
146 |
{renderedPet}
147 |
148 |
149 | {/* temporary user info for testing.. too early to show user their information*/}
150 | {/**/}
151 | {/* Your Temporary user ID: {estimate.servicedetails.userID}*/}
152 | {/**/}
153 | {/**/}
154 | {/* Your Personalized Estimate ID: {estimate.estimateId} */}
155 | {/**/}
156 |
157 | This highly tailored, hassle-free estimate is only one part of our stellar service. Click NEXT to choose a date for your {estimate.servicedetails.typeofservice}.
158 |
159 |
160 |
161 |
162 |
163 | If you need to make any changes to your estimate, please use the edit details button. Do not hit the back arrow or refresh the browser or you will lose your custom estimate.
164 |
165 |
166 | {/* for the user in future cards */}
167 | {/* Please note we expect to take about {estimate.servicedetails.data.totalhours} hours to complete the {estimate.servicedetails.typeofservice} */}
168 | {/**/}
169 | {/* Your anonymous user name is {estimate.servicedetails.userID} and your estimate number is: {estimate.id}.*/}
170 | {/**/}
171 |
172 | {/*
Data for cleaners
*/}
173 | {/*
total hours: {estimate.servicedetails.data.totalhours}
*/}
174 | {/*
total time all rooms: {estimate.servicedetails.data.totaltimerooms}
*/}
175 | {/*
total time all baths: {estimate.servicedetails.data.totaltimebaths}