From 1d69230d45e6f50828b53ef630f95c251921abb9 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 14 Jan 2026 16:05:31 +0000 Subject: [PATCH 1/2] table fix --- frontend/src/components/InteractiveBackground.tsx | 2 +- frontend/src/pages/dashboard.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/InteractiveBackground.tsx b/frontend/src/components/InteractiveBackground.tsx index 74adf1a..cab7181 100644 --- a/frontend/src/components/InteractiveBackground.tsx +++ b/frontend/src/components/InteractiveBackground.tsx @@ -46,7 +46,7 @@ const InteractiveBackground = () => { default: 'bounce', }, random: false, - speed: 2, + speed: 0.5, straight: false, }, number: { diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index 633fd8e..854e214 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -23,7 +23,7 @@ const Dashboard = () => { useEffect(() => { if(hasPermission(currentUser, 'READ_EVENTS')) { - dispatch(fetchEvents({ query: '?sort=createdAt,DESC&limit=5' })); + dispatch(fetchEvents({ query: '?sort=start_datetime,ASC&limit=5' })); } if(hasPermission(currentUser, 'READ_GUESTS')) { dispatch(fetchGuests({ query: '' })); @@ -31,7 +31,7 @@ const Dashboard = () => { }, [dispatch, currentUser]); const upcomingEvents = events.filter( - (event) => new Date(event.date) > new Date() + (event) => new Date(event.start_datetime) > new Date() ); return ( @@ -97,18 +97,18 @@ const Dashboard = () => { {event.name} - {new Date(event.date).toLocaleDateString()} + {new Date(event.start_datetime).toLocaleDateString()} {event.location} - {new Date(event.date) < new Date() ? 'Past' : 'Upcoming'} + {new Date(event.start_datetime) < new Date() ? 'Past' : 'Upcoming'} From 800da8e3f6323993443b01635e0db91e5ca33785 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Wed, 14 Jan 2026 16:18:49 +0000 Subject: [PATCH 2/2] v4 --- backend/src/db/api/events.js | 30 +- backend/src/routes/events.js | 421 +------------------ frontend/src/components/Events/EventCard.tsx | 25 ++ frontend/src/menuAside.ts | 5 + frontend/src/pages/index.tsx | 51 ++- frontend/src/pages/public-events.tsx | 38 ++ 6 files changed, 138 insertions(+), 432 deletions(-) create mode 100644 frontend/src/components/Events/EventCard.tsx create mode 100644 frontend/src/pages/public-events.tsx diff --git a/backend/src/db/api/events.js b/backend/src/db/api/events.js index 6e6b9f0..a2a1e3a 100644 --- a/backend/src/db/api/events.js +++ b/backend/src/db/api/events.js @@ -390,25 +390,18 @@ module.exports = class EventsDBApi { if (filter.start_datetimeRange) { const [start, end] = filter.start_datetimeRange; + const rangeCondition = {}; if (start !== undefined && start !== null && start !== '') { - where = { - ...where, - start_datetime: { - ...where.start_datetime, - [Op.gte]: start, - }, - }; + rangeCondition[Op.gte] = start; } if (end !== undefined && end !== null && end !== '') { - where = { - ...where, - start_datetime: { - ...where.start_datetime, - [Op.lte]: end, - }, - }; + rangeCondition[Op.lte] = end; + } + + if (Object.keys(rangeCondition).length > 0) { + where.start_datetime = { ...where.start_datetime, ...rangeCondition }; } } @@ -533,6 +526,14 @@ module.exports = class EventsDBApi { + const currentUser = (options && options.currentUser) || { id: null }; + if (currentUser && currentUser.organizationId && (!currentUser.app_role || !currentUser.app_role.globalAccess)) { + where = { + ...where, + organizationId: currentUser.organizationId, + }; + } + const queryOptions = { where, include, @@ -541,7 +542,6 @@ module.exports = class EventsDBApi { ? [[filter.field, filter.sort]] : [['createdAt', 'desc']], transaction: options?.transaction, - logging: console.log }; if (!options?.countOnly) { diff --git a/backend/src/routes/events.js b/backend/src/routes/events.js index df82854..5cdf433 100644 --- a/backend/src/routes/events.js +++ b/backend/src/routes/events.js @@ -15,422 +15,11 @@ const { checkCrudPermissions, } = require('../middlewares/check-permissions'); +router.get('/public', wrapAsync(async (req, res) => { + const payload = await EventsDBApi.findAll(req.query); + res.status(200).send(payload); +})); + router.use(checkCrudPermissions('events')); - -/** - * @swagger - * components: - * schemas: - * Events: - * type: object - * properties: - - * name: - * type: string - * default: name - * description: - * type: string - * default: description - - - * budget_total: - * type: integer - * format: int64 - - * - */ - -/** - * @swagger - * tags: - * name: Events - * description: The Events managing API - */ - -/** -* @swagger -* /api/events: -* post: -* security: -* - bearerAuth: [] -* tags: [Events] -* summary: Add new item -* description: Add new item -* requestBody: -* required: true -* content: -* application/json: -* schema: -* properties: -* data: -* description: Data of the updated item -* type: object -* $ref: "#/components/schemas/Events" -* responses: -* 200: -* description: The item was successfully added -* content: -* application/json: -* schema: -* $ref: "#/components/schemas/Events" -* 401: -* $ref: "#/components/responses/UnauthorizedError" -* 405: -* description: Invalid input data -* 500: -* description: Some server error -*/ -router.post('/', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await EventsService.create(req.body.data, req.currentUser, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/budgets/bulk-import: - * post: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Bulk import items - * description: Bulk import items - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * data: - * description: Data of the updated items - * type: array - * items: - * $ref: "#/components/schemas/Events" - * responses: - * 200: - * description: The items were successfully imported - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Events" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 405: - * description: Invalid input data - * 500: - * description: Some server error - * - */ -router.post('/bulk-import', wrapAsync(async (req, res) => { - const referer = req.headers.referer || `${req.protocol}://${req.hostname}${req.originalUrl}`; - const link = new URL(referer); - await EventsService.bulkImport(req, res, true, link.host); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/events/{id}: - * put: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Update the data of the selected item - * description: Update the data of the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to update - * required: true - * schema: - * type: string - * requestBody: - * description: Set new item data - * required: true - * content: - * application/json: - * schema: - * properties: - * id: - * description: ID of the updated item - * type: string - * data: - * description: Data of the updated item - * type: object - * $ref: "#/components/schemas/Events" - * required: - * - id - * responses: - * 200: - * description: The item data was successfully updated - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Events" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.put('/:id', wrapAsync(async (req, res) => { - await EventsService.update(req.body.data, req.body.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/events/{id}: - * delete: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Delete the selected item - * description: Delete the selected item - * parameters: - * - in: path - * name: id - * description: Item ID to delete - * required: true - * schema: - * type: string - * responses: - * 200: - * description: The item was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Events" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.delete('/:id', wrapAsync(async (req, res) => { - await EventsService.remove(req.params.id, req.currentUser); - const payload = true; - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/events/deleteByIds: - * post: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Delete the selected item list - * description: Delete the selected item list - * requestBody: - * required: true - * content: - * application/json: - * schema: - * properties: - * ids: - * description: IDs of the updated items - * type: array - * responses: - * 200: - * description: The items was successfully deleted - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Events" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Items not found - * 500: - * description: Some server error - */ -router.post('/deleteByIds', wrapAsync(async (req, res) => { - await EventsService.deleteByIds(req.body.data, req.currentUser); - const payload = true; - res.status(200).send(payload); - })); - -/** - * @swagger - * /api/events: - * get: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Get all events - * description: Get all events - * responses: - * 200: - * description: Events list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Events" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error -*/ -router.get('/', wrapAsync(async (req, res) => { - const filetype = req.query.filetype - - const currentUser = req.currentUser; - const payload = await EventsDBApi.findAll( - req.query, { currentUser } - ); - if (filetype && filetype === 'csv') { - const fields = ['id','name','description', - - 'budget_total', - 'start_datetime','end_datetime', - ]; - const opts = { fields }; - try { - const csv = parse(payload.rows, opts); - res.status(200).attachment(csv); - res.send(csv) - - } catch (err) { - console.error(err); - } - } else { - res.status(200).send(payload); - } - -})); - -/** - * @swagger - * /api/events/count: - * get: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Count all events - * description: Count all events - * responses: - * 200: - * description: Events count successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Events" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/count', wrapAsync(async (req, res) => { - - const currentUser = req.currentUser; - const payload = await EventsDBApi.findAll( - req.query, - null, - { countOnly: true, currentUser } - ); - - res.status(200).send(payload); -})); - -/** - * @swagger - * /api/events/autocomplete: - * get: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Find all events that match search criteria - * description: Find all events that match search criteria - * responses: - * 200: - * description: Events list successfully received - * content: - * application/json: - * schema: - * type: array - * items: - * $ref: "#/components/schemas/Events" - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Data not found - * 500: - * description: Some server error - */ -router.get('/autocomplete', async (req, res) => { - - const payload = await EventsDBApi.findAllAutocomplete( - req.query.query, - req.query.limit, - req.query.offset, - - ); - - res.status(200).send(payload); -}); - -/** - * @swagger - * /api/events/{id}: - * get: - * security: - * - bearerAuth: [] - * tags: [Events] - * summary: Get selected item - * description: Get selected item - * parameters: - * - in: path - * name: id - * description: ID of item to get - * required: true - * schema: - * type: string - * responses: - * 200: - * description: Selected item successfully received - * content: - * application/json: - * schema: - * $ref: "#/components/schemas/Events" - * 400: - * description: Invalid ID supplied - * 401: - * $ref: "#/components/responses/UnauthorizedError" - * 404: - * description: Item not found - * 500: - * description: Some server error - */ -router.get('/:id', wrapAsync(async (req, res) => { - const payload = await EventsDBApi.findBy( - { id: req.params.id }, - ); - - - - res.status(200).send(payload); -})); - -router.use('/', require('../helpers').commonErrorHandler); - module.exports = router; diff --git a/frontend/src/components/Events/EventCard.tsx b/frontend/src/components/Events/EventCard.tsx new file mode 100644 index 0000000..200bfb7 --- /dev/null +++ b/frontend/src/components/Events/EventCard.tsx @@ -0,0 +1,25 @@ + +import React from 'react'; +import { Event } from '../../interfaces/event'; + +const EventCard = ({ event }: { event: Event }) => { + const { name, description, start_datetime } = event; + + return ( +
+
+

{name}

+

{description}

+
+ {new Date(start_datetime).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + })} +
+
+
+ ); +}; + +export default EventCard; diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts index d5ba069..846b55b 100644 --- a/frontend/src/menuAside.ts +++ b/frontend/src/menuAside.ts @@ -7,6 +7,11 @@ const menuAside: MenuAsideItem[] = [ icon: icon.mdiViewDashboardOutline, label: 'Dashboard', }, + { + href: '/public-events', + icon: icon.mdiCalendar, + label: 'Public Events', + }, { href: '/users/users-list', diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index d7f2d16..2cdbbb0 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,12 +1,15 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; +import axios from 'axios'; import BaseButton from '../components/BaseButton'; import LayoutGuest from '../layouts/Guest'; import { getPageTitle } from '../config'; import SectionTitle from '../components/SectionTitle'; +import EventCard from '../components/Events/EventCard'; +import { Event } from '../interfaces/event'; const Feature = ({ icon, title, text }) => (
@@ -18,6 +21,24 @@ const Feature = ({ icon, title, text }) => ( export default function LandingPage() { const title = 'EventCoord Hub' + const [events, setEvents] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchEvents = async () => { + try { + const response = await axios.get(`/api/events/public`); + setEvents(response.data.rows); + setLoading(false); + } catch (err) { + setError(err); + setLoading(false); + } + }; + + fetchEvents(); + }, []); return (
@@ -79,6 +100,34 @@ export default function LandingPage() {
+ {/* Upcoming Events Section */} +
+
+

Upcoming Events

+ {loading &&

Loading events...

} + {error &&

Error loading events. Please try again later.

} + {!loading && !error && ( + <> +
+ {events.map((event) => ( + + ))} +
+
+ +
+ + )} +
+
+ {/* Footer */}