mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix: Sidebar focus on some items
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
import {PageParams} from "@/types/next";
|
||||||
|
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
|
||||||
|
import {db} from "@/db";
|
||||||
|
import {notFound} from "next/navigation";
|
||||||
|
|
||||||
|
export default async function RoutePage(props: PageParams<{}>) {
|
||||||
|
|
||||||
|
const settings = await db.query.setting.findFirst({
|
||||||
|
where: (fields, {eq}) => eq(fields.name, "system"),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!settings) {
|
||||||
|
notFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<PageHeader className="flex flex-col">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<PageTitle className="mb-3">System settings</PageTitle>
|
||||||
|
</div>
|
||||||
|
</PageHeader>
|
||||||
|
<PageContent className="flex flex-col gap-5">
|
||||||
|
</PageContent>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -22,7 +22,6 @@ export const SidebarMenuCustomMain = () => {
|
|||||||
const {data: session, isPending, error} = authClient.useSession();
|
const {data: session, isPending, error} = authClient.useSession();
|
||||||
const member = authClient.useActiveMember();
|
const member = authClient.useActiveMember();
|
||||||
|
|
||||||
|
|
||||||
if (isPending) return null;
|
if (isPending) return null;
|
||||||
|
|
||||||
if (error || !session) {
|
if (error || !session) {
|
||||||
@@ -39,9 +38,6 @@ export const SidebarMenuCustomMain = () => {
|
|||||||
{title: "Settings", url: "/settings", icon: Settings, details: true, type: "item"}
|
{title: "Settings", url: "/settings", icon: Settings, details: true, type: "item"}
|
||||||
];
|
];
|
||||||
|
|
||||||
// if (activeOrganization && (member?.data?.role === "admin" || member?.data?.role === "owner")) {
|
|
||||||
// groupContent.push({ title: "Settings", url: "/settings", icon: Settings, details:true });
|
|
||||||
// }
|
|
||||||
|
|
||||||
const items: SidebarGroupItem[] = [
|
const items: SidebarGroupItem[] = [
|
||||||
{
|
{
|
||||||
@@ -98,19 +94,14 @@ export const SidebarMenuCustomMain = () => {
|
|||||||
type: "collapse",
|
type: "collapse",
|
||||||
submenu: [
|
submenu: [
|
||||||
{title: "Users", url: "/admin/users", icon: Users, type: "item"},
|
{title: "Users", url: "/admin/users", icon: Users, type: "item"},
|
||||||
{title: "Organizations", url: "/admin/organizations", icon: Building, type: "item"},
|
{title: "Organizations", url: "/admin/organizations", icon: Building, type: "item", details: true},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
url: "/admin/settings",
|
url: "/admin/settings",
|
||||||
icon: Settings,
|
icon: Settings,
|
||||||
details: true,
|
type: "item",
|
||||||
type: "collapse",
|
|
||||||
submenu: [
|
|
||||||
{title: "Email", url: "/admin/settings/email", icon: Mail, type: "item"},
|
|
||||||
{title: "Storage", url: "/admin/settings/storage", icon: PackageOpen, type: "item"},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
import { ChevronRight, ChevronDown, MoreHorizontal } from "lucide-react";
|
import { ChevronRight, ChevronDown, MoreHorizontal } from "lucide-react";
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { buttonVariants } from "@/components/ui/button";
|
import { buttonVariants } from "@/components/ui/button";
|
||||||
@@ -46,43 +46,18 @@ type SidebarMenuCustomBaseProps = {
|
|||||||
|
|
||||||
export const SidebarMenuCustomBase = ({ baseUrl, items }: SidebarMenuCustomBaseProps) => {
|
export const SidebarMenuCustomBase = ({ baseUrl, items }: SidebarMenuCustomBaseProps) => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [activeItem, setActiveItem] = useState("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
const normalize = (url: string) => url.split("?")[0].replace(/\/$/, "");
|
||||||
const normalize = (url: string) => url.split("?")[0].replace(/\/$/, "");
|
|
||||||
|
|
||||||
const findActiveUrl = () => {
|
const isItemActive = (item: SidebarItem) => {
|
||||||
const normalizedPathname = normalize(pathname);
|
const fullUrl = item.not_from_base_url ? normalize(item.url) : normalize(`${baseUrl}${item.url}`);
|
||||||
for (const group of items) {
|
if (item.details) return normalize(pathname).startsWith(fullUrl);
|
||||||
for (const item of group.group_content) {
|
return normalize(pathname) === fullUrl;
|
||||||
if (item.submenu) {
|
};
|
||||||
for (const subItem of item.submenu) {
|
|
||||||
const subItemUrl = normalize(subItem.url);
|
|
||||||
const subFullUrl = normalize(`${baseUrl}${subItemUrl}`);
|
|
||||||
|
|
||||||
if (normalizedPathname === subFullUrl || (subItem.details && normalizedPathname.startsWith(`${subFullUrl}/`))) {
|
|
||||||
return subItemUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemUrl = normalize(item.url);
|
|
||||||
const fullUrl = item.not_from_base_url ? itemUrl : normalize(`${baseUrl}${itemUrl}`);
|
|
||||||
|
|
||||||
if (normalizedPathname === fullUrl || (item.details && normalizedPathname.startsWith(`${fullUrl}/`))) {
|
|
||||||
return itemUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
setActiveItem(findActiveUrl());
|
|
||||||
}, [pathname, baseUrl, items]);
|
|
||||||
|
|
||||||
const isSubActive = (item: SidebarItem) => {
|
const isSubActive = (item: SidebarItem) => {
|
||||||
if (!item.submenu) return false;
|
if (!item.submenu) return false;
|
||||||
return item.submenu.some((sub) => sub.url === activeItem);
|
return item.submenu.some((sub) => isItemActive(sub));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -91,7 +66,7 @@ export const SidebarMenuCustomBase = ({ baseUrl, items }: SidebarMenuCustomBaseP
|
|||||||
<Collapsible key={index} defaultOpen className="group/group-collapsible">
|
<Collapsible key={index} defaultOpen className="group/group-collapsible">
|
||||||
<SidebarGroup>
|
<SidebarGroup>
|
||||||
<SidebarGroupLabel asChild>
|
<SidebarGroupLabel asChild>
|
||||||
<CollapsibleTrigger disabled={group.type == "list"} className="flex w-full items-center text-sm font-medium text-sidebar-foreground/70">
|
<CollapsibleTrigger disabled={group.type === "list"} className="flex w-full items-center text-sm font-medium text-sidebar-foreground/70">
|
||||||
{group.label}
|
{group.label}
|
||||||
{group.type === "collapse" && (
|
{group.type === "collapse" && (
|
||||||
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/group-collapsible:rotate-180" />
|
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/group-collapsible:rotate-180" />
|
||||||
@@ -118,14 +93,10 @@ export const SidebarMenuCustomBase = ({ baseUrl, items }: SidebarMenuCustomBaseP
|
|||||||
<SidebarMenuSub>
|
<SidebarMenuSub>
|
||||||
{item.submenu.map((sub, subIdx) => (
|
{item.submenu.map((sub, subIdx) => (
|
||||||
<SidebarMenuSubItem key={subIdx}>
|
<SidebarMenuSubItem key={subIdx}>
|
||||||
<SidebarMenuSubButton asChild isActive={activeItem === sub.url}>
|
<SidebarMenuSubButton asChild isActive={isItemActive(sub)}>
|
||||||
<Link
|
<Link
|
||||||
className={cn(
|
href={sub.not_from_base_url ? sub.url : `${baseUrl}${sub.url}`}
|
||||||
buttonVariants({ variant: "ghost", size: "sm" }),
|
className={cn(buttonVariants({ variant: "ghost", size: "sm" }), "w-full justify-start")}
|
||||||
"w-full justify-start"
|
|
||||||
)}
|
|
||||||
href={`${baseUrl}${sub.url}`}
|
|
||||||
onClick={() => setActiveItem(sub.url)}
|
|
||||||
>
|
>
|
||||||
<sub.icon />
|
<sub.icon />
|
||||||
<span>{sub.title}</span>
|
<span>{sub.title}</span>
|
||||||
@@ -142,11 +113,10 @@ export const SidebarMenuCustomBase = ({ baseUrl, items }: SidebarMenuCustomBaseP
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarMenuItem key={idx}>
|
<SidebarMenuItem key={idx}>
|
||||||
<SidebarMenuButton asChild isActive={activeItem === item.url} tooltip={item.title}>
|
<SidebarMenuButton asChild isActive={isItemActive(item)} tooltip={item.title}>
|
||||||
<Link
|
<Link
|
||||||
href={item.redirect || item.not_from_base_url ? item.url : `${baseUrl}${item.url}`}
|
href={item.redirect || item.not_from_base_url ? item.url : `${baseUrl}${item.url}`}
|
||||||
target={item.redirect ? "_blank" : ""}
|
target={item.redirect ? "_blank" : ""}
|
||||||
onClick={() => setActiveItem(item.url)}
|
|
||||||
className={cn(buttonVariants({ variant: "ghost", size: "lg" }), "justify-start")}
|
className={cn(buttonVariants({ variant: "ghost", size: "lg" }), "justify-start")}
|
||||||
>
|
>
|
||||||
<item.icon />
|
<item.icon />
|
||||||
@@ -163,11 +133,7 @@ export const SidebarMenuCustomBase = ({ baseUrl, items }: SidebarMenuCustomBaseP
|
|||||||
{item.dropdown.map((dropdown, dIdx) => (
|
{item.dropdown.map((dropdown, dIdx) => (
|
||||||
<DropdownMenuItem key={dIdx} asChild>
|
<DropdownMenuItem key={dIdx} asChild>
|
||||||
<Link
|
<Link
|
||||||
href={
|
href={dropdown.redirect || dropdown.not_from_base_url ? dropdown.url : `${baseUrl}${dropdown.url}`}
|
||||||
dropdown.redirect || dropdown.not_from_base_url
|
|
||||||
? dropdown.url
|
|
||||||
: `${baseUrl}${dropdown.url}`
|
|
||||||
}
|
|
||||||
target={dropdown.redirect ? "_blank" : ""}
|
target={dropdown.redirect ? "_blank" : ""}
|
||||||
>
|
>
|
||||||
<span>{dropdown.title}</span>
|
<span>{dropdown.title}</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user