first commit

This commit is contained in:
Samandar Turgunboyev
2025-10-18 17:14:59 +05:00
parent edf364b389
commit 036a36ce90
92 changed files with 14614 additions and 135 deletions

View File

@@ -0,0 +1,238 @@
"use client";
import { cn } from "@/shared/lib/utils";
import { Button } from "@/shared/ui/button";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/shared/ui/sheet";
import LangToggle from "@/widgets/lang-toggle/ui/lang-toggle";
import {
Briefcase,
Building2,
CalendarCheck2,
ChevronDown,
ChevronRight,
HelpCircle,
Menu,
MessageSquare,
Newspaper,
Plane,
Settings,
Users,
Wallet,
} from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom";
type Role = "admin" | "manager" | "user";
interface SidebarProps {
role: Role;
}
/** --- MENYU TUZILMASI --- **/
const MENU_ITEMS = [
{ label: "Foydalanuvchilar", icon: Users, path: "/user", roles: ["admin"] },
{
label: "Tur firmalar",
icon: Building2,
path: "/agencies",
roles: ["admin", "manager"],
},
{ label: "Xodimlar", icon: Briefcase, path: "/employees", roles: ["admin"] },
{ label: "Byudjet", icon: Wallet, path: "/finance", roles: ["admin"] },
{
label: "Turlar",
icon: Plane,
path: "/tours",
roles: ["admin", "manager"],
children: [
{ label: "Turlar", path: "/tours" },
{ label: "Tur sozlamalari", path: "/tours/setting" },
],
},
{
label: "Bronlar",
icon: CalendarCheck2,
path: "/bookings",
roles: ["admin", "manager", "user"],
},
{
label: "Yangiliklar",
icon: Newspaper,
path: "/news",
roles: ["admin", "manager"],
children: [
{ label: "Yangiliklar", path: "/news" },
{ label: "Kategoriya", path: "/news/categories" },
],
},
{
label: "FAQ",
icon: MessageSquare,
path: "/faq",
roles: ["admin"],
children: [
{ label: "Savollar royxati", path: "/faq" },
{ label: "Savollar kategoriyasi", path: "/faq/categories" },
],
},
{
label: "Arizalar",
icon: HelpCircle,
path: "/support",
roles: ["admin", "manager"],
children: [
{ label: "Agentlik arizalari", path: "/support/tours", roles: ["admin"] },
{ label: "Yordam arizalari", path: "/support/user", roles: ["admin"] },
],
},
{
label: "Tur sozlamalari",
icon: Settings,
path: "/tour-settings",
roles: ["admin"],
children: [
{ label: "Sayt SEOsi", path: "/site-seo/" },
{ label: "Offerta", path: "/site-pages/" },
{ label: "Yordam pagelari", path: "/site-help/" },
{ label: "Sayt sozlamalari", path: "/site-settings/" },
],
},
];
export function Sidebar({ role }: SidebarProps) {
const navigate = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const [isSheetOpen, setIsSheetOpen] = useState(false);
const visibleMenu = useMemo(
() => MENU_ITEMS.filter((item) => item.roles.includes(role)),
[role],
);
const [active, setActive] = useState<string>(location.pathname);
const [openMenus, setOpenMenus] = useState<string[]>([]);
useEffect(() => {
setActive(location.pathname);
}, [location.pathname]);
const handleClick = (path: string) => {
setActive(path);
navigate(path);
setIsSheetOpen(false);
};
const toggleSubMenu = (label: string) => {
setOpenMenus((prev) =>
prev.includes(label) ? prev.filter((m) => m !== label) : [...prev, label],
);
};
const MenuList = (
<ul className="p-2 space-y-1">
{visibleMenu.map(({ label, icon: Icon, path, children }) => {
const isActive = active.startsWith(path);
const isOpen = openMenus.includes(label);
return (
<li key={path}>
<div
className={cn(
"w-full flex items-center justify-between gap-2 px-2 py-3 rounded-md cursor-pointer transition text-md font-medium",
isActive
? "bg-gray-600 text-white"
: "text-gray-400 hover:bg-gray-700 hover:text-white",
)}
onClick={() =>
children ? toggleSubMenu(label) : handleClick(path)
}
>
<div className="flex items-center gap-2">
<Icon className="w-5 h-5" />
{t(label)}
</div>
{children && (
<span className="transition-transform">
{isOpen ? (
<ChevronDown className="w-4 h-4" />
) : (
<ChevronRight className="w-4 h-4" />
)}
</span>
)}
</div>
{children && isOpen && (
<ul className="ml-6 mt-1 space-y-1 border-l border-gray-700 pl-3">
{children.map((sub) => (
<li key={sub.path}>
<Button
variant="ghost"
size="sm"
onClick={() => handleClick(sub.path)}
className={cn(
"w-full justify-start gap-2 text-sm !px-2 !py-2 cursor-pointer",
active === sub.path
? "bg-gray-700 text-white"
: "text-gray-400 hover:text-white hover:bg-gray-800",
)}
>
{t(sub.label)}
</Button>
</li>
))}
</ul>
)}
</li>
);
})}
<LangToggle />
</ul>
);
return (
<div className="lg:border">
<div className="lg:hidden flex items-center justify-between bg-gray-900 p-4 sticky top-0 z-50">
<Sheet open={isSheetOpen} onOpenChange={setIsSheetOpen}>
<div className="flex gap-4">
<LangToggle />
<SheetTrigger asChild>
<Button variant="outline" size="icon">
<Menu className="w-5 h-5" />
</Button>
</SheetTrigger>
</div>
<SheetContent side="left" className="p-0 w-64 bg-gray-900">
<SheetHeader className="p-4 border-b">
<SheetTitle className="flex items-center gap-2">
<img
src="/Logo_white.png"
width={120}
height={120}
alt="logo"
/>
</SheetTitle>
</SheetHeader>
<nav className="overflow-y-auto">{MenuList}</nav>
</SheetContent>
</Sheet>
</div>
<aside className="hidden bg-gray-900 lg:flex w-64 flex-col h-screen">
<div className="flex items-center gap-2 p-4 border-b">
<img src="/Logo_white.png" width={120} height={120} alt="logo" />
</div>
<nav className="flex-1 overflow-y-auto">{MenuList}</nav>
</aside>
</div>
);
}