diff --git a/backend/logics/Mailer/mailer.mjs b/backend/logics/Mailer/mailer.mjs index 64cf594..0656d71 100644 --- a/backend/logics/Mailer/mailer.mjs +++ b/backend/logics/Mailer/mailer.mjs @@ -30,7 +30,7 @@ export class Mailer { const response = { body: { name: userName, - intro: `You have an upcoming event/meeting: ${eventName}`, + intro: `This is to inform you of an upcoming event/meeting: ${eventName}`, table: { data: [ { @@ -59,7 +59,7 @@ export class Mailer { } ] }, - outro: "Kindly be present there are per the scheduled time" + outro: "Kindly be present at the aforementioned timings" } } 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 ( +
+

WEEKLY HOURS

+
+
+ {daysOfWeek.map((day) => ( +
+

{day}

+ {availability[day].map((slot, index) => ( +
+ + + + {slot.selected && slot.startTime >= slot.endTime && +

Start time must be before end time

} + {slot.selected && ( + + )} +
+ ))} + +
+ ))} +
+ +
+
+ ); +} + +export default AvailabilityForm; + + + + + + + + + diff --git a/frontend/frontend/package-lock.json b/frontend/frontend/package-lock.json index 9f9d246..e2f12dd 100644 --- a/frontend/frontend/package-lock.json +++ b/frontend/frontend/package-lock.json @@ -14,6 +14,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.6.8", + "bcryptjs": "^2.4.3", "graphql-request": "^7.0.1", "js-cookie": "^3.0.5", "react": "^18.3.1", @@ -5981,6 +5982,12 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, "node_modules/bfj": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", diff --git a/frontend/frontend/package.json b/frontend/frontend/package.json index 0b49b65..c4f497c 100644 --- a/frontend/frontend/package.json +++ b/frontend/frontend/package.json @@ -9,6 +9,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.6.8", + "bcryptjs": "^2.4.3", "graphql-request": "^7.0.1", "js-cookie": "^3.0.5", "react": "^18.3.1", diff --git a/frontend/frontend/src/App.js b/frontend/frontend/src/App.js index 759aa68..d43e7d4 100644 --- a/frontend/frontend/src/App.js +++ b/frontend/frontend/src/App.js @@ -1,14 +1,14 @@ import React from "react"; import { BrowserRouter as Router, Routes, Route} from "react-router-dom"; -import CreateEvent from "./components/CreateEvent/CreateEvent.js"; -import EventList from "./components/EventList/EventList.js"; -import Login from "./components/login/Login.js"; -import EventDetails from "./components/EventDetails/EventDetails.js"; -import AvailabilityForm from "./components/Availability/Avaiblity.js"; -import Register from "./components/Register/Register.js"; -import CalendarPage from "./components/slotbook/slotbook"; -import BookingPage from "./components/confirmpage/confirmpage"; -import SuccessPage from "./components/successpage/successpage"; +import CreateEvent from "./pages/CreateEvent/CreateEvent.js"; +import EventList from "./pages/EventList/EventList.js"; +import Login from "./pages/login/Login.js"; +import EventDetails from "./pages/EventDetails/EventDetailsCopy.js"; +import AvailabilityForm from "./pages/Availability/Avaiblity.js"; +import Register from "./pages/Register/Register.js"; +import CalendarPage from "./pages/slotbook/slotbook"; +import BookingPage from "./pages/confirmpage/confirmpage"; +import SuccessPage from "./pages/successpage/successpage"; import "./App.css"; @@ -33,6 +33,7 @@ function App() { } /> } /> } /> + {/*} />*/} } /> diff --git a/frontend/frontend/src/components/Availability/AvailabilityForm.css b/frontend/frontend/src/components/Availability/AvailabilityForm.css deleted file mode 100644 index 170f4ee..0000000 --- a/frontend/frontend/src/components/Availability/AvailabilityForm.css +++ /dev/null @@ -1,65 +0,0 @@ - -.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 */ - -.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 diff --git a/frontend/frontend/src/components/successpage/successpage.css b/frontend/frontend/src/components/successpage/successpage.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/frontend/src/components/successpage/successpage.js b/frontend/frontend/src/components/successpage/successpage.js deleted file mode 100644 index dca9608..0000000 --- a/frontend/frontend/src/components/successpage/successpage.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; - -function SuccessPage() { - return ( -
-

Booking Successful!

-

Thank you for booking. You will receive a confirmation email shortly.

-
- ); -} - -export default SuccessPage; diff --git a/frontend/frontend/src/components/Availability/Avaiblity.js b/frontend/frontend/src/pages/Availability/Avaiblity.js similarity index 96% rename from frontend/frontend/src/components/Availability/Avaiblity.js rename to frontend/frontend/src/pages/Availability/Avaiblity.js index acddf5b..9dee373 100644 --- a/frontend/frontend/src/components/Availability/Avaiblity.js +++ b/frontend/frontend/src/pages/Availability/Avaiblity.js @@ -167,6 +167,7 @@ const AvailabilityForm = () => { ); try { + let flag= false; for (const [day, slots] of Object.entries(filteredAvailability)) { for (const slot of slots) { if (slot.selected) { @@ -178,13 +179,18 @@ const AvailabilityForm = () => { }); console.log(data.insert_availability_one.event_name); - if(!(data.insert_availability_one.event_name==eventName)){ + 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 = true; } + } } } - alert('Availability set successfully!'); + if(!flag) { + alert('Availability set successfully!'); + } + } catch (e) { console.error('Error occurred while setting availability', e); alert('Failed to set availability'); @@ -218,7 +224,7 @@ const AvailabilityForm = () => {

Start time must be before end time

} {slot.selected && ( + className="remove-button">X )} ))} diff --git a/frontend/frontend/src/pages/Availability/AvaiblityCopy.js b/frontend/frontend/src/pages/Availability/AvaiblityCopy.js new file mode 100644 index 0000000..3d1e758 --- /dev/null +++ b/frontend/frontend/src/pages/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 ( +
+
+ {daysOfWeek.map((day) => ( +
+

{day}

+ {availability[day]?.map((slot, index) => ( +
+ + + + +
+ ))} + +
+ ))} +
+ +
+ ); +}; + +export default AvailabilityForm; diff --git a/frontend/frontend/src/pages/Availability/AvailabilityForm.css b/frontend/frontend/src/pages/Availability/AvailabilityForm.css new file mode 100644 index 0000000..3e53310 --- /dev/null +++ b/frontend/frontend/src/pages/Availability/AvailabilityForm.css @@ -0,0 +1,118 @@ +/* AvailabilityForm.css */ + +.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; +} + +.title { + font-size: 24px; + font-weight: bold; + margin-bottom: 20px; +} + +.form { + width: 100%; +} + +.days-grid { + 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/pages/Availability/AvailabilityFormCopy.css b/frontend/frontend/src/pages/Availability/AvailabilityFormCopy.css new file mode 100644 index 0000000..8ecf970 --- /dev/null +++ b/frontend/frontend/src/pages/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/Availability/graphqlQueries.js b/frontend/frontend/src/pages/Availability/graphqlQueries.js similarity index 100% rename from frontend/frontend/src/components/Availability/graphqlQueries.js rename to frontend/frontend/src/pages/Availability/graphqlQueries.js diff --git a/frontend/frontend/src/components/CreateEvent/CreateEvent.css b/frontend/frontend/src/pages/CreateEvent/CreateEvent.css similarity index 100% rename from frontend/frontend/src/components/CreateEvent/CreateEvent.css rename to frontend/frontend/src/pages/CreateEvent/CreateEvent.css diff --git a/frontend/frontend/src/components/CreateEvent/CreateEvent.js b/frontend/frontend/src/pages/CreateEvent/CreateEvent.js similarity index 100% rename from frontend/frontend/src/components/CreateEvent/CreateEvent.js rename to frontend/frontend/src/pages/CreateEvent/CreateEvent.js diff --git a/frontend/frontend/src/components/EventDetails/EventDetails.css b/frontend/frontend/src/pages/EventDetails/EventDetails.css similarity index 86% rename from frontend/frontend/src/components/EventDetails/EventDetails.css rename to frontend/frontend/src/pages/EventDetails/EventDetails.css index dbb00fd..26120b6 100644 --- a/frontend/frontend/src/components/EventDetails/EventDetails.css +++ b/frontend/frontend/src/pages/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/EventDetails.js b/frontend/frontend/src/pages/EventDetails/EventDetails.js similarity index 100% rename from frontend/frontend/src/components/EventDetails/EventDetails.js rename to frontend/frontend/src/pages/EventDetails/EventDetails.js diff --git a/frontend/frontend/src/pages/EventDetails/EventDetailsCopy.js b/frontend/frontend/src/pages/EventDetails/EventDetailsCopy.js new file mode 100644 index 0000000..0f55346 --- /dev/null +++ b/frontend/frontend/src/pages/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) { + return
Loading...
; + } + + return ( + +
+
+

Edit Event

+ + + {showEditForm && ( +
+ + + +