From 31ace63bc961bd901b24ed51bf0e3de44c89779c Mon Sep 17 00:00:00 2001 From: deb-cod <83533796+deb-cod@users.noreply.github.com> Date: Mon, 27 May 2024 18:54:42 +0530 Subject: [PATCH 1/2] distinguishable box single apicall avail event --- backend/test.js | 251 ++++++++++++++++++ frontend/frontend/src/App.js | 2 +- .../src/components/Availability/Avaiblity.js | 12 +- .../components/Availability/AvaiblityCopy.js | 211 +++++++++++++++ .../Availability/AvailabilityForm.css | 175 +++++++----- .../Availability/AvailabilityFormCopy.css | 69 +++++ .../components/EventDetails/EventDetails.css | 3 +- .../EventDetails/EventDetailsCopy.js | 125 +++++++++ .../src/components/EventDetails/query.js | 20 ++ .../src/components/EventList/EventList.js | 4 + .../components/confirmpage/confirmpage.css | 22 +- .../src/components/confirmpage/confirmpage.js | 2 +- .../src/components/slotbook/slotBook.css | 3 + .../src/components/slotbook/slotbook.js | 3 +- 14 files changed, 831 insertions(+), 71 deletions(-) create mode 100644 backend/test.js create mode 100644 frontend/frontend/src/components/Availability/AvaiblityCopy.js create mode 100644 frontend/frontend/src/components/Availability/AvailabilityFormCopy.css create mode 100644 frontend/frontend/src/components/EventDetails/EventDetailsCopy.js diff --git a/backend/test.js b/backend/test.js new file mode 100644 index 0000000..d4bb537 --- /dev/null +++ b/backend/test.js @@ -0,0 +1,251 @@ +import React, { useState, useEffect } from 'react'; +import { useParams } from 'react-router-dom'; +import { GraphQLClient } from 'graphql-request'; +import { GET_AVAILABILITY, UPSERT_AVAILABILITY, DELETE_AVAILABILITY } from './graphqlQueries'; +import './AvailabilityForm.css'; + +const graphqlClient = new GraphQLClient('http://localhost:8080/v1/graphql', { + headers: { + 'x-hasura-admin-secret': '123', + }, +}); + +const TimeSelect = ({ disabled, onChange, value }) => { + const times = []; + for (let i = 0; i < 24; i++) { + for (let j = 0; j < 60; j += 30) { + const time = `${i.toString().padStart(2, '0')}:${j.toString().padStart(2, '0')}`; + times.push(time); + } + } + + return ( + + ); +}; + +const AvailabilityForm = () => { + const daysOfWeek = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']; + const {eventName} = useParams(); + const [availability, setAvailability] = useState( + daysOfWeek.reduce((acc, day) => ({...acc, [day]: [{selected: false, startTime: '', endTime: ''}]}), {}) + ); + const [isFormValid, setIsFormValid] = useState(true); + + useEffect(() => { + const fetchAvailability = async () => { + try { + const response = await graphqlClient.request(GET_AVAILABILITY, {eventName}); + const fetchedAvailability = response.availability.reduce((acc, slot) => { + if (!acc[slot.day]) { + acc[slot.day] = []; + } + acc[slot.day].push({selected: true, startTime: slot.start_time, endTime: slot.end_time}); + return acc; + }, {}); + const newAvailability = {...availability}; + for (const day of daysOfWeek) { + if (fetchedAvailability[day]) { + newAvailability[day] = fetchedAvailability[day]; + } + } + setAvailability(newAvailability); + } catch (error) { + console.error('Failed to fetch availability:', error); + } + }; + + fetchAvailability(); + }, [eventName]); + + useEffect(() => { + const validateForm = () => { + for (const day in availability) { + const slots = availability[day]; + for (let i = 0; i < slots.length; i++) { + const slot = slots[i]; + if (slot.selected) { + if (slot.startTime >= slot.endTime) { + setIsFormValid(false); + return; + } + for (let j = 0; j < slots.length; j++) { + if (i !== j && slots[j].selected) { + if ( + (slot.startTime < slots[j].endTime && slot.endTime > slots[j].startTime) || + (slots[j].startTime < slot.endTime && slots[j].endTime > slot.startTime) + ) { + setIsFormValid(false); + return; + } + } + } + } + } + } + setIsFormValid(true); + }; + + validateForm(); + }, [availability]); + + const handleDayChange = (day, index) => async (event) => { + const updatedDaySlots = [...availability[day]]; + if (updatedDaySlots[index]) { + updatedDaySlots[index].selected = event.target.checked; + setAvailability({...availability, [day]: updatedDaySlots}); + + if (!event.target.checked) { + // Delete the slot if it's being deselected + const slotToDelete = availability[day][index]; + try { + const response = await graphqlClient.request(DELETE_AVAILABILITY, { + day: day, + startTime: slotToDelete.startTime, + eventName: eventName, + }); + if (response.delete_availability.affected_rows > 0) { + console.log('Slot deleted successfully'); + } else { + console.error('Failed to delete slot'); + } + } catch (e) { + console.error('Error occurred while deleting slot', e); + } + updatedDaySlots.splice(index, 1); + setAvailability({...availability, [day]: updatedDaySlots}); + } + } + }; + + const handleTimeChange = (day, index, timeType) => (event) => { + const updatedDaySlots = [...availability[day]]; + if (updatedDaySlots[index]) { + updatedDaySlots[index][timeType] = event.target.value; + setAvailability({...availability, [day]: updatedDaySlots}); + } + }; + + // const addTimeSlot = (day) => { + // setAvailability({ + // ...availability, + // [day]: [...availability[day], {selected: false, startTime: '', endTime: ''}] + // }); + // }; + + const deleteTimeSlot = async (day, index) => { + const slotToDelete = availability[day][index]; + try { + const response = await graphqlClient.request(DELETE_AVAILABILITY, { + day: day, + startTime: slotToDelete.startTime, + eventName: eventName, + }); + if (response.delete_availability.affected_rows > 0) { + console.log('Slot deleted successfully'); + } else { + console.error('Failed to delete slot'); + } + } catch (e) { + console.error('Error occurred while deleting slot', e); + } + + const updatedDaySlots = [...availability[day]]; + updatedDaySlots.splice(index, 1); + setAvailability({...availability, [day]: updatedDaySlots}); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + + const filteredAvailability = Object.fromEntries( + Object.entries(availability).filter(([day, slots]) => slots.some(slot => slot.selected)) + ); + + try { + let flag=0; + for (const [day, slots] of Object.entries(filteredAvailability)) { + for (const slot of slots) { + if (slot.selected) { + const data = await graphqlClient.request(UPSERT_AVAILABILITY, { + day: day, + startTime: slot.startTime, + endTime: slot.endTime, + eventName: eventName, + }); + console.log(data.insert_availability_one.event_name); + + if(!(data.insert_availability_one.event_name==eventName)){ + alert(`Availability will not set for Timeline:\n${day} ${slot.startTime}\nas it is already present in Event Name:\n${data.insert_availability_one.event_name}`); + flag+=1; + } + + } + } + } + if(flag!=1) { + alert('Availability set successfully!'); + } + } catch (e) { + console.error('Error occurred while setting availability', e); + alert('Failed to set availability'); + } + }; + + const addTimeSlot = (day) => { + setAvailability({ + ...availability, + [day]: [...availability[day], {selected: false, startTime: '', endTime: ''}] + }); + }; + + return ( +
Start time must be before end time
} {slot.selected && ( + className="remove-button">X )} ))} diff --git a/frontend/frontend/src/components/Availability/AvaiblityCopy.js b/frontend/frontend/src/components/Availability/AvaiblityCopy.js new file mode 100644 index 0000000..3d1e758 --- /dev/null +++ b/frontend/frontend/src/components/Availability/AvaiblityCopy.js @@ -0,0 +1,211 @@ +import React, { useState, useEffect } from 'react'; +import { useEvent } from '../EventDetails/EventDetailsCopy'; +import { GraphQLClient } from 'graphql-request'; +import { useParams } from 'react-router-dom'; +import { UPSERT_AVAILABILITY, DELETE_AVAILABILITY } from './graphqlQueries'; +import './AvailabilityFormCopy.css'; + +const graphqlClient = new GraphQLClient('http://localhost:8080/v1/graphql', { + headers: { + 'x-hasura-admin-secret': '123', + }, +}); + +const TimeSelect = ({ disabled, onChange, value }) => { + const times = []; + for (let i = 0; i < 24; i++) { + for (let j = 0; j < 60; j += 30) { + const time = `${i.toString().padStart(2, '0')}:${j.toString().padStart(2, '0')}`; + times.push(time); + } + } + + return ( + + ); +}; + +const AvailabilityForm = () => { + const daysOfWeek = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']; + const {eventName} = useParams(); + const { availability, setAvailability } = useEvent(); + const [isFormValid, setIsFormValid] = useState(true); + + useEffect(() => { + const validateForm = () => { + for (const day in availability) { + const slots = availability[day]; + for (let i = 0; i < slots.length; i++) { + const slot = slots[i]; + if (slot.selected) { + if (slot.startTime >= slot.endTime) { + setIsFormValid(false); + return; + } + for (let j = 0; j < slots.length; j++) { + if (i !== j && slots[j].selected) { + if ( + (slot.startTime < slots[j].endTime && slot.endTime > slots[j].startTime) || + (slots[j].startTime < slot.endTime && slots[j].endTime > slot.startTime) + ) { + setIsFormValid(false); + return; + } + } + } + } + } + } + setIsFormValid(true); + }; + + validateForm(); + }, [availability]); + + const handleDayChange = (day, index) => async (event) => { + const updatedDaySlots = [...availability[day]]; + if (updatedDaySlots[index]) { + updatedDaySlots[index].selected = event.target.checked; + setAvailability({ ...availability, [day]: updatedDaySlots }); + + if (!event.target.checked) { + // Delete the slot if it's being deselected + const slotToDelete = availability[day][index]; + try { + const response = await graphqlClient.request(DELETE_AVAILABILITY, { + day: day, + startTime: slotToDelete.startTime, + eventName: eventName, + }); + if (response.delete_availability.affected_rows > 0) { + console.log('Slot deleted successfully'); + } else { + console.error('Failed to delete slot'); + } + } catch (e) { + console.error('Error occurred while deleting slot', e); + } + updatedDaySlots.splice(index, 1); + setAvailability({ ...availability, [day]: updatedDaySlots }); + } + } + }; + + const handleTimeChange = (day, index, timeType) => (event) => { + const updatedDaySlots = [...availability[day]]; + if (updatedDaySlots[index]) { + updatedDaySlots[index][timeType] = event.target.value; + setAvailability({ ...availability, [day]: updatedDaySlots }); + } + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + + const filteredAvailability = Object.fromEntries( + Object.entries(availability).filter(([day, slots]) => slots.some(slot => slot.selected)) + ); + + try { + let flag = false; + for (const [day, slots] of Object.entries(filteredAvailability)) { + for (const slot of slots) { + if (slot.selected) { + const response = await graphqlClient.request(UPSERT_AVAILABILITY, { + eventName, + day, + startTime: slot.startTime, + endTime: slot.endTime, + }); + if(!(response.insert_availability_one.event_name===eventName)){ + alert(`Availability will not set for Timeline:\n${day} ${slot.startTime}\nas it is already present in Event Name:\n${response.insert_availability_one.event_name}`); + flag = true; + } + } + } + } + if (!flag) { + alert('Availability saved successfully.'); + } else { + alert('Failed to save some slots.'); + } + } catch (error) { + console.error('Failed to save availability:', error); + } + }; + + const addTimeSlot = (day) => { + const updatedDaySlots = [...(availability[day] || [])]; + updatedDaySlots.push({ selected: true, startTime: '00:00', endTime: '00:00' }); + setAvailability({ ...availability, [day]: updatedDaySlots }); + }; + + const deleteTimeSlot = (day, index) => async () => { + const updatedDaySlots = [...availability[day]]; + const slotToDelete = updatedDaySlots[index]; + if (slotToDelete) { + try { + const response = await graphqlClient.request(DELETE_AVAILABILITY, { + day: day, + startTime: slotToDelete.startTime, + eventName: eventName, + }); + if (response.delete_availability.affected_rows > 0) { + console.log('Slot deleted successfully'); + } else { + console.error('Failed to delete slot'); + } + } catch (e) { + console.error('Error occurred while deleting slot', e); + } + updatedDaySlots.splice(index, 1); + setAvailability({ ...availability, [day]: updatedDaySlots }); + } + }; + + return ( + + ); +}; + +export default AvailabilityForm; diff --git a/frontend/frontend/src/components/Availability/AvailabilityForm.css b/frontend/frontend/src/components/Availability/AvailabilityForm.css index 170f4ee..3e53310 100644 --- a/frontend/frontend/src/components/Availability/AvailabilityForm.css +++ b/frontend/frontend/src/components/Availability/AvailabilityForm.css @@ -1,65 +1,118 @@ +/* AvailabilityForm.css */ .availability-form { - display: flex; - flex-direction: column; - align-items: center; - } - - .title { - font-size: 24px; - font-weight: bold; - margin-bottom: 20px; - } - - .form { - width: 100%; - } - - .days-grid { - display: grid; - grid-template-columns: repeat(7, 1fr); - gap: 20px; - } - - .day { - display: flex; - flex-direction: column; - align-items: center; - } - - .day-name { - font-size: 18px; - font-weight: bold; - margin-bottom: 10px; - } - - .time-slot { - display: flex; - align-items: center; - margin-bottom: 10px; - } - - .add-slot-button { - margin-top: 10px; - } - - .submit-button { - margin-top: 20px; - } - - - /* AvailabilityForm.css */ + display: flex; + flex-direction: column; + align-items: center; + width: 80%; /* Adjust width to better fit the content */ + margin: 0 auto; /* Center the form */ + padding: 20px; /* Add padding for better spacing */ + background-color: #f9f9f9; /* Light background for contrast */ + border-radius: 20px; /* Rounded corners for better aesthetics */ + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */ + border-color: #000000; +} + +.title { + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; +} + +.form { + width: 100%; +} .days-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 10px; /* Reduce the gap between grid items */ - } - - .day { - display: flex; - flex-direction: column; - align-items: center; - padding: 5px; /* Reduce the padding */ - } - \ No newline at end of file + display: grid; + grid-template-columns: repeat(2, 1fr); /* Increase columns for more compact layout */ + gap: 10px; +} + +.day { + display: flex; + flex-direction: column; + align-items: center; + background-color: #fff; /* White background for each day */ + padding: 15px; /* Add padding for space */ + border-radius: 5px; /* Slight rounding for better aesthetics */ + box-shadow: 0 2px 4px rgba(7, 6, 6, 0.1); /* Light shadow for depth */ +} + +.day-name { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + color: #333; /* Darker text color for better readability */ +} + +.time-slot { + display: flex; + align-items: center; + margin-bottom: 10px; + width: 100%; /* Full width for better alignment */ + justify-content: space-between; /* Space out elements evenly */ +} + +.time-slot select, .time-slot input[type="checkbox"] { + margin-right: 5px; /* Space out elements */ +} + +.time-slot .text-red-500 { + font-size: 12px; /* Smaller font for error message */ + margin-left: 5px; +} + +.add-slot-button { + margin-top: 10px; + padding: 5px 10px; /* Add padding for better click area */ + background-color: #4CAF50; /* Green background */ + color: white; /* White text */ + border: none; + border-radius: 5px; /* Rounded corners */ + cursor: pointer; /* Pointer cursor */ + transition: background-color 0.3s ease; /* Smooth transition */ +} + +.add-slot-button:hover { + background-color: #45a049; /* Darker green on hover */ +} + + +.remove-button button{ + background-color:#e53935; +} + +.submit-button { + margin-top: 20px; + padding: 10px 20px; /* Add padding for better click area */ + background-color: #008CBA; /* Blue background */ + color: white; /* White text */ + border: none; + border-radius: 5px; /* Rounded corners */ + cursor: pointer; /* Pointer cursor */ + transition: background-color 0.3s ease; /* Smooth transition */ +} + +.submit-button:disabled { + background-color: #ccc; /* Grey background when disabled */ + cursor: not-allowed; /* Not allowed cursor when disabled */ +} + +.submit-button:hover:not(:disabled) { + background-color: #007bb5; /* Darker blue on hover */ +} + +.time-slot button { + background-color: #f44336; /* Red background for delete button */ + color: white; /* White text */ + border: none; + border-radius: 5px; /* Rounded corners */ + padding: 5px 10px; /* Add padding for better click area */ + cursor: pointer; /* Pointer cursor */ + transition: background-color 0.3s ease; /* Smooth transition */ +} + +.time-slot button:hover { + background-color: #e53935; /* Darker red on hover */ +} diff --git a/frontend/frontend/src/components/Availability/AvailabilityFormCopy.css b/frontend/frontend/src/components/Availability/AvailabilityFormCopy.css new file mode 100644 index 0000000..8ecf970 --- /dev/null +++ b/frontend/frontend/src/components/Availability/AvailabilityFormCopy.css @@ -0,0 +1,69 @@ +.availability-form { + display: flex; + flex-direction: column; + align-items: center; + width: 80%; /* Adjust width to better fit the content */ + margin: 0 auto; /* Center the form */ + padding: 20px; /* Add padding for better spacing */ + background-color: #f9f9f9; /* Light background for contrast */ + border-radius: 20px; /* Rounded corners for better aesthetics */ + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */ + border-color: #000000; +} + +.availability-day { + margin-bottom: 20px; /* Added margin between days for spacing */ + display: flex; + flex-direction: column; + align-items: center; + background-color: #fff; /* White background for each day */ + padding: 15px; /* Add padding for space */ + border-radius: 5px; /* Slight rounding for better aesthetics */ + box-shadow: 0 2px 4px; /* Light shadow for depth */ + +} + +.availability-slot { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.availability-slot input[type="checkbox"] { + margin-right: 10px; /* Added margin to separate checkbox from time inputs */ +} + +.availability-slot button { + margin-left: 10px; /* Added margin to separate delete button from time inputs */ + padding: 5px 10px; /* Adjusted padding for the delete button */ + border: none; + border-radius: 4px; + background-color: #f44336; /* Changed delete button background color */ + color: white; + cursor: pointer; +} + +.availability-slot button:hover { + background-color: #cc0000; /* Darkened delete button color on hover */ +} + +.availability-day h3 { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; +} + +button[type="submit"] { + margin-top: 20px; + padding: 10px 20px; + border: none; + border-radius: 4px; + background-color: #4CAF50; + color: white; + cursor: pointer; +} + +button[type="submit"]:disabled { + background-color: #cccccc; /* Changed disabled button background color */ + cursor: not-allowed; +} diff --git a/frontend/frontend/src/components/EventDetails/EventDetails.css b/frontend/frontend/src/components/EventDetails/EventDetails.css index dbb00fd..26120b6 100644 --- a/frontend/frontend/src/components/EventDetails/EventDetails.css +++ b/frontend/frontend/src/components/EventDetails/EventDetails.css @@ -11,6 +11,7 @@ box-sizing: border-box; border: 1px solid #ccc; border-radius: 8px; + background-color: #f9f9f9; /* Light background for better contrast */ } .event-edit form { @@ -64,5 +65,5 @@ } .edit-button:hover { - background-color: #45a049; + background-color: #2a47a6; } diff --git a/frontend/frontend/src/components/EventDetails/EventDetailsCopy.js b/frontend/frontend/src/components/EventDetails/EventDetailsCopy.js new file mode 100644 index 0000000..0f55346 --- /dev/null +++ b/frontend/frontend/src/components/EventDetails/EventDetailsCopy.js @@ -0,0 +1,125 @@ +import React, { useState, useEffect, createContext, useContext } from 'react'; +import { useParams } from 'react-router-dom'; +import AvailabilityForm from '../Availability/AvaiblityCopy'; +import { GraphQLClient } from 'graphql-request'; +import { GET_EVENT_AND_AVAILABILITY, UPDATE_EVENT_DETAIL } from './query'; +import './EventDetails.css'; + +// Create a context for sharing event data +const EventContext = createContext(); + +const graphqlClient = new GraphQLClient('http://localhost:8080/v1/graphql', { + headers: { + 'x-hasura-admin-secret': '123', + }, +}); + +export const useEvent = () => useContext(EventContext); + +const EventDetails = () => { + const { eventName } = useParams(); + const [event, setEvent] = useState(null); + const [editEvent, setEditEvent] = useState(null); + const [availability, setAvailability] = useState({}); + const [showEditForm, setShowEditForm] = useState(false); + const [showAvailabilityForm, setShowAvailabilityForm] = useState(false); + + useEffect(() => { + const fetchEventAndAvailability = async () => { + try { + const response = await graphqlClient.request(GET_EVENT_AND_AVAILABILITY, { eventName }); + setEvent(response.event); + setEditEvent(response.event); + const newAvailability = response.availability.reduce((acc, slot) => { + if (!acc[slot.day]) { + acc[slot.day] = []; + } + acc[slot.day].push({ selected: true, startTime: slot.start_time, endTime: slot.end_time }); + return acc; + }, {}); + setAvailability(newAvailability); + } catch (error) { + console.error('Failed to fetch event and availability:', error); + } + }; + + fetchEventAndAvailability(); + }, [eventName]); + + const handleEditChange = (e) => { + setEditEvent({ ...editEvent, [e.target.name]: e.target.value }); + }; + + const handleEditSubmit = async (e) => { + e.preventDefault(); + + try { + const response = await graphqlClient.request(UPDATE_EVENT_DETAIL, { + eventName, + data: { + event_name: editEvent.event_name, + duration: editEvent.duration, + location_type: editEvent.location_type, + location_detail: editEvent.location_detail, + }, + }); + + setEvent(response.update_kalenview_create_events_by_pk); + setShowEditForm(false); + } catch (error) { + console.error('Failed to update event:', error); + } + }; + + if (!event) { + returnDuration: {event.duration} minutes
+Location Type: {event.location_type}
+Description: {event.location_detail}
+{eventName}
{organizerName.first_name +" "+ organizerName.last_name}
diff --git a/frontend/frontend/src/components/slotbook/slotBook.css b/frontend/frontend/src/components/slotbook/slotBook.css index eb668c0..e5a47de 100644 --- a/frontend/frontend/src/components/slotbook/slotBook.css +++ b/frontend/frontend/src/components/slotbook/slotBook.css @@ -222,3 +222,6 @@ abbr[title] { border-right: none; /* Remove border on the last half */ } +#half-next{ + background-color: #0d73e0; +} \ No newline at end of file diff --git a/frontend/frontend/src/components/slotbook/slotbook.js b/frontend/frontend/src/components/slotbook/slotbook.js index 98ce725..ba5ede7 100644 --- a/frontend/frontend/src/components/slotbook/slotbook.js +++ b/frontend/frontend/src/components/slotbook/slotbook.js @@ -174,9 +174,10 @@ function CalendarPage() {Thank you for booking. You will receive a confirmation email shortly.
-{eventName}
{organizerName.first_name +" "+ organizerName.last_name}
+{organizerName.first_name + " " + organizerName.last_name}
{duration}
-{eventName}
-//Duration
+//{organizerName.first_name +" "+ organizerName.last_name}
+//{duration}
+// //Thank you for booking. You will receive a confirmation email shortly.
+ +