From b3901d95975744229c17b968368af3c30a91648c Mon Sep 17 00:00:00 2001 From: Laxmi Ray <122423386+Laxmi01345@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:41:56 +0530 Subject: [PATCH] Enhancements to Event Creation and Management UI/UX (#96) * updated * ehanced UI --- backend/dist/config/const.js | 1 + backend/dist/controllers/event.controller.js | 21 +- backend/dist/routes/event.routes.js | 1 + frontend/src/appComponents/EventCard.tsx | 2 +- frontend/src/appComponents/EventCreation.tsx | 491 ++++++++++--------- package-lock.json | 41 +- package.json | 3 +- 7 files changed, 333 insertions(+), 227 deletions(-) diff --git a/backend/dist/config/const.js b/backend/dist/config/const.js index 294a6a0..7c1c050 100644 --- a/backend/dist/config/const.js +++ b/backend/dist/config/const.js @@ -23,6 +23,7 @@ exports.MESSAGES = { ERROR_CREATING_EVENT: "An error occurred while creating the event.", FETCH_EVENTS_SUCCESS: "Events fetched successfully.", UPDATE_EVENT_SUCCESS: "Event updated successfully.", + ERROR_FETCHING_EVENT: "An error occurred while fetching the single event.", }, GENERIC: { INTERNAL_SERVER_ERROR: "Internal Server Error", diff --git a/backend/dist/controllers/event.controller.js b/backend/dist/controllers/event.controller.js index 922a12e..eaddfbd 100644 --- a/backend/dist/controllers/event.controller.js +++ b/backend/dist/controllers/event.controller.js @@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.createQuiz = exports.getAllEventData = exports.createEvent = exports.updateEvent = exports.getClubEventData = exports.deleteEvent = void 0; +exports.getSingleEventData = exports.createQuiz = exports.getAllEventData = exports.createEvent = exports.updateEvent = exports.getClubEventData = exports.deleteEvent = void 0; const database_config_1 = require("../config/database.config"); const const_1 = require("../config/const"); const logger_1 = __importDefault(require("../config/logger")); @@ -154,3 +154,22 @@ const createQuiz = (req, res) => __awaiter(void 0, void 0, void 0, function* () } }); exports.createQuiz = createQuiz; +const getSingleEventData = (req, res) => __awaiter(void 0, void 0, void 0, function* () { + const eventId = Number(req.query.EventID); + try { + const event = yield database_config_1.prisma.event.findUnique({ + where: { EventID: eventId }, + }); + if (!event) { + logger_1.default.warn(`Event not found for EventID: ${eventId}`); + return res.status(404).json({ error: const_1.MESSAGES.EVENT.EVENT_NOT_FOUND }); + } + logger_1.default.info(`Fetched event data for EventID: ${eventId}`); + res.json(event); + } + catch (error) { + logger_1.default.error(`${const_1.MESSAGES.EVENT.ERROR_FETCHING_EVENT}: ${error}`); + res.status(500).json({ error: const_1.MESSAGES.EVENT.ERROR_FETCHING_EVENT }); + } +}); +exports.getSingleEventData = getSingleEventData; diff --git a/backend/dist/routes/event.routes.js b/backend/dist/routes/event.routes.js index c1fda77..2202d61 100644 --- a/backend/dist/routes/event.routes.js +++ b/backend/dist/routes/event.routes.js @@ -6,6 +6,7 @@ const router = (0, express_1.Router)(); router.delete("/deleteEvent", event_controller_1.deleteEvent); //get all events router.get("/getAllEventData", event_controller_1.getAllEventData); +router.get("/getSingleEventData", event_controller_1.getSingleEventData); //get all events for a club router.get("/getClubEventData", event_controller_1.getClubEventData); router.put("/update-event", event_controller_1.updateEvent); diff --git a/frontend/src/appComponents/EventCard.tsx b/frontend/src/appComponents/EventCard.tsx index edc0712..4bd2371 100644 --- a/frontend/src/appComponents/EventCard.tsx +++ b/frontend/src/appComponents/EventCard.tsx @@ -10,7 +10,7 @@ type EventCardProps = { const EventCard: React.FC = ({ name, description, time }) => { return ( -
+

{name}

{description}

diff --git a/frontend/src/appComponents/EventCreation.tsx b/frontend/src/appComponents/EventCreation.tsx index 3557e2a..4234bce 100644 --- a/frontend/src/appComponents/EventCreation.tsx +++ b/frontend/src/appComponents/EventCreation.tsx @@ -4,7 +4,11 @@ import type { FieldApi } from "@tanstack/react-form"; import { useAuth } from "../context/AuthContext"; import axios from "./axiosInstance"; import { useNavigate } from "react-router-dom"; - +import { ImCross } from "react-icons/im"; +import { useRef } from "react"; +import { MdDelete } from "react-icons/md"; +import { GrUpdate } from "react-icons/gr"; +import { CgMoreO } from "react-icons/cg"; // interface Event { // ClubID: string; // EventName: string; @@ -32,6 +36,9 @@ const EventCreation: React.FC = () => { const [loading, setLoading] = useState({ state: false, id: null }); const [updateOrSubmitState, setUpdateOrSubmitState] = useState(false); const [eventId, setEventId] = useState(null); + const [showForm, setShowForm] = useState(false); + const formRef = useRef(null); // Create a reference to the form + const navigate =useNavigate(); const form = useForm({ @@ -86,6 +93,7 @@ const EventCreation: React.FC = () => { if (response.status == 200) { // setEventData([...eventData, response.data]); form.reset(); + setShowForm(false); // Find the event in the eventData array and replace it with the updated event const updatedEventData = eventData.map((event: any) => { @@ -156,237 +164,273 @@ const EventCreation: React.FC = () => { return; } }; + const handleCreateEventClick = () => { + setShowForm(true); + setTimeout(() => { + formRef.current?.scrollIntoView({ behavior: "smooth" }); + formRef.current?.classList.add("highlight"); + setTimeout(() => formRef.current?.classList.remove("highlight"), 2000); // Remove highlight after 2 seconds + }, 100); + }; return ( -
-
+ <> +
+
+
+ +
+
+ + {showForm && ( +
{ - e.preventDefault(); - e.stopPropagation(); - form.handleSubmit(); - }} - > -
- {/* A type-safe field component*/} - - !value ? "EventName is required" : undefined, - onChangeAsyncDebounceMs: 500, - onChangeAsync: async ({ value }) => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - return ( - value.includes("error") && 'No "error" allowed in EventName' - ); - }, - }} - children={(field) => { - // Avoid hasty abstractions. Render props are great! + className="flex justify-center flex-col items-center gap-4 p-4 border border-black rounded-xl w-fit bg-[#6284eb]" + onSubmit={(e) => { + e.preventDefault(); + e.stopPropagation(); + form.handleSubmit(); + }} + ref={formRef} + > +
+ {/* A type-safe field component*/} + + !value ? "EventName is required" : undefined, + onChangeAsyncDebounceMs: 500, + onChangeAsync: async ({ value }) => { + await new Promise((resolve) => setTimeout(resolve, 1000)); return ( - <> - - field.handleChange(e.target.value)} - /> -
- -
- + value.includes("error") && 'No "error" allowed in EventName' ); - }} - /> -
-
- {/* A type-safe field component*/} - - !value ? "Description is required" : undefined, - onChangeAsyncDebounceMs: 500, - onChangeAsync: async ({ value }) => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - return ( - value.includes("error") && - 'No "error" allowed in Description' - ); - }, - }} - children={(field) => { - // Avoid hasty abstractions. Render props are great! - return ( - <> - - field.handleChange(e.target.value)} - /> -
- + }, + }} + children={(field) => { + // Avoid hasty abstractions. Render props are great! + return ( + <> +
+ {setShowForm(false)})}> + New Event
- + + + field.handleChange(e.target.value)} + /> +
+ +
+ + ); + }} + /> +
+
+ {/* A type-safe field component*/} + + !value ? "Description is required" : undefined, + onChangeAsyncDebounceMs: 500, + onChangeAsync: async ({ value }) => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + return ( + value.includes("error") && + 'No "error" allowed in Description' ); - }} - /> -
-
- {/* A type-safe field component*/} - - !value ? "StartDateTime is required" : undefined, - onChangeAsyncDebounceMs: 500, - onChangeAsync: async ({ value }) => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - return ( - value.includes("error") && - 'No "error" allowed in StartDateTime' - ); - }, - }} - children={(field) => { - // Avoid hasty abstractions. Render props are great! + }, + }} + children={(field) => { + // Avoid hasty abstractions. Render props are great! + return ( + <> + + field.handleChange(e.target.value)} + /> +
+ +
+ + ); + }} + /> +
+
+ {/* A type-safe field component*/} + + !value ? "StartDateTime is required" : undefined, + onChangeAsyncDebounceMs: 500, + onChangeAsync: async ({ value }) => { + await new Promise((resolve) => setTimeout(resolve, 1000)); return ( - <> - - field.handleChange(e.target.value)} - /> -
- -
- + value.includes("error") && + 'No "error" allowed in StartDateTime' ); - }} - /> -
-
- {/* A type-safe field component*/} - - !value ? "EndDateTime is required" : undefined, - onChangeAsyncDebounceMs: 500, - onChangeAsync: async ({ value }) => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - return ( - value.includes("error") && - 'No "error" allowed in EndDateTime' - ); - }, - }} - children={(field) => { - // Avoid hasty abstractions. Render props are great! + }, + }} + children={(field) => { + // Avoid hasty abstractions. Render props are great! + return ( + <> + + field.handleChange(e.target.value)} + /> +
+ +
+ + ); + }} + /> +
+
+ {/* A type-safe field component*/} + + !value ? "EndDateTime is required" : undefined, + onChangeAsyncDebounceMs: 500, + onChangeAsync: async ({ value }) => { + await new Promise((resolve) => setTimeout(resolve, 1000)); return ( - <> - - field.handleChange(e.target.value)} - /> -
- -
- + value.includes("error") && + 'No "error" allowed in EndDateTime' ); - }} - /> -
-
- {/* A type-safe field component*/} - - !value ? "Location is required" : undefined, - onChangeAsyncDebounceMs: 500, - onChangeAsync: async ({ value }) => { - await new Promise((resolve) => setTimeout(resolve, 1000)); - return ( - value.includes("error") && 'No "error" allowed in Location' - ); - }, - }} - children={(field) => { - // Avoid hasty abstractions. Render props are great! + }, + }} + children={(field) => { + // Avoid hasty abstractions. Render props are great! + return ( + <> + + field.handleChange(e.target.value)} + /> +
+ +
+ + ); + }} + /> +
+
+ {/* A type-safe field component*/} + + !value ? "Location is required" : undefined, + onChangeAsyncDebounceMs: 500, + onChangeAsync: async ({ value }) => { + await new Promise((resolve) => setTimeout(resolve, 1000)); return ( - <> - - field.handleChange(e.target.value)} - /> -
- -
- + value.includes("error") && 'No "error" allowed in Location' ); - }} - /> -
- [state.canSubmit, state.isSubmitting]} - children={([canSubmit, isSubmitting]) => ( - - )} + }, + }} + children={(field) => { + // Avoid hasty abstractions. Render props are great! + return ( + <> + + field.handleChange(e.target.value)} + /> +
+ +
+ + ); + }} /> - -
-

+

+ [state.canSubmit, state.isSubmitting]} + children={([canSubmit, isSubmitting]) => ( + + )} + /> + +
+ )} + + + +
+
+

Events Created by you:{" "}

- - +
+
+ - - - - - - + + + + @@ -394,7 +438,7 @@ const EventCreation: React.FC = () => { {eventData?.map((event: any) => ( @@ -415,10 +459,11 @@ const EventCreation: React.FC = () => { > {loading.state && loading.id == event.EventID ? "deleting..." - : "Delete"} + : } + + }}> ))}
- EventName + + Event Name DescriptionStartDateTimeEndDateTimeLocation + DescriptionStartDate TimeEndDate TimeLocation Operations
{event.EventName}
-
+
+
+ + ); }; export default EventCreation; diff --git a/package-lock.json b/package-lock.json index 5ecfdaf..ceeee18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "name": "dypcetclubs-root", "version": "0.0.0", "dependencies": { - "dypcetclubs-root": "file:" + "dypcetclubs-root": "file:", + "react-icons": "^5.3.0" }, "devDependencies": { "husky": "^8.0.3" @@ -32,6 +33,44 @@ "funding": { "url": "https://github.com/sponsors/typicode" } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } } } } diff --git a/package.json b/package.json index fc54d35..99154ff 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "husky": "^8.0.3" }, "dependencies": { - "dypcetclubs-root": "file:" + "dypcetclubs-root": "file:", + "react-icons": "^5.3.0" } }