mirror of https://github.com/cocosolos/ixion.git
Add detailed settings view
This commit is contained in:
parent
fb5e67924d
commit
ed229129c9
|
|
@ -12,6 +12,7 @@
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/icons-material": "^5.15.6",
|
"@mui/icons-material": "^5.15.6",
|
||||||
"@mui/material": "^5.15.6",
|
"@mui/material": "^5.15.6",
|
||||||
|
"@mui/x-data-grid": "^7.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3"
|
"react-router-dom": "^6.21.3"
|
||||||
|
|
@ -1051,14 +1052,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/base": {
|
"node_modules/@mui/base": {
|
||||||
"version": "5.0.0-beta.39",
|
"version": "5.0.0-beta.40",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.39.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
|
||||||
"integrity": "sha512-puyUptF7VJ+9/dMIRLF+DLR21cWfvejsA6OnatfJfqFp8aMhya7xQtvYLEfCch6ahvFZvNC9FFEGGR+qkgFjUg==",
|
"integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
"@floating-ui/react-dom": "^2.0.8",
|
"@floating-ui/react-dom": "^2.0.8",
|
||||||
"@mui/types": "^7.2.13",
|
"@mui/types": "^7.2.14",
|
||||||
"@mui/utils": "^5.15.13",
|
"@mui/utils": "^5.15.14",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
|
|
@ -1082,9 +1083,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/core-downloads-tracker": {
|
"node_modules/@mui/core-downloads-tracker": {
|
||||||
"version": "5.15.13",
|
"version": "5.15.15",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.13.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz",
|
||||||
"integrity": "sha512-ERsk9EWpiitSiKnmUdFJGshtFk647l4p7r+mjRWe/F1l5kT1NTTKkaeDLcK3/lsy0udXjMgcG0bNwzbYBdDdhQ==",
|
"integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/mui-org"
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
|
@ -1116,16 +1117,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/material": {
|
"node_modules/@mui/material": {
|
||||||
"version": "5.15.13",
|
"version": "5.15.15",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.13.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz",
|
||||||
"integrity": "sha512-E+QisOJcIzTTyeJ0o3lgYMcyrmCydb2S4cn9vTtGpIB9uR6fQ6La3dIGsXgYEGyeOB9YkWzQbNzYzvyODGEWKA==",
|
"integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
"@mui/base": "5.0.0-beta.39",
|
"@mui/base": "5.0.0-beta.40",
|
||||||
"@mui/core-downloads-tracker": "^5.15.13",
|
"@mui/core-downloads-tracker": "^5.15.15",
|
||||||
"@mui/system": "^5.15.13",
|
"@mui/system": "^5.15.15",
|
||||||
"@mui/types": "^7.2.13",
|
"@mui/types": "^7.2.14",
|
||||||
"@mui/utils": "^5.15.13",
|
"@mui/utils": "^5.15.14",
|
||||||
"@types/react-transition-group": "^4.4.10",
|
"@types/react-transition-group": "^4.4.10",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
|
|
@ -1160,12 +1161,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/private-theming": {
|
"node_modules/@mui/private-theming": {
|
||||||
"version": "5.15.13",
|
"version": "5.15.14",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.13.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz",
|
||||||
"integrity": "sha512-j5Z2pRi6talCunIRIzpQERSaHwLd5EPdHMwIKDVCszro1RAzRZl7WmH68IMCgQmJMeglr+FalqNuq048qptGAg==",
|
"integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
"@mui/utils": "^5.15.13",
|
"@mui/utils": "^5.15.14",
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -1186,9 +1187,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/styled-engine": {
|
"node_modules/@mui/styled-engine": {
|
||||||
"version": "5.15.11",
|
"version": "5.15.14",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz",
|
||||||
"integrity": "sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==",
|
"integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
"@emotion/cache": "^11.11.0",
|
"@emotion/cache": "^11.11.0",
|
||||||
|
|
@ -1217,15 +1218,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/system": {
|
"node_modules/@mui/system": {
|
||||||
"version": "5.15.13",
|
"version": "5.15.15",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.13.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz",
|
||||||
"integrity": "sha512-eHaX3sniZXNWkxX0lmcLxROhQ5La0HkOuF7zxbSdAoHUOk07gboQYmF6hSJ/VBFx/GLanIw67FMTn88vc8niLg==",
|
"integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
"@mui/private-theming": "^5.15.13",
|
"@mui/private-theming": "^5.15.14",
|
||||||
"@mui/styled-engine": "^5.15.11",
|
"@mui/styled-engine": "^5.15.14",
|
||||||
"@mui/types": "^7.2.13",
|
"@mui/types": "^7.2.14",
|
||||||
"@mui/utils": "^5.15.13",
|
"@mui/utils": "^5.15.14",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"csstype": "^3.1.3",
|
"csstype": "^3.1.3",
|
||||||
"prop-types": "^15.8.1"
|
"prop-types": "^15.8.1"
|
||||||
|
|
@ -1256,9 +1257,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/types": {
|
"node_modules/@mui/types": {
|
||||||
"version": "7.2.13",
|
"version": "7.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz",
|
||||||
"integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==",
|
"integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^17.0.0 || ^18.0.0"
|
"@types/react": "^17.0.0 || ^18.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -1269,9 +1270,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mui/utils": {
|
"node_modules/@mui/utils": {
|
||||||
"version": "5.15.13",
|
"version": "5.15.14",
|
||||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.13.tgz",
|
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz",
|
||||||
"integrity": "sha512-qNlR9FLEhORC4zVZ3fzF48213EhP/92N71AcFbhHN73lPJjAbq9lUv+71P7uEdRHdrrOlm8+1zE8/OBy6MUqdg==",
|
"integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
"@types/prop-types": "^15.7.11",
|
"@types/prop-types": "^15.7.11",
|
||||||
|
|
@ -1295,6 +1296,31 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-data-grid": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-WKmFo0eKhj3W7Fv8u5n2XP4UcdzuJ+mEYALiMUDAYsah/hPBH9mA1miXn9DjXF3i3dxgzrTjdJemTgTJxAQZKg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.24.0",
|
||||||
|
"@mui/system": "^5.15.14",
|
||||||
|
"@mui/utils": "^5.15.14",
|
||||||
|
"clsx": "^2.1.0",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"reselect": "^4.1.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@mui/material": "^5.15.14",
|
||||||
|
"react": "^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
|
@ -6699,6 +6725,11 @@
|
||||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/reselect": {
|
||||||
|
"version": "4.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
|
||||||
|
"integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.8",
|
"version": "1.22.8",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mui/icons-material": "^5.15.6",
|
"@mui/icons-material": "^5.15.6",
|
||||||
"@mui/material": "^5.15.6",
|
"@mui/material": "^5.15.6",
|
||||||
|
"@mui/x-data-grid": "^7.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.21.3"
|
"react-router-dom": "^6.21.3"
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import ServerData from './data/ServerData';
|
||||||
import About from './pages/About';
|
import About from './pages/About';
|
||||||
import Home from './pages/Home';
|
import Home from './pages/Home';
|
||||||
import NotFound from './pages/NotFound';
|
import NotFound from './pages/NotFound';
|
||||||
|
import ServerDetails from './pages/ServerDetails';
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
const [alertInfo, setAlertInfo] = useState<{
|
const [alertInfo, setAlertInfo] = useState<{
|
||||||
|
|
@ -58,6 +59,10 @@ export function App() {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/server/:id"
|
||||||
|
element={<ServerDetails setAlertInfo={setAlertInfo} />}
|
||||||
|
/>
|
||||||
<Route path="/about" element={<About />} />
|
<Route path="/about" element={<About />} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ export const fetchDemo = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchData = async (queryParams = {}) => {
|
export const fetchData = async (dir = 'servers', queryParams = {}) => {
|
||||||
const queryString = new URLSearchParams(queryParams).toString();
|
const queryString = new URLSearchParams(queryParams).toString();
|
||||||
const url = `${import.meta.env.PROD ? 'https://api.ixion.dev' : 'http://localhost:8000'}/servers/${queryString ? `?${queryString}` : ''}`;
|
const url = `${import.meta.env.PROD ? 'https://api.ixion.dev' : 'http://localhost:8000'}/${dir}/${queryString ? `?${queryString}` : ''}`;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
signal: AbortSignal.timeout(5000),
|
signal: AbortSignal.timeout(5000),
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
styled,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { postData } from '../apiUtil';
|
import { postData } from '../apiUtil';
|
||||||
import ServerData from '../data/ServerData';
|
import ServerData from '../data/ServerData';
|
||||||
import { AlertResponse } from './Alert';
|
import { AlertResponse } from './Alert';
|
||||||
|
|
@ -51,6 +52,7 @@ export default function AddServer({
|
||||||
servers: ServerData[];
|
servers: ServerData[];
|
||||||
setServers: React.Dispatch<React.SetStateAction<ServerData[]>>;
|
setServers: React.Dispatch<React.SetStateAction<ServerData[]>>;
|
||||||
}) {
|
}) {
|
||||||
|
const navigate = useNavigate();
|
||||||
const containerRef = useRef<HTMLElement>(null);
|
const containerRef = useRef<HTMLElement>(null);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
@ -71,6 +73,7 @@ export default function AddServer({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
});
|
});
|
||||||
if (response.data && typeof response.data === 'object') {
|
if (response.data && typeof response.data === 'object') {
|
||||||
|
navigate(`/server/${response.data.id}`);
|
||||||
setServers([response.data, ...servers]);
|
setServers([response.data, ...servers]);
|
||||||
}
|
}
|
||||||
if (inputRef.current) {
|
if (inputRef.current) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||||
|
import ServerData from '../data/ServerData';
|
||||||
|
|
||||||
|
const expansions = [
|
||||||
|
<ToggleButton
|
||||||
|
key="rotz"
|
||||||
|
value="rotz"
|
||||||
|
aria-label="rotz-enabled"
|
||||||
|
className="py-0"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
RotZ
|
||||||
|
</ToggleButton>,
|
||||||
|
<ToggleButton
|
||||||
|
key="cop"
|
||||||
|
value="cop"
|
||||||
|
aria-label="cop-enabled"
|
||||||
|
className="py-0"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
CoP
|
||||||
|
</ToggleButton>,
|
||||||
|
<ToggleButton
|
||||||
|
key="toau"
|
||||||
|
value="toau"
|
||||||
|
aria-label="toau-enabled"
|
||||||
|
className="py-0"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
ToAU
|
||||||
|
</ToggleButton>,
|
||||||
|
<ToggleButton
|
||||||
|
key="wotg"
|
||||||
|
value="wotg"
|
||||||
|
aria-label="wotg-enabled"
|
||||||
|
className="py-0"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
WotG
|
||||||
|
</ToggleButton>,
|
||||||
|
<ToggleButton
|
||||||
|
key="soa"
|
||||||
|
value="soa"
|
||||||
|
aria-label="soa-enabled"
|
||||||
|
className="py-0"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
SoA
|
||||||
|
</ToggleButton>,
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function ExpansionBar({ server }: { server: ServerData }) {
|
||||||
|
return (
|
||||||
|
<ToggleButtonGroup
|
||||||
|
size="small"
|
||||||
|
value={[
|
||||||
|
server.customizations['LOGIN.RISE_OF_ZILART'] && 'rotz',
|
||||||
|
server.customizations['LOGIN.CHAINS_OF_PROMATHIA'] && 'cop',
|
||||||
|
server.customizations['LOGIN.TREASURES_OF_AHT_URGHAN'] && 'toau',
|
||||||
|
server.customizations['LOGIN.WINGS_OF_THE_GODDESS'] && 'wotg',
|
||||||
|
server.customizations['LOGIN.SEEKERS_OF_ADOULIN'] && 'soa',
|
||||||
|
]}
|
||||||
|
sx={{ '& button': { lineHeight: 1.0 } }}
|
||||||
|
>
|
||||||
|
{expansions}
|
||||||
|
</ToggleButtonGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { fetchData, fetchDemo } from '../apiUtil';
|
import { fetchData } from '../apiUtil';
|
||||||
import SearchState from '../data/SearchState';
|
import SearchState from '../data/SearchState';
|
||||||
import ServerData from '../data/ServerData';
|
import ServerData from '../data/ServerData';
|
||||||
import AddServer from './AddServer';
|
import AddServer from './AddServer';
|
||||||
|
|
@ -52,11 +52,6 @@ export default function Header({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setFetchLoading(50);
|
|
||||||
if (data.length === 0) {
|
|
||||||
data.push(await fetchDemo());
|
|
||||||
}
|
|
||||||
setFetchLoading(75);
|
|
||||||
setServers(data);
|
setServers(data);
|
||||||
setFetchLoading(100);
|
setFetchLoading(100);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
Close,
|
Close,
|
||||||
ContentCopy,
|
ContentCopy,
|
||||||
ExpandMore,
|
ExpandMore,
|
||||||
|
Info,
|
||||||
Launch,
|
Launch,
|
||||||
Public,
|
Public,
|
||||||
PublicOff,
|
PublicOff,
|
||||||
|
|
@ -15,8 +16,6 @@ import {
|
||||||
Chip,
|
Chip,
|
||||||
Divider,
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
ToggleButton,
|
|
||||||
ToggleButtonGroup,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
alpha,
|
alpha,
|
||||||
|
|
@ -29,6 +28,11 @@ import ServerData, {
|
||||||
} from '../data/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 { Accordion, AccordionDetails, AccordionSummary } from './Accordion';
|
||||||
|
import ExpansionBar from './ExpansionsBar';
|
||||||
|
|
||||||
|
function scrollToTop() {
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
|
||||||
export default function ServerCard({ server }: { server: ServerData }) {
|
export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
const [clipboardTooltip, setClipboardTooltip] = useState('Copy server URL.');
|
const [clipboardTooltip, setClipboardTooltip] = useState('Copy server URL.');
|
||||||
|
|
@ -48,7 +52,7 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
handleClipboardTooltipOpen();
|
handleClipboardTooltipOpen();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
handleClipboardTooltipClose();
|
handleClipboardTooltipClose();
|
||||||
setClipboardTooltip('Copy Server URL.');
|
setClipboardTooltip('Copy server URL.');
|
||||||
}, 3000);
|
}, 3000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleClipboardTooltipOpen();
|
handleClipboardTooltipOpen();
|
||||||
|
|
@ -59,7 +63,7 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
handleClipboardTooltipClose();
|
handleClipboardTooltipClose();
|
||||||
setClipboardTooltip('Copy Server URL.');
|
setClipboardTooltip('Copy server URL.');
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -134,54 +138,6 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const expansions = [
|
|
||||||
<ToggleButton
|
|
||||||
key="rotz"
|
|
||||||
value="rotz"
|
|
||||||
aria-label="rotz-enabled"
|
|
||||||
className="py-0"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
RotZ
|
|
||||||
</ToggleButton>,
|
|
||||||
<ToggleButton
|
|
||||||
key="cop"
|
|
||||||
value="cop"
|
|
||||||
aria-label="cop-enabled"
|
|
||||||
className="py-0"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
CoP
|
|
||||||
</ToggleButton>,
|
|
||||||
<ToggleButton
|
|
||||||
key="toau"
|
|
||||||
value="toau"
|
|
||||||
aria-label="toau-enabled"
|
|
||||||
className="py-0"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
ToAU
|
|
||||||
</ToggleButton>,
|
|
||||||
<ToggleButton
|
|
||||||
key="wotg"
|
|
||||||
value="wotg"
|
|
||||||
aria-label="wotg-enabled"
|
|
||||||
className="py-0"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
WotG
|
|
||||||
</ToggleButton>,
|
|
||||||
<ToggleButton
|
|
||||||
key="soa"
|
|
||||||
value="soa"
|
|
||||||
aria-label="soa-enabled"
|
|
||||||
className="py-0"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
SoA
|
|
||||||
</ToggleButton>,
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="mb-2">
|
<Card className="mb-2">
|
||||||
<Accordion className="my-0">
|
<Accordion className="my-0">
|
||||||
|
|
@ -286,20 +242,7 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<ToggleButtonGroup
|
<ExpansionBar server={server} />
|
||||||
size="small"
|
|
||||||
value={[
|
|
||||||
server.customizations['LOGIN.RISE_OF_ZILART'] && 'rotz',
|
|
||||||
server.customizations['LOGIN.CHAINS_OF_PROMATHIA'] && 'cop',
|
|
||||||
server.customizations['LOGIN.TREASURES_OF_AHT_URGHAN'] &&
|
|
||||||
'toau',
|
|
||||||
server.customizations['LOGIN.WINGS_OF_THE_GODDESS'] && 'wotg',
|
|
||||||
server.customizations['LOGIN.SEEKERS_OF_ADOULIN'] && 'soa',
|
|
||||||
]}
|
|
||||||
sx={{ '& button': { lineHeight: 1.0 } }}
|
|
||||||
>
|
|
||||||
{expansions}
|
|
||||||
</ToggleButtonGroup>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<Box className="flex items-center justify-center">
|
<Box className="flex items-center justify-center">
|
||||||
<Typography
|
<Typography
|
||||||
|
|
@ -334,8 +277,8 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
<Divider />
|
<Divider />
|
||||||
<CardContent className="flex justify-between py-1">
|
<CardContent className="flex justify-between py-0">
|
||||||
<Typography variant="caption">
|
<Typography variant="caption" className="flex items-center">
|
||||||
{server.active_sessions} active sessions
|
{server.active_sessions} active sessions
|
||||||
{server.login_limit !== 1 && (
|
{server.login_limit !== 1 && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|
@ -356,7 +299,18 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="caption">
|
<Typography variant="caption" className="flex items-center">
|
||||||
|
<Tooltip title="View full settings." arrow disableInteractive>
|
||||||
|
<IconButton
|
||||||
|
component={Link}
|
||||||
|
to={`/server/${server.id}`}
|
||||||
|
onClick={scrollToTop}
|
||||||
|
className="p-0"
|
||||||
|
disableRipple
|
||||||
|
>
|
||||||
|
<Info className="p-1" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
Updated: {new Date(server.updated).toLocaleString()}
|
Updated: {new Date(server.updated).toLocaleString()}
|
||||||
</Typography>
|
</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export interface ServerSetting {
|
||||||
transform?: (arg: boolean | string | number) => boolean | string | number;
|
transform?: (arg: boolean | string | number) => boolean | string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServerSettings {
|
export interface ServerSettings {
|
||||||
[key: string]: boolean | string | number;
|
[key: string]: boolean | string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,6 +16,7 @@ export default interface ServerData {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
max_level: number;
|
max_level: number;
|
||||||
|
settings?: ServerSettings;
|
||||||
customizations: ServerSettings;
|
customizations: ServerSettings;
|
||||||
login_limit: number;
|
login_limit: number;
|
||||||
active_sessions: number;
|
active_sessions: number;
|
||||||
|
|
@ -28,9 +29,16 @@ export const DemoServerData: ServerData = {
|
||||||
name: 'LandSandBoat Demo',
|
name: 'LandSandBoat Demo',
|
||||||
url: 'github.com/LandSandBoat/server',
|
url: 'github.com/LandSandBoat/server',
|
||||||
max_level: 99,
|
max_level: 99,
|
||||||
|
settings: LsbDefaults,
|
||||||
customizations: {
|
customizations: {
|
||||||
...LsbDefaults,
|
|
||||||
'API.WEBSITE': 'https://landsandboat.github.io/server/',
|
'API.WEBSITE': 'https://landsandboat.github.io/server/',
|
||||||
|
'MAIN.ENABLE_TRUST_CASTING': 1,
|
||||||
|
'MAP.LEVEL_SYNC_ENABLE': true,
|
||||||
|
'LOGIN.RISE_OF_ZILART': true,
|
||||||
|
'LOGIN.CHAINS_OF_PROMATHIA': true,
|
||||||
|
'LOGIN.TREASURES_OF_AHT_URGHAN': true,
|
||||||
|
'LOGIN.WINGS_OF_THE_GODDESS': true,
|
||||||
|
'LOGIN.SEEKERS_OF_ADOULIN': true,
|
||||||
},
|
},
|
||||||
login_limit: 0,
|
login_limit: 0,
|
||||||
active_sessions: 0,
|
active_sessions: 0,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Box } from '@mui/material';
|
import { Box } from '@mui/material';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { fetchData, fetchDemo } from '../apiUtil';
|
import { fetchData } from '../apiUtil';
|
||||||
import { AlertResponse } from '../components/Alert';
|
import { AlertResponse } from '../components/Alert';
|
||||||
import ErrorCard from '../components/ErrorCard';
|
import ErrorCard from '../components/ErrorCard';
|
||||||
import ServerCard from '../components/ServerCard';
|
import ServerCard from '../components/ServerCard';
|
||||||
|
|
@ -35,9 +35,7 @@ export default function Home({
|
||||||
setError('An unknown error occurred.');
|
setError('An unknown error occurred.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.length === 0) {
|
|
||||||
data.push(await fetchDemo());
|
|
||||||
}
|
|
||||||
setServers(data);
|
setServers(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -161,7 +159,7 @@ export default function Home({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{error ? (
|
{error || filteredServers.length === 0 ? (
|
||||||
<ErrorCard error={error} />
|
<ErrorCard error={error} />
|
||||||
) : (
|
) : (
|
||||||
filteredServers.map((server: ServerData) => (
|
filteredServers.map((server: ServerData) => (
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,307 @@
|
||||||
|
import {
|
||||||
|
ContentCopy,
|
||||||
|
Launch,
|
||||||
|
Public,
|
||||||
|
PublicOff,
|
||||||
|
Warning,
|
||||||
|
} from '@mui/icons-material';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
alpha,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { DataGrid, GridColDef } from '@mui/x-data-grid';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Link, useParams } from 'react-router-dom';
|
||||||
|
import { fetchData, fetchDemo } from '../apiUtil';
|
||||||
|
import { AlertResponse } from '../components/Alert';
|
||||||
|
import ErrorCard from '../components/ErrorCard';
|
||||||
|
import ExpansionBar from '../components/ExpansionsBar';
|
||||||
|
import ServerData, {
|
||||||
|
ServerSettings,
|
||||||
|
ServerSettingsInfo,
|
||||||
|
} from '../data/ServerData';
|
||||||
|
import CopyImageIcon from '../images/copy-image.png';
|
||||||
|
|
||||||
|
interface KeyValueRow {
|
||||||
|
id: number;
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
value: string | number | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns: GridColDef[] = [
|
||||||
|
{ field: 'key', headerName: 'Key', flex: 2 },
|
||||||
|
{ field: 'name', headerName: 'Name', flex: 1 },
|
||||||
|
{ field: 'value', headerName: 'Value', flex: 1 },
|
||||||
|
{ field: 'description', headerName: 'Description', flex: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
function ServerSettingsDataGrid({
|
||||||
|
serverSettings,
|
||||||
|
}: {
|
||||||
|
serverSettings: ServerSettings;
|
||||||
|
}) {
|
||||||
|
const rows: KeyValueRow[] = Object.entries(serverSettings).map(
|
||||||
|
([key, value], index) => ({
|
||||||
|
id: index + 1,
|
||||||
|
key,
|
||||||
|
name: ServerSettingsInfo[key]?.name || 'none',
|
||||||
|
value,
|
||||||
|
description: ServerSettingsInfo[key]?.description || '',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className="w-full">
|
||||||
|
<DataGrid
|
||||||
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
checkboxSelection={false}
|
||||||
|
autoHeight
|
||||||
|
density="compact"
|
||||||
|
hideFooterSelectedRowCount
|
||||||
|
showCellVerticalBorder
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ServerDetails({
|
||||||
|
setAlertInfo,
|
||||||
|
}: {
|
||||||
|
setAlertInfo: React.Dispatch<React.SetStateAction<AlertResponse>>;
|
||||||
|
}) {
|
||||||
|
const { id } = useParams();
|
||||||
|
const [server, setServer] = useState<ServerData>();
|
||||||
|
const [error, setError] = useState<string>('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let data: ServerData;
|
||||||
|
const fetchServerData = async () => {
|
||||||
|
try {
|
||||||
|
if (id === 'demo') {
|
||||||
|
data = await fetchDemo();
|
||||||
|
} else {
|
||||||
|
data = await fetchData(`server/${id}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
setAlertInfo({
|
||||||
|
message: err.message,
|
||||||
|
severity: 'error',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setError('An unknown error occurred.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setServer(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchServerData();
|
||||||
|
}, [id, 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(url: string): string {
|
||||||
|
// prepend 'https://' to the URL if it's not already there
|
||||||
|
if (!/^https?:\/\//i.test(url)) {
|
||||||
|
return `https://${url}`;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{error || !server ? (
|
||||||
|
<ErrorCard error={error} />
|
||||||
|
) : (
|
||||||
|
<Card className="mb-2">
|
||||||
|
<Box className="flex px-4">
|
||||||
|
<Box className="flex items-center justify-center">
|
||||||
|
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
||||||
|
<Tooltip
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
title="Server is undergoing maintenance."
|
||||||
|
>
|
||||||
|
<Warning color="warning" />
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
{server.inactivity_counter > 0 ? (
|
||||||
|
<Tooltip
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
title={`Offline for ${server.inactivity_counter} hours.`}
|
||||||
|
>
|
||||||
|
<PublicOff color="error" />
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Tooltip arrow disableInteractive title="Server is online.">
|
||||||
|
<Public color="success" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</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 && (
|
||||||
|
<ServerSettingsDataGrid serverSettings={server.settings} />
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue