mirror of https://github.com/cocosolos/ixion.git
Change ServerCard to accept children
This allows me to pass in chips for summary view or data grid for detailed view. Disable accordion features when data grid child is present.
This commit is contained in:
parent
5d041d5e77
commit
ddf74e8343
|
|
@ -1,5 +1,5 @@
|
||||||
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
|
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||||
import { ServerData } from '../data/ServerData';
|
import { ServerData } from '../../data/ServerData';
|
||||||
|
|
||||||
const expansions = [
|
const expansions = [
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { Check, Close } from '@mui/icons-material';
|
||||||
|
import { Box, Chip, Tooltip } from '@mui/material';
|
||||||
|
import {
|
||||||
|
ServerData,
|
||||||
|
ServerSetting,
|
||||||
|
ServerSettingsInfo,
|
||||||
|
} from '../../data/ServerData';
|
||||||
|
|
||||||
|
export default function SettingsChipCloud({ server }: { server: ServerData }) {
|
||||||
|
const renderSettingsChip = ([key, value]: [
|
||||||
|
string,
|
||||||
|
boolean | string | number,
|
||||||
|
]) => {
|
||||||
|
if (!ServerSettingsInfo[key]) return null;
|
||||||
|
|
||||||
|
const transformValue = (
|
||||||
|
v: boolean | string | number,
|
||||||
|
setting: ServerSetting
|
||||||
|
): string | number | boolean => {
|
||||||
|
return setting.transform?.(v) ?? v;
|
||||||
|
};
|
||||||
|
|
||||||
|
let chipValue: string | number | boolean | JSX.Element = transformValue(
|
||||||
|
value,
|
||||||
|
ServerSettingsInfo[key]
|
||||||
|
);
|
||||||
|
if (typeof chipValue === 'boolean') {
|
||||||
|
chipValue = chipValue ? (
|
||||||
|
<Check color="success" />
|
||||||
|
) : (
|
||||||
|
<Close color="error" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
key={key}
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
title={ServerSettingsInfo[key].description}
|
||||||
|
>
|
||||||
|
<Chip
|
||||||
|
label={`${ServerSettingsInfo[key].name === '' ? key : ServerSettingsInfo[key].name}`}
|
||||||
|
avatar={
|
||||||
|
typeof chipValue === 'object' ? undefined : (
|
||||||
|
<Chip label={chipValue} size="small" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
icon={typeof chipValue === 'object' ? chipValue : undefined}
|
||||||
|
size="small"
|
||||||
|
className="m-1 pr-1"
|
||||||
|
sx={{
|
||||||
|
'& .MuiChip-avatar': {
|
||||||
|
width: 'auto',
|
||||||
|
marginX: 0,
|
||||||
|
order: 2,
|
||||||
|
},
|
||||||
|
'& .MuiChip-icon': {
|
||||||
|
marginX: 0,
|
||||||
|
order: 2,
|
||||||
|
},
|
||||||
|
'&> .MuiChip-label': {
|
||||||
|
paddingRight: 0.5,
|
||||||
|
},
|
||||||
|
boxShadow: '0px 3px 3px rgba(0, 0, 0, .25)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'center',
|
||||||
|
maxWidth: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Object.entries(server.customizations).map(renderSettingsChip)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ import {
|
||||||
ServerSetting,
|
ServerSetting,
|
||||||
ServerSettings,
|
ServerSettings,
|
||||||
ServerSettingsInfo,
|
ServerSettingsInfo,
|
||||||
} from '../data/ServerData';
|
} from '../../data/ServerData';
|
||||||
|
|
||||||
type KeyValueRow = {
|
type KeyValueRow = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -23,11 +23,13 @@ const columns: GridColDef[] = [
|
||||||
{ field: 'description', headerName: 'Description', flex: 6 },
|
{ field: 'description', headerName: 'Description', flex: 6 },
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function ServerSettingsDataGrid({
|
type SettingsDataGridProps = {
|
||||||
serverSettings,
|
|
||||||
}: {
|
|
||||||
serverSettings: ServerSettings;
|
serverSettings: ServerSettings;
|
||||||
}) {
|
};
|
||||||
|
|
||||||
|
export default function SettingsDataGrid({
|
||||||
|
serverSettings,
|
||||||
|
}: SettingsDataGridProps) {
|
||||||
const transformValue = (
|
const transformValue = (
|
||||||
v: boolean | string | number,
|
v: boolean | string | number,
|
||||||
setting: ServerSetting
|
setting: ServerSetting
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
Check,
|
ArrowCircleUp,
|
||||||
Close,
|
|
||||||
ContentCopy,
|
ContentCopy,
|
||||||
ExpandMore,
|
ExpandMore,
|
||||||
Info,
|
Info,
|
||||||
|
|
@ -13,29 +12,40 @@ import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
Chip,
|
|
||||||
Divider,
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
alpha,
|
alpha,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useState } from 'react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import {
|
import {
|
||||||
ServerData,
|
Children,
|
||||||
ServerSetting,
|
ElementType,
|
||||||
ServerSettingsInfo,
|
ReactNode,
|
||||||
} from '../data/ServerData';
|
isValidElement,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { ServerData } from '../data/ServerData';
|
||||||
import CopyImageIcon from '../images/copy-image.png';
|
import CopyImageIcon from '../images/copy-image.png';
|
||||||
import { Accordion, AccordionDetails, AccordionSummary } from './Accordion';
|
import ExpansionBar from './Server/ExpansionsBar';
|
||||||
import ExpansionBar from './ExpansionsBar';
|
import SettingsDataGrid from './Server/SettingsDataGrid';
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionDetails,
|
||||||
|
AccordionSummary,
|
||||||
|
} from './Themed/Accordion';
|
||||||
|
|
||||||
function scrollToTop() {
|
function scrollToTop() {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ServerCard({ server }: { server: ServerData }) {
|
type ServerCardProps = {
|
||||||
|
server: ServerData;
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ServerCard({ server, children }: ServerCardProps) {
|
||||||
const [clipboardTooltip, setClipboardTooltip] = useState('Copy server URL.');
|
const [clipboardTooltip, setClipboardTooltip] = useState('Copy server URL.');
|
||||||
const [clipboardTooltipOpen, setClipboardTooltipOpen] = useState(false);
|
const [clipboardTooltipOpen, setClipboardTooltipOpen] = useState(false);
|
||||||
const handleClipboardTooltipClose = () => {
|
const handleClipboardTooltipClose = () => {
|
||||||
|
|
@ -77,72 +87,39 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderSettingsChip = ([key, value]: [
|
// Check if this is a detailed server card with a data grid
|
||||||
string,
|
const hasSpecificComponent = (
|
||||||
boolean | string | number,
|
childrenProps: ReactNode,
|
||||||
]) => {
|
componentType: ElementType
|
||||||
if (!ServerSettingsInfo[key]) return null;
|
): boolean => {
|
||||||
|
const childrenArray = Children.toArray(childrenProps);
|
||||||
const transformValue = (
|
return childrenArray.some(
|
||||||
v: boolean | string | number,
|
(child) => isValidElement(child) && child.type === componentType
|
||||||
setting: ServerSetting
|
);
|
||||||
): string | number | boolean => {
|
|
||||||
return setting.transform?.(v) ?? v;
|
|
||||||
};
|
};
|
||||||
|
const isSettingsDataGridChild = hasSpecificComponent(
|
||||||
let chipValue: string | number | boolean | JSX.Element = transformValue(
|
children,
|
||||||
value,
|
SettingsDataGrid
|
||||||
ServerSettingsInfo[key]
|
|
||||||
);
|
|
||||||
if (typeof chipValue === 'boolean') {
|
|
||||||
chipValue = chipValue ? (
|
|
||||||
<Check color="success" />
|
|
||||||
) : (
|
|
||||||
<Close color="error" />
|
|
||||||
);
|
);
|
||||||
|
// Manually handle the accordion state to disable closing when detailed view
|
||||||
|
const [expand, setExpand] = useState(isSettingsDataGridChild);
|
||||||
|
const toggleAcordion = () => {
|
||||||
|
if (!isSettingsDataGridChild) {
|
||||||
|
setExpand((prev) => !prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
key={key}
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
title={ServerSettingsInfo[key].description}
|
|
||||||
>
|
|
||||||
<Chip
|
|
||||||
label={`${ServerSettingsInfo[key].name === '' ? key : ServerSettingsInfo[key].name}`}
|
|
||||||
avatar={
|
|
||||||
typeof chipValue === 'object' ? undefined : (
|
|
||||||
<Chip label={chipValue} size="small" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
icon={typeof chipValue === 'object' ? chipValue : undefined}
|
|
||||||
size="small"
|
|
||||||
className="m-1 pr-1"
|
|
||||||
sx={{
|
|
||||||
'& .MuiChip-avatar': {
|
|
||||||
width: 'auto',
|
|
||||||
marginX: 0,
|
|
||||||
order: 2,
|
|
||||||
},
|
|
||||||
'& .MuiChip-icon': {
|
|
||||||
marginX: 0,
|
|
||||||
order: 2,
|
|
||||||
},
|
|
||||||
'&> .MuiChip-label': {
|
|
||||||
paddingRight: 0.5,
|
|
||||||
},
|
|
||||||
boxShadow: '0px 3px 3px rgba(0, 0, 0, .25)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="mb-2">
|
<Card className="mb-2">
|
||||||
<Accordion className="my-0">
|
<Accordion
|
||||||
<AccordionSummary expandIcon={<ExpandMore />}>
|
className="my-0"
|
||||||
|
defaultExpanded={isSettingsDataGridChild}
|
||||||
|
expanded={expand}
|
||||||
|
>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={!isSettingsDataGridChild && <ExpandMore />}
|
||||||
|
onClick={toggleAcordion}
|
||||||
|
>
|
||||||
<Box className="flex flex-col items-center justify-center">
|
<Box className="flex flex-col items-center justify-center">
|
||||||
<Box className="flex content-center">
|
<Box className="flex content-center">
|
||||||
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
||||||
|
|
@ -290,16 +267,7 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
<Divider sx={{ marginY: 1 }} />
|
<Divider sx={{ marginY: 1 }} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Box
|
{children}
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
justifyContent: 'center',
|
|
||||||
maxWidth: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.entries(server.customizations).map(renderSettingsChip)}
|
|
||||||
</Box>
|
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
@ -328,7 +296,13 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
</Box>
|
</Box>
|
||||||
<Box className="flex items-center whitespace-nowrap">
|
<Box className="flex items-center whitespace-nowrap">
|
||||||
{/* <ServerDetailsModal id={server.id} /> */}
|
{/* <ServerDetailsModal id={server.id} /> */}
|
||||||
<Tooltip title="View full settings." arrow disableInteractive>
|
<Tooltip
|
||||||
|
title={
|
||||||
|
isSettingsDataGridChild ? 'Scroll to top.' : 'View full settings.'
|
||||||
|
}
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
component={Link}
|
component={Link}
|
||||||
to={`/server/${encodeURIComponent(server.url)}`}
|
to={`/server/${encodeURIComponent(server.url)}`}
|
||||||
|
|
@ -336,7 +310,11 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
className="p-0"
|
className="p-0"
|
||||||
disableRipple
|
disableRipple
|
||||||
>
|
>
|
||||||
|
{isSettingsDataGridChild ? (
|
||||||
|
<ArrowCircleUp className="p-1" />
|
||||||
|
) : (
|
||||||
<Info className="p-1" />
|
<Info className="p-1" />
|
||||||
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Typography variant="caption">
|
<Typography variant="caption">
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
import { Info } from '@mui/icons-material';
|
|
||||||
import { Card, IconButton, Tooltip, Typography } from '@mui/material';
|
|
||||||
import Modal from '@mui/material/Modal';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { fetchDataFromBackend } from '../apiUtil';
|
|
||||||
import { ServerData } from '../data/ServerData';
|
|
||||||
import ServerSettingsDataGrid from './ServerSettingsDataGrid';
|
|
||||||
|
|
||||||
export default function ServerDetailsModal({ id }: { id: number }) {
|
|
||||||
const [server, setServer] = useState<ServerData>();
|
|
||||||
const [error, setError] = useState<string>('');
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const handleOpen = () => {
|
|
||||||
let data: ServerData;
|
|
||||||
const fetchServerData = async () => {
|
|
||||||
try {
|
|
||||||
data = await fetchDataFromBackend(`server/${id}`);
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
setError(err.message);
|
|
||||||
} else {
|
|
||||||
setError('An unknown error occurred.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setServer(data);
|
|
||||||
};
|
|
||||||
fetchServerData();
|
|
||||||
setOpen(true);
|
|
||||||
};
|
|
||||||
const handleClose = () => setOpen(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Tooltip title="View full settings." arrow disableInteractive>
|
|
||||||
<IconButton className="p-0" disableRipple onClick={handleOpen}>
|
|
||||||
<Info className="p-1" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Modal
|
|
||||||
open={open}
|
|
||||||
onClose={handleClose}
|
|
||||||
aria-labelledby="modal-modal-title"
|
|
||||||
aria-describedby="modal-modal-description"
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
sx={{
|
|
||||||
position: 'absolute' as const,
|
|
||||||
top: '10%',
|
|
||||||
left: '10%',
|
|
||||||
width: '80%',
|
|
||||||
height: '80%',
|
|
||||||
overflow: 'hidden',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
boxShadow: 24,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
sx={{
|
|
||||||
position: 'absolute' as const,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'scroll',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!server || !server.settings ? (
|
|
||||||
<Typography align="center" variant="subtitle1">
|
|
||||||
{error}
|
|
||||||
</Typography>
|
|
||||||
) : (
|
|
||||||
<ServerSettingsDataGrid serverSettings={server.settings} />
|
|
||||||
)}
|
|
||||||
</Card>
|
|
||||||
</Card>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import ErrorCard from '../components/ErrorCard';
|
import ErrorCard from '../components/ErrorCard';
|
||||||
|
import SettingsChipCloud from '../components/Server/SettingsChipCloud';
|
||||||
import ServerCard from '../components/ServerCard';
|
import ServerCard from '../components/ServerCard';
|
||||||
import { SearchState } from '../data/SearchState';
|
import { SearchState } from '../data/SearchState';
|
||||||
import { ServerData } from '../data/ServerData';
|
import { ServerData } from '../data/ServerData';
|
||||||
|
|
@ -129,7 +130,9 @@ export default function Home({
|
||||||
<ErrorCard error="" />
|
<ErrorCard error="" />
|
||||||
) : (
|
) : (
|
||||||
filteredServers.map((server: ServerData) => (
|
filteredServers.map((server: ServerData) => (
|
||||||
<ServerCard key={server.url} server={server} />
|
<ServerCard key={server.url} server={server}>
|
||||||
|
<SettingsChipCloud server={server} />
|
||||||
|
</ServerCard>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,12 @@
|
||||||
import {
|
import { Box } from '@mui/material';
|
||||||
ContentCopy,
|
|
||||||
Launch,
|
|
||||||
Public,
|
|
||||||
PublicOff,
|
|
||||||
Warning,
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
import {
|
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Divider,
|
|
||||||
IconButton,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
alpha,
|
|
||||||
} from '@mui/material';
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link, useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { fetchDataFromBackend, fetchDemo } from '../apiUtil';
|
import { fetchDataFromBackend, fetchDemo } from '../apiUtil';
|
||||||
import { AlertResponse } from '../components/Alert';
|
import { AlertResponse } from '../components/Alert';
|
||||||
import ErrorCard from '../components/ErrorCard';
|
import ErrorCard from '../components/ErrorCard';
|
||||||
import ExpansionBar from '../components/ExpansionsBar';
|
import SettingsDataGrid from '../components/Server/SettingsDataGrid';
|
||||||
import ServerSettingsDataGrid from '../components/ServerSettingsDataGrid';
|
import ServerCard from '../components/ServerCard';
|
||||||
import { ServerData } from '../data/ServerData';
|
import { ServerData } from '../data/ServerData';
|
||||||
import CopyImageIcon from '../images/copy-image.png';
|
|
||||||
|
|
||||||
export default function ServerDetails({
|
export default function ServerDetails({
|
||||||
setAlertInfo,
|
setAlertInfo,
|
||||||
|
|
@ -60,229 +43,16 @@ export default function ServerDetails({
|
||||||
fetchServerData();
|
fetchServerData();
|
||||||
}, [url, setServer, setAlertInfo, setError]);
|
}, [url, setServer, setAlertInfo, setError]);
|
||||||
|
|
||||||
const [clipboardTooltip, setClipboardTooltip] = useState('Copy server URL.');
|
|
||||||
const [clipboardTooltipOpen, setClipboardTooltipOpen] = useState(false);
|
|
||||||
const handleClipboardTooltipClose = () => {
|
|
||||||
setClipboardTooltipOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClipboardTooltipOpen = () => {
|
|
||||||
setClipboardTooltipOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const copyServerUrlToClipboard = async (text: string) => {
|
|
||||||
try {
|
|
||||||
navigator.clipboard.writeText(text);
|
|
||||||
setClipboardTooltip('Copied server URL!');
|
|
||||||
handleClipboardTooltipOpen();
|
|
||||||
setTimeout(() => {
|
|
||||||
handleClipboardTooltipClose();
|
|
||||||
setClipboardTooltip('Copy server URL.');
|
|
||||||
}, 3000);
|
|
||||||
} catch (err) {
|
|
||||||
handleClipboardTooltipOpen();
|
|
||||||
if (err instanceof Error) {
|
|
||||||
setClipboardTooltip(err.message);
|
|
||||||
} else {
|
|
||||||
setClipboardTooltip('Something went wrong!');
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
handleClipboardTooltipClose();
|
|
||||||
setClipboardTooltip('Copy server URL.');
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function formatExternalUrl(serverUrl: string): string {
|
|
||||||
// prepend 'https://' to the URL if it's not already there
|
|
||||||
if (!/^https?:\/\//i.test(serverUrl)) {
|
|
||||||
return `https://${serverUrl}`;
|
|
||||||
}
|
|
||||||
return serverUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{error || !server ? (
|
{error || !server ? (
|
||||||
<ErrorCard error={error} />
|
<ErrorCard error={error} />
|
||||||
) : (
|
) : (
|
||||||
<Card className="mb-2">
|
<ServerCard server={server}>
|
||||||
<Box className="flex px-4">
|
|
||||||
<Box className="flex flex-col items-center justify-center">
|
|
||||||
<Box className="flex content-center">
|
|
||||||
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
|
||||||
<Tooltip
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
title="Server is undergoing maintenance."
|
|
||||||
>
|
|
||||||
<Warning color="warning" />
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
{!server.up ? (
|
|
||||||
<Tooltip
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
title="Server is offline."
|
|
||||||
>
|
|
||||||
<PublicOff color="error" />
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
<Tooltip
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
title="Server is online."
|
|
||||||
>
|
|
||||||
<Public color="success" />
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Box className="flex content-center">
|
|
||||||
<Tooltip
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
title="Estimated server geolocation provided by MaxMind"
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
component={Link}
|
|
||||||
to="https://www.maxmind.com/"
|
|
||||||
target="_blank"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
textDecoration: 'none',
|
|
||||||
}}
|
|
||||||
color={(theme) => alpha(theme.palette.text.primary, 0.5)}
|
|
||||||
>
|
|
||||||
{server.location}
|
|
||||||
</Typography>
|
|
||||||
</Tooltip>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<CardContent className="grow py-1">
|
|
||||||
<Box className="flex content-center">
|
|
||||||
<Typography
|
|
||||||
variant="h5"
|
|
||||||
color={(theme) => alpha(theme.palette.text.primary, 0.87)}
|
|
||||||
sx={{ lineHeight: 1.0 }}
|
|
||||||
>
|
|
||||||
{server.name}
|
|
||||||
</Typography>
|
|
||||||
{typeof server.customizations['API.WEBSITE'] === 'string' &&
|
|
||||||
server.customizations['API.WEBSITE'] !== '' && (
|
|
||||||
<Tooltip arrow disableInteractive title="Visit website.">
|
|
||||||
<IconButton
|
|
||||||
component={Link}
|
|
||||||
to={formatExternalUrl(
|
|
||||||
server.customizations['API.WEBSITE']
|
|
||||||
)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
size="small"
|
|
||||||
className="py-0"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
}}
|
|
||||||
disableRipple
|
|
||||||
>
|
|
||||||
<Launch
|
|
||||||
sx={{
|
|
||||||
fontSize: (theme) =>
|
|
||||||
theme.typography.caption.fontSize,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Box className="flex content-center">
|
|
||||||
<Typography
|
|
||||||
variant="subtitle2"
|
|
||||||
color={(theme) => alpha(theme.palette.text.primary, 0.6)}
|
|
||||||
>
|
|
||||||
{server.url}
|
|
||||||
</Typography>
|
|
||||||
{window.isSecureContext && (
|
|
||||||
<Tooltip
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
title={clipboardTooltip}
|
|
||||||
open={
|
|
||||||
clipboardTooltip === 'Copied server URL!' ||
|
|
||||||
clipboardTooltipOpen
|
|
||||||
}
|
|
||||||
onOpen={handleClipboardTooltipOpen}
|
|
||||||
onClose={handleClipboardTooltipClose}
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
size="small"
|
|
||||||
className="py-0"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
copyServerUrlToClipboard(server.url);
|
|
||||||
}}
|
|
||||||
disableRipple
|
|
||||||
>
|
|
||||||
<ContentCopy
|
|
||||||
sx={{
|
|
||||||
fontSize: (theme) =>
|
|
||||||
theme.typography.caption.fontSize,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<ExpansionBar server={server} />
|
|
||||||
</CardContent>
|
|
||||||
<Box className="flex items-center justify-center">
|
|
||||||
<Typography
|
|
||||||
variant="h5"
|
|
||||||
color={(theme) => theme.palette.text.secondary}
|
|
||||||
>
|
|
||||||
{`Lv.${server.max_level}`}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
<Divider />
|
|
||||||
<CardContent className="flex justify-between py-1">
|
|
||||||
{server.settings && (
|
{server.settings && (
|
||||||
<ServerSettingsDataGrid serverSettings={server.settings} />
|
<SettingsDataGrid serverSettings={server.settings} />
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</ServerCard>
|
||||||
<Divider />
|
|
||||||
<CardContent className="flex justify-between py-1">
|
|
||||||
<Typography variant="caption">
|
|
||||||
{server.active_sessions} active sessions
|
|
||||||
{server.login_limit !== 1 && (
|
|
||||||
<Tooltip
|
|
||||||
title={`Server allows ${server.login_limit === 0 ? 'unlimited' : server.login_limit} simultaneous game sessions per IP.`}
|
|
||||||
arrow
|
|
||||||
disableInteractive
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={CopyImageIcon}
|
|
||||||
alt=""
|
|
||||||
style={{
|
|
||||||
maxHeight: '1.5em',
|
|
||||||
marginLeft: '0.5em',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
}}
|
|
||||||
onContextMenu={(event) => event.preventDefault()}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="caption">
|
|
||||||
Updated: {new Date(server.updated).toLocaleString()}
|
|
||||||
</Typography>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue