Merge remote-tracking branch 'jannaahs/rework-ui' into rework-ui-cleanup-mod-portal

# Conflicts:
#	src/handlers.go
#	src/mods_handler.go
#	src/routes.go
#	ui/App/views/Controls.jsx
#	ui/App/views/Saves/Saves.jsx
This commit is contained in:
knoxfighter
2020-09-10 03:34:30 +02:00
11 changed files with 192 additions and 149 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 910 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -4,7 +4,6 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Factorio Server Manager</title> <title>Factorio Server Manager</title>
<link rel="icon" type="image/png" href="./images/favicon.ico">
<!-- Tell the browser to be responsive to screen width --> <!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">

View File

@ -5,7 +5,18 @@ module.exports = {
purge: [], purge: [],
theme: { theme: {
extend: { extend: {
width: {
72: "18rem",
80: "20rem",
88: "22rem",
96: "24rem",
},
margin: {
72: "18rem",
80: "20rem",
88: "22rem",
96: "24rem",
}
}, },
colors: { colors: {
"gray": { "gray": {

View File

@ -1,10 +1,13 @@
import React, {useEffect} from "react"; import React, {useEffect, useState} from "react";
import server from "../../api/resources/server";
import {NavLink} from "react-router-dom"; import {NavLink} from "react-router-dom";
import Button from "./Button"; import Button from "./Button";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faBars} from "@fortawesome/free-solid-svg-icons";
const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => { const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => {
const [isNavCollapsed, setIsNavCollapsed] = useState(true);
useEffect(() => { useEffect(() => {
(async () => { (async () => {
updateServerStatus() updateServerStatus()
@ -34,6 +37,7 @@ const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => {
const Link = ({children, to, last}) => { const Link = ({children, to, last}) => {
return ( return (
<NavLink <NavLink
onClick={() => setIsNavCollapsed(true)}
exact={true} exact={true}
to={to} to={to}
activeClassName="bg-orange" activeClassName="bg-orange"
@ -42,22 +46,21 @@ const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => {
} }
return ( return (
<div className="flex md:flex-row-reverse flex-wrap"> <>
{/*Main*/}
<div className="w-full md:w-5/6 bg-gray-100 bg-banner bg-fixed min-h-screen">
<div className="container mx-auto bg-gray-100 pt-16 px-6">
{children}
</div>
</div>
{/*Sidebar*/} {/*Sidebar*/}
<div <div className="w-full md:w-88 md:fixed md:top-0 md:left-0 bg-gray-dark md:h-screen">
className="w-full md:w-1/6 bg-gray-dark fixed bottom-0 md:top-0 md:left-0 h-16 md:h-screen"> <div className="py-4 px-2 accentuated">
<div className="py-4 px-2 accentuated items-center text-center"> <div className="mx-4 justify-between flex text-center">
<img src="/images/factorio.jpg" className="inline h-8" alt="Factorio Logo"/> <span className="text-dirty-white text-xl">Factorio Server Manager</span>
<span className="text-dirty-white pl-2 text-xl">Factorio Server Manager</span> <button
className="md:hidden cursor-pointer text-white hover:text-dirty-white"
onClick={() => setIsNavCollapsed(!isNavCollapsed)}
>
<FontAwesomeIcon icon={faBars}/>
</button>
</div> </div>
</div>
<div className={isNavCollapsed ? "hidden md:block" : "block"}>
<div className="py-4 px-2 accentuated"> <div className="py-4 px-2 accentuated">
<h1 className="text-dirty-white text-lg mb-2 mx-4">Server Status</h1> <h1 className="text-dirty-white text-lg mb-2 mx-4">Server Status</h1>
<div className="mx-4 mb-4 text-center"> <div className="mx-4 mb-4 text-center">
@ -88,9 +91,17 @@ const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => {
<Button type="danger" className="w-full" onClick={handleLogout}>Logout</Button> <Button type="danger" className="w-full" onClick={handleLogout}>Logout</Button>
</div> </div>
</div> </div>
<div className="accentuated h-full"/> <div className="accentuated-t accentuated-x md:block hidden"/>
</div> </div>
</div> </div>
{/*Main*/}
<div className="md:ml-88 bg-gray-100 bg-black min-h-screen">
<div className="container mx-auto bg-gray-100 pt-16 px-6">
{children}
</div>
</div>
</>
); );
} }

View File

@ -50,39 +50,48 @@ const Controls = ({serverStatus, updateServerStatus}) => {
<Panel <Panel
title="Server Status" title="Server Status"
content={ content={
<div className="flex"> <div className="lg:flex">
<table className="w-full"> { isRunning
<thead> ? <>
<tr className="text-left py-1"> <div className="lg:w-1/5 mb-2">
<th>Status</th> <div className="font-bold">Status</div>
<th>IP</th> <div>{serverStatus.status}</div>
<th>Port</th> </div>
<th>Factorio Version</th> <div className="lg:w-1/5 mb-2">
<th>Save File</th> <div className="font-bold">IP</div>
</tr> <div>{serverStatus.address}</div>
</thead> </div>
<tbody> <div className="lg:w-1/5 mb-2">
{isRunning <div className="font-bold">Port</div>
? <tr className="py-1"> <div>{serverStatus.port}</div>
<td className="pr-4 py-2">{serverStatus.status}</td> </div>
<td className="pr-4 py-2">{serverStatus.address}</td> <div className="lg:w-1/5 mb-2">
<td className="pr-4 py-2">{serverStatus.port}</td> <div className="font-bold">Factorio Version</div>
<td className="pr-4 py-2">{factorioVersion}</td> <div>{factorioVersion}</div>
<td className="pr-4 py-2">{serverStatus.savefile}</td> </div>
</tr> <div className="lg:w-1/5 mb-2">
: <tr className="py-1"> <div className="font-bold">Save</div>
<td className="pr-4 py-2">{serverStatus.status}</td> <div>{serverStatus.savefile}</div>
<td className="pr-4"> </div>
</>
: <>
<div className="lg:w-1/5 mb-2">
<div className="font-bold">Status</div>
<div>{serverStatus.status}</div>
</div>
<div className="lg:w-1/5 mb-2 mr-0 lg:mr-4">
<div className="font-bold">IP</div>
<input <input
name="ip" name="ip"
className="shadow appearance-none w-full py-2 px-3 text-black" className="shadow appearance-none w-full mr-2 py-2 px-3 text-black"
type="text" type="text"
defaultValue={"0.0.0.0"} defaultValue={"0.0.0.0"}
ref={register({required: true, pattern: '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'})} ref={register({required: true, pattern: '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'})}
/> />
{errors.ip && <span className="block text-red">IP is required and must be valid.</span>} {errors.ip && <span className="block text-red">IP is required and must be valid.</span>}
</td> </div>
<td className="pr-4"> <div className="lg:w-1/5 mb-2 mr-0 lg:mr-4">
<div className="font-bold">Port</div>
<input <input
name="port" name="port"
className="shadow appearance-none w-full py-2 px-3 text-black" className="shadow appearance-none w-full py-2 px-3 text-black"
@ -92,9 +101,13 @@ const Controls = ({serverStatus, updateServerStatus}) => {
ref={register({required: true})} ref={register({required: true})}
/> />
{errors.port && <span className="block text-red">Port is required</span>} {errors.port && <span className="block text-red">Port is required</span>}
</td> </div>
<td className="pr-4 py-2">{factorioVersion}</td> <div className="lg:w-1/5 mb-2 mr-0 lg:mr-4">
<td className="pr-4 py-2"> <div className="font-bold">Factorio Version</div>
<div>{factorioVersion}</div>
</div>
<div className="lg:w-1/5 mb-2">
<div className="font-bold">Save</div>
<div className="relative"> <div className="relative">
<Select <Select
name="save" name="save"
@ -104,21 +117,19 @@ const Controls = ({serverStatus, updateServerStatus}) => {
<option value={save.name} key={save.name}>{save.name}</option>))} <option value={save.name} key={save.name}>{save.name}</option>))}
</Select> </Select>
</div> </div>
</td> </div>
</tr> </>
} }
</tbody>
</table>
</div> </div>
} }
actions={ actions={
<div className="flex"> <div className="md:flex">
{isRunning {isRunning
? <> ? <>
<Button onClick={stopServer} size="sm" className="mr-2" type="default">Save & Stop Server</Button> <Button onClick={stopServer} size="sm" className="w-full md:w-auto mb-2 md:mb-0 md:mr-2" type="default">Save & Stop Server</Button>
<Button onClick={killServer} size="sm" type="danger">Kill Server</Button> <Button onClick={killServer} size="sm" type="danger" className="w-full md:w-auto">Kill Server</Button>
</> </>
: <Button isSubmit={true} size="sm" type="success">Start Server</Button> : <Button isSubmit={true} size="sm" type="success" className="w-full md:w-auto">Start Server</Button>
} }
</div> </div>
} }

View File

@ -6,6 +6,7 @@ import {useHistory, useLocation} from "react-router";
import Panel from "../components/Panel"; import Panel from "../components/Panel";
import Input from "../components/Input"; import Input from "../components/Input";
import Label from "../components/Label"; import Label from "../components/Label";
import {Flash} from "../components/Flash";
const Login = ({handleLogin}) => { const Login = ({handleLogin}) => {
const {register, handleSubmit, errors} = useForm(); const {register, handleSubmit, errors} = useForm();
@ -32,7 +33,7 @@ const Login = ({handleLogin}) => {
}, []) }, [])
return ( return (
<div className="h-screen overflow-hidden flex items-center justify-center bg-banner"> <div className="h-screen overflow-hidden flex items-center justify-center bg-black">
<Panel <Panel
title="Login" title="Login"
content={ content={
@ -58,6 +59,7 @@ const Login = ({handleLogin}) => {
</form> </form>
} }
/> />
<Flash/>
</div> </div>
); );
}; };

View File

@ -33,10 +33,10 @@ const Saves = ({serverStatus}) => {
return ( return (
<> <>
<div className="flex mb-6"> <div className="lg:flex mb-6">
<Panel <Panel
title="Create Save" title="Create Save"
className="w-1/2 mr-3" className="lg:w-1/2 lg:mr-3 mb-6 lg:mb-0"
content={ content={
serverStatus.status === "running" serverStatus.status === "running"
? <p className="text-red-light pt-4 pb-24"> ? <p className="text-red-light pt-4 pb-24">
@ -48,7 +48,7 @@ const Saves = ({serverStatus}) => {
/> />
<Panel <Panel
title="Upload Save" title="Upload Save"
className="w-1/2 ml-3" className="lg:w-1/2 lg:ml-3"
content={<UploadSaveForm onSuccess={updateList}/>} content={<UploadSaveForm onSuccess={updateList}/>}
/> />
</div> </div>
@ -57,7 +57,8 @@ const Saves = ({serverStatus}) => {
className="mb-4" className="mb-4"
title="Saves" title="Saves"
content={ content={
<table className="w-full"> <div className="overflow-x-auto">
<table style={{"width" : "max-content"}}>
<thead> <thead>
<tr className="text-left py-1"> <tr className="text-left py-1">
<th>Name</th> <th>Name</th>
@ -68,20 +69,24 @@ const Saves = ({serverStatus}) => {
</thead> </thead>
<tbody> <tbody>
{saves.map(save => {saves.map(save =>
<tr className="py-1" key={save.name}> <tr className="py-2 md:py-1" key={save.name}>
<td className="pr-4">{save.name}</td> <td className="pr-4">{save.name}</td>
<td className="pr-4">{(new Date(save.last_mod)).toISOString().replace('T', ' ').split('.')[0]}</td> <td className="pr-4">{(new Date(save.last_mod)).toISOString().replace('T', ' ').split('.')[0]}</td>
<td>{parseFloat(save.size / 1024 / 1024).toFixed(3)} MB</td> <td className="pr-4">{parseFloat(save.size / 1024 / 1024).toFixed(3)} MB</td>
<td> <td>
<a href={`/api/saves/dl/${save.name}`} className="mr-2"> <a href={`/api/saves/dl/${save.name}`} className="mr-2">
<FontAwesomeIcon className="text-gray-light cursor-pointer hover:text-orange" icon={faDownload}/> <FontAwesomeIcon
className="text-gray-light cursor-pointer hover:text-orange"
icon={faDownload}/>
</a> </a>
<FontAwesomeIcon className="text-red cursor-pointer hover:text-red-light mr-2" onClick={() => deleteSave(save)} icon={faTrashAlt}/> <FontAwesomeIcon className="text-red cursor-pointer hover:text-red-light mr-2"
onClick={() => deleteSave(save)} icon={faTrashAlt}/>
</td> </td>
</tr> </tr>
)} )}
</tbody> </tbody>
</table> </table>
</div>
} }
/> />
</> </>

View File

@ -7,4 +7,13 @@ const client = Axios.create({
} }
}); });
client.interceptors.response.use(res => res, err => {
if(err.response.status === 502) {
window.flash("Service not available", "red");
} else if (err.response.status !== 401) {
window.flash(err.response.data, "red");
}
return Promise.reject(err);
});
export default client; export default client;

View File

@ -2,11 +2,6 @@
@import "tailwindcss/utilities"; @import "tailwindcss/utilities";
@import "tailwindcss/components"; @import "tailwindcss/components";
.bg-banner {
background-image: url("/images/factorio-main-banner.jpg");
background-position: center center;
}
.accentuated-t { .accentuated-t {
border-top: 2px solid rgba(255, 255, 255, 0.1); border-top: 2px solid rgba(255, 255, 255, 0.1);
} }