updated left menu and page template
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
|
||||
import Menu from "./store";
|
||||
|
||||
// Define TypeScript interfaces for menu structure
|
||||
export interface LanguageTranslation {
|
||||
tr: string;
|
||||
en: string;
|
||||
}
|
||||
|
||||
export interface MenuThirdLevel {
|
||||
name: string;
|
||||
lg: LanguageTranslation;
|
||||
siteUrl: string;
|
||||
}
|
||||
|
||||
export interface MenuSecondLevel {
|
||||
name: string;
|
||||
lg: LanguageTranslation;
|
||||
subList: MenuThirdLevel[];
|
||||
}
|
||||
|
||||
export interface MenuFirstLevel {
|
||||
name: string;
|
||||
lg: LanguageTranslation;
|
||||
subList: MenuSecondLevel[];
|
||||
}
|
||||
|
||||
// Define interfaces for the filtered menu structure
|
||||
export interface FilteredMenuThirdLevel {
|
||||
name: string;
|
||||
lg: LanguageTranslation;
|
||||
siteUrl: string;
|
||||
}
|
||||
|
||||
export interface FilteredMenuSecondLevel {
|
||||
name: string;
|
||||
lg: LanguageTranslation;
|
||||
subList: FilteredMenuThirdLevel[];
|
||||
}
|
||||
|
||||
export interface FilteredMenuFirstLevel {
|
||||
name: string;
|
||||
lg: LanguageTranslation;
|
||||
subList: FilteredMenuSecondLevel[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the menu structure based on intersections with provided URLs
|
||||
* @param {string[]} siteUrls - Array of site URLs to check for intersection
|
||||
* @returns {Array} - Filtered menu structure with only matching items
|
||||
*/
|
||||
export function transformMenu(siteUrls: string[]) {
|
||||
// Process the menu structure
|
||||
const filteredMenu: FilteredMenuFirstLevel[] = Menu.reduce(
|
||||
(acc: FilteredMenuFirstLevel[], firstLevel: MenuFirstLevel) => {
|
||||
// Create a new first level item with empty subList
|
||||
const newFirstLevel: FilteredMenuFirstLevel = {
|
||||
name: firstLevel.name,
|
||||
lg: { ...firstLevel.lg },
|
||||
subList: [],
|
||||
};
|
||||
|
||||
// Process second level items
|
||||
firstLevel.subList.forEach((secondLevel: MenuSecondLevel) => {
|
||||
// Create a new second level item with empty subList
|
||||
const newSecondLevel: FilteredMenuSecondLevel = {
|
||||
name: secondLevel.name,
|
||||
lg: { ...secondLevel.lg },
|
||||
subList: [],
|
||||
};
|
||||
|
||||
// Process third level items
|
||||
secondLevel.subList.forEach((thirdLevel: MenuThirdLevel) => {
|
||||
// Check if the third level's siteUrl matches exactly
|
||||
if (
|
||||
thirdLevel.siteUrl &&
|
||||
siteUrls.some((url) => url === thirdLevel.siteUrl)
|
||||
) {
|
||||
// Create a modified third level item
|
||||
const newThirdLevel: FilteredMenuThirdLevel = {
|
||||
name: thirdLevel.name,
|
||||
lg: { ...thirdLevel.lg },
|
||||
siteUrl: thirdLevel.siteUrl,
|
||||
};
|
||||
|
||||
// Add the modified third level to the second level's subList
|
||||
newSecondLevel.subList.push(newThirdLevel);
|
||||
}
|
||||
});
|
||||
|
||||
// Only add the second level to the first level if it has any matching third level items
|
||||
if (newSecondLevel.subList.length > 0) {
|
||||
newFirstLevel.subList.push(newSecondLevel);
|
||||
}
|
||||
});
|
||||
|
||||
// Only add the first level to the result if it has any matching second level items
|
||||
if (newFirstLevel.subList.length > 0) {
|
||||
acc.push(newFirstLevel);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return filteredMenu;
|
||||
}
|
||||
116
WebServices/client-frontend/src/components/menuCleint/menu.tsx
Normal file
116
WebServices/client-frontend/src/components/menuCleint/menu.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { Home, ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { transformMenu } from "./handler";
|
||||
import type { LanguageTranslation } from "./handler";
|
||||
|
||||
interface ClientMenuProps {
|
||||
siteUrls: string[];
|
||||
lang: keyof LanguageTranslation;
|
||||
}
|
||||
|
||||
const ClientMenu: React.FC<ClientMenuProps> = ({ siteUrls, lang }) => {
|
||||
const transformedMenu = transformMenu(siteUrls) || [];
|
||||
|
||||
// State to track which menu items are expanded
|
||||
const [firstLayerIndex, setFirstLayerIndex] = useState<number>(-1);
|
||||
const [secondLayerIndex, setSecondLayerIndex] = useState<number>(-1);
|
||||
|
||||
// Handle first level menu click
|
||||
const handleFirstLevelClick = (index: number) => {
|
||||
setFirstLayerIndex(index === firstLayerIndex ? -1 : index);
|
||||
setSecondLayerIndex(-1); // Reset second layer selection when first layer changes
|
||||
};
|
||||
|
||||
// Handle second level menu click
|
||||
const handleSecondLevelClick = (index: number) => {
|
||||
setSecondLayerIndex(index === secondLayerIndex ? -1 : index);
|
||||
};
|
||||
|
||||
// No need for a navigation handler since we'll use Link components
|
||||
|
||||
return (
|
||||
<div className="w-full bg-white shadow-sm rounded-lg overflow-hidden">
|
||||
<div className="p-4 border-b border-gray-200">
|
||||
<h2 className="text-xl font-bold text-center text-gray-800">
|
||||
Dashboard
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<nav className="flex flex-col">
|
||||
{transformedMenu &&
|
||||
transformedMenu.map((item, firstIndex) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="border-b border-gray-100 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => handleFirstLevelClick(firstIndex)}
|
||||
className={`flex items-center justify-between w-full p-4 text-left transition-colors ${
|
||||
firstIndex === firstLayerIndex
|
||||
? "bg-emerald-50 text-emerald-700"
|
||||
: "text-gray-700 hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<span className="font-medium">{item.lg[lang]}</span>
|
||||
{firstIndex === firstLayerIndex ? (
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* First level separator and second layer */}
|
||||
{firstIndex === firstLayerIndex && (
|
||||
<div className="bg-gray-50 border-t border-gray-100">
|
||||
{item.subList.map((subItem, secondIndex) => (
|
||||
<div
|
||||
key={subItem.name}
|
||||
className="border-b border-gray-100 last:border-b-0"
|
||||
>
|
||||
<button
|
||||
onClick={() => handleSecondLevelClick(secondIndex)}
|
||||
className={`flex items-center justify-between w-full p-3 pl-8 text-left transition-colors ${
|
||||
secondIndex === secondLayerIndex
|
||||
? "bg-emerald-100 text-emerald-800"
|
||||
: "text-gray-600 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<span className="font-medium">{subItem.lg[lang]}</span>
|
||||
{secondIndex === secondLayerIndex ? (
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
) : (
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Second level separator and third layer */}
|
||||
{firstIndex === firstLayerIndex &&
|
||||
secondIndex === secondLayerIndex && (
|
||||
<div className="bg-gray-100 border-t border-gray-200">
|
||||
{subItem.subList.map((subSubItem) => (
|
||||
<Link
|
||||
key={subSubItem.name}
|
||||
href={subSubItem.siteUrl}
|
||||
className="flex items-center w-full p-3 pl-12 text-left text-gray-700 hover:bg-gray-200 transition-colors"
|
||||
>
|
||||
<Home className="h-4 w-4 mr-2 text-gray-500" />
|
||||
<span>{subSubItem.lg[lang]}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClientMenu;
|
||||
203
WebServices/client-frontend/src/components/menuCleint/store.tsx
Normal file
203
WebServices/client-frontend/src/components/menuCleint/store.tsx
Normal file
@@ -0,0 +1,203 @@
|
||||
const Individual = {
|
||||
name: "Individual",
|
||||
lg: {
|
||||
tr: "Birey",
|
||||
en: "Individual",
|
||||
},
|
||||
siteUrl: "/individual",
|
||||
};
|
||||
|
||||
const User = {
|
||||
name: "User",
|
||||
lg: {
|
||||
tr: "Kullanıcı",
|
||||
en: "User",
|
||||
},
|
||||
siteUrl: "/user",
|
||||
};
|
||||
|
||||
const Build = {
|
||||
name: "Build",
|
||||
lg: {
|
||||
tr: "Apartman",
|
||||
en: "Build",
|
||||
},
|
||||
siteUrl: "/build",
|
||||
};
|
||||
|
||||
const Dashboard = {
|
||||
name: "Dashboard",
|
||||
lg: {
|
||||
tr: "Pano",
|
||||
en: "Dashboard",
|
||||
},
|
||||
siteUrl: "/dashboard",
|
||||
};
|
||||
|
||||
const BuildParts = {
|
||||
name: "BuildParts",
|
||||
lg: {
|
||||
tr: "Daireler",
|
||||
en: "Build Parts",
|
||||
},
|
||||
siteUrl: "/build/parts",
|
||||
};
|
||||
|
||||
const BuildArea = {
|
||||
name: "BuildArea",
|
||||
lg: {
|
||||
tr: "Daire Alanları",
|
||||
en: "Build Area",
|
||||
},
|
||||
siteUrl: "/build/area",
|
||||
};
|
||||
|
||||
const ManagementAccounting = {
|
||||
name: "ManagementAccounting",
|
||||
lg: {
|
||||
tr: "Yönetim Cari Hareketler",
|
||||
en: "ManagementAccounting",
|
||||
},
|
||||
siteUrl: "/management/accounting",
|
||||
};
|
||||
|
||||
const ManagementBudget = {
|
||||
name: "ManagementBudget",
|
||||
lg: {
|
||||
tr: "Yönetim Bütçe İşlemleri",
|
||||
en: "Management Budget",
|
||||
},
|
||||
siteUrl: "/management/budget",
|
||||
};
|
||||
|
||||
const BuildPartsAccounting = {
|
||||
name: "BuildPartsAccounting",
|
||||
lg: {
|
||||
tr: "Daire Cari Hareketler",
|
||||
en: "Build Parts Accounting",
|
||||
},
|
||||
siteUrl: "/build/parts/accounting",
|
||||
};
|
||||
|
||||
const AnnualMeeting = {
|
||||
name: "AnnualMeeting",
|
||||
lg: {
|
||||
tr: "Yıllık Olağan Toplantı Tanımlama ve Davet",
|
||||
en: "Annual Meetings and Invitations",
|
||||
},
|
||||
siteUrl: "/annual/meeting",
|
||||
};
|
||||
|
||||
const AnnualMeetingClose = {
|
||||
name: "AnnualMeetingClose",
|
||||
lg: {
|
||||
tr: "Yıllık Olağan Toplantı kapatma ve Cari Yaratma",
|
||||
en: "Annual Meeting Close and Accountings",
|
||||
},
|
||||
siteUrl: "/annual/meeting/close",
|
||||
};
|
||||
|
||||
const EmergencyMeeting = {
|
||||
name: "EmergencyMeeting",
|
||||
lg: {
|
||||
tr: "Acil Toplantı Tanımlama ve Davet",
|
||||
en: "Emergency Meeting and Invitations",
|
||||
},
|
||||
siteUrl: "/emergency/meeting",
|
||||
};
|
||||
|
||||
const EmergencyMeetingClose = {
|
||||
name: "EmergencyMeetingClose",
|
||||
lg: {
|
||||
tr: "Acil Olağan Toplantı kapatma ve Cari Yaratma",
|
||||
en: "Emergency Meeting Close and Accountings",
|
||||
},
|
||||
siteUrl: "/emergency/meeting/close",
|
||||
};
|
||||
|
||||
const MeetingParticipations = {
|
||||
name: "MeetingParticipations",
|
||||
lg: {
|
||||
tr: "Toplantı Katılım İşlemleri",
|
||||
en: "Meeting Participations",
|
||||
},
|
||||
siteUrl: "/meeting/participation",
|
||||
};
|
||||
|
||||
const Menu = [
|
||||
{
|
||||
name: "Dashboard",
|
||||
lg: {
|
||||
tr: "Pano",
|
||||
en: "Dashboard",
|
||||
},
|
||||
subList: [
|
||||
{
|
||||
name: "Dashboard",
|
||||
lg: {
|
||||
tr: "Pano",
|
||||
en: "Dashboard",
|
||||
},
|
||||
subList: [Dashboard],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Definitions",
|
||||
lg: {
|
||||
tr: "Tanımlar",
|
||||
en: "Definitions",
|
||||
},
|
||||
subList: [
|
||||
{
|
||||
name: "People",
|
||||
lg: {
|
||||
tr: "Kişiler",
|
||||
en: "People",
|
||||
},
|
||||
subList: [Individual, User],
|
||||
},
|
||||
{
|
||||
name: "Building",
|
||||
lg: {
|
||||
tr: "Binalar",
|
||||
en: "Building",
|
||||
},
|
||||
subList: [Build, BuildParts, BuildArea],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Building Management",
|
||||
lg: {
|
||||
tr: "Bina Yönetimi",
|
||||
en: "Building Management",
|
||||
},
|
||||
subList: [
|
||||
{
|
||||
name: "Management Accounting",
|
||||
lg: {
|
||||
tr: "Cari işlemler",
|
||||
en: "Management Accounting",
|
||||
},
|
||||
subList: [ManagementAccounting, ManagementBudget, BuildPartsAccounting],
|
||||
},
|
||||
{
|
||||
name: "Meetings",
|
||||
lg: {
|
||||
tr: "Toplantılar",
|
||||
en: "Meetings",
|
||||
},
|
||||
subList: [
|
||||
AnnualMeeting,
|
||||
AnnualMeetingClose,
|
||||
EmergencyMeeting,
|
||||
EmergencyMeetingClose,
|
||||
MeetingParticipations,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default Menu;
|
||||
Reference in New Issue
Block a user