diff --git a/frontend/src/config.ts b/frontend/src/config.ts index a9783c8..2eb0fb4 100644 --- a/frontend/src/config.ts +++ b/frontend/src/config.ts @@ -8,8 +8,8 @@ export const localStorageStyleKey = 'style' export const containerMaxW = 'xl:max-w-full xl:mx-auto 2xl:mx-20' -export const appTitle = 'created by Flatlogic generator!' +export const appTitle = 'L\'Escale Marocaine' export const getPageTitle = (currentPageTitle: string) => `${currentPageTitle} — ${appTitle}` -export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || '' +export const tinyKey = process.env.NEXT_PUBLIC_TINY_KEY || '' \ No newline at end of file diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index f659d63..ec77b0a 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -7,7 +7,7 @@ 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 { getPageTitle, appTitle } from '../config' import Link from "next/link"; import { hasPermission } from "../helpers/userPermissions"; @@ -16,15 +16,15 @@ import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator'; import { SmartWidget } from '../components/SmartWidget/SmartWidget'; import { useAppDispatch, useAppSelector } from '../stores/hooks'; + 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 loadingMessage = 'Loading...'; + const loadingMessage = '...'; - const [users, setUsers] = React.useState(loadingMessage); const [roles, setRoles] = React.useState(loadingMessage); const [permissions, setPermissions] = React.useState(loadingMessage); @@ -39,29 +39,24 @@ const Dashboard = () => { const [suppliers, setSuppliers] = React.useState(loadingMessage); const [ingredients, setIngredients] = 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','restaurants','categories','dishes','menus','orders','tables','reservations','reviews','suppliers','ingredients',]; - const fns = [setUsers,setRoles,setPermissions,setRestaurants,setCategories,setDishes,setMenus,setOrders,setTables,setReservations,setReviews,setSuppliers,setIngredients,]; + const entities = ['users','roles','permissions','restaurants','categories','dishes','menus','orders','tables','reservations','reviews','suppliers','ingredients']; + const fns = [setUsers,setRoles,setPermissions,setRestaurants,setCategories,setDishes,setMenus,setOrders,setTables,setReservations,setReviews,setSuppliers,setIngredients]; 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) => { @@ -74,10 +69,11 @@ const Dashboard = () => { }); }); } - - async function getWidgets(roleId) { + + async function getWidgets(roleId: string) { await dispatch(fetchWidgets(roleId)); } + React.useEffect(() => { if (!currentUser) return; loadData().then(); @@ -88,439 +84,138 @@ const Dashboard = () => { if (!currentUser || !widgetsRole?.role?.value) return; getWidgets(widgetsRole?.role?.value || '').then(); }, [widgetsRole?.role?.value]); - - return ( - <> - - - {getPageTitle('Overview')} - - - - - {''} - - - {hasPermission(currentUser, 'CREATE_ROLES') && } - {!!rolesWidgets.length && - hasPermission(currentUser, 'CREATE_ROLES') && ( -

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

- )} -
- {(isFetchingQuery || loading) && ( -
- {' '} - Loading widgets... + return ( + <> + + {getPageTitle('Dashboard')} + + + + {''} + + +
+

+ Welcome back, {currentUser?.firstName || 'Manager'}! +

+

+ Here is what's happening at L'Escale Marocaine today. +

- )} - { rolesWidgets && - rolesWidgets.map((widget) => ( - - ))} -
+ )} - {!!rolesWidgets.length &&
} - -
- - - {hasPermission(currentUser, 'READ_USERS') && -
-
-
-
- Users -
-
- {users} -
-
-
+
+ {(isFetchingQuery || loading) && ( +
+ Loading metrics...
-
+ )} + + {rolesWidgets && rolesWidgets.map((widget) => ( + + ))}
- } - - {hasPermission(currentUser, 'READ_ROLES') && -
-
-
-
- Roles + +
+ {hasPermission(currentUser, 'READ_RESERVATIONS') && ( + +
+
+
+
Reservations
+
{reservations}
+
+ +
-
- {roles} + + )} + + {hasPermission(currentUser, 'READ_ORDERS') && ( + +
+
+
+
Active Orders
+
{orders}
+
+ +
-
-
- -
-
+ + )} + + {hasPermission(currentUser, 'READ_DISHES') && ( + +
+
+
+
Menu Items
+
{dishes}
+
+ +
+
+ + )}
- } - - {hasPermission(currentUser, 'READ_PERMISSIONS') && -
-
-
-
- Permissions -
-
- {permissions} -
-
-
- -
-
+ +
+ {/* Secondary Metrics */} + {[ + { label: 'Restaurants', val: restaurants, path: '/restaurants/restaurants-list', icon: icon.mdiStorefront, color: 'text-blue-500', perm: 'READ_RESTAURANTS' }, + { label: 'Categories', val: categories, path: '/categories/categories-list', icon: icon.mdiShape, color: 'text-purple-500', perm: 'READ_CATEGORIES' }, + { label: 'Tables', val: tables, path: '/tables/tables-list', icon: icon.mdiTableLarge, color: 'text-yellow-600', perm: 'READ_TABLES' }, + { label: 'Reviews', val: reviews, path: '/reviews/reviews-list', icon: icon.mdiStar, color: 'text-red-400', perm: 'READ_REVIEWS' }, + ].map((item, idx) => ( + hasPermission(currentUser, item.perm) && ( + +
+
+ +
+
{item.label}
+
{item.val}
+
+
+
+ + ) + ))}
- } - - {hasPermission(currentUser, 'READ_RESTAURANTS') && -
-
-
-
- Restaurants -
-
- {restaurants} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_CATEGORIES') && -
-
-
-
- Categories -
-
- {categories} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_DISHES') && -
-
-
-
- Dishes -
-
- {dishes} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_MENUS') && -
-
-
-
- Menus -
-
- {menus} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ORDERS') && -
-
-
-
- Orders -
-
- {orders} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_TABLES') && -
-
-
-
- Tables -
-
- {tables} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_RESERVATIONS') && -
-
-
-
- Reservations -
-
- {reservations} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_REVIEWS') && -
-
-
-
- Reviews -
-
- {reviews} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_SUPPLIERS') && -
-
-
-
- Suppliers -
-
- {suppliers} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_INGREDIENTS') && -
-
-
-
- Ingredients -
-
- {ingredients} -
-
-
- -
-
-
- } - - -
- - - ) + + + ) } Dashboard.getLayout = function getLayout(page: ReactElement) { - return {page} + return {page} } -export default Dashboard +export default Dashboard \ No newline at end of file diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 275458d..087e293 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,166 +1,132 @@ - import React, { useEffect, useState } 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 { getPageTitle, appTitle } from '../config'; +import { getPexelsImage } from '../helpers/pexels'; +import { mdiFood, mdiSilverwareVariant, mdiTimelineClockOutline } from '@mdi/js'; +import BaseIcon from '../components/BaseIcon'; +export default function LandingPage() { + const [heroImage, setHeroImage] = useState(null) -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 = 'AI App Draft' - - // Fetch Pexels image/video useEffect(() => { async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); + // We search for Moroccan food specifically to match the theme + const image = await getPexelsImage('Moroccan Food'); + setHeroImage(image); } fetchData(); }, []); - const imageBlock = (image) => ( -
-
- - Photo by {image?.photographer} on Pexels - + const features = [ + { + title: 'Authentic Flavors', + description: 'Experience the rich heritage of Moroccan spices and traditional cooking techniques.', + icon: mdiFood, + }, + { + title: 'Elegant Dining', + description: 'A refined atmosphere inspired by the beautiful Riads of Marrakech.', + icon: mdiSilverwareVariant, + }, + { + title: 'Easy Reservations', + description: 'Book your table in seconds and manage your dining experience online.', + icon: mdiTimelineClockOutline, + } + ] + + return ( +
+ + {getPageTitle('Authentic Moroccan Cuisine')} + + + {/* Hero Section */} + +
+ {/* Background Overlay */} +
+
+
+ + {/* Content */} +
+

+ {appTitle} +

+

+ A journey through the vibrant colors and exquisite tastes of Morocco. +

+
+ + + Admin Portal + +
+
+
+
+ + {/* Features / Story */} +
+
+
+

Tradition in Every Bite

+
+
+ +
+ {features.map((feature, index) => ( +
+
+ +
+

{feature.title}

+

+ {feature.description} +

+
+ ))} +
+
+ + {/* Footer */} +
+
+

{appTitle}

+

Authentic Moroccan Dining Experience

+
+ Menu + Gallery + Contact +
+

+ © 2026 {appTitle}. Crafted for an authentic experience. +

+
+
); - - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- - -
) - } - }; - - return ( -
- - {getPageTitle('Starter Page')} - - - -
- {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

-
- - - - - -
-
-
-
-
-

© 2026 {title}. All rights reserved

- - Privacy Policy - -
- -
- ); } -Starter.getLayout = function getLayout(page: ReactElement) { - return {page}; -}; - +LandingPage.getLayout = function getLayout(page: ReactElement) { + return {page}; +}; \ No newline at end of file