This is an automated email from the ASF dual-hosted git repository.

hulk 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 6bac9a4  feat(webui/shard): redesign the shard page (#325)
6bac9a4 is described below

commit 6bac9a4202a1aa3fd6a1f55e47352441242fa06c
Author: Agnik Misra <[email protected]>
AuthorDate: Mon Jul 14 12:20:17 2025 +0530

    feat(webui/shard): redesign the shard page (#325)
---
 .../[namespace]/clusters/[cluster]/page.tsx        | 1239 ++++++++++++++++++--
 webui/src/app/ui/formCreation.tsx                  |   12 +-
 webui/src/app/ui/sidebar.tsx                       |  106 +-
 3 files changed, 1237 insertions(+), 120 deletions(-)

diff --git a/webui/src/app/namespaces/[namespace]/clusters/[cluster]/page.tsx 
b/webui/src/app/namespaces/[namespace]/clusters/[cluster]/page.tsx
index a970e64..0517517 100644
--- a/webui/src/app/namespaces/[namespace]/clusters/[cluster]/page.tsx
+++ b/webui/src/app/namespaces/[namespace]/clusters/[cluster]/page.tsx
@@ -19,22 +19,90 @@
 
 "use client";
 
-import { Box, Container, Typography, Chip, Badge } from "@mui/material";
+import {
+    Box,
+    Typography,
+    Chip,
+    Paper,
+    Grid,
+    Button,
+    IconButton,
+    Tooltip,
+    Popover,
+    RadioGroup,
+    FormControlLabel,
+    Radio,
+    Fade,
+    Badge,
+} from "@mui/material";
 import { ClusterSidebar } from "../../../../ui/sidebar";
 import { useState, useEffect } from "react";
-import { listShards } from "@/app/lib/api";
+import { listShards, listNodes, fetchCluster } from "@/app/lib/api";
 import { AddShardCard, ResourceCard } from "@/app/ui/createCard";
 import Link from "next/link";
 import { useRouter } from "next/navigation";
 import { LoadingSpinner } from "@/app/ui/loadingSpinner";
 import DnsIcon from "@mui/icons-material/Dns";
 import StorageIcon from "@mui/icons-material/Storage";
+import DeviceHubIcon from "@mui/icons-material/DeviceHub";
 import EmptyState from "@/app/ui/emptyState";
+import SearchIcon from "@mui/icons-material/Search";
+import FilterListIcon from "@mui/icons-material/FilterList";
+import SortIcon from "@mui/icons-material/Sort";
+import CheckIcon from "@mui/icons-material/Check";
+import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
+import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
+import ChevronRightIcon from "@mui/icons-material/ChevronRight";
+import AddIcon from "@mui/icons-material/Add";
+import WarningIcon from "@mui/icons-material/Warning";
+import InfoIcon from "@mui/icons-material/Info";
+import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
+import { ShardCreation, MigrateSlot } from "@/app/ui/formCreation";
+
+interface ResourceCounts {
+    shards: number;
+    nodes: number;
+    withSlots: number;
+    migrating: number;
+}
+
+interface ShardData {
+    index: number;
+    nodes: any[];
+    slotRanges: string[];
+    migratingSlot: number;
+    importingSlot: number;
+    targetShardIndex: number;
+    nodeCount: number;
+    hasSlots: boolean;
+    hasMigration: boolean;
+    hasImporting: boolean;
+}
+
+type FilterOption =
+    | "all"
+    | "with-migration"
+    | "no-migration"
+    | "with-slots"
+    | "no-slots"
+    | "with-importing";
+type SortOption = "index-asc" | "index-desc" | "nodes-desc" | "nodes-asc";
 
 export default function Cluster({ params }: { params: { namespace: string; 
cluster: string } }) {
     const { namespace, cluster } = params;
-    const [shardsData, setShardsData] = useState<any[]>([]);
+    const [shardsData, setShardsData] = useState<ShardData[]>([]);
+    const [resourceCounts, setResourceCounts] = useState<ResourceCounts>({
+        shards: 0,
+        nodes: 0,
+        withSlots: 0,
+        migrating: 0,
+    });
     const [loading, setLoading] = useState<boolean>(true);
+    const [searchTerm, setSearchTerm] = useState<string>("");
+    const [filterAnchorEl, setFilterAnchorEl] = useState<null | 
HTMLElement>(null);
+    const [sortAnchorEl, setSortAnchorEl] = useState<null | HTMLElement>(null);
+    const [filterOption, setFilterOption] = useState<FilterOption>("all");
+    const [sortOption, setSortOption] = useState<SortOption>("index-asc");
     const router = useRouter();
 
     useEffect(() => {
@@ -48,7 +116,55 @@ export default function Cluster({ params }: { params: { 
namespace: string; clust
                     return;
                 }
 
-                setShardsData(fetchedShards);
+                let totalNodes = 0;
+                let withSlots = 0;
+                let migrating = 0;
+
+                const processedShards = await Promise.all(
+                    fetchedShards.map(async (shard: any, index: number) => {
+                        const nodeCount = shard.nodes?.length || 0;
+                        totalNodes += nodeCount;
+
+                        const hasSlots = shard.slot_ranges && 
shard.slot_ranges.length > 0;
+                        if (hasSlots) withSlots++;
+
+                        // Ensure we're using the correct field names from the 
API
+                        // Handle null values properly - null means no 
migration/import
+                        // Also handle missing fields (import_slot might not 
be present in all responses)
+                        const migratingSlot =
+                            shard.migrating_slot !== null && 
shard.migrating_slot !== undefined
+                                ? shard.migrating_slot
+                                : -1;
+                        const importingSlot =
+                            shard.import_slot !== null && shard.import_slot 
!== undefined
+                                ? shard.import_slot
+                                : -1;
+
+                        const hasMigration = migratingSlot >= 0;
+                        if (hasMigration) migrating++;
+
+                        return {
+                            index,
+                            nodes: shard.nodes || [],
+                            slotRanges: shard.slot_ranges || [],
+                            migratingSlot,
+                            importingSlot,
+                            targetShardIndex: shard.target_shard_index || -1,
+                            nodeCount,
+                            hasSlots,
+                            hasMigration,
+                            hasImporting: importingSlot >= 0,
+                        };
+                    })
+                );
+
+                setShardsData(processedShards);
+                setResourceCounts({
+                    shards: processedShards.length,
+                    nodes: totalNodes,
+                    withSlots,
+                    migrating,
+                });
             } catch (error) {
                 console.error("Error fetching shards:", error);
             } finally {
@@ -58,6 +174,63 @@ export default function Cluster({ params }: { params: { 
namespace: string; clust
         fetchData();
     }, [namespace, cluster, router]);
 
+    const handleFilterClick = (event: React.MouseEvent<HTMLElement>) => {
+        setFilterAnchorEl(event.currentTarget);
+    };
+
+    const handleSortClick = (event: React.MouseEvent<HTMLElement>) => {
+        setSortAnchorEl(event.currentTarget);
+    };
+
+    const handleFilterClose = () => {
+        setFilterAnchorEl(null);
+    };
+
+    const handleSortClose = () => {
+        setSortAnchorEl(null);
+    };
+
+    const filteredAndSortedShards = shardsData
+        .filter((shard) => {
+            if (!`shard ${shard.index + 
1}`.toLowerCase().includes(searchTerm.toLowerCase())) {
+                return false;
+            }
+
+            switch (filterOption) {
+                case "with-migration":
+                    return shard.hasMigration;
+                case "no-migration":
+                    return !shard.hasMigration;
+                case "with-slots":
+                    return shard.hasSlots;
+                case "no-slots":
+                    return !shard.hasSlots;
+                case "with-importing":
+                    return shard.hasImporting;
+                default:
+                    return true;
+            }
+        })
+        .sort((a, b) => {
+            switch (sortOption) {
+                case "index-asc":
+                    return a.index - b.index;
+                case "index-desc":
+                    return b.index - a.index;
+                case "nodes-desc":
+                    return b.nodeCount - a.nodeCount;
+                case "nodes-asc":
+                    return a.nodeCount - b.nodeCount;
+                default:
+                    return 0;
+            }
+        });
+
+    const isFilterOpen = Boolean(filterAnchorEl);
+    const isSortOpen = Boolean(sortAnchorEl);
+    const filterId = isFilterOpen ? "filter-popover" : undefined;
+    const sortId = isSortOpen ? "sort-popover" : undefined;
+
     if (loading) {
         return <LoadingSpinner />;
     }
@@ -70,104 +243,1002 @@ export default function Cluster({ params }: { params: { 
namespace: string; clust
 
     return (
         <div className="flex h-full">
-            <ClusterSidebar namespace={namespace} />
-            <div className="flex-1 overflow-auto">
-                <Box className="container-inner">
-                    <Box className="mb-6 flex items-center justify-between">
+            <div className="relative h-full">
+                <ClusterSidebar namespace={namespace} />
+            </div>
+            <div className="no-scrollbar flex-1 overflow-y-auto bg-white pb-8 
dark:bg-dark">
+                <Box className="px-6 py-4 sm:px-8 sm:py-6">
+                    <div className="mb-4 flex flex-col gap-3 sm:mb-5 
lg:flex-row lg:items-center lg:justify-between">
                         <div>
                             <Typography
-                                variant="h5"
-                                className="flex items-center font-medium 
text-gray-800 dark:text-gray-100"
+                                variant="h4"
+                                className="flex items-center font-medium 
text-gray-900 dark:text-white"
                             >
-                                <StorageIcon className="mr-2 text-primary 
dark:text-primary-light" />
+                                <StorageIcon className="mr-3 text-primary 
dark:text-primary-light" />
                                 {cluster}
-                                <Chip
-                                    label={`${shardsData.length} shards`}
-                                    size="small"
-                                    color="primary"
-                                    className="ml-3"
-                                />
                             </Typography>
                             <Typography
-                                variant="body2"
-                                className="mt-1 text-gray-500 
dark:text-gray-400"
+                                variant="body1"
+                                className="mt-0.5 text-gray-500 
dark:text-gray-400"
                             >
-                                Cluster in namespace: {namespace}
+                                Manage shards in this cluster
                             </Typography>
                         </div>
-                    </Box>
-
-                    <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 
lg:grid-cols-3 xl:grid-cols-4">
-                        <Box className="col-span-1">
-                            <AddShardCard namespace={namespace} 
cluster={cluster} />
-                        </Box>
-
-                        {shardsData.length > 0 ? (
-                            shardsData.map((shard, index) => (
-                                <Link
-                                    key={index}
-                                    
href={`/namespaces/${namespace}/clusters/${cluster}/shards/${index}`}
-                                    className="col-span-1"
+
+                        <div className="flex w-full flex-row items-center 
gap-2 lg:w-auto">
+                            <div className="search-container relative max-w-md 
flex-grow transition-all duration-300 lg:min-w-[280px]">
+                                <div className="search-inner relative w-full 
rounded-lg bg-gray-50 transition-all duration-300 focus-within:bg-white 
focus-within:shadow-md dark:bg-dark-paper/90 dark:focus-within:bg-dark-paper">
+                                    <div className="pointer-events-none 
absolute inset-y-0 left-3 flex items-center">
+                                        <SearchIcon
+                                            className="text-gray-400"
+                                            sx={{ fontSize: 18 }}
+                                        />
+                                    </div>
+                                    <input
+                                        type="text"
+                                        placeholder="Search shards..."
+                                        className="w-full rounded-lg border-0 
bg-transparent py-2.5 pl-9 pr-4 text-sm text-gray-800 outline-none ring-1 
ring-gray-200 transition-all focus:ring-2 focus:ring-primary dark:text-gray-200 
dark:ring-gray-700 dark:focus:ring-primary-light"
+                                        value={searchTerm}
+                                        onChange={(e) => 
setSearchTerm(e.target.value)}
+                                    />
+                                    {searchTerm && (
+                                        <button
+                                            className="absolute inset-y-0 
right-3 flex items-center text-gray-400 transition-colors hover:text-gray-600 
dark:hover:text-gray-300"
+                                            onClick={() => setSearchTerm("")}
+                                        >
+                                            <span className="text-xs">✕</span>
+                                        </button>
+                                    )}
+                                </div>
+                            </div>
+
+                            <div className="flex flex-shrink-0 gap-3">
+                                <ShardCreation
+                                    position="page"
+                                    namespace={namespace}
+                                    cluster={cluster}
                                 >
-                                    <ResourceCard
-                                        title={`Shard ${index + 1}`}
-                                        tags={[
-                                            {
-                                                label: `${shard.nodes.length} 
nodes`,
-                                                color: "secondary",
-                                            },
-                                            ...(shard.migrating_slot >= 0
-                                                ? [{ label: "Migrating", 
color: "warning" }]
-                                                : []),
-                                        ]}
+                                    <Button
+                                        variant="outlined"
+                                        color="primary"
+                                        className="whitespace-nowrap 
rounded-lg px-5 py-2.5 font-medium shadow-sm transition-all hover:shadow-md"
+                                        startIcon={<AddIcon />}
+                                        disableElevation
+                                        size="medium"
                                     >
-                                        <div className="space-y-2 text-sm">
-                                            <div className="flex 
justify-between">
-                                                <span className="text-gray-500 
dark:text-gray-400">
-                                                    Slots:
-                                                </span>
-                                                <span className="font-medium">
-                                                    
{formatSlotRanges(shard.slot_ranges)}
-                                                </span>
-                                            </div>
+                                        Create Shard
+                                    </Button>
+                                </ShardCreation>
+                                <MigrateSlot
+                                    position="page"
+                                    namespace={namespace}
+                                    cluster={cluster}
+                                >
+                                    <Button
+                                        variant="outlined"
+                                        color="warning"
+                                        className="whitespace-nowrap 
rounded-lg px-5 py-2.5 font-medium shadow-sm transition-all hover:shadow-md"
+                                        startIcon={<SwapHorizIcon />}
+                                        disableElevation
+                                        size="medium"
+                                    >
+                                        Migrate Slot
+                                    </Button>
+                                </MigrateSlot>
+                            </div>
+                        </div>
+                    </div>
 
-                                            {shard.target_shard_index >= 0 && (
-                                                <div className="flex 
justify-between">
-                                                    <span 
className="text-gray-500 dark:text-gray-400">
-                                                        Target Shard:
-                                                    </span>
-                                                    <span 
className="font-medium">
-                                                        
{shard.target_shard_index + 1}
-                                                    </span>
-                                                </div>
-                                            )}
-
-                                            {shard.migrating_slot >= 0 && (
-                                                <div className="flex 
justify-between">
-                                                    <span 
className="text-gray-500 dark:text-gray-400">
-                                                        Migrating Slot:
-                                                    </span>
-                                                    <Badge color="warning" 
variant="dot">
-                                                        <span 
className="font-medium">
-                                                            
{shard.migrating_slot}
+                    <div className="mb-4 sm:mb-5">
+                        <Grid container spacing={2}>
+                            <Grid item xs={12} sm={6} lg={3}>
+                                <Paper
+                                    elevation={0}
+                                    className="relative h-full overflow-hidden 
rounded-2xl border border-gray-100 p-4 transition-all hover:-translate-y-1 
hover:shadow-md dark:border-gray-800 dark:bg-dark-paper"
+                                >
+                                    <div className="flex items-center 
justify-between">
+                                        <div className="flex h-12 w-12 
items-center justify-center rounded-xl bg-purple-50 text-purple-500 
dark:bg-purple-900/30 dark:text-purple-400">
+                                            <DnsIcon sx={{ fontSize: 24 }} />
+                                        </div>
+                                        <div className="flex flex-col 
items-end">
+                                            <Typography
+                                                variant="h4"
+                                                className="font-semibold 
text-gray-900 dark:text-white"
+                                            >
+                                                {resourceCounts.shards}
+                                            </Typography>
+                                            <Typography
+                                                variant="body2"
+                                                className="text-gray-500 
dark:text-gray-400"
+                                            >
+                                                Shards
+                                            </Typography>
+                                        </div>
+                                    </div>
+                                    <div className="absolute -bottom-4 
-right-4 h-24 w-24 rounded-full bg-purple-500/5 blur-xl"></div>
+                                </Paper>
+                            </Grid>
+
+                            <Grid item xs={12} sm={6} lg={3}>
+                                <Paper
+                                    elevation={0}
+                                    className="relative h-full overflow-hidden 
rounded-2xl border border-gray-100 p-4 transition-all hover:-translate-y-1 
hover:shadow-md dark:border-gray-800 dark:bg-dark-paper"
+                                >
+                                    <div className="flex items-center 
justify-between">
+                                        <div className="flex h-12 w-12 
items-center justify-center rounded-xl bg-green-50 text-green-500 
dark:bg-green-900/30 dark:text-green-400">
+                                            <DeviceHubIcon sx={{ fontSize: 24 
}} />
+                                        </div>
+                                        <div className="flex flex-col 
items-end">
+                                            <Typography
+                                                variant="h4"
+                                                className="font-semibold 
text-gray-900 dark:text-white"
+                                            >
+                                                {resourceCounts.nodes}
+                                            </Typography>
+                                            <Typography
+                                                variant="body2"
+                                                className="text-gray-500 
dark:text-gray-400"
+                                            >
+                                                Nodes
+                                            </Typography>
+                                        </div>
+                                    </div>
+                                    <div className="absolute -bottom-4 
-right-4 h-24 w-24 rounded-full bg-green-500/5 blur-xl"></div>
+                                </Paper>
+                            </Grid>
+
+                            <Grid item xs={12} sm={6} lg={3}>
+                                <Paper
+                                    elevation={0}
+                                    className="relative h-full overflow-hidden 
rounded-2xl border border-gray-100 p-4 transition-all hover:-translate-y-1 
hover:shadow-md dark:border-gray-800 dark:bg-dark-paper"
+                                >
+                                    <div className="flex items-center 
justify-between">
+                                        <div className="flex h-12 w-12 
items-center justify-center rounded-xl bg-amber-50 text-amber-500 
dark:bg-amber-900/30 dark:text-amber-400">
+                                            <StorageIcon sx={{ fontSize: 24 }} 
/>
+                                        </div>
+                                        <div className="flex flex-col 
items-end">
+                                            <Typography
+                                                variant="h4"
+                                                className="font-semibold 
text-gray-900 dark:text-white"
+                                            >
+                                                {resourceCounts.withSlots}
+                                            </Typography>
+                                            <Typography
+                                                variant="body2"
+                                                className="text-gray-500 
dark:text-gray-400"
+                                            >
+                                                With Slots
+                                            </Typography>
+                                        </div>
+                                    </div>
+                                    <div className="absolute -bottom-4 
-right-4 h-24 w-24 rounded-full bg-amber-500/5 blur-xl"></div>
+                                </Paper>
+                            </Grid>
+
+                            <Grid item xs={12} sm={6} lg={3}>
+                                <Paper
+                                    elevation={0}
+                                    className="relative h-full overflow-hidden 
rounded-2xl border border-gray-100 p-4 transition-all hover:-translate-y-1 
hover:shadow-md dark:border-gray-800 dark:bg-dark-paper"
+                                >
+                                    <div className="flex items-center 
justify-between">
+                                        <div className="flex h-12 w-12 
items-center justify-center rounded-xl bg-orange-50 text-orange-500 
dark:bg-orange-900/30 dark:text-orange-400">
+                                            <WarningIcon sx={{ fontSize: 24 }} 
/>
+                                        </div>
+                                        <div className="flex flex-col 
items-end">
+                                            <Typography
+                                                variant="h4"
+                                                className="font-semibold 
text-gray-900 dark:text-white"
+                                            >
+                                                {resourceCounts.migrating}
+                                            </Typography>
+                                            <Typography
+                                                variant="body2"
+                                                className="text-gray-500 
dark:text-gray-400"
+                                            >
+                                                Migrating
+                                            </Typography>
+                                        </div>
+                                    </div>
+                                    <div className="absolute -bottom-4 
-right-4 h-24 w-24 rounded-full bg-orange-500/5 blur-xl"></div>
+                                </Paper>
+                            </Grid>
+                        </Grid>
+                    </div>
+
+                    <Paper
+                        elevation={0}
+                        className="overflow-hidden rounded-2xl border 
border-gray-100 transition-all hover:shadow-md dark:border-gray-800 
dark:bg-dark-paper"
+                    >
+                        <div className="border-b border-gray-100 px-6 py-3 
dark:border-gray-800 sm:px-8">
+                            <div className="flex items-center justify-between">
+                                <Typography
+                                    variant="h6"
+                                    className="font-medium text-gray-800 
dark:text-gray-100"
+                                >
+                                    All Shards
+                                </Typography>
+                                <div className="flex items-center gap-2">
+                                    <Tooltip title="Filter">
+                                        <IconButton
+                                            size="small"
+                                            onClick={handleFilterClick}
+                                            aria-describedby={filterId}
+                                            className="rounded-full bg-gray-50 
text-gray-500 hover:bg-gray-100 dark:bg-gray-800 dark:text-gray-400 
dark:hover:bg-gray-700"
+                                        >
+                                            <FilterListIcon fontSize="small" />
+                                        </IconButton>
+                                    </Tooltip>
+                                    <Tooltip title="Sort">
+                                        <IconButton
+                                            size="small"
+                                            onClick={handleSortClick}
+                                            aria-describedby={sortId}
+                                            className="rounded-full bg-gray-50 
text-gray-500 hover:bg-gray-100 dark:bg-gray-800 dark:text-gray-400 
dark:hover:bg-gray-700"
+                                        >
+                                            <SortIcon fontSize="small" />
+                                        </IconButton>
+                                    </Tooltip>
+                                </div>
+                            </div>
+                        </div>
+
+                        <Popover
+                            id={filterId}
+                            open={isFilterOpen}
+                            anchorEl={filterAnchorEl}
+                            onClose={handleFilterClose}
+                            anchorOrigin={{
+                                vertical: "bottom",
+                                horizontal: "right",
+                            }}
+                            transformOrigin={{
+                                vertical: "top",
+                                horizontal: "right",
+                            }}
+                            TransitionComponent={Fade}
+                            PaperProps={{
+                                className:
+                                    "rounded-xl shadow-xl border 
border-gray-100 dark:border-gray-700",
+                                elevation: 3,
+                                sx: { width: 280 },
+                            }}
+                        >
+                            <div className="p-4">
+                                <div className="mb-3 flex items-center 
justify-between border-b border-gray-100 pb-2 dark:border-gray-700">
+                                    <Typography variant="subtitle1" 
className="font-medium">
+                                        Filter Shards
+                                    </Typography>
+                                </div>
+
+                                <RadioGroup
+                                    value={filterOption}
+                                    onChange={(e) =>
+                                        setFilterOption(e.target.value as 
FilterOption)
+                                    }
+                                >
+                                    <div className="space-y-2">
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="all"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <span 
className="text-sm font-medium">
+                                                            All shards
                                                         </span>
-                                                    </Badge>
-                                                </div>
-                                            )}
+                                                        <Chip
+                                                            size="small"
+                                                            
label={shardsData.length}
+                                                            className="ml-2"
+                                                            sx={{ height: 20, 
fontSize: "0.7rem" }}
+                                                        />
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
                                         </div>
-                                    </ResourceCard>
-                                </Link>
-                            ))
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="with-migration"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <span 
className="text-sm font-medium">
+                                                            With migration
+                                                        </span>
+                                                        <Chip
+                                                            size="small"
+                                                            label={
+                                                                
shardsData.filter(
+                                                                    (shard) => 
shard.hasMigration
+                                                                ).length
+                                                            }
+                                                            className="ml-2"
+                                                            sx={{ height: 20, 
fontSize: "0.7rem" }}
+                                                        />
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="no-migration"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <span 
className="text-sm font-medium">
+                                                            No migration
+                                                        </span>
+                                                        <Chip
+                                                            size="small"
+                                                            label={
+                                                                
shardsData.filter(
+                                                                    (shard) => 
!shard.hasMigration
+                                                                ).length
+                                                            }
+                                                            className="ml-2"
+                                                            sx={{ height: 20, 
fontSize: "0.7rem" }}
+                                                        />
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="with-slots"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <span 
className="text-sm font-medium">
+                                                            With slots
+                                                        </span>
+                                                        <Chip
+                                                            size="small"
+                                                            label={
+                                                                
shardsData.filter(
+                                                                    (shard) => 
shard.hasSlots
+                                                                ).length
+                                                            }
+                                                            className="ml-2"
+                                                            sx={{ height: 20, 
fontSize: "0.7rem" }}
+                                                        />
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="no-slots"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <span 
className="text-sm font-medium">
+                                                            No slots
+                                                        </span>
+                                                        <Chip
+                                                            size="small"
+                                                            label={
+                                                                
shardsData.filter(
+                                                                    (shard) => 
!shard.hasSlots
+                                                                ).length
+                                                            }
+                                                            className="ml-2"
+                                                            sx={{ height: 20, 
fontSize: "0.7rem" }}
+                                                        />
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="with-importing"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <span 
className="text-sm font-medium">
+                                                            With importing
+                                                        </span>
+                                                        <Chip
+                                                            size="small"
+                                                            label={
+                                                                
shardsData.filter(
+                                                                    (shard) => 
shard.hasImporting
+                                                                ).length
+                                                            }
+                                                            className="ml-2"
+                                                            sx={{ height: 20, 
fontSize: "0.7rem" }}
+                                                        />
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+                                    </div>
+                                </RadioGroup>
+
+                                <div className="mt-4 flex justify-end">
+                                    <Button
+                                        variant="text"
+                                        size="small"
+                                        onClick={handleFilterClose}
+                                        className="rounded-lg px-3 py-1 
text-xs"
+                                    >
+                                        Close
+                                    </Button>
+                                </div>
+                            </div>
+                        </Popover>
+
+                        <Popover
+                            id={sortId}
+                            open={isSortOpen}
+                            anchorEl={sortAnchorEl}
+                            onClose={handleSortClose}
+                            anchorOrigin={{
+                                vertical: "bottom",
+                                horizontal: "right",
+                            }}
+                            transformOrigin={{
+                                vertical: "top",
+                                horizontal: "right",
+                            }}
+                            TransitionComponent={Fade}
+                            PaperProps={{
+                                className:
+                                    "rounded-xl shadow-xl border 
border-gray-100 dark:border-gray-700",
+                                elevation: 3,
+                                sx: { width: 280 },
+                            }}
+                        >
+                            <div className="p-4">
+                                <div className="mb-3 flex items-center 
justify-between border-b border-gray-100 pb-2 dark:border-gray-700">
+                                    <Typography variant="subtitle1" 
className="font-medium">
+                                        Sort Shards
+                                    </Typography>
+                                </div>
+
+                                <RadioGroup
+                                    value={sortOption}
+                                    onChange={(e) => 
setSortOption(e.target.value as SortOption)}
+                                >
+                                    <div className="space-y-2">
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="index-asc"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <ArrowUpwardIcon
+                                                            style={{ fontSize: 
16 }}
+                                                            className="mr-1 
text-gray-500"
+                                                        />
+                                                        <span 
className="text-sm font-medium">
+                                                            Index 
1-{shardsData.length}
+                                                        </span>
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="index-desc"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <ArrowDownwardIcon
+                                                            style={{ fontSize: 
16 }}
+                                                            className="mr-1 
text-gray-500"
+                                                        />
+                                                        <span 
className="text-sm font-medium">
+                                                            Index 
{shardsData.length}-1
+                                                        </span>
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="nodes-desc"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <ArrowDownwardIcon
+                                                            style={{ fontSize: 
16 }}
+                                                            className="mr-1 
text-gray-500"
+                                                        />
+                                                        <span 
className="text-sm font-medium">
+                                                            Most nodes
+                                                        </span>
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+
+                                        <div className="rounded-lg bg-gray-50 
p-2 dark:bg-gray-800">
+                                            <FormControlLabel
+                                                value="nodes-asc"
+                                                control={
+                                                    <Radio
+                                                        size="small"
+                                                        
className="text-primary"
+                                                        checkedIcon={
+                                                            <div 
className="flex h-5 w-5 items-center justify-center rounded-full border-2 
border-primary bg-primary text-white">
+                                                                <CheckIcon
+                                                                    style={{ 
fontSize: 12 }}
+                                                                />
+                                                            </div>
+                                                        }
+                                                    />
+                                                }
+                                                label={
+                                                    <div className="flex 
items-center">
+                                                        <ArrowUpwardIcon
+                                                            style={{ fontSize: 
16 }}
+                                                            className="mr-1 
text-gray-500"
+                                                        />
+                                                        <span 
className="text-sm font-medium">
+                                                            Least nodes
+                                                        </span>
+                                                    </div>
+                                                }
+                                                className="m-0 w-full"
+                                            />
+                                        </div>
+                                    </div>
+                                </RadioGroup>
+
+                                <div className="mt-4 flex justify-end">
+                                    <Button
+                                        variant="text"
+                                        size="small"
+                                        onClick={handleSortClose}
+                                        className="rounded-lg px-3 py-1 
text-xs"
+                                    >
+                                        Close
+                                    </Button>
+                                </div>
+                            </div>
+                        </Popover>
+
+                        {filteredAndSortedShards.length > 0 ? (
+                            <div className="divide-y divide-gray-100 
dark:divide-gray-800">
+                                {filteredAndSortedShards.map((shard) => (
+                                    <div
+                                        key={shard.index}
+                                        className="group p-2 transition-colors 
hover:bg-gray-50 dark:hover:bg-gray-800/30"
+                                    >
+                                        <Paper
+                                            elevation={0}
+                                            className="overflow-hidden 
rounded-xl border border-transparent bg-white p-4 transition-all 
group-hover:border-primary/10 group-hover:shadow-sm dark:bg-dark-paper 
dark:group-hover:border-primary-dark/20"
+                                        >
+                                            <div className="flex flex-col 
items-start sm:flex-row sm:items-center">
+                                                <div className="mb-3 flex h-14 
w-14 flex-shrink-0 items-center justify-center rounded-xl bg-green-50 
text-green-500 dark:bg-green-900/30 dark:text-green-400 sm:mb-0">
+                                                    <DnsIcon sx={{ fontSize: 
28 }} />
+                                                </div>
+
+                                                <div className="flex flex-1 
flex-col sm:ml-5 sm:flex-row sm:items-center sm:overflow-hidden">
+                                                    <div className="flex-1 
overflow-hidden">
+                                                        <Link
+                                                            
href={`/namespaces/${namespace}/clusters/${cluster}/shards/${shard.index}`}
+                                                            className="block"
+                                                        >
+                                                            <div 
className="flex items-center gap-2">
+                                                                <Typography
+                                                                    
variant="h6"
+                                                                    
className="truncate font-medium text-gray-900 transition-colors 
hover:text-primary dark:text-gray-100 dark:hover:text-primary-light"
+                                                                >
+                                                                    Shard 
{shard.index + 1}
+                                                                </Typography>
+
+                                                                
{shard.hasMigration &&
+                                                                    
shard.migratingSlot >= 0 && (
+                                                                        <div 
className="flex items-center gap-1 rounded-full border border-orange-200 
bg-orange-50 px-2.5 py-1 dark:border-orange-800 dark:bg-orange-900/30">
+                                                                            
<div className="h-1.5 w-1.5 animate-pulse rounded-full bg-orange-500"></div>
+                                                                            
<span className="text-xs font-medium text-orange-700 dark:text-orange-300">
+                                                                               
 Migrating{" "}
+                                                                               
 {
+                                                                               
     shard.migratingSlot
+                                                                               
 }
+                                                                            
</span>
+                                                                        </div>
+                                                                    )}
+
+                                                                
{!shard.hasMigration &&
+                                                                    
!shard.hasImporting && (
+                                                                        <div 
className="flex items-center gap-1 rounded-full border border-green-200 
bg-green-50 px-2.5 py-1 dark:border-green-800 dark:bg-green-900/30">
+                                                                            
<div className="h-1.5 w-1.5 rounded-full bg-green-500"></div>
+                                                                            
<span className="text-xs font-medium text-green-700 dark:text-green-300">
+                                                                               
 Stable
+                                                                            
</span>
+                                                                        </div>
+                                                                    )}
+
+                                                                
{shard.hasImporting &&
+                                                                    
shard.importingSlot >= 0 && (
+                                                                        <div 
className="flex items-center gap-1 rounded-full border border-blue-200 
bg-blue-50 px-2.5 py-1 dark:border-blue-800 dark:bg-blue-900/30">
+                                                                            
<div className="h-1.5 w-1.5 animate-pulse rounded-full bg-blue-500"></div>
+                                                                            
<span className="text-xs font-medium text-blue-700 dark:text-blue-300">
+                                                                               
 Importing{" "}
+                                                                               
 {
+                                                                               
     shard.importingSlot
+                                                                               
 }
+                                                                            
</span>
+                                                                        </div>
+                                                                    )}
+                                                            </div>
+
+                                                            <div 
className="mt-2 space-y-1">
+                                                                <Typography
+                                                                    
variant="body2"
+                                                                    
className="flex items-center text-gray-500 dark:text-gray-400"
+                                                                >
+                                                                    
<DeviceHubIcon
+                                                                        sx={{ 
fontSize: 14 }}
+                                                                        
className="mr-1"
+                                                                    />
+                                                                    
{shard.nodeCount} nodes
+                                                                </Typography>
+
+                                                                
{shard.hasSlots ? (
+                                                                    <div 
className="flex items-center">
+                                                                        
<Typography
+                                                                            
variant="body2"
+                                                                            
className="flex items-center text-gray-500 dark:text-gray-400"
+                                                                        >
+                                                                            
<StorageIcon
+                                                                               
 sx={{
+                                                                               
     fontSize: 14,
+                                                                               
 }}
+                                                                               
 className="mr-1"
+                                                                            />
+                                                                            
Slots:{" "}
+                                                                            
{formatSlotRanges(
+                                                                               
 shard.slotRanges
+                                                                            )}
+                                                                        
</Typography>
+                                                                        <Chip
+                                                                            
size="small"
+                                                                            
label={`${shard.slotRanges.length} range${shard.slotRanges.length !== 1 ? "s" : 
""}`}
+                                                                            
color="primary"
+                                                                            
variant="outlined"
+                                                                            
className="ml-2"
+                                                                            
sx={{
+                                                                               
 height: 20,
+                                                                               
 fontSize: "0.7rem",
+                                                                            }}
+                                                                        />
+                                                                    </div>
+                                                                ) : (
+                                                                    <Typography
+                                                                        
variant="body2"
+                                                                        
className="flex items-center text-gray-400 dark:text-gray-500"
+                                                                    >
+                                                                        
<StorageIcon
+                                                                            
sx={{ fontSize: 14 }}
+                                                                            
className="mr-1"
+                                                                        />
+                                                                        No 
slots assigned
+                                                                    
</Typography>
+                                                                )}
+
+                                                                
{shard.targetShardIndex >= 0 && (
+                                                                    <Typography
+                                                                        
variant="body2"
+                                                                        
className="flex items-center text-amber-600 dark:text-amber-400"
+                                                                    >
+                                                                        
<InfoIcon
+                                                                            
sx={{ fontSize: 14 }}
+                                                                            
className="mr-1"
+                                                                        />
+                                                                        Target 
shard:{" "}
+                                                                        
{shard.targetShardIndex + 1}
+                                                                    
</Typography>
+                                                                )}
+                                                            </div>
+                                                        </Link>
+                                                    </div>
+
+                                                    <div className="mt-3 flex 
space-x-2 overflow-x-auto sm:ml-6 sm:mt-0 md:hidden lg:flex xl:hidden 2xl:flex">
+                                                        <Chip
+                                                            icon={
+                                                                <DeviceHubIcon 
fontSize="small" />
+                                                            }
+                                                            
label={`${shard.nodeCount} nodes`}
+                                                            size="small"
+                                                            color="secondary"
+                                                            variant="outlined"
+                                                            
className="whitespace-nowrap"
+                                                        />
+
+                                                        {shard.hasSlots ? (
+                                                            <Chip
+                                                                icon={
+                                                                    
<StorageIcon fontSize="small" />
+                                                                }
+                                                                
label={`${shard.slotRanges.length} slot${shard.slotRanges.length !== 1 ? "s" : 
""}`}
+                                                                size="small"
+                                                                color="primary"
+                                                                
variant="outlined"
+                                                                
className="whitespace-nowrap"
+                                                            />
+                                                        ) : (
+                                                            <Chip
+                                                                
icon={<InfoIcon fontSize="small" />}
+                                                                label="No 
slots"
+                                                                size="small"
+                                                                color="info"
+                                                                
variant="outlined"
+                                                                
className="whitespace-nowrap"
+                                                            />
+                                                        )}
+
+                                                        {shard.hasMigration && 
(
+                                                            <Chip
+                                                                icon={
+                                                                    
<WarningIcon fontSize="small" />
+                                                                }
+                                                                
label={`Migrating ${shard.migratingSlot}`}
+                                                                size="small"
+                                                                color="warning"
+                                                                
variant="outlined"
+                                                                
className="whitespace-nowrap"
+                                                            />
+                                                        )}
+
+                                                        {shard.hasImporting && 
(
+                                                            <Chip
+                                                                
icon={<InfoIcon fontSize="small" />}
+                                                                
label={`Importing ${shard.importingSlot}`}
+                                                                size="small"
+                                                                color="info"
+                                                                
variant="outlined"
+                                                                
className="whitespace-nowrap"
+                                                            />
+                                                        )}
+
+                                                        
{shard.targetShardIndex >= 0 && (
+                                                            <Chip
+                                                                
label={`Target: ${shard.targetShardIndex + 1}`}
+                                                                size="small"
+                                                                color="primary"
+                                                                
variant="outlined"
+                                                                
className="whitespace-nowrap"
+                                                            />
+                                                        )}
+                                                    </div>
+
+                                                    <div className="hidden 
space-x-3 sm:ml-8 md:flex lg:hidden xl:flex 2xl:hidden">
+                                                        <div className="flex 
flex-col items-center rounded-lg border border-gray-100 bg-gray-50 px-4 py-2 
dark:border-gray-800 dark:bg-gray-800/50">
+                                                            <Typography
+                                                                
variant="caption"
+                                                                
className="text-gray-500 dark:text-gray-400"
+                                                            >
+                                                                Nodes
+                                                            </Typography>
+                                                            <Typography
+                                                                
variant="subtitle1"
+                                                                
className="font-semibold text-gray-900 dark:text-white"
+                                                            >
+                                                                
{shard.nodeCount}
+                                                            </Typography>
+                                                        </div>
+
+                                                        <div className="flex 
flex-col items-center rounded-lg border border-gray-100 bg-gray-50 px-4 py-2 
dark:border-gray-800 dark:bg-gray-800/50">
+                                                            <Typography
+                                                                
variant="caption"
+                                                                
className="text-gray-500 dark:text-gray-400"
+                                                            >
+                                                                Slots
+                                                            </Typography>
+                                                            <Typography
+                                                                
variant="subtitle1"
+                                                                
className="font-semibold text-gray-900 dark:text-white"
+                                                            >
+                                                                {shard.hasSlots
+                                                                    ? 
shard.slotRanges.length
+                                                                    : 0}
+                                                            </Typography>
+                                                        </div>
+
+                                                        <div className="flex 
flex-col items-center rounded-lg border border-gray-100 bg-gray-50 px-4 py-2 
dark:border-gray-800 dark:bg-gray-800/50">
+                                                            <Typography
+                                                                
variant="caption"
+                                                                
className="text-gray-500 dark:text-gray-400"
+                                                            >
+                                                                Status
+                                                            </Typography>
+                                                            <Typography
+                                                                
variant="subtitle1"
+                                                                
className="font-semibold text-gray-900 dark:text-white"
+                                                            >
+                                                                
{shard.hasMigration
+                                                                    ? 
"Migrating"
+                                                                    : 
shard.hasImporting
+                                                                      ? 
"Importing"
+                                                                      : 
"Stable"}
+                                                            </Typography>
+                                                        </div>
+                                                    </div>
+
+                                                    <div className="ml-2 mt-3 
flex items-center space-x-2 sm:mt-0">
+                                                        <Link
+                                                            
href={`/namespaces/${namespace}/clusters/${cluster}/shards/${shard.index}`}
+                                                            
className="rounded-full bg-primary/10 p-2 text-primary transition-colors 
hover:bg-primary/20 dark:bg-primary-dark/20 dark:text-primary-light 
dark:hover:bg-primary-dark/30"
+                                                        >
+                                                            <ChevronRightIcon 
/>
+                                                        </Link>
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </Paper>
+                                    </div>
+                                ))}
+                            </div>
                         ) : (
-                            <Box className="col-span-full">
+                            <div className="p-12">
                                 <EmptyState
-                                    title="No shards found"
-                                    description="Create a shard to get started"
-                                    icon={<DnsIcon sx={{ fontSize: 60 }} />}
+                                    title={
+                                        filterOption !== "all"
+                                            ? "No matching shards"
+                                            : "No shards found"
+                                    }
+                                    description={
+                                        filterOption !== "all"
+                                            ? "Try changing your filter 
settings"
+                                            : searchTerm
+                                              ? "Try adjusting your search 
term"
+                                              : "Create a shard to get started"
+                                    }
+                                    icon={<DnsIcon sx={{ fontSize: 64 }} />}
+                                    action={{
+                                        label: "Create Shard",
+                                        onClick: () => {},
+                                    }}
                                 />
-                            </Box>
+                                <div className="hidden">
+                                    <ShardCreation
+                                        position="card"
+                                        namespace={namespace}
+                                        cluster={cluster}
+                                    />
+                                </div>
+                            </div>
                         )}
-                    </div>
+
+                        {filteredAndSortedShards.length > 0 && (
+                            <div className="bg-gray-50 px-6 py-4 
dark:bg-gray-800/30 sm:px-8">
+                                <Typography
+                                    variant="body2"
+                                    className="text-gray-500 
dark:text-gray-400"
+                                >
+                                    Showing {filteredAndSortedShards.length} 
of {shardsData.length}{" "}
+                                    shards
+                                </Typography>
+                            </div>
+                        )}
+                    </Paper>
                 </Box>
             </div>
         </div>
diff --git a/webui/src/app/ui/formCreation.tsx 
b/webui/src/app/ui/formCreation.tsx
index dbdcf0d..0a731aa 100644
--- a/webui/src/app/ui/formCreation.tsx
+++ b/webui/src/app/ui/formCreation.tsx
@@ -45,6 +45,7 @@ type ShardFormProps = {
     position: string;
     namespace: string;
     cluster: string;
+    children?: React.ReactNode;
 };
 
 type NodeFormProps = {
@@ -158,7 +159,12 @@ export const ClusterCreation: React.FC<ClusterFormProps> = 
({ position, namespac
     );
 };
 
-export const ShardCreation: React.FC<ShardFormProps> = ({ position, namespace, 
cluster }) => {
+export const ShardCreation: React.FC<ShardFormProps> = ({
+    position,
+    namespace,
+    cluster,
+    children,
+}) => {
     const router = useRouter();
     const handleSubmit = async (formData: FormData) => {
         const fieldsToValidate = ["nodes"];
@@ -203,7 +209,9 @@ export const ShardCreation: React.FC<ShardFormProps> = ({ 
position, namespace, c
                 },
             ]}
             onSubmit={handleSubmit}
-        />
+        >
+            {children}
+        </FormDialog>
     );
 };
 
diff --git a/webui/src/app/ui/sidebar.tsx b/webui/src/app/ui/sidebar.tsx
index 8fcedc4..9fcbe60 100644
--- a/webui/src/app/ui/sidebar.tsx
+++ b/webui/src/app/ui/sidebar.tsx
@@ -185,6 +185,18 @@ export function ClusterSidebar({ namespace }: { namespace: 
string }) {
     const [clusters, setClusters] = useState<string[]>([]);
     const [error, setError] = useState<string | null>(null);
     const [isOpen, setIsOpen] = useState(true);
+    const [sidebarWidth, setSidebarWidth] = useState(260);
+    const [isMobile, setIsMobile] = useState(false);
+
+    useEffect(() => {
+        const checkMobile = () => {
+            setIsMobile(window.innerWidth < 768);
+        };
+
+        checkMobile();
+        window.addEventListener("resize", checkMobile);
+        return () => window.removeEventListener("resize", checkMobile);
+    }, []);
 
     useEffect(() => {
         const fetchData = async () => {
@@ -198,52 +210,78 @@ export function ClusterSidebar({ namespace }: { 
namespace: string }) {
         fetchData();
     }, [namespace]);
 
+    const toggleSidebar = () => {
+        if (isMobile) {
+            setSidebarWidth(isOpen ? 0 : 260);
+        }
+        setIsOpen(!isOpen);
+    };
+
     return (
         <Paper
-            className="flex h-full w-64 flex-col overflow-hidden border-r 
border-light-border/50 bg-white/90 backdrop-blur-sm dark:border-dark-border/50 
dark:bg-dark-paper/90"
+            className="sidebar-container flex h-full flex-col overflow-hidden 
border-r border-light-border/50 bg-white/90 backdrop-blur-sm transition-all 
duration-300 dark:border-dark-border/50 dark:bg-dark-paper/90"
             elevation={0}
             sx={{
+                width: `${sidebarWidth}px`,
+                minWidth: isMobile ? 0 : "260px",
+                maxWidth: "260px",
                 borderTopRightRadius: "16px",
                 borderBottomRightRadius: "16px",
                 boxShadow: "4px 0 15px rgba(0, 0, 0, 0.03)",
+                transition: "width 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
             }}
         >
-            <Box className="p-4 pb-2">
-                <ClusterCreation namespace={namespace} position="sidebar" />
-            </Box>
+            {isMobile && (
+                <button
+                    onClick={toggleSidebar}
+                    className="sidebar-toggle-btn absolute -right-10 top-4 
z-50 flex h-9 w-9 items-center justify-center rounded-full bg-white 
text-gray-600 shadow-lg transition-all hover:bg-gray-50 dark:bg-dark-paper 
dark:text-gray-300 dark:hover:bg-dark-border"
+                >
+                    {isOpen ? (
+                        <ChevronRightIcon />
+                    ) : (
+                        <ChevronRightIcon sx={{ transform: "rotate(180deg)" }} 
/>
+                    )}
+                </button>
+            )}
 
-            <Box className="px-4 py-2">
-                <SidebarHeader
-                    title="Clusters"
-                    count={clusters.length}
-                    isOpen={isOpen}
-                    toggleOpen={() => setIsOpen(!isOpen)}
-                    icon={<StorageIcon fontSize="small" />}
-                />
-            </Box>
+            <div className="sidebar-inner w-[260px]">
+                <Box className="p-4 pb-2">
+                    <ClusterCreation namespace={namespace} position="sidebar" 
/>
+                </Box>
 
-            <Collapse in={isOpen} className="flex-1 overflow-hidden">
-                <div className="h-full overflow-hidden px-4">
-                    <div className="custom-scrollbar max-h-[calc(100vh-180px)] 
overflow-y-auto rounded-xl bg-gray-50/50 p-2 dark:bg-dark-border/20">
-                        {error && (
-                            <div className="my-2 rounded-lg bg-red-50 p-2 
text-center text-sm text-red-600 dark:bg-red-900/20 dark:text-red-400">
-                                {error}
-                            </div>
-                        )}
-                        <List className="p-0">
-                            {clusters.map((cluster) => (
-                                <Link
-                                    
href={`/namespaces/${namespace}/clusters/${cluster}`}
-                                    passHref
-                                    key={cluster}
-                                >
-                                    <Item type="cluster" item={cluster} 
namespace={namespace} />
-                                </Link>
-                            ))}
-                        </List>
+                <Box className="px-4 py-2">
+                    <SidebarHeader
+                        title="Clusters"
+                        count={clusters.length}
+                        isOpen={isOpen}
+                        toggleOpen={toggleSidebar}
+                        icon={<StorageIcon fontSize="small" />}
+                    />
+                </Box>
+
+                <Collapse in={isOpen} className="flex-1 overflow-hidden">
+                    <div className="h-full overflow-hidden px-4">
+                        <div className="sidebar-scrollbar 
max-h-[calc(100vh-200px)] overflow-y-auto rounded-xl bg-gray-50/50 p-2 
dark:bg-dark-border/20">
+                            {error && (
+                                <div className="my-2 rounded-lg bg-red-50 p-2 
text-center text-sm text-red-600 dark:bg-red-900/20 dark:text-red-400">
+                                    {error}
+                                </div>
+                            )}
+                            <List className="p-0">
+                                {clusters.map((cluster) => (
+                                    <Link
+                                        
href={`/namespaces/${namespace}/clusters/${cluster}`}
+                                        passHref
+                                        key={cluster}
+                                    >
+                                        <Item type="cluster" item={cluster} 
namespace={namespace} />
+                                    </Link>
+                                ))}
+                            </List>
+                        </div>
                     </div>
-                </div>
-            </Collapse>
+                </Collapse>
+            </div>
         </Paper>
     );
 }


Reply via email to