new menu structure tested new version will be deployed

This commit is contained in:
Berkay 2025-05-28 16:05:23 +03:00
parent 6ce95b5df6
commit b714585a2e
18 changed files with 679 additions and 402 deletions

View File

@ -31,49 +31,49 @@ services:
# cpus: 1
# mem_limit: 2048m
account_service:
container_name: account_service
build:
context: .
dockerfile: api_services/api_builds/account_service/Dockerfile
env_file:
- api_env.env
networks:
- wag-services
environment:
- API_PATH=app:app
- API_HOST=0.0.0.0
- API_PORT=8004
- API_LOG_LEVEL=info
- API_RELOAD=1
- API_APP_NAME=evyos-account-api-gateway
- API_TITLE=WAG API Account Api Gateway
- API_DESCRIPTION=This api is serves as web account api gateway only to evyos web services.
- API_APP_URL=https://account_service
ports:
- "8004:8004"
# account_service:
# container_name: account_service
# build:
# context: .
# dockerfile: api_services/api_builds/account_service/Dockerfile
# env_file:
# - api_env.env
# networks:
# - wag-services
# environment:
# - API_PATH=app:app
# - API_HOST=0.0.0.0
# - API_PORT=8004
# - API_LOG_LEVEL=info
# - API_RELOAD=1
# - API_APP_NAME=evyos-account-api-gateway
# - API_TITLE=WAG API Account Api Gateway
# - API_DESCRIPTION=This api is serves as web account api gateway only to evyos web services.
# - API_APP_URL=https://account_service
# ports:
# - "8004:8004"
building_service:
container_name: building_service
build:
context: .
dockerfile: api_services/api_builds/building_service/Dockerfile
env_file:
- api_env.env
networks:
- wag-services
environment:
- API_PATH=app:app
- API_HOST=0.0.0.0
- API_PORT=8006
- API_LOG_LEVEL=info
- API_RELOAD=1
- API_APP_NAME=evyos-building-api-gateway
- API_TITLE=WAG API Building Api Gateway
- API_DESCRIPTION=This api is serves as web building api gateway only to evyos web services.
- API_APP_URL=https://building_service
ports:
- "8006:8006"
# building_service:
# container_name: building_service
# build:
# context: .
# dockerfile: api_services/api_builds/building_service/Dockerfile
# env_file:
# - api_env.env
# networks:
# - wag-services
# environment:
# - API_PATH=app:app
# - API_HOST=0.0.0.0
# - API_PORT=8006
# - API_LOG_LEVEL=info
# - API_RELOAD=1
# - API_APP_NAME=evyos-building-api-gateway
# - API_TITLE=WAG API Building Api Gateway
# - API_DESCRIPTION=This api is serves as web building api gateway only to evyos web services.
# - API_APP_URL=https://building_service
# ports:
# - "8006:8006"
identity_service:
container_name: identity_service

View File

@ -9,11 +9,7 @@ const MainEnPage: React.FC<MaindasboardPageProps> = async ({ params, searchParam
const searchParameters = await searchParams;
const tokenValid = await checkAccessTokenIsValid()
if (!tokenValid) { redirect("/auth/login") }
return (
<div className="flex flex-col items-center justify-center">
<DashboardLayout params={parameters} searchParams={searchParameters} lang="en" />
</div>
);
return <div className="flex flex-col items-center justify-center"><DashboardLayout params={parameters} searchParams={searchParameters} lang="en" /></div>
}
export default MainEnPage;

View File

@ -3,33 +3,28 @@ import ContentToRenderNoPage from "@/pages/mutual/noContent/page";
import pageIndexMulti from "@/pages/multi/index";
const PageToBeChildrendMulti: React.FC<ContentProps> = ({
lang,
activePageUrl,
mode,
userData,
userLoading,
userError,
selectionData,
selectionLoading,
selectionError,
useReloadWindow
onlineData,
onlineLoading,
onlineError,
searchParams,
refreshOnline,
updateOnline,
refreshUser,
updateUser,
}) => {
const pageComponents = pageIndexMulti[activePageUrl];
if (!pageComponents) { return <ContentToRenderNoPage lang={lang} /> }
if (!pageComponents) { return <ContentToRenderNoPage lang={onlineData.lang} /> }
const ComponentKey = Object.keys(pageComponents)[0];
const PageComponent = pageComponents[ComponentKey];
if (!PageComponent) { return <ContentToRenderNoPage lang={lang} /> }
if (!PageComponent) { return <ContentToRenderNoPage lang={onlineData.lang} /> }
return <PageComponent
lang={lang}
activePageUrl={activePageUrl}
mode={mode}
userData={userData}
userLoading={userLoading}
userError={userError}
selectionData={selectionData}
selectionLoading={selectionLoading}
selectionError={selectionError}
useReloadWindow={useReloadWindow}
activePageUrl={activePageUrl} searchParams={searchParams}
userData={userData} userLoading={userLoading} userError={userError} refreshUser={refreshUser} updateUser={updateUser}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
/>;
}

View File

@ -1,82 +1,68 @@
'use client';
import { FC, Suspense, useMemo, memo } from "react";
import { FC, Suspense, memo } from "react";
import { ContentProps, ModeTypes, ModeTypesList } from "@/validations/mutual/dashboard/props";
import { LanguageTypes } from "@/validations/mutual/language/validations";
import PageToBeChildrendMulti from "./PageToBeChildrendMulti";
import LoadingContent from "@/components/mutual/loader/component";
// Create a memoized version of PageToBeChildrendMulti to prevent unnecessary re-renders
const MemoizedMultiPage = memo(PageToBeChildrendMulti);
// const ContentComponent: FC<ContentProps> = async ({ lang, translations, activePageUrl, isMulti, mode }) => {
// const modeFromQuery = ModeTypesList.includes(mode || '') ? mode : 'shortList'
// const renderProps = { lang, translations, activePageUrl, mode: modeFromQuery as ModeTypes }
// const PageToBeChildrend = isMulti ? PageToBeChildrendMulti : PageToBeChildrendSingle
// const loadingContent = <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" />
// const classNameDiv = "fixed top-24 left-80 right-0 py-10 px-15 border-emerald-150 border-l-2 overflow-y-auto h-[calc(100vh-64px)]"
// return <div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrend {...renderProps} /></Suspense></div>
// };
const translations = {
en: {
errorLoadingContent: "Error Loading Content",
contentArea: "Content Area",
contentLoading: "Content Loading",
contentLoadingDescription: "The requested page is currently unavailable or still loading.",
pageUrl: "Page URL",
language: "Language",
},
tr: {
errorLoadingContent: "İçerik Yüklenirken Hata",
contentArea: "İçerik Alanı",
contentLoading: "İçerik Yükleniyor",
contentLoadingDescription: "İçerik Yüklenirken Hata",
pageUrl: "Sayfa URL",
language: "Dil",
}
}
// Static fallback component to avoid state updates during render
const FallbackContent: FC<{ lang: string; activePageUrl: string; mode: string }> = memo(({ lang, activePageUrl, mode }) => (
const FallbackContent: FC<{ lang: LanguageTypes; activePageUrl: string }> = memo(({ lang, activePageUrl }) => (
<div className="p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-4">Content Loading</h2>
<p className="text-gray-600 mb-4">The requested page is currently unavailable or still loading.</p>
<h2 className="text-2xl font-bold mb-4">{translations[lang].contentLoading}</h2>
<p className="text-gray-600 mb-4">{translations[lang].contentLoadingDescription}</p>
<div className="p-4 bg-blue-50 border border-blue-200 rounded-md">
<p className="text-sm text-blue-700">Page URL: {activePageUrl}</p>
<p className="text-sm text-blue-700">Language: {lang}</p>
<p className="text-sm text-blue-700">Mode: {mode}</p>
<p className="text-sm text-blue-700">{translations[lang].pageUrl}: {activePageUrl}</p>
<p className="text-sm text-blue-700">{translations[lang].language}: {lang}</p>
</div>
</div>
));
const ContentComponent: FC<ContentProps> = ({
lang, activePageUrl, mode,
userData, userLoading, userError,
selectionData, selectionLoading, selectionError,
searchParams, activePageUrl, mode, userData, userLoading, userError, refreshUser, updateUser,
onlineData, onlineLoading, onlineError, refreshOnline, updateOnline,
}) => {
const page = useMemo(() => { const extractedPage = activePageUrl.split('/').pop(); return extractedPage }, [activePageUrl]);
const modeFromQuery: string = ModeTypesList.includes(mode || '') ? (mode || 'shortList') : 'shortList';
const loadingContent = <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" />;
const classNameDiv = "fixed top-24 left-80 right-0 py-10 px-15 border-emerald-150 border-l-2 overflow-y-auto h-[calc(100vh-64px)]";
const lang = onlineData?.lang as LanguageTypes || 'en';
if (selectionLoading || userLoading) { return <div className={classNameDiv}>{loadingContent}</div> }
if (selectionError || userError) {
return <div className={classNameDiv}>
<div className="p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-4 text-red-600">Error Loading Content</h2>
<p className="text-gray-600 mb-4">{selectionError || userError}</p>
if (userLoading) { return <div className={classNameDiv}>{loadingContent}</div> }
if (userError) {
return (
<div className={classNameDiv}><div className="p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-4 text-red-600">{translations[lang].errorLoadingContent}</h2><p className="text-gray-600 mb-4">{userError}</p></div>
</div>
</div>
)
}
return (
<div className={classNameDiv}>
<Suspense fallback={loadingContent}>
<div className="p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-4">Content Area</h2>
{/* Fallback Content */}
{(!userData || !selectionData) && (
<FallbackContent
lang={lang || ''}
activePageUrl={activePageUrl || ''}
mode={modeFromQuery}
/>
)}
{/* Wrap component in memo to prevent unnecessary re-renders */}
<h2 className="text-2xl font-bold mb-4">{translations[lang].contentArea}</h2>
{(!userData) && (<FallbackContent lang={lang} activePageUrl={activePageUrl || ''} />)}
<MemoizedMultiPage
lang={lang || ''}
activePageUrl={activePageUrl || ''}
mode={modeFromQuery as ModeTypes}
userData={userData}
userLoading={userLoading}
userError={userError}
selectionData={selectionData}
selectionLoading={selectionLoading}
selectionError={selectionError}
/>
activePageUrl={activePageUrl || ''} searchParams={searchParams}
userData={userData} userLoading={userLoading} userError={userError} refreshUser={refreshUser} updateUser={updateUser}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
</div>
</Suspense>
</div>

View File

@ -2,6 +2,7 @@
import { FC } from "react";
import { langGetKey } from "@/lib/langGet";
import { FooterProps } from "@/validations/mutual/dashboard/props";
import { LanguageTypes } from "@/validations/mutual/language/validations";
const translations = {
en: {
@ -15,9 +16,11 @@ const translations = {
}
const FooterComponent: FC<FooterProps> = ({
lang, activePageUrl, useReloadWindow, configData, configLoading, configError
activePageUrl, configData, configLoading, configError,
onlineData, onlineLoading, onlineError
}) => {
// Use the config context hook
const lang = onlineData?.lang as LanguageTypes || 'en';
return (
<div className="fixed text-center bottom-0 left-0 right-0 h-16 p-4 border-t border-emerald-150 border-t-2 shadow-sm backdrop-blur-sm bg-emerald-50">

View File

@ -3,6 +3,7 @@ import { FC } from "react";
import { HeaderProps } from "@/validations/mutual/dashboard/props";
import { langGetKey } from "@/lib/langGet";
import LanguageSelectionComponent from "@/components/mutual/languageSelection/component";
import { LanguageTypes } from "@/validations/mutual/language/validations";
const translations = {
en: {
@ -16,10 +17,10 @@ const translations = {
}
const HeaderComponent: FC<HeaderProps> = ({
lang, activePageUrl, prefix, mode,
onlineData, onlineLoading, onlineError, refreshOnline, updateOnline,
userData, userLoading, userError,
activePageUrl, onlineData, onlineLoading, onlineError, refreshOnline, updateOnline,
userData, userLoading, userError, refreshUser, updateUser
}) => {
const lang = onlineData?.lang as LanguageTypes || 'en';
return (
<div className="flex justify-between h-24 items-center p-4 border-emerald-150 border-b-2 shadow-sm backdrop-blur-sm sticky top-0 z-50 bg-emerald-50">
<div className="flex flex-row justify-center items-center">
@ -29,12 +30,11 @@ const HeaderComponent: FC<HeaderProps> = ({
<div className="flex items-center">
{!onlineLoading && onlineData && onlineData.userType && (
<div className="mr-4 text-sm">
<span className="font-semibold">{onlineData.lang || lang}</span>
<span className="font-semibold">{lang}</span>
<span className="ml-2 text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full">{onlineData.userType}</span>
</div>
)}<LanguageSelectionComponent
lang={lang} activePage={activePageUrl} prefix={prefix}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
activePage={activePageUrl} onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
/>
</div>
</div>

View File

@ -1,6 +1,7 @@
'use client';
import { FC, Suspense } from "react";
import { MenuProps } from "@/validations/mutual/dashboard/props";
import { LanguageTypes } from "@/validations/mutual/language/validations";
import UserProfileSection from "./userProfileSection";
import ClientSelectionSection from "./clientSelectionSection";
@ -11,7 +12,7 @@ import MenuEmptyState from "./menuEmptyState";
import LoadingContent from "@/components/mutual/loader/component";
const MenuComponent: FC<MenuProps> = ({
lang, activePageUrl, useReloadWindow, availableApplications,
activePageUrl, availableApplications, prefix,
onlineData, onlineLoading, onlineError,
userData, userLoading, userError,
selectionData, selectionLoading, selectionError,
@ -21,21 +22,21 @@ const MenuComponent: FC<MenuProps> = ({
if (menuError) { return <MenuErrorState error={menuError} />; } // Render error state
if (availableApplications.length === 0) { return <MenuEmptyState />; } // Render empty state
function handleClientSelection(client: any) { console.log('Client selected:', client) }
const lang = onlineData?.lang as LanguageTypes || 'en';
return (
<div className="fixed top-24 p-5 left-0 right-0 w-80 border-emerald-150 border-r-2 overflow-y-auto h-[calc(100vh-6rem)]">
<div className="flex flex-col">
{/* User Profile Section */}
<Suspense fallback={<div><LoadingContent height="h-16" size="w-36 h-48" key={"loading-conent"} plane="h-full w-full" /></div>}>
<Suspense fallback={<div><LoadingContent height="h-16" size="w-36 h-48" key={"loading-content"} plane="h-full w-full" /></div>}>
<UserProfileSection userData={userData} onlineData={onlineData} />
</Suspense>
{/* Client Selection Section */}
<Suspense fallback={<div><LoadingContent height="h-16" size="w-36 h-48" key={"loading-conent"} plane="h-full w-full" /></div>}>
<Suspense fallback={<div><LoadingContent height="h-16" size="w-36 h-48" key={"loading-content"} plane="h-full w-full" /></div>}>
<ClientSelectionSection selectionData={selectionData} initialSelectedClient={selectionData} onClientSelect={handleClientSelection} />
</Suspense>
{/* Menu Items Section */}
<Suspense fallback={<div><LoadingContent height="h-16" size="w-36 h-48" key={"loading-conent"} plane="h-full w-full" /></div>}>
<MenuItemsSection availableApplications={availableApplications} activePageUrl={activePageUrl} lang={lang} />
<Suspense fallback={<div><LoadingContent height="h-16" size="w-36 h-48" key={"loading-content"} plane="h-full w-full" /></div>}>
<MenuItemsSection availableApplications={availableApplications} activePageUrl={activePageUrl} lang={lang} prefix={prefix} />
</Suspense>
</div>
</div>

View File

@ -1,14 +1,13 @@
'use client';
import { FC, useState, useEffect } from "react";
import React, { FC, useEffect, useState } from "react";
import { menuTranslation } from "@/languages/mutual/menu";
import FirstLayerDropdown from "./firstLayerComponent";
import SecondLayerDropdown from "./secondLayerComponent";
import ThirdLayerDropdown from "./thirdLayerComponent";
import { parseURlFormString } from "@/lib/menuGet";
import { menuTranslation } from "@/languages/mutual/menu";
// Define types for menu structure
type ThirdLayerItem = Record<string, any>;
type TranslationItem = { value: string; key: string };
type ThirdLayerItemData = { path: string; translation: TranslationItem[] };
type ThirdLayerItem = Record<string, ThirdLayerItemData>;
type SecondLayerItems = Record<string, ThirdLayerItem>;
type FirstLayerItems = Record<string, SecondLayerItems>;
type MenuStructure = FirstLayerItems;
@ -17,159 +16,86 @@ interface MenuItemsSectionProps {
availableApplications: string[];
activePageUrl: string;
lang: string;
prefix?: string;
}
// Helper function to get translation for a URL path
const getMenuTranslation = (translations: any, urlPath: string) => {
if (translations[urlPath]) {
return translations[urlPath];
}
const cleanPath = urlPath.startsWith('/') ? urlPath.substring(1) : urlPath;
if (translations[cleanPath]) {
return translations[cleanPath];
}
const pathWithSlash = urlPath.startsWith('/') ? urlPath : `/${urlPath}`;
if (translations[pathWithSlash]) {
return translations[pathWithSlash];
}
const keys = Object.keys(translations);
for (const key of keys) {
const cleanKey = key.startsWith('/') ? key.substring(1) : key;
const cleanUrlPath = urlPath.startsWith('/') ? urlPath.substring(1) : urlPath;
const menuStaticTranslation = {
tr: { menu: "Menü" },
en: { menu: "Menu" }
}
if (cleanUrlPath.includes(cleanKey) || cleanKey.includes(cleanUrlPath)) {
return translations[key];
}
}
const parts = urlPath.split('/');
return parts[parts.length - 1] || urlPath;
};
const MenuItemsSection: FC<MenuItemsSectionProps> = ({ availableApplications, activePageUrl, lang }) => {
const MenuItemsSection: FC<MenuItemsSectionProps> = ({ availableApplications, activePageUrl, lang, prefix }) => {
const [expandedFirstLayer, setExpandedFirstLayer] = useState<string | null>(null);
const [expandedSecondLayer, setExpandedSecondLayer] = useState<string | null>(null);
const [menuStructure, setMenuStructure] = useState<MenuStructure>({});
const menuTranslationWLang = menuTranslation[lang as keyof typeof menuTranslation];
const activePathLayers = parseURlFormString(activePageUrl).data;
const activeFirstLayer = activePathLayers[0] || null;
const activeSecondLayer = activePathLayers[1] || null;
const activeThirdLayer = activePathLayers.slice(2, activePathLayers.length).join("/");
const activeParsedLayer = (menuTranslationWLang[activePageUrl as keyof typeof menuTranslationWLang] as unknown as TranslationItem[]) || [];
const activeFirstLayer = activeParsedLayer[0] ? activeParsedLayer[0].key : null;
const activeSecondLayer = activeParsedLayer[1] ? activeParsedLayer[1].key : null;
const activeThirdLayer = activeParsedLayer[2] ? activeParsedLayer[2].key : null;
useEffect(() => {
const newMenuStructure: MenuStructure = {};
availableApplications.forEach((appPath: string) => {
const cleanPath = appPath.startsWith('/') ? appPath.substring(1) : appPath;
const pathParts = cleanPath.split('/');
if (pathParts.length >= 3) {
const firstLayer = pathParts[0];
const secondLayer = pathParts[1];
const thirdLayer = pathParts.slice(2).join('/');
const pathTranslation = menuTranslationWLang[appPath as keyof typeof menuTranslationWLang] as unknown as TranslationItem[] | undefined;
if (pathTranslation && pathTranslation.length >= 3) {
const firstLayer = pathTranslation[0] ? pathTranslation[0].key : '';
const secondLayer = pathTranslation[1] ? pathTranslation[1].key : '';
const thirdLayer = pathTranslation[2] ? pathTranslation[2].key : '';
if (!newMenuStructure[firstLayer]) { newMenuStructure[firstLayer] = {} }
if (!newMenuStructure[firstLayer][secondLayer]) { newMenuStructure[firstLayer][secondLayer] = {} }
newMenuStructure[firstLayer][secondLayer][thirdLayer] = true;
newMenuStructure[firstLayer][secondLayer][thirdLayer] = { path: appPath, translation: pathTranslation };
}
});
setMenuStructure(newMenuStructure);
}, [availableApplications]);
}, [availableApplications, menuTranslationWLang]);
useEffect(() => {
if (activeFirstLayer) {
setExpandedFirstLayer(activeFirstLayer);
if (activeSecondLayer) {
setExpandedSecondLayer(activeSecondLayer)
}
}
}, [activeFirstLayer, activeSecondLayer]);
const handleFirstLayerClick = (key: string) => {
if (expandedFirstLayer === key) {
setExpandedFirstLayer(null);
setExpandedSecondLayer(null)
} else {
setExpandedFirstLayer(key);
setExpandedSecondLayer(null)
}
};
const handleSecondLayerClick = (key: string) => {
if (expandedSecondLayer === key) {
setExpandedSecondLayer(null)
} else {
setExpandedSecondLayer(key)
}
};
useEffect(() => { if (activeFirstLayer) { setExpandedFirstLayer(activeFirstLayer); if (activeSecondLayer) { setExpandedSecondLayer(activeSecondLayer) } } }, [activeFirstLayer, activeSecondLayer]);
const handleFirstLayerClick = (key: string) => { if (expandedFirstLayer === key) { setExpandedFirstLayer(null); setExpandedSecondLayer(null) } else { setExpandedFirstLayer(key); setExpandedSecondLayer(null) } };
const handleSecondLayerClick = (key: string) => { if (expandedSecondLayer === key) { setExpandedSecondLayer(null) } else { setExpandedSecondLayer(key) } };
const renderThirdLayerItems = (firstLayerKey: string, secondLayerKey: string, thirdLayerItems: ThirdLayerItem) => {
const baseUrl = `/${firstLayerKey}/${secondLayerKey}`;
return Object.keys(thirdLayerItems).map(thirdLayerKey => {
return Object.entries(thirdLayerItems).map(([thirdLayerKey, itemData]) => {
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey && activeThirdLayer === thirdLayerKey;
const mergeUrl = `${baseUrl}/${thirdLayerKey}`;
const url = `/${lang}${baseUrl}/${thirdLayerKey}`;
return (
<div key={`${thirdLayerKey}-item`} className="ml-2 my-1">
<ThirdLayerDropdown
isActive={isActive}
innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)}
url={url}
/>
</div>
);
const url = itemData ? itemData.path || '' : '';
const translation = itemData ? itemData.translation || [] : [];
const displayText = translation[2]?.value || thirdLayerKey;
return <div key={`${thirdLayerKey}-item`} className="ml-2 my-1"><ThirdLayerDropdown isActive={isActive} innerText={displayText} url={`${prefix}${url}`} /></div>;
});
};
const renderSecondLayerItems = (firstLayerKey: string, secondLayerItems: SecondLayerItems) => {
return Object.entries(secondLayerItems).map(([secondLayerKey, thirdLayerItems]) => {
const isActive = activeFirstLayer === firstLayerKey && activeSecondLayer === secondLayerKey;
const isExpanded = expandedSecondLayer === secondLayerKey;
const mergeUrl = `/${firstLayerKey}/${secondLayerKey}`;
const anyThirdLayerItem = Object.values(thirdLayerItems)[0];
const translation = anyThirdLayerItem ? anyThirdLayerItem.translation : [];
const displayText = translation[1]?.value || secondLayerKey;
return (
<div key={`${secondLayerKey}-item`} className="ml-2 my-1">
<SecondLayerDropdown
isActive={isActive}
isExpanded={isExpanded}
innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)}
onClick={() => handleSecondLayerClick(secondLayerKey)}
/>
{isExpanded && (
<div className="ml-2 mt-1">
{renderThirdLayerItems(firstLayerKey, secondLayerKey, thirdLayerItems)}
</div>
)}
<SecondLayerDropdown isActive={isActive} isExpanded={isExpanded} innerText={displayText} onClick={() => handleSecondLayerClick(secondLayerKey)} />
{isExpanded && <div className="ml-2 mt-1">{renderThirdLayerItems(firstLayerKey, secondLayerKey, thirdLayerItems)}</div>}
</div>
);
});
};
const renderFirstLayerItems = () => {
return Object.entries(menuStructure).map(([firstLayerKey, secondLayerItems]) => {
const isActive = activeFirstLayer === firstLayerKey;
const isExpanded = expandedFirstLayer === firstLayerKey;
const mergeUrl = `/${firstLayerKey}`;
const anySecondLayer = Object.values(secondLayerItems)[0];
const anyThirdLayerItem = anySecondLayer ? Object.values(anySecondLayer)[0] : null;
const translation = anyThirdLayerItem ? anyThirdLayerItem.translation : [];
const displayText = translation[0]?.value || firstLayerKey;
return (
<div key={`${firstLayerKey}-item`} className="mb-2">
<FirstLayerDropdown
isActive={isActive}
isExpanded={isExpanded}
innerText={getMenuTranslation(menuTranslationWLang, mergeUrl)}
onClick={() => handleFirstLayerClick(firstLayerKey)}
/>
{isExpanded && (
<div className="mt-1">
{renderSecondLayerItems(firstLayerKey, secondLayerItems)}
</div>
)}
<FirstLayerDropdown isActive={isActive} isExpanded={isExpanded} innerText={displayText} onClick={() => handleFirstLayerClick(firstLayerKey)} />
{isExpanded && <div className="mt-1">{renderSecondLayerItems(firstLayerKey, secondLayerItems)}</div>}
</div>
);
});
};
return (
<div className="mt-1">
<h3 className="text-sm font-semibold mb-1">Menu</h3>
{renderFirstLayerItems()}
</div>
);
return <div className="mt-1"><h3 className="text-sm font-semibold mb-1">{menuStaticTranslation[lang as keyof typeof menuStaticTranslation].menu}</h3>{renderFirstLayerItems()}</div>;
};
export default MenuItemsSection;

View File

@ -17,9 +17,7 @@ const UserProfileSection: FC<UserProfileSectionProps> = ({ userData, onlineData
<div className="bg-amber-300 p-2 hover:bg-amber-400 transition-all">
<div className="flex items-center">
<div className="mr-2">
{userData && userData.avatar ? (
<img className="rounded-full border border-white" src={userData.avatar} alt="Avatar" width={40} height={40} />
) : (
{userData && userData.avatar ? (<img className="rounded-full border border-white" src={userData.avatar} alt="Avatar" width={40} height={40} />) : (
<div className="w-10 h-10 rounded-full bg-amber-400 flex items-center justify-center border border-white">
<div className="text-white text-sm font-bold">{userData?.email ? userData.email.slice(0, 2).toUpperCase() : 'U'}</div>
</div>

View File

@ -99,6 +99,7 @@ async function setContextPageOnline(
body: JSON.stringify(setOnline),
signal: controller.signal,
});
console.log("result", await result.json());
// Clear the timeout
clearTimeout(timeoutId);

View File

@ -7,9 +7,7 @@ import { LanguageTypes } from "@/validations/mutual/language/validations";
import LanguageSelectionItem from "./languageItem";
interface LanguageSelectionComponentProps {
lang: LanguageTypes;
activePage: string;
prefix: string;
onlineData: any;
onlineLoading: boolean;
onlineError: any;
@ -17,13 +15,14 @@ interface LanguageSelectionComponentProps {
updateOnline: (newOnline: any) => Promise<boolean>;
}
const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({ lang, activePage, prefix, onlineData, onlineLoading, onlineError, refreshOnline, updateOnline }) => {
const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({ activePage, onlineData, onlineLoading, onlineError, refreshOnline, updateOnline }) => {
const lang = onlineData?.lang as LanguageTypes || 'en';
const translations = langGet(lang, languageSelectionTranslation);
const getPageWithLocale = (locale: LanguageTypes): string => { return `${prefix}/${activePage}` }
const languageButtons = [
{ activeLang: lang, buttonsLang: "en", refUrl: getPageWithLocale("en"), innerText: langGetKey(translations, "english") },
{ activeLang: lang, buttonsLang: "tr", refUrl: getPageWithLocale("tr"), innerText: langGetKey(translations, "turkish") }
]
{ activeLang: lang, buttonsLang: "en", refUrl: "en", innerText: langGetKey(translations, "english") },
{ activeLang: lang, buttonsLang: "tr", refUrl: "tr", innerText: langGetKey(translations, "turkish") }
];
return (
<div className="flex items-end justify-end">
<DropdownMenu>
@ -32,8 +31,8 @@ const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({
</DropdownMenuTrigger>
<DropdownMenuContent>
{languageButtons.map((props, index) => (
<LanguageSelectionItem key={props.buttonsLang} {...props} onlineData={onlineData}
onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
<LanguageSelectionItem key={props.buttonsLang} {...props}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
))}
</DropdownMenuContent>
</DropdownMenu>

View File

@ -1,27 +1,176 @@
// const menuTranslationEn = {
// "/definitions/identifications/people": "People",
// "/definitions/identifications/users": "Users",
// "/definitions/building/parts": "Build Parts",
// "/definitions/building/areas": "Building Areas",
// "/building/accounts/managment/accounts": "Management Accounts",
// "/building/accounts/managment/budgets": "Management Budgets",
// "/building/accounts/parts/accounts": "Parts Accounts",
// "/building/accounts/parts/budgets": "Parts Budgets",
// "/building/meetings/regular/actions": "Regular Meeting Actions",
// "/building/meetings/regular/accounts": "Regular Meeting Accounts",
// "/building/meetings/ergunt/actions": "Ergunt Meeting Actions",
// "/building/meetings/ergunt/accounts": "Ergunt Meeting Accounts",
// "/building/meetings/invited/attendance": "Meeting Invited Attendance",
// };
const menuTranslationEn = {
"/definitions": "Definitions",
"/definitions/identifications": "Identifications",
"/definitions/identifications/people": "People",
"/definitions/identifications/users": "Users",
// New menu
"/dashboard": [
{ value: "Dashboard", key: "dashboard" },
{ value: "Dashboard", key: "dashboard" },
{ value: "Dashboard", key: "dashboard" },
],
"/individual": [
{ value: "Individual", key: "individual" },
{ value: "Individual", key: "individual" },
{ value: "Individual", key: "individual" },
],
"/user": [
{ value: "User", key: "user" },
{ value: "User", key: "user" },
{ value: "User", key: "user" },
],
"/build": [
{ value: "Build", key: "build" },
{ value: "Build", key: "build" },
{ value: "Build", key: "build" },
],
"/build/parts": [
{ value: "Build", key: "build" },
{ value: "Parts", key: "parts" },
{ value: "Build", key: "build" },
],
"/management/budget/actions": [
{ value: "Management", key: "management" },
{ value: "Budget", key: "budget" },
{ value: "Actions", key: "actions" },
],
"/management/budget": [
{ value: "Management", key: "management" },
{ value: "Budget", key: "budget" },
{ value: "Budget", key: "budget" },
],
"/annual/meeting/close": [
{ value: "Annual", key: "annual" },
{ value: "Meeting", key: "meeting" },
{ value: "Close", key: "close" },
],
"/emergency/meeting": [
{ value: "Emergency", key: "emergency" },
{ value: "Meeting", key: "meeting" },
{ value: "Meeting", key: "meeting" },
],
"/emergency/meeting/close": [
{ value: "Emergency", key: "emergency" },
{ value: "Meeting", key: "meeting" },
{ value: "Close", key: "close" },
],
"/tenant/accounting": [
{ value: "Tenant", key: "tenant" },
{ value: "Accounting", key: "accounting" },
{ value: "Accounting", key: "accounting" },
],
"/meeting/participation": [
{ value: "Meeting", key: "meeting" },
{ value: "Participation", key: "participation" },
{ value: "Participation", key: "participation" },
],
"/tenant/messageToBM": [
{ value: "Tenant", key: "tenant" },
{ value: "Message To BM", key: "messageToBM" },
{ value: "Message To BM", key: "messageToBM" },
],
"/tenant/messageToOwner": [
{ value: "Tenant", key: "tenant" },
{ value: "Message To Owner", key: "messageToOwner" },
{ value: "Message To Owner", key: "messageToOwner" },
],
"/management/accounting": [
{ value: "Management", key: "management" },
{ value: "Accounting", key: "accounting" },
{ value: "Accounting", key: "accounting" },
],
"/build/area": [
{ value: "Build", key: "build" },
{ value: "Area", key: "area" },
{ value: "Area", key: "area" },
],
"/management/budget/status": [
{ value: "Management", key: "management" },
{ value: "Budget", key: "budget" },
{ value: "Status", key: "status" },
],
"/definitions/building": "Building",
"/definitions/building/parts": "Build Parts",
"/definitions/building/areas": "Building Areas",
"/building": "Building",
"/building/accounts": "Account Actions",
"/building/accounts/managment/accounts": "Management Accounts",
"/building/accounts/managment/budgets": "Management Budgets",
"/building/accounts/parts/accounts": "Parts Accounts",
"/building/accounts/parts/budgets": "Parts Budgets",
"/building/meetings": "Meetings",
"/building/meetings/regular/actions": "Regular Meeting Actions",
"/building/meetings/regular/accounts": "Regular Meeting Accounts",
"/building/meetings/ergunt/actions": "Ergunt Meeting Actions",
"/building/meetings/ergunt/accounts": "Ergunt Meeting Accounts",
"/building/meetings/invited/attendance": "Meeting Invited Attendance",
// Early menu
"/definitions/identifications/people": [
{ value: "Definitions", key: "definitions" },
{ value: "Identifications", key: "identifications" },
{ value: "People", key: "people" },
],
"/definitions/identifications/users": [
{ value: "Definitions", key: "definitions" },
{ value: "Identifications", key: "identifications" },
{ value: "Users", key: "users" },
],
"/definitions/building/parts": [
{ value: "Definitions", key: "definitions" },
{ value: "Building", key: "building" },
{ value: "Parts", key: "parts" },
],
"/definitions/building/areas": [
{ value: "Definitions", key: "definitions" },
{ value: "Building", key: "building" },
{ value: "Areas", key: "areas" },
],
"/building/accounts/managment/accounts": [
{ value: "Building", key: "building" },
{ value: "Accounts", key: "accounts" },
{ value: "Managment", key: "managment" },
],
"/building/accounts/managment/budgets": [
{ value: "Building", key: "building" },
{ value: "Accounts", key: "accounts" },
{ value: "Managment", key: "managment" },
],
"/building/accounts/parts/accounts": [
{ value: "Building", key: "building" },
{ value: "Accounts", key: "accounts" },
{ value: "Parts", key: "parts" },
],
"/building/accounts/parts/budgets": [
{ value: "Building", key: "building" },
{ value: "Accounts", key: "accounts" },
{ value: "Parts", key: "parts" },
],
"/building/meetings/regular/actions": [
{ value: "Building", key: "building" },
{ value: "Meetings", key: "meetings" },
{ value: "Regular", key: "regular" },
],
"/building/meetings/regular/accounts": [
{ value: "Building", key: "building" },
{ value: "Meetings", key: "meetings" },
{ value: "Regular", key: "regular" },
],
"/building/meetings/ergunt/actions": [
{ value: "Building", key: "building" },
{ value: "Meetings", key: "meetings" },
{ value: "Ergunt", key: "ergunt" },
],
"/building/meetings/ergunt/accounts": [
{ value: "Building", key: "building" },
{ value: "Meetings", key: "meetings" },
{ value: "Ergunt", key: "ergunt" },
],
"/building/meetings/invited/attendance": [
{ value: "Building", key: "building" },
{ value: "Meetings", key: "meetings" },
{ value: "Invited", key: "invited" },
],
};
export { menuTranslationEn };
// export { menuTranslationEn, menuIndex };

View File

@ -1,26 +1,176 @@
// const menuTranslationTr = {
// "/definitions/identifications/people": "Kişiler",
// "/definitions/identifications/users": "Kullanıcılar",
// "/definitions/building/parts": "Daireler",
// "/definitions/building/areas": "Bina Alanları",
// "/building/accounts/managment/accounts": "Bina Hesapları",
// "/building/accounts/managment/budgets": "Bina Bütçesi",
// "/building/accounts/parts/accounts": "Daire Hesapları",
// "/building/accounts/parts/budgets": "Daire Bütçesi",
// "/building/meetings/regular/actions": "Düzenli Toplantı Eylemleri",
// "/building/meetings/regular/accounts": "Düzenli Toplantı Accounts",
// "/building/meetings/ergunt/actions": "Ergunt Toplantı Eylemleri",
// "/building/meetings/ergunt/accounts": "Ergunt Toplantı Accounts",
// "/building/meetings/invited/attendance": "Toplantı Davetli Katılımlar",
// };
const menuTranslationTr = {
"/definitions": "Tanımlamalar",
"/definitions/identifications": "Tanımlamalar",
"/definitions/identifications/people": "Kişiler",
"/definitions/identifications/users": "Kullanıcılar",
// New menu
"/dashboard": [
{ value: "Dashboard", key: "dashboard" },
{ value: "Dashboard", key: "dashboard" },
{ value: "Dashboard", key: "dashboard" },
],
"/individual": [
{ value: "Individual", key: "individual" },
{ value: "Individual", key: "individual" },
{ value: "Individual", key: "individual" },
],
"/user": [
{ value: "User", key: "user" },
{ value: "User", key: "user" },
{ value: "User", key: "user" },
],
"/build": [
{ value: "Build", key: "build" },
{ value: "Build", key: "build" },
{ value: "Build", key: "build" },
],
"/build/parts": [
{ value: "Build", key: "build" },
{ value: "Parts", key: "parts" },
{ value: "Build", key: "build" },
],
"/management/budget/actions": [
{ value: "Management", key: "management" },
{ value: "Budget", key: "budget" },
{ value: "Actions", key: "actions" },
],
"/management/budget": [
{ value: "Management", key: "management" },
{ value: "Budget", key: "budget" },
{ value: "Budget", key: "budget" },
],
"/annual/meeting/close": [
{ value: "Annual", key: "annual" },
{ value: "Meeting", key: "meeting" },
{ value: "Close", key: "close" },
],
"/emergency/meeting": [
{ value: "Emergency", key: "emergency" },
{ value: "Meeting", key: "meeting" },
{ value: "Meeting", key: "meeting" },
],
"/emergency/meeting/close": [
{ value: "Emergency", key: "emergency" },
{ value: "Meeting", key: "meeting" },
{ value: "Close", key: "close" },
],
"/tenant/accounting": [
{ value: "Tenant", key: "tenant" },
{ value: "Accounting", key: "accounting" },
{ value: "Accounting", key: "accounting" },
],
"/meeting/participation": [
{ value: "Meeting", key: "meeting" },
{ value: "Participation", key: "participation" },
{ value: "Participation", key: "participation" },
],
"/tenant/messageToBM": [
{ value: "Tenant", key: "tenant" },
{ value: "Message To BM", key: "messageToBM" },
{ value: "Message To BM", key: "messageToBM" },
],
"/tenant/messageToOwner": [
{ value: "Tenant", key: "tenant" },
{ value: "Message To Owner", key: "messageToOwner" },
{ value: "Message To Owner", key: "messageToOwner" },
],
"/management/accounting": [
{ value: "Management", key: "management" },
{ value: "Accounting", key: "accounting" },
{ value: "Accounting", key: "accounting" },
],
"/build/area": [
{ value: "Build", key: "build" },
{ value: "Area", key: "area" },
{ value: "Area", key: "area" },
],
"/management/budget/status": [
{ value: "Management", key: "management" },
{ value: "Budget", key: "budget" },
{ value: "Status", key: "status" },
],
"/definitions/building": "Bina",
"/definitions/building/parts": "Daireler",
"/definitions/building/areas": "Bina Alanları",
"/building": "Bina",
"/building/accounts": "Account Eylemleri",
"/building/accounts/managment/accounts": "Bina Hesapları",
"/building/accounts/managment/budgets": "Bina Bütçesi",
"/building/accounts/parts/accounts": "Daire Hesapları",
"/building/accounts/parts/budgets": "Daire Bütçesi",
"/building/meetings": "Toplantılar",
"/building/meetings/regular/actions": "Düzenli Toplantı Eylemleri",
"/building/meetings/regular/accounts": "Düzenli Toplantı Accounts",
"/building/meetings/ergunt/actions": "Ergunt Toplantı Eylemleri",
"/building/meetings/ergunt/accounts": "Ergunt Toplantı Accounts",
"/building/meetings/invited/attendance": "Toplantı Davetli Katılımlar",
// Early menu
"/definitions/identifications/people": [
{ value: "Tanımlamalar", key: "definitions" },
{ value: "Tanımlamalar", key: "identifications" },
{ value: "Kişiler", key: "people" },
],
"/definitions/identifications/users": [
{ value: "Tanımlamalar", key: "definitions" },
{ value: "Tanımlamalar", key: "identifications" },
{ value: "Kullanıcılar", key: "users" },
],
"/definitions/building/parts": [
{ value: "Tanımlamalar", key: "definitions" },
{ value: "Bina", key: "building" },
{ value: "Daireler", key: "parts" },
],
"/definitions/building/areas": [
{ value: "Tanımlamalar", key: "definitions" },
{ value: "Bina", key: "building" },
{ value: "Bina Alanları", key: "areas" },
],
"/building/accounts/managment/accounts": [
{ value: "Bina", key: "building" },
{ value: "Hesap Eylemleri", key: "accounts" },
{ value: "Yönetim", key: "managment" },
],
"/building/accounts/managment/budgets": [
{ value: "Bina", key: "building" },
{ value: "Hesap Eylemleri", key: "accounts" },
{ value: "Yönetim", key: "managment" },
],
"/building/accounts/parts/accounts": [
{ value: "Bina", key: "building" },
{ value: "Hesap Eylemleri", key: "accounts" },
{ value: "Daireler", key: "parts" },
],
"/building/accounts/parts/budgets": [
{ value: "Bina", key: "building" },
{ value: "Hesap Eylemleri", key: "accounts" },
{ value: "Daireler", key: "parts" },
],
"/building/meetings/regular/actions": [
{ value: "Bina", key: "building" },
{ value: "Toplantılar", key: "meetings" },
{ value: "Düzenli", key: "regular" },
],
"/building/meetings/regular/accounts": [
{ value: "Bina", key: "building" },
{ value: "Toplantılar", key: "meetings" },
{ value: "Düzenli", key: "regular" },
],
"/building/meetings/ergunt/actions": [
{ value: "Bina", key: "building" },
{ value: "Toplantılar", key: "meetings" },
{ value: "Acil", key: "ergunt" },
],
"/building/meetings/ergunt/accounts": [
{ value: "Bina", key: "building" },
{ value: "Toplantılar", key: "meetings" },
{ value: "Acil", key: "ergunt" },
],
"/building/meetings/invited/attendance": [
{ value: "Bina", key: "building" },
{ value: "Toplantılar", key: "meetings" },
{ value: "Davetli", key: "invited" },
],
};
export { menuTranslationTr };

View File

@ -1,68 +1,48 @@
'use client';
import HeaderComponent from "@/components/custom/header/component";
import MenuComponent from "@/components/custom/menu/component";
import ContentComponent from "@/components/custom/content/component";
import FooterComponent from "@/components/custom/footer/component";
import React, { FC, useEffect } from 'react';
import React, { FC } from 'react';
import { ClientProviders } from "@/components/mutual/providers/client-providers";
import { ClientOnline, ClientMenu, ClientSelection, ClientUser, ClientSettings } from "@/types/mutual/context/validations";
import { ModeTypes } from "@/validations/mutual/dashboard/props";
import type { LanguageTypes } from "@/validations/mutual/language/validations";
// Import all context hooks
import { useMenu } from "@/components/mutual/context/menu/context";
import { useOnline } from "@/components/mutual/context/online/context";
import { useSelection } from "@/components/mutual/context/selection/context";
import { useUser } from "@/components/mutual/context/user/context";
import { useConfig } from "@/components/mutual/context/config/context";
interface ClientLayoutProps {
allProps: {
lang: LanguageTypes;
activePageUrl: string;
mode: ModeTypes;
prefix: string;
};
packs?: {
menu?: ClientMenu;
selection?: ClientSelection;
user?: ClientUser;
settings?: ClientSettings;
online?: ClientOnline;
};
useReloadWindow?: () => void;
}
import HeaderComponent from "@/components/custom/header/component";
import MenuComponent from "@/components/custom/menu/component";
import ContentComponent from "@/components/custom/content/component";
import FooterComponent from "@/components/custom/footer/component";
interface ClientLayoutProps { activePageUrl: string, searchParams: Record<string, any> }
const ClientLayout: FC<ClientLayoutProps> = ({ activePageUrl, searchParams }) => {
const ClientLayout: FC<ClientLayoutProps> = ({ allProps }) => {
const { onlineData, isLoading: onlineLoading, error: onlineError, refreshOnline, updateOnline } = useOnline();
const { userData, isLoading: userLoading, error: userError } = useUser();
const { availableApplications, isLoading: menuLoading, error: menuError, menuData, refreshMenu } = useMenu();
const { selectionData, isLoading: selectionLoading, error: selectionError } = useSelection();
const { configData, isLoading: configLoading, error: configError } = useConfig();
const { userData, isLoading: userLoading, error: userError, refreshUser, updateUser } = useUser();
const { availableApplications, isLoading: menuLoading, error: menuError, menuData, refreshMenu, updateMenu } = useMenu();
const { selectionData, isLoading: selectionLoading, error: selectionError, refreshSelection, updateSelection } = useSelection();
const { configData, isLoading: configLoading, error: configError, refreshConfig, updateConfig } = useConfig();
const prefix = "/panel"
const mode = (searchParams?.mode as ModeTypes) || 'shortList';
console.log("onlineData", onlineData)
return (
<ClientProviders>
<div className="flex flex-col min-w-screen">
<HeaderComponent {...allProps}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError}
refreshOnline={refreshOnline} updateOnline={updateOnline}
userData={userData} userLoading={userLoading} userError={userError}
/>
<MenuComponent {...allProps}
availableApplications={availableApplications}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError}
userData={userData} userLoading={userLoading} userError={userError}
selectionData={selectionData} selectionLoading={selectionLoading} selectionError={selectionError}
menuData={menuData} menuLoading={menuLoading} menuError={menuError} />
<ContentComponent {...allProps}
userData={userData} userLoading={userLoading} userError={userError}
selectionData={selectionData} selectionLoading={selectionLoading} selectionError={selectionError}
/>
<FooterComponent {...allProps}
availableApplications={availableApplications}
configData={configData} configLoading={configLoading} configError={configError}
/>
<HeaderComponent activePageUrl={activePageUrl} searchParams={searchParams}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
userData={userData} userLoading={userLoading} userError={userError} refreshUser={refreshUser} updateUser={updateUser} />
<MenuComponent availableApplications={availableApplications} activePageUrl={activePageUrl} prefix={prefix} searchParams={searchParams}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
userData={userData} userLoading={userLoading} userError={userError} refreshUser={refreshUser} updateUser={updateUser}
selectionData={selectionData} selectionLoading={selectionLoading} selectionError={selectionError} refreshSelection={refreshSelection} updateSelection={updateSelection}
menuData={menuData} menuLoading={menuLoading} menuError={menuError} refreshMenu={refreshMenu} updateMenu={updateMenu} />
<ContentComponent activePageUrl={activePageUrl} mode={mode} searchParams={searchParams}
userData={userData} userLoading={userLoading} userError={userError} refreshUser={refreshUser} updateUser={updateUser}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
<FooterComponent activePageUrl={activePageUrl} searchParams={searchParams}
configData={configData} configLoading={configLoading} configError={configError} refreshConfig={refreshConfig} updateConfig={updateConfig}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
</div>
</ClientProviders>
);

View File

@ -1,13 +1,10 @@
'use server';
import { FC } from "react";
import { DashboardLayoutProps, ModeTypes } from "@/validations/mutual/dashboard/props";
import { DashboardLayoutProps } from "@/validations/mutual/dashboard/props";
import { ClientLayout } from "./client";
const DashboardLayout: FC<DashboardLayoutProps> = async ({ params, searchParams, lang }) => {
const mode = (searchParams?.mode as ModeTypes) || 'shortList';
const activePageUrl = `/${params.page?.join('/')}`;
const allProps = { lang: lang || '', activePageUrl, mode, prefix: "/panel" };
return <ClientLayout allProps={allProps} />
const DashboardLayout: FC<DashboardLayoutProps> = async ({ params, searchParams }) => {
const activePageUrl = `/${params.page?.join('/')}`; return <ClientLayout activePageUrl={activePageUrl} searchParams={searchParams} />
}
export { DashboardLayout };

View File

@ -1,8 +1,85 @@
import { ContentProps } from "@/validations/mutual/dashboard/props";
import superUserBuildingPartsTenantSomething from "./building/parts/tenantSomething/page";
function getPageComponent(
baseUrl: string,
pageKey: string
): React.FC<ContentProps> | null {
const pageGroup = pageIndexMulti[baseUrl];
if (!pageGroup) {
console.log(`No page group found for URL: ${baseUrl}`);
return null;
}
const PageComponent = pageGroup[pageKey];
if (!PageComponent) {
console.log(
`No page component found for key: ${pageKey} in URL group: ${baseUrl}`
);
return null;
}
return PageComponent;
}
const pageIndexMulti: Record<string, Record<string, React.FC<ContentProps>>> = {
"/main/pages/user/dashboard": { superUserBuildingPartsTenantSomething },
// New pages
"/dashboard": {
superUserBuildingPartsTenantSomething,
},
"/individual": {
superUserBuildingPartsTenantSomething,
},
"/user": {
superUserBuildingPartsTenantSomething,
},
"/build": {
superUserBuildingPartsTenantSomething,
},
"/build/parts": {
superUserBuildingPartsTenantSomething,
},
"/management/budget/actions": {
superUserBuildingPartsTenantSomething,
},
"/management/budget": {
superUserBuildingPartsTenantSomething,
},
"/annual/meeting/close": {
superUserBuildingPartsTenantSomething,
},
"/emergency/meeting": {
superUserBuildingPartsTenantSomething,
},
"/emergency/meeting/close": {
superUserBuildingPartsTenantSomething,
},
"/tenant/accounting": {
superUserBuildingPartsTenantSomething,
},
"/meeting/participation": {
superUserBuildingPartsTenantSomething,
},
"/tenant/messageToBM": {
superUserBuildingPartsTenantSomething,
},
"/tenant/messageToOwner": {
superUserBuildingPartsTenantSomething,
},
"/management/accounting": {
superUserBuildingPartsTenantSomething,
},
"/build/area": {
superUserBuildingPartsTenantSomething,
},
"/management/budget/status": {
superUserBuildingPartsTenantSomething,
},
// Early pages
"/main/pages/user/dashboard": {
superUserBuildingPartsTenantSomething,
},
"/definitions/identifications/people": {
superUserBuildingPartsTenantSomething,
},

View File

@ -7,7 +7,7 @@ import pageIndexMulti from "@/pages/multi/index";
import pageIndexSingle from "@/pages/single/index";
import ContentToRenderNoPage from "@/pages/mutual/noContent/page";
async function resolveWhichPageToRenderSingle({ activePageUrl }: ResolverProps): Promise<React.FC<ContentProps> | null> {
async function resolveWhichPageToRenderSingle({ activePageUrl }: ResolverProps): Promise<React.FC<ContentProps> | React.FC<{ lang: string }> | null> {
try {
return `${activePageUrl}` in pageIndexSingle ? pageIndexSingle[`${activePageUrl}`] : ContentToRenderNoPage
} catch (error) {
@ -16,7 +16,7 @@ async function resolveWhichPageToRenderSingle({ activePageUrl }: ResolverProps):
return ContentToRenderNoPage
}
async function resolveWhichPageToRenderMulti({ activePageUrl }: ResolverProps): Promise<React.FC<ContentProps> | null> {
async function resolveWhichPageToRenderMulti({ activePageUrl }: ResolverProps): Promise<React.FC<ContentProps> | React.FC<{ lang: string }> | null> {
const pageToRender = await retrievePageToRender(activePageUrl) // TODO: Retrieve page to render
try {
const ApplicationToRender = pageIndexMulti[activePageUrl][pageToRender]

View File

@ -16,49 +16,9 @@ type ModeTypes = "shortList" | "fullList" | "create" | "update" | "view";
const ModeTypesList = ["shortList", "fullList", "create", "update", "view"];
interface ContentProps {
lang: LanguageTypes;
activePageUrl: string;
mode?: ModeTypes;
userData: any;
userLoading: boolean;
userError: any;
selectionData: any;
selectionLoading: boolean;
selectionError: any;
}
interface MenuProps {
lang: LanguageTypes;
availableApplications: string[];
activePageUrl: string;
onlineData: any;
onlineLoading: boolean;
onlineError: any;
userData: any;
userLoading: boolean;
userError: any;
selectionData: any;
selectionLoading: boolean;
selectionError: any;
menuData: any;
menuLoading: boolean;
menuError: any;
}
interface FooterProps {
lang: LanguageTypes;
availableApplications: string[];
activePageUrl: string;
configData: any;
configLoading: boolean;
configError: any;
}
interface HeaderProps {
lang: LanguageTypes;
activePageUrl: string;
prefix: string;
mode?: ModeTypes;
searchParams: Record<string, any>;
onlineData: any;
onlineLoading: boolean;
onlineError: any;
@ -67,6 +27,65 @@ interface HeaderProps {
userData: any;
userLoading: boolean;
userError: any;
refreshUser: () => Promise<void>;
updateUser: (newUser: any) => Promise<boolean>;
}
interface MenuProps {
availableApplications: string[];
searchParams: Record<string, any>;
activePageUrl: string;
prefix?: string;
onlineData: any;
onlineLoading: boolean;
onlineError: any;
refreshOnline: () => Promise<void>;
updateOnline: (newOnline: any) => Promise<boolean>;
userData: any;
userLoading: boolean;
userError: any;
refreshUser: () => Promise<void>;
updateUser: (newUser: any) => Promise<boolean>;
selectionData: any;
selectionLoading: boolean;
selectionError: any;
refreshSelection: () => Promise<void>;
updateSelection: (newSelection: any) => Promise<boolean>;
menuData: any;
menuLoading: boolean;
menuError: any;
refreshMenu: () => Promise<void>;
updateMenu: (newMenu: any) => Promise<boolean>;
}
interface FooterProps {
searchParams: Record<string, any>;
activePageUrl: string;
configData: any;
configLoading: boolean;
configError: any;
refreshConfig: () => Promise<void>;
updateConfig: (newConfig: any) => Promise<boolean>;
onlineData: any;
onlineLoading: boolean;
onlineError: any;
refreshOnline: () => Promise<void>;
updateOnline: (newOnline: any) => Promise<boolean>;
}
interface HeaderProps {
activePageUrl: string;
searchParams: Record<string, any>;
onlineData: any;
onlineLoading: boolean;
onlineError: any;
refreshOnline: () => Promise<void>;
updateOnline: (newOnline: any) => Promise<boolean>;
userData: any;
userLoading: boolean;
userError: any;
refreshUser: () => Promise<void>;
updateUser: (newUser: any) => Promise<boolean>;
}
export type {