diff --git a/ui/App/App.jsx b/ui/App/App.jsx index b141bfb..dfced49 100644 --- a/ui/App/App.jsx +++ b/ui/App/App.jsx @@ -8,6 +8,9 @@ import {BrowserRouter} from "react-router-dom"; import Logs from "./views/Logs"; import Saves from "./views/Saves"; import Layout from "./components/Layout"; +import server from "../api/resources/server"; +import Mods from "./views/Mods"; +import UserManagement from "./views/UserManagment"; const App = () => { @@ -15,6 +18,13 @@ const App = () => { const [serverStatus, setServerStatus] = useState(null); const history = useHistory(); + const updateServerStatus = async () => { + const status = await server.status(); + if (status.success) { + setServerStatus(status) + } + } + const handleAuthenticationStatus = async () => { const status = await user.status(); setIsAuthenticated(status.success); @@ -31,7 +41,7 @@ const App = () => { const ProtectedRoute = useCallback(({component: Component, ...rest}) => ( ( isAuthenticated - ? + ? : { ()}/> - + - + - + diff --git a/ui/App/components/Layout.jsx b/ui/App/components/Layout.jsx index 3e7a3e3..e039908 100644 --- a/ui/App/components/Layout.jsx +++ b/ui/App/components/Layout.jsx @@ -3,14 +3,11 @@ import server from "../../api/resources/server"; import {NavLink} from "react-router-dom"; import Button from "../elements/Button"; -const Layout = ({children, handleLogout, serverStatus, setServerStatus}) => { +const Layout = ({children, handleLogout, serverStatus, updateServerStatus}) => { useEffect(() => { (async () => { - const status = await server.status(); - if (status.success) { - setServerStatus(status) - } + updateServerStatus() })(); }, []); diff --git a/ui/App/elements/Button.jsx b/ui/App/elements/Button.jsx index c2b0594..fe823e4 100644 --- a/ui/App/elements/Button.jsx +++ b/ui/App/elements/Button.jsx @@ -1,8 +1,9 @@ import React from "react"; -const Button = ({ children, type, onClick, isSubmit, className }) => { +const Button = ({ children, type, onClick, isSubmit, className, size }) => { let color = ''; + let padding = ''; switch (type) { case 'success': @@ -15,9 +16,17 @@ const Button = ({ children, type, onClick, isSubmit, className }) => { color = 'bg-gray-light hover:glow-orange hover:bg-orange' } + switch (size) { + case 'sm': + padding = 'py-1 px-2'; + break; + default: + padding = 'py-2 px-4' + } + return ( - ); diff --git a/ui/App/elements/ButtonLink.jsx b/ui/App/elements/ButtonLink.jsx index 7f9fb93..72839f0 100644 --- a/ui/App/elements/ButtonLink.jsx +++ b/ui/App/elements/ButtonLink.jsx @@ -1,11 +1,36 @@ import React from "react"; -const ButtonLink = () => { +const ButtonLink = ({children, href, type, target, className, size}) => { + + let color = ''; + let padding = ''; + + switch (type) { + case 'success': + color = 'bg-green hover:glow-green hover:bg-green-light'; + break; + case 'danger': + color = 'bg-red hover:glow-red hover:bg-red-light'; + break; + default: + color = 'bg-gray-light hover:glow-orange hover:bg-orange' + } + + switch (size) { + case 'sm': + padding = 'py-1 px-2'; + break; + default: + padding = 'py-2 px-4' + } + return ( - + + {children} + ); } diff --git a/ui/App/elements/Panel.jsx b/ui/App/elements/Panel.jsx index 586387f..eeca886 100644 --- a/ui/App/elements/Panel.jsx +++ b/ui/App/elements/Panel.jsx @@ -1,8 +1,8 @@ import React from "react"; -const Panel = ({title, content, actions}) => { +const Panel = ({title, content, actions, className}) => { return ( -
+
{title}
diff --git a/ui/App/forms/CreateSaveForm.jsx b/ui/App/forms/CreateSaveForm.jsx new file mode 100644 index 0000000..47cd83d --- /dev/null +++ b/ui/App/forms/CreateSaveForm.jsx @@ -0,0 +1,35 @@ +import {useForm} from "react-hook-form"; +import Button from "../elements/Button"; +import React from "react"; +import saves from "../../api/resources/saves"; + +const CreateSaveForm = ({onSuccess}) => { + const {register, handleSubmit, errors} = useForm(); + + + const onSubmit = async data => { + const res = await saves.create(data.savefile); + if (res.success) { + onSuccess(); + } + }; + + return ( +
+
+ + + {errors.savefile && Savefile Name is required} +
+ +
+ ) +} + +export default CreateSaveForm; \ No newline at end of file diff --git a/ui/App/forms/UploadSaveForm.jsx b/ui/App/forms/UploadSaveForm.jsx new file mode 100644 index 0000000..f10f68e --- /dev/null +++ b/ui/App/forms/UploadSaveForm.jsx @@ -0,0 +1,35 @@ +import Button from "../elements/Button"; +import React from "react"; +import {useForm} from "react-hook-form"; +import saves from "../../api/resources/saves"; + + +const UploadSaveForm = ({onSuccess}) => { + + const {register, handleSubmit, errors} = useForm(); + const onSubmit = async data => { + const res = await saves.upload(data.savefile); + if (res.success) { + onSuccess(); + } + }; + + return ( +
+
+ + + {errors.savefile && Savefile Name is required} +
+ +
+ ) +} + +export default UploadSaveForm; \ No newline at end of file diff --git a/ui/App/views/Controls.jsx b/ui/App/views/Controls.jsx index 5107506..ba403e6 100644 --- a/ui/App/views/Controls.jsx +++ b/ui/App/views/Controls.jsx @@ -1,21 +1,26 @@ -import React, {useEffect, useState} from "react"; +import React, {useCallback, useEffect, useMemo, useState} from "react"; import Panel from "../elements/Panel"; import Button from "../elements/Button"; import server from "../../api/resources/server"; -const Controls = ({serverStatus}) => { +const Controls = ({serverStatus, updateServerStatus}) => { const [factorioVersion, setFactorioVersion] = useState('unknown'); - const isRunning = () => { - return serverStatus.data.status === 'running'; + const isRunning = serverStatus.data.status === 'running'; + + const startServer = async () => { + await server.start('0.0.0.0',34197,'DEATH.zip'); + await updateServerStatus(); } - const startServer = () => { - + const stopServer = async () => { + await server.stop(); + await updateServerStatus(); } - const stopServer = () => { - + const killServer = async () => { + await server.kill(); + await updateServerStatus(); } useEffect(() => { @@ -31,7 +36,7 @@ const Controls = ({serverStatus}) => { +
@@ -54,10 +59,10 @@ const Controls = ({serverStatus}) => { } actions={
- { isRunning() + { isRunning ? <> - + : } diff --git a/ui/App/views/Mods.jsx b/ui/App/views/Mods.jsx new file mode 100644 index 0000000..331217a --- /dev/null +++ b/ui/App/views/Mods.jsx @@ -0,0 +1,22 @@ +import Panel from "../elements/Panel"; +import React from "react"; + +const Mods = () => { + + return ( + <> + Test} + /> + + Test} + /> + + ) +} + +export default Mods; \ No newline at end of file diff --git a/ui/App/views/Saves.jsx b/ui/App/views/Saves.jsx index a11b3ab..7b552d3 100644 --- a/ui/App/views/Saves.jsx +++ b/ui/App/views/Saves.jsx @@ -1,44 +1,87 @@ import React, {useEffect, useState} from "react"; -import save from "../../api/resources/saves"; +import saveClient from "../../api/resources/saves"; import Panel from "../elements/Panel"; +import ButtonLink from "../elements/ButtonLink"; +import Button from "../elements/Button"; +import {useForm} from "react-hook-form"; +import CreateSaveForm from "../forms/CreateSaveForm"; +import UploadSaveForm from "../forms/UploadSaveForm"; -const Saves = () => { +const Saves = ({serverStatus}) => { const [saves, setSaves] = useState([]); + const {register, handleSubmit, errors} = useForm(); + + const updateList = async () => { + const res = await saveClient.list(); + if (res.success) { + setSaves(res.data); + } + } useEffect(() => { - (async () => { - const list = await save.list(); - if (list.success) { - console.log(list) - setSaves(list.data); - } - })() - }, []) + updateList() + }, []); + + const deleteSave = async (save) => { + const res = await saveClient.delete(save); + if (res.success) { + updateList() + } + } return ( - -
- - - - - - - - - {saves.map(save => - - - - - - )} - -
NameLast Modified AtSizeActions
{save.name}{save.last_mod}{parseFloat(save.size / 1024 / 1024).toFixed(3)} MB
} - /> + <> +
+ + Create a new Save is only possible if the Factorio server is + not running. +

+ : + } + /> + } + /> +
+ + + + + Name + Last Modified At + Size + Actions + + + + {saves.map(save => + + {save.name} + {(new Date(save.last_mod)).toISOString().replace('T', ' ').split('.')[0]} + {parseFloat(save.size / 1024 / 1024).toFixed(3)} MB + + Download + + + + )} + + + } + /> + ) } diff --git a/ui/App/views/UserManagment.jsx b/ui/App/views/UserManagment.jsx new file mode 100644 index 0000000..46ed447 --- /dev/null +++ b/ui/App/views/UserManagment.jsx @@ -0,0 +1,20 @@ +import Panel from "../elements/Panel"; +import React from "react"; + +const UserManagement = () => { + return ( + <> + + + + ) +} + +export default UserManagement; \ No newline at end of file diff --git a/ui/api/resources/saves.js b/ui/api/resources/saves.js index 68e9559..502869a 100644 --- a/ui/api/resources/saves.js +++ b/ui/api/resources/saves.js @@ -5,4 +5,12 @@ export default { const response = await client.get('/api/saves/list'); return response.data; }, + delete: async (save) => { + const response = await client.get(`/api/saves/rm/${save.name}`); + return response.data; + }, + create: async (name) => { + const response = await client.get(`/api/saves/create/${name}`); + return response.data; + } } \ No newline at end of file diff --git a/ui/api/resources/server.js b/ui/api/resources/server.js index a003538..0b94b9c 100644 --- a/ui/api/resources/server.js +++ b/ui/api/resources/server.js @@ -9,4 +9,20 @@ export default { const response = await client.get('/api/server/status'); return response.data; }, + stop: async () => { + const response = await client.post('/api/server/stop'); + return response.data; + }, + start: async (ip, port, savefile) => { + const response = await client.post('/api/server/start', { + bindip: ip, + savefile, + port + }); + return response.data; + }, + kill: async () => { + const response = await client.get('/api/server/kill'); + return response.data; + } } \ No newline at end of file