Merge branch 'develop' of repo.ravanertebat.com:TM/tm_panel into develop
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user