From ffe92674e5c3a51ded4ce6fe5c404af0c3a1a313 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 27 Jul 2020 16:02:33 +0200 Subject: [PATCH 01/35] Improve panels design --- assets/sass/layout.scss | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 9890bcc..12ae4d8 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -252,15 +252,23 @@ section > h2, .panel > h2 { align-items: center; position: relative; text-align: center; - margin-top: 16px; + margin-top: 4px; - &::before, &::after { + font-size: 24px; + line-height: 1; + + .feather { + margin: 0 16px 0 0; + opacity: 0.1; + } + + &::after { content: ""; flex: 1; - margin: 0 32px; + margin: 0 16px; height: 0; border-bottom: 1px solid $defaultTextColor; - opacity: 0.2; + opacity: 0.1; } } @@ -353,7 +361,7 @@ form { & + .feather { position: absolute; - z-index: -1; + pointer-events: none; right: 8px; bottom: 8px; @@ -557,7 +565,8 @@ button, .button { } .panel { - margin: 16px 0; + position: relative; + margin: 16px 0 48px; padding: 8px; background-color: $panelBackground; border-radius: 5px; @@ -565,6 +574,14 @@ button, .button { p { margin: 16px 8px; } + + > .feather:first-child { + position: absolute; + --icon-size: 24px; + opacity: 0.1; + top: 8px; + left: 8px; + } } .sub-panel { From 3b94b9818c945645c2e6e5e14407f2b3ac9bf07b Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 27 Jul 2020 17:29:52 +0200 Subject: [PATCH 02/35] Fix .inline-fields alignment --- assets/sass/layout.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 12ae4d8..964fa6f 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -348,6 +348,7 @@ form { display: block; padding: 32px 8px 8px 8px; width: 100%; + height: 60px; } select { @@ -426,7 +427,7 @@ form { .inline-fields { display: flex; flex-direction: row; - align-items: center; + align-items: start; margin: 16px auto; .form-field { From 7d75df2f56d17bfcd0cd48b453b6b4a2a3fde2bf Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 30 Jul 2020 10:54:14 +0200 Subject: [PATCH 03/35] Improve tips styling and allow it in any button --- assets/sass/layout.scss | 82 +++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 964fa6f..a4091db 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -21,6 +21,46 @@ body { background-color: $backgroundColor; } +@mixin tip { + position: relative; + + .tip { + visibility: hidden; + position: absolute; + z-index: 10000; + pointer-events: none; + display: block; + width: max-content; + height: 30px; + padding: 4px 8px; + line-height: 22px; + top: calc(100% + 8px); + left: 50%; + transform: translateX(-50%); + + text-align: center; + font-size: 18px; + color: $defaultTextColor; + opacity: 0; + transition: opacity ease-out 100ms, visibility step-end 150ms; + transition-delay: 0ms; + background-color: #000; + border-radius: 5px; + + text-transform: initial; + font-weight: initial; + } + + &:hover, &:active { + .tip { + visibility: visible; + opacity: 1; + transition: opacity ease-out 100ms; + transition-delay: 150ms; + } + } +} + header { display: flex; flex-direction: row; @@ -77,13 +117,6 @@ header { &:not(button) { background-color: rgba(255, 255, 255, 0.07); } - - .tip { - visibility: visible; - opacity: 1; - transition: opacity ease-out 100ms; - transition-delay: 150ms; - } } .feather { @@ -99,11 +132,6 @@ header { .feather { margin-right: 0; } - - .tip { - text-transform: initial; - font-weight: initial; - } } form { @@ -179,32 +207,12 @@ header { @media (min-width: $menuLayoutSwitchTreshold) { nav ul li { - a, button { - .tip { - visibility: hidden; - position: absolute; - display: block; - width: max-content; - height: 30px; - padding: 4px 8px; - line-height: 22px; - top: calc(100% + 8px); - left: 50%; - transform: translateX(-50%); - - text-align: center; - font-size: 18px; - color: $defaultTextColor; - opacity: 0; - transition: opacity ease-out 100ms, visibility step-end 150ms; - transition-delay: 0ms; - background-color: #000; - border-radius: 5px; - } + a, button, .button { + @include tip; } &:last-child { - a, button { + a, button, .button { .tip { left: unset; right: 4px; @@ -226,6 +234,10 @@ footer { main { flex: 1; padding: 8px; + + button, .button { + @include tip; + } } h1 { From 95f61333f6fefe57f20b7fd6f7fdf7d3d916d25c Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 30 Jul 2020 10:54:29 +0200 Subject: [PATCH 04/35] Add basic breadcrumb style --- assets/sass/layout.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index a4091db..1ae1210 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -564,6 +564,19 @@ button, .button { } } +.breadcrumb { + list-style: none; + display: flex; + flex-direction: row; + margin: 0; + padding: 8px; + + > *:not(:first-child)::before { + content: '›'; + padding: 0 8px; + } +} + // --- // --- Layout helpers // --- From 3f3bec85fc806c992ab0d952af496415d49f5209 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 30 Jul 2020 11:14:02 +0200 Subject: [PATCH 05/35] layout.scss: fix select chevron position when field has error --- assets/sass/layout.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 1ae1210..178bd47 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -376,7 +376,7 @@ form { position: absolute; pointer-events: none; right: 8px; - bottom: 8px; + top: 30px; transition: transform 150ms ease-out; } From b136bdc64ebce998018e73618ca314d5ceee525d Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sun, 30 Aug 2020 09:32:22 +0200 Subject: [PATCH 06/35] Fix main header css selector and set default icon size to 16px --- assets/sass/layout.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 178bd47..cd3f0d8 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -61,7 +61,7 @@ body { } } -header { +body > header { display: flex; flex-direction: row; justify-content: space-between; @@ -623,7 +623,7 @@ button, .button { // --- .feather { flex-shrink: 0; - --icon-size: 24px; + --icon-size: 16px; width: var(--icon-size); height: var(--icon-size); stroke: currentColor; From 4316daf241b31b31a873df8cf18fcfad8f797bad Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sun, 30 Aug 2020 21:02:37 +0200 Subject: [PATCH 07/35] header: add menu dropdowns and auth-user special menu item --- assets/sass/layout.scss | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index cd3f0d8..a11e09a 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -62,6 +62,7 @@ body { } body > header { + z-index: 0; display: flex; flex-direction: row; justify-content: space-between; @@ -101,6 +102,7 @@ body > header { font-size: 20px; li { + position: relative; list-style: none; a, button { @@ -140,6 +142,35 @@ body > header { align-items: center; padding: 0; } + + &.auth-user { + img { + width: 48px; + height: 48px; + border-radius: 3px; + margin-right: 8px; + } + } + + .dropdown { + position: absolute; + z-index: -1; + top: 100%; + right: 0; + display: none; + + white-space: nowrap; + background: $headerBackground; + border-radius: 0 0 3px 3px; + + a { + padding: 0 8px; + } + } + + &:hover .dropdown { + display: block; + } } } @@ -177,7 +208,7 @@ body > header { } } - ul { + > ul { flex-direction: column; position: absolute; z-index: 10; @@ -200,6 +231,12 @@ body > header { font-weight: inherit; } } + + .dropdown { + position: initial; + display: block; + padding-left: 32px; + } } } } From 7143e7a84323d0f714f9af76e20b034bcc0930e1 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sun, 30 Aug 2020 21:02:57 +0200 Subject: [PATCH 08/35] Add textarea styling --- assets/sass/layout.scss | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index a11e09a..c228448 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -378,7 +378,7 @@ form { } } - input, select, .input-group { + input, select, textarea, .input-group { border: 0; color: $defaultTextColor; background: lighten($panelBackground, 4%); @@ -393,7 +393,7 @@ form { } } - input, select, .form-display { + input, select, textarea, .form-display { display: block; padding: 32px 8px 8px 8px; width: 100%; @@ -424,6 +424,11 @@ form { } } + textarea { + resize: vertical; + min-height: 100px; + } + input[type=color] { height: calc(32px + 8px + 32px); } From 61c8f32c47a49a6352edf0c919fd769b982617c7 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sun, 30 Aug 2020 21:04:59 +0200 Subject: [PATCH 09/35] Fix header z-index --- assets/sass/layout.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index c228448..8d29983 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -62,7 +62,7 @@ body { } body > header { - z-index: 0; + z-index: 50; display: flex; flex-direction: row; justify-content: space-between; From f45daa4a9f5dbc341d18ff3faa1a2ae86d7fb888 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 2 Sep 2020 20:41:58 +0200 Subject: [PATCH 10/35] Improve links hover feedback --- assets/sass/layout.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 8d29983..f46c5e7 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -334,7 +334,7 @@ a { text-decoration: none; &:hover { - color: lighten($secondary, 5%); + color: lighten($secondary, 10%); } .feather.feather-external-link { From 54faa5c873c52d62a304dcada9e31ff2ee359f72 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 3 Sep 2020 08:28:57 +0200 Subject: [PATCH 11/35] Use config package's multi-directory built-in support This avoids full object overriding when you just want to set specific fields --- config/default.ts | 4 ++-- config/production.ts | 4 ++-- config/test.ts | 4 ++-- package.json | 2 +- src/main.ts | 6 ++++++ tsconfig.json | 3 +-- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/config/default.ts b/config/default.ts index 8f2cf14..aed8601 100644 --- a/config/default.ts +++ b/config/default.ts @@ -1,4 +1,4 @@ -export default Object.assign(require("wms-core/config/default").default, { +export default { app: { name: 'Example App', contact_email: 'contact@example.net' @@ -42,4 +42,4 @@ export default Object.assign(require("wms-core/config/default").default, { cache: false }, approval_mode: false, -}); \ No newline at end of file +} diff --git a/config/production.ts b/config/production.ts index d3ba121..4143867 100644 --- a/config/production.ts +++ b/config/production.ts @@ -1,4 +1,4 @@ -export default Object.assign(require("wms-core/config/production").default, { +export default { log_level: "DEBUG", db_log_level: "ERROR", public_url: "https://watch-my.stream", @@ -12,4 +12,4 @@ export default Object.assign(require("wms-core/config/production").default, { secure: true, allow_invalid_tls: false } -}); \ No newline at end of file +} diff --git a/config/test.ts b/config/test.ts index 149ace2..61f7c4a 100644 --- a/config/test.ts +++ b/config/test.ts @@ -1,4 +1,4 @@ -export default Object.assign(require("wms-core/config/test").default, { +export default { mysql: { host: "localhost", user: "root", @@ -6,4 +6,4 @@ export default Object.assign(require("wms-core/config/test").default, { database: "wms2_test", create_database_automatically: true } -}); \ No newline at end of file +} diff --git a/package.json b/package.json index beda726..312a920 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "test": "jest --verbose --runInBand", "dist-webpack": "webpack --mode production", - "dist": "tsc && npm run dist-webpack", + "dist": "(test ! -d dist || rm -r dist) && tsc && npm run dist-webpack && mkdir -p dist/core-config && cp -r node_modules/wms-core/config/* dist/core-config/", "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", "start": "yarn dist && node dist/main.js" }, diff --git a/src/main.ts b/src/main.ts index 1706351..17025eb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,9 @@ +// Load config from specified path or default + wms-core/config (default defaults) +process.env['NODE_CONFIG_DIR'] = + (process.env['NODE_CONFIG_DIR'] || './config/') + + require('path').delimiter + + __dirname + '/core-config/'; + import Logger from "wms-core/Logger"; import App from "./App"; import config from "config"; diff --git a/tsconfig.json b/tsconfig.json index a6bf63a..803227e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,6 @@ ] }, "include": [ - "src/**/*", - "node_modules/wms-core" + "src/**/*" ] } \ No newline at end of file From 52929e5be01141894b6431141f903943691e0105 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 3 Sep 2020 08:29:37 +0200 Subject: [PATCH 12/35] package.json: fix repository url --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 312a920..da81981 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "example-app", "version": "0.1.0", "description": "Example App based on wms-core", - "repository": "git@gitlab.com:ArisuOngaku/wms-boilerplate.git", + "repository": "https://gitlab.com/ArisuOngaku/wms-boilerplate", "author": "Alice Gaudon ", "private": true, "main": "dist/main.js", From bce48f81b66847fc14814dfc23fa102eeedd4f13 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 3 Sep 2020 08:30:15 +0200 Subject: [PATCH 13/35] package.json: upgrade dependencies and move wms-core to regular deps --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index da81981..9135767 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,13 @@ "@types/formidable": "^1.0.31", "@types/jest": "^26.0.4", "@types/mysql": "^2.15.15", - "@types/node": "^13.13.2", + "@types/node": "^14.6.3", "@types/nodemailer": "^6.4.0", "@types/nunjucks": "^3.1.3", "@types/ws": "^7.2.6", "babel-loader": "^8.1.0", "concurrently": "^5.1.0", - "css-loader": "^3.5.2", + "css-loader": "^4.2.2", "feather-icons": "^4.28.0", "file-loader": "^6.0.0", "imagemin": "^7.0.1", @@ -38,19 +38,19 @@ "imagemin-svgo": "^8.0.0", "img-loader": "^3.0.1", "jest": "^26.1.0", - "mini-css-extract-plugin": "^0.9.0", + "mini-css-extract-plugin": "^0.11.0", "node-sass": "^4.14.0", "nodemon": "^2.0.3", - "sass-loader": "^9.0.2", + "sass-loader": "^10.0.1", "ts-jest": "^26.1.1", - "typescript": "^3.8.3", + "typescript": "^4.0.2", "uglifyjs-webpack-plugin": "^2.2.0", "webpack": "^4.43.0", - "webpack-cli": "^3.3.11", - "wms-core": "^0" + "webpack-cli": "^3.3.11" }, "dependencies": { "config": "^3.3.1", - "express": "^4.17.1" + "express": "^4.17.1", + "wms-core": "^0.21.7" } } From 5157b12b77b5ffe0cea7b8f2caa26793fafe2195 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 3 Sep 2020 11:46:00 +0200 Subject: [PATCH 14/35] tsconfig.json: restore wms-core/types include and add test --- src/controllers/HomeController.ts | 8 ++++++++ tsconfig.json | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/controllers/HomeController.ts b/src/controllers/HomeController.ts index 847c759..ad035b4 100644 --- a/src/controllers/HomeController.ts +++ b/src/controllers/HomeController.ts @@ -5,6 +5,7 @@ export default class HomeController extends Controller { routes(): void { this.get('/', this.getHome, 'home'); this.get('/about', this.getAbout, 'about'); + this.get('/back', this.goBack, 'about'); } private async getHome(req: Request, res: Response) { @@ -14,4 +15,11 @@ export default class HomeController extends Controller { private async getAbout(req: Request, res: Response) { res.render('about'); } + + /** + * This is to test and assert that wms-core extended types are available + */ + private async goBack(req: Request, res: Response) { + res.redirectBack(); + } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 803227e..18f1bee 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,7 @@ ] }, "include": [ - "src/**/*" + "src/**/*", + "node_modules/wms-core/types" ] } \ No newline at end of file From 3b124a993bfcb9e7799bef59c50055bd99f02482 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 3 Sep 2020 12:58:07 +0200 Subject: [PATCH 15/35] main.ts: fix config path order --- package.json | 2 +- src/main.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9135767..f108311 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "test": "jest --verbose --runInBand", "dist-webpack": "webpack --mode production", - "dist": "(test ! -d dist || rm -r dist) && tsc && npm run dist-webpack && mkdir -p dist/core-config && cp -r node_modules/wms-core/config/* dist/core-config/", + "dist": "(test ! -d dist || rm -r dist) && tsc && npm run dist-webpack", "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", "start": "yarn dist && node dist/main.js" }, diff --git a/src/main.ts b/src/main.ts index 17025eb..b3f32d5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,14 +1,16 @@ // Load config from specified path or default + wms-core/config (default defaults) process.env['NODE_CONFIG_DIR'] = - (process.env['NODE_CONFIG_DIR'] || './config/') + __dirname + '/../node_modules/wms-core/config/' + require('path').delimiter - + __dirname + '/core-config/'; + + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/'); import Logger from "wms-core/Logger"; import App from "./App"; import config from "config"; (async () => { + Logger.debug('Config path:', process.env['NODE_CONFIG_DIR']); + const app = new App(config.get('port')); await app.start(); })().catch(err => { From 3598f6818368f72566349931483285bbbc4089a7 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sat, 5 Sep 2020 17:36:08 +0200 Subject: [PATCH 16/35] Add font-awesome --- assets/js/app.js | 1 + assets/js/font-awesome.js | 4 ++++ package.json | 1 + 3 files changed, 6 insertions(+) create mode 100644 assets/js/font-awesome.js diff --git a/assets/js/app.js b/assets/js/app.js index 10fa287..039ac2e 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -3,6 +3,7 @@ import './message_icons'; import './forms'; import './copyable_text'; import './main_menu'; +import './font-awesome'; import '../sass/app.scss'; diff --git a/assets/js/font-awesome.js b/assets/js/font-awesome.js new file mode 100644 index 0000000..5ce65ff --- /dev/null +++ b/assets/js/font-awesome.js @@ -0,0 +1,4 @@ +import '../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss'; +import '../../node_modules/@fortawesome/fontawesome-free/scss/regular.scss'; +import '../../node_modules/@fortawesome/fontawesome-free/scss/solid.scss'; +import '../../node_modules/@fortawesome/fontawesome-free/scss/brands.scss'; diff --git a/package.json b/package.json index f108311..500a667 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "devDependencies": { "@babel/core": "^7.9.0", "@babel/preset-env": "^7.9.5", + "@fortawesome/fontawesome-free": "^5.14.0", "@types/config": "^0.0.36", "@types/express": "^4.17.6", "@types/express-session": "^1.17.0", From c68d5819b10cb18ebd029c364857e34cd1dc8b4c Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sat, 5 Sep 2020 17:34:26 +0200 Subject: [PATCH 17/35] Allow links that are target=_blank to opt-out from the icon --- assets/js/external_links.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/js/external_links.js b/assets/js/external_links.js index 7108c4e..d72b537 100644 --- a/assets/js/external_links.js +++ b/assets/js/external_links.js @@ -2,7 +2,9 @@ import feather from "feather-icons"; document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('a[target="_blank"]').forEach(el => { - el.innerHTML += ``; + if (!el.classList.contains('no-icon')) { + el.innerHTML += ``; + } }); feather.replace(); From f70112e95654789495b5b8ee50d149c3c22661b3 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 10 Sep 2020 15:19:31 +0200 Subject: [PATCH 18/35] Add optional input field illustration feather icon --- assets/sass/_vars.scss | 1 + assets/sass/layout.scss | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/assets/sass/_vars.scss b/assets/sass/_vars.scss index 21d06e7..f0eb1fc 100644 --- a/assets/sass/_vars.scss +++ b/assets/sass/_vars.scss @@ -9,6 +9,7 @@ $defaultTextColor: #ffffff; $headerBackground: darken($primary, 7.5%); $footerBackground: lighten($headerBackground, 1%); $panelBackground: lighten($headerBackground, 1%); +$inputBackground: darken($panelBackground, 4%); $info: #4499ff; $infoText: darken($info, 42%); diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index f46c5e7..9ab7d43 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -353,6 +353,20 @@ form { display: flex; flex-direction: column; margin: 16px auto; + background: $inputBackground; + border-radius: 5px; + overflow: hidden; + + > .feather.icon { + position: absolute; + top: 50%; + right: 8px; + transform: translateY(-50%); + z-index: 0; + + --icon-size: 24px; + opacity: 0.75; + } label { position: absolute; @@ -379,10 +393,10 @@ form { } input, select, textarea, .input-group { + z-index: 1; border: 0; color: $defaultTextColor; - background: lighten($panelBackground, 4%); - border-radius: 5px; + background: transparent; font-size: 16px; &:focus, &:not([value=""]), &[type="file"] { @@ -435,17 +449,22 @@ form { &.inline { flex-direction: row; + align-items: center; input[type=checkbox] { - text-align: left; width: min-content; height: min-content; + margin: 8px; + text-align: left; & ~ label { position: static; + flex-grow: 1; display: inline; - padding-left: 8px; + padding: 8px; + font-size: 16px; + text-align: left; } } } From 16895e52e8c3b6489303ea71d44eb5ac71c84a4b Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 10 Sep 2020 16:17:06 +0200 Subject: [PATCH 19/35] Fix input hint and errors display --- assets/sass/layout.scss | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 9ab7d43..808aafb 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -349,15 +349,17 @@ form { text-align: center; .form-field { - position: relative; display: flex; flex-direction: column; margin: 16px auto; - background: $inputBackground; - border-radius: 5px; - overflow: hidden; - > .feather.icon { + .control { + position: relative; + background: $inputBackground; + border-radius: 5px; + } + + .feather.icon { position: absolute; top: 50%; right: 8px; @@ -447,7 +449,8 @@ form { height: calc(32px + 8px + 32px); } - &.inline { + &.inline .control { + display: flex; flex-direction: row; align-items: center; From ab8e756034e072e0090c54535aa62d45cf497753 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 10 Sep 2020 21:02:17 +0200 Subject: [PATCH 20/35] Make sure textareas have the right font --- assets/sass/layout.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 808aafb..6168bf5 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -443,6 +443,7 @@ form { textarea { resize: vertical; min-height: 100px; + font-family: inherit; } input[type=color] { From a92b657e4aeba63c50190a5554934e62c2c1d120 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 15 Sep 2020 16:30:25 +0200 Subject: [PATCH 21/35] forms: make value property update a function and update textareas too --- assets/js/forms.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/assets/js/forms.js b/assets/js/forms.js index dd3f184..a65d2f9 100644 --- a/assets/js/forms.js +++ b/assets/js/forms.js @@ -1,13 +1,20 @@ // For labels to update their state (css selectors based on the value attribute) document.addEventListener('DOMContentLoaded', () => { - document.querySelectorAll('input').forEach(el => { - if (el.type !== 'checkbox') { - el.setAttribute('value', el.value); - el.addEventListener('change', () => { - el.setAttribute('value', el.value); - }); - } - }); + window.updateInputs = () => { + document.querySelectorAll('input, textarea').forEach(el => { + if (!el.inputSetup) { + el.inputSetup = true; + if (el.type !== 'checkbox') { + el.setAttribute('value', el.value); + el.addEventListener('change', () => { + el.setAttribute('value', el.value); + }); + } + } + }); + }; + + updateInputs(); }); window.applyFormMessages = function (formElement, messages) { From 7664d368486ec0df356a9b6ce2c40e7aca87d70e Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 15 Sep 2020 16:34:21 +0200 Subject: [PATCH 22/35] Force .feather icon vertical alignment --- assets/sass/layout.scss | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index 6168bf5..fbe8b5b 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -687,10 +687,16 @@ button, .button { // --- Feather // --- .feather { + display: inline-flex; + justify-content: center; + align-items: center; + flex-shrink: 0; - --icon-size: 16px; width: var(--icon-size); height: var(--icon-size); + + --icon-size: 16px; + font-size: var(--icon-size); stroke: currentColor; stroke-width: 2; stroke-linecap: square; From a3a19da038d6a91f2ad44b85f5cfbd3e054c945c Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 15 Sep 2020 16:32:02 +0200 Subject: [PATCH 23/35] Fix inline controls styling --- assets/sass/layout.scss | 46 +++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index fbe8b5b..dd587d4 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -450,32 +450,38 @@ form { height: calc(32px + 8px + 32px); } - &.inline .control { + &.inline { display: flex; flex-direction: row; - align-items: center; - input[type=checkbox] { - width: min-content; - height: min-content; - margin: 8px; - text-align: left; + .control { + display: flex; + flex-direction: row; + align-items: center; + flex-grow: 1; - & ~ label { - position: static; - flex-grow: 1; - display: inline; - padding: 8px; - - font-size: 16px; + input[type=checkbox] { + width: min-content; + height: min-content; + margin: 8px; text-align: left; + + & ~ label { + position: static; + flex-grow: 1; + display: inline; + padding: 8px; + + font-size: 16px; + text-align: left; + } } } } .input-group { display: flex; - flex-shrink: 1; + flex-grow: 1; flex-direction: row; div { @@ -484,19 +490,9 @@ form { input { width: 100%; - margin-top: 24px; - padding-top: 8px; border: 0; background: transparent; } - - > input + * { - position: absolute; - top: 32px; - right: 28px; - user-select: none; - text-align: right; - } } } } From 5f2215f4919ae52a858e7aff260c5780c76e3223 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 16 Sep 2020 12:38:33 +0200 Subject: [PATCH 24/35] Prevent tooltips and dropdowns from overflowing on y axis --- assets/js/app.js | 1 + assets/js/tooltips-and-dropdowns.js | 30 +++++++++++++++++++++++++++++ assets/sass/layout.scss | 6 +++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 assets/js/tooltips-and-dropdowns.js diff --git a/assets/js/app.js b/assets/js/app.js index 039ac2e..48eba69 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -2,6 +2,7 @@ import './external_links'; import './message_icons'; import './forms'; import './copyable_text'; +import './tooltips-and-dropdowns'; import './main_menu'; import './font-awesome'; diff --git a/assets/js/tooltips-and-dropdowns.js b/assets/js/tooltips-and-dropdowns.js new file mode 100644 index 0000000..3fa6e5b --- /dev/null +++ b/assets/js/tooltips-and-dropdowns.js @@ -0,0 +1,30 @@ +document.addEventListener('DOMContentLoaded', () => { + window.updateTooltips = () => { + console.debug('Update tooltips'); + const elements = document.querySelectorAll('.tip, .dropdown'); + + // Calculate max potential displacement + let max = 0; + elements.forEach(el => { + const box = el.getBoundingClientRect(); + if (max < box.height) max = box.height; + }); + + // Prevent displacement + elements.forEach(el => { + if (!el.tooltipSetup) { + el.tooltipSetup = true; + const box = el.getBoundingClientRect(); + if (box.bottom >= document.body.clientHeight - (max + 32)) { + el.classList.add('top'); + } + } + }); + }; + window.addEventListener('popstate', () => { + updateTooltips(); + }); + window.requestAnimationFrame(() => { + updateTooltips(); + }); +}); \ No newline at end of file diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index dd587d4..e05bbcb 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -49,6 +49,11 @@ body { text-transform: initial; font-weight: initial; + + &.top { + top: auto; + bottom: calc(100% + 8px); + } } &:hover, &:active { @@ -157,7 +162,6 @@ body > header { z-index: -1; top: 100%; right: 0; - display: none; white-space: nowrap; background: $headerBackground; From dcdc8dd7040641e62844b12955aa32224ed1c4ce Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 23 Sep 2020 13:30:48 +0200 Subject: [PATCH 25/35] Convert frontend js to typescript --- assets/config.json | 2 +- assets/js/copyable_text.js | 12 ------ assets/js/main_menu.js | 17 -------- assets/js/tooltips-and-dropdowns.js | 30 -------------- assets/ts/PersistentWebSocket.ts | 39 +++++++++++++++++++ assets/{js/app.js => ts/app.ts} | 3 +- assets/ts/copyable_text.ts | 15 +++++++ .../external_links.ts} | 2 +- .../font-awesome.js => ts/font-awesome.ts} | 0 assets/{js/forms.js => ts/forms.ts} | 36 +++++++++-------- assets/ts/main_menu.ts | 21 ++++++++++ .../message_icons.js => ts/message_icons.ts} | 15 ++++--- assets/ts/tooltips-and-dropdowns.ts | 31 +++++++++++++++ package.json | 2 + tsconfig.frontend.json | 18 +++++++++ tsconfig.json | 3 +- webpack.config.js | 13 +++++++ 17 files changed, 174 insertions(+), 85 deletions(-) delete mode 100644 assets/js/copyable_text.js delete mode 100644 assets/js/main_menu.js delete mode 100644 assets/js/tooltips-and-dropdowns.js create mode 100644 assets/ts/PersistentWebSocket.ts rename assets/{js/app.js => ts/app.ts} (87%) create mode 100644 assets/ts/copyable_text.ts rename assets/{js/external_links.js => ts/external_links.ts} (98%) rename assets/{js/font-awesome.js => ts/font-awesome.ts} (100%) rename assets/{js/forms.js => ts/forms.ts} (52%) create mode 100644 assets/ts/main_menu.ts rename assets/{js/message_icons.js => ts/message_icons.ts} (60%) create mode 100644 assets/ts/tooltips-and-dropdowns.ts create mode 100644 tsconfig.frontend.json diff --git a/assets/config.json b/assets/config.json index 5c18915..6bcf54c 100644 --- a/assets/config.json +++ b/assets/config.json @@ -1,6 +1,6 @@ { "bundles": { - "app": "js/app.js", + "app": "ts/app.ts", "layout": "sass/layout.scss", "error": "sass/error.scss", "logo": "img/logo.svg", diff --git a/assets/js/copyable_text.js b/assets/js/copyable_text.js deleted file mode 100644 index 6501a1a..0000000 --- a/assets/js/copyable_text.js +++ /dev/null @@ -1,12 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - document.querySelectorAll('.copyable-text').forEach(el => { - const contentEl = el.querySelector('.content'); - contentEl.addEventListener('click', () => { - window.getSelection().selectAllChildren(contentEl); - }); - el.querySelector('.copy-button').addEventListener('click', () => { - window.getSelection().selectAllChildren(contentEl); - document.execCommand('copy'); - }); - }); -}); \ No newline at end of file diff --git a/assets/js/main_menu.js b/assets/js/main_menu.js deleted file mode 100644 index f2a89da..0000000 --- a/assets/js/main_menu.js +++ /dev/null @@ -1,17 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - const menuButton = document.getElementById('menu-button'); - const mainMenu = document.getElementById('main-menu'); - - menuButton.addEventListener('click', (e) => { - e.stopPropagation(); - mainMenu.classList.toggle('open'); - }); - - mainMenu.addEventListener('click', (e) => { - e.stopPropagation(); - }); - - document.addEventListener('click', () => { - mainMenu.classList.remove('open'); - }); -}); \ No newline at end of file diff --git a/assets/js/tooltips-and-dropdowns.js b/assets/js/tooltips-and-dropdowns.js deleted file mode 100644 index 3fa6e5b..0000000 --- a/assets/js/tooltips-and-dropdowns.js +++ /dev/null @@ -1,30 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - window.updateTooltips = () => { - console.debug('Update tooltips'); - const elements = document.querySelectorAll('.tip, .dropdown'); - - // Calculate max potential displacement - let max = 0; - elements.forEach(el => { - const box = el.getBoundingClientRect(); - if (max < box.height) max = box.height; - }); - - // Prevent displacement - elements.forEach(el => { - if (!el.tooltipSetup) { - el.tooltipSetup = true; - const box = el.getBoundingClientRect(); - if (box.bottom >= document.body.clientHeight - (max + 32)) { - el.classList.add('top'); - } - } - }); - }; - window.addEventListener('popstate', () => { - updateTooltips(); - }); - window.requestAnimationFrame(() => { - updateTooltips(); - }); -}); \ No newline at end of file diff --git a/assets/ts/PersistentWebSocket.ts b/assets/ts/PersistentWebSocket.ts new file mode 100644 index 0000000..b53f948 --- /dev/null +++ b/assets/ts/PersistentWebSocket.ts @@ -0,0 +1,39 @@ +export default class PersistentWebsocket { + private webSocket?: WebSocket; + + public constructor( + protected readonly url: string, + private readonly handler: MessageHandler, + protected readonly reconnectOnClose: boolean = true, + ) { + } + + public run() { + this.webSocket = new WebSocket(this.url); + this.webSocket.addEventListener('open', (e) => { + console.debug('Websocket connected'); + }); + this.webSocket.addEventListener('message', (e) => { + this.handler(this.webSocket!, e); + }); + this.webSocket.addEventListener('error', (e) => { + console.error('Websocket error', e); + }); + this.webSocket.addEventListener('close', (e) => { + this.webSocket = undefined; + console.debug('Websocket closed', e.code, e.reason); + + if (this.reconnectOnClose) { + setTimeout(() => this.run(), 1000); + } + }); + } + + public send(data: string) { + if (!this.webSocket) throw new Error('WebSocket not connected'); + + this.webSocket.send(data); + } +} + +export type MessageHandler = (webSocket: WebSocket, e: MessageEvent) => void; diff --git a/assets/js/app.js b/assets/ts/app.ts similarity index 87% rename from assets/js/app.js rename to assets/ts/app.ts index 48eba69..8594552 100644 --- a/assets/js/app.js +++ b/assets/ts/app.ts @@ -6,6 +6,5 @@ import './tooltips-and-dropdowns'; import './main_menu'; import './font-awesome'; +// css import '../sass/app.scss'; - -console.log('Hello world!'); \ No newline at end of file diff --git a/assets/ts/copyable_text.ts b/assets/ts/copyable_text.ts new file mode 100644 index 0000000..60d58ab --- /dev/null +++ b/assets/ts/copyable_text.ts @@ -0,0 +1,15 @@ +document.addEventListener('DOMContentLoaded', () => { + document.querySelectorAll('.copyable-text').forEach(el => { + const contentEl = el.querySelector('.content'); + const selection = window.getSelection(); + if (contentEl && selection) { + contentEl.addEventListener('click', () => { + selection.selectAllChildren(contentEl); + }); + el.querySelector('.copy-button')?.addEventListener('click', () => { + selection.selectAllChildren(contentEl); + document.execCommand('copy'); + }); + } + }); +}); diff --git a/assets/js/external_links.js b/assets/ts/external_links.ts similarity index 98% rename from assets/js/external_links.js rename to assets/ts/external_links.ts index d72b537..bc9ba26 100644 --- a/assets/js/external_links.js +++ b/assets/ts/external_links.ts @@ -8,4 +8,4 @@ document.addEventListener('DOMContentLoaded', () => { }); feather.replace(); -}); \ No newline at end of file +}); diff --git a/assets/js/font-awesome.js b/assets/ts/font-awesome.ts similarity index 100% rename from assets/js/font-awesome.js rename to assets/ts/font-awesome.ts diff --git a/assets/js/forms.js b/assets/ts/forms.ts similarity index 52% rename from assets/js/forms.js rename to assets/ts/forms.ts index a65d2f9..46cd108 100644 --- a/assets/js/forms.js +++ b/assets/ts/forms.ts @@ -1,25 +1,29 @@ -// For labels to update their state (css selectors based on the value attribute) -document.addEventListener('DOMContentLoaded', () => { - window.updateInputs = () => { - document.querySelectorAll('input, textarea').forEach(el => { - if (!el.inputSetup) { - el.inputSetup = true; - if (el.type !== 'checkbox') { +/* + * For labels to update their state (css selectors based on the value attribute) + */ +export function updateInputs() { + document.querySelectorAll('input, textarea').forEach(el => { + if (!el.dataset.inputSetup) { + el.dataset.inputSetup = 'true'; + if (el.type !== 'checkbox') { + el.setAttribute('value', el.value); + el.addEventListener('change', () => { el.setAttribute('value', el.value); - el.addEventListener('change', () => { - el.setAttribute('value', el.value); - }); - } + }); } - }); - }; + } + }); +} +document.addEventListener('DOMContentLoaded', () => { updateInputs(); }); -window.applyFormMessages = function (formElement, messages) { +export function applyFormMessages(formElement: HTMLFormElement, messages: { [p: string]: any }) { for (const fieldName of Object.keys(messages)) { const field = formElement.querySelector('#field-' + fieldName); + if (!field) continue; + let parent = field.parentElement; while (parent && !parent.classList.contains('form-field')) parent = parent.parentElement; @@ -28,9 +32,9 @@ window.applyFormMessages = function (formElement, messages) { if (!err) { err = document.createElement('div'); err.classList.add('error'); - parent.insertBefore(err, parent.querySelector('.hint') || parent); + parent?.insertBefore(err, parent.querySelector('.hint') || parent); } err.innerHTML = ` ${messages[fieldName].message}`; } } -} \ No newline at end of file +} diff --git a/assets/ts/main_menu.ts b/assets/ts/main_menu.ts new file mode 100644 index 0000000..4e19e40 --- /dev/null +++ b/assets/ts/main_menu.ts @@ -0,0 +1,21 @@ +document.addEventListener('DOMContentLoaded', () => { + const menuButton = document.getElementById('menu-button'); + const mainMenu = document.getElementById('main-menu'); + + if (menuButton) { + menuButton.addEventListener('click', (e) => { + e.stopPropagation(); + mainMenu?.classList.toggle('open'); + }); + } + + if (mainMenu) { + mainMenu.addEventListener('click', (e) => { + e.stopPropagation(); + }); + + document.addEventListener('click', () => { + mainMenu.classList.remove('open'); + }); + } +}); diff --git a/assets/js/message_icons.js b/assets/ts/message_icons.ts similarity index 60% rename from assets/js/message_icons.js rename to assets/ts/message_icons.ts index b625f55..b737de3 100644 --- a/assets/js/message_icons.js +++ b/assets/ts/message_icons.ts @@ -1,21 +1,26 @@ import feather from "feather-icons"; document.addEventListener('DOMContentLoaded', () => { - const messageTypeToIcon = { + const messageTypeToIcon: { [p: string]: string } = { info: 'info', success: 'check', warning: 'alert-triangle', error: 'x-circle', question: 'help-circle', }; - document.querySelectorAll('.message').forEach(el => { - const type = el.dataset['type']; + + document.querySelectorAll('.message').forEach(el => { const icon = el.querySelector('.icon'); + const type = el.dataset['type']; + if (!icon || !type) return; + if (!messageTypeToIcon[type]) throw new Error(`No icon for type ${type}`); + const svgContainer = document.createElement('div'); svgContainer.innerHTML = feather.icons[messageTypeToIcon[type]].toSvg(); - el.insertBefore(svgContainer.firstChild, icon); + + if (svgContainer.firstChild) el.insertBefore(svgContainer.firstChild, icon); icon.remove(); }); feather.replace(); -}); \ No newline at end of file +}); diff --git a/assets/ts/tooltips-and-dropdowns.ts b/assets/ts/tooltips-and-dropdowns.ts new file mode 100644 index 0000000..e66c732 --- /dev/null +++ b/assets/ts/tooltips-and-dropdowns.ts @@ -0,0 +1,31 @@ +export function updateTooltips() { + console.debug('Updating tooltips'); + const elements = document.querySelectorAll('.tip, .dropdown'); + + // Calculate max potential displacement + let max = 0; + elements.forEach(el => { + const box = el.getBoundingClientRect(); + if (max < box.height) max = box.height; + }); + + // Prevent displacement + elements.forEach(el => { + if (!el.dataset.tooltipSetup) { + el.dataset.tooltipSetup = 'true'; + const box = el.getBoundingClientRect(); + if (box.bottom >= document.body.clientHeight - (max + 32)) { + el.classList.add('top'); + } + } + }); +} + +document.addEventListener('DOMContentLoaded', () => { + window.addEventListener('popstate', () => { + updateTooltips(); + }); + window.requestAnimationFrame(() => { + updateTooltips(); + }); +}); diff --git a/package.json b/package.json index 500a667..3ffa1b4 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@types/config": "^0.0.36", "@types/express": "^4.17.6", "@types/express-session": "^1.17.0", + "@types/feather-icons": "^4.7.0", "@types/formidable": "^1.0.31", "@types/jest": "^26.0.4", "@types/mysql": "^2.15.15", @@ -44,6 +45,7 @@ "nodemon": "^2.0.3", "sass-loader": "^10.0.1", "ts-jest": "^26.1.1", + "ts-loader": "^8.0.4", "typescript": "^4.0.2", "uglifyjs-webpack-plugin": "^2.2.0", "webpack": "^4.43.0", diff --git a/tsconfig.frontend.json b/tsconfig.frontend.json new file mode 100644 index 0000000..4b5a3f5 --- /dev/null +++ b/tsconfig.frontend.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "public/js", + "target": "ES6", + "strict": true, + "lib": [ + "es2020", + "DOM" + ], + "typeRoots": [ + "./node_modules/@types" + ] + }, + "include": [ + "assets/ts/**/*" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 18f1bee..19d375e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "target": "ES6", "strict": true, "lib": [ - "es2020" + "es2020", + "DOM" ], "typeRoots": [ "./node_modules/@types" diff --git a/webpack.config.js b/webpack.config.js index 478541b..655dee0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -48,6 +48,16 @@ const config = { test: /\.(woff2?|eot|ttf|otf)$/i, use: 'file-loader?name=../fonts/[name].[ext]', }, + { + test: /\.tsx?$/i, + use: { + loader: 'ts-loader', + options: { + configFile: 'tsconfig.frontend.json', + } + }, + exclude: '/node_modules/' + }, { test: /\.(png|jpe?g|gif|svg)$/i, use: [ @@ -68,6 +78,9 @@ const config = { } ], }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, plugins: [ new MiniCssExtractPlugin({ filename: '../css/[name].css', From d91110fb67b18416765229ee6109bafb98df8b85 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Thu, 1 Oct 2020 14:38:16 +0200 Subject: [PATCH 26/35] Drop UglifyJS webpack plugin in favor of Terser --- package.json | 1 + webpack.config.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3ffa1b4..4c917b4 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "node-sass": "^4.14.0", "nodemon": "^2.0.3", "sass-loader": "^10.0.1", + "terser-webpack-plugin": "^4.2.2", "ts-jest": "^26.1.1", "ts-loader": "^8.0.4", "typescript": "^4.0.2", diff --git a/webpack.config.js b/webpack.config.js index 655dee0..598acfd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,6 +1,6 @@ const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); +const TerserPlugin = require('terser-webpack-plugin'); const dev = process.env.NODE_ENV === 'development'; @@ -90,8 +90,9 @@ const config = { if (!dev) { config.optimization = { + minimize: true, minimizer: [ - new UglifyJSPlugin(), + new TerserPlugin(), ] }; } From a8bad1c8c1448ca255bb11adc17fe08a31846ba1 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 5 Oct 2020 13:38:03 +0200 Subject: [PATCH 27/35] Upgrade wms-core and add eslint --- .eslintrc.json | 114 ++++++++++++++++++++++++++++ assets/ts/PersistentWebSocket.ts | 10 +-- assets/ts/forms.ts | 23 +++--- assets/ts/tooltips-and-dropdowns.ts | 2 +- package.json | 12 ++- src/App.ts | 21 ++--- src/controllers/HomeController.ts | 10 +-- src/main.ts | 8 +- test/App.test.ts | 2 +- tsconfig.json | 5 +- tsconfig.test.json | 14 ++++ 11 files changed, 181 insertions(+), 40 deletions(-) create mode 100644 .eslintrc.json create mode 100644 tsconfig.test.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..cdf57c6 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,114 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "parserOptions": { + "project": [ + "./tsconfig.json", + "./tsconfig.test.json", + "./tsconfig.frontend.json" + ] + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "indent": [ + "error", + 4, + { + "SwitchCase": 1 + } + ], + "no-trailing-spaces": "error", + "max-len": [ + "error", + { + "code": 120, + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true + } + ], + "semi": "off", + "@typescript-eslint/semi": [ + "error" + ], + "no-extra-semi": "error", + "eol-last": "error", + "comma-dangle": "off", + "@typescript-eslint/comma-dangle": [ + "error", + { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "always-multiline", + "enums": "always-multiline", + "generics": "always-multiline", + "tuples": "always-multiline" + } + ], + "no-extra-parens": "off", + "@typescript-eslint/no-extra-parens": [ + "error" + ], + "no-nested-ternary": "error", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/explicit-module-boundary-types": "error", + "@typescript-eslint/no-unnecessary-condition": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_" + } + ], + "@typescript-eslint/no-non-null-assertion": "error", + "no-useless-return": "error", + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": [ + "error" + ], + "no-return-await": "off", + "@typescript-eslint/return-await": [ + "error", + "always" + ], + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { + "accessibility": "explicit" + } + ], + "@typescript-eslint/no-floating-promises": "error" + }, + "ignorePatterns": [ + "jest.config.js", + "webpack.config.js", + "dist/**/*", + "public/**/*", + "config/**/*" + ], + "overrides": [ + { + "files": [ + "test/**/*" + ], + "rules": { + "max-len": [ + "error", + { + "code": 120, + "ignoreTemplateLiterals": true, + "ignoreRegExpLiterals": true, + "ignoreStrings": true + } + ] + } + } + ] +} diff --git a/assets/ts/PersistentWebSocket.ts b/assets/ts/PersistentWebSocket.ts index b53f948..98e474f 100644 --- a/assets/ts/PersistentWebSocket.ts +++ b/assets/ts/PersistentWebSocket.ts @@ -8,13 +8,13 @@ export default class PersistentWebsocket { ) { } - public run() { - this.webSocket = new WebSocket(this.url); - this.webSocket.addEventListener('open', (e) => { + public run(): void { + const _webSocket = this.webSocket = new WebSocket(this.url); + this.webSocket.addEventListener('open', () => { console.debug('Websocket connected'); }); this.webSocket.addEventListener('message', (e) => { - this.handler(this.webSocket!, e); + this.handler(_webSocket, e); }); this.webSocket.addEventListener('error', (e) => { console.error('Websocket error', e); @@ -29,7 +29,7 @@ export default class PersistentWebsocket { }); } - public send(data: string) { + public send(data: string): void { if (!this.webSocket) throw new Error('WebSocket not connected'); this.webSocket.send(data); diff --git a/assets/ts/forms.ts b/assets/ts/forms.ts index 46cd108..d08a65e 100644 --- a/assets/ts/forms.ts +++ b/assets/ts/forms.ts @@ -1,7 +1,9 @@ /* * For labels to update their state (css selectors based on the value attribute) */ -export function updateInputs() { +import {ValidationError} from "wms-core/db/Validator"; + +export function updateInputs(): void { document.querySelectorAll('input, textarea').forEach(el => { if (!el.dataset.inputSetup) { el.dataset.inputSetup = 'true'; @@ -19,7 +21,10 @@ document.addEventListener('DOMContentLoaded', () => { updateInputs(); }); -export function applyFormMessages(formElement: HTMLFormElement, messages: { [p: string]: any }) { +export function applyFormMessages( + formElement: HTMLFormElement, + messages: { [p: string]: ValidationError }, +): void { for (const fieldName of Object.keys(messages)) { const field = formElement.querySelector('#field-' + fieldName); if (!field) continue; @@ -27,14 +32,12 @@ export function applyFormMessages(formElement: HTMLFormElement, messages: { [p: let parent = field.parentElement; while (parent && !parent.classList.contains('form-field')) parent = parent.parentElement; - if (field) { - let err = field.querySelector('.error'); - if (!err) { - err = document.createElement('div'); - err.classList.add('error'); - parent?.insertBefore(err, parent.querySelector('.hint') || parent); - } - err.innerHTML = ` ${messages[fieldName].message}`; + let err = field.querySelector('.error'); + if (!err) { + err = document.createElement('div'); + err.classList.add('error'); + parent?.insertBefore(err, parent.querySelector('.hint') || parent); } + err.innerHTML = ` ${messages[fieldName].message}`; } } diff --git a/assets/ts/tooltips-and-dropdowns.ts b/assets/ts/tooltips-and-dropdowns.ts index e66c732..069b405 100644 --- a/assets/ts/tooltips-and-dropdowns.ts +++ b/assets/ts/tooltips-and-dropdowns.ts @@ -1,4 +1,4 @@ -export function updateTooltips() { +export function updateTooltips(): void { console.debug('Updating tooltips'); const elements = document.querySelectorAll('.tip, .dropdown'); diff --git a/package.json b/package.json index 4c917b4..04056cc 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,12 @@ "scripts": { "test": "jest --verbose --runInBand", "dist-webpack": "webpack --mode production", - "dist": "(test ! -d dist || rm -r dist) && tsc && npm run dist-webpack", + "dist": "yarn compile && yarn dist-webpack", + "clean": "(test ! -d dist || rm -r dist)", + "compile": "yarn clean && tsc && mv dist/src/* dist/", "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", - "start": "yarn dist && node dist/main.js" + "start": "yarn dist && node dist/main.js", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "devDependencies": { "@babel/core": "^7.9.0", @@ -28,9 +31,12 @@ "@types/nodemailer": "^6.4.0", "@types/nunjucks": "^3.1.3", "@types/ws": "^7.2.6", + "@typescript-eslint/eslint-plugin": "^4.3.0", + "@typescript-eslint/parser": "^4.3.0", "babel-loader": "^8.1.0", "concurrently": "^5.1.0", "css-loader": "^4.2.2", + "eslint": "^7.10.0", "feather-icons": "^4.28.0", "file-loader": "^6.0.0", "imagemin": "^7.0.1", @@ -55,6 +61,6 @@ "dependencies": { "config": "^3.3.1", "express": "^4.17.1", - "wms-core": "^0.21.7" + "wms-core": "^0.22.0-rc.24" } } diff --git a/src/App.ts b/src/App.ts index 713f51b..c8dac6b 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,6 +1,5 @@ import Application from "wms-core/Application"; -import {Type} from "wms-core/Utils"; -import Migration from "wms-core/db/Migration"; +import Migration, {MigrationType} from "wms-core/db/Migration"; import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; import CreateLogsTable from "wms-core/migrations/CreateLogsTable"; import ExpressAppComponent from "wms-core/components/ExpressAppComponent"; @@ -18,16 +17,17 @@ import CsrfProtectionComponent from "wms-core/components/CsrfProtectionComponent import WebSocketServerComponent from "wms-core/components/WebSocketServerComponent"; import HomeController from "./controllers/HomeController"; import AutoUpdateComponent from "wms-core/components/AutoUpdateComponent"; +import packageJson = require('../package.json'); export default class App extends Application { - private readonly port: number; - - constructor(port: number) { - super(require('../package.json').version); - this.port = port; + public constructor( + private readonly addr: string, + private readonly port: number, + ) { + super(packageJson.version); } - protected getMigrations(): Type[] { + protected getMigrations(): MigrationType[] { return [ CreateMigrationsTable, CreateLogsTable, @@ -44,7 +44,7 @@ export default class App extends Application { const redisComponent = new RedisComponent(); const mysqlComponent = new MysqlComponent(); - const expressAppComponent = new ExpressAppComponent(this.port); + const expressAppComponent = new ExpressAppComponent(this.addr, this.port); this.use(expressAppComponent); this.use(new NunjucksComponent()); this.use(new LogRequestsComponent()); @@ -79,9 +79,10 @@ export default class App extends Application { } private registerWebSocketListeners() { + // WebSocket listeners } private registerControllers() { this.use(new HomeController()); } -} \ No newline at end of file +} diff --git a/src/controllers/HomeController.ts b/src/controllers/HomeController.ts index ad035b4..e6e3fb3 100644 --- a/src/controllers/HomeController.ts +++ b/src/controllers/HomeController.ts @@ -2,24 +2,24 @@ import Controller from "wms-core/Controller"; import {Request, Response} from "express"; export default class HomeController extends Controller { - routes(): void { + public routes(): void { this.get('/', this.getHome, 'home'); this.get('/about', this.getAbout, 'about'); this.get('/back', this.goBack, 'about'); } - private async getHome(req: Request, res: Response) { + protected async getHome(req: Request, res: Response): Promise { res.render('home'); } - private async getAbout(req: Request, res: Response) { + protected async getAbout(req: Request, res: Response): Promise { res.render('about'); } /** * This is to test and assert that wms-core extended types are available */ - private async goBack(req: Request, res: Response) { + protected async goBack(req: Request, res: Response): Promise { res.redirectBack(); } -} \ No newline at end of file +} diff --git a/src/main.ts b/src/main.ts index b3f32d5..76e7561 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,9 @@ +import {delimiter} from "path"; + // Load config from specified path or default + wms-core/config (default defaults) process.env['NODE_CONFIG_DIR'] = __dirname + '/../node_modules/wms-core/config/' - + require('path').delimiter + + delimiter + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/'); import Logger from "wms-core/Logger"; @@ -11,8 +13,8 @@ import config from "config"; (async () => { Logger.debug('Config path:', process.env['NODE_CONFIG_DIR']); - const app = new App(config.get('port')); + const app = new App(config.get('listen_addr'), config.get('port')); await app.start(); })().catch(err => { Logger.error(err); -}); \ No newline at end of file +}); diff --git a/test/App.test.ts b/test/App.test.ts index 451f0fb..d116a22 100644 --- a/test/App.test.ts +++ b/test/App.test.ts @@ -2,4 +2,4 @@ describe('Write your tests', () => { test('Remove this when you have some tests', () => { expect(false).toBe(true); }); -}); \ No newline at end of file +}); diff --git a/tsconfig.json b/tsconfig.json index 19d375e..5ea85f7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,10 +11,11 @@ ], "typeRoots": [ "./node_modules/@types" - ] + ], + "resolveJsonModule": true }, "include": [ "src/**/*", "node_modules/wms-core/types" ] -} \ No newline at end of file +} diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..897a6b4 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "typeRoots": [ + "node_modules/@types", + "src/types", + "test/types" + ] + }, + "include": [ + "src/types/**/*", + "test/**/*" + ] +} \ No newline at end of file From 292e843cd16591aa345d372016140c64bc549620 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 2 Nov 2020 18:17:55 +0100 Subject: [PATCH 28/35] Upgrade packages i.e. wms-core --- config/{default.ts => default.json5} | 2 +- config/{production.ts => production.json5} | 2 +- config/{test.ts => test.json5} | 2 +- package.json | 15 +++++++-------- src/App.ts | 6 ++++-- src/main.ts | 6 +++--- 6 files changed, 17 insertions(+), 16 deletions(-) rename config/{default.ts => default.json5} (98%) rename config/{production.ts => production.json5} (94%) rename config/{test.ts => test.json5} (90%) diff --git a/config/default.ts b/config/default.json5 similarity index 98% rename from config/default.ts rename to config/default.json5 index aed8601..1831e21 100644 --- a/config/default.ts +++ b/config/default.json5 @@ -1,4 +1,4 @@ -export default { +{ app: { name: 'Example App', contact_email: 'contact@example.net' diff --git a/config/production.ts b/config/production.json5 similarity index 94% rename from config/production.ts rename to config/production.json5 index 4143867..6993e0e 100644 --- a/config/production.ts +++ b/config/production.json5 @@ -1,4 +1,4 @@ -export default { +{ log_level: "DEBUG", db_log_level: "ERROR", public_url: "https://watch-my.stream", diff --git a/config/test.ts b/config/test.json5 similarity index 90% rename from config/test.ts rename to config/test.json5 index 61f7c4a..c184427 100644 --- a/config/test.ts +++ b/config/test.json5 @@ -1,4 +1,4 @@ -export default { +{ mysql: { host: "localhost", user: "root", diff --git a/package.json b/package.json index 04056cc..3ad46b8 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@typescript-eslint/parser": "^4.3.0", "babel-loader": "^8.1.0", "concurrently": "^5.1.0", - "css-loader": "^4.2.2", + "css-loader": "^5.0.0", "eslint": "^7.10.0", "feather-icons": "^4.28.0", "file-loader": "^6.0.0", @@ -46,21 +46,20 @@ "imagemin-svgo": "^8.0.0", "img-loader": "^3.0.1", "jest": "^26.1.0", - "mini-css-extract-plugin": "^0.11.0", - "node-sass": "^4.14.0", + "mini-css-extract-plugin": "^1.2.1", + "node-sass": "^5.0.0", "nodemon": "^2.0.3", "sass-loader": "^10.0.1", - "terser-webpack-plugin": "^4.2.2", + "terser-webpack-plugin": "^5.0.3", "ts-jest": "^26.1.1", "ts-loader": "^8.0.4", "typescript": "^4.0.2", - "uglifyjs-webpack-plugin": "^2.2.0", - "webpack": "^4.43.0", - "webpack-cli": "^3.3.11" + "webpack": "^5.3.2", + "webpack-cli": "^4.1.0" }, "dependencies": { "config": "^3.3.1", "express": "^4.17.1", - "wms-core": "^0.22.0-rc.24" + "wms-core": "^0.22.0" } } diff --git a/src/App.ts b/src/App.ts index c8dac6b..6633f86 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,7 +1,6 @@ import Application from "wms-core/Application"; import Migration, {MigrationType} from "wms-core/db/Migration"; import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; -import CreateLogsTable from "wms-core/migrations/CreateLogsTable"; import ExpressAppComponent from "wms-core/components/ExpressAppComponent"; import NunjucksComponent from "wms-core/components/NunjucksComponent"; import MysqlComponent from "wms-core/components/MysqlComponent"; @@ -18,6 +17,8 @@ import WebSocketServerComponent from "wms-core/components/WebSocketServerCompone import HomeController from "./controllers/HomeController"; import AutoUpdateComponent from "wms-core/components/AutoUpdateComponent"; import packageJson = require('../package.json'); +import DummyMigration from "wms-core/migrations/DummyMigration"; +import DropLegacyLogsTable from "wms-core/migrations/DropLegacyLogsTable"; export default class App extends Application { public constructor( @@ -30,7 +31,8 @@ export default class App extends Application { protected getMigrations(): MigrationType[] { return [ CreateMigrationsTable, - CreateLogsTable, + DummyMigration, + DropLegacyLogsTable, ]; } diff --git a/src/main.ts b/src/main.ts index 76e7561..e243999 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,15 +6,15 @@ process.env['NODE_CONFIG_DIR'] = + delimiter + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/'); -import Logger from "wms-core/Logger"; +import {log} from "wms-core/Logger"; import App from "./App"; import config from "config"; (async () => { - Logger.debug('Config path:', process.env['NODE_CONFIG_DIR']); + log.debug('Config path:', process.env['NODE_CONFIG_DIR']); const app = new App(config.get('listen_addr'), config.get('port')); await app.start(); })().catch(err => { - Logger.error(err); + log.error(err); }); From 9e07d4bff4eeee005c4e976ef9ab0ed66b42ce38 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 2 Nov 2020 18:37:39 +0100 Subject: [PATCH 29/35] config: remove insecure default values --- config/default.json5 | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/default.json5 b/config/default.json5 index 1831e21..e0c6b9b 100644 --- a/config/default.json5 +++ b/config/default.json5 @@ -8,7 +8,6 @@ public_url: "http://localhost:4899", public_websocket_url: "ws://localhost:4899", port: 4899, - gitlab_webhook_token: 'secret', mysql: { connectionLimit: 10, host: "localhost", @@ -23,7 +22,6 @@ prefix: 'example_app' }, session: { - secret: "very_secret_not_known", cookie: { secure: false } From a4df579937f3a0d184dadb16ba09562b8851e00a Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 3 Nov 2020 10:37:30 +0100 Subject: [PATCH 30/35] Rename "dist" task to more relevant "build" --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 3ad46b8..fd6dc27 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,14 @@ "private": true, "main": "dist/main.js", "scripts": { - "test": "jest --verbose --runInBand", "dist-webpack": "webpack --mode production", - "dist": "yarn compile && yarn dist-webpack", "clean": "(test ! -d dist || rm -r dist)", "compile": "yarn clean && tsc && mv dist/src/* dist/", + "build": "yarn compile && yarn dist-webpack", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", - "start": "yarn dist && node dist/main.js", - "lint": "eslint . --ext .js,.jsx,.ts,.tsx" + "start": "yarn build && node dist/main.js", + "test": "jest --verbose --runInBand" }, "devDependencies": { "@babel/core": "^7.9.0", From 04f19f5a28ba90cca4adeb8125ef19cb7a258c94 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 3 Nov 2020 17:26:32 +0100 Subject: [PATCH 31/35] Fix yarn dev script --- package.json | 6 +++--- src/main.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index fd6dc27..501e8f2 100644 --- a/package.json +++ b/package.json @@ -5,15 +5,15 @@ "repository": "https://gitlab.com/ArisuOngaku/wms-boilerplate", "author": "Alice Gaudon ", "private": true, - "main": "dist/main.js", + "main": "dist/src/main.js", "scripts": { "dist-webpack": "webpack --mode production", "clean": "(test ! -d dist || rm -r dist)", - "compile": "yarn clean && tsc && mv dist/src/* dist/", + "compile": "yarn clean && tsc", "build": "yarn compile && yarn dist-webpack", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", - "start": "yarn build && node dist/main.js", + "start": "yarn build && node dist/src/main.js", "test": "jest --verbose --runInBand" }, "devDependencies": { diff --git a/src/main.ts b/src/main.ts index e243999..14be157 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,9 +2,9 @@ import {delimiter} from "path"; // Load config from specified path or default + wms-core/config (default defaults) process.env['NODE_CONFIG_DIR'] = - __dirname + '/../node_modules/wms-core/config/' + __dirname + '/../../node_modules/wms-core/config/' + delimiter - + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/'); + + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../../config/'); import {log} from "wms-core/Logger"; import App from "./App"; From f16d63c74fd8e9c2437ecb2a3ac5d1ad35931eb6 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Tue, 3 Nov 2020 20:29:07 +0100 Subject: [PATCH 32/35] frontend: refactor $mobileThreshold and container media queries --- assets/sass/_vars.scss | 2 +- assets/sass/layout.scss | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/assets/sass/_vars.scss b/assets/sass/_vars.scss index f0eb1fc..31b7580 100644 --- a/assets/sass/_vars.scss +++ b/assets/sass/_vars.scss @@ -28,4 +28,4 @@ $errorText: darken($error, 30%); $errorColor: desaturate($errorText, 50%); // Responsivity -$menuLayoutSwitchTreshold: 700px; +$mobileThreshold: 632px; diff --git a/assets/sass/layout.scss b/assets/sass/layout.scss index e05bbcb..5a00bff 100644 --- a/assets/sass/layout.scss +++ b/assets/sass/layout.scss @@ -183,7 +183,7 @@ body > header { } } - @media (max-width: $menuLayoutSwitchTreshold) { + @media (max-width: $mobileThreshold) { flex-direction: row-reverse; .logo { @@ -246,7 +246,7 @@ body > header { } } - @media (min-width: $menuLayoutSwitchTreshold) { + @media (min-width: $mobileThreshold) { nav ul li { a, button, .button { @include tip; @@ -274,7 +274,7 @@ footer { main { flex: 1; - padding: 8px; + padding: 8px 0; button, .button { @include tip; @@ -649,10 +649,22 @@ button, .button { text-align: center; } -.container { +@mixin container { + width: $mobileThreshold; padding: 0 16px; - max-width: 632px; - margin: 0 auto; + + @media (min-width: $mobileThreshold) { + margin: 0 auto; + } + + @media (max-width: $mobileThreshold) { + width: 100%; + padding: 0 8px; + } +} + +.container { + @include container; } .panel { From ba5b90a4f91df5742033afdf14c21e7752176b13 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Fri, 13 Nov 2020 10:54:35 +0100 Subject: [PATCH 33/35] swaf rename: rename project to swaf-boilerplate --- README.md | 3 +++ app.service | 11 +++++---- assets/img/logo.svg | 1 - assets/ts/forms.ts | 2 +- config/test.json5 | 2 +- package.json | 6 ++--- src/App.ts | 38 +++++++++++++++---------------- src/controllers/HomeController.ts | 4 ++-- src/main.ts | 6 ++--- tsconfig.json | 2 +- views/about.njk | 4 ++-- 11 files changed, 42 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e69de29..96b65aa 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +# swaf boilerplate + +Boilerplate for a quickstart with [swaf](https://eternae.ink/arisu/swaf) diff --git a/app.service b/app.service index 32f2b99..ece8fd0 100644 --- a/app.service +++ b/app.service @@ -1,13 +1,16 @@ +# Please customize values i.e. paths, user, group, WorkingDirectory based on your environment. Do not use the same +# user and group for different applications. + [Unit] -Description=WMS website +Description=swaf based website After=network-online.target Wants=network-online.target [Service] Type=simple -User=wms -Group=wms -WorkingDirectory=/home/wms/live +User=swaf +Group=swaf +WorkingDirectory=/home/swaf/live Restart=on-success Environment=NODE_ENV=production ExecStart=/bin/node . diff --git a/assets/img/logo.svg b/assets/img/logo.svg index c33da66..4f8fb6e 100644 --- a/assets/img/logo.svg +++ b/assets/img/logo.svg @@ -19,7 +19,6 @@ id="svg6" sodipodi:docname="logo.svg" inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" - inkscape:export-filename="/r/arisu/dev/streams/wms/assets/img/logox1024.png" inkscape:export-xdpi="4096" inkscape:export-ydpi="4096"> ('input, textarea').forEach(el => { diff --git a/config/test.json5 b/config/test.json5 index c184427..cda9d47 100644 --- a/config/test.json5 +++ b/config/test.json5 @@ -3,7 +3,7 @@ host: "localhost", user: "root", password: "", - database: "wms2_test", + database: "swaf_test", create_database_automatically: true } } diff --git a/package.json b/package.json index 501e8f2..1550885 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "example-app", "version": "0.1.0", - "description": "Example App based on wms-core", - "repository": "https://gitlab.com/ArisuOngaku/wms-boilerplate", + "description": "Example App based on swaf", + "repository": "https://eternae.ink/arisu/swaf-boilerplate", "author": "Alice Gaudon ", "private": true, "main": "dist/src/main.js", @@ -60,6 +60,6 @@ "dependencies": { "config": "^3.3.1", "express": "^4.17.1", - "wms-core": "^0.22.0" + "swaf": "^0.22.5" } } diff --git a/src/App.ts b/src/App.ts index 6633f86..f645a64 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1,24 +1,24 @@ -import Application from "wms-core/Application"; -import Migration, {MigrationType} from "wms-core/db/Migration"; -import CreateMigrationsTable from "wms-core/migrations/CreateMigrationsTable"; -import ExpressAppComponent from "wms-core/components/ExpressAppComponent"; -import NunjucksComponent from "wms-core/components/NunjucksComponent"; -import MysqlComponent from "wms-core/components/MysqlComponent"; -import LogRequestsComponent from "wms-core/components/LogRequestsComponent"; -import RedisComponent from "wms-core/components/RedisComponent"; -import ServeStaticDirectoryComponent from "wms-core/components/ServeStaticDirectoryComponent"; -import MaintenanceComponent from "wms-core/components/MaintenanceComponent"; -import MailComponent from "wms-core/components/MailComponent"; -import SessionComponent from "wms-core/components/SessionComponent"; -import RedirectBackComponent from "wms-core/components/RedirectBackComponent"; -import FormHelperComponent from "wms-core/components/FormHelperComponent"; -import CsrfProtectionComponent from "wms-core/components/CsrfProtectionComponent"; -import WebSocketServerComponent from "wms-core/components/WebSocketServerComponent"; +import Application from "swaf/Application"; +import Migration, {MigrationType} from "swaf/db/Migration"; +import CreateMigrationsTable from "swaf/migrations/CreateMigrationsTable"; +import ExpressAppComponent from "swaf/components/ExpressAppComponent"; +import NunjucksComponent from "swaf/components/NunjucksComponent"; +import MysqlComponent from "swaf/components/MysqlComponent"; +import LogRequestsComponent from "swaf/components/LogRequestsComponent"; +import RedisComponent from "swaf/components/RedisComponent"; +import ServeStaticDirectoryComponent from "swaf/components/ServeStaticDirectoryComponent"; +import MaintenanceComponent from "swaf/components/MaintenanceComponent"; +import MailComponent from "swaf/components/MailComponent"; +import SessionComponent from "swaf/components/SessionComponent"; +import RedirectBackComponent from "swaf/components/RedirectBackComponent"; +import FormHelperComponent from "swaf/components/FormHelperComponent"; +import CsrfProtectionComponent from "swaf/components/CsrfProtectionComponent"; +import WebSocketServerComponent from "swaf/components/WebSocketServerComponent"; import HomeController from "./controllers/HomeController"; -import AutoUpdateComponent from "wms-core/components/AutoUpdateComponent"; +import AutoUpdateComponent from "swaf/components/AutoUpdateComponent"; import packageJson = require('../package.json'); -import DummyMigration from "wms-core/migrations/DummyMigration"; -import DropLegacyLogsTable from "wms-core/migrations/DropLegacyLogsTable"; +import DummyMigration from "swaf/migrations/DummyMigration"; +import DropLegacyLogsTable from "swaf/migrations/DropLegacyLogsTable"; export default class App extends Application { public constructor( diff --git a/src/controllers/HomeController.ts b/src/controllers/HomeController.ts index e6e3fb3..d8a0379 100644 --- a/src/controllers/HomeController.ts +++ b/src/controllers/HomeController.ts @@ -1,4 +1,4 @@ -import Controller from "wms-core/Controller"; +import Controller from "swaf/Controller"; import {Request, Response} from "express"; export default class HomeController extends Controller { @@ -17,7 +17,7 @@ export default class HomeController extends Controller { } /** - * This is to test and assert that wms-core extended types are available + * This is to test and assert that swaf extended types are available */ protected async goBack(req: Request, res: Response): Promise { res.redirectBack(); diff --git a/src/main.ts b/src/main.ts index 14be157..0033726 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,12 @@ import {delimiter} from "path"; -// Load config from specified path or default + wms-core/config (default defaults) +// Load config from specified path or default + swaf/config (default defaults) process.env['NODE_CONFIG_DIR'] = - __dirname + '/../../node_modules/wms-core/config/' + __dirname + '/../../node_modules/swaf/config/' + delimiter + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../../config/'); -import {log} from "wms-core/Logger"; +import {log} from "swaf/Logger"; import App from "./App"; import config from "config"; diff --git a/tsconfig.json b/tsconfig.json index 5ea85f7..9034d02 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,6 @@ }, "include": [ "src/**/*", - "node_modules/wms-core/types" + "node_modules/swaf/types" ] } diff --git a/views/about.njk b/views/about.njk index dbb6047..f1e7a0a 100644 --- a/views/about.njk +++ b/views/about.njk @@ -8,7 +8,7 @@

This is us

-

And we like wms!

+

And we like swaf!

-{% endblock %} \ No newline at end of file +{% endblock %} From 9f3a541c3e2fac185152ca8b0c839539ecb7251c Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 25 Jan 2021 11:43:36 +0100 Subject: [PATCH 34/35] Add maildev as a dev dependency --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1550885..1681a4a 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "imagemin-svgo": "^8.0.0", "img-loader": "^3.0.1", "jest": "^26.1.0", + "maildev": "^1.1.0", "mini-css-extract-plugin": "^1.2.1", "node-sass": "^5.0.0", "nodemon": "^2.0.3", From 5eaebd5d120372a232b7338a05e9a6e17098828f Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 25 Jan 2021 13:17:41 +0100 Subject: [PATCH 35/35] Upgrade dependencies and bump swaf to ^0.23.0 --- .gitignore | 2 ++ package.json | 18 +++++++++--------- src/App.ts | 27 +++++++++++++-------------- src/controllers/HomeController.ts | 2 +- src/main.ts | 10 +++++----- tsconfig.frontend.json | 1 + tsconfig.json | 1 + 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 7626c60..570ee35 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ node_modules public dist yarn-error.log + +src/package.json diff --git a/package.json b/package.json index 1681a4a..2eefcc6 100644 --- a/package.json +++ b/package.json @@ -5,22 +5,22 @@ "repository": "https://eternae.ink/arisu/swaf-boilerplate", "author": "Alice Gaudon ", "private": true, - "main": "dist/src/main.js", + "main": "dist/main.js", "scripts": { - "dist-webpack": "webpack --mode production", + "test": "jest --verbose --runInBand", "clean": "(test ! -d dist || rm -r dist)", + "prepareSources": "cp package.json src/", "compile": "yarn clean && tsc", - "build": "yarn compile && yarn dist-webpack", - "lint": "eslint . --ext .js,.jsx,.ts,.tsx", - "dev": "concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", - "start": "yarn build && node dist/src/main.js", - "test": "jest --verbose --runInBand" + "build": "yarn prepareSources && yarn compile && webpack --mode production", + "dev": "yarn prepareSources && concurrently -k -n \"Typescript,Node,Webpack,Maildev\" -p \"[{name}]\" -c \"blue,green,red,yellow\" \"tsc --watch\" \"nodemon\" \"webpack --watch --mode development\" \"maildev\"", + "start": "yarn build && node", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "devDependencies": { "@babel/core": "^7.9.0", "@babel/preset-env": "^7.9.5", "@fortawesome/fontawesome-free": "^5.14.0", - "@types/config": "^0.0.36", + "@types/config": "^0.0.38", "@types/express": "^4.17.6", "@types/express-session": "^1.17.0", "@types/feather-icons": "^4.7.0", @@ -61,6 +61,6 @@ "dependencies": { "config": "^3.3.1", "express": "^4.17.1", - "swaf": "^0.22.5" + "swaf": "^0.23.0" } } diff --git a/src/App.ts b/src/App.ts index f645a64..6272da0 100644 --- a/src/App.ts +++ b/src/App.ts @@ -10,15 +10,15 @@ import ServeStaticDirectoryComponent from "swaf/components/ServeStaticDirectoryC import MaintenanceComponent from "swaf/components/MaintenanceComponent"; import MailComponent from "swaf/components/MailComponent"; import SessionComponent from "swaf/components/SessionComponent"; -import RedirectBackComponent from "swaf/components/RedirectBackComponent"; import FormHelperComponent from "swaf/components/FormHelperComponent"; import CsrfProtectionComponent from "swaf/components/CsrfProtectionComponent"; import WebSocketServerComponent from "swaf/components/WebSocketServerComponent"; import HomeController from "./controllers/HomeController"; import AutoUpdateComponent from "swaf/components/AutoUpdateComponent"; -import packageJson = require('../package.json'); import DummyMigration from "swaf/migrations/DummyMigration"; import DropLegacyLogsTable from "swaf/migrations/DropLegacyLogsTable"; +import PreviousUrlComponent from "swaf/components/PreviousUrlComponent"; +import packageJson = require('./package.json'); export default class App extends Application { public constructor( @@ -43,41 +43,40 @@ export default class App extends Application { } private registerComponents() { - const redisComponent = new RedisComponent(); - const mysqlComponent = new MysqlComponent(); - - const expressAppComponent = new ExpressAppComponent(this.addr, this.port); - this.use(expressAppComponent); - this.use(new NunjucksComponent()); + // Base + this.use(new ExpressAppComponent(this.addr, this.port)); this.use(new LogRequestsComponent()); // Static files this.use(new ServeStaticDirectoryComponent('public')); this.use(new ServeStaticDirectoryComponent('node_modules/feather-icons/dist', '/icons')); + // Dynamic views and routes + this.use(new NunjucksComponent()); + this.use(new PreviousUrlComponent()); + // Maintenance this.use(new MaintenanceComponent(this, () => { - return redisComponent.canServe() && mysqlComponent.canServe(); + return this.as(RedisComponent).canServe() && this.as(MysqlComponent).canServe(); })); this.use(new AutoUpdateComponent()); // Services - this.use(mysqlComponent); + this.use(new MysqlComponent()); this.use(new MailComponent()); // Session - this.use(redisComponent); - this.use(new SessionComponent(redisComponent)); + this.use(new RedisComponent()); + this.use(new SessionComponent(this.as(RedisComponent))); // Utils - this.use(new RedirectBackComponent()); this.use(new FormHelperComponent()); // Middlewares this.use(new CsrfProtectionComponent()); // WebSocket server - this.use(new WebSocketServerComponent(this, expressAppComponent, redisComponent)); + this.use(new WebSocketServerComponent(this, this.as(ExpressAppComponent), this.as(RedisComponent))); } private registerWebSocketListeners() { diff --git a/src/controllers/HomeController.ts b/src/controllers/HomeController.ts index d8a0379..b5e1740 100644 --- a/src/controllers/HomeController.ts +++ b/src/controllers/HomeController.ts @@ -20,6 +20,6 @@ export default class HomeController extends Controller { * This is to test and assert that swaf extended types are available */ protected async goBack(req: Request, res: Response): Promise { - res.redirectBack(); + res.redirect(req.getPreviousUrl() || Controller.route('home')); } } diff --git a/src/main.ts b/src/main.ts index 0033726..babc7df 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,19 +2,19 @@ import {delimiter} from "path"; // Load config from specified path or default + swaf/config (default defaults) process.env['NODE_CONFIG_DIR'] = - __dirname + '/../../node_modules/swaf/config/' + __dirname + '/../node_modules/swaf/config/' + delimiter - + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../../config/'); + + (process.env['NODE_CONFIG_DIR'] || __dirname + '/../config/'); -import {log} from "swaf/Logger"; +import {logger} from "swaf/Logger"; import App from "./App"; import config from "config"; (async () => { - log.debug('Config path:', process.env['NODE_CONFIG_DIR']); + logger.debug('Config path:', process.env['NODE_CONFIG_DIR']); const app = new App(config.get('listen_addr'), config.get('port')); await app.start(); })().catch(err => { - log.error(err); + logger.error(err); }); diff --git a/tsconfig.frontend.json b/tsconfig.frontend.json index 4b5a3f5..ad74720 100644 --- a/tsconfig.frontend.json +++ b/tsconfig.frontend.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "public/js", + "rootDir": "./assets", "target": "ES6", "strict": true, "lib": [ diff --git a/tsconfig.json b/tsconfig.json index 9034d02..12eec0a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "module": "CommonJS", "esModuleInterop": true, "outDir": "dist", + "rootDir": "./src", "target": "ES6", "strict": true, "lib": [