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

@@ -1,4 +1,4 @@
import { LanguageRoutes } from '@/shared/config/i18n/type';
import { LanguageRoutes } from "@/shared/config/i18n/type";
const languages: { name: string; key: LanguageRoutes }[] = [
{
@@ -6,11 +6,7 @@ const languages: { name: string; key: LanguageRoutes }[] = [
key: LanguageRoutes.UZ,
},
{
name: 'Ўзбекча',
key: LanguageRoutes.KI,
},
{
name: 'Русский',
name: "Русский",
key: LanguageRoutes.RU,
},
];

View File

@@ -1,14 +1,14 @@
import { LanguageRoutes } from '@/shared/config/i18n/type';
import { Button } from '@/shared/ui/button';
import { LanguageRoutes } from "@/shared/config/i18n/type";
import { Button } from "@/shared/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/shared/ui/dropdown-menu';
import { languages } from '@/widgets/lang-toggle/lib/data';
import { GlobeIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
} from "@/shared/ui/dropdown-menu";
import { languages } from "@/widgets/lang-toggle/lib/data";
import { GlobeIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
const LangToggle = () => {
const { i18n } = useTranslation();
@@ -21,7 +21,7 @@ const LangToggle = () => {
<DropdownMenuTrigger asChild>
<Button variant="outline">
<GlobeIcon />
<span>{languages.find((e) => e.key == i18n.language)?.name}</span>
<span>{languages.find((e) => e.key === i18n.language)?.name}</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">

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>
);
}

View File

@@ -1,17 +1,8 @@
import { getPosts } from '@/shared/config/api/test/test.request';
import LangToggle from '@/widgets/lang-toggle/ui/lang-toggle';
import ModeToggle from '@/widgets/theme-toggle/ui/theme-toggle';
import { useQuery } from '@tanstack/react-query';
import GitHubButton from 'react-github-btn';
import LangToggle from "@/widgets/lang-toggle/ui/lang-toggle";
import ModeToggle from "@/widgets/theme-toggle/ui/theme-toggle";
import GitHubButton from "react-github-btn";
const Welcome = () => {
const { data } = useQuery({
queryKey: ['posts'],
queryFn: () => getPosts(),
});
console.log('data', data);
return (
<div className="custom-container h-screen rounded-2xl flex items-center justify-center">
<div className="flex flex-col gap-2 items-center">