Compare commits

..

2 commits

Author SHA1 Message Date
S. Hattry
d2b03dca96 add eslint-stylint 2022-09-26 12:27:09 +02:00
S. Hattry
e799901d4a npm audit fix 2022-09-26 09:16:43 +02:00
21 changed files with 5746 additions and 2096 deletions

View file

@ -11,5 +11,8 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.vue]
indent_size = 2
[*.{yml,yaml}]
indent_size = 2

41
.eslintrc.json Normal file
View file

@ -0,0 +1,41 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-strongly-recommended",
"@vue/eslint-config-typescript"
],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
"no-undef": "off"
}
},
{
"files": ["*.vue"],
"rules": {
"indent": "off"
}
}
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
"vue/one-component-per-file": "off"
}
}

View file

@ -1,6 +1,7 @@
stages:
- Security
- CodingStandards
- Builds
composer-security:
stage: Security
@ -52,3 +53,66 @@ phpcs:
needs:
- composer-security
- npm-security
eslint:
stage: CodingStandards
image: node:17.9.1
before_script:
- npm install
cache:
paths:
- node_modules/
script:
- npm run eslint-ci --silent > eslint-report.xml
artifacts:
when: on_failure
expose_as: 'eslint'
paths:
- eslint-report.xml
reports:
junit: eslint-report.xml
allow_failure: false
needs:
- composer-security
- npm-security
stylelint:
stage: CodingStandards
image: node:17.9.1
before_script:
- npm install
cache:
paths:
- node_modules/
script:
- npm run stylelint-ci --silent > stylelint-report.json
artifacts:
when: on_failure
expose_as: 'stylelint'
paths:
- stylelint-report.json
allow_failure: false
needs:
- composer-security
- npm-security
front-build:
stage: Builds
image: node:17.9.1
before_script:
- npm install
cache:
paths:
- node_modules/
script:
- npm run prod > front-build-report.json
artifacts:
when: on_failure
expose_as: 'front-build-report'
paths:
- front-build-report.json
allow_failure: false
needs:
- phpcs
- eslint
- stylelint

24
.stylelintrc.json Normal file
View file

@ -0,0 +1,24 @@
{
"extends": [
"stylelint-config-sass-guidelines",
"stylelint-config-idiomatic-order"
],
"ignoreFiles": [
"**/vendor/*.css",
"**/vendor/**/*.css",
"**/vendor/*.scss",
"**/vendor/**/*.scss",
"**/vendor/*.sass",
"**/vendor/**/*.sass",
"**/vendor/*.less",
"**/vendor/**/*.less"
],
"customSyntax": "postcss-scss",
"rules": {
"indentation": 4,
"string-quotes": "double",
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null
}
}

View file

@ -10,3 +10,6 @@ npm install && npm run prod
```
## Dependencies
- Memcached
## TODO
- Replace fontawesome

3
jsconfig.json Normal file
View file

@ -0,0 +1,3 @@
{
"include": ["./resources/js/**/*"]
}

7396
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,26 +7,46 @@
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
"production": "mix --production",
"eslint": "node ./node_modules/.bin/eslint -c .eslintrc.json ./resources",
"eslint-ci": "node ./node_modules/.bin/eslint -c .eslintrc.json ./resources -f junit",
"stylelint": "npx stylelint '**/*.scss'",
"stylelint-ci": "npx stylelint '**/*.scss' -f json"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.1.1",
"@types/bootstrap": "^5.1.12",
"@types/webpack-env": "^1.17.0",
"@rushstack/eslint-patch": "^1.2.0",
"@types/bootstrap": "^5.2.4",
"@types/webpack-env": "^1.18.0",
"@typescript-eslint/eslint-plugin": "^5.38.0",
"@typescript-eslint/parser": "^5.38.0",
"@vue/eslint-config-typescript": "^11.0.1",
"axios": "^0.21",
"bootstrap": "^5.1.3",
"clean-webpack-plugin": "^4.0.0",
"laravel-mix": "^6.0.6",
"eslint-plugin-vue": "^9.4.0",
"eslint-webpack-plugin": "^3.2.0",
"eslint": "^8.23.1",
"laravel-mix-clean": "^0.1.0",
"laravel-mix-eslint": "^0.2.0",
"laravel-mix-polyfill": "^3.0.1",
"laravel-mix-stylelint": "^1.0.0",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"prettier": "^2.7.1",
"resolve-url-loader": "^5.0.0",
"sass": "^1.52.1",
"sass-loader": "^12.6.0",
"sass": "^1.54.9",
"stylelint-config-idiomatic-order": "^8.1.0",
"stylelint-config-sass-guidelines": "^9.0.1",
"stylelint-webpack-plugin": "^3.3.0",
"stylelint": "^14.11.0",
"sweetalert2": "^11.4.17",
"ts-loader": "^9.3.0",
"typescript": "^4.7.3",
"vue": "^3.2.36",
"vue-loader": "^16.8.3"
"typescript-eslint": "^0.0.1-alpha.0",
"typescript": "^4.8.3",
"vue-eslint-parser": "^9.1.0",
"vue-loader": "^16.8.3",
"vue": "^3.2.36"
}
}

View file

@ -2,7 +2,7 @@
$body-bg: #f8fafc;
// Typography
$font-family-sans-serif: 'Nunito', sans-serif;
$font-family-sans-serif: "Nunito", sans-serif;
$font-size-base: 0.9rem;
$line-height-base: 1.6;
@ -17,3 +17,4 @@ $yellow: #ffed4a;
$green: #38c172;
$teal: #4dc0b5;
$cyan: #6cb2eb;
$white: #fff;

View file

@ -1,59 +1,38 @@
// Fonts
@import url('https://fonts.googleapis.com/css?family=Nunito');
@import url("https://fonts.googleapis.com/css?family=Nunito");
// Variables
@import 'variables';
@import "variables";
// Bootstrap
@import '~bootstrap/scss/bootstrap';
@import "~bootstrap/scss/bootstrap";
.green {
color: #73ce29;
}
.red {
color: #e44a5a;
}
.status {
padding: 3px 5px 2px;
border-radius: 5px;
color: #FFF;
text-transform: uppercase;
color: #fff;
font-size: 0.8em;
font-weight: bold;
padding: 3px 5px 2px;
text-transform: uppercase;
&.checked {
background-color: #73ce29;
}
&.unchecked {
background-color: #f7b542;
}
}
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
margin: 0 !important;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield !important;
}
.otp_fields {
display: flex;
justify-content: center;
& input {
width: 60px;
height: 60px;
text-align: center;
font-size: 35px;
&:not(:last-child) {
margin-right: 10px;
}
}
}
.btn-primary, .btn-danger {
color: white;
.btn-primary,
.btn-danger {
color: $white;
}

File diff suppressed because one or more lines are too long

130
resources/scss/vendor/_normalize.scss vendored Normal file
View file

@ -0,0 +1,130 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html {line-height: 1.15;
text-size-adjust: 100%;}body {margin: 0;}a {background-color: transparent;}
code {font-family: monospace,monospace;
font-size: 1em;}[hidden] {display: none;}
html {font-family: system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
line-height: 1.5;}
*,
::after,
::before {box-sizing: border-box;
border: 0 solid #e2e8f0;}
a {color: inherit;
text-decoration: inherit;}code {font-family: Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;}
svg,
video {display: block;
vertical-align: middle;}
video {max-width: 100%;
height: auto;}
.bg-white {--bg-opacity: 1;
background-color: #fff;
background-color: rgba(255, 255, 255, var(--bg-opacity));}
.bg-gray-100 {--bg-opacity: 1;
background-color: #f7fafc;
background-color: rgba(247, 250, 252, var(--bg-opacity));}
.border-gray-200 {--border-opacity: 1;
border-color: #edf2f7;
border-color: rgba(237, 242, 247, var(--border-opacity));}
.border-gray-400 {--border-opacity: 1;
border-color: #cbd5e0;
border-color: rgba(203, 213, 224, var(--border-opacity));}.border-t {border-top-width: 1px;}.border-r {border-right-width: 1px;}.flex {display: flex;}.grid {display: grid;}.hidden {display: none;}.items-center {align-items: center;}.justify-center {justify-content: center;}.font-semibold {font-weight: 600;}.h-5 {height: 1.25rem;}.h-8 {height: 2rem;}.h-16 {height: 4rem;}.text-sm {font-size: 0.875rem;}.text-lg {font-size: 1.125rem;}.leading-7 {line-height: 1.75rem;}
.mx-auto {margin-right: auto;
margin-left: auto;}.ml-1 {margin-left: 0.25rem;}.mt-2 {margin-top: 0.5rem;}.mr-2 {margin-right: 0.5rem;}.ml-2 {margin-left: 0.5rem;}.mt-4 {margin-top: 1rem;}.ml-4 {margin-left: 1rem;}.mt-8 {margin-top: 2rem;}.ml-12 {margin-left: 3rem;}.-mt-px {margin-top: -1px;}.max-w-xl {max-width: 36rem;}.max-w-6xl {max-width: 72rem;}.min-h-screen {min-height: 100vh;}.overflow-hidden {overflow: hidden;}.p-6 {padding: 1.5rem;}
.py-4 {padding-top: 1rem;
padding-bottom: 1rem;}
.px-4 {padding-right: 1rem;
padding-left: 1rem;}
.px-6 {padding-right: 1.5rem;
padding-left: 1.5rem;}.pt-8 {padding-top: 2rem;}.fixed {position: fixed;}.relative {position: relative;}.top-0 {top: 0;}.right-0 {right: 0;}.shadow {box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06);}.text-center {text-align: center;}
.text-gray-200 {--text-opacity: 1;
color: #edf2f7;
color: rgba(237, 242, 247, var(--text-opacity));}
.text-gray-300 {--text-opacity: 1;
color: #e2e8f0;
color: rgba(226, 232, 240, var(--text-opacity));}
.text-gray-400 {--text-opacity: 1;
color: #cbd5e0;
color: rgba(203, 213, 224, var(--text-opacity));}
.text-gray-500 {--text-opacity: 1;
color: #a0aec0;
color: rgba(160, 174, 192, var(--text-opacity));}
.text-gray-600 {--text-opacity: 1;
color: #718096;
color: rgba(113, 128, 150, var(--text-opacity));}
.text-gray-700 {--text-opacity: 1;
color: #4a5568;
color: rgba(74, 85, 104, var(--text-opacity));}
.text-gray-900 {--text-opacity: 1;
color: #1a202c;
color: rgba(26, 32, 44, var(--text-opacity));}.uppercase {text-transform: uppercase;}.underline {text-decoration: underline;}
.antialiased {-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;}.tracking-wider {letter-spacing: 0.05em;}.w-5 {width: 1.25rem;}.w-8 {width: 2rem;}.w-auto {width: auto;}.grid-cols-1 {grid-template-columns: repeat(1, minmax(0, 1fr));}@keyframes spin {0% {transform: rotate(0deg);}to {transform: rotate(1turn);}}@keyframes spin {0% {transform: rotate(0deg);}to {transform: rotate(1turn);}}@keyframes ping {0% {opacity: 1;
transform: scale(1);}
75%,
to {opacity: 0;
transform: scale(2);}}@keyframes ping {0% {opacity: 1;
transform: scale(1);}
75%,
to {opacity: 0;
transform: scale(2);}}@keyframes pulse {0%,
to {opacity: 1;}50% {opacity: 0.5;}}@keyframes pulse {0%,
to {opacity: 1;}50% {opacity: 0.5;}}@keyframes bounce {0%,
to {animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
transform: translateY(-25%);}
50% {animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
transform: translateY(0);}}@keyframes bounce {0%,
to {animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
transform: translateY(-25%);}
50% {animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
transform: translateY(0);}}@media (min-width:640px) {.sm\:rounded-lg {border-radius: 0.5rem;}.sm\:block {display: block;}.sm\:items-center {align-items: center;}.sm\:justify-start {justify-content: flex-start;}.sm\:justify-between {justify-content: space-between;}.sm\:h-20 {height: 5rem;}.sm\:ml-0 {margin-left: 0;}
.sm\:px-6 {padding-right: 1.5rem;
padding-left: 1.5rem;}.sm\:pt-0 {padding-top: 0;}.sm\:text-left {text-align: left;}.sm\:text-right {text-align: right;}}@media (min-width:768px) {.md\:border-t-0 {border-top-width: 0;}.md\:border-l {border-left-width: 1px;}.md\:grid-cols-2 {grid-template-columns: repeat(2, minmax(0, 1fr));}}@media (min-width:1024px) {.lg\:px-8 {padding-right: 2rem;
padding-left: 2rem;}}@media (prefers-color-scheme:dark) {.dark\:bg-gray-800 {--bg-opacity: 1;
background-color: #2d3748;
background-color: rgba(45, 55, 72, var(--bg-opacity));}
.dark\:bg-gray-900 {--bg-opacity: 1;
background-color: #1a202c;
background-color: rgba(26, 32, 44, var(--bg-opacity));}
.dark\:border-gray-700 {--border-opacity: 1;
border-color: #4a5568;
border-color: rgba(74, 85, 104, var(--border-opacity));}
.dark\:text-white {--text-opacity: 1;
color: #fff;
color: rgba(255, 255, 255, var(--text-opacity));}
.dark\:text-gray-400 {--text-opacity: 1;
color: #cbd5e0;
color: rgba(203, 213, 224, var(--text-opacity));}}

View file

@ -12,14 +12,14 @@ window.axios = axios;
window.bootstrap = bootstrap;
// enable bs tooltips
var tooltipTriggerList = [].slice.call(
document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipTriggerList = [].slice.call(
document.querySelectorAll("[data-bs-toggle=\"tooltip\"]")
);
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
const token = document.head.querySelector('meta[name="csrf-token"]');
const token = document.head.querySelector("meta[name=\"csrf-token\"]");
if (token) {
window.axios.defaults.headers.common["X-CSRF-TOKEN"] = String(

View file

@ -0,0 +1,5 @@
declare module "*.vue" {
import type { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}

View file

@ -25,7 +25,7 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
elements = document.getElementsByClassName("confirmDoneJS");
for (let element of elements) {
for (const element of elements) {
element.addEventListener("submit", function (e) {
e.preventDefault();
const el = e.target;
@ -47,7 +47,7 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
elements = document.getElementsByClassName("confirmSubmitJS");
for (let element of elements) {
for (const element of elements) {
element.addEventListener("submit", function (e) {
e.preventDefault();
const el = e.target;
@ -69,7 +69,7 @@ window.addEventListener("DOMContentLoaded", () => {
});
}
elements = document.getElementsByClassName("confirmJS");
for (let element of elements) {
for (const element of elements) {
element.addEventListener("submit", function (e) {
e.preventDefault();
const el = e.target;

View file

@ -8,20 +8,20 @@ export default {
methods: {
confirm(
title: string,
text: string = "",
text = "",
self: HTMLElement,
after: (response: SweetAlertResult<any>) => void = (
response: SweetAlertResult<any>
) => {},
after: (response: SweetAlertResult) => void,
options: SweetAlertOptions = {}
) {
if (!self) {
throw Error('self is needed to set "this" on callback');
throw Error("self is needed to set 'this' on callback");
}
if (!after) {
throw Error("callback is needed when using confirm");
}
let icon = options.icon ?? "warning";
const icon = options.icon ?? "warning";
Swal.fire({
title: title,
text: text,
@ -36,7 +36,7 @@ export default {
reverseButtons: true,
// * Bootstrap Styling
customClass: {
confirmButton: "btn btn-lg btn-success ms-3 me-3",
confirmButton: "btn btn-lg btn-success ms-3 me-3",
cancelButton: "btn btn-lg btn-danger ms-3 me-3",
},
buttonsStyling: false,

View file

@ -16,7 +16,7 @@ export default {
translation = key
.split(".")
.reduce(
(t, i) => t[i as any] ?? null,
(t, i) => t[i as never] ?? null,
window.__SYSTEM._translations.php
) as string;
if (translation) {
@ -27,8 +27,9 @@ export default {
}
if (translationNotFound) {
translation = (window.__SYSTEM._translations.json[key as any] ??
key) as string;
translation = (window.__SYSTEM._translations.json[key as never] ??
key) as string;
}
const vars = translation.match(/:[0-9A-Za-z_]+/g);
if (vars) {

View file

@ -1,13 +1,19 @@
window.addEventListener("DOMContentLoaded", () => {
let elements = document.getElementsByClassName("otp-field");
const elements = document.getElementsByClassName(
"otp-field"
) as HTMLCollectionOf<HTMLInputElement>;
for (const element of elements) {
element.addEventListener("input", checkFields);
}
});
function checkFields(e: Event) {
const target = e.target as HTMLInputElement;
const target = e.target as HTMLInputElement | null;
if (target) {
target.value = target.value.replace(/[^0-9.]/g, '').replace(/(\..*?)\..*/g, '$1');
target.value = target.value
.replace(/[^0-9.]/g, "")
.replace(/(\..*?)\..*/g, "$1");
}
}

View file

@ -21,7 +21,7 @@ document.addEventListener("DOMContentLoaded", () => {
.replace(/^-/, "")
.replace("-component", "");
let el = document.getElementById(htmlKey),
const el = document.getElementById(htmlKey),
elGroup = document.getElementsByClassName(htmlKey);
// * Init using ID
if (el) {

View file

@ -1,26 +1,17 @@
{
"sourceMap": true,
"compilerOptions": {
"target": "esnext",
"lib": [
"es5", "es6", "dom", "dom.iterable"
],
"lib": ["es5", "es6", "es2021", "dom", "dom.iterable"],
"module": "esnext",
// "isolatedModules": false,
// "experimentalDecorators": true,
// "strictNullChecks": true,
// "removeComments": true,
// "allowSyntheticDefaultImports": true,
// this enables stricter inference for data properties on `this`
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"typeRoots": ["resources/ts/@types", "node_modules/@types"],
"typeRoots": ["resources/ts/@types", "node_modules/@types"]
},
"include": [
"resources/**/*.js",
"resources/**/*.ts",
"resources/**/*.vue"
],
"compileOnSave": false
"include": ["resources/**/*.js", "resources/**/*.ts", "resources/**/*.vue"],
"exclude": ["node_modules"],
"compileOnSave": false,
"experimentalDecorators": true
}

13
webpack.mix.js vendored
View file

@ -3,6 +3,8 @@ const mix = require('laravel-mix')
require('laravel-mix-polyfill')
require('laravel-mix-clean')
require("laravel-mix-eslint");
require("laravel-mix-stylelint");
const assetDir = 'assets'
const polyfill = {
@ -76,9 +78,18 @@ mix.ts("resources/ts/back.ts", "public/js/back.js", { transpileOnly: true })
extractStyles: true,
globalStyles: false,
})
.eslint({
fix: true,
extensions: ["js", "ts"],
})
.stylelint({
configFile: ".stylelintrc.json",
files: ["**/*.scss"],
})
.sass("resources/scss/back.scss", "public/css/back.css")
.polyfill(polyfill)
.version();
.version()
.sourceMaps(false, "inline-source-map");
// * Errors
mix.sass("resources/scss/errors.scss", "public/css/errors.css")