mirror of
https://github.com/ershisan99/db-studio.git
synced 2025-12-16 12:33:05 +00:00
remove prime ng and fix table layout. Fix select
This commit is contained in:
Binary file not shown.
@@ -5,7 +5,6 @@ import {
|
|||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
ScrollArea,
|
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
@@ -167,7 +166,7 @@ export const DataTable = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"flex flex-col gap-4 flex-1 max-h-full h-full pb-3"}>
|
<div className={"flex flex-col gap-4 flex-1 max-h-full pb-3"}>
|
||||||
<div className={"flex gap-4 items-center justify-between"}>
|
<div className={"flex gap-4 items-center justify-between"}>
|
||||||
<h1 className="text-2xl font-bold flex items-center gap-2">
|
<h1 className="text-2xl font-bold flex items-center gap-2">
|
||||||
<Rows3 /> {tableName}
|
<Rows3 /> {tableName}
|
||||||
@@ -201,103 +200,105 @@ export const DataTable = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ScrollArea className="rounded-md border min-h-0 h-full w-full min-w-0">
|
<div className="rounded-md border min-h-0 h-full w-full min-w-0 flex flex-col">
|
||||||
<Table
|
<div className={"flex flex-col flex-1 overflow-auto relative"}>
|
||||||
className={"table-fixed min-w-full"}
|
<Table
|
||||||
{...{
|
className={"table-fixed min-w-full"}
|
||||||
style: {
|
{...{
|
||||||
width: table.getCenterTotalSize(),
|
style: {
|
||||||
},
|
width: table.getCenterTotalSize(),
|
||||||
}}
|
},
|
||||||
>
|
}}
|
||||||
<TableHeader>
|
>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
<TableHeader>
|
||||||
<TableRow key={headerGroup.id}>
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
{headerGroup.headers.map((header) => {
|
<TableRow key={headerGroup.id} className={"sticky top-0"}>
|
||||||
const sorted = header.column.getIsSorted();
|
{headerGroup.headers.map((header) => {
|
||||||
|
const sorted = header.column.getIsSorted();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHead
|
<TableHead
|
||||||
className={"p-0 relative"}
|
className={"p-0 relative"}
|
||||||
key={header.id}
|
key={header.id}
|
||||||
style={{
|
style={{
|
||||||
width: header.getSize(),
|
width: header.getSize(),
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={header.column.getToggleSortingHandler()}
|
|
||||||
title={
|
|
||||||
header.column.getNextSortingOrder() === "asc"
|
|
||||||
? "Sort ascending"
|
|
||||||
: header.column.getNextSortingOrder() === "desc"
|
|
||||||
? "Sort descending"
|
|
||||||
: "Clear sort"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{header.isPlaceholder
|
|
||||||
? null
|
|
||||||
: flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext(),
|
|
||||||
)}
|
|
||||||
<ArrowUp
|
|
||||||
className={cn(
|
|
||||||
"ml-2 size-4 opacity-0 transition-transform",
|
|
||||||
sorted && "opacity-100",
|
|
||||||
(sorted as string) === "desc" && "rotate-180",
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
<div
|
|
||||||
{...{
|
|
||||||
onDoubleClick: () => header.column.resetSize(),
|
|
||||||
onMouseDown: header.getResizeHandler(),
|
|
||||||
onTouchStart: header.getResizeHandler(),
|
|
||||||
className: `resizer ${header.column.getIsResizing() ? "isResizing" : ""}`,
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</TableHead>
|
<Button
|
||||||
);
|
variant="ghost"
|
||||||
})}
|
onClick={header.column.getToggleSortingHandler()}
|
||||||
</TableRow>
|
title={
|
||||||
))}
|
header.column.getNextSortingOrder() === "asc"
|
||||||
</TableHeader>
|
? "Sort ascending"
|
||||||
<TableBody>
|
: header.column.getNextSortingOrder() === "desc"
|
||||||
{table.getRowModel().rows?.length ? (
|
? "Sort descending"
|
||||||
table.getRowModel().rows.map((row) => (
|
: "Clear sort"
|
||||||
<TableRow
|
}
|
||||||
key={row.id}
|
>
|
||||||
data-state={row.getIsSelected() && "selected"}
|
{header.isPlaceholder
|
||||||
>
|
? null
|
||||||
{row.getVisibleCells().map((cell) => (
|
: flexRender(
|
||||||
<TableCell
|
header.column.columnDef.header,
|
||||||
key={cell.id}
|
header.getContext(),
|
||||||
style={{
|
)}
|
||||||
width: cell.column.getSize(),
|
<ArrowUp
|
||||||
}}
|
className={cn(
|
||||||
>
|
"ml-2 size-4 opacity-0 transition-transform",
|
||||||
{flexRender(
|
sorted && "opacity-100",
|
||||||
cell.column.columnDef.cell,
|
(sorted as string) === "desc" && "rotate-180",
|
||||||
cell.getContext(),
|
)}
|
||||||
)}
|
/>
|
||||||
</TableCell>
|
</Button>
|
||||||
))}
|
<div
|
||||||
|
{...{
|
||||||
|
onDoubleClick: () => header.column.resetSize(),
|
||||||
|
onMouseDown: header.getResizeHandler(),
|
||||||
|
onTouchStart: header.getResizeHandler(),
|
||||||
|
className: `resizer ${header.column.getIsResizing() ? "isResizing" : ""}`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))}
|
||||||
) : (
|
</TableHeader>
|
||||||
<TableRow>
|
<TableBody>
|
||||||
<TableCell
|
{table.getRowModel().rows?.length ? (
|
||||||
colSpan={columns.length}
|
table.getRowModel().rows.map((row) => (
|
||||||
className="h-24 text-center"
|
<TableRow
|
||||||
>
|
key={row.id}
|
||||||
No results.
|
data-state={row.getIsSelected() && "selected"}
|
||||||
</TableCell>
|
>
|
||||||
</TableRow>
|
{row.getVisibleCells().map((cell) => (
|
||||||
)}
|
<TableCell
|
||||||
</TableBody>
|
key={cell.id}
|
||||||
</Table>
|
style={{
|
||||||
</ScrollArea>
|
width: cell.column.getSize(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext(),
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<DataTablePagination table={table} />
|
<DataTablePagination table={table} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ function RawSessionSelector() {
|
|||||||
value={currentSessionId ? currentSessionId.toString() : ""}
|
value={currentSessionId ? currentSessionId.toString() : ""}
|
||||||
onValueChange={handleSessionSelected}
|
onValueChange={handleSessionSelected}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="max-w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder="Select a Database" />
|
<span className={"truncate max-w-[calc(100%-1.5rem)]"}>
|
||||||
|
<SelectValue placeholder="Select a Database" />
|
||||||
|
</span>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>{mappedSessions}</SelectContent>
|
<SelectContent>{mappedSessions}</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
119
frontend/src/components/sidebar/sidebar.tsx
Normal file
119
frontend/src/components/sidebar/sidebar.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import { SessionSelector } from "@/components/sidebar/session-selector";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
buttonVariants,
|
||||||
|
} from "@/components/ui";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { Route } from "@/routes/__root";
|
||||||
|
import { useDatabasesListQuery, useTablesListQuery } from "@/services/db";
|
||||||
|
import { useUiStore } from "@/state";
|
||||||
|
import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
||||||
|
import { Database, Rows3 } from "lucide-react";
|
||||||
|
import type { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
function SidebarContent() {
|
||||||
|
const { data } = useDatabasesListQuery();
|
||||||
|
|
||||||
|
const showSidebar = useUiStore.use.showSidebar();
|
||||||
|
const params = useParams({ strict: false });
|
||||||
|
const dbName = params.dbName ?? "";
|
||||||
|
const { data: tables } = useTablesListQuery({ dbName });
|
||||||
|
const navigate = useNavigate({ from: Route.fullPath });
|
||||||
|
|
||||||
|
const handleSelectedDb = (dbName: string) => {
|
||||||
|
void navigate({ to: "/db/$dbName/tables", params: { dbName } });
|
||||||
|
};
|
||||||
|
if (!showSidebar) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SessionSelector />
|
||||||
|
<Select value={dbName} onValueChange={handleSelectedDb}>
|
||||||
|
<SelectTrigger className="w-full mt-4">
|
||||||
|
<SelectValue placeholder="Select a Database" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{data?.map((db) => {
|
||||||
|
return (
|
||||||
|
<SelectItem value={db} key={db}>
|
||||||
|
{db}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<nav className="flex flex-col gap-1 mt-4">
|
||||||
|
{dbName && (
|
||||||
|
<Link
|
||||||
|
to={"/db/$dbName/tables"}
|
||||||
|
params={{ dbName }}
|
||||||
|
activeOptions={{ exact: true }}
|
||||||
|
title={dbName}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 rounded py-1.5 pl-1.5",
|
||||||
|
"hover:bg-muted",
|
||||||
|
"[&.active]:bg-muted [&.active]:font-semibold",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Database className={"size-4"} />
|
||||||
|
<span className={"max-w-full inline-block truncate"}>{dbName}</span>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
{tables?.map((table) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={table.table_name}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center gap-2 px-2.5 rounded py-1.5 justify-between w-full",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
className={cn(
|
||||||
|
"max-w-full inline-block truncate",
|
||||||
|
"hover:underline",
|
||||||
|
"[&.active]:font-medium",
|
||||||
|
)}
|
||||||
|
title={table.table_name}
|
||||||
|
to={"/db/$dbName/tables/$tableName"}
|
||||||
|
params={{ tableName: table.table_name, dbName: dbName }}
|
||||||
|
>
|
||||||
|
{table.table_name}
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className={cn(
|
||||||
|
"shrink-0",
|
||||||
|
"hover:underline",
|
||||||
|
buttonVariants({ variant: "ghost", size: "iconSm" }),
|
||||||
|
"[&.active]:bg-muted",
|
||||||
|
)}
|
||||||
|
title={"Explore Data"}
|
||||||
|
aria-label={"Explore Data"}
|
||||||
|
to={"/db/$dbName/tables/$tableName/data"}
|
||||||
|
params={{ tableName: table.table_name, dbName: dbName }}
|
||||||
|
search={{ pageIndex: 0, pageSize: 10 }}
|
||||||
|
>
|
||||||
|
<Rows3 className={"size-4 shrink-0"} />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarContainer({ children }: PropsWithChildren) {
|
||||||
|
return <aside className={"p-3"}>{children}</aside>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Sidebar() {
|
||||||
|
return (
|
||||||
|
<SidebarContainer>
|
||||||
|
<SidebarContent />
|
||||||
|
</SidebarContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ const SelectTrigger = forwardRef<
|
|||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
|
|
||||||
const Table = forwardRef<HTMLTableElement, HTMLAttributes<HTMLTableElement>>(
|
const Table = forwardRef<HTMLTableElement, HTMLAttributes<HTMLTableElement>>(
|
||||||
({ className, ...props }, ref) => (
|
({ className, ...props }, ref) => (
|
||||||
<div className="relative w-full overflow-auto">
|
<div className="relative w-full overflow-auto rounded-md">
|
||||||
<table
|
<table
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("w-full caption-bottom text-sm", className)}
|
className={cn("w-full caption-bottom text-sm", className)}
|
||||||
@@ -23,7 +23,11 @@ const TableHeader = forwardRef<
|
|||||||
HTMLTableSectionElement,
|
HTMLTableSectionElement,
|
||||||
HTMLAttributes<HTMLTableSectionElement>
|
HTMLAttributes<HTMLTableSectionElement>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
<thead
|
||||||
|
ref={ref}
|
||||||
|
className={cn("[&_tr]:border-b [&_tr]:bg-background", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
));
|
));
|
||||||
TableHeader.displayName = "TableHeader";
|
TableHeader.displayName = "TableHeader";
|
||||||
|
|
||||||
|
|||||||
@@ -74,11 +74,11 @@
|
|||||||
text-underline-position: under;
|
text-underline-position: under;
|
||||||
--sidebar-width: 264px;
|
--sidebar-width: 264px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-closed {
|
.sidebar-closed {
|
||||||
--sidebar-width: 0;
|
--sidebar-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-rows-layout {
|
.grid-rows-layout {
|
||||||
grid-template-rows: 60px 1fr;
|
grid-template-rows: 60px 1fr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,11 @@
|
|||||||
import { SessionSelector } from "@/components/session-selector";
|
|
||||||
import { SettingsDialog } from "@/components/settings-dialog";
|
import { SettingsDialog } from "@/components/settings-dialog";
|
||||||
import {
|
import { Sidebar } from "@/components/sidebar/sidebar";
|
||||||
Button,
|
import { Button, ModeToggle } from "@/components/ui";
|
||||||
ModeToggle,
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
buttonVariants,
|
|
||||||
} from "@/components/ui";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useDatabasesListQuery, useTablesListQuery } from "@/services/db";
|
|
||||||
import { useUiStore } from "@/state";
|
import { useUiStore } from "@/state";
|
||||||
import {
|
import { Link, Outlet, createRootRoute } from "@tanstack/react-router";
|
||||||
Link,
|
|
||||||
Outlet,
|
|
||||||
createRootRoute,
|
|
||||||
useNavigate,
|
|
||||||
useParams,
|
|
||||||
} from "@tanstack/react-router";
|
|
||||||
import { TanStackRouterDevtools } from "@tanstack/router-devtools";
|
import { TanStackRouterDevtools } from "@tanstack/router-devtools";
|
||||||
import { Database, PanelLeft, PanelLeftClose, Rows3 } from "lucide-react";
|
import { PanelLeft, PanelLeftClose } from "lucide-react";
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
component: Root,
|
component: Root,
|
||||||
@@ -31,16 +15,6 @@ function Root() {
|
|||||||
const showSidebar = useUiStore.use.showSidebar();
|
const showSidebar = useUiStore.use.showSidebar();
|
||||||
const toggleSidebar = useUiStore.use.toggleSidebar();
|
const toggleSidebar = useUiStore.use.toggleSidebar();
|
||||||
|
|
||||||
const { data } = useDatabasesListQuery();
|
|
||||||
const params = useParams({ strict: false });
|
|
||||||
const dbName = params.dbName ?? "";
|
|
||||||
const navigate = useNavigate({ from: Route.fullPath });
|
|
||||||
|
|
||||||
const handleSelectedDb = (dbName: string) => {
|
|
||||||
void navigate({ to: "/db/$dbName/tables", params: { dbName } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data: tables } = useTablesListQuery({ dbName });
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@@ -70,86 +44,7 @@ function Root() {
|
|||||||
<SettingsDialog />
|
<SettingsDialog />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<Sidebar />
|
||||||
<aside className={"p-3"}>
|
|
||||||
{showSidebar && (
|
|
||||||
<>
|
|
||||||
<SessionSelector />
|
|
||||||
<Select value={dbName} onValueChange={handleSelectedDb}>
|
|
||||||
<SelectTrigger className="w-full mt-4">
|
|
||||||
<SelectValue placeholder="Select a Database" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{data?.map((db) => {
|
|
||||||
return (
|
|
||||||
<SelectItem value={db} key={db}>
|
|
||||||
{db}
|
|
||||||
</SelectItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<nav className="flex flex-col gap-1 mt-4">
|
|
||||||
{dbName && (
|
|
||||||
<Link
|
|
||||||
to={"/db/$dbName/tables"}
|
|
||||||
params={{ dbName }}
|
|
||||||
activeOptions={{ exact: true }}
|
|
||||||
title={dbName}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2 rounded py-1.5 pl-1.5",
|
|
||||||
"hover:bg-muted",
|
|
||||||
"[&.active]:bg-muted [&.active]:font-semibold",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Database className={"size-4"} />
|
|
||||||
<span className={"max-w-full inline-block truncate"}>
|
|
||||||
{dbName}
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
{tables?.map((table) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={table.table_name}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2 px-2.5 rounded py-1.5 justify-between w-full",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
className={cn(
|
|
||||||
"max-w-full inline-block truncate",
|
|
||||||
"hover:underline",
|
|
||||||
"[&.active]:font-medium",
|
|
||||||
)}
|
|
||||||
title={table.table_name}
|
|
||||||
to={"/db/$dbName/tables/$tableName"}
|
|
||||||
params={{ tableName: table.table_name, dbName: dbName }}
|
|
||||||
>
|
|
||||||
{table.table_name}
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
className={cn(
|
|
||||||
"shrink-0",
|
|
||||||
"hover:underline",
|
|
||||||
buttonVariants({ variant: "ghost", size: "iconSm" }),
|
|
||||||
"[&.active]:bg-muted",
|
|
||||||
)}
|
|
||||||
title={"Explore Data"}
|
|
||||||
aria-label={"Explore Data"}
|
|
||||||
to={"/db/$dbName/tables/$tableName/data"}
|
|
||||||
params={{ tableName: table.table_name, dbName: dbName }}
|
|
||||||
search={{ pageIndex: 0, pageSize: 10 }}
|
|
||||||
>
|
|
||||||
<Rows3 className={"size-4 shrink-0"} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</nav>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</aside>
|
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
<TanStackRouterDevtools />
|
<TanStackRouterDevtools />
|
||||||
|
|||||||
Reference in New Issue
Block a user