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 # cpus: 1
# mem_limit: 2048m # mem_limit: 2048m
account_service: # account_service:
container_name: account_service # container_name: account_service
build: # build:
context: . # context: .
dockerfile: api_services/api_builds/account_service/Dockerfile # dockerfile: api_services/api_builds/account_service/Dockerfile
env_file: # env_file:
- api_env.env # - api_env.env
networks: # networks:
- wag-services # - wag-services
environment: # environment:
- API_PATH=app:app # - API_PATH=app:app
- API_HOST=0.0.0.0 # - API_HOST=0.0.0.0
- API_PORT=8004 # - API_PORT=8004
- API_LOG_LEVEL=info # - API_LOG_LEVEL=info
- API_RELOAD=1 # - API_RELOAD=1
- API_APP_NAME=evyos-account-api-gateway # - API_APP_NAME=evyos-account-api-gateway
- API_TITLE=WAG API 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_DESCRIPTION=This api is serves as web account api gateway only to evyos web services.
- API_APP_URL=https://account_service # - API_APP_URL=https://account_service
ports: # ports:
- "8004:8004" # - "8004:8004"
building_service: # building_service:
container_name: building_service # container_name: building_service
build: # build:
context: . # context: .
dockerfile: api_services/api_builds/building_service/Dockerfile # dockerfile: api_services/api_builds/building_service/Dockerfile
env_file: # env_file:
- api_env.env # - api_env.env
networks: # networks:
- wag-services # - wag-services
environment: # environment:
- API_PATH=app:app # - API_PATH=app:app
- API_HOST=0.0.0.0 # - API_HOST=0.0.0.0
- API_PORT=8006 # - API_PORT=8006
- API_LOG_LEVEL=info # - API_LOG_LEVEL=info
- API_RELOAD=1 # - API_RELOAD=1
- API_APP_NAME=evyos-building-api-gateway # - API_APP_NAME=evyos-building-api-gateway
- API_TITLE=WAG API 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_DESCRIPTION=This api is serves as web building api gateway only to evyos web services.
- API_APP_URL=https://building_service # - API_APP_URL=https://building_service
ports: # ports:
- "8006:8006" # - "8006:8006"
identity_service: identity_service:
container_name: 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 searchParameters = await searchParams;
const tokenValid = await checkAccessTokenIsValid() const tokenValid = await checkAccessTokenIsValid()
if (!tokenValid) { redirect("/auth/login") } if (!tokenValid) { redirect("/auth/login") }
return ( return <div className="flex flex-col items-center justify-center"><DashboardLayout params={parameters} searchParams={searchParameters} lang="en" /></div>
<div className="flex flex-col items-center justify-center">
<DashboardLayout params={parameters} searchParams={searchParameters} lang="en" />
</div>
);
} }
export default MainEnPage; export default MainEnPage;

View File

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

View File

@ -1,82 +1,68 @@
'use client'; 'use client';
import { FC, Suspense, memo } from "react";
import { FC, Suspense, useMemo, memo } from "react";
import { ContentProps, ModeTypes, ModeTypesList } from "@/validations/mutual/dashboard/props"; import { ContentProps, ModeTypes, ModeTypesList } from "@/validations/mutual/dashboard/props";
import { LanguageTypes } from "@/validations/mutual/language/validations";
import PageToBeChildrendMulti from "./PageToBeChildrendMulti"; import PageToBeChildrendMulti from "./PageToBeChildrendMulti";
import LoadingContent from "@/components/mutual/loader/component"; import LoadingContent from "@/components/mutual/loader/component";
// Create a memoized version of PageToBeChildrendMulti to prevent unnecessary re-renders
const MemoizedMultiPage = memo(PageToBeChildrendMulti); const MemoizedMultiPage = memo(PageToBeChildrendMulti);
// const ContentComponent: FC<ContentProps> = async ({ lang, translations, activePageUrl, isMulti, mode }) => { const translations = {
// const modeFromQuery = ModeTypesList.includes(mode || '') ? mode : 'shortList' en: {
// const renderProps = { lang, translations, activePageUrl, mode: modeFromQuery as ModeTypes } errorLoadingContent: "Error Loading Content",
// const PageToBeChildrend = isMulti ? PageToBeChildrendMulti : PageToBeChildrendSingle contentArea: "Content Area",
// const loadingContent = <LoadingContent height="h-16" size="w-36 h-48" plane="h-full w-full" /> contentLoading: "Content Loading",
// 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)]" contentLoadingDescription: "The requested page is currently unavailable or still loading.",
// return <div className={classNameDiv}><Suspense fallback={loadingContent}><PageToBeChildrend {...renderProps} /></Suspense></div> 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: LanguageTypes; activePageUrl: string }> = memo(({ lang, activePageUrl }) => (
const FallbackContent: FC<{ lang: string; activePageUrl: string; mode: string }> = memo(({ lang, activePageUrl, mode }) => (
<div className="p-6 bg-white rounded-lg shadow-md"> <div className="p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-4">Content Loading</h2> <h2 className="text-2xl font-bold mb-4">{translations[lang].contentLoading}</h2>
<p className="text-gray-600 mb-4">The requested page is currently unavailable or still loading.</p> <p className="text-gray-600 mb-4">{translations[lang].contentLoadingDescription}</p>
<div className="p-4 bg-blue-50 border border-blue-200 rounded-md"> <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">{translations[lang].pageUrl}: {activePageUrl}</p>
<p className="text-sm text-blue-700">Language: {lang}</p> <p className="text-sm text-blue-700">{translations[lang].language}: {lang}</p>
<p className="text-sm text-blue-700">Mode: {mode}</p>
</div> </div>
</div> </div>
)); ));
const ContentComponent: FC<ContentProps> = ({ const ContentComponent: FC<ContentProps> = ({
lang, activePageUrl, mode, searchParams, activePageUrl, mode, userData, userLoading, userError, refreshUser, updateUser,
userData, userLoading, userError, onlineData, onlineLoading, onlineError, refreshOnline, updateOnline,
selectionData, selectionLoading, selectionError,
}) => { }) => {
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 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 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 (userLoading) { return <div className={classNameDiv}>{loadingContent}</div> }
if (selectionError || userError) { if (userError) {
return <div className={classNameDiv}> return (
<div className="p-6 bg-white rounded-lg shadow-md"> <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> <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>
<p className="text-gray-600 mb-4">{selectionError || userError}</p>
</div> </div>
</div> )
} }
return ( return (
<div className={classNameDiv}> <div className={classNameDiv}>
<Suspense fallback={loadingContent}> <Suspense fallback={loadingContent}>
<div className="p-6 bg-white rounded-lg shadow-md"> <div className="p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold mb-4">Content Area</h2> <h2 className="text-2xl font-bold mb-4">{translations[lang].contentArea}</h2>
{(!userData) && (<FallbackContent lang={lang} activePageUrl={activePageUrl || ''} />)}
{/* Fallback Content */}
{(!userData || !selectionData) && (
<FallbackContent
lang={lang || ''}
activePageUrl={activePageUrl || ''}
mode={modeFromQuery}
/>
)}
{/* Wrap component in memo to prevent unnecessary re-renders */}
<MemoizedMultiPage <MemoizedMultiPage
lang={lang || ''} activePageUrl={activePageUrl || ''} searchParams={searchParams}
activePageUrl={activePageUrl || ''} userData={userData} userLoading={userLoading} userError={userError} refreshUser={refreshUser} updateUser={updateUser}
mode={modeFromQuery as ModeTypes} onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
userData={userData}
userLoading={userLoading}
userError={userError}
selectionData={selectionData}
selectionLoading={selectionLoading}
selectionError={selectionError}
/>
</div> </div>
</Suspense> </Suspense>
</div> </div>

View File

@ -2,6 +2,7 @@
import { FC } from "react"; import { FC } from "react";
import { langGetKey } from "@/lib/langGet"; import { langGetKey } from "@/lib/langGet";
import { FooterProps } from "@/validations/mutual/dashboard/props"; import { FooterProps } from "@/validations/mutual/dashboard/props";
import { LanguageTypes } from "@/validations/mutual/language/validations";
const translations = { const translations = {
en: { en: {
@ -15,9 +16,11 @@ const translations = {
} }
const FooterComponent: FC<FooterProps> = ({ const FooterComponent: FC<FooterProps> = ({
lang, activePageUrl, useReloadWindow, configData, configLoading, configError activePageUrl, configData, configLoading, configError,
onlineData, onlineLoading, onlineError
}) => { }) => {
// Use the config context hook // Use the config context hook
const lang = onlineData?.lang as LanguageTypes || 'en';
return ( 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"> <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 { HeaderProps } from "@/validations/mutual/dashboard/props";
import { langGetKey } from "@/lib/langGet"; import { langGetKey } from "@/lib/langGet";
import LanguageSelectionComponent from "@/components/mutual/languageSelection/component"; import LanguageSelectionComponent from "@/components/mutual/languageSelection/component";
import { LanguageTypes } from "@/validations/mutual/language/validations";
const translations = { const translations = {
en: { en: {
@ -16,10 +17,10 @@ const translations = {
} }
const HeaderComponent: FC<HeaderProps> = ({ const HeaderComponent: FC<HeaderProps> = ({
lang, activePageUrl, prefix, mode, activePageUrl, onlineData, onlineLoading, onlineError, refreshOnline, updateOnline,
onlineData, onlineLoading, onlineError, refreshOnline, updateOnline, userData, userLoading, userError, refreshUser, updateUser
userData, userLoading, userError,
}) => { }) => {
const lang = onlineData?.lang as LanguageTypes || 'en';
return ( 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 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"> <div className="flex flex-row justify-center items-center">
@ -29,12 +30,11 @@ const HeaderComponent: FC<HeaderProps> = ({
<div className="flex items-center"> <div className="flex items-center">
{!onlineLoading && onlineData && onlineData.userType && ( {!onlineLoading && onlineData && onlineData.userType && (
<div className="mr-4 text-sm"> <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> <span className="ml-2 text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full">{onlineData.userType}</span>
</div> </div>
)}<LanguageSelectionComponent )}<LanguageSelectionComponent
lang={lang} activePage={activePageUrl} prefix={prefix} activePage={activePageUrl} onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline}
/> />
</div> </div>
</div> </div>

View File

@ -1,6 +1,7 @@
'use client'; 'use client';
import { FC, Suspense } from "react"; import { FC, Suspense } from "react";
import { MenuProps } from "@/validations/mutual/dashboard/props"; import { MenuProps } from "@/validations/mutual/dashboard/props";
import { LanguageTypes } from "@/validations/mutual/language/validations";
import UserProfileSection from "./userProfileSection"; import UserProfileSection from "./userProfileSection";
import ClientSelectionSection from "./clientSelectionSection"; import ClientSelectionSection from "./clientSelectionSection";
@ -11,7 +12,7 @@ import MenuEmptyState from "./menuEmptyState";
import LoadingContent from "@/components/mutual/loader/component"; import LoadingContent from "@/components/mutual/loader/component";
const MenuComponent: FC<MenuProps> = ({ const MenuComponent: FC<MenuProps> = ({
lang, activePageUrl, useReloadWindow, availableApplications, activePageUrl, availableApplications, prefix,
onlineData, onlineLoading, onlineError, onlineData, onlineLoading, onlineError,
userData, userLoading, userError, userData, userLoading, userError,
selectionData, selectionLoading, selectionError, selectionData, selectionLoading, selectionError,
@ -21,21 +22,21 @@ const MenuComponent: FC<MenuProps> = ({
if (menuError) { return <MenuErrorState error={menuError} />; } // Render error state if (menuError) { return <MenuErrorState error={menuError} />; } // Render error state
if (availableApplications.length === 0) { return <MenuEmptyState />; } // Render empty state if (availableApplications.length === 0) { return <MenuEmptyState />; } // Render empty state
function handleClientSelection(client: any) { console.log('Client selected:', client) } function handleClientSelection(client: any) { console.log('Client selected:', client) }
const lang = onlineData?.lang as LanguageTypes || 'en';
return ( 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="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"> <div className="flex flex-col">
{/* User Profile Section */} {/* 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} /> <UserProfileSection userData={userData} onlineData={onlineData} />
</Suspense> </Suspense>
{/* Client Selection Section */} {/* 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} /> <ClientSelectionSection selectionData={selectionData} initialSelectedClient={selectionData} onClientSelect={handleClientSelection} />
</Suspense> </Suspense>
{/* Menu Items Section */} {/* Menu Items 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>}>
<MenuItemsSection availableApplications={availableApplications} activePageUrl={activePageUrl} lang={lang} /> <MenuItemsSection availableApplications={availableApplications} activePageUrl={activePageUrl} lang={lang} prefix={prefix} />
</Suspense> </Suspense>
</div> </div>
</div> </div>

View File

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

View File

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

View File

@ -7,9 +7,7 @@ import { LanguageTypes } from "@/validations/mutual/language/validations";
import LanguageSelectionItem from "./languageItem"; import LanguageSelectionItem from "./languageItem";
interface LanguageSelectionComponentProps { interface LanguageSelectionComponentProps {
lang: LanguageTypes;
activePage: string; activePage: string;
prefix: string;
onlineData: any; onlineData: any;
onlineLoading: boolean; onlineLoading: boolean;
onlineError: any; onlineError: any;
@ -17,13 +15,14 @@ interface LanguageSelectionComponentProps {
updateOnline: (newOnline: any) => Promise<boolean>; 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 translations = langGet(lang, languageSelectionTranslation);
const getPageWithLocale = (locale: LanguageTypes): string => { return `${prefix}/${activePage}` }
const languageButtons = [ const languageButtons = [
{ activeLang: lang, buttonsLang: "en", refUrl: getPageWithLocale("en"), innerText: langGetKey(translations, "english") }, { activeLang: lang, buttonsLang: "en", refUrl: "en", innerText: langGetKey(translations, "english") },
{ activeLang: lang, buttonsLang: "tr", refUrl: getPageWithLocale("tr"), innerText: langGetKey(translations, "turkish") } { activeLang: lang, buttonsLang: "tr", refUrl: "tr", innerText: langGetKey(translations, "turkish") }
] ];
return ( return (
<div className="flex items-end justify-end"> <div className="flex items-end justify-end">
<DropdownMenu> <DropdownMenu>
@ -32,8 +31,8 @@ const LanguageSelectionComponent: React.FC<LanguageSelectionComponentProps> = ({
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
{languageButtons.map((props, index) => ( {languageButtons.map((props, index) => (
<LanguageSelectionItem key={props.buttonsLang} {...props} onlineData={onlineData} <LanguageSelectionItem key={props.buttonsLang} {...props}
onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} /> onlineData={onlineData} onlineLoading={onlineLoading} onlineError={onlineError} refreshOnline={refreshOnline} updateOnline={updateOnline} />
))} ))}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </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 = { const menuTranslationEn = {
"/definitions": "Definitions", // New menu
"/definitions/identifications": "Identifications", "/dashboard": [
"/definitions/identifications/people": "People", { value: "Dashboard", key: "dashboard" },
"/definitions/identifications/users": "Users", { 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", // Early menu
"/definitions/building/parts": "Build Parts", "/definitions/identifications/people": [
"/definitions/building/areas": "Building Areas", { value: "Definitions", key: "definitions" },
{ value: "Identifications", key: "identifications" },
"/building": "Building", { value: "People", key: "people" },
"/building/accounts": "Account Actions", ],
"/building/accounts/managment/accounts": "Management Accounts", "/definitions/identifications/users": [
"/building/accounts/managment/budgets": "Management Budgets", { value: "Definitions", key: "definitions" },
"/building/accounts/parts/accounts": "Parts Accounts", { value: "Identifications", key: "identifications" },
"/building/accounts/parts/budgets": "Parts Budgets", { value: "Users", key: "users" },
],
"/building/meetings": "Meetings", "/definitions/building/parts": [
"/building/meetings/regular/actions": "Regular Meeting Actions", { value: "Definitions", key: "definitions" },
"/building/meetings/regular/accounts": "Regular Meeting Accounts", { value: "Building", key: "building" },
"/building/meetings/ergunt/actions": "Ergunt Meeting Actions", { value: "Parts", key: "parts" },
"/building/meetings/ergunt/accounts": "Ergunt Meeting Accounts", ],
"/building/meetings/invited/attendance": "Meeting Invited Attendance", "/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 };
// 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 = { const menuTranslationTr = {
"/definitions": "Tanımlamalar", // New menu
"/definitions/identifications": "Tanımlamalar", "/dashboard": [
"/definitions/identifications/people": "Kişiler", { value: "Dashboard", key: "dashboard" },
"/definitions/identifications/users": "Kullanıcılar", { 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", // Early menu
"/definitions/building/parts": "Daireler", "/definitions/identifications/people": [
"/definitions/building/areas": "Bina Alanları", { value: "Tanımlamalar", key: "definitions" },
{ value: "Tanımlamalar", key: "identifications" },
"/building": "Bina", { value: "Kişiler", key: "people" },
"/building/accounts": "Account Eylemleri", ],
"/building/accounts/managment/accounts": "Bina Hesapları", "/definitions/identifications/users": [
"/building/accounts/managment/budgets": "Bina Bütçesi", { value: "Tanımlamalar", key: "definitions" },
"/building/accounts/parts/accounts": "Daire Hesapları", { value: "Tanımlamalar", key: "identifications" },
"/building/accounts/parts/budgets": "Daire Bütçesi", { value: "Kullanıcılar", key: "users" },
],
"/building/meetings": "Toplantılar", "/definitions/building/parts": [
"/building/meetings/regular/actions": "Düzenli Toplantı Eylemleri", { value: "Tanımlamalar", key: "definitions" },
"/building/meetings/regular/accounts": "Düzenli Toplantı Accounts", { value: "Bina", key: "building" },
"/building/meetings/ergunt/actions": "Ergunt Toplantı Eylemleri", { value: "Daireler", key: "parts" },
"/building/meetings/ergunt/accounts": "Ergunt Toplantı Accounts", ],
"/building/meetings/invited/attendance": "Toplantı Davetli Katılımlar", "/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 }; export { menuTranslationTr };

View File

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

View File

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

View File

@ -1,8 +1,85 @@
import { ContentProps } from "@/validations/mutual/dashboard/props"; import { ContentProps } from "@/validations/mutual/dashboard/props";
import superUserBuildingPartsTenantSomething from "./building/parts/tenantSomething/page"; 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>>> = { 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": { "/definitions/identifications/people": {
superUserBuildingPartsTenantSomething, superUserBuildingPartsTenantSomething,
}, },

View File

@ -7,7 +7,7 @@ import pageIndexMulti from "@/pages/multi/index";
import pageIndexSingle from "@/pages/single/index"; import pageIndexSingle from "@/pages/single/index";
import ContentToRenderNoPage from "@/pages/mutual/noContent/page"; 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 { try {
return `${activePageUrl}` in pageIndexSingle ? pageIndexSingle[`${activePageUrl}`] : ContentToRenderNoPage return `${activePageUrl}` in pageIndexSingle ? pageIndexSingle[`${activePageUrl}`] : ContentToRenderNoPage
} catch (error) { } catch (error) {
@ -16,7 +16,7 @@ async function resolveWhichPageToRenderSingle({ activePageUrl }: ResolverProps):
return ContentToRenderNoPage 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 const pageToRender = await retrievePageToRender(activePageUrl) // TODO: Retrieve page to render
try { try {
const ApplicationToRender = pageIndexMulti[activePageUrl][pageToRender] 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"]; const ModeTypesList = ["shortList", "fullList", "create", "update", "view"];
interface ContentProps { interface ContentProps {
lang: LanguageTypes;
activePageUrl: string; activePageUrl: string;
mode?: ModeTypes; mode?: ModeTypes;
userData: any; searchParams: Record<string, 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;
onlineData: any; onlineData: any;
onlineLoading: boolean; onlineLoading: boolean;
onlineError: any; onlineError: any;
@ -67,6 +27,65 @@ interface HeaderProps {
userData: any; userData: any;
userLoading: boolean; userLoading: boolean;
userError: any; 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 { export type {