dashy/assets/js/68d50eef.62b70386.js

1 line
42 KiB
JavaScript

"use strict";(self.webpackChunkdashy=self.webpackChunkdashy||[]).push([[992],{5680:(e,n,t)=>{t.d(n,{xA:()=>p,yg:()=>m});var i=t(6540);function a(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function o(e,n){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);n&&(i=i.filter((function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable}))),t.push.apply(t,i)}return t}function r(e){for(var n=1;n<arguments.length;n++){var t=null!=arguments[n]?arguments[n]:{};n%2?o(Object(t),!0).forEach((function(n){a(e,n,t[n])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):o(Object(t)).forEach((function(n){Object.defineProperty(e,n,Object.getOwnPropertyDescriptor(t,n))}))}return e}function l(e,n){if(null==e)return{};var t,i,a=function(e,n){if(null==e)return{};var t,i,a={},o=Object.keys(e);for(i=0;i<o.length;i++)t=o[i],n.indexOf(t)>=0||(a[t]=e[t]);return a}(e,n);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i<o.length;i++)t=o[i],n.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(a[t]=e[t])}return a}var s=i.createContext({}),u=function(e){var n=i.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):r(r({},n),e)),t},p=function(e){var n=u(e.components);return i.createElement(s.Provider,{value:n},e.children)},g="mdxType",d={inlineCode:"code",wrapper:function(e){var n=e.children;return i.createElement(i.Fragment,{},n)}},c=i.forwardRef((function(e,n){var t=e.components,a=e.mdxType,o=e.originalType,s=e.parentName,p=l(e,["components","mdxType","originalType","parentName"]),g=u(t),c=a,m=g["".concat(s,".").concat(c)]||g[c]||d[c]||o;return t?i.createElement(m,r(r({ref:n},p),{},{components:t})):i.createElement(m,r({ref:n},p))}));function m(e,n){var t=arguments,a=n&&n.mdxType;if("string"==typeof e||a){var o=t.length,r=new Array(o);r[0]=c;var l={};for(var s in n)hasOwnProperty.call(n,s)&&(l[s]=n[s]);l.originalType=e,l[g]="string"==typeof e?e:a,r[1]=l;for(var u=2;u<o;u++)r[u]=t[u];return i.createElement.apply(null,r)}return i.createElement.apply(null,t)}c.displayName="MDXCreateElement"},7682:(e,n,t)=>{t.r(n),t.d(n,{contentTitle:()=>r,default:()=>g,frontMatter:()=>o,metadata:()=>l,toc:()=>s});var i=t(8168),a=(t(6540),t(5680));const o={},r="Developing",l={unversionedId:"developing",id:"developing",isDocsHomePage:!1,title:"Developing",description:"This article outlines how to get Dashy running in a development environment, and outlines the basics of the architecture.",source:"@site/docs/developing.md",sourceDirName:".",slug:"/developing",permalink:"/docs/developing",editUrl:"https://github.com/Lissy93/dashy/edit/gh-pages/docs/docs/developing.md",version:"current",frontMatter:{},sidebar:"dashySidebar",previous:{title:"Contributing",permalink:"/docs/contributing"},next:{title:"Development Guides",permalink:"/docs/development-guides"}},s=[{value:"Setting up the Dev Environment",id:"setting-up-the-dev-environment",children:[{value:"Prerequisites",id:"prerequisites",children:[]},{value:"Running the Project",id:"running-the-project",children:[]},{value:"Project Commands",id:"project-commands",children:[]},{value:"Environmental Variables",id:"environmental-variables",children:[]},{value:"Environment Modes",id:"environment-modes",children:[]}]},{value:"Git Strategy",id:"git-strategy",children:[{value:"Git Flow",id:"git-flow",children:[]},{value:"Git Branch Naming",id:"git-branch-naming",children:[]},{value:"Commit Emojis",id:"commit-emojis",children:[]},{value:"PR Guidelines",id:"pr-guidelines",children:[]}]},{value:"Resources for Beginners",id:"resources-for-beginners",children:[]},{value:"App Info",id:"app-info",children:[{value:"Style Guide",id:"style-guide",children:[]},{value:"Application Structure",id:"application-structure",children:[]}]},{value:"Development Tools",id:"development-tools",children:[{value:"Performance - Lighthouse",id:"performance---lighthouse",children:[]},{value:"Dependencies - BundlePhobia",id:"dependencies---bundlephobia",children:[]}]},{value:"Notes",id:"notes-1",children:[{value:"Known Warnings",id:"known-warnings",children:[]}]}],u={toc:s},p="wrapper";function g(e){let{components:n,...t}=e;return(0,a.yg)(p,(0,i.A)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.yg)("h1",{id:"developing"},"Developing"),(0,a.yg)("p",null,"This article outlines how to get Dashy running in a development environment, and outlines the basics of the architecture.\nIf you're adding new features, you may want to check out the ",(0,a.yg)("a",{parentName:"p",href:"/docs/development-guides"},"Development Guides")," docs, for tutorials covering basic tasks."),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#setting-up-the-dev-environment"},"Setting up the Development Environment"),(0,a.yg)("ul",{parentName:"li"},(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#prerequisites"},"Prerequisites")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#running-the-project"},"Running the App")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#project-commands"},"Project Commands")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#environmental-variables"},"Environmental Variables")))),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#git-strategy"},"Git Strategy"),(0,a.yg)("ul",{parentName:"li"},(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#git-flow"},"Flow")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#git-branch-naming"},"Branches")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#commit-emojis"},"Commit emojis")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#pr-guidelines"},"PR Guidelines")))),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#resources-for-beginners"},"Resources for Beginners")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#app-info"},"App Info")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#style-guide"},"Code Style Guide")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#application-structure"},"Application Structure")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#development-tools"},"Development Tools")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"#notes"},"Misc / Notes"))),(0,a.yg)("h2",{id:"setting-up-the-dev-environment"},"Setting up the Dev Environment"),(0,a.yg)("h3",{id:"prerequisites"},"Prerequisites"),(0,a.yg)("p",null,"You will need either the latest or LTS version of ",(0,a.yg)("strong",{parentName:"p"},(0,a.yg)("a",{parentName:"strong",href:"https://nodejs.org/"},"Node.js"))," to build and serve the application and ",(0,a.yg)("strong",{parentName:"p"},(0,a.yg)("a",{parentName:"strong",href:"https://git-scm.com/downloads"},"Git"))," to easily fetch the code, and push any changes. If you plan on running or deploying the container, you'll also need ",(0,a.yg)("strong",{parentName:"p"},(0,a.yg)("a",{parentName:"strong",href:"https://docs.docker.com/get-docker/"},"Docker")),". To avoid any unexpected issues, ensure you've got at least ",(0,a.yg)("strong",{parentName:"p"},(0,a.yg)("a",{parentName:"strong",href:"https://www.npmjs.com/get-npm"},"NPM"))," V 7.5 or ",(0,a.yg)("strong",{parentName:"p"},(0,a.yg)("a",{parentName:"strong",href:"https://classic.yarnpkg.com/en/docs/install/#windows-stable"},"Yarn"))," 1.22 (you may find ",(0,a.yg)("a",{parentName:"p",href:"https://github.com/nvm-sh/nvm"},"NVM")," helpful for switching/ managing versions)."),(0,a.yg)("h3",{id:"running-the-project"},"Running the Project"),(0,a.yg)("ol",null,(0,a.yg)("li",{parentName:"ol"},"Get Code: ",(0,a.yg)("inlineCode",{parentName:"li"},"git clone https://github.com/Lissy93/dashy.git")),(0,a.yg)("li",{parentName:"ol"},"Navigate into the directory: ",(0,a.yg)("inlineCode",{parentName:"li"},"cd dashy")),(0,a.yg)("li",{parentName:"ol"},"Install dependencies: ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn")),(0,a.yg)("li",{parentName:"ol"},"Start dev server: ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn dev"))),(0,a.yg)("p",null,"Dashy should now be being served on ",(0,a.yg)("a",{parentName:"p",href:"http://localhost:8080/"},"http://localhost:8080/"),". Hot reload is enabled, so making changes to any of the files will trigger them to be rebuilt and the page refreshed."),(0,a.yg)("h3",{id:"project-commands"},"Project Commands"),(0,a.yg)("h4",{id:"basics"},"Basics"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn build"))," - In the interest of speed, the application is pre-compiled, this means that the config file is read during build-time, and therefore the app needs to rebuilt for any new changes to take effect. Luckily this is very straight forward. Just run ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn build")," or ",(0,a.yg)("inlineCode",{parentName:"li"},"docker exec -it [container-id] yarn build")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn start"))," - Starts a web server, and serves up the production site from ",(0,a.yg)("inlineCode",{parentName:"li"},"./dist")," (must run build command first)")),(0,a.yg)("h4",{id:"development"},"Development"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn dev"))," - Starts the development server with hot reloading"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn lint"))," - Lints code to ensure it follows a consistent, neat style"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn test"))," - Runs tests, and outputs results")),(0,a.yg)("h4",{id:"utils-and-checks"},"Utils and Checks"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn validate-config"))," - If you have quite a long configuration file, you may wish to check that it's all good to go, before deploying the app. This can be done with ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn validate-config")," or ",(0,a.yg)("inlineCode",{parentName:"li"},"docker exec -it [container-id] yarn validate-config"),". Your config file needs to be in ",(0,a.yg)("inlineCode",{parentName:"li"},"/user-data/conf.yml")," (or within your Docker container at ",(0,a.yg)("inlineCode",{parentName:"li"},"/app/user-data/conf.yml"),"). This will first check that your YAML is valid, and then validates it against Dashy's ",(0,a.yg)("a",{parentName:"li",href:"https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js"},"schema"),"."),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn health-check"))," - Checks that the application is up and running on it's specified port, and outputs current status and response times. Useful for integrating into your monitoring service, if you need to maintain high system availability")),(0,a.yg)("h4",{id:"alternate-start-commands"},"Alternate Start Commands"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn build-and-start"))," - Builds the app, runs checks and starts the production server. Commands are run in parallel, and so is faster than running them in independently. Uses the ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn build")," and ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn start")," commands"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn build-watch"))," - If you find yourself making frequent changes to your configuration, and do not want to have to keep manually rebuilding, then this option is for you. It will watch for changes to any files within the projects root, and then trigger a rebuild. Note that if you are developing new features, then ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn dev")," would be more appropriate, as it's significantly faster at recompiling (under 1 second), and has hot reloading, linting and testing integrated"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("strong",{parentName:"li"},(0,a.yg)("inlineCode",{parentName:"strong"},"yarn pm2-start"))," - Starts the Node server using ",(0,a.yg)("a",{parentName:"li",href:"https://pm2.keymetrics.io/"},"PM2"),", a process manager for Node.js applications, that helps them stay alive. PM2 has some built-in basic monitoring features, and an optional ",(0,a.yg)("a",{parentName:"li",href:"https://pm2.io/"},"management solution"),". If you are running the app on bare metal, it is recommended to use this start command")),(0,a.yg)("h4",{id:"notes"},"Notes"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},"If you are using NPM, replace ",(0,a.yg)("inlineCode",{parentName:"li"},"yarn")," with ",(0,a.yg)("inlineCode",{parentName:"li"},"npm run")),(0,a.yg)("li",{parentName:"ul"},"If you are using Docker, precede each command with ",(0,a.yg)("inlineCode",{parentName:"li"},"docker exec -it [container-id]"),". Container ID can be found by running ",(0,a.yg)("inlineCode",{parentName:"li"},"docker ps")),(0,a.yg)("li",{parentName:"ul"},"You can manage the app using the ",(0,a.yg)("a",{parentName:"li",href:"https://cli.vuejs.org/guide/cli-service.html"},"Vue-CLI Service"),", with ",(0,a.yg)("inlineCode",{parentName:"li"},"npx vue-cli-service [command]"),". Or to start the Vue Management UI, run ",(0,a.yg)("inlineCode",{parentName:"li"},"npx vue ui"),", and open ",(0,a.yg)("inlineCode",{parentName:"li"},"http://localhost:8000"))),(0,a.yg)("h3",{id:"environmental-variables"},"Environmental Variables"),(0,a.yg)("p",null,"All environmental variables are optional. Currently there are not many environmental variables used, as most of the user preferences are stored under ",(0,a.yg)("inlineCode",{parentName:"p"},"appConfig")," in the ",(0,a.yg)("inlineCode",{parentName:"p"},"conf.yml")," file."),(0,a.yg)("p",null,"You can set variables either in your environment, or using the ",(0,a.yg)("a",{parentName:"p",href:"https://github.com/Lissy93/dashy/blob/master/.env"},(0,a.yg)("inlineCode",{parentName:"a"},".env"))," file."),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"NODE_ENV")," - Current environment, can be either development, production or test"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"PORT")," - The port to expose the running application on"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"HOST")," - The host that Dashy is running on, domain or IP"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"BASE_URL")," - The default base path for serving up static assets"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"VUE_APP_DOMAIN")," - Usually the same as BASE_URL, but accessible in frontend"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"INTEGRITY")," - Should enable SRI for build script and link resources"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"IS_DOCKER")," - Computed automatically on build. Indicates if running in container"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"VUE_APP_VERSION")," - Again, set automatically using package.json during build time"),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("inlineCode",{parentName:"li"},"BACKUP_DIR")," - Directory for conf.yml backups")),(0,a.yg)("h3",{id:"environment-modes"},"Environment Modes"),(0,a.yg)("p",null,"You can set the environment using the ",(0,a.yg)("inlineCode",{parentName:"p"},"NODE_ENV")," variable. By default, the correct environment should be selected based on the script you run to start the app. The following environments are supported: ",(0,a.yg)("inlineCode",{parentName:"p"},"production"),", ",(0,a.yg)("inlineCode",{parentName:"p"},"development")," and ",(0,a.yg)("inlineCode",{parentName:"p"},"test"),". For more info, see ",(0,a.yg)("a",{parentName:"p",href:"https://cli.vuejs.org/guide/mode-and-env.html#modes"},"Vue CLI Environment Modes"),"."),(0,a.yg)("hr",null),(0,a.yg)("h2",{id:"git-strategy"},"Git Strategy"),(0,a.yg)("h3",{id:"git-flow"},"Git Flow"),(0,a.yg)("p",null,"Like most Git repos, we are following the ",(0,a.yg)("a",{parentName:"p",href:"https://guides.github.com/introduction/flow"},"Github Flow")," standard."),(0,a.yg)("ol",null,(0,a.yg)("li",{parentName:"ol"},"Create a branch (or fork if you don'd have write access)"),(0,a.yg)("li",{parentName:"ol"},"Code some awesome stuff \ud83e\uddd1\u200d\ud83d\udcbb"),(0,a.yg)("li",{parentName:"ol"},"Add, commit and push your changes to your branch/ fork"),(0,a.yg)("li",{parentName:"ol"},"Head over to GitHub and create a Pull Request"),(0,a.yg)("li",{parentName:"ol"},"Fill in the required sections in the template, and hit submit"),(0,a.yg)("li",{parentName:"ol"},"Follow up with any reviews on your code"),(0,a.yg)("li",{parentName:"ol"},"Merge \ud83c\udf89")),(0,a.yg)("h3",{id:"git-branch-naming"},"Git Branch Naming"),(0,a.yg)("p",null,"The format of your branch name should be something similar to: ",(0,a.yg)("inlineCode",{parentName:"p"},"[TYPE]/[TICKET]_[TITLE]"),"\nFor example, ",(0,a.yg)("inlineCode",{parentName:"p"},"FEATURE/420_Awesome-feature")," or ",(0,a.yg)("inlineCode",{parentName:"p"},"FIX/690_login-server-error")),(0,a.yg)("h3",{id:"commit-emojis"},"Commit Emojis"),(0,a.yg)("p",null,"Using a single emoji at the start of each commit message, to indicate the type task, makes the commit ledger easier to understand, plus it looks cool."),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},"\ud83c\udfa8 ",(0,a.yg)("inlineCode",{parentName:"li"},":art:")," - Improve structure / format of the code."),(0,a.yg)("li",{parentName:"ul"},"\u26a1\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":zap:")," - Improve performance."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udd25 ",(0,a.yg)("inlineCode",{parentName:"li"},":fire:")," - Remove code or files."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udc1b ",(0,a.yg)("inlineCode",{parentName:"li"},":bug:")," - Fix a bug."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\ude91\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":ambulance:")," - Critical hotfix"),(0,a.yg)("li",{parentName:"ul"},"\u2728 ",(0,a.yg)("inlineCode",{parentName:"li"},":sparkles:")," - Introduce new features."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udcdd ",(0,a.yg)("inlineCode",{parentName:"li"},":memo:")," - Add or update documentation."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\ude80 ",(0,a.yg)("inlineCode",{parentName:"li"},":rocket:")," - Deploy stuff."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udc84 ",(0,a.yg)("inlineCode",{parentName:"li"},":lipstick:")," - Add or update the UI and style files."),(0,a.yg)("li",{parentName:"ul"},"\ud83c\udf89 ",(0,a.yg)("inlineCode",{parentName:"li"},":tada:")," - Begin a project."),(0,a.yg)("li",{parentName:"ul"},"\u2705 ",(0,a.yg)("inlineCode",{parentName:"li"},":white_check_mark:")," - Add, update, or pass tests."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udd12\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":lock:")," - Fix security issues."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udd16 ",(0,a.yg)("inlineCode",{parentName:"li"},":bookmark:")," - Make a Release or Version tag."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udea8 ",(0,a.yg)("inlineCode",{parentName:"li"},":rotating_light:")," - Fix compiler / linter warnings."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udea7 ",(0,a.yg)("inlineCode",{parentName:"li"},":construction:")," - Work in progress."),(0,a.yg)("li",{parentName:"ul"},"\u2b06\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":arrow_up:")," - Upgrade dependencies."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udc77 ",(0,a.yg)("inlineCode",{parentName:"li"},":construction_worker:")," - Add or update CI build system."),(0,a.yg)("li",{parentName:"ul"},"\u267b\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":recycle:")," - Refactor code."),(0,a.yg)("li",{parentName:"ul"},"\ud83e\ude79 ",(0,a.yg)("inlineCode",{parentName:"li"},":adhesive_bandage:")," - Simple fix for a non-critical issue."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\udd27 ",(0,a.yg)("inlineCode",{parentName:"li"},":wrench:")," - Add or update configuration files."),(0,a.yg)("li",{parentName:"ul"},"\ud83c\udf71 ",(0,a.yg)("inlineCode",{parentName:"li"},":bento:")," - Add or update assets."),(0,a.yg)("li",{parentName:"ul"},"\ud83d\uddc3\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":card_file_box:")," - Perform database schema related changes."),(0,a.yg)("li",{parentName:"ul"},"\u270f\ufe0f ",(0,a.yg)("inlineCode",{parentName:"li"},":pencil2:")," - Fix typos."),(0,a.yg)("li",{parentName:"ul"},"\ud83c\udf10 ",(0,a.yg)("inlineCode",{parentName:"li"},":globe_with_meridians:")," - Internationalization and translations.")),(0,a.yg)("p",null,"For a full list of options, see ",(0,a.yg)("a",{parentName:"p",href:"https://gitmoji.dev/"},"gitmoji.dev")),(0,a.yg)("h3",{id:"pr-guidelines"},"PR Guidelines"),(0,a.yg)("p",null,"Once you've made your changes, and pushed them to your fork or branch, you're ready to open a pull request!"),(0,a.yg)("p",null,"For a pull request to be merged, it must:"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},"Must be backwards compatible"),(0,a.yg)("li",{parentName:"ul"},"The build, lint and tests (run by GH actions) must pass"),(0,a.yg)("li",{parentName:"ul"},"There must not be any merge conflicts")),(0,a.yg)("p",null,"When you submit your PR, include the required info, by filling out the PR template. Including:"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},"A brief description of your changes"),(0,a.yg)("li",{parentName:"ul"},"The issue, ticket or discussion number (if applicable)"),(0,a.yg)("li",{parentName:"ul"},"For UI relate updates include a screenshot"),(0,a.yg)("li",{parentName:"ul"},"If any dependencies were added, explain why it was needed, state the cost associated, and confirm it does not introduce any security issues"),(0,a.yg)("li",{parentName:"ul"},"Finally, check the checkboxes, to confirm that the standards are met, and hit submit!")),(0,a.yg)("hr",null),(0,a.yg)("h2",{id:"resources-for-beginners"},"Resources for Beginners"),(0,a.yg)("p",null,"New to Web Development? Glad you're here! Dashy is a pretty simple app, so it should make a good candidate for your first PR. Presuming that you already have a basic knowledge of JavaScript, the following articles should point you in the right direction for getting up to speed with the technologies used in this project:"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://opensource.guide/how-to-contribute/"},"Open Source for Beginners")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://v3.vuejs.org/guide/introduction.html"},"Introduction to Vue.js")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://www.taniarascia.com/getting-started-with-vue/"},"Vue.js Walkthrough")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://github.com/lukehoban/es6features"},"ES6 Features")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://blog.logrocket.com/the-definitive-guide-to-scss/"},"Definitive guide to SCSS")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://docker-curriculum.com/"},"Complete beginners guide to Docker")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://training.play-with-docker.com/"},"Docker Classroom - Interactive Tutorials")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://www.freecodecamp.org/news/learn-typescript-in-5-minutes-13eda868daeb/"},"Quick start TypeScript guide")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://www.typescripttutorial.net/"},"Complete TypeScript tutorial series")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://blog.logrocket.com/vue-typescript-tutorial-examples/"},"Using TypeScript with Vue.js")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"http://git-cheatsheet.com/"},"Git cheat sheet")),(0,a.yg)("li",{parentName:"ul"},(0,a.yg)("a",{parentName:"li",href:"https://www.freecodecamp.org/news/what-is-npm-a-node-package-manager-tutorial-for-beginners/"},"Basics of using NPM"))),(0,a.yg)("p",null,"As well as Node, Git and Docker- you'll also need an IDE (e.g. ",(0,a.yg)("a",{parentName:"p",href:"https://code.visualstudio.com/"},"VS Code")," or ",(0,a.yg)("a",{parentName:"p",href:"https://www.vim.org/"},"Vim"),") and a terminal (Windows users may find ",(0,a.yg)("a",{parentName:"p",href:"https://docs.microsoft.com/en-us/windows/wsl/"},"WSL")," more convenient)."),(0,a.yg)("hr",null),(0,a.yg)("h2",{id:"app-info"},"App Info"),(0,a.yg)("h3",{id:"style-guide"},"Style Guide"),(0,a.yg)("p",null,"Linting is done using ",(0,a.yg)("a",{parentName:"p",href:"https://eslint.org/"},"ESLint"),", and using the ",(0,a.yg)("a",{parentName:"p",href:"https://github.com/vuejs/eslint-config-standard"},"Vue.js Styleguide"),", which is very similar to the ",(0,a.yg)("a",{parentName:"p",href:"https://github.com/airbnb/javascript"},"AirBnB Styleguide"),". You can run ",(0,a.yg)("inlineCode",{parentName:"p"},"yarn lint")," to report and fix issues. While the dev server is running, issues will be reported to the console automatically, and any lint errors will trigger the build to fail. Note that all lint checks must pass before any PR can be merged. Linting is also run as a git pre-commit hook"),(0,a.yg)("p",null,"The most significant things to note are:"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},"Indentation should be done with two spaces"),(0,a.yg)("li",{parentName:"ul"},"Strings should use single quotes"),(0,a.yg)("li",{parentName:"ul"},"All statements must end in a semi-colon"),(0,a.yg)("li",{parentName:"ul"},"The final element in all objects must be preceded with a comma"),(0,a.yg)("li",{parentName:"ul"},"Maximum line length is 100"),(0,a.yg)("li",{parentName:"ul"},"There must be exactly one blank line between sections, before function names, and at the end of the file"),(0,a.yg)("li",{parentName:"ul"},"With conditionals, put else on the same line as your if block's closing brace"),(0,a.yg)("li",{parentName:"ul"},"All multiline blocks must use braces"),(0,a.yg)("li",{parentName:"ul"},"Avoid console statements in the frontend")),(0,a.yg)("p",null,"Styleguides:"),(0,a.yg)("ul",null,(0,a.yg)("li",{parentName:"ul"},"Vue: ",(0,a.yg)("a",{parentName:"li",href:"https://vuejs.org/v2/style-guide/"},"Vue styleguide")),(0,a.yg)("li",{parentName:"ul"},"JavaScript: ",(0,a.yg)("a",{parentName:"li",href:"https://github.com/airbnb/javascript"},"github.com/airbnb/javascript"))),(0,a.yg)("hr",null),(0,a.yg)("h3",{id:"application-structure"},"Application Structure"),(0,a.yg)("h4",{id:"files-in-the-root-"},"Files in the Root: ",(0,a.yg)("inlineCode",{parentName:"h4"},"./")),(0,a.yg)("pre",null,(0,a.yg)("code",{parentName:"pre",className:"language-text"},"\u256e\n\u251c\u2500\u2500 package.json # Project meta-data, dependencies and paths to scripts\n\u251c\u2500\u2500 src/ # Project front-end source code\n\u251c\u2500\u2500 server.js # A Node.js server to serve up the /dist directory\n\u251c\u2500\u2500 services/ # All server-side endpoints and utilities\n\u251c\u2500\u2500 vue.config.js # Vue.js configuration\n\u251c\u2500\u2500 Dockerfile # The blueprint for building the Docker container\n\u251c\u2500\u2500 docker-compose.yml # A Docker run command\n\u251c\u2500\u2500 .env # Location for any environmental variables\n\u251c\u2500\u2500 yarn.lock # Auto-generated list of current packages and version numbers\n\u251c\u2500\u2500 docs/ # Markdown documentation\n\u251c\u2500\u2500 README.md # Readme, basic info for getting started\n\u251c\u2500\u2500 LICENSE.md # License for use\n\u256f\n")),(0,a.yg)("h4",{id:"frontend-source-src"},"Frontend Source: ",(0,a.yg)("inlineCode",{parentName:"h4"},"./src/")),(0,a.yg)("pre",null,(0,a.yg)("code",{parentName:"pre",className:"language-text"},"./src\n\u251c\u2500\u2500 App.vue # Vue.js starting file\n\u251c\u2500\u2500 assets # Static non-compiled assets\n\u2502 \u251c\u2500\u2500 fonts # .ttf font files\n\u2502 \u251c\u2500\u2500 locales # All app text, each language in a separate JSON file\n\u2502 \u2570\u2500\u2500 interface-icons # SVG icons used in the app\n\u251c\u2500\u2500 components # All front-end Vue web components\n\u2502 \u251c\u2500\u2500 Charts # Charting components for dynamically displaying widget data\n\u2502 \u2502 \u251c\u2500\u2500 Gauge.vue # A speed-dial style chart for showing 0 - 100 values\n\u2502 \u2502 \u2570\u2500\u2500 PercentageChart.vue # A horizontal bar for showing percentage breakdowns\n\u2502 \u251c\u2500\u2500 Configuration # Components relating to the user config pop-up\n\u2502 \u2502 \u251c\u2500\u2500 AppInfoModal.vue # A modal showing core app info, like version, language, etc\n\u2502 \u2502 \u251c\u2500\u2500 AppVersion.vue # Shows current version from package.json, compares with GitHub\n\u2502 \u2502 \u251c\u2500\u2500 CloudBackupRestore.vue # Form where the user manages cloud sync options\n\u2502 \u2502 \u251c\u2500\u2500 ConfigContainer.vue # Main container, wrapping all other config components\n\u2502 \u2502 \u251c\u2500\u2500 CustomCss.vue # Form where the user can input custom CSS\n\u2502 \u2502 \u251c\u2500\u2500 EditSiteMeta.vue # Form where the user can edit site meta data\n\u2502 \u2502 \u251c\u2500\u2500 JsonEditor.vue # JSON editor, where the user can modify the main config file\n\u2502 \u2502 \u2570\u2500\u2500 RebuildApp.vue # A component allowing user to trigger a rebuild through the UI\n\u2502 \u251c\u2500\u2500 FormElements # Basic form elements used throughout the app\n\u2502 \u2502 \u251c\u2500\u2500 Button.vue # Standard button component\n\u2502 \u2502 \u251c\u2500\u2500 Radio.vue # Standard radio button input\n\u2502 \u2502 \u251c\u2500\u2500 Select.vue # Standard dropdown input selector\n\u2502 \u2502 \u251c\u2500\u2500 Input.vue # Standard text field input component\n\u2502 \u2502 \u2570\u2500\u2500 Toggle.vue # Standard on / off toggle switch\n\u2502 \u251c\u2500\u2500 InteractiveEditor # Components for the interactive UI config editor\n\u2502 \u2502 \u251c\u2500\u2500 AddNewSectionLauncher # Button that launches the EditSection form, used for adding new section\n\u2502 \u2502 \u251c\u2500\u2500 EditAppConfig.vue # Form for editing appConfig\n\u2502 \u2502 \u251c\u2500\u2500 EditPageInfo.vue # Form for editing pageInfo\n\u2502 \u2502 \u251c\u2500\u2500 EditSection.vue # Form for adding / editing sections\n\u2502 \u2502 \u251c\u2500\u2500 EditItem.vue # Form for adding or editing items\n\u2502 \u2502 \u251c\u2500\u2500 EditModeSaveMenu.vue # The bar at the bottom of screen in edit mode, containing save buttons\n\u2502 \u2502 \u251c\u2500\u2500 EditModeTopBanner.vue # The bar at the top of screen in edit mode\n\u2502 \u2502 \u251c\u2500\u2500 ExportConfigMenu.vue # Modal for viewing / exporting edited config\n\u2502 \u2502 \u251c\u2500\u2500 MoveItemTo.vue # Form for moving / copying items to other sections\n\u2502 \u2502 \u2570\u2500\u2500 SaveCancelButtons.vue # Buttons visible in all the edit menus, to save or cancel changes\n\u2502 \u251c\u2500\u2500 LinkItems # Components for Sections and Link Items\n\u2502 \u2502 \u251c\u2500\u2500 Collapsable.vue # The collapsible functionality of sections\n\u2502 \u2502 \u251c\u2500\u2500 IframeModal.vue # Pop-up iframe modal, for viewing websites within the app\n\u2502 \u2502 \u251c\u2500\u2500 Item.vue # Main link item, which is displayed within an item group\n\u2502 \u2502 \u251c\u2500\u2500 ItemGroup.vue # Item group is a section containing icons\n\u2502 \u2502 \u251c\u2500\u2500 ItemIcon.vue # The icon used by both items and sections\n\u2502 \u2502 \u251c\u2500\u2500 ItemOpenMethodIcon.vue # A small icon, visible on hover, indicating opening method\n\u2502 \u2502 \u251c\u2500\u2500 ItemContextMenu.vue # The right-click menu, for showing Item opening methods and info\n\u2502 \u2502 \u251c\u2500\u2500 SectionContextMenu.vue # The right-click menu, for showing Section edit/ open options\n\u2502 \u2502 \u2570\u2500\u2500 StatusIndicator.vue # Traffic light dot, showing if app is online or down\n\u2502 \u251c\u2500\u2500 Minimal View # Components used for the startpage / minimal alternative view\n\u2502 \u2502 \u251c\u2500\u2500 MinimalHeading.vue # Title part of minimal view\n\u2502 \u2502 \u251c\u2500\u2500 MinimalSearch.vue # Search bar for minimal view\n\u2502 \u2502 \u2570\u2500\u2500 MinimalSection.vue # Tabbed-Item section for minimal view\n\u2502 \u251c\u2500\u2500 PageStrcture # Components relating the main structure of the page\n\u2502 \u2502 \u251c\u2500\u2500 Footer.vue # Footer, visible at the bottom of all pages\n\u2502 \u2502 \u251c\u2500\u2500 Header.vue # Header, visible at the top of pages, and includes title and nav\n\u2502 \u2502 \u251c\u2500\u2500 LoadingScreen.vue # Splash screen shown on first load\n\u2502 \u2502 \u251c\u2500\u2500 Nav.vue # Navigation bar, includes a list of links\n\u2502 \u2502 \u2570\u2500\u2500 PageTitle.vue # Page title and sub-title, visible within the Header\n\u2502 \u251c\u2500\u2500 Workspace # Components used for the multi-tasking/ Workspace view\n\u2502 \u2502 \u251c\u2500\u2500 MultiTaskingWeb.vue # When multi-tasking enabled, generates new iframe\n\u2502 \u2502 \u251c\u2500\u2500 SideBar.vue # The left sidebar for the workspace view\n\u2502 \u2502 \u251c\u2500\u2500 SideBarItem.vue # App item for the sidebar view\n\u2502 \u2502 \u251c\u2500\u2500 SideBarSection.vue # Collapsible collection of items within workspace sidebar\n\u2502 \u2502 \u251c\u2500\u2500 WebContent.vue # Workspace iframe view, displays content of current app\n\u2502 \u2502 \u2570\u2500\u2500 WidgetView.vue # Workspace container for displaying widgets in main content\n\u2502 \u251c\u2500\u2500 Widgets # Directory contains all custom widget components\n\u2502 \u2502 \u2570\u2500\u2500 .... # Too many to list, see widget docs instead\n\u2502 \u2570\u2500\u2500 Settings # Components relating to the quick-settings, in the top-right\n\u2502 \u251c\u2500\u2500 AuthButtons.vue # Logout button and other app info\n\u2502 \u251c\u2500\u2500 ConfigLauncher.vue # Icon that when clicked will launch the Configuration component\n\u2502 \u251c\u2500\u2500 CustomThemeMaker.vue # Color pickers for letting user build their own theme\n\u2502 \u251c\u2500\u2500 ItemSizeSelector.vue # Set of buttons used to set and save item size\n\u2502 \u251c\u2500\u2500 KeyboardShortcutInfo.vue# Small pop-up displaying the available keyboard shortcuts\n\u2502 \u251c\u2500\u2500 LanguageSwitcher.vue # Dropdown in a modal for changing app language\n\u2502 \u251c\u2500\u2500 LayoutSelector.vue # Set of buttons, letting the user select their desired layout\n\u2502 \u251c\u2500\u2500 SearchBar.vue # The input field in the header, used for searching the app\n\u2502 \u251c\u2500\u2500 SettingsContainer.vue # Container that wraps all the quick-settings components\n\u2502 \u2570\u2500\u2500 ThemeSelector.vue # Drop-down menu enabling the user to select and change themes\n\u251c\u2500\u2500 main.js # Main front-end entry point\n\u251c\u2500\u2500 registerServiceWorker.js # Registers and manages service workers, for PWA apps\n\u251c\u2500\u2500 router.js # Defines all available application routes\n\u251c\u2500\u2500 styles # Directory of all globally used common SCSS styles\n\u2502 \u251c\u2500\u2500 color-palette.scss # All color variable names and default values\n\u2502 \u251c\u2500\u2500 color-themes.scss # All variable values for built-in themes\n\u2502 \u251c\u2500\u2500 dimensions.scss # Dimensions and sizes as variables\n\u2502 \u251c\u2500\u2500 global-styles.scss # Basics and style resets used globally\n\u2502 \u251c\u2500\u2500 media-queries.scss # Screen sizes and media queries\n\u2502 \u251c\u2500\u2500 style-helpers.scss # SCSS functions used for modifying values\n\u2502 \u251c\u2500\u2500 typography.scss # Font and text styles used globally\n\u2502 \u2570\u2500\u2500 user-defined-themes.scss # Empty, put any custom styles or themes here\n\u251c\u2500\u2500 mixins # Reusable component bases, extended by other views / components\n\u2502 \u251c\u2500\u2500 ChartingMixin.js # Functions for rendering charts in widget components\n\u2502 \u251c\u2500\u2500 GlancesMixin.js # Functions for fetching system info from Glances for widgets\n\u2502 \u251c\u2500\u2500 HomeMixin.js # Functions for homepage, used by default, minimal and workspace views\n\u2502 \u2570\u2500\u2500 WidgetMixin.js # Functions for all widgets, like data fetching, updating and error handling\n\u251c\u2500\u2500 utils # Directory of re-used helper functions\n\u2502 \u251c\u2500\u2500 ArrowKeyNavigation.js # Functionality for arrow-key navigation\n\u2502 \u251c\u2500\u2500 Auth.js # Handles all authentication related actions\n\u2502 \u251c\u2500\u2500 CheckSectionVisibility.js # Checks which parts of the page should be visible/ hidden based on config\n\u2502 \u251c\u2500\u2500 ClickOutside.js # A directive for detecting click, used to hide dropdown, modal or context menu\n\u2502 \u251c\u2500\u2500 ConfigHelpers.js # Helper functions for managing configuration\n\u2502 \u251c\u2500\u2500 CloudBackup.js # Functionality for encrypting, processing and network calls\n\u2502 \u251c\u2500\u2500 ConfigSchema.json # The schema, used to validate the users conf.yml file\n\u2502 \u251c\u2500\u2500 ConfigAccumulator.js # Central place for managing and combining config\n\u2502 \u251c\u2500\u2500 ConfigHelpers.json # Collection of helper functions to process config using accumulator\n\u2502 \u251c\u2500\u2500 ConfigValidator.js # A helper script that validates the config file against schema\n\u2502 \u251c\u2500\u2500 CoolConsole.js # Prints info, warning and error messages to browser console, with a cool style\n\u2502 \u251c\u2500\u2500 defaults.js # Global constants and their default values\n\u2502 \u251c\u2500\u2500 emojis.json # List of emojis with unicode and shortcode, used for emoji icon feature\n\u2502 \u251c\u2500\u2500 EmojiUnicodeRegex.js # Regular expression to validate emoji unicode format, for emoji icons\n\u2502 \u251c\u2500\u2500 ErrorHandler.js # Helper function called when an error is returned\n\u2502 \u251c\u2500\u2500 InitServiceWorker.js # Initializes and manages service worker, if enabled\n\u2502 \u251c\u2500\u2500 Search.js # Helper functions for searching/ filtering items in all views\n\u2502 \u251c\u2500\u2500 JsonToYaml.js # Function that parses and converts raw JSON into valid YAML\n\u2502 \u251c\u2500\u2500 KeycloakAuth.js # Singleton class to manage Keycloak authentication\n\u2502 \u251c\u2500\u2500 languages.js # Handles fetching, switching and validating languages\n\u2502 \u2570\u2500\u2500 ThemeHelper.js # Function that handles the fetching and setting of user themes\n\u2570\u2500\u2500 views # Directory of available pages, corresponding to available routes\n \u251c\u2500\u2500 Home.vue # The home page container\n \u251c\u2500\u2500 About.vue # About page\n \u251c\u2500\u2500 Login.vue # TAuthentication page\n \u251c\u2500\u2500 Minimal.vue # The minimal view\n \u2570\u2500\u2500 Workspace.vue # The workspace view with apps in sidebar\n")),(0,a.yg)("h4",{id:"visualisation-of-source-directory"},"Visualisation of Source Directory"),(0,a.yg)("p",null,(0,a.yg)("img",{parentName:"p",src:"https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/repo-visualization.svg",alt:"File Breakdown"})),(0,a.yg)("hr",null),(0,a.yg)("h2",{id:"development-tools"},"Development Tools"),(0,a.yg)("h3",{id:"performance---lighthouse"},"Performance - Lighthouse"),(0,a.yg)("p",null,"The easiest method of checking performance is to use Chromium's build in auditing tool, Lighthouse. To run the test, open Developer Tools (usually F12) --\x3e Lighthouse and click on the 'Generate Report' button at the bottom."),(0,a.yg)("h3",{id:"dependencies---bundlephobia"},"Dependencies - BundlePhobia"),(0,a.yg)("p",null,(0,a.yg)("a",{parentName:"p",href:"https://bundlephobia.com/"},"BundlePhobia")," is a really useful app that lets you analyze the cost of adding any particular dependency to an application"),(0,a.yg)("hr",null),(0,a.yg)("h2",{id:"notes-1"},"Notes"),(0,a.yg)("h3",{id:"known-warnings"},"Known Warnings"),(0,a.yg)("p",null,"When running the build command, several warnings appear. These are not errors, and do not affect the security or performance of the application. They will be addressed in a future update"),(0,a.yg)("p",null,(0,a.yg)("inlineCode",{parentName:"p"},"WARN A new version of sass-loader is available. Please upgrade for best experience.")," - Currently we're using an older version of SASS loader, since the more recent releases do not seem to be compatible with the Vue CLI's webpack configuration."),(0,a.yg)("p",null,(0,a.yg)("inlineCode",{parentName:"p"},"WARN asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).")," - For the PWA to support Windows 10, a splash screen asset is required, and is quite large. This throws a warning, however PWA assets are not loaded until needed, so shouldn't have any impact on application performance. A similar warning is thrown for the Raleway font, and that is looking to be addressed."))}g.isMDXComponent=!0}}]);