From 80dc76c888e4cf0060894cfdcc93d60b52fe19e2 Mon Sep 17 00:00:00 2001 From: Alonza0314 Date: Sat, 9 May 2026 06:07:36 +0000 Subject: [PATCH 1/4] style: version 1 --- frontend/src/App.css | 38 +---- frontend/src/Dashboard.tsx | 303 +++++++++++++++++++++++++++++++---- frontend/src/index.css | 28 +++- frontend/src/index.tsx | 1 + frontend/src/pages/Login.tsx | 155 +++++++++++++----- 5 files changed, 418 insertions(+), 107 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index 74b5e053..eb5d560f 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,38 +1,4 @@ -.App { - text-align: center; -} - .App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + width: 120px; + height: auto; } diff --git a/frontend/src/Dashboard.tsx b/frontend/src/Dashboard.tsx index 348a2868..2982a5e5 100644 --- a/frontend/src/Dashboard.tsx +++ b/frontend/src/Dashboard.tsx @@ -1,6 +1,7 @@ import React, { useContext, useState, useEffect } from "react"; import { styled, createTheme, ThemeProvider } from "@mui/material/styles"; import CssBaseline from "@mui/material/CssBaseline"; +import useMediaQuery from "@mui/material/useMediaQuery"; import MuiDrawer from "@mui/material/Drawer"; import Box from "@mui/material/Box"; import MuiAppBar, { AppBarProps as MuiAppBarProps } from "@mui/material/AppBar"; @@ -14,6 +15,7 @@ import Grid from "@mui/material/Grid"; import Paper from "@mui/material/Paper"; import MenuIcon from "@mui/icons-material/Menu"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; +import EventAvailableIcon from "@mui/icons-material/EventAvailable"; import { MainListItems } from "./ListItems"; import { LoginContext } from "./LoginContext"; import SimpleListMenu from "./SimpleListMenu"; @@ -49,6 +51,8 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open" position: "relative", whiteSpace: "nowrap", width: drawerWidth, + backgroundColor: "#FCFCFD", + borderRight: "1px solid #EEEEEE", transition: theme.transitions.create("width", { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, @@ -71,6 +75,114 @@ const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open" const mdTheme = createTheme(); +const teslaTheme = createTheme({ + palette: { + primary: { + main: "#3E6AE1", + contrastText: "#FFFFFF", + }, + secondary: { + main: "#171A20", + }, + background: { + default: "#FFFFFF", + paper: "#FFFFFF", + }, + text: { + primary: "#171A20", + secondary: "#393C41", + }, + divider: "#EEEEEE", + }, + shape: { + borderRadius: 4, + }, + typography: { + fontFamily: '"Universal Sans Text", -apple-system, Arial, sans-serif', + h6: { + fontSize: "1rem", + fontWeight: 500, + letterSpacing: 0, + }, + button: { + fontSize: "0.875rem", + fontWeight: 500, + textTransform: "none", + }, + }, + components: { + MuiButton: { + styleOverrides: { + root: { + borderRadius: 4, + minHeight: 40, + transition: "border-color 0.33s, background-color 0.33s, color 0.33s, box-shadow 0.25s", + boxShadow: "none", + border: "3px solid transparent", + }, + containedPrimary: { + backgroundColor: "#3E6AE1", + color: "#FFFFFF", + "&:hover": { + backgroundColor: "#365FCC", + boxShadow: "none", + }, + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + boxShadow: "none", + border: "1px solid #EEEEEE", + }, + }, + }, + MuiTableCell: { + styleOverrides: { + head: { + color: "#171A20", + fontWeight: 500, + fontSize: "0.875rem", + }, + body: { + color: "#393C41", + fontSize: "0.875rem", + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + "& .MuiInputBase-root": { + borderRadius: 4, + }, + }, + }, + }, + MuiListItemButton: { + styleOverrides: { + root: { + borderRadius: 4, + margin: "4px 10px", + minHeight: 40, + transition: "color 0.33s, background-color 0.33s", + "&.Mui-selected, &.Mui-selected:hover, &:hover": { + backgroundColor: "rgba(62, 106, 225, 0.08)", + }, + }, + }, + }, + MuiDivider: { + styleOverrides: { + root: { + borderColor: "#EEEEEE", + }, + }, + }, + }, +}); + export interface DashboardProps { children: React.ReactNode; title: string; @@ -79,6 +191,14 @@ export interface DashboardProps { function Dashboard(props: DashboardProps) { const [open, setOpen] = React.useState(true); + const isMobile = useMediaQuery(mdTheme.breakpoints.down("md")); + + useEffect(() => { + if (isMobile) { + setOpen(false); + } + }, [isMobile]); + const toggleDrawer = () => { setOpen(!open); }; @@ -157,39 +277,48 @@ function Dashboard(props: DashboardProps) { } }; return ( - + - + - + {props.title} - - - - - - - - - - - {/* Moved to drop down menu */} - {/* */} - - + + + + + + + + + + + + ) : ( + + + + + + + + + + + + + )} - theme.palette.mode === "light" ? theme.palette.grey[100] : theme.palette.grey[900], + backgroundColor: "#F4F4F4", flexGrow: 1, height: "100vh", overflow: "auto", + pb: 10, }} > - + + + {props.title} + + + - + {props.children} + + + + + + Schedule a Drive Today + + + ); diff --git a/frontend/src/index.css b/frontend/src/index.css index 714ab0eb..45376240 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,11 +1,33 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", - "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + font-family: "Universal Sans Text", -apple-system, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + color: #171a20; + background-color: #ffffff; +} + +* { + box-sizing: border-box; +} + +a { + color: inherit; + text-decoration: none; +} + +button, +input, +textarea, +select { + font: inherit; +} + +::selection { + background: #3e6ae1; + color: #ffffff; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index c75d1802..c96d520c 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,6 +1,7 @@ import React from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; +import "./App.css"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index a2ed5df2..2b5fb9a8 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -7,11 +7,62 @@ import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import Container from "@mui/material/Container"; import { createTheme, ThemeProvider } from "@mui/material/styles"; +import Paper from "@mui/material/Paper"; import axios from "../axios"; import { useNavigate } from "react-router-dom"; import { LoginContext } from "../LoginContext"; -const theme = createTheme(); +const theme = createTheme({ + palette: { + primary: { + main: "#3E6AE1", + contrastText: "#FFFFFF", + }, + background: { + default: "#FFFFFF", + paper: "#FFFFFF", + }, + text: { + primary: "#171A20", + secondary: "#393C41", + }, + }, + shape: { + borderRadius: 4, + }, + typography: { + fontFamily: '"Universal Sans Text", -apple-system, Arial, sans-serif', + }, + components: { + MuiButton: { + styleOverrides: { + root: { + minHeight: 40, + borderRadius: 4, + textTransform: "none", + fontWeight: 500, + border: "3px solid transparent", + boxShadow: "none", + transition: "border-color 0.33s, background-color 0.33s, color 0.33s, box-shadow 0.25s", + }, + containedPrimary: { + "&:hover": { + boxShadow: "none", + }, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + "& .MuiInputBase-root": { + borderRadius: 4, + }, + }, + }, + }, + }, +}); export default function SignIn() { const navigation = useNavigate(); @@ -42,47 +93,77 @@ export default function SignIn() { return ( - + - - logo -
- - {error} - - - - - + + logo + + Webconsole + + + {error} + + + + + + - +
); From 51c7db9a37176030b2f608d8ceae11ba08306770 Mon Sep 17 00:00:00 2001 From: Alonza0314 Date: Sat, 9 May 2026 06:11:34 +0000 Subject: [PATCH 2/4] style: version 2 --- .gitignore | 3 + frontend/src/Dashboard.tsx | 125 +++++++++++++++++++++-------------- frontend/src/index.css | 13 +++- frontend/src/pages/Login.tsx | 32 +++++---- 4 files changed, 106 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index 8bf65cd4..51d829b8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ cscope.* # Binary webconsole + +# design +design diff --git a/frontend/src/Dashboard.tsx b/frontend/src/Dashboard.tsx index 2982a5e5..4fcc4652 100644 --- a/frontend/src/Dashboard.tsx +++ b/frontend/src/Dashboard.tsx @@ -78,36 +78,37 @@ const mdTheme = createTheme(); const teslaTheme = createTheme({ palette: { primary: { - main: "#3E6AE1", + main: "#146ef5", contrastText: "#FFFFFF", }, secondary: { - main: "#171A20", + main: "#080808", }, background: { default: "#FFFFFF", paper: "#FFFFFF", }, text: { - primary: "#171A20", - secondary: "#393C41", + primary: "#080808", + secondary: "#363636", }, - divider: "#EEEEEE", + divider: "#d8d8d8", }, shape: { - borderRadius: 4, + borderRadius: 6, }, typography: { - fontFamily: '"Universal Sans Text", -apple-system, Arial, sans-serif', + fontFamily: '"WF Visual Sans Variable", Arial, sans-serif', h6: { fontSize: "1rem", - fontWeight: 500, + fontWeight: 600, letterSpacing: 0, }, button: { - fontSize: "0.875rem", + fontSize: "1rem", fontWeight: 500, textTransform: "none", + letterSpacing: "-0.16px", }, }, components: { @@ -116,15 +117,18 @@ const teslaTheme = createTheme({ root: { borderRadius: 4, minHeight: 40, - transition: "border-color 0.33s, background-color 0.33s, color 0.33s, box-shadow 0.25s", + transition: "transform 0.33s, border-color 0.33s, background-color 0.33s, color 0.33s, box-shadow 0.25s", boxShadow: "none", - border: "3px solid transparent", + border: "1px solid transparent", + "&:hover": { + transform: "translateX(6px)", + }, }, containedPrimary: { - backgroundColor: "#3E6AE1", + backgroundColor: "#146ef5", color: "#FFFFFF", "&:hover": { - backgroundColor: "#365FCC", + backgroundColor: "#0055d4", boxShadow: "none", }, }, @@ -133,20 +137,23 @@ const teslaTheme = createTheme({ MuiPaper: { styleOverrides: { root: { - boxShadow: "none", - border: "1px solid #EEEEEE", + boxShadow: + "rgba(0, 0, 0, 0) 0px 84px 24px, rgba(0, 0, 0, 0.01) 0px 54px 22px, rgba(0, 0, 0, 0.04) 0px 30px 18px, rgba(0, 0, 0, 0.08) 0px 13px 13px, rgba(0, 0, 0, 0.09) 0px 3px 7px", + border: "1px solid #d8d8d8", }, }, }, MuiTableCell: { styleOverrides: { head: { - color: "#171A20", - fontWeight: 500, + color: "#080808", + fontWeight: 600, fontSize: "0.875rem", + letterSpacing: "0.8px", + textTransform: "uppercase", }, body: { - color: "#393C41", + color: "#363636", fontSize: "0.875rem", }, }, @@ -166,9 +173,10 @@ const teslaTheme = createTheme({ borderRadius: 4, margin: "4px 10px", minHeight: 40, - transition: "color 0.33s, background-color 0.33s", + transition: "color 0.33s, background-color 0.33s, transform 0.33s", "&.Mui-selected, &.Mui-selected:hover, &:hover": { - backgroundColor: "rgba(62, 106, 225, 0.08)", + backgroundColor: "rgba(20, 110, 245, 0.1)", + transform: "translateX(6px)", }, }, }, @@ -176,7 +184,7 @@ const teslaTheme = createTheme({ MuiDivider: { styleOverrides: { root: { - borderColor: "#EEEEEE", + borderColor: "#d8d8d8", }, }, }, @@ -284,10 +292,10 @@ function Dashboard(props: DashboardProps) { position="fixed" open={open && !isMobile} sx={{ - backgroundColor: "rgba(255, 255, 255, 0.75)", - color: "#171A20", - backdropFilter: "blur(8px)", - borderBottom: "1px solid #EEEEEE", + backgroundColor: "rgba(255, 255, 255, 0.92)", + color: "#080808", + backdropFilter: "blur(6px)", + borderBottom: "1px solid #d8d8d8", boxShadow: "none", transition: "background-color 0.33s, color 0.33s", }} @@ -310,7 +318,7 @@ function Dashboard(props: DashboardProps) { - + {props.title} @@ -391,7 +399,8 @@ function Dashboard(props: DashboardProps) { + webconsole control plane + + @@ -454,7 +476,8 @@ function Dashboard(props: DashboardProps) { display: "flex", flexDirection: "column", backgroundColor: "#FFFFFF", - borderRadius: "12px", + borderRadius: "8px", + border: "1px solid #d8d8d8", }} > {props.children} @@ -471,7 +494,7 @@ function Dashboard(props: DashboardProps) { left: isMobile ? 0 : open ? `${drawerWidth}px` : 0, right: 0, backgroundColor: "#FFFFFF", - borderTop: "1px solid #EEEEEE", + borderTop: "1px solid #d8d8d8", px: 2, py: 1, display: "flex", @@ -481,7 +504,7 @@ function Dashboard(props: DashboardProps) { zIndex: teslaTheme.zIndex.appBar, }} > - + Schedule a Drive Today diff --git a/frontend/src/index.css b/frontend/src/index.css index 45376240..25cd17f8 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,9 +1,16 @@ +:root { + --wf-blue: #146ef5; + --wf-black: #080808; + --wf-border: #d8d8d8; + --wf-muted: #363636; +} + body { margin: 0; - font-family: "Universal Sans Text", -apple-system, Arial, sans-serif; + font-family: "WF Visual Sans Variable", Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - color: #171a20; + color: var(--wf-black); background-color: #ffffff; } @@ -24,7 +31,7 @@ select { } ::selection { - background: #3e6ae1; + background: var(--wf-blue); color: #ffffff; } diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 2b5fb9a8..178dcf5f 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -15,7 +15,7 @@ import { LoginContext } from "../LoginContext"; const theme = createTheme({ palette: { primary: { - main: "#3E6AE1", + main: "#146ef5", contrastText: "#FFFFFF", }, background: { @@ -23,15 +23,15 @@ const theme = createTheme({ paper: "#FFFFFF", }, text: { - primary: "#171A20", - secondary: "#393C41", + primary: "#080808", + secondary: "#363636", }, }, shape: { borderRadius: 4, }, typography: { - fontFamily: '"Universal Sans Text", -apple-system, Arial, sans-serif', + fontFamily: '"WF Visual Sans Variable", Arial, sans-serif', }, components: { MuiButton: { @@ -41,12 +41,16 @@ const theme = createTheme({ borderRadius: 4, textTransform: "none", fontWeight: 500, - border: "3px solid transparent", + border: "1px solid transparent", boxShadow: "none", - transition: "border-color 0.33s, background-color 0.33s, color 0.33s, box-shadow 0.25s", + transition: "transform 0.33s, border-color 0.33s, background-color 0.33s, color 0.33s, box-shadow 0.25s", + "&:hover": { + transform: "translateX(6px)", + }, }, containedPrimary: { "&:hover": { + backgroundColor: "#0055d4", boxShadow: "none", }, }, @@ -101,7 +105,8 @@ export default function SignIn() { display: "flex", alignItems: "center", justifyContent: "center", - backgroundColor: "#F4F4F4", + background: + "radial-gradient(circle at 20% -10%, rgba(122, 61, 255, 0.12) 0%, rgba(122, 61, 255, 0) 35%), radial-gradient(circle at 85% 15%, rgba(20, 110, 245, 0.12) 0%, rgba(20, 110, 245, 0) 40%), #ffffff", }} > @@ -110,9 +115,10 @@ export default function SignIn() { width: "100%", maxWidth: 420, p: 4, - border: "1px solid #EEEEEE", - boxShadow: "none", - borderTop: "4px solid #3E6AE1", + border: "1px solid #d8d8d8", + borderTop: "4px solid #146ef5", + boxShadow: + "rgba(0, 0, 0, 0) 0px 84px 24px, rgba(0, 0, 0, 0.01) 0px 54px 22px, rgba(0, 0, 0, 0.04) 0px 30px 18px, rgba(0, 0, 0, 0.08) 0px 13px 13px, rgba(0, 0, 0, 0.09) 0px 3px 7px", }} > Webconsole From 78d3f676a9457b199b3ae6a8e02c6e0effec283d Mon Sep 17 00:00:00 2001 From: Alonza0314 Date: Mon, 11 May 2026 08:42:38 +0000 Subject: [PATCH 3/4] style: less dark and charging card --- frontend/src/Dashboard.tsx | 10 +++- frontend/src/pages/Component/ChargingCfg.tsx | 60 ++++++++++++-------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/frontend/src/Dashboard.tsx b/frontend/src/Dashboard.tsx index 4fcc4652..900fd409 100644 --- a/frontend/src/Dashboard.tsx +++ b/frontend/src/Dashboard.tsx @@ -137,12 +137,18 @@ const teslaTheme = createTheme({ MuiPaper: { styleOverrides: { root: { - boxShadow: - "rgba(0, 0, 0, 0) 0px 84px 24px, rgba(0, 0, 0, 0.01) 0px 54px 22px, rgba(0, 0, 0, 0.04) 0px 30px 18px, rgba(0, 0, 0, 0.08) 0px 13px 13px, rgba(0, 0, 0, 0.09) 0px 3px 7px", + boxShadow: "0 6px 18px rgba(8, 8, 8, 0.07), 0 2px 6px rgba(8, 8, 8, 0.04)", border: "1px solid #d8d8d8", }, }, }, + MuiCard: { + styleOverrides: { + root: { + boxShadow: "0 5px 14px rgba(8, 8, 8, 0.06), 0 1px 4px rgba(8, 8, 8, 0.04)", + }, + }, + }, MuiTableCell: { styleOverrides: { head: { diff --git a/frontend/src/pages/Component/ChargingCfg.tsx b/frontend/src/pages/Component/ChargingCfg.tsx index 397443af..3351ff3d 100644 --- a/frontend/src/pages/Component/ChargingCfg.tsx +++ b/frontend/src/pages/Component/ChargingCfg.tsx @@ -1,11 +1,12 @@ import React from "react"; import { Box, - Grid, + Card, Table, TableBody, TableCell, TableRow, + Typography, } from "@mui/material"; import { ChargingData } from "../../api/api"; @@ -17,34 +18,43 @@ const ChargingCfg = ({ const isOnlineCharging = chargingData.chargingMethod === "Online"; return ( - - - -

Charging Config

-
-
- - - - Charging Method - {chargingData.chargingMethod} - - - {isOnlineCharging && ( + + + + + Charging Config + + +
- Quota - {chargingData.quota} + Charging Method + {chargingData.chargingMethod} - )} - - - Unit Cost - {chargingData.unitCost} - - -
+ {isOnlineCharging && ( + + + Quota + {chargingData.quota} + + + )} + + + Unit Cost + {chargingData.unitCost} + + + +
); }; From 70c1acb758cd145a2df5ebf87fc16b4a2c9e074c Mon Sep 17 00:00:00 2001 From: Alonza0314 Date: Thu, 14 May 2026 01:09:35 +0000 Subject: [PATCH 4/4] chore: revert original layout --- frontend/src/pages/SubscriberList.tsx | 257 +++++++++----------------- 1 file changed, 87 insertions(+), 170 deletions(-) diff --git a/frontend/src/pages/SubscriberList.tsx b/frontend/src/pages/SubscriberList.tsx index 6867547b..10e48c7f 100644 --- a/frontend/src/pages/SubscriberList.tsx +++ b/frontend/src/pages/SubscriberList.tsx @@ -1,12 +1,5 @@ -import React, { - useState, - useEffect, - useMemo, - useCallback, - CSSProperties, -} from "react"; +import React, { useState, useEffect, useMemo, useCallback } from "react"; import { useNavigate } from "react-router-dom"; -import { List } from "react-window"; import axios from "../axios"; import { Subscriber } from "../api/api"; @@ -21,6 +14,11 @@ import { LinearProgress, Snackbar, Stack, + Table, + TableBody, + TableCell, + TableHead, + TableRow, TextField, Typography, Checkbox, @@ -31,124 +29,14 @@ import { formatMultipleDeleteSubscriberToJson, } from "../lib/jsonFormating"; -const ROW_HEIGHT = 52; -const MAX_LIST_HEIGHT = 530; const BULK_DELETE_WARN_THRESHOLD = 100; const BULK_DELETE_BATCH_SIZE = 500; -// Shared between header and virtual rows — both must use the same template. -const COL_CHECKBOX = "52px"; -const COL_PLMN = "18%"; -const COL_UEID = "1fr"; -const COL_DELETE = "110px"; -const COL_VIEW = "90px"; -const COL_EDIT = "90px"; -const GRID_COLS = `${COL_CHECKBOX} ${COL_PLMN} ${COL_UEID} ${COL_DELETE} ${COL_VIEW} ${COL_EDIT}`; - interface Props { refresh: boolean; setRefresh: (v: boolean) => void; } -type RowSharedProps = { - rows: Subscriber[]; - selected: MultipleDeleteSubscriberData[]; - onDelete: (id: string, plmn: string) => void; - onModify: (s: Subscriber) => void; - onEdit: (s: Subscriber) => void; - onRowClick: (item: MultipleDeleteSubscriberData) => void; -}; - -// Defined outside SubscriberList so its reference is stable across renders. -type VirtualRowProps = { - ariaAttributes: Record; - index: number; - style: CSSProperties; -} & RowSharedProps; - -function VirtualRow({ - index, - style, - rows, - selected, - onDelete, - onModify, - onEdit, - onRowClick, -}: VirtualRowProps) { - const row = rows[index]; - if (!row) return null; - - const item: MultipleDeleteSubscriberData = { - ueId: row.ueId!, - plmnID: row.plmnID!, - }; - - const isItemSelected = selected.some( - (s) => s.ueId === item.ueId && s.plmnID === item.plmnID - ); - - return ( - onRowClick(item)} - sx={{ - display: "grid", - gridTemplateColumns: GRID_COLS, - alignItems: "center", - borderBottom: "1px solid", - borderColor: "divider", - backgroundColor: isItemSelected ? "action.selected" : "transparent", - "&:hover": { - backgroundColor: isItemSelected ? "action.focus" : "action.hover", - }, - cursor: "pointer", - boxSizing: "border-box", - }} - > - - - - - {row.plmnID} - - - {row.ueId} - - - - - - - - - - - - ); -} - function SubscriberList(props: Props) { const navigation = useNavigate(); const [data, setData] = useState([]); @@ -207,6 +95,9 @@ function SubscriberList(props: Props) { setSearchTerm(e.target.value); }; + const isSelected = (item: MultipleDeleteSubscriberData) => + selected.some((s) => s.ueId === item.ueId && s.plmnID === item.plmnID); + const onDelete = useCallback( (id: string, plmn: string) => { if (!window.confirm("Delete subscriber?")) return; @@ -329,17 +220,6 @@ function SubscriberList(props: Props) { ); } - const listHeight = Math.min(filteredData.length * ROW_HEIGHT, MAX_LIST_HEIGHT); - - const rowProps: RowSharedProps = { - rows: filteredData, - selected, - onDelete, - onModify, - onEdit, - onRowClick: handleRowClick, - }; - return ( <>
@@ -379,47 +259,84 @@ function SubscriberList(props: Props) {
)} - - - 0 && selected.length < filteredData.length} - checked={filteredData.length > 0 && selected.length === filteredData.length} - onChange={handleSelectAllClick} - size="small" - /> - - PLMN - UE ID - Delete - View - Edit - - - {/* react-window requires an explicit pixel height — percentage heights - resolve to 0 because ResizeObserver cannot measure unconstrained flex containers. */} - - - rowComponent={VirtualRow} - rowProps={rowProps} - rowCount={filteredData.length} - rowHeight={ROW_HEIGHT} - style={{ height: listHeight, width: "100%" }} - /> - + + + + + 0 && selected.length < filteredData.length} + checked={filteredData.length > 0 && selected.length === filteredData.length} + onChange={handleSelectAllClick} + /> + + PLMN + UE ID + Delete + View + Edit + + + + {filteredData.map((row, index) => { + const item = { ueId: row.ueId!, plmnID: row.plmnID! }; + const isItemSelected = isSelected(item); + + return ( + handleRowClick(item)} + role="checkbox" + aria-checked={isItemSelected} + selected={isItemSelected} + > + + + + {row.plmnID} + {row.ueId} + + + + + + + + + + + ); + })} + +