Merge branch 'develop' of repo.ravanertebat.com:TM/tm_panel into develop

This commit is contained in:
Karim Pirzargar
2026-04-27 12:29:33 +03:30
3 changed files with 73 additions and 12 deletions
@@ -19,6 +19,7 @@ export function ThemeToggleSwitch() {
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const [isTransitioning, setIsTransitioning] = useState(false); const [isTransitioning, setIsTransitioning] = useState(false);
const isDarkTheme = theme === "dark"; const isDarkTheme = theme === "dark";
const tripleTapMaxDelay = 500;
useEffect(() => { useEffect(() => {
setMounted(true); setMounted(true);
@@ -34,6 +35,45 @@ export function ThemeToggleSwitch() {
return () => window.clearTimeout(transitionTimer); return () => window.clearTimeout(transitionTimer);
}, [isTransitioning]); }, [isTransitioning]);
useEffect(() => {
if (!mounted) return;
let tapCount = 0;
let lastTapAt = 0;
const isTypingInInput = (target: EventTarget | null) => {
if (!(target instanceof HTMLElement)) return false;
const tagName = target.tagName.toLowerCase();
if (tagName === "input" || tagName === "textarea" || tagName === "select") {
return true;
}
return target.isContentEditable;
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.repeat) return;
if (event.key.toLowerCase() !== "t") return;
if (event.ctrlKey || event.metaKey || event.altKey) return;
if (isTypingInInput(event.target)) return;
const now = Date.now();
tapCount = now - lastTapAt <= tripleTapMaxDelay ? tapCount + 1 : 1;
lastTapAt = now;
if (tapCount === 3) {
setIsTransitioning(true);
setTheme(theme === "dark" ? "light" : "dark");
tapCount = 0;
lastTapAt = 0;
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [mounted, setTheme, theme]);
if (!mounted) { if (!mounted) {
return null; return null;
} }
+9 -1
View File
@@ -29,12 +29,14 @@ export const NAV_DATA: NavData = [
url: "/", url: "/",
icon: Icons.HomeIcon, icon: Icons.HomeIcon,
items: [], items: [],
visible: true,
}, },
{ {
title: "Admins", title: "Admins",
url: "/admins", url: "/admins",
icon: Icons.User, icon: Icons.User,
items: [], items: [],
visible: true,
}, },
{ {
title: "Manage Companies", title: "Manage Companies",
@@ -46,12 +48,14 @@ export const NAV_DATA: NavData = [
url: "/companies", url: "/companies",
title: "Companies", title: "Companies",
items: [], items: [],
visible: true,
}, },
{ {
icon: Icons.Building, icon: Icons.Building,
url: "/company-categories", url: "/company-categories",
title: "Company Categories", title: "Company Categories",
items: [], items: [],
visible: true,
}, },
], ],
}, },
@@ -60,18 +64,21 @@ export const NAV_DATA: NavData = [
url: "/customers", url: "/customers",
icon: Icons.User, icon: Icons.User,
items: [], items: [],
visible: true,
}, },
{ {
title: "Tenders", title: "Tenders",
url: "/tenders", url: "/tenders",
icon: Icons.Table, icon: Icons.Table,
items: [], items: [],
visible: true,
}, },
{ {
title: "Notifications", title: "Notifications",
url: "/notification-history", url: "/notification-history",
icon: Icons.NotificationIcon, icon: Icons.NotificationIcon,
items: [], items: [],
visible: true,
}, },
{ {
title: "Contact Us", title: "Contact Us",
@@ -85,13 +92,14 @@ export const NAV_DATA: NavData = [
url: "/marketing", url: "/marketing",
icon: GlobeSearch, icon: GlobeSearch,
items: [], items: [],
visible:false visible: true
}, },
{ {
title: "Inquiries", title: "Inquiries",
url: "/inquiries", url: "/inquiries",
icon: Icons.BriefcaseTimer, icon: Icons.BriefcaseTimer,
items: [], items: [],
visible: true,
}, },
], ],
}, },
+24 -11
View File
@@ -15,6 +15,7 @@ export function Sidebar() {
const pathname = usePathname(); const pathname = usePathname();
const { setIsOpen, isOpen, isMobile, toggleSidebar } = useSidebarContext(); const { setIsOpen, isOpen, isMobile, toggleSidebar } = useSidebarContext();
const [expandedItems, setExpandedItems] = useState<string[]>([]); const [expandedItems, setExpandedItems] = useState<string[]>([]);
const isVisible = (item: { visible?: boolean }) => item.visible !== false;
const toggleExpanded = (title: string) => { const toggleExpanded = (title: string) => {
setExpandedItems((prev) => setExpandedItems((prev) =>
@@ -26,9 +27,9 @@ export function Sidebar() {
const newExpandedItems: string[] = []; const newExpandedItems: string[] = [];
NAV_DATA.forEach((section) => { NAV_DATA.forEach((section) => {
section.items.forEach((item) => { section.items.filter(isVisible).forEach((item) => {
if (item.items.length > 0) { if (item.items.length > 0) {
const hasActiveSubItem = item.items.some( const hasActiveSubItem = item.items.filter(isVisible).some(
(subItem: NavSubItem) => (subItem: NavSubItem) =>
subItem.url === pathname || subItem.url === pathname ||
(pathname.startsWith(subItem.url) && subItem.url !== "/"), (pathname.startsWith(subItem.url) && subItem.url !== "/"),
@@ -115,7 +116,14 @@ export function Sidebar() {
isCollapsed ? "w-full px-1" : "pr-3", isCollapsed ? "w-full px-1" : "pr-3",
)} )}
> >
{NAV_DATA.map((section) => ( {NAV_DATA.map((section) => {
const visibleItems = section.items.filter(isVisible);
if (!visibleItems.length) {
return null;
}
return (
<div key={section.label} className="mb-6"> <div key={section.label} className="mb-6">
{!isCollapsed && ( {!isCollapsed && (
<h2 className="mb-4 px-2 text-[11px] font-semibold uppercase tracking-[0.12em] text-dark-5 dark:text-dark-6"> <h2 className="mb-4 px-2 text-[11px] font-semibold uppercase tracking-[0.12em] text-dark-5 dark:text-dark-6">
@@ -125,18 +133,21 @@ export function Sidebar() {
<nav role="navigation" aria-label={section.label}> <nav role="navigation" aria-label={section.label}>
<ul className={cn("space-y-2", isCollapsed && "space-y-1")}> <ul className={cn("space-y-2", isCollapsed && "space-y-1")}>
{section.items.map((item) => ( {visibleItems.map((item) => {
const visibleSubItems = item.items.filter(isVisible);
return (
<li key={item.title}> <li key={item.title}>
{item.items.length ? ( {visibleSubItems.length ? (
<div> <div>
<MenuItem <MenuItem
isActive={ isActive={
item.items.some( visibleSubItems.some(
({ url }: { url: string }) => ({ url }: { url: string }) =>
url === pathname, url === pathname,
) || ) ||
(item.items.length > 0 && (visibleSubItems.length > 0 &&
item.items.some( visibleSubItems.some(
({ url }: { url: string }) => ({ url }: { url: string }) =>
pathname.startsWith(url) && url !== "/", pathname.startsWith(url) && url !== "/",
)) ))
@@ -178,7 +189,7 @@ export function Sidebar() {
className="ml-9 mr-0 space-y-1.5 pb-[15px] pr-0 pt-2" className="ml-9 mr-0 space-y-1.5 pb-[15px] pr-0 pt-2"
role="menu" role="menu"
> >
{item.items.map((subItem: NavSubItem) => ( {visibleSubItems.map((subItem: NavSubItem) => (
<li key={subItem.title} role="none"> <li key={subItem.title} role="none">
<MenuItem <MenuItem
as="link" as="link"
@@ -232,11 +243,13 @@ export function Sidebar() {
})() })()
)} )}
</li> </li>
))} );
})}
</ul> </ul>
</nav> </nav>
</div> </div>
))} );
})}
</div> </div>
{/* Toggle button at bottom for collapsed state */} {/* Toggle button at bottom for collapsed state */}