diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 00a8785..254b73c 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -/// +/// // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/frontend/src/components/CardBoxWidget.tsx b/frontend/src/components/CardBoxWidget.tsx new file mode 100644 index 0000000..7e79087 --- /dev/null +++ b/frontend/src/components/CardBoxWidget.tsx @@ -0,0 +1,31 @@ +import { mdiTrendingDown, mdiTrendingUp, mdiTrendingNeutral } from '@mdi/js' +import React, { ReactNode } from 'react' +import BaseIcon from './BaseIcon' +import CardBox from './CardBox' + +type Props = { + icon: string + iconColor: string + number: number + label: string + children?: ReactNode + className?: string +} + +export default function CardBoxWidget(props: Props) { + return ( + +
+
+ {props.icon && ( + + )} +
+

{props.label}

+

{props.number}

+
+
+
+
+ ) +} diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index e5c6381..633fd8e 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -1,410 +1,141 @@ -import * as icon from '@mdi/js'; -import Head from 'next/head' -import React from 'react' -import axios from 'axios'; -import type { ReactElement } from 'react' -import LayoutAuthenticated from '../layouts/Authenticated' -import SectionMain from '../components/SectionMain' -import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' -import BaseIcon from "../components/BaseIcon"; -import { getPageTitle } from '../config' -import Link from "next/link"; - -import { hasPermission } from "../helpers/userPermissions"; -import { fetchWidgets } from '../stores/roles/rolesSlice'; -import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator'; -import { SmartWidget } from '../components/SmartWidget/SmartWidget'; - +import React, { useEffect } from 'react'; +import type { ReactElement } from 'react'; +import Head from 'next/head'; +import Link from 'next/link'; +import { mdiChartTimelineVariant, mdiCalendar, mdiAccountMultiple, mdiCalendarClock } from '@mdi/js'; +import LayoutAuthenticated from '../layouts/Authenticated'; +import SectionMain from '../components/SectionMain'; +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'; +import CardBoxWidget from '../components/CardBoxWidget'; +import CardBox from '../components/CardBox'; +import { getPageTitle } from '../config'; import { useAppDispatch, useAppSelector } from '../stores/hooks'; +import { fetch as fetchEvents } from '../stores/events/eventsSlice'; +import { fetch as fetchGuests } from '../stores/guests/guestsSlice'; +import {hasPermission} from "../helpers/userPermissions"; + const Dashboard = () => { - const dispatch = useAppDispatch(); - const iconsColor = useAppSelector((state) => state.style.iconsColor); - const corners = useAppSelector((state) => state.style.corners); - const cardsStyle = useAppSelector((state) => state.style.cardsStyle); + const dispatch = useAppDispatch(); + const { currentUser } = useAppSelector((state) => state.auth); + const events = useAppSelector((state) => state.events.events); + const eventsCount = useAppSelector((state) => state.events.count); + const guestsCount = useAppSelector((state) => state.guests.count); - const loadingMessage = 'Loading...'; - - - const [users, setUsers] = React.useState(loadingMessage); - const [roles, setRoles] = React.useState(loadingMessage); - const [permissions, setPermissions] = React.useState(loadingMessage); - const [events, setEvents] = React.useState(loadingMessage); - const [venues, setVenues] = React.useState(loadingMessage); - const [vendors, setVendors] = React.useState(loadingMessage); - const [schedules, setSchedules] = React.useState(loadingMessage); - const [guests, setGuests] = React.useState(loadingMessage); - const [budgets, setBudgets] = React.useState(loadingMessage); - - - const [widgetsRole, setWidgetsRole] = React.useState({ - role: { value: '', label: '' }, - }); - const { currentUser } = useAppSelector((state) => state.auth); - const { isFetchingQuery } = useAppSelector((state) => state.openAi); - - const { rolesWidgets, loading } = useAppSelector((state) => state.roles); - - - async function loadData() { - const entities = ['users','roles','permissions','events','venues','vendors','schedules','guests','budgets',]; - const fns = [setUsers,setRoles,setPermissions,setEvents,setVenues,setVendors,setSchedules,setGuests,setBudgets,]; - - const requests = entities.map((entity, index) => { - - if(hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) { - return axios.get(`/${entity.toLowerCase()}/count`); - } else { - fns[index](null); - return Promise.resolve({data: {count: null}}); - } - - }); - - Promise.allSettled(requests).then((results) => { - results.forEach((result, i) => { - if (result.status === 'fulfilled') { - fns[i](result.value.data.count); - } else { - fns[i](result.reason.message); - } - }); - }); + useEffect(() => { + if(hasPermission(currentUser, 'READ_EVENTS')) { + dispatch(fetchEvents({ query: '?sort=createdAt,DESC&limit=5' })); } - - async function getWidgets(roleId) { - await dispatch(fetchWidgets(roleId)); + if(hasPermission(currentUser, 'READ_GUESTS')) { + dispatch(fetchGuests({ query: '' })); } - React.useEffect(() => { - if (!currentUser) return; - loadData().then(); - setWidgetsRole({ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } }); - }, [currentUser]); + }, [dispatch, currentUser]); + + const upcomingEvents = events.filter( + (event) => new Date(event.date) > new Date() + ); - React.useEffect(() => { - if (!currentUser || !widgetsRole?.role?.value) return; - getWidgets(widgetsRole?.role?.value || '').then(); - }, [widgetsRole?.role?.value]); - return ( <> - - {getPageTitle('Overview')} - + {getPageTitle('Event Dashboard')} - + {''} - - {hasPermission(currentUser, 'CREATE_ROLES') && } - {!!rolesWidgets.length && - hasPermission(currentUser, 'CREATE_ROLES') && ( -

- {`${widgetsRole?.role?.label || 'Users'}'s widgets`} -

- )} -
- {(isFetchingQuery || loading) && ( -
- {' '} - Loading widgets... -
- )} - - { rolesWidgets && - rolesWidgets.map((widget) => ( - - ))} +
+ + +
- {!!rolesWidgets.length &&
} - -
- - - {hasPermission(currentUser, 'READ_USERS') && -
-
-
-
- Users -
-
- {users} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ROLES') && -
-
-
-
- Roles -
-
- {roles} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_PERMISSIONS') && -
-
-
-
- Permissions -
-
- {permissions} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_EVENTS') && -
-
-
-
- Events -
-
- {events} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_VENUES') && -
-
-
-
- Venues -
-
- {venues} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_VENDORS') && -
-
-
-
- Vendors -
-
- {vendors} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_SCHEDULES') && -
-
-
-
- Schedules -
-
- {schedules} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_GUESTS') && -
-
-
-
- Guests -
-
- {guests} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_BUDGETS') && -
-
-
-
- Budgets -
-
- {budgets} -
-
-
- -
-
-
- } - - -
+ + {''} + + + +
+ + + + + + + + + + + + {events.length > 0 ? ( + events.map((event) => ( + + + + + + + + )) + ) : ( + + + + )} + +
+ Event Name + + Date + + Location + + Status + + Actions +
{event.name} + {new Date(event.date).toLocaleDateString()} + {event.location} + + {new Date(event.date) < new Date() ? 'Past' : 'Upcoming'} + + + + View + +
+ No events found. +
+
+
- ) -} + ); +}; Dashboard.getLayout = function getLayout(page: ReactElement) { - return {page} -} + return {page}; +}; -export default Dashboard +export default Dashboard; diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 9808e34..3ac2144 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,173 +1,98 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import type { ReactElement } from 'react'; import Head from 'next/head'; import Link from 'next/link'; import BaseButton from '../components/BaseButton'; -import CardBox from '../components/CardBox'; -import SectionFullScreen from '../components/SectionFullScreen'; import LayoutGuest from '../layouts/Guest'; -import BaseDivider from '../components/BaseDivider'; -import BaseButtons from '../components/BaseButtons'; import { getPageTitle } from '../config'; -import { useAppSelector } from '../stores/hooks'; -import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; -import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'; +import SectionTitle from '../components/SectionTitle'; +const Feature = ({ icon, title, text }) => ( +
+
{icon}
+

{title}

+

{text}

+
+); -export default function Starter() { - const [illustrationImage, setIllustrationImage] = useState({ - src: undefined, - photographer: undefined, - photographer_url: undefined, - }) - const [illustrationVideo, setIllustrationVideo] = useState({video_files: []}) - const [contentType, setContentType] = useState('image'); - const [contentPosition, setContentPosition] = useState('left'); - const textColor = useAppSelector((state) => state.style.linkColor); - - const title = 'EventCoord Hub' - - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); - - const imageBlock = (image) => ( - - ); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- - -
) - } - }; +export default function LandingPage() { + const title = 'EventCoord Hub' return ( -
+
- {getPageTitle('Starter Page')} + {getPageTitle('Welcome')} - -
- {contentType === 'image' && contentPosition !== 'background' - ? imageBlock(illustrationImage) - : null} - {contentType === 'video' && contentPosition !== 'background' - ? videoBlock(illustrationVideo) - : null} -
- - - -
-

This is a React.js/Node.js app generated by the Flatlogic Web App Generator

-

For guides and documentation please check - your local README.md and the Flatlogic documentation

-
- - - - - - -
+ {/* Hero Section */} +
+
+

EventCoord Hub

+

Event planning with venues, vendors, schedules, guests, and budgets. Built for fast coordination and status tracking.

+
-
- -
-

© 2024 {title}. All rights reserved

- - Privacy Policy - -
+ + {/* Features Section */} +
+
+ Features +
+ + + + + + +
+
+
+ + {/* Footer */} +
+
+

© {new Date().getFullYear()} {title}. All rights reserved

+ + Privacy Policy + +
+
); } -Starter.getLayout = function getLayout(page: ReactElement) { +LandingPage.getLayout = function getLayout(page: ReactElement) { return {page}; };