This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks-controller.git
The following commit(s) were added to refs/heads/unstable by this push:
new 5fc828c Support namespace CRUD (#149)
5fc828c is described below
commit 5fc828c1da4720dbb40f65f3df424101d7c673af
Author: Venjy <[email protected]>
AuthorDate: Mon Mar 4 21:40:40 2024 +0800
Support namespace CRUD (#149)
* Add banner and namespace list
* Support namespace creation and deletion
* Add error message when fail to create namespace
---------
Co-authored-by: datavisorwenjiejiang <[email protected]>
---
webui/package.json | 4 +
webui/public/logo.svg | 240 +++++++++++++++++++++++
webui/src/app/{ => cluster}/layout.tsx | 27 +--
webui/src/app/{layout.tsx => cluster/page.tsx} | 33 ++--
webui/src/app/globals.css | 27 ---
webui/src/app/layout.tsx | 9 +-
webui/src/app/{layout.tsx => lib/actions.ts} | 36 ++--
webui/src/app/lib/api.ts | 82 ++++++++
webui/src/app/page.tsx | 14 +-
webui/src/app/{page.tsx => ui/banner.tsx} | 38 ++--
webui/src/app/ui/namespace/namespaceCreation.tsx | 89 +++++++++
webui/src/app/ui/namespace/namespaceItem.tsx | 81 ++++++++
webui/src/app/{layout.tsx => ui/nav-links.tsx} | 42 ++--
webui/src/app/ui/sidebar.tsx | 44 +++++
14 files changed, 638 insertions(+), 128 deletions(-)
diff --git a/webui/package.json b/webui/package.json
index f7f2418..4ae3d9b 100644
--- a/webui/package.json
+++ b/webui/package.json
@@ -11,7 +11,11 @@
"dependencies": {
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
+ "@mui/icons-material": "^5.15.7",
"@mui/material": "^5.15.5",
+ "@types/js-yaml": "^4.0.9",
+ "axios": "^1.6.7",
+ "js-yaml": "^4.1.0",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18"
diff --git a/webui/public/logo.svg b/webui/public/logo.svg
new file mode 100644
index 0000000..cfba0d1
--- /dev/null
+++ b/webui/public/logo.svg
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="203px"
height="202px" viewBox="0 0 203 202" enable-background="new 0 0 203 202"
xml:space="preserve"> <image id="image0" width="203" height="202" x="0" y="0"
+
href="data:image/png;base64,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
+LgAAAABJRU5ErkJggg==" />
+</svg>
diff --git a/webui/src/app/layout.tsx b/webui/src/app/cluster/layout.tsx
similarity index 63%
copy from webui/src/app/layout.tsx
copy to webui/src/app/cluster/layout.tsx
index ec5e29d..0c4c376 100644
--- a/webui/src/app/layout.tsx
+++ b/webui/src/app/cluster/layout.tsx
@@ -17,25 +17,10 @@
* under the License.
*/
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
-
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Kvrocks Controller",
- description: "Kvrocks Controller",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
+export default function Layout({children}: {children: React.ReactNode}) {
return (
- <html lang="en">
- <body className={inter.className}>{children}</body>
- </html>
- );
-}
+ <>
+ {children}
+ </>
+ )
+}
\ No newline at end of file
diff --git a/webui/src/app/layout.tsx b/webui/src/app/cluster/page.tsx
similarity index 63%
copy from webui/src/app/layout.tsx
copy to webui/src/app/cluster/page.tsx
index ec5e29d..62a9847 100644
--- a/webui/src/app/layout.tsx
+++ b/webui/src/app/cluster/page.tsx
@@ -17,25 +17,18 @@
* under the License.
*/
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
+import { Box, Container } from "@mui/material";
+import Sidebar from "../ui/sidebar";
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Kvrocks Controller",
- description: "Kvrocks Controller",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
+export default function Cluster() {
return (
- <html lang="en">
- <body className={inter.className}>{children}</body>
- </html>
- );
-}
+ <div className="flex h-full">
+ <Sidebar />
+ <Container maxWidth={false} disableGutters sx={{height: '100%',
overflowY: 'auto', marginLeft: '16px'}}>
+ <div>
+ todo: show all clusters in selected namespace here
+ </div>
+ </Container>
+ </div>
+ )
+}
\ No newline at end of file
diff --git a/webui/src/app/globals.css b/webui/src/app/globals.css
index bd5c251..7c7084d 100644
--- a/webui/src/app/globals.css
+++ b/webui/src/app/globals.css
@@ -20,30 +20,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
-
-:root {
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
- }
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(to bottom,
- transparent,
- rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb));
-}
-
-@layer utilities {
- .text-balance {
- text-wrap: balance;
- }
-}
\ No newline at end of file
diff --git a/webui/src/app/layout.tsx b/webui/src/app/layout.tsx
index ec5e29d..a1e1a0a 100644
--- a/webui/src/app/layout.tsx
+++ b/webui/src/app/layout.tsx
@@ -20,6 +20,8 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
+import Banner from "./ui/banner";
+import { Container } from "@mui/material";
const inter = Inter({ subsets: ["latin"] });
@@ -35,7 +37,12 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
- <body className={inter.className}>{children}</body>
+ <body className={inter.className}>
+ <Banner />
+ <Container sx={{marginTop: '64px', height: 'calc(100vh -
64px)'}} maxWidth={false} disableGutters>
+ {children}
+ </Container>
+ </body>
</html>
);
}
diff --git a/webui/src/app/layout.tsx b/webui/src/app/lib/actions.ts
similarity index 60%
copy from webui/src/app/layout.tsx
copy to webui/src/app/lib/actions.ts
index ec5e29d..55ff8d0 100644
--- a/webui/src/app/layout.tsx
+++ b/webui/src/app/lib/actions.ts
@@ -16,26 +16,22 @@
* specific language governing permissions and limitations
* under the License.
*/
+'use server';
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
+import { redirect } from "next/navigation";
+import { createNamespace, deleteNamespace } from "./api";
+import { revalidatePath } from "next/cache";
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Kvrocks Controller",
- description: "Kvrocks Controller",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
- <html lang="en">
- <body className={inter.className}>{children}</body>
- </html>
- );
+export async function createNamespaceAction(name: string): Promise<string> {
+ const errMsg = await createNamespace(name);
+ if(!errMsg) {
+ revalidatePath('/cluster');
+ }
+ return errMsg;
}
+
+export async function deleteNamespaceAction(name: string): Promise<string> {
+ const result = deleteNamespace(name);
+ revalidatePath('/cluster');
+ return result;
+}
\ No newline at end of file
diff --git a/webui/src/app/lib/api.ts b/webui/src/app/lib/api.ts
new file mode 100644
index 0000000..723d0bf
--- /dev/null
+++ b/webui/src/app/lib/api.ts
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import yaml from 'js-yaml';
+import fs from 'fs';
+import path from 'path';
+import axios, { AxiosError } from 'axios';
+
+const configFile = './config/config.yaml';
+const apiPrefix = '/api/v1';
+let host;
+try {
+ const wholeFilePath = path.join(process.cwd(), '..', configFile);
+ const doc = yaml.load(fs.readFileSync(wholeFilePath, 'utf8'));
+ host = (doc as any)['addr'];
+} catch (error) {
+ host = '127.0.0.1:9379';
+}
+const apiHost = `http://${host}${apiPrefix}`;
+
+export async function fetchNamespaces(): Promise<string[]> {
+ try {
+ const { data: responseData } = await
axios.get(`${apiHost}/namespaces`);
+ return responseData.data.namespaces || [];
+ } catch (error) {
+ handleError(error);
+ return [];
+ }
+}
+export async function createNamespace(name: string): Promise<string> {
+ try {
+ const { data: responseData } = await
axios.post(`${apiHost}/namespaces`, {namespace: name});
+ if(responseData?.data == 'created') {
+ return '';
+ } else {
+ return handleError(responseData);
+ }
+ } catch (error) {
+ return handleError(error);
+ }
+}
+
+export async function deleteNamespace(name: string): Promise<string> {
+ try {
+ const { data: responseData } = await
axios.delete(`${apiHost}/namespaces/${name}`);
+ if(responseData?.data == 'ok') {
+ return '';
+ } else {
+ return handleError(responseData);
+ }
+ } catch (error) {
+ return handleError(error);
+ }
+}
+
+function handleError(error: any): string {
+ let message: string = '';
+ if(error instanceof AxiosError) {
+ message = error.response?.data?.error?.message || error.message;
+ } else if (error instanceof Error) {
+ message = error.message;
+ } else if (typeof error === 'object') {
+ message = error?.error?.message || error?.message;
+ }
+ return message || 'Unknown error';
+}
\ No newline at end of file
diff --git a/webui/src/app/page.tsx b/webui/src/app/page.tsx
index 23a1068..78456a6 100644
--- a/webui/src/app/page.tsx
+++ b/webui/src/app/page.tsx
@@ -17,17 +17,19 @@
* under the License.
*/
-import { Button } from "@mui/material";
-import Image from "next/image";
+import { Button, Container, Typography } from "@mui/material";
export default function Home() {
return (
- <main className="flex flex-col items-center justify-center
min-h-screen space-y-4">
- <h1 className="text-4xl font-bold">Kvrocks Controler UI</h1>
- <p className="text-xl">Work in progress...</p>
+ <div
+ style={{minHeight: 'calc(100vh - 64px)', height: 'calc(100vh -
64px)'}}
+ className={'flex flex-col items-center justify-center space-y-2
h-full'}
+ >
+ <Typography variant="h3">Kvrocks Controler UI</Typography>
+ <Typography variant="body1">Work in progress...</Typography>
<Button size="large" variant="outlined" sx={{ textTransform:
'none' }} href="https://github.com/apache/kvrocks-controller/issues/135">
Click here to submit your suggestions
</Button>
- </main>
+ </div>
);
}
diff --git a/webui/src/app/page.tsx b/webui/src/app/ui/banner.tsx
similarity index 56%
copy from webui/src/app/page.tsx
copy to webui/src/app/ui/banner.tsx
index 23a1068..8839817 100644
--- a/webui/src/app/page.tsx
+++ b/webui/src/app/ui/banner.tsx
@@ -17,17 +17,31 @@
* under the License.
*/
-import { Button } from "@mui/material";
+import { AppBar, Container, Toolbar } from "@mui/material";
import Image from "next/image";
+import NavLinks from "./nav-links";
-export default function Home() {
- return (
- <main className="flex flex-col items-center justify-center
min-h-screen space-y-4">
- <h1 className="text-4xl font-bold">Kvrocks Controler UI</h1>
- <p className="text-xl">Work in progress...</p>
- <Button size="large" variant="outlined" sx={{ textTransform:
'none' }} href="https://github.com/apache/kvrocks-controller/issues/135">
- Click here to submit your suggestions
- </Button>
- </main>
- );
-}
+const links = [
+ {
+ url: '/',
+ title: 'Home'
+ },{
+ url: '/cluster',
+ title: 'cluster'
+ },{
+ url: 'https://kvrocks.apache.org',
+ title: 'community',
+ _blank: true
+ },
+]
+
+export default function Banner() {
+ return (<AppBar>
+ <Container maxWidth={false}>
+ <Toolbar className="space-x-4">
+ <Image src="/logo.svg" width={40} height={40}
alt='logo'></Image>
+ <NavLinks links={links}/>
+ </Toolbar>
+ </Container>
+ </AppBar>)
+}
\ No newline at end of file
diff --git a/webui/src/app/ui/namespace/namespaceCreation.tsx
b/webui/src/app/ui/namespace/namespaceCreation.tsx
new file mode 100644
index 0000000..a0bcf23
--- /dev/null
+++ b/webui/src/app/ui/namespace/namespaceCreation.tsx
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use client';
+import { createNamespaceAction } from "@/app/lib/actions";
+import { Alert, Button, Dialog, DialogActions, DialogContent,
DialogContentText, DialogTitle, Snackbar, TextField } from "@mui/material";
+import { useCallback, useState } from "react";
+
+export default function NamespaceCreation() {
+ const [showDialog, setShowDialog] = useState(false);
+ const openDialog = useCallback(() => setShowDialog(true), []);
+ const closeDialog = useCallback(() => setShowDialog(false), []);
+ const [errorMessage, setErrorMessage] = useState('');
+
+ return (
+ <>
+ <Button variant="outlined" onClick={openDialog}>Create
Namespace</Button>
+ <Dialog
+ open={showDialog}
+ PaperProps={{
+ component: 'form',
+ action: async (formData: FormData) => {
+ const formObj = Object.fromEntries(formData.entries());
+ if(typeof formObj['name'] === 'string') {
+ const errMsg = await
createNamespaceAction(formObj['name']);
+ if (errMsg) {
+ setErrorMessage(errMsg);
+ }
+ closeDialog();
+ }
+ },
+ }}
+ onClose={closeDialog}
+ >
+ <DialogTitle>Create Namespace</DialogTitle>
+ <DialogContent
+ sx={{
+ width: '500px'
+ }}
+ >
+ <TextField
+ autoFocus
+ required
+ name="name"
+ label="Input Name"
+ type="name"
+ fullWidth
+ variant="standard"
+ />
+ </DialogContent>
+ <DialogActions>
+ <Button onClick={closeDialog}>Cancel</Button>
+ <Button type="submit">Create</Button>
+ </DialogActions>
+ </Dialog>
+ <Snackbar
+ open={!!errorMessage}
+ autoHideDuration={5000}
+ onClose={() => setErrorMessage('')}
+ anchorOrigin={{vertical: 'bottom', horizontal: 'right'}}
+ >
+ <Alert
+ onClose={() => setErrorMessage('')}
+ severity="error"
+ variant="filled"
+ sx={{ width: '100%' }}
+ >
+ {errorMessage}
+ </Alert>
+ </Snackbar>
+ </>
+ )
+}
diff --git a/webui/src/app/ui/namespace/namespaceItem.tsx
b/webui/src/app/ui/namespace/namespaceItem.tsx
new file mode 100644
index 0000000..21145e0
--- /dev/null
+++ b/webui/src/app/ui/namespace/namespaceItem.tsx
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+'use client';
+import { Button, Dialog, DialogActions, DialogContent, DialogContentText,
DialogTitle, IconButton, ListItem, ListItemButton, ListItemText, Menu,
MenuItem, Tooltip } from "@mui/material";
+import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
+import { useCallback, useRef, useState } from "react";
+import { deleteNamespaceAction } from "@/app/lib/actions";
+
+export default function NamespaceItem({ item }: {item: string}) {
+ const [hover, setHover] = useState<boolean>(false);
+ const [showMenu, setShowMenu] = useState<boolean>(false);
+ const listItemTextRef = useRef(null);
+ const openMenu = useCallback(() => setShowMenu(true), []);
+ const closeMenu = useCallback(() => (setShowMenu(false), setHover(false)),
[]);
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState<boolean>(false);
+ const openDeleteConfirmDialog = useCallback(() =>
(setShowDeleteConfirm(true), closeMenu()), [closeMenu]);
+ const closeDeleteConfirmDialog = useCallback(() =>
setShowDeleteConfirm(false), []);
+ const confirmDelete = useCallback(async () => {
+ await deleteNamespaceAction(item);
+ closeMenu();
+ },[item, closeMenu])
+ return (<ListItem
+ disablePadding
+ secondaryAction={
+ hover && <IconButton onClick={openMenu} ref={listItemTextRef} >
+ <MoreHorizIcon />
+ </IconButton>
+ }
+ onMouseEnter={() => setHover(true)}
+ onMouseLeave={() => !showMenu && setHover(false)}
+ >
+ <ListItemButton sx={{paddingRight: '10px'}}>
+ <Tooltip title={item} arrow>
+ <ListItemText classes={{primary: 'overflow-hidden
text-ellipsis text-nowrap'}} primary={`${item}`} />
+ </Tooltip>
+ </ListItemButton>
+ <Menu
+ id={item}
+ open={showMenu}
+ onClose={closeMenu}
+ anchorEl={listItemTextRef.current}
+ anchorOrigin={{
+ vertical: 'center',
+ horizontal: 'center',
+ }}
+ >
+ <MenuItem color="red"
onClick={openDeleteConfirmDialog}>Delete</MenuItem>
+ </Menu>
+ <Dialog
+ open={showDeleteConfirm}
+ >
+ <DialogTitle>Confirm</DialogTitle>
+ <DialogContent>
+ <DialogContentText>
+ Please confirm you want to delete namespace {item}
+ </DialogContentText>
+ </DialogContent>
+ <DialogActions>
+ <Button onClick={closeDeleteConfirmDialog}>Cancel</Button>
+ <Button onClick={confirmDelete} color="error">Delete</Button>
+ </DialogActions>
+ </Dialog>
+ </ListItem>)
+}
diff --git a/webui/src/app/layout.tsx b/webui/src/app/ui/nav-links.tsx
similarity index 62%
copy from webui/src/app/layout.tsx
copy to webui/src/app/ui/nav-links.tsx
index ec5e29d..66312d6 100644
--- a/webui/src/app/layout.tsx
+++ b/webui/src/app/ui/nav-links.tsx
@@ -17,25 +17,25 @@
* under the License.
*/
-import type { Metadata } from "next";
-import { Inter } from "next/font/google";
-import "./globals.css";
+import { Button } from "@mui/material"
+import Link from "next/link"
-const inter = Inter({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Kvrocks Controller",
- description: "Kvrocks Controller",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
- <html lang="en">
- <body className={inter.className}>{children}</body>
- </html>
- );
-}
+export default function NavLinks({links}: {
+ links: Array<{
+ url: string,
+ title: string,
+ _blank?: boolean,
+ }>
+}) {
+ return <>
+ {links.map(link => <Link
+ key={link.url}
+ href={link.url}
+ {...(link._blank ? {target: '_blank'} : {})}
+ >
+ <Button color="inherit">
+ {link.title}
+ </Button>
+ </Link>)}
+ </>
+}
\ No newline at end of file
diff --git a/webui/src/app/ui/sidebar.tsx b/webui/src/app/ui/sidebar.tsx
new file mode 100644
index 0000000..73c976a
--- /dev/null
+++ b/webui/src/app/ui/sidebar.tsx
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Divider, List } from "@mui/material";
+import { fetchNamespaces } from "@/app/lib/api";
+import NamespaceItem from "./namespace/namespaceItem";
+import NamespaceCreation from "./namespace/namespaceCreation";
+
+export default async function Sidebar() {
+ const namespaces = await fetchNamespaces();
+ return (
+ <div className="w-60 h-full flex">
+ <List className="w-full overflow-y-auto">
+ <div className="mt-2 mb-4 text-center">
+ <NamespaceCreation />
+ </div>
+ {namespaces.map((namespace, index) => (<>
+ {index === 0 && (
+ <Divider variant="middle"/>
+ )}
+ <NamespaceItem key={namespace} item={namespace} />
+ <Divider variant="middle"/>
+ </>))}
+ </List>
+ <Divider orientation="vertical" flexItem/>
+ </div>
+ )
+}
\ No newline at end of file