barcha apilar ulandi

This commit is contained in:
Samandar Turgunboyev
2025-10-31 18:42:21 +05:00
parent 39f5b8ca3c
commit 77bce24399
19 changed files with 1306 additions and 656 deletions

View File

@@ -1,5 +1,6 @@
"use client";
import { removeAuthToken, removeRefAuthToken } from "@/shared/lib/authCookies";
import { cn } from "@/shared/lib/utils";
import { Button } from "@/shared/ui/button";
import {
@@ -10,12 +11,14 @@ import {
SheetTrigger,
} from "@/shared/ui/sheet";
import LangToggle from "@/widgets/lang-toggle/ui/lang-toggle";
import { useQueryClient } from "@tanstack/react-query";
import {
Briefcase,
Building2,
ChevronDown,
ChevronRight,
HelpCircle,
LogOut,
Menu,
MessageSquare,
Newspaper,
@@ -27,6 +30,7 @@ import {
import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom";
import { toast } from "sonner";
type Role =
| "superuser"
@@ -46,13 +50,13 @@ const MENU_ITEMS = [
label: "Foydalanuvchilar",
icon: Users,
path: "/user",
roles: ["moderator", "admin", "superuser", "moderator"],
roles: ["moderator", "admin", "superuser", "operator"],
},
{
label: "Tur firmalar",
icon: Building2,
path: "/agencies",
roles: ["moderator", "admin", "superuser", "moderator"],
roles: ["moderator", "admin", "superuser", "operator"],
},
{
label: "Xodimlar",
@@ -64,20 +68,13 @@ const MENU_ITEMS = [
label: "Bronlar",
icon: Wallet,
path: "/finance",
roles: ["moderator", "admin", "superuser", "buxgalter"],
roles: ["moderator", "admin", "superuser", "buxgalter", "tour_admin"],
},
{
label: "Turlar",
icon: Plane,
path: "/tours",
roles: [
"moderator",
"admin",
"superuser",
"tour_admin",
"operator",
"buxgalter",
],
roles: ["moderator", "admin", "superuser", "tour_admin"],
children: [
{ label: "Turlar", path: "/tours" },
{
@@ -87,19 +84,6 @@ const MENU_ITEMS = [
},
],
},
// {
// label: "Bronlar",
// icon: CalendarCheck2,
// path: "/bookings",
// roles: [
// "moderator",
// "admin",
// "superuser",
// "tour_admin",
// "operator",
// "buxgalter",
// ],
// },
{
label: "Yangiliklar",
icon: Newspaper,
@@ -129,7 +113,7 @@ const MENU_ITEMS = [
{
label: "Agentlik arizalari",
path: "/support/tours",
roles: ["moderator", "admin", "superuser", "operator"],
roles: ["moderator", "admin", "superuser"],
},
{
label: "Yordam arizalari",
@@ -164,12 +148,21 @@ export function Sidebar({ role }: SidebarProps) {
const navigate = useNavigate();
const { t } = useTranslation();
const location = useLocation();
const queryClient = useQueryClient();
const [isSheetOpen, setIsSheetOpen] = useState(false);
const visibleMenu = useMemo(
() => MENU_ITEMS.filter((item) => item.roles.includes(role)),
[role],
);
const visibleMenu = useMemo(() => {
return MENU_ITEMS.filter((item) => item.roles.includes(role)).map(
(item) => ({
...item,
children: item.children
? item.children.filter(
(child) => !child.roles || child.roles.includes(role),
)
: [],
}),
);
}, [role]);
const [active, setActive] = useState<string>(location.pathname);
const [openMenus, setOpenMenus] = useState<string[]>([]);
@@ -190,71 +183,93 @@ export function Sidebar({ role }: SidebarProps) {
);
};
const handleLogout = () => {
removeAuthToken();
removeRefAuthToken();
queryClient.clear();
toast.success(t("Tizimdan chiqdingiz"));
navigate("/auth/login");
};
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);
<div className="flex flex-col h-full justify-between">
<ul className="p-2 space-y-1 flex-1 overflow-y-auto">
{visibleMenu.map(({ label, icon: Icon, path, children }) => {
const isActive = active.startsWith(path);
const isOpen = openMenus.includes(label);
const hasChildren = children?.length > 0;
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)}
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={() =>
hasChildren ? toggleSubMenu(label) : handleClick(path)
}
>
<div className="flex items-center gap-2">
<Icon className="w-5 h-5" />
{t(label)}
</div>
{hasChildren && (
<span className="transition-transform">
{isOpen ? (
<ChevronDown className="w-4 h-4" />
) : (
<ChevronRight className="w-4 h-4" />
)}
</span>
)}
</div>
{children && (
<span className="transition-transform">
{isOpen ? (
<ChevronDown className="w-4 h-4" />
) : (
<ChevronRight className="w-4 h-4" />
)}
</span>
{hasChildren && 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>
)}
</div>
</li>
);
})}
<LangToggle />
</ul>
{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>
<div className="border-t border-gray-700 mt-2 pt-3 px-2">
<Button
onClick={handleLogout}
variant="ghost"
className="w-full flex items-center justify-start gap-2 text-red-400 hover:bg-red-900/20 hover:text-red-300 mt-2"
>
<LogOut className="w-5 h-5" />
<span>{t("Chiqish")}</span>
</Button>
</div>
</div>
);
return (
<div className="lg:border">
{/* Mobil versiya */}
<div className="lg:hidden flex items-center justify-end bg-gray-900 p-4 sticky top-0 z-50">
<Sheet open={isSheetOpen} onOpenChange={setIsSheetOpen}>
<div className="flex gap-4">
@@ -277,15 +292,17 @@ export function Sidebar({ role }: SidebarProps) {
/>
</SheetTitle>
</SheetHeader>
<nav className="overflow-y-auto">{MenuList}</nav>
<nav className="h-full">{MenuList}</nav>
</SheetContent>
</Sheet>
</div>
{/* Desktop versiya */}
<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>
<nav className="flex-1">{MenuList}</nav>
</aside>
</div>
);