[FEATURE] Autocomplete & theming

This commit is contained in:
dunemask 2023-10-09 13:42:11 -06:00
parent ef00eef464
commit 5a9212a814
18 changed files with 114 additions and 35 deletions

View file

@ -1,4 +1,6 @@
// Imports // Imports
import { ThemeProvider } from "@mui/material/styles";
import mclTheme from "./util/theme.js";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { SettingsProvider } from "@mcl/settings"; import { SettingsProvider } from "@mcl/settings";
import Viewport from "./nav/Viewport.jsx"; import Viewport from "./nav/Viewport.jsx";
@ -11,9 +13,11 @@ export default function MCL() {
<div className="minecluster"> <div className="minecluster">
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<SettingsProvider> <SettingsProvider>
<ThemeProvider theme={mclTheme}>
<BrowserRouter> <BrowserRouter>
<Viewport /> <Viewport />
</BrowserRouter> </BrowserRouter>
</ThemeProvider>
</SettingsProvider> </SettingsProvider>
</QueryClientProvider> </QueryClientProvider>
</div> </div>

View file

@ -13,7 +13,7 @@ import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import MenuIcon from "@mui/icons-material/Menu"; import MenuIcon from "@mui/icons-material/Menu";
import Drawer from "@mui/material/Drawer"; import Drawer from "@mui/material/Drawer";
import ListItemIcon from "@mui/material/ListItemIcon"; import HomeIcon from "@mui/icons-material/Home";
import ListItemText from "@mui/material/ListItemText"; import ListItemText from "@mui/material/ListItemText";
import List from "@mui/material/List"; import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton"; import ListItemButton from "@mui/material/ListItemButton";
@ -36,11 +36,27 @@ export default function MCLMenu() {
theme.zIndex.modal + 2 - (isDrawer ? 1 : 0); theme.zIndex.modal + 2 - (isDrawer ? 1 : 0);
return ( return (
<AppBar position="fixed" sx={{ bgcolor: "black", zIndex: drawerIndex() }}> <AppBar
position="fixed"
color="primary"
sx={{ zIndex: drawerIndex(), bgcolor: "black" }}
enableColorOnDark={false}
>
<Box sx={{ flexGrow: 1, margin: "0 20px" }}> <Box sx={{ flexGrow: 1, margin: "0 20px" }}>
<Toolbar disableGutters> <Toolbar disableGutters>
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}> <Typography
{navHeader()} variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: "flex" }}
color="white"
>
<IconButton component={Link} to="/" color="inherit">
<HomeIcon />
</IconButton>
<span style={{ margin: "auto 0", color: "inherit" }}>
{navHeader()}{" "}
</span>
</Typography> </Typography>
</Toolbar> </Toolbar>
</Box> </Box>

View file

@ -1,6 +1,8 @@
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar"; import Toolbar from "@mui/material/Toolbar";
import MCLPortal from "./MCLPortal.jsx"; import MCLPortal from "./MCLPortal.jsx";
import Button from "@mui/material/Button";
import SpeedDialIcon from "@mui/material/SpeedDialIcon";
// Import Navbar // Import Navbar
/*import Navbar from "./Navbar.jsx";*/ /*import Navbar from "./Navbar.jsx";*/
import MCLMenu from "./MCLMenu.jsx"; import MCLMenu from "./MCLMenu.jsx";

View file

@ -1,13 +1,12 @@
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import CreateOptions from "./CreateOptions.jsx"; import CreateOptions from "./CreateOptions.jsx";
export default function Create(){ export default function Create() {
return ( return (
<Box className="create"> <Box className="create">
{/*<CreateMenu />*/} {/*<CreateMenu />*/}
{/*<CreateOptions />*/} <Box className="create-wrapper" sx={{ display: "flex" }}>
<CreateOptions />
</Box>
</Box> </Box>
); );
} }

View file

@ -1,4 +1,5 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Autocomplete from "@mui/material/Autocomplete";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -20,6 +21,8 @@ const defaultServer = {
}; };
export default function Create() { export default function Create() {
const [wl, setWl] = useState([]);
const [spec, setSpec] = useState(defaultServer); const [spec, setSpec] = useState(defaultServer);
const versionList = useVersionList(); const versionList = useVersionList();
const [versions, setVersions] = useState(["latest"]); const [versions, setVersions] = useState(["latest"]);
@ -42,6 +45,9 @@ export default function Create() {
const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value); const coreUpdate = (attr) => (e) => updateSpec(attr, e.target.value);
const whitelistUpdate = (e) => alert("Whitelist not Implimented");
const opUpdate = (e) => alert("Op not implimented");
function upsertSpec() { function upsertSpec() {
if (validateSpec() !== "validated") return; if (validateSpec() !== "validated") return;
createServer(spec); createServer(spec);
@ -60,23 +66,31 @@ export default function Create() {
} }
return ( return (
<Box className="create"> <Box
<Box className="create-options" sx={{maxWidth:"400px"}}> className="create-options"
<FormControl fullWidth> sx={{ width: "100%", maxWidth: "600px", margin: "auto" }}
>
<FormControl fullWidth sx={{ mt: "2rem", display: "flex", gap: ".5rem" }}>
<TextField <TextField
label="Name" label="Name"
onChange={coreUpdate("name")} onChange={coreUpdate("name")}
defaultValue={spec.name} defaultValue={spec.name}
required required
/> />
<TextField label="URL" onChange={coreUpdate("url")} required /> <TextField
label="Host"
onChange={coreUpdate("url")}
helperText="Example: host.mc.example.com"
FormHelperTextProps={{ sx: { ml: 0 } }}
required
/>
<TextField <TextField
label="Version" label="Version"
onChange={coreUpdate("version")} onChange={coreUpdate("version")}
value={spec.version} value={spec.version}
select select
required required
SelectProps={{ MenuProps: { sx: { maxHeight: "12rem" } } }} SelectProps={{ MenuProps: { sx: { maxHeight: "20rem" } } }}
> >
{versions.map((v, k) => ( {versions.map((v, k) => (
<MenuItem value={v} key={k}> <MenuItem value={v} key={k}>
@ -109,9 +123,16 @@ export default function Create() {
<MenuItem value={"hard"}>Hard</MenuItem> <MenuItem value={"hard"}>Hard</MenuItem>
</TextField> </TextField>
<TextField label="Whitelist" onChange={coreUpdate("whitelist")} /> <Autocomplete
multiple
id="whitelist-autocomplete"
options={[]}
onChange={whitelistUpdate}
freeSolo
renderInput={(p) => <TextField {...p} label="Whitelist" />}
/>
<TextField label="Ops" onChange={coreUpdate("ops")} /> <TextField label="Ops" onChange={coreUpdate("ops")} />
<TextField label="Icon" onChange={coreUpdate("icon")} required /> {/*<TextField label="Icon" onChange={coreUpdate("icon")} required />*/}
<TextField <TextField
label="Max Players" label="Max Players"
onChange={coreUpdate("maxPlayers")} onChange={coreUpdate("maxPlayers")}
@ -131,7 +152,7 @@ export default function Create() {
<MenuItem value={"spectator"}>Spectator</MenuItem> <MenuItem value={"spectator"}>Spectator</MenuItem>
</TextField> </TextField>
<TextField label="Seed" onChange={coreUpdate("seed")} /> <TextField label="Seed" onChange={coreUpdate("seed")} />
<TextField label="Modpack" onChange={coreUpdate("modpack")} /> {/*<TextField label="Modpack" onChange={coreUpdate("modpack")} />*/}
<TextField <TextField
label="Memory" label="Memory"
onChange={coreUpdate("memory")} onChange={coreUpdate("memory")}
@ -149,6 +170,5 @@ export default function Create() {
</Button> </Button>
</FormControl> </FormControl>
</Box> </Box>
</Box>
); );
} }

View file

@ -1,9 +1,14 @@
import { Link } from "react-router-dom";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import ServerCard from "../servers/ServerCard.jsx"; import ServerCard from "@mcl/components/servers/ServerCard.jsx";
import RconDialog, { useRconDialog } from "../servers/RconDialog.jsx"; import RconDialog, {
import Overview from "../overview/Overview.jsx"; useRconDialog,
} from "@mcl/components/servers/RconDialog.jsx";
import Overview from "@mcl/components/overview/Overview.jsx";
import Button from "@mui/material/Button";
import SpeedDialIcon from "@mui/material/SpeedDialIcon";
import "@mcl/css/server-card.css"; import "@mcl/css/server-card.css";
import "@mcl/css/overview.css"; import "@mcl/css/overview.css";
import { useServerInstances } from "@mcl/queries"; import { useServerInstances } from "@mcl/queries";
@ -53,7 +58,23 @@ export default function Home() {
dialogToggle={rconToggle} dialogToggle={rconToggle}
serverName={server} serverName={server}
/> />
<Button
component={Link}
to="/mcl/create"
color="primary"
variant="contained"
sx={{
position: "absolute",
bottom: 16,
right: 16,
padding: "1rem",
borderRadius: "100%",
height: "4rem",
width: "4rem",
}}
>
<SpeedDialIcon />
</Button>
</Box> </Box>
); );
} }

View file

@ -14,13 +14,13 @@ const fetchApiPost = (subPath, json) => async () =>
export const useServerStatus = (server) => export const useServerStatus = (server) =>
useQuery( useQuery(
[`server-status-${server}`], [`server-status-${server}`],
fetchApiPost("/server/status", { name: server }) fetchApiPost("/server/status", { name: server }),
); );
export const useServerMetrics = (server) => export const useServerMetrics = (server) =>
useQuery( useQuery(
[`server-metrics-${server}`], [`server-metrics-${server}`],
fetchApiPost("/server/metrics", { name: server }), fetchApiPost("/server/metrics", { name: server }),
{ refetchInterval: 10000 } { refetchInterval: 10000 },
); );
export const useStartServer = (server) => export const useStartServer = (server) =>
postJsonApi("/server/start", { name: server }, "server-instances"); postJsonApi("/server/start", { name: server }, "server-instances");
@ -41,8 +41,8 @@ export const useSystemAvailable = () =>
export const useVersionList = () => export const useVersionList = () =>
useQuery(["minecraft-versions"], () => useQuery(["minecraft-versions"], () =>
fetch("https://piston-meta.mojang.com/mc/game/version_manifest.json").then( fetch("https://piston-meta.mojang.com/mc/game/version_manifest.json").then(
(r) => r.json() (r) => r.json(),
) ),
); );
const postJsonApi = (subPath, body, invalidate, method = "POST") => { const postJsonApi = (subPath, body, invalidate, method = "POST") => {

16
src/util/theme.js Normal file
View file

@ -0,0 +1,16 @@
// Generated using https://zenoo.github.io/mui-theme-creator/
import { createTheme } from "@mui/material/styles";
const themeOptions = {
palette: {
mode: "light",
primary: {
main: "rgba(109,216,144,255)",
},
secondary: {
main: "#f50057",
},
},
};
export default createTheme(themeOptions);

View file

@ -26,10 +26,11 @@ export default () => {
base: "/mcl/", base: "/mcl/",
resolve: { resolve: {
alias: { alias: {
"@mcl/css": path.resolve("./public/css"), "@mcl/css": path.resolve("./src/css"),
"@mcl/settings": path.resolve("./src/ctx/SettingsContext.jsx"), "@mcl/settings": path.resolve("./src/ctx/SettingsContext.jsx"),
"@mcl/pages": path.resolve("./src/pages"), "@mcl/pages": path.resolve("./src/pages"),
"@mcl/queries": path.resolve("./src/util/queries.js"), "@mcl/queries": path.resolve("./src/util/queries.js"),
"@mcl/components": path.resolve("./src/components"),
"@mcl": path.resolve("./src"), "@mcl": path.resolve("./src"),
}, },
}, },