diff --git a/README.md b/README.md index ef41ec9..725c7dc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ GCGM is the first [Grasscutter](https://github.com/Grasscutters/Grasscutter) plu ## Currently Planned Features: - [x] Loading basic web page -- [ ] Nice looking CSS +- [x] Nice looking CSS +- [ ] Websockets +- [ ] Widgets - [ ] Server performance stats - [ ] See players registered to dispatch server - [ ] See players currently online @@ -15,7 +17,7 @@ The features listed are to achieve an MVP for the first release. ## Important Notes: This plugin is made to run on the current [Development](https://github.com/Grasscutters/Grasscutter/tree/development) branch of Grasscutter. \ -This plugin is in very early development and the web dashboard only displays the default react webpage. \ +This plugin is in very early development and the web dashboard only displays a side panel, so it is not really in a usable state. \ **If you require support please ask on the [Grasscutter Discord](https://discord.gg/T5vZU6UyeG). However, support is not guarenteed.** ## Setup @@ -24,11 +26,12 @@ Coming soon! ### Compile yourself 1. Pull the latest code from github using ``git clone https://github.com/Grasscutters/gcgm-plugin`` in your terminal of choice. -2. Navigate into the newly created ``gcgm-plugin`` folder and run ``gradlew build`` (cmd) **or** ``./gradlew build`` (Powershell, Linux & Mac). -3. Assuming the build succeeded, in your file explorer navigate to the ``gc-plugin`` folder, you should have a ``gcgm-plugin.jar`` file, copy it. -4. Navigate to your ``Grasscutter`` server, find the ``plugins`` folder and paste the ``gcgm-plugin.jar`` into it. -5. Start your server. -6. Your server should then start and after a few seconds, you should be greated with these messages and the server will quit. +2. Locate your grasscutter server and copy the ``grasscutter`` server jar into the newly created ``gcgm-plugin/gc-plugin/lib`` folder +3. Navigate back into the project root folder called ``gcgm-plugin`` folder and run ``gradlew build`` (cmd) **or** ``./gradlew build`` (Powershell, Linux & Mac). +4. Assuming the build succeeded, in your file explorer navigate to the ``gc-plugin`` folder, you should have a ``gcgm-plugin.jar`` file, copy it. +5. Navigate to your ``Grasscutter`` server, find the ``plugins`` folder and paste the ``gcgm-plugin.jar`` into it. +6. Start your server. +7. Your server should then start and after a few seconds, you should be greated with these messages and the server will quit. ``` [WARN] The './plugins/gcgm/www' folder does not exist. [WARN] Please extract the contents of 'DefaultWebApp.zip' from within './plugins/gcgm' to './plugins/gcgm/www diff --git a/gc-plugin/.gitignore b/gc-plugin/.gitignore index a25fc6d..fc5123b 100644 --- a/gc-plugin/.gitignore +++ b/gc-plugin/.gitignore @@ -1 +1,2 @@ +lib/grasscutter-*.jar gcgm-plugin.jar \ No newline at end of file diff --git a/gc-plugin/build.gradle b/gc-plugin/build.gradle index 04fba7f..6d50449 100644 --- a/gc-plugin/build.gradle +++ b/gc-plugin/build.gradle @@ -6,12 +6,14 @@ * User Manual available at https://docs.gradle.org/5.6.3/userguide/tutorial_java_projects.html */ buildscript { - + } plugins { // Apply the java plugin to add support for Java id 'java' + + id 'idea' } sourceCompatibility = 17 @@ -41,7 +43,7 @@ dependencies { implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.2.6' - implementation group: 'tech.xigam', name: 'grasscutter', version: '1.0.2-dev' + //implementation group: 'tech.xigam', name: 'grasscutter', version: '1.0.2-dev' implementation 'io.jsonwebtoken:jjwt-api:0.11.3' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.3', 'io.jsonwebtoken:jjwt-gson:0.11.3' diff --git a/gc-plugin/src/main/java/com/benj4/gcgm/GCGMPlugin.java b/gc-plugin/src/main/java/com/benj4/gcgm/GCGMPlugin.java index c930248..f5c8efa 100644 --- a/gc-plugin/src/main/java/com/benj4/gcgm/GCGMPlugin.java +++ b/gc-plugin/src/main/java/com/benj4/gcgm/GCGMPlugin.java @@ -1,49 +1,35 @@ package com.benj4.gcgm; -import com.sun.net.httpserver.HttpExchange; import emu.grasscutter.Grasscutter; import emu.grasscutter.plugin.Plugin; -import emu.grasscutter.server.dispatch.DispatchServer; import emu.grasscutter.utils.Utils; -import net.lingala.zip4j.ZipFile; +import express.Express; +import io.javalin.http.staticfiles.Location; import java.io.*; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import static com.benj4.gcgm.util.WebUtil.getFileExtensionImproved; -import static com.benj4.gcgm.util.WebUtil.responseStream; public class GCGMPlugin extends Plugin { - File webData; @Override public void onLoad() { - Grasscutter.getLogger().info("Loading GCGM..."); - - File pluginDataDir = new File(Utils.toFilePath(Grasscutter.getConfig().PLUGINS_FOLDER + "/gcgm")); - webData = new File(Utils.toFilePath(Grasscutter.getConfig().PLUGINS_FOLDER + "/gcgm/www")); - String zipFileLoc = Utils.toFilePath(Grasscutter.getConfig().PLUGINS_FOLDER + "/gcgm/DefaultWebApp.zip"); + File pluginDataDir = getDataFolder(); + webData = new File(Utils.toFilePath(getDataFolder().getPath() + "/www")); + String zipFileLoc = Utils.toFilePath(getDataFolder().getPath() + "/DefaultWebApp.zip"); if(!pluginDataDir.exists() && !pluginDataDir.mkdirs()) { - Grasscutter.getLogger().error("Failed to create plugin data directory directory: " + pluginDataDir.getAbsolutePath()); + Grasscutter.getLogger().error("[GCGM] Failed to create plugin data directory directory: " + pluginDataDir.getAbsolutePath()); return; } if(!webData.exists()) { - Grasscutter.getLogger().warn("The './plugins/gcgm/www' folder does not exist."); + Grasscutter.getLogger().warn("[GCGM] The './plugins/GCGM/www' folder does not exist."); // Get the ZIP URL url = null; @@ -59,12 +45,12 @@ public class GCGMPlugin extends Plugin { try { // Copy the the zip from resources to the plugin's data directory if(!new File(zipFileLoc).exists()) { - Grasscutter.getLogger().info("Copying 'DefaultWebApp.zip' to './plugins/gcgm'"); + Grasscutter.getLogger().info("[GCGM] Copying 'DefaultWebApp.zip' to './plugins/GCGM'"); Files.copy(defaultWebAppZip, Paths.get(new File(zipFileLoc).toURI()), StandardCopyOption.REPLACE_EXISTING); } - Grasscutter.getLogger().warn("Please extract the contents of 'DefaultWebApp.zip' from within './plugins/gcgm' to './plugins/gcgm/www"); - Grasscutter.getLogger().warn("Your server will now exit to allow this process to be completed"); + Grasscutter.getLogger().warn("[GCGM] Please extract the contents of 'DefaultWebApp.zip' from within './plugins/GCGM' to './plugins/GCGM/www"); + Grasscutter.getLogger().warn("[GCGM] Your server will now exit to allow this process to be completed"); System.exit(0); } catch (Exception e) { @@ -72,90 +58,31 @@ public class GCGMPlugin extends Plugin { } } else { if(new File(zipFileLoc).exists()) { - Grasscutter.getLogger().info("Note: You can now safely delete 'DefaultWebApp.zip' from within './plugins/gcgm'"); + Grasscutter.getLogger().info("[GCGM] Note: You can now safely delete 'DefaultWebApp.zip' from within './plugins/GCGM'"); } } - Grasscutter.getLogger().info("GCGM has now loaded..."); + Grasscutter.getLogger().info("[GCGM] GCGM has now loaded..."); } @Override public void onEnable() { - Grasscutter.getLogger().info("GCGM Enabled"); + Express app = Grasscutter.getDispatchServer().getServer(); - List wwwFiles = listf(webData.getAbsolutePath()); + app.raw().config.precompressStaticFiles = false; + app.raw().config.addStaticFiles("/gm", webData.getAbsolutePath(), Location.EXTERNAL); + app.raw().config.addSinglePageRoot("/gm", Utils.toFilePath(getDataFolder().getPath() + "/www/index.html"), Location.EXTERNAL); - Grasscutter.getDispatchServer().getServer().createContext("/gm", t -> { - File file = new File(Utils.toFilePath(webData.getAbsolutePath() + "/index.html")); - - responseStream(t, file, "text/html"); - }); - - for (File file : wwwFiles) { - String fromWebRoot = file.getAbsolutePath().replace(webData.getAbsolutePath(), ""); - fromWebRoot = fromWebRoot.replace("\\", "/"); - - Grasscutter.getDispatchServer().getServer().createContext("/gm" + fromWebRoot, t -> { - String fileExtension = getFileExtensionImproved(file.getAbsolutePath()); - String contentType = ""; - switch(fileExtension.toLowerCase()) { - case "png" -> { - contentType = "image/png"; - } - case "js" -> { - contentType = "text/javascript"; - } - case "css" -> { - contentType = "text/css"; - } - case "html" -> { - contentType = "text/html"; - } - case "json", "map" -> { - // A .map file is probably .css.map or .js.map file. So i'll return the save as .json - contentType = "application/json"; - } - case "ico" -> { - contentType = "image/x-icon"; - - } - case "svg" -> { - contentType = "image/svg+xml"; - - } - default ->{ - contentType = "unknown"; - Grasscutter.getLogger().error("Unknown content type for extension: " + fileExtension); - Grasscutter.getLogger().error("For file: " + file.getAbsolutePath()); - } - } - responseStream(t, file, contentType); - }); - } + Grasscutter.getLogger().info("[GCGM] GCGM Enabled"); + Grasscutter.getLogger().info("[GCGM] You can access your GM panel by navigating to http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + + ":" + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 ? Grasscutter.getConfig().getDispatchOptions().PublicPort : Grasscutter.getConfig().getDispatchOptions().Port) + + "/gm" + ); } - - @Override public void onDisable() { - Grasscutter.getLogger().info("GCGM Disabled"); - } - - private List listf(String directoryName) { - File directory = new File(directoryName); - List files = new ArrayList(); - - // Get all files from a directory. - File[] fList = directory.listFiles(); - if (fList != null) { - for (File file : fList) { - if (file.isFile()) { - files.add(file); - } else if (file.isDirectory()) { - files.addAll(listf(file.getAbsolutePath())); - } - } - } - return files; + Grasscutter.getLogger().info("[GCGM] GCGM Disabled"); } } diff --git a/gc-plugin/src/main/java/com/benj4/gcgm/util/WebUtil.java b/gc-plugin/src/main/java/com/benj4/gcgm/util/WebUtil.java deleted file mode 100644 index 5fe48ee..0000000 --- a/gc-plugin/src/main/java/com/benj4/gcgm/util/WebUtil.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.benj4.gcgm.util; - -import com.sun.net.httpserver.HttpExchange; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.file.Files; -import java.util.Collections; - -public class WebUtil { - private static final String WINDOWS_FILE_SEPARATOR = "\\"; - private static final String UNIX_FILE_SEPARATOR = "/"; - private static final String FILE_EXTENSION = "."; - - public static String getFileExtensionImproved(String fileName) { - - if (fileName == null) { - throw new IllegalArgumentException("fileName must not be null!"); - } - - String extension = ""; - - int indexOfLastExtension = fileName.lastIndexOf(FILE_EXTENSION); - - // check last file separator, windows and unix - int lastSeparatorPosWindows = fileName.lastIndexOf(WINDOWS_FILE_SEPARATOR); - int lastSeparatorPosUnix = fileName.lastIndexOf(UNIX_FILE_SEPARATOR); - - // takes the greater of the two values, which mean last file separator - int indexOflastSeparator = Math.max(lastSeparatorPosWindows, lastSeparatorPosUnix); - - // make sure the file extension appear after the last file separator - if (indexOfLastExtension > indexOflastSeparator) { - extension = fileName.substring(indexOfLastExtension + 1); - } - - return extension; - } - - public static void responseStream(HttpExchange t, File file, String contentType) throws IOException { - t.sendResponseHeaders(200, file.length()); - t.getResponseHeaders().put("Content-Type", Collections.singletonList(contentType)); - OutputStream outputStream=t.getResponseBody(); - Files.copy(file.toPath(), outputStream); - outputStream.close(); - } -} diff --git a/gc-plugin/src/main/resources/plugin.json b/gc-plugin/src/main/resources/plugin.json index 51710a0..2ed3bfe 100644 --- a/gc-plugin/src/main/resources/plugin.json +++ b/gc-plugin/src/main/resources/plugin.json @@ -1,7 +1,7 @@ { - "name": "GCGM Plugin", + "name": "GCGM", "description": "Grasscutter Game Master Management UI", - "version": "1.0.0", + "version": "dev-1.0.0", "mainClass": "com.benj4.gcgm.GCGMPlugin", "authors": ["4Benj_"] diff --git a/web-interface/package-lock.json b/web-interface/package-lock.json index 22b4d12..7a7e84b 100644 --- a/web-interface/package-lock.json +++ b/web-interface/package-lock.json @@ -8,6 +8,9 @@ "name": "web-interface", "version": "0.1.0", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.35", + "@fortawesome/free-solid-svg-icons": "^5.15.3", + "@fortawesome/react-fontawesome": "^0.1.14", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", "@testing-library/user-event": "^13.5.0", @@ -2032,6 +2035,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.1.18", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz", + "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.x" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -17542,6 +17590,35 @@ } } }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.1.18", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz", + "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==", + "requires": { + "prop-types": "^15.8.1" + } + }, "@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", diff --git a/web-interface/package.json b/web-interface/package.json index 05d811c..0d82e7f 100644 --- a/web-interface/package.json +++ b/web-interface/package.json @@ -3,6 +3,9 @@ "version": "0.1.0", "private": true, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.35", + "@fortawesome/free-solid-svg-icons": "^5.15.3", + "@fortawesome/react-fontawesome": "^0.1.14", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", "@testing-library/user-event": "^13.5.0", diff --git a/web-interface/src/App.js b/web-interface/src/App.js index 3784575..4cbc81d 100644 --- a/web-interface/src/App.js +++ b/web-interface/src/App.js @@ -1,24 +1,9 @@ -import logo from './logo.svg'; import './App.css'; +import Dashboard from './Dashboard'; function App() { return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
+ ); } diff --git a/web-interface/src/App.test.js b/web-interface/src/App.test.js deleted file mode 100644 index 1f03afe..0000000 --- a/web-interface/src/App.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/web-interface/src/Components/NavigationButton.js b/web-interface/src/Components/NavigationButton.js new file mode 100644 index 0000000..25a9171 --- /dev/null +++ b/web-interface/src/Components/NavigationButton.js @@ -0,0 +1,11 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +export default function NavigationButton(props) { + return ( +
+ +

{props.name}

+
+ ) +} diff --git a/web-interface/src/Components/Sidepanel.css b/web-interface/src/Components/Sidepanel.css new file mode 100644 index 0000000..74db689 --- /dev/null +++ b/web-interface/src/Components/Sidepanel.css @@ -0,0 +1,170 @@ +.dashboard .sidepanel { + position: relative; + float: left; + height: 100vh; + width: 350px; + + /*border-radius: 15px; + + -webkit-box-shadow: 0px 0px 6px 1px rgba(0,0,0,0.75); + -moz-box-shadow: 0px 0px 6px 1px rgba(0,0,0,0.75); + box-shadow: 0px 0px 6px 1px rgba(0,0,0,0.75);*/ + background-color: #fff; + + text-align: center; +} + +.dashboard .sidepanel .logo { + position: relative; + + margin: 15px auto; + /*border-bottom: 1px black solid;*/ + height: 65px; + width: 250px; + + transition: 0.4s linear; +} + +.dashboard .sidepanel .logo:hover { + height: 70px; + width: 275px; + background-color: #eee; + border-radius: 10px; + + -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); + -moz-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); + box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); + cursor: pointer; +} + +.dashboard .sidepanel .logo img { + position: absolute; + transform: translate(0%, -50%); + top: 50%; + left: 5px; + + float: left; + height: 45px; + transition: 0.4s linear; +} + +.dashboard .sidepanel .logo:hover img { + height: 50px; + left: 10px; +} + +.dashboard .sidepanel .logo p { + float: left; + margin: 0; + margin-left: 60px; + line-height: 65px; + font-size: 19px; + transition: 0.4s linear; +} + +.dashboard .sidepanel .logo:hover p { + float: left; + margin: 0; + margin-left: 70px; + line-height: 70px; + font-size: 21px; +} + +.dashboard .sidepanel hr { + position: absolute; + transform: translate(-50%, -50%); + top: 10%; + left: 50%; + width: 70% +} + +.dashboard .navigation { + position:absolute; + transform: translate(-50%, 0%); + top: 8%; + left: 50%; + + margin-top: 60px; +} + +.dashboard .navigation .nav-button { + position: relative; + line-height: 35px; + height: 35px; + width: 200px; + margin: auto; + margin-bottom: 15px; +} + +.dashboard .navigation .nav-button { + cursor: pointer; +} + +.dashboard .navigation .nav-button svg { + position: absolute; + transform: translate(-50%, -50%); + top: 50%; + left: 10%; + width: 18px; + height: 18px; + float: left; + padding: 4px; + + + border-color: #aaa; + border-radius: 5px; + border-style: solid; + border-width: 2px; + + transition: 0.3s linear; +} + +.dashboard .navigation .nav-button:hover svg { + width: 22px; + height: 22px; + + background-color: #64BDF7; +} + +.dashboard .navigation .nav-button p { + float: left; + margin: 0; + padding-left: 60px; + + transition: 0.3s linear; +} + +.dashboard .navigation .nav-button:hover p { + padding-left: 70px; + font-size: 18px; +} + +.dashboard .simple-button#collapse { + position: absolute; + bottom: 2%; + right: 15%; +} + +.dashboard .simple-button { + position: relative; + height: 18px; + margin: auto; + margin-bottom: 13px; +} + +.dashboard .simple-button svg { + position: absolute; + transform: translate(0, -50%); + top: 50%; + left: 4%; + width: 18px; + height: 18px; + float: left; + padding: 4px; + + + border-color: #aaa; + border-radius: 5px; + border-style: solid; + border-width: 2px; +} \ No newline at end of file diff --git a/web-interface/src/Components/Sidepanel.js b/web-interface/src/Components/Sidepanel.js new file mode 100644 index 0000000..1fe96c9 --- /dev/null +++ b/web-interface/src/Components/Sidepanel.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react' +import gclogo from "../img/grasscutter-icon.png" +import NavigationButton from './NavigationButton' +import SimpleButton from './SimpleButton' +import { faArrowLeft, faCogs, faGamepad, faHome, faNetworkWired } from '@fortawesome/free-solid-svg-icons' +import './Sidepanel.css' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +export default class Sidepanel extends Component { + render() { + return ( +
+
+ grasscutter logo +

GCGM Dashboard

+
+
+
+ + + + +
+ +
+ ) + } +} diff --git a/web-interface/src/Components/SimpleButton.js b/web-interface/src/Components/SimpleButton.js new file mode 100644 index 0000000..e39176c --- /dev/null +++ b/web-interface/src/Components/SimpleButton.js @@ -0,0 +1,10 @@ +import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +export default function SimpleButton(props) { + return ( +
+ +
+ ) +} diff --git a/web-interface/src/Dashboard.css b/web-interface/src/Dashboard.css new file mode 100644 index 0000000..e140fa0 --- /dev/null +++ b/web-interface/src/Dashboard.css @@ -0,0 +1,6 @@ +.dashboard { + position: relative; + background-color: #F8F9FB; + width: 100vw; + height: 100vh; +} \ No newline at end of file diff --git a/web-interface/src/Dashboard.js b/web-interface/src/Dashboard.js new file mode 100644 index 0000000..3bd5c7f --- /dev/null +++ b/web-interface/src/Dashboard.js @@ -0,0 +1,14 @@ +import React, { Component } from 'react' +import Sidepanel from './Components/Sidepanel' + +import './Dashboard.css' + +export default class Dashboard extends Component { + render() { + return ( +
+ +
+ ) + } +} diff --git a/web-interface/src/img/grasscutter-icon.png b/web-interface/src/img/grasscutter-icon.png new file mode 100644 index 0000000..6ccf698 Binary files /dev/null and b/web-interface/src/img/grasscutter-icon.png differ diff --git a/web-interface/src/index.js b/web-interface/src/index.js index d563c0f..2cb1087 100644 --- a/web-interface/src/index.js +++ b/web-interface/src/index.js @@ -2,7 +2,6 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; -import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( @@ -10,8 +9,3 @@ root.render( ); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/web-interface/src/logo.svg b/web-interface/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/web-interface/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/web-interface/src/reportWebVitals.js b/web-interface/src/reportWebVitals.js deleted file mode 100644 index 5253d3a..0000000 --- a/web-interface/src/reportWebVitals.js +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/web-interface/src/setupTests.js b/web-interface/src/setupTests.js deleted file mode 100644 index 8f2609b..0000000 --- a/web-interface/src/setupTests.js +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom';