Compare commits

..

2 Commits

Author SHA1 Message Date
865e6465cf Added theming
All checks were successful
Build dev on push / build-api (push) Successful in 7s
Build dev on push / build-redirector (push) Successful in 7s
Build dev on push / build-web (push) Successful in 48s
2025-01-10 21:57:47 +01:00
581462d22a QR Codes with logos
All checks were successful
Build on push / build-api (push) Successful in 7s
Build on push / build-redirector (push) Successful in 6s
Build on push / build-web (push) Successful in 55s
2025-01-10 20:13:08 +01:00
18 changed files with 1219 additions and 437 deletions

View File

@ -1 +1 @@
1.0.4 1.2.0

232
web/package-lock.json generated
View File

@ -27,6 +27,7 @@
"ngx-markdown": "^18.1.0", "ngx-markdown": "^18.1.0",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^17.18.12", "primeng": "^17.18.12",
"qrcode-with-logos": "^1.1.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"
@ -6290,7 +6291,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -6300,7 +6300,6 @@
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
@ -6859,6 +6858,15 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/camelcase-css": { "node_modules/camelcase-css": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@ -7195,7 +7203,6 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"color-name": "~1.1.4" "color-name": "~1.1.4"
@ -7208,7 +7215,6 @@
"version": "1.1.4", "version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/colorette": { "node_modules/colorette": {
@ -8279,6 +8285,15 @@
} }
} }
}, },
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/deep-is": { "node_modules/deep-is": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@ -8442,6 +8457,12 @@
"dev": true, "dev": true,
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
"license": "MIT"
},
"node_modules/dir-glob": { "node_modules/dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -9764,7 +9785,6 @@
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC", "license": "ISC",
"engines": { "engines": {
"node": "6.* || 8.* || >= 10.*" "node": "6.* || 8.* || >= 10.*"
@ -13459,6 +13479,15 @@
"node": ">= 4" "node": ">= 4"
} }
}, },
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/package-json-from-dist": { "node_modules/package-json-from-dist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@ -13616,7 +13645,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -13864,6 +13892,15 @@
"pathe": "^1.1.2" "pathe": "^1.1.2"
} }
}, },
"node_modules/pngjs": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/points-on-curve": { "node_modules/points-on-curve": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
@ -14526,6 +14563,165 @@
"node": ">=0.9" "node": ">=0.9"
} }
}, },
"node_modules/qrcode": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
"yargs": "^15.3.1"
},
"bin": {
"qrcode": "bin/qrcode"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/qrcode-with-logos": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/qrcode-with-logos/-/qrcode-with-logos-1.1.1.tgz",
"integrity": "sha512-vh0LWvwalp7471ODXd4rcXrbKgRpfwfdrjfb3pdr9+Zz/s27+5clRHFxzh5orTm1rusStfHGMACP7GWVAkzpKQ==",
"license": "MIT",
"dependencies": {
"qrcode": "^1.4.4"
}
},
"node_modules/qrcode/node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/qrcode/node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"license": "MIT"
},
"node_modules/qrcode/node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/qrcode/node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"license": "ISC"
},
"node_modules/qrcode/node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"license": "MIT",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/qrcode/node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"license": "ISC",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/qs": { "node_modules/qs": {
"version": "6.13.0", "version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
@ -14792,7 +14988,6 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -14808,6 +15003,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"license": "ISC"
},
"node_modules/require-relative": { "node_modules/require-relative": {
"version": "0.8.7", "version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
@ -15424,6 +15625,12 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"license": "ISC"
},
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@ -15964,7 +16171,6 @@
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-regex": "^5.0.1" "ansi-regex": "^5.0.1"
@ -17880,6 +18086,12 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/which-module": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"license": "ISC"
},
"node_modules/wildcard": { "node_modules/wildcard": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
@ -17901,7 +18113,6 @@
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^4.0.0", "ansi-styles": "^4.0.0",
@ -17967,14 +18178,12 @@
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -17984,7 +18193,6 @@
"version": "4.2.3", "version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"emoji-regex": "^8.0.0", "emoji-regex": "^8.0.0",

View File

@ -32,6 +32,7 @@
"ngx-markdown": "^18.1.0", "ngx-markdown": "^18.1.0",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primeng": "^17.18.12", "primeng": "^17.18.12",
"qrcode-with-logos": "^1.1.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.10" "zone.js": "~0.14.10"

View File

@ -1,4 +1,4 @@
<main *ngIf="isLoggedIn && !hideUI; else home"> <main *ngIf="isLoggedIn && !hideUI; else home" [class]="theme">
<app-header></app-header> <app-header></app-header>
<div class="app"> <div class="app">

View File

@ -1,16 +1,17 @@
import { Component, OnDestroy } from "@angular/core"; import { Component, OnDestroy } from '@angular/core';
import { SidebarService } from "src/app/service/sidebar.service"; import { SidebarService } from 'src/app/service/sidebar.service';
import { Subject } from "rxjs"; import { Subject } from 'rxjs';
import { takeUntil } from "rxjs/operators"; import { takeUntil } from 'rxjs/operators';
import { AuthService } from "src/app/service/auth.service"; import { AuthService } from 'src/app/service/auth.service';
import { GuiService } from "src/app/service/gui.service"; import { GuiService } from 'src/app/service/gui.service';
@Component({ @Component({
selector: "app-root", selector: 'app-root',
templateUrl: "./app.component.html", templateUrl: './app.component.html',
styleUrl: "./app.component.scss", styleUrl: './app.component.scss',
}) })
export class AppComponent implements OnDestroy { export class AppComponent implements OnDestroy {
theme = 'open-redirect';
showSidebar = false; showSidebar = false;
hideUI = false; hideUI = false;
isLoggedIn = false; isLoggedIn = false;
@ -19,23 +20,27 @@ export class AppComponent implements OnDestroy {
constructor( constructor(
private sidebar: SidebarService, private sidebar: SidebarService,
private auth: AuthService, private auth: AuthService,
private gui: GuiService, private gui: GuiService
) { ) {
this.auth.loadUser(); this.auth.loadUser();
this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe((user) => { this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe(user => {
this.isLoggedIn = user !== null && user !== undefined; this.isLoggedIn = user !== null && user !== undefined;
}); });
this.sidebar.visible$ this.sidebar.visible$
.pipe(takeUntil(this.unsubscribe$)) .pipe(takeUntil(this.unsubscribe$))
.subscribe((visible) => { .subscribe(visible => {
this.showSidebar = visible; this.showSidebar = visible;
}); });
this.gui.hideGui$.pipe(takeUntil(this.unsubscribe$)).subscribe((hide) => { this.gui.hideGui$.pipe(takeUntil(this.unsubscribe$)).subscribe(hide => {
this.hideUI = hide; this.hideUI = hide;
}); });
this.gui.theme$.pipe(takeUntil(this.unsubscribe$)).subscribe(theme => {
this.theme = theme;
});
} }
ngOnDestroy() { ngOnDestroy() {

View File

@ -17,6 +17,18 @@
</div> </div>
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<div class="flex items-center justify-center">
<p-button
type="button"
icon="pi pi-palette"
class="btn icon-btn p-button-text"
(onClick)="themeMenu.toggle($event)"></p-button>
<p-menu
#themeMenu
[popup]="true"
[model]="themeList"
class="lang-menu"></p-menu>
</div>
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<p-button <p-button
type="button" type="button"

View File

@ -1,21 +1,24 @@
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from '@angular/core';
import { MenuItem, PrimeNGConfig } from "primeng/api"; import { MenuItem, PrimeNGConfig } from 'primeng/api';
import { Subject } from "rxjs"; import { Subject } from 'rxjs';
import { takeUntil } from "rxjs/operators"; import { takeUntil } from 'rxjs/operators';
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from '@ngx-translate/core';
import { GuiService } from "src/app/service/gui.service"; import { GuiService } from 'src/app/service/gui.service';
import { User } from "src/app/model/auth/user"; import { User } from 'src/app/model/auth/user';
import { AuthService } from "src/app/service/auth.service"; import { AuthService } from 'src/app/service/auth.service';
import { MenuElement } from "src/app/model/view/menu-element"; import { MenuElement } from 'src/app/model/view/menu-element';
import { SidebarService } from "src/app/service/sidebar.service"; import { SidebarService } from 'src/app/service/sidebar.service';
import { SettingsService } from 'src/app/service/settings.service';
@Component({ @Component({
selector: "app-header", selector: 'app-header',
templateUrl: "./header.component.html", templateUrl: './header.component.html',
styleUrls: ["./header.component.scss"], styleUrls: ['./header.component.scss'],
}) })
export class HeaderComponent implements OnInit, OnDestroy { export class HeaderComponent implements OnInit, OnDestroy {
langList: MenuItem[] = []; langList: MenuItem[] = [];
themeList: MenuItem[] = [];
userMenuList!: MenuItem[]; userMenuList!: MenuItem[];
user: User | null = null; user: User | null = null;
private unsubscribe$ = new Subject<void>(); private unsubscribe$ = new Subject<void>();
@ -29,22 +32,30 @@ export class HeaderComponent implements OnInit, OnDestroy {
private guiService: GuiService, private guiService: GuiService,
private auth: AuthService, private auth: AuthService,
private sidebarService: SidebarService, private sidebarService: SidebarService,
private settings: SettingsService
) { ) {
this.guiService.isMobile$ this.guiService.isMobile$
.pipe(takeUntil(this.unsubscribe$)) .pipe(takeUntil(this.unsubscribe$))
.subscribe((isMobile) => { .subscribe(isMobile => {
this.isMobile = isMobile; this.isMobile = isMobile;
if (isMobile) { if (isMobile) {
this.sidebarService.hide(); this.sidebarService.hide();
} }
}); });
this.auth.user$ this.auth.user$.pipe(takeUntil(this.unsubscribe$)).subscribe(async user => {
.pipe(takeUntil(this.unsubscribe$))
.subscribe(async (user) => {
this.user = user; this.user = user;
await this.initMenuLists(); await this.initMenuLists();
}); });
this.themeList = this.settings.settings.themes.map(theme => {
return {
label: theme.label,
command: () => {
this.guiService.theme$.next(theme.name);
},
};
});
} }
async ngOnInit() { async ngOnInit() {
@ -69,17 +80,17 @@ export class HeaderComponent implements OnInit, OnDestroy {
async initLangMenuList() { async initLangMenuList() {
this.langList = [ this.langList = [
{ {
label: "English", label: 'English',
command: () => { command: () => {
this.translate("en"); this.translate('en');
this.setLang("en"); this.setLang('en');
}, },
}, },
{ {
label: "Deutsch", label: 'Deutsch',
command: () => { command: () => {
this.translate("de"); this.translate('de');
this.setLang("de"); this.setLang('de');
}, },
}, },
]; ];
@ -96,13 +107,13 @@ export class HeaderComponent implements OnInit, OnDestroy {
separator: true, separator: true,
}, },
{ {
label: this.translateService.instant("header.logout"), label: this.translateService.instant('header.logout'),
command: () => { command: () => {
this.auth.logout().then(() => { this.auth.logout().then(() => {
console.log("logout"); console.log('logout');
}); });
}, },
icon: "pi pi-sign-out", icon: 'pi pi-sign-out',
}, },
]; ];
} }
@ -110,19 +121,19 @@ export class HeaderComponent implements OnInit, OnDestroy {
translate(lang: string) { translate(lang: string) {
this.translateService.use(lang); this.translateService.use(lang);
this.translateService this.translateService
.get("primeng") .get('primeng')
.subscribe((res) => this.config.setTranslation(res)); .subscribe(res => this.config.setTranslation(res));
} }
async loadLang() { async loadLang() {
const lang = "en"; const lang = 'en';
this.setLang(lang); this.setLang(lang);
this.translate(lang); this.translate(lang);
} }
setLang(lang: string) { setLang(lang: string) {
// this.settings.setSetting(`lang`, lang); // this.settings.setSetting(`lang`, lang);
console.log("setLang", lang); console.log('setLang', lang);
} }
toggleSidebar() { toggleSidebar() {

View File

@ -23,6 +23,13 @@
</span> </span>
</div> </div>
</div> </div>
<div class="flex justify-between">
<div class="flex">
<p-button
class="icon-btn btn"
icon="pi pi-qrcode"
(onClick)="qrDialog(url)"></p-button>
</div>
<div class="flex justify-end"> <div class="flex justify-end">
<p-button <p-button
class="icon-btn btn" class="icon-btn btn"
@ -58,6 +65,7 @@
(click)="restore(url)"></p-button> (click)="restore(url)"></p-button>
</div> </div>
</div> </div>
</div>
</ng-template> </ng-template>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
@ -112,3 +120,46 @@
</div> </div>
</div> </div>
</div> </div>
<p-dialog header="QR Code" [(visible)]="qrCodeDialog.visible" [modal]="true" [closable]="true"
(onHide)="qrDialogClose()"
[style]="{width: '320px'}">
<div class="flex flex-col gap-5">
<div class="flex flex-col gap-3">
<div class="flex gap-1 justify-center items-center">
<input pInputText type="number" class="w-20"
[(ngModel)]="qrCodeDialog.width"/>px
</div>
<p-slider [(ngModel)]="qrCodeDialog.width" [min]="32" [max]="8192" (onChange)="qr()"></p-slider>
</div>
<div>
<p-fileUpload
#imageUpload
maxFileSize="1000000"
customUpload="true"
chooseLabel="{{ 'common.choose' | translate }}"
uploadLabel="{{ 'common.upload' | translate }}"
cancelLabel="{{ 'common.cancel' | translate }}"
name="logo"
class="icon-btn btn"
mode="basic"
chooseIcon="pi pi-image"
accept="image/*"
[maxFileSize]="1000000000"
[customUpload]="true"
(uploadHandler)="logoUpload($event)"
[auto]="true"
></p-fileUpload>
</div>
<div>
<img id="qrCode" alt="QR Code"/>
</div>
<div class="flex justify-end">
<p-button class="btn"
label="{{ 'common.download' | translate }}"
icon="pi pi-save"
(onClick)="downloadQr()"></p-button>
</div>
</div>
</p-dialog>

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, ViewChild } from '@angular/core';
import { PageBase } from 'src/app/core/base/page-base'; import { PageBase } from 'src/app/core/base/page-base';
import { ToastService } from 'src/app/service/toast.service'; import { ToastService } from 'src/app/service/toast.service';
import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service'; import { ConfirmationDialogService } from 'src/app/service/confirmation-dialog.service';
@ -9,6 +9,8 @@ import { ShortUrlsColumns } from 'src/app/modules/admin/short-urls/short-urls.co
import { AuthService } from 'src/app/service/auth.service'; import { AuthService } from 'src/app/service/auth.service';
import { Filter } from 'src/app/model/graphql/filter/filter.model'; import { Filter } from 'src/app/model/graphql/filter/filter.model';
import { SettingsService } from 'src/app/service/settings.service'; import { SettingsService } from 'src/app/service/settings.service';
import QrCodeWithLogo from 'qrcode-with-logos';
import { FileUpload, FileUploadHandlerEvent } from 'primeng/fileupload';
@Component({ @Component({
selector: 'app-short-urls', selector: 'app-short-urls',
@ -19,14 +21,31 @@ export class ShortUrlsPage
extends PageBase<ShortUrl, ShortUrlsDataService, ShortUrlsColumns> extends PageBase<ShortUrl, ShortUrlsDataService, ShortUrlsColumns>
implements OnInit implements OnInit
{ {
@ViewChild('imageUpload') imageUpload!: FileUpload;
shortUrlsWithoutGroup: ShortUrl[] = []; shortUrlsWithoutGroup: ShortUrl[] = [];
groupedShortUrls: { [key: string]: ShortUrl[] } = {}; groupedShortUrls: { [key: string]: ShortUrl[] } = {};
qrCodeDialog: {
visible: boolean;
width: number;
shortUrl: ShortUrl | undefined;
qrCode: QrCodeWithLogo | undefined;
logo: string | undefined;
} = {
visible: false,
width: 512,
shortUrl: undefined,
qrCode: undefined,
logo: undefined,
};
get groupsFromGroupedShortUrls() { get groupsFromGroupedShortUrls() {
return Object.keys(this.groupedShortUrls); return Object.keys(this.groupedShortUrls);
} }
private hide_deleted_filter: Filter = { deleted: { equal: false } }; private hide_deleted_filter: Filter = { deleted: { equal: false } };
get showDeleted() { get showDeleted() {
return !this.filter.some( return !this.filter.some(
f => JSON.stringify(f) === JSON.stringify(this.hide_deleted_filter) f => JSON.stringify(f) === JSON.stringify(this.hide_deleted_filter)
@ -138,6 +157,54 @@ export class ShortUrlsPage
this.load(); this.load();
} }
qrDialog(shortUrl: ShortUrl) {
this.qrCodeDialog.visible = true;
this.qrCodeDialog.shortUrl = shortUrl;
this.qrCodeDialog.width = 512;
setTimeout(() => this.qr(), 100);
}
qrDialogClose() {
this.qrCodeDialog.visible = false;
this.qrCodeDialog.shortUrl = undefined;
this.qrCodeDialog.qrCode = undefined;
}
qr() {
if (!this.qrCodeDialog.shortUrl) return;
const shortUrl = this.qrCodeDialog.shortUrl.shortUrl;
const targetUrl = `${this.settings.settings.api.redirector}/${shortUrl}`;
this.qrCodeDialog.qrCode = new QrCodeWithLogo({
content: targetUrl,
width: this.qrCodeDialog.width,
image: document.getElementById('qrCode') as HTMLImageElement,
logo: this.qrCodeDialog.logo
? {
src: this.qrCodeDialog.logo,
}
: undefined,
});
}
async downloadQr() {
if (!this.qrCodeDialog.qrCode || !this.qrCodeDialog.shortUrl) return;
await this.qrCodeDialog.qrCode.downloadImage(
`${this.qrCodeDialog.shortUrl?.shortUrl}.png`
);
this.qrCodeDialog.visible = false;
this.qrCodeDialog.shortUrl = undefined;
}
logoUpload(event: FileUploadHandlerEvent) {
if (!event.files || event.files.length === 0) return;
this.qrCodeDialog.logo = URL.createObjectURL(event.files[0]);
this.qr();
this.imageUpload.clear();
}
open(url: string) { open(url: string) {
window.open(`${this.settings.settings.api.redirector}/${url}`, '_blank'); window.open(`${this.settings.settings.api.redirector}/${url}`, '_blank');
} }

View File

@ -61,6 +61,7 @@ import { InMemoryCache } from '@apollo/client/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { tokenInterceptor } from 'src/app/core/token.interceptor'; import { tokenInterceptor } from 'src/app/core/token.interceptor';
import { SettingsService } from 'src/app/service/settings.service'; import { SettingsService } from 'src/app/service/settings.service';
import { SliderModule } from 'primeng/slider';
const sharedModules = [ const sharedModules = [
StepsModule, StepsModule,
@ -107,6 +108,7 @@ const sharedModules = [
TriStateCheckboxModule, TriStateCheckboxModule,
InputTextareaModule, InputTextareaModule,
TranslateModule, TranslateModule,
SliderModule,
]; ];
const sharedComponents = [ const sharedComponents = [

View File

@ -3,6 +3,7 @@ import { BehaviorSubject, filter } from 'rxjs';
import { Logger } from 'src/app/service/logger.service'; import { Logger } from 'src/app/service/logger.service';
import { NavigationEnd, Router } from '@angular/router'; import { NavigationEnd, Router } from '@angular/router';
import { SidebarService } from 'src/app/service/sidebar.service'; import { SidebarService } from 'src/app/service/sidebar.service';
import { SettingsService } from 'src/app/service/settings.service';
const logger = new Logger('GuiService'); const logger = new Logger('GuiService');
@ -14,10 +15,14 @@ export class GuiService {
isTablet$ = new BehaviorSubject<boolean>(this.isTabletByWindowWith()); isTablet$ = new BehaviorSubject<boolean>(this.isTabletByWindowWith());
hideGui$ = new BehaviorSubject<boolean>(false); hideGui$ = new BehaviorSubject<boolean>(false);
theme$ = new BehaviorSubject<string>(
this.settingsService.settings.themes[0].name
);
constructor( constructor(
private router: Router, private router: Router,
private sidebarService: SidebarService private sidebarService: SidebarService,
private settingsService: SettingsService
) { ) {
this.router.events this.router.events
.pipe(filter(event => event instanceof NavigationEnd)) .pipe(filter(event => event instanceof NavigationEnd))

View File

@ -1,12 +1,12 @@
import { HttpClient } from "@angular/common/http"; import { HttpClient } from '@angular/common/http';
import { Injectable } from "@angular/core"; import { Injectable } from '@angular/core';
import { throwError } from "rxjs"; import { throwError } from 'rxjs';
import { catchError } from "rxjs/operators"; import { catchError } from 'rxjs/operators';
import { AppSettings } from "src/app/model/config/app-settings"; import { AppSettings } from 'src/app/model/config/app-settings';
import { environment } from "src/environments/environment"; import { environment } from 'src/environments/environment';
@Injectable({ @Injectable({
providedIn: "root", providedIn: 'root',
}) })
export class SettingsService { export class SettingsService {
settings!: AppSettings; settings!: AppSettings;
@ -18,13 +18,19 @@ export class SettingsService {
this.http this.http
.get<AppSettings>(`/assets/config/${environment.config}`) .get<AppSettings>(`/assets/config/${environment.config}`)
.pipe( .pipe(
catchError((error) => { catchError(error => {
reject(error); reject(error);
return throwError(() => error); return throwError(() => error);
}), })
) )
.subscribe((settings) => { .subscribe(settings => {
this.settings = settings; this.settings = settings;
if (this.settings.themes.length === 0) {
this.settings.themes.push({
label: 'Open-redirect',
name: 'open-redirect',
});
}
resolve(); resolve();
}); });
}); });

View File

@ -5,7 +5,11 @@
"themes": [ "themes": [
{ {
"label": "Open-redirect", "label": "Open-redirect",
"name": "Open-redirect" "name": "open-redirect"
},
{
"label": "Maxlan",
"name": "maxlan"
} }
], ],
"api": { "api": {

View File

@ -17,12 +17,15 @@
"api_error": "API Fehler", "api_error": "API Fehler",
"api_key": "API Key", "api_key": "API Key",
"by": "von", "by": "von",
"cancel": "Abbrechen",
"choose": "Auswählen",
"close": "Schließen", "close": "Schließen",
"copied": "Kopiert!", "copied": "Kopiert!",
"copied_to_clipboard": "In zwischenablage kopiert", "copied_to_clipboard": "In zwischenablage kopiert",
"created": "Erstellt", "created": "Erstellt",
"deleted": "Gelöscht", "deleted": "Gelöscht",
"description": "Beschreibung", "description": "Beschreibung",
"download": "Herunterladen",
"edited_at": "Bearbeitet am", "edited_at": "Bearbeitet am",
"editor": "Bearbeiter", "editor": "Bearbeiter",
"error": "Fehler", "error": "Fehler",
@ -36,6 +39,7 @@
"role": "Rolle", "role": "Rolle",
"save": "Speichern", "save": "Speichern",
"updated": "Bearbeitet", "updated": "Bearbeitet",
"upload": "Hochladen",
"urls": "URLs", "urls": "URLs",
"warning": "Warnung" "warning": "Warnung"
}, },
@ -102,6 +106,9 @@
"users.delete": "Löschen", "users.delete": "Löschen",
"users.update": "Bearbeiten" "users.update": "Bearbeiten"
}, },
"qr": {
"width": "Breite"
},
"role": { "role": {
"count_header": "Rolle(n)" "count_header": "Rolle(n)"
}, },

View File

@ -17,12 +17,15 @@
"api_error": "API error", "api_error": "API error",
"api_key": "API Key", "api_key": "API Key",
"by": "by", "by": "by",
"cancel": "Cancel",
"choose": "Choose",
"close": "Close", "close": "Close",
"copied": "Copied!", "copied": "Copied!",
"copied_to_clipboard": "Copied to clipboard", "copied_to_clipboard": "Copied to clipboard",
"created": "Created", "created": "Created",
"deleted": "Deleted", "deleted": "Deleted",
"description": "Description", "description": "Description",
"download": "Download",
"edited_at": "Edited at", "edited_at": "Edited at",
"editor": "Editor", "editor": "Editor",
"error": "Fehler", "error": "Fehler",
@ -36,6 +39,7 @@
"role": "Role", "role": "Role",
"save": "Save", "save": "Save",
"updated": "Updated", "updated": "Updated",
"upload": "Upload",
"urls": "URLs", "urls": "URLs",
"warning": "Warning" "warning": "Warning"
}, },
@ -102,6 +106,9 @@
"users.delete": "Delete", "users.delete": "Delete",
"users.update": "Update" "users.update": "Update"
}, },
"qr": {
"width": "Width"
},
"role": { "role": {
"count_header": "Role(s)" "count_header": "Role(s)"
}, },

View File

@ -10,6 +10,7 @@
@import 'tailwindcss/utilities'; @import 'tailwindcss/utilities';
@import 'styles/theme'; @import 'styles/theme';
@import 'styles/theme_maxlan';
@import 'styles/tablet'; @import 'styles/tablet';
@import 'styles/mobile'; @import 'styles/mobile';
@ -58,7 +59,6 @@ body {
input, input,
.p-checkbox-box, .p-checkbox-box,
.p-dropdown { .p-dropdown {
border: 1px solid $accentColor;
padding: 10px; padding: 10px;
} }
@ -70,16 +70,6 @@ body {
} }
} }
@layer utilities {
.divider {
border-bottom: 1px solid $accentColor;
}
.v-divider {
border-left: 1px solid $accentColor;
}
}
header { header {
height: $headerHeight; height: $headerHeight;
display: flex; display: flex;
@ -129,7 +119,6 @@ main {
.component { .component {
margin: 0 10px; margin: 0 10px;
overflow: auto; overflow: auto;
background-color: $backgroundColor;
flex: 1; flex: 1;
padding: 10px; padding: 10px;
} }
@ -141,7 +130,6 @@ footer {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
color: $textColor;
} }
.btn { .btn {

View File

@ -1,9 +1,10 @@
$headerColor: #a2271f; .open-redirect {
$headerColor: #ef9d0d;
// generated with https://colorffy.com/dark-theme-generator?colors=314390-121212 // generated with https://colorffy.com/dark-theme-generator?colors=314390-121212
$textColor: #fff; $textColor: #fff;
$textColorHighlight: #314390; $textColorHighlight: #ef9d0d;
$textColorHighlight2: #a2271f; $textColorHighlight2: #b76f00;
// https://tailwindcss.com/docs/customizing-colors // https://tailwindcss.com/docs/customizing-colors
$backgroundColor: #1e293b; // slate-800 $backgroundColor: #1e293b; // slate-800
@ -15,9 +16,8 @@ $infoColor: #1ea97c;
$warningColor: #ffc107; $warningColor: #ffc107;
$errorColor: #ff5252; $errorColor: #ff5252;
body {
background-color: $backgroundColor2; background-color: $backgroundColor2;
}
@layer utilities { @layer utilities {
.highlight { .highlight {
@ -404,3 +404,4 @@ p-slider {
} }
} }
} }
}

View File

@ -0,0 +1,407 @@
.maxlan {
$headerColor: #a2271f;
// generated with https://colorffy.com/dark-theme-generator?colors=314390-121212
$textColor: #fff;
$textColorHighlight: #314390;
$textColorHighlight2: #a2271f;
// https://tailwindcss.com/docs/customizing-colors
$backgroundColor: #1e293b; // slate-800
$backgroundColor2: #0f172a; // slate-900
$backgroundColor3: #334155; // slate-700
$accentColor: #475569; // slate-600
$infoColor: #1ea97c;
$warningColor: #ffc107;
$errorColor: #ff5252;
background-color: $backgroundColor2;
@layer utilities {
.highlight {
color: $textColorHighlight;
}
.highlight2 {
color: $textColorHighlight2 !important;
}
.bg {
background-color: $backgroundColor;
}
.bg2 {
background-color: $backgroundColor2;
}
.bg3 {
background-color: $backgroundColor3;
}
.accent {
color: $accentColor;
}
.info {
color: $infoColor;
}
.warning {
color: $warningColor;
}
.error {
color: $errorColor;
}
.deleted {
color: $accentColor !important;
}
}
h1,
h2,
h3 {
color: $headerColor;
}
.app {
.component {
background-color: $backgroundColor;
}
}
.btn-base {
color: $textColor;
background: $textColorHighlight !important;
border: 1px solid $textColorHighlight;
&:focus {
box-shadow: none !important;
}
&:hover {
background: transparent !important;
}
}
.btn {
.p-button {
@extend .btn-base;
}
}
.icon-btn {
background-color: transparent !important;
border: none;
.p-button {
color: $textColor;
background: transparent !important;
padding: 0;
&:hover {
color: $textColorHighlight;
}
}
}
.text-btn {
background-color: transparent !important;
.p-button {
color: $textColor;
background: transparent !important;
border: none !important;
&:hover {
color: $textColorHighlight;
background-color: transparent !important;
}
}
}
.icon-btn-without-hover {
&:hover {
background-color: transparent !important;
}
}
.icon-btn {
&:hover {
background-color: transparent !important;
}
}
.danger-btn,
.danger-icon-btn {
background-color: transparent !important;
.p-button {
color: $textColor;
background: transparent !important;
&:hover {
color: $errorColor;
}
}
}
.hidden-columns-select {
.active-while-panel-open {
.p-button {
color: $textColorHighlight;
}
}
}
.custom-spinner {
.p-progress-spinner-circle {
stroke: $textColorHighlight !important;
}
}
p-paginator {
.p-paginator-element {
&:hover {
color: $textColorHighlight;
}
}
}
.p-highlight {
color: $textColorHighlight2;
box-shadow: none !important;
&:hover {
color: $textColorHighlight2 !important;
}
}
.p-multiselect,
.p-paginator,
.p-dropdown,
input {
background-color: $backgroundColor;
}
.p-inputtext:enabled:focus,
.p-inputtext:enabled:hover,
.p-multiselect:not(.p-disabled):hover,
.p-multiselect:not(.p-disabled).p-focus,
.p-dropdown:not(.p-disabled):hover,
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box:hover,
.p-checkbox:not(.p-checkbox-disabled) .p-checkbox-box.p-focus {
border-color: $textColorHighlight;
box-shadow: none !important;
}
.p-checkbox .p-checkbox-box.p-highlight {
border-color: $textColorHighlight;
background: $textColorHighlight;
box-shadow: none !important;
}
p-checkbox {
.p-checkbox {
.p-checkbox-box {
background-color: $backgroundColor;
}
.p-checkbox-box.p-highlight {
border-color: $textColorHighlight;
background-color: $textColorHighlight;
}
}
}
p-inputSwitch {
.p-inputswitch {
&.p-focus .p-inputswitch-slider {
box-shadow: none !important;
}
.p-inputswitch-slider {
background-color: $headerColor;
&.p-highlight {
border-color: $textColorHighlight;
background: $textColorHighlight;
box-shadow: none !important;
}
}
&.p-inputswitch-checked {
.p-inputswitch-slider {
background-color: $headerColor;
&:before {
background-color: $textColor;
}
}
}
}
}
p-panel-menu {
.p-panelmenu {
.p-panelmenu-content
.p-menuitem:not(.p-highlight):not(.p-disabled)
> .p-menuitem-content:hover
.p-menuitem-link
.p-menuitem-text,
.p-panelmenu-content
.p-menuitem:not(.p-highlight):not(.p-disabled)
> .p-menuitem-content:hover
.p-menuitem-link
.p-menuitem-icon,
.p-panelmenu
.p-panelmenu-content
.p-menuitem:not(.p-highlight):not(.p-disabled)
> .p-menuitem-content:hover
.p-menuitem-link
.p-submenu-icon {
color: $textColorHighlight;
}
.p-panelmenu-header-content {
&:hover {
.p-panelmenu-header-action {
color: $textColorHighlight;
}
}
}
}
}
p-menubar {
.p-menubar {
background-color: $backgroundColor2;
}
.p-menubar-root-list {
display: flex;
gap: 10px;
}
.p-menuitem {
border-radius: 0.5rem;
&:hover {
.p-menuitem-content .p-menuitem-link .p-menuitem-text {
color: $textColorHighlight2;
}
}
}
.p-focus {
background-color: $backgroundColor2;
.p-menuitem-content {
background-color: $backgroundColor2;
}
}
}
p-dropdown {
.p-dropdown:not(.p-disabled).p-focus {
box-shadow: none !important;
border-color: $headerColor !important;
}
.p-dropdown-panel {
background-color: $backgroundColor2;
}
}
p-multiselect {
.p-multiselect-panel,
.p-multiselect-panel .p-multiselect-header {
background-color: $backgroundColor2;
}
}
p-table {
.p-datatable {
.p-datatable-header,
.p-datatable-footer,
.p-datatable-thead > tr > th,
.p-datatable-tbody > tr {
background-color: $backgroundColor;
border-bottom: 1px solid $accentColor;
}
.p-sortable-column.p-highlight,
.p-sortable-column:not(.p-highlight):hover {
background-color: transparent !important;
}
.p-sortable-column:not(.p-highlight):hover,
.p-sortable-column:not(.p-highlight):hover .p-sortable-column-icon {
color: $textColorHighlight;
}
}
}
p-sidebar {
.p-sidebar {
background-color: $backgroundColor;
}
}
.p-steps .p-steps-item.p-highlight .p-steps-number {
background-color: $headerColor;
color: $textColor;
}
.p-dialog {
.p-dialog-header {
background-color: $backgroundColor;
}
.p-dialog-content {
background-color: $backgroundColor;
padding-top: 1rem;
}
.p-dialog-footer {
background-color: $backgroundColor;
}
}
.p-badge {
background: $headerColor;
color: $textColor;
}
p-password {
background-color: transparent;
border: none;
.p-password-panel {
background-color: transparent;
}
}
p-slider {
.p-slider {
.p-slider-range {
background: $headerColor;
}
.p-slider-handle {
border-color: $headerColor;
&:hover {
background: $headerColor;
}
&:focus {
box-shadow: none !important;
}
}
}
}
}