This commit is contained in:
parent
dec5e1e5db
commit
87cce983e9
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"extends": ["next/core-web-vitals", "plugin:tailwindcss/recommended"]
|
"extends": ["next/core-web-vitals", "plugin:tailwindcss/recommended"],
|
||||||
|
"ignorePatterns": ["**/src/api/*"]
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ const config = {
|
|||||||
petstore: {
|
petstore: {
|
||||||
output: {
|
output: {
|
||||||
mode: "tags-split",
|
mode: "tags-split",
|
||||||
target: "api",
|
target: "src/api",
|
||||||
schemas: "api/model",
|
schemas: "src/api/model",
|
||||||
client: "swr",
|
client: "swr",
|
||||||
// mock: true,
|
// mock: true,
|
||||||
},
|
},
|
||||||
|
@ -20,6 +20,7 @@ import MenuIcon from "@mui/icons-material/Menu";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { tw } from "@/utils/tailwind";
|
import { tw } from "@/utils/tailwind";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { MachineContextProvider } from "@/components/hooks/useMachines";
|
||||||
|
|
||||||
const roboto = localFont({
|
const roboto = localFont({
|
||||||
src: [
|
src: [
|
||||||
@ -81,45 +82,47 @@ export default function RootLayout({
|
|||||||
<body id="__next" className={roboto.className}>
|
<body id="__next" className={roboto.className}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<div className="flex h-screen overflow-hidden">
|
<MachineContextProvider>
|
||||||
<Sidebar
|
<div className="flex h-screen overflow-hidden">
|
||||||
show={showSidebar}
|
<Sidebar
|
||||||
onClose={() => setShowSidebar(false)}
|
show={showSidebar}
|
||||||
/>
|
onClose={() => setShowSidebar(false)}
|
||||||
<div
|
/>
|
||||||
className={tw`${
|
<div
|
||||||
!showSidebar && translate
|
className={tw`${
|
||||||
} flex h-full w-full flex-col overflow-y-scroll transition-[margin] duration-150 ease-in-out`}
|
!showSidebar && translate
|
||||||
>
|
} flex h-full w-full flex-col overflow-y-scroll transition-[margin] duration-150 ease-in-out`}
|
||||||
<div className="static top-0 mb-2 py-2">
|
>
|
||||||
<div className="grid grid-cols-3">
|
<div className="static top-0 mb-2 py-2">
|
||||||
<div className="col-span-1">
|
<div className="grid grid-cols-3">
|
||||||
<IconButton
|
<div className="col-span-1">
|
||||||
hidden={true}
|
<IconButton
|
||||||
onClick={() => setShowSidebar((c) => !c)}
|
hidden={true}
|
||||||
>
|
onClick={() => setShowSidebar((c) => !c)}
|
||||||
{!showSidebar && <MenuIcon />}
|
>
|
||||||
</IconButton>
|
{!showSidebar && <MenuIcon />}
|
||||||
</div>
|
</IconButton>
|
||||||
<div className="col-span-1 block w-full text-center font-semibold text-white lg:hidden ">
|
</div>
|
||||||
<Image
|
<div className="col-span-1 block w-full text-center font-semibold text-white lg:hidden ">
|
||||||
src="/logo.svg"
|
<Image
|
||||||
alt="Clan Logo"
|
src="/logo.svg"
|
||||||
width={58}
|
alt="Clan Logo"
|
||||||
height={58}
|
width={58}
|
||||||
priority
|
height={58}
|
||||||
/>
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="px-1">
|
<div className="px-1">
|
||||||
<div className="relative flex h-full flex-1 flex-col">
|
<div className="relative flex h-full flex-1 flex-col">
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</MachineContextProvider>
|
||||||
</body>
|
</body>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</StyledEngineProvider>
|
</StyledEngineProvider>
|
||||||
|
10
pkgs/ui/src/app/machines/edit/[name]/page.tsx
Normal file
10
pkgs/ui/src/app/machines/edit/[name]/page.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
interface DeviceEditProps {
|
||||||
|
params: { name: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EditDevice(props: DeviceEditProps) {
|
||||||
|
const {
|
||||||
|
params: { name },
|
||||||
|
} = props;
|
||||||
|
return <div>{name}</div>;
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import NodeTable from "./NodeTable";
|
|
||||||
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import { tableData } from "@/data/nodeDataStatic";
|
import { tableData } from "@/data/nodeDataStatic";
|
||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
|
import { NodeTable } from "@/components/table";
|
||||||
|
import { useMachines } from "@/components/hooks/useMachines";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const { data, isLoading } = useMachines();
|
||||||
|
console.log({ data, isLoading });
|
||||||
return (
|
return (
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<NodeTable tableData={tableData} />
|
<NodeTable tableData={tableData} />
|
@ -1,194 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import * as React from "react";
|
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
|
||||||
import Switch from "@mui/material/Switch";
|
|
||||||
import Stack from "@mui/material/Stack/Stack";
|
|
||||||
import NodePieChart from "./NodePieChart";
|
|
||||||
|
|
||||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
|
||||||
import { Card, CardContent, FormGroup, useTheme } from "@mui/material";
|
|
||||||
import hexRgb from "hex-rgb";
|
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
||||||
import { NodeStatus, TableData } from "@/data/nodeData";
|
|
||||||
|
|
||||||
interface EnhancedTableToolbarProps {
|
|
||||||
tableData: TableData[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function PieCardData(props: { pieData: PieData[]; debugSx: any }) {
|
|
||||||
const { pieData, debugSx } = props;
|
|
||||||
|
|
||||||
const cardData = React.useMemo(() => {
|
|
||||||
return pieData
|
|
||||||
.filter((pieItem) => pieItem.value > 0)
|
|
||||||
.concat({
|
|
||||||
name: "Total",
|
|
||||||
value: pieData.reduce((a, b) => a + b.value, 0),
|
|
||||||
color: "#000000",
|
|
||||||
});
|
|
||||||
}, [pieData]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Stack
|
|
||||||
sx={{ ...debugSx, paddingTop: 6 }}
|
|
||||||
height={350}
|
|
||||||
id="cardBox"
|
|
||||||
display="flex"
|
|
||||||
flexDirection="column"
|
|
||||||
justifyContent="flex-start"
|
|
||||||
flexWrap="wrap"
|
|
||||||
>
|
|
||||||
{cardData.map((pieItem) => (
|
|
||||||
<Card
|
|
||||||
key={pieItem.name}
|
|
||||||
sx={{
|
|
||||||
marginBottom: 2,
|
|
||||||
marginRight: 2,
|
|
||||||
width: 110,
|
|
||||||
height: 110,
|
|
||||||
backgroundColor: hexRgb(pieItem.color, {
|
|
||||||
format: "css",
|
|
||||||
alpha: 0.25,
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardContent>
|
|
||||||
<Typography
|
|
||||||
variant="h4"
|
|
||||||
component="div"
|
|
||||||
gutterBottom={true}
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
{pieItem.value}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
sx={{ mb: 1.5 }}
|
|
||||||
color="text.secondary"
|
|
||||||
textAlign="center"
|
|
||||||
>
|
|
||||||
{pieItem.name}
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PieData {
|
|
||||||
name: string;
|
|
||||||
value: number;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function EnhancedTableToolbar(
|
|
||||||
props: React.PropsWithChildren<EnhancedTableToolbarProps>,
|
|
||||||
) {
|
|
||||||
const { tableData } = props;
|
|
||||||
const theme = useTheme();
|
|
||||||
const is_lg = useMediaQuery(theme.breakpoints.down("lg"));
|
|
||||||
const [debug, setDebug] = React.useState<boolean>(false);
|
|
||||||
|
|
||||||
const debugSx = debug
|
|
||||||
? {
|
|
||||||
"--Grid-borderWidth": "1px",
|
|
||||||
borderTop: "var(--Grid-borderWidth) solid",
|
|
||||||
borderLeft: "var(--Grid-borderWidth) solid",
|
|
||||||
borderColor: "divider",
|
|
||||||
"& > div": {
|
|
||||||
borderRight: "var(--Grid-borderWidth) solid",
|
|
||||||
borderBottom: "var(--Grid-borderWidth) solid",
|
|
||||||
borderColor: "divider",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const pieData: PieData[] = React.useMemo(() => {
|
|
||||||
const online = tableData.filter(
|
|
||||||
(row) => row.status === NodeStatus.Online,
|
|
||||||
).length;
|
|
||||||
const offline = tableData.filter(
|
|
||||||
(row) => row.status === NodeStatus.Offline,
|
|
||||||
).length;
|
|
||||||
const pending = tableData.filter(
|
|
||||||
(row) => row.status === NodeStatus.Pending,
|
|
||||||
).length;
|
|
||||||
|
|
||||||
return [
|
|
||||||
{ name: "Online", value: online, color: theme.palette.success.main },
|
|
||||||
{ name: "Offline", value: offline, color: theme.palette.error.main },
|
|
||||||
{ name: "Pending", value: pending, color: theme.palette.warning.main },
|
|
||||||
];
|
|
||||||
}, [tableData, theme]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Grid2 container spacing={1} sx={debugSx}>
|
|
||||||
<Grid2 key="Header" xs={6}>
|
|
||||||
<Typography
|
|
||||||
sx={{ marginLeft: 3, marginTop: 1 }}
|
|
||||||
variant="h6"
|
|
||||||
id="tableTitle"
|
|
||||||
component="div"
|
|
||||||
>
|
|
||||||
NODES
|
|
||||||
</Typography>
|
|
||||||
</Grid2>
|
|
||||||
{/* Debug Controls */}
|
|
||||||
<Grid2 key="Debug-Controls" xs={6} justifyContent="left" display="flex">
|
|
||||||
<FormGroup>
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Switch
|
|
||||||
onChange={() => {
|
|
||||||
setDebug(!debug);
|
|
||||||
}}
|
|
||||||
checked={debug}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Debug"
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Grid2>
|
|
||||||
|
|
||||||
{/* Pie Chart Grid */}
|
|
||||||
<Grid2
|
|
||||||
key="PieChart"
|
|
||||||
md={6}
|
|
||||||
xs={12}
|
|
||||||
display="flex"
|
|
||||||
justifyContent="center"
|
|
||||||
alignItems="center"
|
|
||||||
>
|
|
||||||
<Box height={350} width={400}>
|
|
||||||
<NodePieChart data={pieData} showLabels={is_lg} />
|
|
||||||
</Box>
|
|
||||||
</Grid2>
|
|
||||||
|
|
||||||
{/* Card Stack Grid */}
|
|
||||||
<Grid2
|
|
||||||
key="CardStack"
|
|
||||||
lg={6}
|
|
||||||
display="flex"
|
|
||||||
sx={{ display: { lg: "flex", xs: "none", md: "flex" } }}
|
|
||||||
>
|
|
||||||
<PieCardData pieData={pieData} debugSx={debugSx} />
|
|
||||||
</Grid2>
|
|
||||||
|
|
||||||
{/*Toolbar Grid */}
|
|
||||||
<Grid2 key="Toolbar" xs={12}>
|
|
||||||
<Toolbar
|
|
||||||
sx={{
|
|
||||||
pl: { sm: 2 },
|
|
||||||
pr: { xs: 1, sm: 1 },
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</Toolbar>
|
|
||||||
</Grid2>
|
|
||||||
</Grid2>
|
|
||||||
);
|
|
||||||
}
|
|
@ -29,7 +29,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useListMachines } from "../../../../api/default/default";
|
import { useListMachines } from "@api/default/default";
|
||||||
|
|
||||||
export async function generateStaticParams() {
|
export async function generateStaticParams() {
|
||||||
return [{ id: "1" }, { id: "2" }];
|
return [{ id: "1" }, { id: "2" }];
|
||||||
@ -46,7 +46,7 @@ function getTemplate(params: { id: string }) {
|
|||||||
interface TemplateDetailProps {
|
interface TemplateDetailProps {
|
||||||
params: { id: string };
|
params: { id: string };
|
||||||
}
|
}
|
||||||
export default function TemplateDetail({ params }: TemplateDetailProps) {
|
export function TemplateDetail({ params }: TemplateDetailProps) {
|
||||||
const { data, isLoading } = useListMachines();
|
const { data, isLoading } = useListMachines();
|
||||||
console.log({ data, isLoading });
|
console.log({ data, isLoading });
|
||||||
const details = getTemplate(params);
|
const details = getTemplate(params);
|
||||||
|
93
pkgs/ui/src/components/hooks/useMachines.tsx
Normal file
93
pkgs/ui/src/components/hooks/useMachines.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { useListMachines } from "@/api/default/default";
|
||||||
|
import { Machine, MachinesResponse } from "@/api/model";
|
||||||
|
import { AxiosError, AxiosResponse } from "axios";
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
Dispatch,
|
||||||
|
ReactNode,
|
||||||
|
SetStateAction,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { KeyedMutator } from "swr";
|
||||||
|
|
||||||
|
type Filter = {
|
||||||
|
name: keyof Machine;
|
||||||
|
value: Machine[keyof Machine];
|
||||||
|
};
|
||||||
|
type Filters = Filter[];
|
||||||
|
|
||||||
|
type MachineContextType =
|
||||||
|
| {
|
||||||
|
rawData: AxiosResponse<MachinesResponse, any> | undefined;
|
||||||
|
data: Machine[];
|
||||||
|
isLoading: boolean;
|
||||||
|
error: AxiosError<any> | undefined;
|
||||||
|
isValidating: boolean;
|
||||||
|
|
||||||
|
filters: Filters;
|
||||||
|
setFilters: Dispatch<SetStateAction<Filters>>;
|
||||||
|
mutate: KeyedMutator<AxiosResponse<MachinesResponse, any>>;
|
||||||
|
swrKey: string | false | Record<any, any>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
isLoading: true;
|
||||||
|
data: readonly [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
isLoading: true,
|
||||||
|
data: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const MachineContext = createContext<MachineContextType>(initialState);
|
||||||
|
|
||||||
|
interface MachineContextProviderProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MachineContextProvider = (props: MachineContextProviderProps) => {
|
||||||
|
const { children } = props;
|
||||||
|
const {
|
||||||
|
data: rawData,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
isValidating,
|
||||||
|
mutate,
|
||||||
|
swrKey,
|
||||||
|
} = useListMachines();
|
||||||
|
const [filters, setFilters] = useState<Filters>([]);
|
||||||
|
|
||||||
|
const data = useMemo(() => {
|
||||||
|
if (!isLoading && !error && !isValidating && rawData) {
|
||||||
|
const { machines } = rawData.data;
|
||||||
|
return machines.filter((m) =>
|
||||||
|
filters.every((f) => m[f.name] === f.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [isLoading, error, isValidating, rawData, filters]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MachineContext.Provider
|
||||||
|
value={{
|
||||||
|
rawData,
|
||||||
|
data,
|
||||||
|
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
isValidating,
|
||||||
|
|
||||||
|
filters,
|
||||||
|
setFilters,
|
||||||
|
|
||||||
|
swrKey,
|
||||||
|
mutate,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MachineContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMachines = () => React.useContext(MachineContext);
|
@ -38,8 +38,8 @@ const menuEntries: MenuEntry[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <DevicesIcon />,
|
icon: <DevicesIcon />,
|
||||||
label: "Devices",
|
label: "Machines",
|
||||||
to: "/nodes",
|
to: "/machines",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <AppsIcon />,
|
icon: <AppsIcon />,
|
||||||
|
82
pkgs/ui/src/components/table/enhancedTableToolbar.tsx
Normal file
82
pkgs/ui/src/components/table/enhancedTableToolbar.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
|
import Grid2 from "@mui/material/Unstable_Grid2";
|
||||||
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
|
import { useTheme } from "@mui/material";
|
||||||
|
|
||||||
|
import { NodeStatus, TableData } from "@/data/nodeData";
|
||||||
|
import { PieCards } from "./pieCards";
|
||||||
|
import { PieData, NodePieChart } from "./nodePieChart";
|
||||||
|
|
||||||
|
interface EnhancedTableToolbarProps {
|
||||||
|
tableData: TableData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EnhancedTableToolbar(
|
||||||
|
props: React.PropsWithChildren<EnhancedTableToolbarProps>,
|
||||||
|
) {
|
||||||
|
const { tableData } = props;
|
||||||
|
const theme = useTheme();
|
||||||
|
const is_lg = useMediaQuery(theme.breakpoints.down("lg"));
|
||||||
|
|
||||||
|
const pieData: PieData[] = useMemo(() => {
|
||||||
|
const online = tableData.filter(
|
||||||
|
(row) => row.status === NodeStatus.Online,
|
||||||
|
).length;
|
||||||
|
const offline = tableData.filter(
|
||||||
|
(row) => row.status === NodeStatus.Offline,
|
||||||
|
).length;
|
||||||
|
const pending = tableData.filter(
|
||||||
|
(row) => row.status === NodeStatus.Pending,
|
||||||
|
).length;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ name: "Online", value: online, color: theme.palette.success.main },
|
||||||
|
{ name: "Offline", value: offline, color: theme.palette.error.main },
|
||||||
|
{ name: "Pending", value: pending, color: theme.palette.warning.main },
|
||||||
|
];
|
||||||
|
}, [tableData, theme]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid2 container spacing={1}>
|
||||||
|
{/* Pie Chart Grid */}
|
||||||
|
<Grid2
|
||||||
|
key="PieChart"
|
||||||
|
md={6}
|
||||||
|
xs={12}
|
||||||
|
display="flex"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Box height={350} width={400}>
|
||||||
|
<NodePieChart data={pieData} showLabels={is_lg} />
|
||||||
|
</Box>
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
{/* Card Stack Grid */}
|
||||||
|
<Grid2
|
||||||
|
key="CardStack"
|
||||||
|
lg={6}
|
||||||
|
display="flex"
|
||||||
|
sx={{ display: { lg: "flex", xs: "none", md: "flex" } }}
|
||||||
|
>
|
||||||
|
<PieCards pieData={pieData} />
|
||||||
|
</Grid2>
|
||||||
|
|
||||||
|
{/*Toolbar Grid */}
|
||||||
|
<Grid2 key="Toolbar" xs={12}>
|
||||||
|
<Toolbar
|
||||||
|
sx={{
|
||||||
|
pl: { sm: 2 },
|
||||||
|
pr: { xs: 1, sm: 1 },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Toolbar>
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
);
|
||||||
|
}
|
1
pkgs/ui/src/components/table/index.tsx
Normal file
1
pkgs/ui/src/components/table/index.tsx
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { NodeTable } from "./nodeTable";
|
@ -1,14 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { PieChart, Pie, Cell, ResponsiveContainer, Legend } from "recharts";
|
||||||
PieChart,
|
|
||||||
Pie,
|
|
||||||
Sector,
|
|
||||||
Cell,
|
|
||||||
ResponsiveContainer,
|
|
||||||
Legend,
|
|
||||||
} from "recharts";
|
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { useTheme } from "@mui/material/styles";
|
||||||
import { Box, Color } from "@mui/material";
|
import { Box } from "@mui/material";
|
||||||
|
|
||||||
export interface PieData {
|
export interface PieData {
|
||||||
name: string;
|
name: string;
|
||||||
@ -21,7 +14,7 @@ interface Props {
|
|||||||
showLabels?: boolean;
|
showLabels?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NodePieChart(props: Props) {
|
export function NodePieChart(props: Props) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { data, showLabels } = props;
|
const { data, showLabels } = props;
|
||||||
|
|
@ -13,9 +13,10 @@ import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
|||||||
|
|
||||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
||||||
import { Collapse } from "@mui/material";
|
import { Collapse } from "@mui/material";
|
||||||
|
|
||||||
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
||||||
|
|
||||||
export default function NodeRow(props: {
|
export function NodeRow(props: {
|
||||||
row: TableData;
|
row: TableData;
|
||||||
selected: string | undefined;
|
selected: string | undefined;
|
||||||
setSelected: (a: string | undefined) => void;
|
setSelected: (a: string | undefined) => void;
|
||||||
@ -58,13 +59,13 @@ export default function NodeRow(props: {
|
|||||||
//const labelId = `enhanced-table-checkbox-${index}`;
|
//const labelId = `enhanced-table-checkbox-${index}`;
|
||||||
|
|
||||||
// Speed optimization. We compare string pointers here instead of the string content.
|
// Speed optimization. We compare string pointers here instead of the string content.
|
||||||
const isSelected = selected == row.id;
|
const isSelected = selected == row.name;
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<unknown>, id: string) => {
|
const handleClick = (event: React.MouseEvent<unknown>, name: string) => {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
setSelected(undefined);
|
setSelected(undefined);
|
||||||
} else {
|
} else {
|
||||||
setSelected(id);
|
setSelected(name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,29 +1,27 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import { useState, ChangeEvent, SetStateAction, Dispatch } from "react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import TablePagination from "@mui/material/TablePagination";
|
import TablePagination from "@mui/material/TablePagination";
|
||||||
import Paper from "@mui/material/Paper";
|
import Paper from "@mui/material/Paper";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
import SearchIcon from "@mui/icons-material/Search";
|
||||||
import NodeTableContainer from "./NodeTableContainer";
|
|
||||||
|
|
||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material";
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
import { TableData } from "@/data/nodeData";
|
|
||||||
import EnhancedTableToolbar from "./EnhancedTableToolbar";
|
|
||||||
import { table } from "console";
|
|
||||||
import StickySpeedDial from "./StickySpeedDial";
|
|
||||||
|
|
||||||
|
import { TableData } from "@/data/nodeData";
|
||||||
|
import { EnhancedTableToolbar } from "./enhancedTableToolbar";
|
||||||
|
import { StickySpeedDial } from "./stickySpeedDial";
|
||||||
|
import { NodeTableContainer } from "./nodeTableContainer";
|
||||||
export interface SearchBarProps {
|
export interface SearchBarProps {
|
||||||
search: string;
|
search: string;
|
||||||
setSearch: React.Dispatch<React.SetStateAction<string>>;
|
setSearch: Dispatch<SetStateAction<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SearchBar(props: SearchBarProps) {
|
function SearchBar(props: SearchBarProps) {
|
||||||
const { search, setSearch } = props;
|
const { search, setSearch } = props;
|
||||||
const handleSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleSearch = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setSearch(event.target.value);
|
setSearch(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,24 +41,22 @@ export interface NodeTableProps {
|
|||||||
tableData: TableData[];
|
tableData: TableData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NodeTable(props: NodeTableProps) {
|
export function NodeTable(props: NodeTableProps) {
|
||||||
let { tableData } = props;
|
let { tableData } = props;
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const is_xs = useMediaQuery(theme.breakpoints.only("xs"));
|
const is_xs = useMediaQuery(theme.breakpoints.only("xs"));
|
||||||
|
|
||||||
const [selected, setSelected] = React.useState<string | undefined>(undefined);
|
const [selected, setSelected] = useState<string | undefined>(undefined);
|
||||||
const [page, setPage] = React.useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
const [rowsPerPage, setRowsPerPage] = useState(5);
|
||||||
const [search, setSearch] = React.useState<string>("");
|
const [search, setSearch] = useState<string>("");
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
setPage(newPage);
|
setPage(newPage);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeRowsPerPage = (
|
const handleChangeRowsPerPage = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
event: React.ChangeEvent<HTMLInputElement>,
|
|
||||||
) => {
|
|
||||||
setRowsPerPage(parseInt(event.target.value, 10));
|
setRowsPerPage(parseInt(event.target.value, 10));
|
||||||
setPage(0);
|
setPage(0);
|
||||||
};
|
};
|
@ -10,7 +10,7 @@ import TableHead from "@mui/material/TableHead";
|
|||||||
import TableRow from "@mui/material/TableRow";
|
import TableRow from "@mui/material/TableRow";
|
||||||
import TableSortLabel from "@mui/material/TableSortLabel";
|
import TableSortLabel from "@mui/material/TableSortLabel";
|
||||||
import { visuallyHidden } from "@mui/utils";
|
import { visuallyHidden } from "@mui/utils";
|
||||||
import NodeRow from "./NodeRow";
|
import { NodeRow } from "./nodeRow";
|
||||||
|
|
||||||
import { TableData } from "@/data/nodeData";
|
import { TableData } from "@/data/nodeData";
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ interface NodeTableContainerProps {
|
|||||||
setSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
|
setSelected: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NodeTableContainer(props: NodeTableContainerProps) {
|
export function NodeTableContainer(props: NodeTableContainerProps) {
|
||||||
const { tableData, page, rowsPerPage, dense, selected, setSelected } = props;
|
const { tableData, page, rowsPerPage, dense, selected, setSelected } = props;
|
||||||
const [order, setOrder] = React.useState<NodeOrder>("asc");
|
const [order, setOrder] = React.useState<NodeOrder>("asc");
|
||||||
const [orderBy, setOrderBy] = React.useState<keyof TableData>("status");
|
const [orderBy, setOrderBy] = React.useState<keyof TableData>("status");
|
||||||
@ -184,7 +184,7 @@ export default function NodeTableContainer(props: NodeTableContainerProps) {
|
|||||||
{visibleRows.map((row, index) => {
|
{visibleRows.map((row, index) => {
|
||||||
return (
|
return (
|
||||||
<NodeRow
|
<NodeRow
|
||||||
key={row.id}
|
key={row.name}
|
||||||
row={row}
|
row={row}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
setSelected={setSelected}
|
setSelected={setSelected}
|
73
pkgs/ui/src/components/table/pieCards.tsx
Normal file
73
pkgs/ui/src/components/table/pieCards.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { Card, CardContent, Stack, Typography } from "@mui/material";
|
||||||
|
import hexRgb from "hex-rgb";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
interface PieData {
|
||||||
|
name: string;
|
||||||
|
value: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PieCardsProps {
|
||||||
|
pieData: PieData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PieCards(props: PieCardsProps) {
|
||||||
|
const { pieData } = props;
|
||||||
|
|
||||||
|
const cardData = useMemo(() => {
|
||||||
|
return pieData
|
||||||
|
.filter((pieItem) => pieItem.value > 0)
|
||||||
|
.concat({
|
||||||
|
name: "Total",
|
||||||
|
value: pieData.reduce((a, b) => a + b.value, 0),
|
||||||
|
color: "#000000",
|
||||||
|
});
|
||||||
|
}, [pieData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack
|
||||||
|
sx={{ paddingTop: 6 }}
|
||||||
|
height={350}
|
||||||
|
id="cardBox"
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
|
justifyContent="flex-start"
|
||||||
|
flexWrap="wrap"
|
||||||
|
>
|
||||||
|
{cardData.map((pieItem) => (
|
||||||
|
<Card
|
||||||
|
key={pieItem.name}
|
||||||
|
sx={{
|
||||||
|
marginBottom: 2,
|
||||||
|
marginRight: 2,
|
||||||
|
width: 110,
|
||||||
|
height: 110,
|
||||||
|
backgroundColor: hexRgb(pieItem.color, {
|
||||||
|
format: "css",
|
||||||
|
alpha: 0.25,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CardContent>
|
||||||
|
<Typography
|
||||||
|
variant="h4"
|
||||||
|
component="div"
|
||||||
|
gutterBottom={true}
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
{pieItem.value}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
sx={{ mb: 1.5 }}
|
||||||
|
color="text.secondary"
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
{pieItem.name}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
@ -1,57 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { alpha } from "@mui/material/styles";
|
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Table from "@mui/material/Table";
|
|
||||||
import TableBody from "@mui/material/TableBody";
|
|
||||||
import TableCell from "@mui/material/TableCell";
|
|
||||||
import TableContainer from "@mui/material/TableContainer";
|
|
||||||
import TableHead from "@mui/material/TableHead";
|
|
||||||
import TablePagination from "@mui/material/TablePagination";
|
|
||||||
import TableRow from "@mui/material/TableRow";
|
|
||||||
import TableSortLabel from "@mui/material/TableSortLabel";
|
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
import Paper from "@mui/material/Paper";
|
|
||||||
import IconButton from "@mui/material/IconButton";
|
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
|
||||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
|
||||||
import Switch from "@mui/material/Switch";
|
|
||||||
import DeleteIcon from "@mui/icons-material/Delete";
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
import FilterListIcon from "@mui/icons-material/FilterList";
|
|
||||||
import SpeedDial, { CloseReason, OpenReason } from "@mui/material/SpeedDial";
|
import SpeedDial, { CloseReason, OpenReason } from "@mui/material/SpeedDial";
|
||||||
import SpeedDialIcon from "@mui/material/SpeedDialIcon";
|
import SpeedDialIcon from "@mui/material/SpeedDialIcon";
|
||||||
import SpeedDialAction from "@mui/material/SpeedDialAction";
|
import SpeedDialAction from "@mui/material/SpeedDialAction";
|
||||||
import { visuallyHidden } from "@mui/utils";
|
|
||||||
import CircleIcon from "@mui/icons-material/Circle";
|
|
||||||
import Stack from "@mui/material/Stack/Stack";
|
|
||||||
import EditIcon from "@mui/icons-material/ModeEdit";
|
import EditIcon from "@mui/icons-material/ModeEdit";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
|
||||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
|
||||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
|
||||||
import NodePieChart, { PieData } from "./NodePieChart";
|
|
||||||
import Fab from "@mui/material/Fab";
|
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import Grid2 from "@mui/material/Unstable_Grid2"; // Grid version 2
|
export function StickySpeedDial(props: { selected: string | undefined }) {
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Collapse,
|
|
||||||
Container,
|
|
||||||
FormGroup,
|
|
||||||
useTheme,
|
|
||||||
} from "@mui/material";
|
|
||||||
import hexRgb from "hex-rgb";
|
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
||||||
import { NodeStatus, NodeStatusKeys, TableData } from "@/data/nodeData";
|
|
||||||
import { jsx } from "@emotion/react";
|
|
||||||
|
|
||||||
export default function StickySpeedDial(props: {
|
|
||||||
selected: string | undefined;
|
|
||||||
}) {
|
|
||||||
const { selected } = props;
|
const { selected } = props;
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
|
|
||||||
@ -72,7 +31,7 @@ export default function StickySpeedDial(props: {
|
|||||||
function editDial() {
|
function editDial() {
|
||||||
if (isSomethingSelected) {
|
if (isSomethingSelected) {
|
||||||
return (
|
return (
|
||||||
<Link href="/nodes/edit" style={{ marginTop: 7.5 }}>
|
<Link href={`/machines/edit/${selected}`} style={{ marginTop: 7.5 }}>
|
||||||
<EditIcon color="action" />
|
<EditIcon color="action" />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
@ -105,7 +64,7 @@ export default function StickySpeedDial(props: {
|
|||||||
<SpeedDialAction
|
<SpeedDialAction
|
||||||
key="Add"
|
key="Add"
|
||||||
icon={
|
icon={
|
||||||
<Link href="/nodes/add" style={{ marginTop: 7.5 }}>
|
<Link href="/machines/add" style={{ marginTop: 7.5 }}>
|
||||||
<AddIcon color="action" />
|
<AddIcon color="action" />
|
||||||
</Link>
|
</Link>
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
export interface TableData {
|
export interface TableData {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
|
||||||
status: NodeStatusKeys;
|
status: NodeStatusKeys;
|
||||||
last_seen: number;
|
last_seen: number;
|
||||||
}
|
}
|
||||||
@ -15,7 +14,6 @@ export type NodeStatusKeys = (typeof NodeStatus)[keyof typeof NodeStatus];
|
|||||||
|
|
||||||
function createData(
|
function createData(
|
||||||
name: string,
|
name: string,
|
||||||
id: string,
|
|
||||||
status: NodeStatusKeys,
|
status: NodeStatusKeys,
|
||||||
last_seen: number,
|
last_seen: number,
|
||||||
): TableData {
|
): TableData {
|
||||||
@ -25,7 +23,6 @@ function createData(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
id,
|
|
||||||
status,
|
status,
|
||||||
last_seen: last_seen,
|
last_seen: last_seen,
|
||||||
};
|
};
|
||||||
@ -95,72 +92,62 @@ function getRandomLastSeen(status: NodeStatusKeys): number {
|
|||||||
export const tableData = [
|
export const tableData = [
|
||||||
createData(
|
createData(
|
||||||
"Matchbox",
|
"Matchbox",
|
||||||
"42:0:f21:6916:e333:c47e:4b5c:e74c",
|
|
||||||
NodeStatus.Pending,
|
NodeStatus.Pending,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Ahorn",
|
"Ahorn",
|
||||||
"42:0:3c46:b51c:b34d:b7e1:3b02:8d24",
|
|
||||||
NodeStatus.Online,
|
NodeStatus.Online,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Yellow",
|
"Yellow",
|
||||||
"42:0:3c46:98ac:9c80:4f25:50e3:1d8f",
|
|
||||||
NodeStatus.Offline,
|
NodeStatus.Offline,
|
||||||
16.0,
|
16.0,
|
||||||
),
|
),
|
||||||
createData(
|
createData("Rauter", NodeStatus.Offline, 6.0),
|
||||||
"Rauter",
|
|
||||||
"42:0:61ea:b777:61ea:803:f885:3523",
|
|
||||||
NodeStatus.Offline,
|
|
||||||
6.0,
|
|
||||||
),
|
|
||||||
createData(
|
createData(
|
||||||
"Porree",
|
"Porree",
|
||||||
"42:0:e644:4499:d034:895e:34c8:6f9a",
|
|
||||||
NodeStatus.Offline,
|
NodeStatus.Offline,
|
||||||
13,
|
13,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Helsinki",
|
"Helsinki",
|
||||||
"42:0:3c46:fd4a:acf9:e971:6036:8047",
|
|
||||||
NodeStatus.Online,
|
NodeStatus.Online,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Kelle",
|
"Kelle",
|
||||||
"42:0:3c46:362d:a9aa:4996:c78e:839a",
|
|
||||||
NodeStatus.Online,
|
NodeStatus.Online,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Shodan",
|
"Shodan",
|
||||||
"42:0:3c46:6745:adf4:a844:26c4:bf91",
|
|
||||||
NodeStatus.Online,
|
NodeStatus.Online,
|
||||||
0.0,
|
0.0,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Qubasa",
|
"Qubasa",
|
||||||
"42:0:3c46:123e:bbea:3529:db39:6764",
|
|
||||||
NodeStatus.Offline,
|
NodeStatus.Offline,
|
||||||
7.0,
|
7.0,
|
||||||
),
|
),
|
||||||
createData(
|
createData(
|
||||||
"Green",
|
"Green",
|
||||||
"42:0:a46e:5af:632c:d2fe:a71d:cde0",
|
|
||||||
NodeStatus.Offline,
|
NodeStatus.Offline,
|
||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
createData("Gum", "42:0:e644:238d:3e46:c884:6ec5:16c", NodeStatus.Offline, 0),
|
createData("Gum", NodeStatus.Offline, 0),
|
||||||
createData("Xu", "42:0:ca48:c2c2:19fb:a0e9:95b9:794f", NodeStatus.Online, 0),
|
createData("Xu", NodeStatus.Online, 0),
|
||||||
createData(
|
createData("Zaatar", NodeStatus.Online, 0),
|
||||||
"Zaatar",
|
|
||||||
"42:0:3c46:156e:10b6:3bd6:6e82:b2cd",
|
|
||||||
NodeStatus.Online,
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// A function to execute the createData function with dummy data in a loop 100 times and return an array
|
// A function to execute the createData function with dummy data in a loop 100 times and return an array
|
||||||
@ -174,7 +161,7 @@ export function executeCreateData(): TableData[] {
|
|||||||
let last_seen = getRandomLastSeen(status);
|
let last_seen = getRandomLastSeen(status);
|
||||||
|
|
||||||
// Call the createData function and push the result to the array
|
// Call the createData function and push the result to the array
|
||||||
result.push(createData(name, id, status, last_seen));
|
result.push(createData(name, status, last_seen));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -1,601 +1,589 @@
|
|||||||
export const tableData = [
|
export const tableData = [
|
||||||
{
|
{
|
||||||
name: "Bob",
|
name: "Bob",
|
||||||
id: "f435:9678:eff4:cedf:7725:421c:82c0:611c",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 83,
|
last_seen: 83,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Peggy",
|
name: "Peggy With %",
|
||||||
id: "aa49:f231:04ac:f0d2:cf9a:4635:57e3:4b78",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 294,
|
last_seen: 294,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wendy",
|
name: "Wendy",
|
||||||
id: "a453:4ec6:c401:30dc:89a0:1567:90c9:4a72",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 332,
|
last_seen: 332,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "3f14:92c3:1090:bef0:7dc6:9eaf:755c:6f7f",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 48,
|
last_seen: 48,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "855e:d1ff:d20c:d1b8:86c4:9ffe:ca2a:fefa",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 292,
|
last_seen: 292,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "7bfb:3980:dc1a:37e2:2464:6028:393b:315f",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 240,
|
last_seen: 240,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mallory",
|
name: "Mallory",
|
||||||
id: "d457:0a59:ea77:963f:f20b:ca55:57f2:a54b",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mallory",
|
name: "Mallory",
|
||||||
id: "bbef:a90a:f7ff:5f40:23fc:a1d8:6b2f:7b0a",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 95,
|
last_seen: 95,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zoe",
|
name: "Zoe",
|
||||||
id: "1057:7b4d:0708:66a1:5d36:5d8c:6620:4f5f",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "74d3:60dc:5e12:dc52:7c7f:6903:4ff1:ba20",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 193,
|
last_seen: 193,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "6770:c213:bdd1:0226:26e0:a8d4:949a:f2cc",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 181,
|
last_seen: 181,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bob",
|
name: "Bob",
|
||||||
id: "5551:7363:ba70:6927:57f3:24ea:386d:e2d9",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "12eb:9b2b:0c94:0731:15e6:7cd1:8984:7669",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 130,
|
last_seen: 130,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "785e:b8a0:289b:5108:a982:ecff:8f0b:9db2",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 336,
|
last_seen: 336,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "ee4b:ce86:4885:2729:089e:7f93:d3dd:7a09",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 241,
|
last_seen: 241,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "6c05:c65d:ef3b:be64:ae58:e1fd:7aaa:bd30",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Alice",
|
name: "Alice",
|
||||||
id: "0fe6:f3b1:58c2:ef75:12de:6514:d1a6:eda5",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 94,
|
last_seen: 94,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "095b:1b3f:cfeb:72f8:e84c:91f7:2c62:06e9",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 131,
|
last_seen: 131,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Walter",
|
name: "Walter",
|
||||||
id: "8d0a:56b0:1537:1b2e:9581:d640:cd29:21cf",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 273,
|
last_seen: 273,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Walter",
|
name: "Walter",
|
||||||
id: "d8ab:813a:cbc6:d379:d3be:cc4b:20b8:94ae",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 200,
|
last_seen: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Trent",
|
name: "Trent",
|
||||||
id: "0199:cb5c:a8cd:af82:2d9a:e63a:a157:6ee6",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 352,
|
last_seen: 352,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Peggy",
|
name: "Peggy",
|
||||||
id: "1b7b:8da6:623a:75ea:2385:8777:76b9:9ba8",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zoe",
|
name: "Zoe",
|
||||||
id: "6ed3:ee3b:f7df:044a:355a:7790:0bf3:a308",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wendy",
|
name: "Wendy",
|
||||||
id: "efed:821d:a944:0775:ead4:e520:6402:8dc4",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Heidi",
|
name: "Heidi",
|
||||||
id: "b616:82c5:4a37:c020:9568:aaa4:6390:22d5",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "2c05:e6fc:a359:bf63:65e7:ee0e:49e3:6cc5",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "e9cb:eddc:1a94:22e4:4e3d:e927:c599:db04",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bob",
|
name: "Bob",
|
||||||
id: "be03:3537:719f:5152:f277:03ef:c6c9:c953",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 69,
|
last_seen: 69,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Oscar",
|
name: "Oscar",
|
||||||
id: "cede:fa4a:518d:b885:d1fa:00f4:d97a:2258",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 280,
|
last_seen: 280,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "9337:53e7:1aae:1b94:6035:e601:e562:350c",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "d50b:0ddc:0c9d:cd34:40ee:34c9:75ff:d16c",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "368f:8855:59ed:b8c3:4fff:7630:9948:877f",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 63,
|
last_seen: 63,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "0fcd:74ae:8dad:8d10:370b:46c5:a403:eab8",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 242,
|
last_seen: 242,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wendy",
|
name: "Wendy",
|
||||||
id: "285f:3015:2f09:2fde:25ee:87be:6d2c:f4f3",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 60,
|
last_seen: 60,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "942d:9c5c:1f4a:95ba:1046:c643:a874:83ce",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "06f5:5b67:98e9:31b4:ddba:fb81:afb1:7677",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 154,
|
last_seen: 154,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zoe",
|
name: "Zoe",
|
||||||
id: "ec58:13b0:b7ef:6e19:6c78:2c35:4fa3:092a",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 264,
|
last_seen: 264,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "7151:20e3:3969:2933:c23b:6d9a:9723:d1bd",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David",
|
name: "David",
|
||||||
id: "0466:bd3f:0fff:2119:b9c0:ee60:2a90:6b54",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David",
|
name: "David",
|
||||||
id: "814d:b00c:4a98:7aa2:5354:7076:f48e:1609",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 249,
|
last_seen: 249,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David",
|
name: "David",
|
||||||
id: "8633:f51b:b643:829a:08b0:fc2a:7cad:abda",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 359,
|
last_seen: 359,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Eve",
|
name: "Eve",
|
||||||
id: "0fd5:7b72:4445:0e27:95c6:1b80:99dc:6590",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wendy",
|
name: "Wendy",
|
||||||
id: "dc9b:bac9:8ec8:9167:5da5:8530:fcb7:1458",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bob",
|
name: "Bob",
|
||||||
id: "0412:6da1:82ef:6cb1:467e:34e9:34df:5742",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "d03b:aba5:ee40:1961:f824:0d9b:9669:2e3e",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zoe",
|
name: "Zoe",
|
||||||
id: "ad06:abfd:05c2:fc17:a8e0:857e:b04c:d3fe",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 25,
|
last_seen: 25,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "d2ca:12cb:d271:44a2:2813:e826:964b:b292",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 11,
|
last_seen: 11,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "aaa9:9754:34e2:7251:af6e:df7a:5422:96cd",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 131,
|
last_seen: 131,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mallory",
|
name: "Mallory",
|
||||||
id: "8511:8f44:45d7:cc8a:43ff:908c:a35f:19bc",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 141,
|
last_seen: 141,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Alice",
|
name: "Alice",
|
||||||
id: "8bc2:8f83:3081:d90c:087e:13bf:002b:934b",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Peggy",
|
name: "Peggy",
|
||||||
id: "9b33:5e44:310f:f6ab:81ae:df8e:be7b:ddc4",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 354,
|
last_seen: 354,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "9776:1f62:b8dc:1d14:bf9c:bf41:99b7:5c11",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David",
|
name: "David",
|
||||||
id: "874a:a4df:321a:5367:4b63:4a0c:48d6:0a0d",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 199,
|
last_seen: 199,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "05f0:7188:a2fb:cfe3:7d85:e3de:d6c2:07e9",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 46,
|
last_seen: 46,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Grace",
|
name: "Grace",
|
||||||
id: "dcbd:5ddf:496d:dd27:0e98:9905:7c24:664d",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 284,
|
last_seen: 284,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "3cb9:d178:2cb1:ae2c:dd3d:ce93:34e0:cc68",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "c974:9ef6:df64:188c:4622:aa71:497b:1fd8",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 18,
|
last_seen: 18,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "3ba0:9362:3b2f:ec10:03c2:7bd7:67ad:759c",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Walter",
|
name: "Walter",
|
||||||
id: "b57d:3d13:6654:d944:0e7c:61d6:9dc0:15d8",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "de00:f53f:0be4:8a57:047a:f99d:8ab2:fe7c",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 41,
|
last_seen: 41,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Trent",
|
name: "Trent",
|
||||||
id: "bb32:acd8:bbfb:ea3e:1b58:dc17:4cc4:64ce",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 326,
|
last_seen: 326,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Oscar",
|
name: "Oscar",
|
||||||
id: "a3e9:1060:b0df:2adb:a340:3f34:5348:d882",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 57,
|
last_seen: 57,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wendy",
|
name: "Wendy",
|
||||||
id: "6b49:c86c:41c8:c021:2103:b707:26a9:6d6a",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "aaf2:68cb:da7e:0844:8a12:5fb9:cff5:9de8",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "b87d:0cc5:e3be:a600:045e:4a26:f7e2:6b3d",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 342,
|
last_seen: 342,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "13e6:1b80:ba7e:6275:21c8:dc96:6ab1:ac69",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 213,
|
last_seen: 213,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "14ad:2010:7648:fbc8:337c:6984:d7e2:5202",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "f79c:e3b8:1082:7a9d:7adf:f5b6:e333:affd",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "534c:0d80:97d5:7b54:a1c3:5457:c5e8:62ee",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 133,
|
last_seen: 133,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "e829:f930:b7d7:6005:cc34:7bca:0163:7903",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "18c3:6ab2:93ea:0756:52e4:33a6:0c2b:bd79",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 109,
|
last_seen: 109,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Eve",
|
name: "Eve",
|
||||||
id: "49a3:28ad:6121:2e35:1b72:e5d7:5efc:0626",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Trent",
|
name: "Trent",
|
||||||
id: "d900:540b:b764:2468:ad9e:2716:ab4f:9955",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 117,
|
last_seen: 117,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zoe",
|
name: "Zoe",
|
||||||
id: "9d3d:de15:014a:ac0d:74b9:2ae8:08d4:8848",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 114,
|
last_seen: 114,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Peggy",
|
name: "Peggy",
|
||||||
id: "2145:0e6a:f66a:8e93:71b7:385d:c25b:6a2b",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 71,
|
last_seen: 71,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "834c:adcc:a3d7:b5fc:d5cd:4209:e280:e625",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 200,
|
last_seen: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Heidi",
|
name: "Heidi",
|
||||||
id: "5ecc:904c:2d31:177b:346d:4c23:5169:a982",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Ivan",
|
name: "Ivan",
|
||||||
id: "0446:9092:762c:48b6:310a:e6fb:be48:4631",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "David",
|
name: "David",
|
||||||
id: "160c:97aa:f4ff:52d0:717e:2263:7262:90ed",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 275,
|
last_seen: 275,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Charlie",
|
name: "Charlie",
|
||||||
id: "6832:d934:2a76:27c0:88ea:2b05:5bf8:4f86",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Eve",
|
name: "Eve",
|
||||||
id: "5363:e485:d9b9:89a8:3ee4:5d24:b2b5:2ab5",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 343,
|
last_seen: 343,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Peggy",
|
name: "Peggy",
|
||||||
id: "5673:e221:3236:0a33:29a9:5c5f:ff03:c98d",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 130,
|
last_seen: 130,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Grace",
|
name: "Grace",
|
||||||
id: "8946:27f1:2abe:f009:b109:f0af:92d1:4c5b",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 72,
|
last_seen: 72,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Walter",
|
name: "Walter",
|
||||||
id: "7bc3:a683:660d:ffc3:a40f:b8ab:2246:ed38",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 264,
|
last_seen: 264,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mallory",
|
name: "Mallory",
|
||||||
id: "5b5d:129e:8c3e:4f34:94bf:ffe5:7a8a:b3ae",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Eve",
|
name: "Eve",
|
||||||
id: "d7db:981a:8885:838b:70b6:a691:1c26:4d59",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 172,
|
last_seen: 172,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Peggy",
|
name: "Peggy",
|
||||||
id: "1255:426b:034c:0171:d67e:ef4c:d9a5:7da6",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 81,
|
last_seen: 81,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Frank",
|
name: "Frank",
|
||||||
id: "1c79:ef2f:c01a:d53b:016d:e2fc:3566:6b85",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "b159:2924:969d:e1f6:e295:f19d:9bf2:8f58",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 221,
|
last_seen: 221,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "bba4:85aa:bcc7:5ef6:920c:7d74:e921:7d47",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 168,
|
last_seen: 168,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "dc3f:5b3a:3e71:552a:9cdb:10f2:699a:d8e9",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 217,
|
last_seen: 217,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "e084:f4cf:be5b:83c1:f1f5:1159:d24d:dea2",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Trent",
|
name: "Trent",
|
||||||
id: "113c:335a:c844:7307:b192:1c44:fb34:4cc0",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 77,
|
last_seen: 77,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sybil",
|
name: "Sybil",
|
||||||
id: "05a0:cbe8:4b89:47e3:81e9:39b7:4966:bbed",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 145,
|
last_seen: 145,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Alice",
|
name: "Alice",
|
||||||
id: "0f33:461d:c779:067e:424e:b933:c855:c376",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Trent",
|
name: "Trent",
|
||||||
id: "a883:16c8:abb4:3ab3:7d6f:beb4:1398:bc9c",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 189,
|
last_seen: 189,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Wendy",
|
name: "Wendy",
|
||||||
id: "2e7c:1162:1488:6a1e:34fe:0725:bd56:4461",
|
|
||||||
status: "Pending",
|
status: "Pending",
|
||||||
last_seen: 338,
|
last_seen: 338,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Judy",
|
name: "Judy",
|
||||||
id: "c4e1:6d39:5079:097b:3228:1391:4f59:1be6",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 352,
|
last_seen: 352,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Victor",
|
name: "Victor",
|
||||||
id: "d32c:1045:4297:251a:6ec8:16c8:541d:7925",
|
|
||||||
status: "Offline",
|
status: "Offline",
|
||||||
last_seen: 61,
|
last_seen: 61,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Zoe",
|
name: "Zoe",
|
||||||
id: "b6cf:6f86:f510:9f39:93a9:aba9:4632:bb69",
|
|
||||||
status: "Online",
|
status: "Online",
|
||||||
last_seen: 0,
|
last_seen: 0,
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"],
|
||||||
"API": ["./api/*"]
|
"@API/*": ["./src/api/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
Loading…
Reference in New Issue
Block a user