forked from github-starred/komodo
improve topbar navigation
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@ui/select";
|
} from "@ui/select";
|
||||||
import { Switch } from "@ui/switch";
|
import { Switch } from "@ui/switch";
|
||||||
|
import { CommandList } from "cmdk";
|
||||||
import {
|
import {
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
ArrowUp,
|
ArrowUp,
|
||||||
@@ -291,9 +292,8 @@ const ProcedureConfigInner = ({
|
|||||||
header: "Delete",
|
header: "Delete",
|
||||||
cell: ({ row: { index } }) => (
|
cell: ({ row: { index } }) => (
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
title="Delete"
|
title="Delete Row"
|
||||||
icon={<Trash2 className="w-4 h-4" />}
|
icon={<Trash2 className="w-4 h-4" />}
|
||||||
variant="destructive"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setConfig({
|
setConfig({
|
||||||
...config,
|
...config,
|
||||||
@@ -343,6 +343,7 @@ const ExecutionTypeSelector = ({
|
|||||||
value={search}
|
value={search}
|
||||||
onValueChange={setSearch}
|
onValueChange={setSearch}
|
||||||
/>
|
/>
|
||||||
|
<CommandList>
|
||||||
<CommandEmpty className="flex justify-evenly items-center">
|
<CommandEmpty className="flex justify-evenly items-center">
|
||||||
Empty.
|
Empty.
|
||||||
<SearchX className="w-3 h-3" />
|
<SearchX className="w-3 h-3" />
|
||||||
@@ -368,6 +369,7 @@ const ExecutionTypeSelector = ({
|
|||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
</Command>
|
</Command>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -52,7 +52,10 @@ export const ServerComponents: RequiredResourceComponents = {
|
|||||||
},
|
},
|
||||||
Actions: SERVER_ACTIONS,
|
Actions: SERVER_ACTIONS,
|
||||||
Page: {
|
Page: {
|
||||||
Stats: ({ id }) => <ServerStats server_id={id} />,
|
Stats: ({ id }) => {
|
||||||
|
const status = useServer(id)?.info.status;
|
||||||
|
return status === "Ok" && <ServerStats server_id={id} />;
|
||||||
|
},
|
||||||
Deployments: ({ id }) => {
|
Deployments: ({ id }) => {
|
||||||
const deployments = useRead("ListDeployments", {}).data?.filter(
|
const deployments = useRead("ListDeployments", {}).data?.filter(
|
||||||
(deployment) => deployment.info.server_id === id
|
(deployment) => deployment.info.server_id === id
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ export const TagsFilter = () => {
|
|||||||
?.filter((tag) => !tags.includes(tag._id!.$oid))
|
?.filter((tag) => !tags.includes(tag._id!.$oid))
|
||||||
.map((tag) => (
|
.map((tag) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="cursor-pointer"
|
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
|
className="cursor-pointer"
|
||||||
onClick={() => setTags([...tags, tag._id!.$oid])}
|
onClick={() => setTags([...tags, tag._id!.$oid])}
|
||||||
>
|
>
|
||||||
{tag.name}
|
{tag.name}
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
import { useRead, useResourceParamType } from "@lib/hooks";
|
import { useRead, useResourceParamType } from "@lib/hooks";
|
||||||
import { ResourceComponents } from "./resources";
|
import { ResourceComponents } from "./resources";
|
||||||
import { Box, Boxes, FolderTree, Key, Tag, UserCircle2 } from "lucide-react";
|
import {
|
||||||
|
Box,
|
||||||
|
Boxes,
|
||||||
|
FileQuestion,
|
||||||
|
FolderTree,
|
||||||
|
Home,
|
||||||
|
Key,
|
||||||
|
SearchX,
|
||||||
|
Tag,
|
||||||
|
UserCircle2,
|
||||||
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -10,16 +20,28 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@ui/dropdown-menu";
|
} from "@ui/dropdown-menu";
|
||||||
import { Button } from "@ui/button";
|
import { Button } from "@ui/button";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||||
import { RESOURCE_TARGETS } from "@lib/utils";
|
import { RESOURCE_TARGETS } from "@lib/utils";
|
||||||
import { Omnibar } from "./omnibar";
|
import { Omnibar } from "./omnibar";
|
||||||
import { WsStatusIndicator } from "@lib/socket";
|
import { WsStatusIndicator } from "@lib/socket";
|
||||||
import { HeaderUpdates } from "./updates/header";
|
import { HeaderUpdates } from "./updates/header";
|
||||||
import { Logout } from "./util";
|
import { Logout } from "./util";
|
||||||
import { ThemeToggle } from "@ui/theme";
|
import { ThemeToggle } from "@ui/theme";
|
||||||
|
import { UsableResource } from "@types";
|
||||||
|
import { atomWithStorage } from "jotai/utils";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "@ui/popover";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList,
|
||||||
|
} from "@ui/command";
|
||||||
|
|
||||||
export const Topbar = () => {
|
export const Topbar = () => {
|
||||||
const type = useResourceParamType();
|
|
||||||
return (
|
return (
|
||||||
<div className="sticky top-0 border-b bg-background z-50 w-full">
|
<div className="sticky top-0 border-b bg-background z-50 w-full">
|
||||||
<div className="container flex items-center justify-between py-4 gap-8">
|
<div className="container flex items-center justify-between py-4 gap-8">
|
||||||
@@ -28,8 +50,8 @@ export const Topbar = () => {
|
|||||||
MONITOR
|
MONITOR
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<ResourceTypeDropdown />
|
<PrimaryDropdown />
|
||||||
{type && <ResourcesDropdown />}
|
<SecondaryDropdown />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex md:gap-4">
|
<div className="flex md:gap-4">
|
||||||
@@ -47,21 +69,22 @@ export const Topbar = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResourceTypeDropdown = () => {
|
const PrimaryDropdown = () => {
|
||||||
const type = useResourceParamType();
|
const type = useResourceParamType();
|
||||||
const Components = ResourceComponents[type];
|
const Components = type && ResourceComponents[type];
|
||||||
|
|
||||||
const [icon, title] = type
|
const [icon, title] = Components
|
||||||
? [<Components.Icon />, type + "s"]
|
? [<Components.Icon />, type + "s"]
|
||||||
: location.pathname === "/tree"
|
: location.pathname === "/"
|
||||||
? [<FolderTree className="w-4 h-4" />, "Tree"]
|
? [<Home className="w-4 h-4" />, "Home"]
|
||||||
: location.pathname === "/keys"
|
: location.pathname === "/keys"
|
||||||
? [<Key className="w-4 h-4" />, "Api Keys"]
|
? [<Key className="w-4 h-4" />, "Api Keys"]
|
||||||
: location.pathname === "/tags"
|
: location.pathname === "/tags"
|
||||||
? [<Tag className="w-4 h-4" />, "Tags"]
|
? [<Tag className="w-4 h-4" />, "Tags"]
|
||||||
: location.pathname === "/users"
|
: location.pathname === "/users"
|
||||||
? [<UserCircle2 className="w-4 h-4" />, "Users"]
|
? [<UserCircle2 className="w-4 h-4" />, "Users"]
|
||||||
: [<Box className="w-4 h-4" />, "Dashboard"];
|
: [<FileQuestion className="w-4 h-4" />, "Unknown"];
|
||||||
|
// : [<Box className="w-4 h-4" />, "Dashboard"];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -77,23 +100,8 @@ const ResourceTypeDropdown = () => {
|
|||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<DropdownMenuItem className="flex items-center gap-2 cursor-pointer">
|
<DropdownMenuItem className="flex items-center gap-2 cursor-pointer">
|
||||||
<Box className="w-4 h-4" />
|
<Home className="w-4 h-4" />
|
||||||
Dashboard
|
Home
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
|
|
||||||
<Link to="/resources">
|
|
||||||
<DropdownMenuItem className="flex items-center gap-2 cursor-pointer">
|
|
||||||
<Boxes className="w-4 h-4" />
|
|
||||||
Resources
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
<Link to="/tree">
|
|
||||||
<DropdownMenuItem className="flex items-center gap-2 cursor-pointer">
|
|
||||||
<FolderTree className="w-4 h-4" />
|
|
||||||
Tree
|
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
@@ -140,48 +148,118 @@ const ResourceTypeDropdown = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResourcesDropdown = () => {
|
export type HomeView = "Dashboard" | "Tree" | "Resources";
|
||||||
const type = useResourceParamType();
|
|
||||||
const id = useParams().id as string;
|
|
||||||
const list = useRead(`List${type}s`, {}).data;
|
|
||||||
|
|
||||||
const selected = list?.find((i) => i.id === id);
|
export const homeViewAtom = atomWithStorage<HomeView>(
|
||||||
const Components = ResourceComponents[type];
|
"home-view-v1",
|
||||||
|
"Dashboard"
|
||||||
|
);
|
||||||
|
|
||||||
|
const ICONS = {
|
||||||
|
Dashboard: () => <Box className="w-4 h-4" />,
|
||||||
|
Tree: () => <FolderTree className="w-4 h-4" />,
|
||||||
|
Resources: () => <Boxes className="w-4 h-4" />,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SecondaryDropdown = () => {
|
||||||
|
const [view, setView] = useAtom(homeViewAtom);
|
||||||
|
|
||||||
|
const type = useResourceParamType();
|
||||||
|
if (type) return <ResourcesDropdown type={type} />;
|
||||||
|
if (location.pathname !== "/") return;
|
||||||
|
|
||||||
|
const Icon = ICONS[view];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" className="w-48 justify-between px-3">
|
<Button variant="ghost" className="w-48 justify-between px-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Components.Icon id={selected?.id} />
|
<Icon />
|
||||||
{selected ? selected.name : `All ${type}s`}
|
{view}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-48" side="bottom">
|
<DropdownMenuContent className="w-48" side="bottom">
|
||||||
<DropdownMenuGroup>
|
<DropdownMenuGroup>
|
||||||
<Link to={`/${type.toLowerCase()}s`}>
|
{Object.entries(ICONS).map(([view, Icon]) => (
|
||||||
<DropdownMenuItem className="flex items-center gap-2">
|
<DropdownMenuItem
|
||||||
<Components.Icon />
|
key={view}
|
||||||
All {type}s
|
className="flex items-center gap-2"
|
||||||
|
onClick={() => setView(view as HomeView)}
|
||||||
|
>
|
||||||
|
<Icon />
|
||||||
|
{view}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</Link>
|
|
||||||
</DropdownMenuGroup>
|
|
||||||
<DropdownMenuGroup>
|
|
||||||
{!list?.length && (
|
|
||||||
<DropdownMenuItem disabled>No {type}s Found.</DropdownMenuItem>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{list?.map(({ id, name }) => (
|
|
||||||
<Link key={id} to={`/${type.toLowerCase()}s/${id}`}>
|
|
||||||
<DropdownMenuItem className="flex items-center gap-2">
|
|
||||||
<Components.Icon id={id} />
|
|
||||||
{name}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</Link>
|
|
||||||
))}
|
))}
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ResourcesDropdown = ({ type }: { type: UsableResource }) => {
|
||||||
|
const nav = useNavigate();
|
||||||
|
const id = useParams().id as string;
|
||||||
|
const list = useRead(`List${type}s`, {}).data;
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [input, setInput] = useState("");
|
||||||
|
|
||||||
|
const selected = list?.find((i) => i.id === id);
|
||||||
|
const Components = ResourceComponents[type];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={setOpen}>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button variant="ghost" className="justify-between w-[300px] px-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Components.Icon id={selected?.id} />
|
||||||
|
{selected ? selected.name : `All ${type}s`}
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[300px] max-h-[400px] p-0" sideOffset={12}>
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder={`Search ${type}s`}
|
||||||
|
className="h-9"
|
||||||
|
value={input}
|
||||||
|
onValueChange={setInput}
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty className="flex justify-evenly items-center">
|
||||||
|
{`No ${type}s Found`}
|
||||||
|
<SearchX className="w-3 h-3" />
|
||||||
|
</CommandEmpty>
|
||||||
|
|
||||||
|
<CommandGroup>
|
||||||
|
<CommandItem
|
||||||
|
onSelect={() => {
|
||||||
|
setOpen(false);
|
||||||
|
nav(`/${type.toLowerCase()}s`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="link" className="flex gap-2 items-center p-0">
|
||||||
|
<Components.Icon />
|
||||||
|
All {type}s
|
||||||
|
</Button>
|
||||||
|
</CommandItem>
|
||||||
|
{list?.map((resource) => (
|
||||||
|
<CommandItem
|
||||||
|
key={resource.id}
|
||||||
|
onSelect={() => {
|
||||||
|
setOpen(false);
|
||||||
|
nav(`/${type.toLowerCase()}s/${resource.id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Components.Link id={resource.id} />
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -143,9 +143,12 @@ export const useAuth = <
|
|||||||
|
|
||||||
// ============== UTILITY ==============
|
// ============== UTILITY ==============
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually returns UsableResoure | undefined
|
||||||
|
*/
|
||||||
export const useResourceParamType = () => {
|
export const useResourceParamType = () => {
|
||||||
const type = useParams().type;
|
const type = useParams().type;
|
||||||
if (!type) return undefined as unknown as UsableResource;
|
if (!type) return undefined;
|
||||||
return (type[0].toUpperCase() + type.slice(1, -1)) as UsableResource;
|
return (type[0].toUpperCase() + type.slice(1, -1)) as UsableResource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ export const object_keys = <T extends object>(o: T): (keyof T)[] =>
|
|||||||
Object.keys(o) as (keyof T)[];
|
Object.keys(o) as (keyof T)[];
|
||||||
|
|
||||||
export const RESOURCE_TARGETS: UsableResource[] = [
|
export const RESOURCE_TARGETS: UsableResource[] = [
|
||||||
"Procedure",
|
|
||||||
"Deployment",
|
"Deployment",
|
||||||
"Server",
|
"Server",
|
||||||
"Build",
|
"Build",
|
||||||
|
"Procedure",
|
||||||
"Repo",
|
"Repo",
|
||||||
"Builder",
|
"Builder",
|
||||||
"Alerter",
|
"Alerter",
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export const AllResources = () => {
|
|||||||
if (!count) return;
|
if (!count) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section title={type + "s"} actions={<Components.New />}>
|
<Section key={type} title={type + "s"} actions={<Components.New />}>
|
||||||
<Components.Table />
|
<Components.Table />
|
||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
17
frontend/src/pages/home/index.tsx
Normal file
17
frontend/src/pages/home/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { homeViewAtom } from "@components/topbar";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { Dashboard } from "./dashboard";
|
||||||
|
import { AllResources } from "./all_resources";
|
||||||
|
import { Tree } from "./tree";
|
||||||
|
|
||||||
|
export const Home = () => {
|
||||||
|
const [view, _] = useAtom(homeViewAtom);
|
||||||
|
switch (view) {
|
||||||
|
case "Dashboard":
|
||||||
|
return <Dashboard />;
|
||||||
|
case "Resources":
|
||||||
|
return <AllResources />;
|
||||||
|
case "Tree":
|
||||||
|
return <Tree />;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@ import { usePushRecentlyViewed, useResourceParamType } from "@lib/hooks";
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
export const Resource = () => {
|
export const Resource = () => {
|
||||||
const type = useResourceParamType();
|
const type = useResourceParamType()!;
|
||||||
const id = useParams().id as string;
|
const id = useParams().id as string;
|
||||||
usePushRecentlyViewed({ type, id });
|
usePushRecentlyViewed({ type, id });
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ export const Resource = () => {
|
|||||||
<Components.Icon id={id} />
|
<Components.Icon id={id} />
|
||||||
<Components.Status id={id} />
|
<Components.Status id={id} />
|
||||||
</div>
|
</div>
|
||||||
{Components.Info.map((Info) => (
|
{Components.Info.map((Info, i) => (
|
||||||
<>
|
<>
|
||||||
| <Info id={id} />
|
| <Info key={i} id={id} />
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -40,8 +40,8 @@ export const Resource = () => {
|
|||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
<div className="flex gap-4 items-center">
|
<div className="flex gap-4 items-center">
|
||||||
{Components.Actions.map((Action) => (
|
{Components.Actions.map((Action, i) => (
|
||||||
<Action id={id} />
|
<Action key={i} id={id} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ export const Resource = () => {
|
|||||||
<ResourceUpdates type={type} id={id} />
|
<ResourceUpdates type={type} id={id} />
|
||||||
{/* <ResourcePermissions type={type} id={id} /> */}
|
{/* <ResourcePermissions type={type} id={id} /> */}
|
||||||
{Object.entries(Components.Page).map(([section, Component]) => (
|
{Object.entries(Components.Page).map(([section, Component]) => (
|
||||||
<Component id={id} key={section} />
|
<Component key={section} id={id} />
|
||||||
))}
|
))}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { Layout } from "@components/layouts";
|
import { Layout } from "@components/layouts";
|
||||||
import { useUser } from "@lib/hooks";
|
import { useUser } from "@lib/hooks";
|
||||||
import { Dashboard } from "@pages/dashboard";
|
|
||||||
import { Login } from "@pages/login";
|
import { Login } from "@pages/login";
|
||||||
import { Resource } from "@pages/resource";
|
import { Resource } from "@pages/resource";
|
||||||
import { Resources } from "@pages/resources";
|
import { Resources } from "@pages/resources";
|
||||||
import { Keys } from "@pages/keys";
|
import { Keys } from "@pages/keys";
|
||||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||||
import { Tree } from "@pages/tree";
|
import { Tree } from "@pages/home/tree";
|
||||||
import { Tags } from "@pages/tags";
|
import { Tags } from "@pages/tags";
|
||||||
import { ResourceUpdates } from "@pages/resource_update";
|
import { ResourceUpdates } from "@pages/resource_update";
|
||||||
import { UserPage, UsersPage } from "@pages/users";
|
import { UserPage, UsersPage } from "@pages/users";
|
||||||
import { AllResources } from "@pages/all_resources";
|
import { AllResources } from "@pages/home/all_resources";
|
||||||
import { UserDisabled } from "@pages/user_disabled";
|
import { UserDisabled } from "@pages/user_disabled";
|
||||||
|
import { Home } from "@pages/home";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
element: <Layout />,
|
element: <Layout />,
|
||||||
children: [
|
children: [
|
||||||
{ path: "", element: <Dashboard /> },
|
{ path: "", element: <Home /> },
|
||||||
{ path: "keys", element: <Keys /> },
|
{ path: "keys", element: <Keys /> },
|
||||||
{ path: "tags", element: <Tags /> },
|
{ path: "tags", element: <Tags /> },
|
||||||
{ path: "tree", element: <Tree /> },
|
{ path: "tree", element: <Tree /> },
|
||||||
|
|||||||
Reference in New Issue
Block a user