diff --git a/src/shared/config/api/URLs.ts b/src/shared/config/api/URLs.ts
index 9373368..1f0a376 100644
--- a/src/shared/config/api/URLs.ts
+++ b/src/shared/config/api/URLs.ts
@@ -27,10 +27,13 @@ const SITE_SETTING = "dashboard/dashboard-site-settings/";
const SUPPORT_USER = "dashboard/dashboard-support/";
const SUPPORT_AGENCY = "dashboard/dashboard-travel-agency-request/";
const USER_ORDERS = "dashboard/dashboard-ticket-order/";
+const AGENCY_ORDERS = "dashboard/dashboard-site-travel-agency-report/";
const POPULAR_TOURS = "dashboard/dashboard-ticket-featured/";
const BANNER = "dashboard/dashboard-site-banner/";
+const TOUR_ADMIN = "dashboard/dashboard-tour-admin/";
export {
+ AGENCY_ORDERS,
AUTH_LOGIN,
BANNER,
BASE_URL,
@@ -57,6 +60,7 @@ export {
SITE_SETTING,
SUPPORT_AGENCY,
SUPPORT_USER,
+ TOUR_ADMIN,
TOUR_TRANSPORT,
UPDATE_USER,
USER_ORDERS,
diff --git a/src/shared/config/i18n/locales/ru/translation.json b/src/shared/config/i18n/locales/ru/translation.json
index 99a25b2..bd6bd2f 100644
--- a/src/shared/config/i18n/locales/ru/translation.json
+++ b/src/shared/config/i18n/locales/ru/translation.json
@@ -466,5 +466,30 @@
"Reytingi baland turlar": "Высокорейтинговые туры",
"Status muvaffaqiyatli yangilandi": "Статус успешно обновлён",
"Statusni yangilashda xatolik yuz berdi": "Ошибка обновления статуса",
- "Refunded": "Подтверждено"
+ "Refunded": "Подтверждено",
+ "Partner Agencies": "Партнерские агентства",
+ "Bookings": "Заказы",
+ "Destinations": "Количество видов",
+ "Total Revenue": "Общий доход",
+ "From completed bookings": "Из завершенных бронирований",
+ "To'langan summa": "Выплаченная сумма",
+ "Kutilayotgan summa": "Ожидаемая сумма",
+ "Average Rating": "Средняя оценка",
+ "Tour Overview": "Добавленные туры",
+ "Reviews": "Комментарии",
+ "Tour Information": "Данные о типе",
+ "Agentlik nomi": "Название агентства",
+ "Manzili": "Адрес",
+ "Id raqami va ulushi": "Номер ID и доля",
+ "Ulushi": "Доля",
+ "Tour Inclusions": "Доходы",
+ "Platformaga tegishli": "Принадлежащий платформе",
+ "Platformaga daromadi": "Доход от платформы",
+ "Agentlik daromadi": "Агентский доход",
+ "Recent Bookings": "Последние бронирования",
+ "Travel Date": "Дата поездки",
+ "Booking Date": "Дата бронирования",
+ "Yangi foydalanuvchi ma'lumotlari": "Данные нового пользователя",
+ "Agentlik uchun tizimga kirish ma'lumotlari": "Входные данные для агентства",
+ "Haqiqatan ham bu foydalanuvchini o'chirmoqchimisiz? Bu amalni qaytarib bo'lmaydi.": "Вы уверены, что хотите удалить этого пользователя? Это действие необратимо."
}
diff --git a/src/shared/config/i18n/locales/uz/translation.json b/src/shared/config/i18n/locales/uz/translation.json
index 16293ac..f45156a 100644
--- a/src/shared/config/i18n/locales/uz/translation.json
+++ b/src/shared/config/i18n/locales/uz/translation.json
@@ -467,5 +467,30 @@
"Reytingi baland turlar": "Reytingi baland turlar",
"Status muvaffaqiyatli yangilandi": "Status muvaffaqiyatli yangilandi",
"Statusni yangilashda xatolik yuz berdi": "Statusni yangilashda xatolik yuz berdi",
- "Refunded": "Tasdiqlangan"
+ "Refunded": "Tasdiqlangan",
+ "Partner Agencies": "Hamkor agentliklar",
+ "Bookings": "Buyurtmalar",
+ "Destinations": "Turlar soni",
+ "Total Revenue": "Jami daromad",
+ "From completed bookings": "Yakunlangan bandlovlardan",
+ "To'langan summa": "To'langan summa",
+ "Kutilayotgan summa": "Kutilayotgan summa",
+ "Average Rating": "O‘rtacha baho",
+ "Tour Overview": "Qo'shilgan turlar",
+ "Reviews": "Sharhlar",
+ "Tour Information": "Tur ma’lumotlari",
+ "Agentlik nomi": "Agentlik nomi",
+ "Manzili": "Manzili",
+ "Id raqami va ulushi": "Id raqami va ulushi",
+ "Ulushi": "Ulushi",
+ "Tour Inclusions": "Kirimlar",
+ "Platformaga tegishli": "Platformaga tegishli",
+ "Platformaga daromadi": "Platformaga daromadi",
+ "Agentlik daromadi": "Agentlik daromadi",
+ "Recent Bookings": "Oxirgi bandlovlar",
+ "Travel Date": "Sayohat sanasi",
+ "Booking Date": "Bandlov sanasi",
+ "Yangi foydalanuvchi ma'lumotlari": "Yangi foydalanuvchi ma'lumotlari",
+ "Agentlik uchun tizimga kirish ma'lumotlari": "Agentlik uchun tizimga kirish ma'lumotlari",
+ "Haqiqatan ham bu foydalanuvchini o'chirmoqchimisiz? Bu amalni qaytarib bo'lmaydi.": "Haqiqatan ham bu foydalanuvchini o'chirmoqchimisiz? Bu amalni qaytarib bo'lmaydi."
}
diff --git a/src/shared/lib/formatPhone.ts b/src/shared/lib/formatPhone.ts
index 47486b6..3664c00 100644
--- a/src/shared/lib/formatPhone.ts
+++ b/src/shared/lib/formatPhone.ts
@@ -1,36 +1,30 @@
/**
- * Format the number (+998 00 111-22-33)
- * @param value Number to be formatted (XXXYYZZZAABB)
- * @returns string +998 00 111-22-33
+ * Format phone number: +998 00 111-22-33 yoki +888 00 111-22-33
*/
const formatPhone = (value: string) => {
- // Keep only numbers
- const digits = value.replace(/\D/g, '');
+ // faqat raqamlarni olish
+ const digits = value.replace(/\D/g, "");
- // Return empty string if data is not available
- if (digits.length === 0) {
- return '';
- }
+ // agar hech narsa yo'q bo'lsa — input bo'sh bo'lib tursin
+ if (digits.length === 0) return "";
- const prefix = digits.startsWith('998') ? '+998 ' : '+998 ';
+ // prefiksni aniqlash (faqat agar 998 yoki 888 bilan boshlangan bo'lsa)
+ let prefix = "";
+ if (digits.startsWith("998")) prefix = "+998 ";
+ else if (digits.startsWith("888")) prefix = "+888 ";
+
+ // agar 998 ham 888 ham emas bo‘lsa — foydalanuvchi hali prefiks kiritmagan, hech narsa qaytarmaymiz
+ if (!prefix) return "+" + digits;
+
+ // prefiksni olib tashlab, asosiy raqam qismini olish
+ const core = digits.replace(/^998|^888/, "");
let formattedNumber = prefix;
- if (digits.length > 3) {
- formattedNumber += digits.slice(3, 5);
- }
-
- if (digits.length > 5) {
- formattedNumber += ' ' + digits.slice(5, 8);
- }
-
- if (digits.length > 8) {
- formattedNumber += '-' + digits.slice(8, 10);
- }
-
- if (digits.length > 10) {
- formattedNumber += '-' + digits.slice(10, 12);
- }
+ if (core.length > 0) formattedNumber += core.slice(0, 2);
+ if (core.length > 2) formattedNumber += " " + core.slice(2, 5);
+ if (core.length > 5) formattedNumber += "-" + core.slice(5, 7);
+ if (core.length > 7) formattedNumber += "-" + core.slice(7, 9);
return formattedNumber.trim();
};
diff --git a/src/widgets/sidebar/ui/Sidebar.tsx b/src/widgets/sidebar/ui/Sidebar.tsx
index 13467be..541856e 100644
--- a/src/widgets/sidebar/ui/Sidebar.tsx
+++ b/src/widgets/sidebar/ui/Sidebar.tsx
@@ -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
(location.pathname);
const [openMenus, setOpenMenus] = useState([]);
@@ -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 = (
-
- {visibleMenu.map(({ label, icon: Icon, path, children }) => {
- const isActive = active.startsWith(path);
- const isOpen = openMenus.includes(label);
+
+
+ {visibleMenu.map(({ label, icon: Icon, path, children }) => {
+ const isActive = active.startsWith(path);
+ const isOpen = openMenus.includes(label);
+ const hasChildren = children?.length > 0;
- return (
- -
-
- children ? toggleSubMenu(label) : handleClick(path)
- }
- >
-
-
- {t(label)}
+ return (
+
-
+
+ hasChildren ? toggleSubMenu(label) : handleClick(path)
+ }
+ >
+
+
+ {t(label)}
+
+ {hasChildren && (
+
+ {isOpen ? (
+
+ ) : (
+
+ )}
+
+ )}
- {children && (
-
- {isOpen ? (
-
- ) : (
-
- )}
-
+
+ {hasChildren && isOpen && (
+
+ {children.map((sub) => (
+ -
+
+
+ ))}
+
)}
-
+
+ );
+ })}
+
+
- {children && isOpen && (
-
- {children.map((sub) => (
- -
-
-
- ))}
-
- )}
-
- );
- })}
-
-
-
+
+
+
+
);
return (
+ {/* Mobil versiya */}
@@ -277,15 +292,17 @@ export function Sidebar({ role }: SidebarProps) {
/>
-
+
+
+ {/* Desktop versiya */}
);