Added Asciidoctor for Backend Dokumentation
@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'org.springframework.boot' version '2.1.3.RELEASE'
|
id 'org.springframework.boot' version '2.1.3.RELEASE'
|
||||||
id 'java'
|
id 'java'
|
||||||
|
id 'org.asciidoctor.jvm.convert' version '2.2.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'io.spring.dependency-management'
|
apply plugin: 'io.spring.dependency-management'
|
||||||
@ -13,6 +14,8 @@ sourceCompatibility = '1.8'
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' }
|
||||||
}
|
}
|
||||||
|
|
||||||
bootWar{
|
bootWar{
|
||||||
@ -73,6 +76,6 @@ dependencies {
|
|||||||
|
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.+'
|
testCompile group: 'junit', name: 'junit', version: '4.+'
|
||||||
|
|
||||||
|
compile "io.github.swagger2markup:swagger2markup:1.3.1"
|
||||||
|
|
||||||
}
|
}
|
||||||
9
frontend/.editorconfig
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
2
frontend/.eslintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/dist
|
||||||
|
quasar.conf.js
|
||||||
41
frontend/.eslintrc.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
parser: 'babel-eslint',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
|
||||||
|
env: {
|
||||||
|
browser: true
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
|
||||||
|
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/essential',
|
||||||
|
// '@vue/prettier'
|
||||||
|
],
|
||||||
|
|
||||||
|
// required to lint *.vue files
|
||||||
|
plugins: [
|
||||||
|
'vue'
|
||||||
|
],
|
||||||
|
|
||||||
|
globals: {
|
||||||
|
'ga': true, // Google Analytics
|
||||||
|
'cordova': true,
|
||||||
|
'__statics': true,
|
||||||
|
'process': true
|
||||||
|
},
|
||||||
|
|
||||||
|
// add your custom rules here
|
||||||
|
rules: {
|
||||||
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
|
||||||
|
// allow console.log during development only
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||||
|
// allow debugger during development only
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
20
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.quasar
|
||||||
|
.DS_Store
|
||||||
|
.thumbs.db
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
/src-cordova/node_modules
|
||||||
|
/src-cordova/platforms
|
||||||
|
/src-cordova/plugins
|
||||||
|
/src-cordova/www
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
8
frontend/.postcssrc.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
// to edit target browsers: use "browserslist" field in package.json
|
||||||
|
require('autoprefixer')
|
||||||
|
]
|
||||||
|
}
|
||||||
35
frontend/.stylintrc
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"blocks": "never",
|
||||||
|
"brackets": "never",
|
||||||
|
"colons": "never",
|
||||||
|
"colors": "always",
|
||||||
|
"commaSpace": "always",
|
||||||
|
"commentSpace": "always",
|
||||||
|
"cssLiteral": "never",
|
||||||
|
"depthLimit": false,
|
||||||
|
"duplicates": true,
|
||||||
|
"efficient": "always",
|
||||||
|
"extendPref": false,
|
||||||
|
"globalDupe": true,
|
||||||
|
"indentPref": 2,
|
||||||
|
"leadingZero": "never",
|
||||||
|
"maxErrors": false,
|
||||||
|
"maxWarnings": false,
|
||||||
|
"mixed": false,
|
||||||
|
"namingConvention": false,
|
||||||
|
"namingConventionStrict": false,
|
||||||
|
"none": "never",
|
||||||
|
"noImportant": false,
|
||||||
|
"parenSpace": "never",
|
||||||
|
"placeholder": false,
|
||||||
|
"prefixVarsWithDollar": "always",
|
||||||
|
"quotePref": "single",
|
||||||
|
"semicolons": "never",
|
||||||
|
"sortOrder": false,
|
||||||
|
"stackedProperties": "never",
|
||||||
|
"trailingWhitespace": "never",
|
||||||
|
"universal": "never",
|
||||||
|
"valid": true,
|
||||||
|
"zeroUnits": "never",
|
||||||
|
"zIndexNormalize": false
|
||||||
|
}
|
||||||
3
frontend/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Quasar App
|
||||||
|
|
||||||
|
> WIP
|
||||||
5
frontend/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@quasar/babel-preset-app'
|
||||||
|
]
|
||||||
|
}
|
||||||
10204
frontend/package-lock.json
generated
Normal file
41
frontend/package.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "labswp_2019_sose_geocaching_frontend",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "BuGa Geocaching SPA",
|
||||||
|
"productName": "labswp_2019_sose_geocaching_frontend",
|
||||||
|
"cordovaId": "",
|
||||||
|
"author": "Timo Volkmann <volkmann@stud.hs-heilbronn.de>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint --ext .js,.vue src",
|
||||||
|
"test": "echo \"No test specified\" && exit 0",
|
||||||
|
"dev": "quasar dev",
|
||||||
|
"build": "quasar build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@quasar/extras": "^1.1.1",
|
||||||
|
"ajv": "6.8.1",
|
||||||
|
"axios": "^0.18.0",
|
||||||
|
"quasar": "^1.0.0-beta.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@quasar/app": "^1.0.0-beta.13",
|
||||||
|
"@vue/eslint-config-prettier": "^4.0.0",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
|
"eslint": "^5.10.0",
|
||||||
|
"eslint-loader": "^2.1.1",
|
||||||
|
"eslint-plugin-vue": "^5.0.0",
|
||||||
|
"strip-ansi": "=3.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.9.0",
|
||||||
|
"npm": ">= 5.6.0",
|
||||||
|
"yarn": ">= 1.6.0"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"last 1 version, not dead, ie >= 11"
|
||||||
|
],
|
||||||
|
"resolutions": {
|
||||||
|
"ajv": "6.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
189
frontend/quasar.conf.js
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Configuration for your app
|
||||||
|
|
||||||
|
module.exports = function (ctx) {
|
||||||
|
return {
|
||||||
|
// app boot file (/src/boot)
|
||||||
|
// --> boot files are part of "main.js"
|
||||||
|
boot: [
|
||||||
|
'axios'
|
||||||
|
],
|
||||||
|
|
||||||
|
css: [
|
||||||
|
'app.styl'
|
||||||
|
],
|
||||||
|
|
||||||
|
extras: [
|
||||||
|
'roboto-font',
|
||||||
|
'material-icons' // optional, you are not bound to it
|
||||||
|
// 'ionicons-v4',
|
||||||
|
// 'mdi-v3',
|
||||||
|
// 'fontawesome-v5',
|
||||||
|
// 'eva-icons'
|
||||||
|
],
|
||||||
|
|
||||||
|
framework: {
|
||||||
|
// all: true, // --- includes everything; for dev only!
|
||||||
|
|
||||||
|
components: [
|
||||||
|
'QLayout',
|
||||||
|
'QHeader',
|
||||||
|
'QDrawer',
|
||||||
|
'QPageContainer',
|
||||||
|
'QPage',
|
||||||
|
'QToolbar',
|
||||||
|
'QToolbarTitle',
|
||||||
|
'QBtn',
|
||||||
|
'QIcon',
|
||||||
|
'QList',
|
||||||
|
'QItem',
|
||||||
|
'QItemSection',
|
||||||
|
'QItemLabel',
|
||||||
|
'QTable',
|
||||||
|
'QTh',
|
||||||
|
'QTr',
|
||||||
|
'QTd',
|
||||||
|
'QCard',
|
||||||
|
'QCardSection',
|
||||||
|
'QCardActions',
|
||||||
|
'QCheckbox',
|
||||||
|
'QSeparator',
|
||||||
|
'QImg',
|
||||||
|
'QTabs',
|
||||||
|
'QTab',
|
||||||
|
'QRouteTab',
|
||||||
|
'QTabPanels',
|
||||||
|
'QTabPanel',
|
||||||
|
'QInput',
|
||||||
|
'QFab',
|
||||||
|
'QFabAction',
|
||||||
|
'QDialog',
|
||||||
|
'QFooter',
|
||||||
|
'QPageSticky',
|
||||||
|
'QAvatar',
|
||||||
|
'QSpinnerPuff',
|
||||||
|
'QExpansionItem',
|
||||||
|
'QParallax',
|
||||||
|
],
|
||||||
|
|
||||||
|
directives: [
|
||||||
|
'Ripple',
|
||||||
|
'ClosePopup'
|
||||||
|
],
|
||||||
|
|
||||||
|
// Quasar plugins
|
||||||
|
plugins: [
|
||||||
|
'Notify'
|
||||||
|
]
|
||||||
|
|
||||||
|
// iconSet: 'ionicons-v4'
|
||||||
|
// lang: 'de' // Quasar language
|
||||||
|
},
|
||||||
|
|
||||||
|
supportIE: false,
|
||||||
|
|
||||||
|
build: {
|
||||||
|
scopeHoisting: true,
|
||||||
|
// vueRouterMode: 'history',
|
||||||
|
// vueCompiler: true,
|
||||||
|
// gzip: true,
|
||||||
|
// analyze: true,
|
||||||
|
// extractCSS: false,
|
||||||
|
//distDir: 'springwebdir/{ctx.modeName}',
|
||||||
|
distDir: '../src/main/resources/public/',
|
||||||
|
extendWebpack (cfg) {
|
||||||
|
cfg.module.rules.push({
|
||||||
|
enforce: 'pre',
|
||||||
|
test: /\.(js|vue)$/,
|
||||||
|
loader: 'eslint-loader',
|
||||||
|
exclude: /node_modules/
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
// https: true,
|
||||||
|
port: 8081,
|
||||||
|
open: true // opens browser window automatically
|
||||||
|
},
|
||||||
|
|
||||||
|
// animations: 'all' --- includes all animations
|
||||||
|
animations: [],
|
||||||
|
|
||||||
|
ssr: {
|
||||||
|
pwa: false
|
||||||
|
},
|
||||||
|
|
||||||
|
pwa: {
|
||||||
|
// workboxPluginMode: 'InjectManifest',
|
||||||
|
// workboxOptions: {},
|
||||||
|
manifest: {
|
||||||
|
// name: 'Quasar App',
|
||||||
|
// short_name: 'Quasar-PWA',
|
||||||
|
// description: 'Best PWA App in town!',
|
||||||
|
display: 'standalone',
|
||||||
|
orientation: 'portrait',
|
||||||
|
background_color: '#ffffff',
|
||||||
|
theme_color: '#027be3',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
'src': 'statics/icons/icon-128x128.png',
|
||||||
|
'sizes': '128x128',
|
||||||
|
'type': 'image/png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'src': 'statics/icons/icon-192x192.png',
|
||||||
|
'sizes': '192x192',
|
||||||
|
'type': 'image/png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'src': 'statics/icons/icon-256x256.png',
|
||||||
|
'sizes': '256x256',
|
||||||
|
'type': 'image/png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'src': 'statics/icons/icon-384x384.png',
|
||||||
|
'sizes': '384x384',
|
||||||
|
'type': 'image/png'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'src': 'statics/icons/icon-512x512.png',
|
||||||
|
'sizes': '512x512',
|
||||||
|
'type': 'image/png'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
cordova: {
|
||||||
|
// id: 'org.cordova.quasar.app'
|
||||||
|
},
|
||||||
|
|
||||||
|
electron: {
|
||||||
|
// bundler: 'builder', // or 'packager'
|
||||||
|
|
||||||
|
extendWebpack (cfg) {
|
||||||
|
// do something with Electron main process Webpack cfg
|
||||||
|
// chainWebpack also available besides this extendWebpack
|
||||||
|
},
|
||||||
|
|
||||||
|
packager: {
|
||||||
|
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
|
||||||
|
|
||||||
|
// OS X / Mac App Store
|
||||||
|
// appBundleId: '',
|
||||||
|
// appCategoryType: '',
|
||||||
|
// osxSign: '',
|
||||||
|
// protocol: 'myapp://path',
|
||||||
|
|
||||||
|
// Window only
|
||||||
|
// win32metadata: { ... }
|
||||||
|
},
|
||||||
|
|
||||||
|
builder: {
|
||||||
|
// https://www.electron.build/configuration/configuration
|
||||||
|
|
||||||
|
// appId: 'quasar-app'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
frontend/src/App.vue
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<template>
|
||||||
|
<div id="q-app">
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "App"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
191
frontend/src/assets/quasar-logo-full.svg
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="67.407623mm"
|
||||||
|
height="62.908276mm"
|
||||||
|
viewBox="0 0 238.84591 222.90334"
|
||||||
|
id="svg3570"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="quasar-logo-full.svg">
|
||||||
|
<defs
|
||||||
|
id="defs3572" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.979899"
|
||||||
|
inkscape:cx="-39.753589"
|
||||||
|
inkscape:cy="27.706388"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="g4895-4-4"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0"
|
||||||
|
fit-margin-left="0"
|
||||||
|
fit-margin-right="0"
|
||||||
|
fit-margin-bottom="0"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1056"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="24"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3575">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-277.71988,-312.33911)">
|
||||||
|
<g
|
||||||
|
id="g4895-4-4"
|
||||||
|
transform="translate(1419.0442,398.9018)">
|
||||||
|
<g
|
||||||
|
transform="translate(-29.620665,-4)"
|
||||||
|
id="g4579-2-20">
|
||||||
|
<g
|
||||||
|
id="g4445-2-0"
|
||||||
|
transform="translate(12.499948,7.809312)">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="44.860481"
|
||||||
|
inkscape:export-xdpi="44.860481"
|
||||||
|
inkscape:export-filename="/home/emanuele/Desktop/logo1.png"
|
||||||
|
transform="translate(-712.85583,-503.26814)"
|
||||||
|
id="g4561-6-7-0">
|
||||||
|
<g
|
||||||
|
transform="translate(16.233481,0)"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:50.25774765px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#263238;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
id="flowRoot4513-6-6-08">
|
||||||
|
<path
|
||||||
|
d="m -402.73125,631.46823 q -0.6125,0.0438 -1.3125,0.0875 -0.65625,0 -1.4,0 l -9.31875,0 q -12.81875,0 -12.81875,-8.44375 l 0,-13.475 q 0,-8.26875 12.6,-8.26875 l 9.75625,0 q 12.6,0 12.6,8.26875 l 0,13.475 q 0,5.03125 -4.4625,7.04375 l 3.10625,2.14375 q 1.35625,0.83125 1.35625,1.70625 0,0.875 -0.7,1.3125 -0.65625,0.48125 -1.88125,0.48125 -0.30625,0 -0.7875,-0.13125 -0.4375,-0.0875 -1.05,-0.48125 l -5.6875,-3.71875 z m 5.38125,-21.74375 q 0,-4.76875 -7.9625,-4.76875 l -9.58125,0 q -7.9625,0 -7.9625,4.76875 l 0,13.3875 q 0,4.94375 8.3125,4.94375 l 8.88125,0 q 8.3125,0 8.3125,-4.94375 l 0,-13.3875 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
|
||||||
|
id="path3428" />
|
||||||
|
<path
|
||||||
|
d="m -368.0585,631.64323 q -11.2875,0 -11.2875,-6.9125 l 0,-12.73125 q 0,-1.8375 2.31875,-1.8375 2.31875,0 2.31875,1.8375 l 0,12.775 q 0,3.325 6.475,3.325 l 8.3125,0 q 6.475,0 6.475,-3.325 l 0,-12.775 q 0,-1.8375 2.31875,-1.8375 2.3625,0 2.3625,1.8375 l 0,12.73125 q 0,6.9125 -11.2875,6.9125 l -8.00625,0 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
|
||||||
|
id="path3430" />
|
||||||
|
<path
|
||||||
|
d="m -327.2833,631.64323 q -9.3625,0 -9.3625,-5.81875 l 0,-2.49375 q 0,-5.775 9.3625,-5.775 l 18.59375,0 0,-0.65625 q 0,-3.0625 -5.38125,-3.0625 l -6.16875,0 q -2.1875,0 -2.1875,-1.70625 0,-1.75 2.1875,-1.75 l 6.16875,0 q 9.93125,0 9.93125,6.51875 l 0,8.575 q 0,6.16875 -9.5375,6.16875 l -13.60625,0 z m 13.34375,-3.4125 q 5.25,0 5.25,-2.8875 l 0,-4.76875 -18.24375,0 q -5.11875,0 -5.11875,2.66875 l 0,2.275 q 0,2.7125 5.11875,2.7125 l 12.99375,0 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
|
||||||
|
id="path3432" />
|
||||||
|
<path
|
||||||
|
d="m -262.77031,626.74323 q 0,4.9 -9.975,4.9 l -17.0625,0 q -2.1875,0 -2.1875,-1.70625 0,-1.70625 2.1875,-1.70625 l 17.0625,0 q 5.38125,0 5.38125,-1.4875 l 0,-2.45 q 0,-1.4875 -5.38125,-1.4875 l -9.0125,0 q -9.975,0 -9.975,-4.76875 l 0,-2.05625 q 0,-5.6 10.28125,-5.6 l 5.99375,0 q 2.23125,0 2.23125,1.75 0,0.875 -0.6125,1.3125 -0.56875,0.39375 -1.61875,0.39375 l -5.99375,0 q -5.73125,0 -5.73125,2.14375 l 0,1.925 q 0,1.79375 5.6875,1.79375 l 9.0125,0 q 9.7125,0 9.7125,4.4625 l 0,2.58125 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
|
||||||
|
id="path3434" />
|
||||||
|
<path
|
||||||
|
d="m -241.91709,631.64323 q -9.3625,0 -9.3625,-5.81875 l 0,-2.49375 q 0,-5.775 9.3625,-5.775 l 18.59375,0 0,-0.65625 q 0,-3.0625 -5.38125,-3.0625 l -6.16875,0 q -2.1875,0 -2.1875,-1.70625 0,-1.75 2.1875,-1.75 l 6.16875,0 q 9.93125,0 9.93125,6.51875 l 0,8.575 q 0,6.16875 -9.5375,6.16875 l -13.60625,0 z m 13.34375,-3.4125 q 5.25,0 5.25,-2.8875 l 0,-4.76875 -18.24375,0 q -5.11875,0 -5.11875,2.66875 l 0,2.275 q 0,2.7125 5.11875,2.7125 l 12.99375,0 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
|
||||||
|
id="path3436" />
|
||||||
|
<path
|
||||||
|
d="m -205.62285,617.33698 q 0,-6.95625 11.2875,-6.95625 l 3.36875,0 q 2.23125,0 2.23125,1.79375 0,1.79375 -2.23125,1.79375 l -3.54375,0 q -6.475,0 -6.475,3.28125 l 0,12.775 q 0,1.8375 -2.31875,1.8375 -2.31875,0 -2.31875,-1.8375 l 0,-12.6875 z"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:43.75px;font-family:'Neuropol X';-inkscape-font-specification:'Neuropol X';text-align:start;letter-spacing:5px;word-spacing:0px;text-anchor:start;fill:#263238;fill-opacity:1"
|
||||||
|
id="path3438" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g5443-0-1-5-1-9"
|
||||||
|
transform="matrix(0.55595317,0,0,0.55595317,-521.93484,-328.66104)"
|
||||||
|
inkscape:export-filename="/home/emanuele/Desktop/logo1.png"
|
||||||
|
inkscape:export-xdpi="44.860481"
|
||||||
|
inkscape:export-ydpi="44.860481">
|
||||||
|
<g
|
||||||
|
inkscape:export-ydpi="3.4165223"
|
||||||
|
inkscape:export-xdpi="3.4165223"
|
||||||
|
transform="matrix(0.09527033,0,0,0.09527033,-1695.2716,706.62921)"
|
||||||
|
id="g8856-6-1-1-9-0-1-9">
|
||||||
|
<circle
|
||||||
|
r="1485"
|
||||||
|
cy="-1361.2571"
|
||||||
|
cx="8317.3574"
|
||||||
|
id="circle8858-1-3-7-6-5-3-0"
|
||||||
|
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:50;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
inkscape:export-xdpi="10.031387"
|
||||||
|
inkscape:export-ydpi="10.031387" />
|
||||||
|
<path
|
||||||
|
inkscape:export-ydpi="10.031387"
|
||||||
|
inkscape:export-xdpi="10.031387"
|
||||||
|
style="opacity:1;fill:#263238;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:square;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 8560.3823,-1361.3029 a 242.947,242.947 0 0 1 -242.947,242.948 242.947,242.947 0 0 1 -242.947,-242.948 242.947,242.947 0 0 1 242.947,-242.946 242.947,242.947 0 0 1 242.947,242.946 z"
|
||||||
|
id="path8860-5-4-8-2-9-0-9"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
id="path8862-5-5-9-1-3-6-3"
|
||||||
|
d="m 9395.8755,-1984.028 a 1245.372,1245.372 0 0 0 -190.8415,-249.4971 l -280.8618,162.1556 c -87.542,-74.7796 -187.0349,-132.0588 -293.2407,-169.9527 -95.8868,97.1766 -172.0602,205.7604 -226.9672,323.8487 312.6411,-21.2772 635.5313,91.8725 935.2898,326.0721 l 176.7612,-102.0532 a 1245.372,1245.372 0 0 0 -120.1398,-290.5734 z"
|
||||||
|
clip-path="none"
|
||||||
|
mask="none"
|
||||||
|
style="fill:#1976d2;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-x="-514.04855"
|
||||||
|
inkscape:transform-center-y="-444.04649" />
|
||||||
|
<path
|
||||||
|
inkscape:transform-center-y="265.80217"
|
||||||
|
inkscape:transform-center-x="-689.63727"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#42a5f5;fill-opacity:1"
|
||||||
|
mask="none"
|
||||||
|
clip-path="none"
|
||||||
|
d="m 9395.9474,-738.70387 a 1245.372,1245.372 0 0 0 120.6501,-290.02213 l -280.8618,-162.1557 c 20.99,-113.2034 20.8488,-228.0063 0.563,-338.9302 -132.1008,-34.4521 -264.2238,-46.1283 -393.9448,-34.635 174.7471,260.1165 238.2017,596.32248 185.2582,973.02076 l 176.7612,102.05309 a 1245.372,1245.372 0 0 0 191.5741,-249.33082 z"
|
||||||
|
id="path8864-4-8-1-2-4-4-4" />
|
||||||
|
<path
|
||||||
|
id="path8866-7-5-5-0-6-4-7"
|
||||||
|
d="m 8317.501,-115.97954 a 1245.372,1245.372 0 0 0 311.4916,-40.52501 l 0,-324.31131 c 108.5321,-38.42382 207.8837,-95.94755 293.8037,-168.97752 -36.214,-131.6287 -92.1636,-251.88868 -166.9776,-358.48372 -137.894,281.39369 -397.3296,504.44998 -750.0316,646.9487 l 0,204.10623 a 1245.372,1245.372 0 0 0 311.7139,41.24263 z"
|
||||||
|
clip-path="none"
|
||||||
|
mask="none"
|
||||||
|
style="fill:#1976d2;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-x="-117.49007"
|
||||||
|
inkscape:transform-center-y="639.34029" />
|
||||||
|
<path
|
||||||
|
inkscape:transform-center-y="444.04652"
|
||||||
|
inkscape:transform-center-x="514.04857"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#42a5f5;fill-opacity:1"
|
||||||
|
mask="none"
|
||||||
|
clip-path="none"
|
||||||
|
d="m 7238.9827,-738.57936 a 1245.372,1245.372 0 0 0 190.8415,249.49714 l 280.8618,-162.15566 c 87.5421,74.77965 187.0349,132.05879 293.2407,169.95271 95.8868,-97.17659 172.0602,-205.76036 226.9672,-323.8487 -312.6411,21.27714 -635.5313,-91.87254 -935.2898,-326.07203 l -176.7612,102.0531 a 1245.372,1245.372 0 0 0 120.1398,290.57344 z"
|
||||||
|
id="path8868-6-7-4-7-2-7-3" />
|
||||||
|
<path
|
||||||
|
id="path8870-5-3-9-3-5-5-1"
|
||||||
|
d="m 7238.9108,-1983.9035 a 1245.372,1245.372 0 0 0 -120.6501,290.0221 l 280.8618,162.1557 c -20.99,113.2035 -20.8488,228.0063 -0.563,338.9302 132.1008,34.4521 264.2238,46.1283 393.9448,34.635 -174.7471,-260.1165 -238.2017,-596.3225 -185.2582,-973.0207 l -176.7612,-102.0532 a 1245.372,1245.372 0 0 0 -191.5741,249.3309 z"
|
||||||
|
clip-path="none"
|
||||||
|
mask="none"
|
||||||
|
style="fill:#1976d2;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
inkscape:transform-center-x="689.63729"
|
||||||
|
inkscape:transform-center-y="-265.80221" />
|
||||||
|
<path
|
||||||
|
inkscape:transform-center-y="-639.34032"
|
||||||
|
inkscape:transform-center-x="117.49005"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#42a5f5;fill-opacity:1"
|
||||||
|
mask="none"
|
||||||
|
clip-path="none"
|
||||||
|
d="m 8317.3572,-2606.6279 a 1245.372,1245.372 0 0 0 -311.4915,40.525 l -1e-4,324.3113 c -108.5321,38.4239 -207.8837,95.9476 -293.8037,168.9776 36.214,131.6287 92.1637,251.8886 166.9776,358.4837 137.894,-281.3937 397.3296,-504.45 750.0316,-646.9487 l 1e-4,-204.1063 a 1245.372,1245.372 0 0 0 -311.714,-41.2426 z"
|
||||||
|
id="path8872-6-3-2-1-3-3-7" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 13 KiB |
1
frontend/src/assets/sad.svg
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
0
frontend/src/boot/.gitkeep
Normal file
15
frontend/src/boot/axios.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
const axiosInstance = axios.create({
|
||||||
|
baseURL: process.env.API,
|
||||||
|
timeout: 10000
|
||||||
|
});
|
||||||
|
console.log("process.env.DEV: "+process.env.DEV);
|
||||||
|
console.log("process.env.API: "+process.env.API);
|
||||||
|
|
||||||
|
export default async ({ Vue }) => {
|
||||||
|
//Vue.prototype.$axios = axios;
|
||||||
|
Vue.prototype.$axios = axiosInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { axiosInstance }
|
||||||
7
frontend/src/boot/map.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import VueLayers from 'vuelayers'
|
||||||
|
import 'vuelayers/lib/style.css' // needs css-loader
|
||||||
|
|
||||||
|
export default async ({ Vue }) => {
|
||||||
|
Vue.use(VueLayers);
|
||||||
|
}
|
||||||
7
frontend/src/boot/qr-scanner.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import VueQrcodeReader from "vue-qrcode-reader";
|
||||||
|
// import qrscanner from "../components/qrscanner";
|
||||||
|
|
||||||
|
// "async" is optional
|
||||||
|
export default async ({ Vue }) => {
|
||||||
|
Vue.use(VueQrcodeReader);
|
||||||
|
}
|
||||||
0
frontend/src/components/.gitkeep
Normal file
22
frontend/src/components/map.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'mymap',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
zoom: 15,
|
||||||
|
center: [ 9.208858198755664, 49.14785422283188],
|
||||||
|
rotation: 0,
|
||||||
|
geolocPosition: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
220
frontend/src/components/qrscanner.vue
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="q-ma-md" v-if="askForPermission">
|
||||||
|
|
||||||
|
<q-btn @click="toggleCamera(!activateCamera)" :loading="loading" unelevated color="positive" stack
|
||||||
|
icon="filter_center_focus"
|
||||||
|
label="QR-Code scannen" class="full-width q-mt-sm"/>
|
||||||
|
<p class="q-mt-sm"><b>Hinweis:</b> Um den QR-Code scannen zu können, müssen Sie den Zugriff auf Ihre Kamera erlauben.</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="activateCamera">
|
||||||
|
<qrcode-drop-zone @decode="onDecode" @init="logErrors">
|
||||||
|
<qrcode-stream :paused="paused" @decode="onDecode" @init="onInit">
|
||||||
|
<div v-if="validating" >
|
||||||
|
<div class="light-dimmed fit">
|
||||||
|
</div>
|
||||||
|
<q-spinner-oval
|
||||||
|
class="absolute-center"
|
||||||
|
color="primary"
|
||||||
|
size="10em"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</qrcode-stream>
|
||||||
|
</qrcode-drop-zone>
|
||||||
|
<div class="q-ma-md">
|
||||||
|
<q-btn v-if="!askForPermission" @click="toggleCamera(!activateCamera)" :loading="loading" unelevated color="primary" stack
|
||||||
|
label="Schließen" class="full-width"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<qrcode-capture v-if="noStreamApiSupport" @decode="onDecode"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'qrscanner',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
askForPermission: true,
|
||||||
|
activateCamera: false,
|
||||||
|
isValid: false,
|
||||||
|
validating: false,
|
||||||
|
loading: false,
|
||||||
|
paused: false,
|
||||||
|
result: null,
|
||||||
|
params: null,
|
||||||
|
noStreamApiSupport: false,
|
||||||
|
|
||||||
|
cacheID: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
console.log("beforeRouteUpdate");
|
||||||
|
this.askForPermission = true;
|
||||||
|
this.activateCamera = false;
|
||||||
|
this.isValid = false;
|
||||||
|
this.validating = false;
|
||||||
|
this.loading = false;
|
||||||
|
this.paused = false;
|
||||||
|
this.result = null;
|
||||||
|
this.params = null;
|
||||||
|
this.noStreamApiSupport = false;
|
||||||
|
this.setCacheId();
|
||||||
|
this.updateCameraState();
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.setCacheId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setCacheId() {
|
||||||
|
console.log("qr-code: 'cache' from url: " + this.$route.params.cache);
|
||||||
|
this.cacheID = this.$route.params.cache ? this.$route.params.cache : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async onDecode(content) {
|
||||||
|
this.result = content;
|
||||||
|
|
||||||
|
this.pauseCamera();
|
||||||
|
|
||||||
|
this.validating = true;
|
||||||
|
this.isValid = await this.validate();
|
||||||
|
|
||||||
|
this.validating = false;
|
||||||
|
this.unPauseCamera();
|
||||||
|
// window.setTimeout(() => {
|
||||||
|
// }, 2000)
|
||||||
|
// //this.unPauseCamera();
|
||||||
|
},
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let params = this.setParams();
|
||||||
|
console.log(params);
|
||||||
|
if (!params.cacheID || !params.stationID || !params.durchgefuehrterCacheID || !params.token) {
|
||||||
|
console.log("Mindestens 1 Parameter konnte nicht erkannt werden!");
|
||||||
|
let header = "Fehler!";
|
||||||
|
let message = "Der erkannte QR-Code ist fehlerhaft oder gehört nicht zu BuGa Geocaching.";
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', { message: message, title: header, });
|
||||||
|
resolve(false);
|
||||||
|
} else if (isNaN(params.cacheID) || isNaN(params.stationID) || isNaN(params.durchgefuehrterCacheID)) {
|
||||||
|
console.log("Mindestens 1 Parameter ist fehlerhaft!");
|
||||||
|
let header = "Fehler!";
|
||||||
|
let message = "Der erkannte QR-Code ist fehlerhaft oder gehört nicht zu BuGa Geocaching.";
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', { message: message, title: header, });
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
this.$axios.put('/api/checkStation', null, { params })
|
||||||
|
.then((response) => {
|
||||||
|
console.log("resolve(true)");
|
||||||
|
console.log("cache access definition");
|
||||||
|
console.log(response.data.cacheAccesDefinition);
|
||||||
|
resolve(true);
|
||||||
|
this.askForPermission = true;
|
||||||
|
this.activateCamera = false;
|
||||||
|
if (Number(response.data.cacheAccesDefinition.id) === 0) {
|
||||||
|
this.$router.push({path: `/station/${params.cacheID}/${params.stationID}`});
|
||||||
|
} else if (Number(response.data.cacheAccesDefinition.id) === 1) {
|
||||||
|
this.$router.push({path: `/CacheEnd/${params.cacheID}`});
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log("resolve(false)");
|
||||||
|
let header = "";
|
||||||
|
let message = "";
|
||||||
|
if (error.response) {
|
||||||
|
console.log(error.response);
|
||||||
|
if (this.$route.path === "/qr-scanner") {
|
||||||
|
console.log(this.$route);
|
||||||
|
header = "Falsche Station!";
|
||||||
|
message = "Bitte setze einen bereits angefangenen Cache " +
|
||||||
|
"an der richtigen Station fort oder beginne einen neuen Cache indem du eine der " +
|
||||||
|
"Startstationen einscannst. Du findest die Startstationen über die Karte.";
|
||||||
|
} else {
|
||||||
|
header = "Falsche Station!";
|
||||||
|
message = "Du hast diese Station schon gescannt oder sie ist nicht die korrekte Folgestation.";
|
||||||
|
message += "Prüfe ob du das Rätsel korrekt gelöst hast und finde die richtige Station.";
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
console.log(error.request);
|
||||||
|
header = "Server nicht erreichbar!";
|
||||||
|
message = "Die Verbindung zum Server ist gestört. Versuchen Sie es später noch einmal.";
|
||||||
|
} else {
|
||||||
|
console.log(error);
|
||||||
|
console.log('Error', error.message);
|
||||||
|
header = "Fehler!";
|
||||||
|
message = error.message;
|
||||||
|
}
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', { message: message, title: header, });
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
window.setTimeout(() => {
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setParams() {
|
||||||
|
console.log("setParams: ");
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
}
|
||||||
|
params.cacheID = this.result.split('/')[0];
|
||||||
|
params.stationID = this.result.split('/')[1];
|
||||||
|
params.durchgefuehrterCacheID = this.CacheID ? this.cacheID : params.cacheID;
|
||||||
|
console.log(params.cacheID + " und " + params.stationID);
|
||||||
|
this.updateProps();
|
||||||
|
return params;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateProps() {
|
||||||
|
console.log("emit result!");
|
||||||
|
this.$emit('result', this.params)
|
||||||
|
},
|
||||||
|
updateCameraState() {
|
||||||
|
console.log("Camera State:");
|
||||||
|
console.log(!this.askForPermission);
|
||||||
|
this.$emit('camera', !this.askForPermission)
|
||||||
|
},
|
||||||
|
|
||||||
|
pauseCamera() {
|
||||||
|
this.paused = true
|
||||||
|
},
|
||||||
|
|
||||||
|
unPauseCamera() {
|
||||||
|
this.paused = false
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleCamera(bool) {
|
||||||
|
this.activateCamera = bool;
|
||||||
|
if (this.askForPermission === false) {
|
||||||
|
this.askForPermission = true;
|
||||||
|
}
|
||||||
|
this.updateCameraState();
|
||||||
|
},
|
||||||
|
|
||||||
|
logErrors(promise) {
|
||||||
|
promise.catch(console.error)
|
||||||
|
},
|
||||||
|
|
||||||
|
async onInit(promise) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
await promise
|
||||||
|
} catch (error) {
|
||||||
|
if (error.name === 'StreamApiNotSupportedError') {
|
||||||
|
this.noStreamApiSupport = true
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
this.askForPermission = false;
|
||||||
|
this.updateCameraState();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
1
frontend/src/css/app.styl
Normal file
@ -0,0 +1 @@
|
|||||||
|
// app global css
|
||||||
19
frontend/src/css/quasar.variables.styl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Quasar Stylus Variables
|
||||||
|
// --------------------------------------------------
|
||||||
|
// To customize the look and feel of this app, you can override
|
||||||
|
// the Stylus variables found in Quasar's source Stylus files.
|
||||||
|
|
||||||
|
// Check documentation for full list of Quasar variables
|
||||||
|
|
||||||
|
// It's highly recommended to change the default colors
|
||||||
|
// to match your app's branding.
|
||||||
|
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||||
|
|
||||||
|
$primary = #027BE3
|
||||||
|
$secondary = #26A69A
|
||||||
|
$accent = #9C27B0
|
||||||
|
|
||||||
|
$positive = #21BA45
|
||||||
|
$negative = #C10015
|
||||||
|
$info = #31CCEC
|
||||||
|
$warning = #F2C037
|
||||||
20
frontend/src/index.template.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><%= htmlWebpackPlugin.options.productName %></title>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="description" content="<%= htmlWebpackPlugin.options.productDescription %>">
|
||||||
|
<meta name="format-detection" content="telephone=no">
|
||||||
|
<meta name="msapplication-tap-highlight" content="no">
|
||||||
|
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (htmlWebpackPlugin.options.ctx.mode.cordova) { %>, viewport-fit=cover<% } %>">
|
||||||
|
|
||||||
|
<link rel="icon" href="statics/buga_logo.svg" type="image/x-icon">
|
||||||
|
<!-- <link rel="icon" type="image/png" sizes="32x32" href="statics/icons/favicon-32x32.png">-->
|
||||||
|
<!-- <link rel="icon" type="image/png" sizes="16x16" href="statics/icons/favicon-16x16.png">-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- DO NOT touch the following DIV -->
|
||||||
|
<div id="q-app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
316
frontend/src/layouts/MyLayout.vue
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout view="hhh Lpr lFf" @click.native="counter()">
|
||||||
|
<q-header id="qheader" class="bg-white text-grey-14" elevated>
|
||||||
|
<q-toolbar class="q-pa-md">
|
||||||
|
<router-link to="/">
|
||||||
|
<q-img src="statics/buga_logo.svg" style="width: 40px;" class="q-mr-sm"/>
|
||||||
|
</router-link>
|
||||||
|
<q-toolbar-title>
|
||||||
|
Geocaching
|
||||||
|
</q-toolbar-title>
|
||||||
|
<!-- <router-link v-if="getUser" to="/Profile" class="text-grey-14">-->
|
||||||
|
<a href="#" v-if="getUser" class="row q-mx-sm items-center q-gutter-x-sm text-grey-14">
|
||||||
|
<p class="q-ma-none gt-xs">{{ getUser.email }}</p>
|
||||||
|
<q-avatar size="33px" font-size="33px" color="white" icon="account_circle">
|
||||||
|
</q-avatar>
|
||||||
|
<q-menu
|
||||||
|
transition-show="jump-down"
|
||||||
|
transition-hide="jump-up"
|
||||||
|
anchor="bottom right" self="top right"
|
||||||
|
:offset="[0, 10]">
|
||||||
|
<q-item clickable v-close-popup to="/Profile" class="text-grey-14">
|
||||||
|
<q-item-section>
|
||||||
|
Profil
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="perm_identity"/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable v-close-popup @click="logout" class="text-grey-14">
|
||||||
|
<q-item-section>
|
||||||
|
Logout
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="logout"/>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-menu>
|
||||||
|
</a>
|
||||||
|
<!-- </router-link>-->
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
class="q-pa-sm"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
ripple
|
||||||
|
@click="leftDrawerOpen = !leftDrawerOpen"
|
||||||
|
aria-label="Menu"
|
||||||
|
>
|
||||||
|
<q-icon name="menu"/>
|
||||||
|
</q-btn>
|
||||||
|
<!--<div>Quasar v{{ $q.version }}</div>-->
|
||||||
|
</q-toolbar>
|
||||||
|
</q-header>
|
||||||
|
|
||||||
|
<q-drawer
|
||||||
|
side="left"
|
||||||
|
v-model="leftDrawerOpen"
|
||||||
|
bordered
|
||||||
|
show-if-above
|
||||||
|
content-class="">
|
||||||
|
<q-list>
|
||||||
|
<q-item-label header>BuGa Geocaching</q-item-label>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="directions"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Startseite</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-if="this.$store.state.auth.isAuthenticated"
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/qr-scanner"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="filter_center_focus"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>QR-Code Scanner</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/Overview"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="map"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Alle Caches</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-if="this.$store.state.auth.isAuthenticated"
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/mycaches"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="bookmarks"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Meine Caches</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/ranking"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="list"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Top 10 Rangliste</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-if="this.$store.state.auth.isAuthenticated"
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/Profile"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="perm_identity"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Profil</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-if="!this.$store.state.auth.isAuthenticated"
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/Register"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="assignment"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Registrieren</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-if="this.$store.state.auth.isAuthenticated"
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
@click="logout()"
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="logout"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Logout</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item
|
||||||
|
v-if="!this.$store.state.auth.isAuthenticated"
|
||||||
|
clickable
|
||||||
|
class="text-primary"
|
||||||
|
v-ripple
|
||||||
|
tag="a"
|
||||||
|
to="/Login"
|
||||||
|
>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-icon name="logout"/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>Login</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <q-item-->
|
||||||
|
<!-- disable-->
|
||||||
|
<!-- class="text-grey-5"-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <q-item-section avatar>-->
|
||||||
|
<!-- </q-item-section>-->
|
||||||
|
<!-- <q-item-section>-->
|
||||||
|
<!-- <q-item-label>dev: {{ clickCounter }} clicks</q-item-label>-->
|
||||||
|
<!-- </q-item-section>-->
|
||||||
|
<!-- </q-item>-->
|
||||||
|
|
||||||
|
|
||||||
|
</q-list>
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-page-container>
|
||||||
|
<router-view/>
|
||||||
|
</q-page-container>
|
||||||
|
|
||||||
|
<q-dialog v-model="dialogShow" persistent transition-show="scale" transition-hide="scale">
|
||||||
|
<q-card :class="dialogColorBg" style="min-width: 300px">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">{{ dialogTitle }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section>
|
||||||
|
{{ dialogMessage }}
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right" class="bg-white text-teal">
|
||||||
|
<q-btn @click="dialogClose" flat label="OK" :color="dialogColorBtn" v-close-popup/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MyLayout",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
leftDrawerOpen: this.$q.platform.is.desktop,
|
||||||
|
clickCounter: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dialogShow: {
|
||||||
|
get() {
|
||||||
|
// console.log("get dialogShow: "+this.$store.state.dialog.dialog.show)
|
||||||
|
return this.$store.state.dialog.dialog.show;
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
// console.log("set dialogShow: "+val)
|
||||||
|
this.$store.state.dialog.dialog.show = val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dialogMessage: {
|
||||||
|
get() {
|
||||||
|
// console.log("get dialogMessage: "+this.$store.state.dialog.dialog.message)
|
||||||
|
return this.$store.state.dialog.dialog.message;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dialogTitle: {
|
||||||
|
get() {
|
||||||
|
// console.log("get dialogTitle: "+this.$store.state.dialog.dialog.title)
|
||||||
|
return this.$store.state.dialog.dialog.title;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dialogColorBg: {
|
||||||
|
get() {
|
||||||
|
// console.log("get dialogColorBg: " + this.$store.state.dialog.dialog.colorBackground)
|
||||||
|
return this.$store.state.dialog.dialog.colorBackground;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dialogColorBtn: {
|
||||||
|
get() {
|
||||||
|
// console.log("get dialogColorBtn: "+this.$store.state.dialog.dialog.colorButton)
|
||||||
|
return this.$store.state.dialog.dialog.colorButton;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getUser: {
|
||||||
|
get() {
|
||||||
|
// console.log("get dialogColorBtn: "+this.$store.state.dialog.dialog.colorButton)
|
||||||
|
return this.$store.getters['auth/GET_USER'];
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
//this.evalAuthentication();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
counter() {
|
||||||
|
this.clickCounter++;
|
||||||
|
//console.log(this.clickCounter);
|
||||||
|
},
|
||||||
|
dialogClose() {
|
||||||
|
console.log("dialogClose(): ")
|
||||||
|
this.$store.commit('dialog/RESET_MESSAGE_DIALOG');
|
||||||
|
},
|
||||||
|
evalAuthentication() {
|
||||||
|
this.$store.commit('auth/SET_AUTHENTICATED');
|
||||||
|
this.$store.commit('auth/SET_USER');
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
console.log("logout()");
|
||||||
|
console.log(JSON.parse(localStorage.getItem('userToken')));
|
||||||
|
localStorage.removeItem('userToken');
|
||||||
|
this.evalAuthentication();
|
||||||
|
this.$router.push({path: `/login`})
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
278
frontend/src/pages/Cache.vue
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<form>
|
||||||
|
<div class="q-pa-md q-gutter-y-md">
|
||||||
|
<p class="text-h5">{{ isNewCache ? "Neuen Cache erstellen" : "Cache bearbeiten"}}</p>
|
||||||
|
<q-input class="col" dense stack-label filled v-model="computedCache.name" label="Name"/>
|
||||||
|
<q-input class="col" dense stack-label filled v-model="computedCache.rankingPoints" label="Punktewert"/>
|
||||||
|
<p class="text-h6">Beschreibung</p>
|
||||||
|
<q-editor
|
||||||
|
:toolbar="[
|
||||||
|
['bold', 'italic', 'strike', 'underline'],
|
||||||
|
['undo', 'redo'],
|
||||||
|
['hr', 'link'],
|
||||||
|
['fullscreen'],
|
||||||
|
]"
|
||||||
|
v-model="computedCache.description"
|
||||||
|
min-height="5rem"
|
||||||
|
max-height="50vh"
|
||||||
|
/>
|
||||||
|
<p class="text-h6">Belohnung</p>
|
||||||
|
<q-editor
|
||||||
|
:toolbar="[
|
||||||
|
['bold', 'italic', 'strike', 'underline'],
|
||||||
|
['undo', 'redo'],
|
||||||
|
['hr', 'link'],
|
||||||
|
['fullscreen'],
|
||||||
|
]"
|
||||||
|
v-model="computedCache.reward.rewardDescription"
|
||||||
|
min-height="5rem"
|
||||||
|
max-height="50vh"
|
||||||
|
/>
|
||||||
|
<p class="text-h6">Stationen</p>
|
||||||
|
<q-list bordered separator class="rounded-borders" v-if="computedCache.stationen.length > 0">
|
||||||
|
|
||||||
|
<q-item v-for="(station, index) in computedCache.stationen" :key="index">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar color="primary" text-color="white">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</q-avatar>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section top>
|
||||||
|
<q-item-label lines="1">
|
||||||
|
<span class="text-grey-8">ID: {{ station.id ? station.id : "keine" }}</span>
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label lines="1" class="q-mt-xs text-body2">
|
||||||
|
<span class="cursor-pointer" v-html="station.description"></span>
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label caption lines="1">
|
||||||
|
Koordinaten: {{ station.lattitude }}, {{ station.longitude }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section side>
|
||||||
|
<div class="text-grey-8 q-gutter-xs">
|
||||||
|
<q-btn @click="deleteStation(index)" class="" color="" flat dense round icon="delete"/>
|
||||||
|
<q-btn @click="editStation(index)" class="" color="" flat dense round icon="edit"/>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
|
||||||
|
</q-list>
|
||||||
|
<p class=""><b>Hinweis:</b> Ein Cache muss mindestens aus zwei Stationen bestehen (inklusive der Endstation).</p>
|
||||||
|
<div class="row reverse">
|
||||||
|
<q-btn @click="addStation" class="full-width" unelevated color="primary" label="Station hinzufügen" icon-right="add"/>
|
||||||
|
</div>
|
||||||
|
<p class="text-h6">Endstation</p>
|
||||||
|
<q-card flat bordered class="rounded-borders">
|
||||||
|
<q-item>
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-avatar color="primary" text-color="white">
|
||||||
|
{{ computedCache.stationen.length + 1 }}
|
||||||
|
</q-avatar>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section top>
|
||||||
|
<q-item-label lines="1">
|
||||||
|
<span class="text-grey-8">ID: {{ computedEndstation.id ? computedEndstation.id : "keine" }}</span>
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label lines="1" class="q-mt-xs text-body2">
|
||||||
|
<span class="cursor-pointer">Endstation</span>
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label caption lines="1">
|
||||||
|
Koordinaten: {{ computedEndstation.lattitude }}, {{ computedEndstation.longitude }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section side>
|
||||||
|
<div class="text-grey-8 q-gutter-xs">
|
||||||
|
<q-btn @click="editEndStation" class="" color="" flat dense round icon="edit"/>
|
||||||
|
</div>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-card>
|
||||||
|
<div class="row q-mt-xl">
|
||||||
|
<q-btn @click="saveCache" class="full-width q-mb-md" color="primary" unelevated stack label="Cache speichern"
|
||||||
|
icon="save_alt"/>
|
||||||
|
<q-btn to="/overview" class="full-width" color="negative" unelevated stack label="Abbrechen" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapGetters} from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Cache",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
scrolldown: false,
|
||||||
|
isNewCache: this.$route.params.id === undefined,
|
||||||
|
//tempCache: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
console.log("beforeRouteUpdate: reset data and fetch");
|
||||||
|
this.$store.commit('cacheCollector/RESET_NEW_CACHE');
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeCreate: function () {
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
console.log("isNewCache: " + this.isNewCache);
|
||||||
|
console.log("Route Params: " + this.$route.params.id);
|
||||||
|
if (!this.isNewCache && !this.$store.getters['cacheCollector/GET_LOCK']) {
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("/api/allCaches");
|
||||||
|
//console.log(JSON.stringify(response.data));
|
||||||
|
console.log(response.data);
|
||||||
|
let cache = response.data.find(cache => Number(cache.id) === Number(this.$route.params.id));
|
||||||
|
console.log(cache);
|
||||||
|
let stations = cache.stationen.filter(station => station.description !== "Endstation");
|
||||||
|
let endstation = cache.stationen.find(station => station.description === "Endstation");
|
||||||
|
console.log(stations);
|
||||||
|
console.log(endstation);
|
||||||
|
this.$store.commit('cacheCollector/SET_CACHE', cache);
|
||||||
|
this.$store.commit('cacheCollector/SET_STATIONS', stations);
|
||||||
|
this.$store.commit('cacheCollector/SET_ENDSTATION', endstation);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$store.commit('cacheCollector/SET_LOCK', false);
|
||||||
|
},
|
||||||
|
beforeMount: function () {
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
console.log("Scrolldown: " + this.scrolldown)
|
||||||
|
},
|
||||||
|
updated: function () {
|
||||||
|
if (this.scrolldown) {
|
||||||
|
console.log("scroll down...");
|
||||||
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
|
this.scrolldown = false
|
||||||
|
}
|
||||||
|
//this.SET_CACHE(this.tempCache);
|
||||||
|
//this.scrollToBottom();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
editEndStation() {
|
||||||
|
console.log("editEndStation()");
|
||||||
|
const station = this.$store.getters['cacheCollector/GET_ENDSTATION'];
|
||||||
|
console.log(station);
|
||||||
|
this.$store.commit('cacheCollector/SET_TEMPSTATION', station);
|
||||||
|
this.$router.push({path: `/tempendstation/`}); // add parameter
|
||||||
|
|
||||||
|
},
|
||||||
|
addStation() {
|
||||||
|
this.$router.push({path: '/station'});
|
||||||
|
},
|
||||||
|
editStation(index) {
|
||||||
|
console.log("editStation(" + index + ")");
|
||||||
|
console.log(this.$store.getters['cacheCollector/GET_STATIONS']);
|
||||||
|
const station = this.$store.getters['cacheCollector/GET_STATIONS'][index];
|
||||||
|
console.log(station);
|
||||||
|
this.$store.commit('cacheCollector/SET_TEMPSTATION', station);
|
||||||
|
this.$store.commit('cacheCollector/SET_LOCK', true);
|
||||||
|
this.$router.push({path: `/tempstation/${index}`}); // add parameter
|
||||||
|
},
|
||||||
|
deleteStation(index) {
|
||||||
|
this.$store.commit('cacheCollector/REMOVE_STATION', index);
|
||||||
|
},
|
||||||
|
saveCache() {
|
||||||
|
// commit to store, send to api, if success -> reset store
|
||||||
|
console.log("saveCache!");
|
||||||
|
let cache = JSON.parse(JSON.stringify(this.computedCache));
|
||||||
|
console.log(cache);
|
||||||
|
cache.stationen.push(this.computedEndstation);
|
||||||
|
console.log("isnewcache "+this.isNewCache)
|
||||||
|
if (this.isNewCache) {
|
||||||
|
console.log("CREATECACHE")
|
||||||
|
this.$axios.post('/api/createCache', cache)
|
||||||
|
.then((response) => {
|
||||||
|
console.log("POST api/createCache: " + response.statusText);
|
||||||
|
this.$store.commit('cacheCollector/RESET_NEW_CACHE');
|
||||||
|
this.$router.push({path: '/overview'});
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Bitte Eingaben überprüfen!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log("EDITCACHE")
|
||||||
|
this.$axios.put('/api/editCache', cache)
|
||||||
|
.then((response) => {
|
||||||
|
console.log("POST api/editCache: " + response.statusText);
|
||||||
|
this.$store.commit('cacheCollector/RESET_NEW_CACHE');
|
||||||
|
this.$router.push({path: '/overview'});
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Bitte Eingaben überprüfen!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
computedCache: {
|
||||||
|
set(value) {
|
||||||
|
console.log("set cache");
|
||||||
|
this.$store.commit('cacheCollector/SET_CACHE', value);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
console.log("get cache");
|
||||||
|
return this.$store.getters['cacheCollector/GET_CACHE'];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computedStations: {
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('cacheCollector/SET_STATIONS', value);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.$store.getters['cacheCollector/GET_STATIONS'];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computedEndstation: {
|
||||||
|
set(value) {
|
||||||
|
this.$store.commit('cacheCollector/SET_ENDSTATION', value);
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.$store.getters['cacheCollector/GET_ENDSTATION'];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
52
frontend/src/pages/CacheEnd.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="q-ma-md">
|
||||||
|
<p class="text-h4">{{ cacheName }}</p>
|
||||||
|
<p class="text-h5">Herzlichen Glückwunsch!</p>
|
||||||
|
<p class="">Du hast alle Stationen gefunden und diesen Cache damit erfolgreich abgeschlossen!</p>
|
||||||
|
<p class="">Dir wurden <b>{{ rankingPoints }} Punkte</b> gutgeschrieben.</p>
|
||||||
|
<p class="text-h5">Deine Belohnung:</p>
|
||||||
|
<p v-html="cache.reward.rewardDescription"></p>
|
||||||
|
|
||||||
|
<div class="column q-gutter-y-md">
|
||||||
|
<q-btn unelevated color="primary" label="Rangliste" to="/ranking"/>
|
||||||
|
<q-btn unelevated color="primary" label="Zur Übersicht" to="/overview"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cacheID: "",
|
||||||
|
cacheName: "",
|
||||||
|
cache: {
|
||||||
|
reward: "",
|
||||||
|
},
|
||||||
|
rankingPoints: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("/api/allCaches");
|
||||||
|
console.log(JSON.stringify(response.data));
|
||||||
|
console.log(response.data);
|
||||||
|
const cache = response.data.find(cache => cache.id === Number(this.$route.params.cache));
|
||||||
|
this.cache = cache;
|
||||||
|
this.cacheName = cache.name;
|
||||||
|
this.rankingPoints = cache.rankingPoints;
|
||||||
|
this.cacheID = this.$route.params.cache;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
125
frontend/src/pages/CacheStart.vue
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
|
||||||
|
data-projection="EPSG:4326" style="height: 200px" >
|
||||||
|
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
|
||||||
|
|
||||||
|
<vl-geoloc @update:position="geolocPosition = $event">
|
||||||
|
<template slot-scope="geoloc">
|
||||||
|
<vl-feature v-if="geoloc.position" id="position-feature">
|
||||||
|
<vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
|
||||||
|
</vl-feature>
|
||||||
|
</template>
|
||||||
|
</vl-geoloc>
|
||||||
|
|
||||||
|
<vl-layer-tile id="osm">
|
||||||
|
<vl-source-osm></vl-source-osm>
|
||||||
|
</vl-layer-tile>
|
||||||
|
</vl-map>
|
||||||
|
<div v-if="!cameraActive" class="q-ma-md">
|
||||||
|
<p class="text-h4">{{ cacheName }}</p>
|
||||||
|
<p>{{ instruction }}</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<qrscanner @result="updateResult($event)" @camera="updateCamera($event)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueLayers from 'vuelayers'
|
||||||
|
import 'vuelayers/lib/style.css' // needs css-loader
|
||||||
|
|
||||||
|
Vue.use(VueLayers);
|
||||||
|
import qrscanner from "../components/qrscanner";
|
||||||
|
export default {
|
||||||
|
name: "CacheStart",
|
||||||
|
components: {qrscanner},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
result: null,
|
||||||
|
cameraActive: false,
|
||||||
|
cacheID: "",
|
||||||
|
cacheName: "",
|
||||||
|
//code: "8/7",
|
||||||
|
instruction: "Bitte begib Dich zu der auf der Karte angezeigten Position. An dieser Position wirst Du einen QR-Code finden. Wenn du ihn gefunden hast, drücke den Knopf zum Starten des QR-Scanners und gib uns die Berechtigung, Deine Kamera zu öffnen. Nachdem Du den QR-Code gescannt hast, erhältst du ein Rätsel zur Position der nächsten Station. Die Lösung zu dem Rätsel ist also das Versteck des nächsten QR-Codes.",
|
||||||
|
// Following Params belong to QR-Code Scanner
|
||||||
|
askForPermission: true,
|
||||||
|
activateCamera: false,
|
||||||
|
isValid: false,
|
||||||
|
validating: false,
|
||||||
|
loading: false,
|
||||||
|
paused: false,
|
||||||
|
params: null,
|
||||||
|
noStreamApiSupport: false,
|
||||||
|
lon: "",
|
||||||
|
lat: "",
|
||||||
|
stationID: [],
|
||||||
|
iFrameURL: "",
|
||||||
|
zoom: 15,
|
||||||
|
center: [ 9.208858198755664, 49.14785422283188],
|
||||||
|
rotation: 0,
|
||||||
|
geolocPosition: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("/api/allCaches");
|
||||||
|
console.log(response.data);
|
||||||
|
const cache = response.data.find(cache => cache.id === Number(this.$route.params.cache));
|
||||||
|
this.cacheName = cache.name;
|
||||||
|
this.cacheID = this.$route.params.cache;
|
||||||
|
this.stationID = cache.stationen[0];
|
||||||
|
this.lon = this.stationID.longitude;
|
||||||
|
this.lat = this.stationID.lattitude;
|
||||||
|
this.iFrameURL = `https://seserver.se.hs-heilbronn.de:3000/api/map/lon=${this.lon}&lat=${this.lat}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
checkStation() {
|
||||||
|
let params = this.setParams();
|
||||||
|
console.log(params);
|
||||||
|
this.$axios.get('/api/checkStation', {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response);
|
||||||
|
console.log("Anfrage erfolgreich");
|
||||||
|
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
console.log("Anfrage fehlgeschlagen");
|
||||||
|
})
|
||||||
|
this.$router.push({path: `/station/${params.cacheID}/${params.stationID}`})
|
||||||
|
},
|
||||||
|
|
||||||
|
setParams() {
|
||||||
|
console.log("setParams: ");
|
||||||
|
let params = {};
|
||||||
|
params.cacheID = this.result.split('/')[0];
|
||||||
|
params.stationID = this.result.split('/')[1];
|
||||||
|
params.durchgefuehrterCacheID = this.cacheID;
|
||||||
|
console.log("CacheID: " + params.cacheID + " und StationID: " + params.stationID);
|
||||||
|
console.log(params.durchgefuehrterCacheID);
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateResult(event) {
|
||||||
|
console.log("updateResult()");
|
||||||
|
console.log(event);
|
||||||
|
this.result = event;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateCamera(event) {
|
||||||
|
console.log("updateCamera()");
|
||||||
|
this.cameraActive = event;
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
17
frontend/src/pages/Error404.vue
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<template>
|
||||||
|
<div class="fixed-center text-center">
|
||||||
|
<p>
|
||||||
|
<img src="~assets/sad.svg" style="width:30vw;max-width:150px;" />
|
||||||
|
</p>
|
||||||
|
<p class="text-faded">Sorry, nothing here...<strong>(404)</strong></p>
|
||||||
|
<q-btn color="secondary" style="width:200px;" @click="$router.push('/')"
|
||||||
|
>Zur Startseite</q-btn
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Error404"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
49
frontend/src/pages/Index.vue
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<q-card class="my-card q-mb-md" style="max-width: 600px">
|
||||||
|
<q-parallax
|
||||||
|
src="https://www.buga2019.de/we-bilder/2.Aktuelles/BUGA-Zwerg/zwerg-im-gras-buga-heilbronn-2019.jpg?m=1550501506"
|
||||||
|
:height="200"
|
||||||
|
/>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">GeoCaching</div>
|
||||||
|
<div class="text-body2">Willkommen</div>
|
||||||
|
<q-expansion-item
|
||||||
|
expand-separator
|
||||||
|
rounded-borders
|
||||||
|
class="bg-green-3 text-black shadow-2 full-width q-mt-md"
|
||||||
|
label="Was ist GeoCaching?"
|
||||||
|
>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
GeoCaching ist eine spielerische Führung, eine Schnitzeljagd, über die Bundesgartenschau.
|
||||||
|
Es gibt mehrere Routen (Caches), von denen man wählen kann. Auf jedem Cache gibt es mehrere Stationen, bei denen Rätsel und Aufgaben gelöst werden müssen, um die nächste zu finden.
|
||||||
|
Eine Station ist eine QR-Code, bei dessen einscannen das nächste Rätsel freigeschaltet wird.
|
||||||
|
Findet ein Cacher alle Stationen eines Caches erhält er eine kleine Belohnung und sammelt Punkte für eine Rangliste.
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
<q-card class="my-card" style="max-width: 600px">
|
||||||
|
<q-parallax
|
||||||
|
src="https://www.buga2019.de/we-bilder/1.Bundesgartenschau/171205_BUGA-2019_Sommerinsel_LOMA_430px.jpg?m=1550501561"
|
||||||
|
:height="200"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">Loslegen</div>
|
||||||
|
<q-btn color="green-3" text-color="black" class="full-width q-mt-md" label="Zu den Caches" to="/Overview"/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
149
frontend/src/pages/Login.vue
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<form class="login" @submit.prevent="login">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<p class="text-black text-h5"> Login </p>
|
||||||
|
<div class="column q-gutter-lg" style="">
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.email" type="text" label="Email"
|
||||||
|
autocomplete="username" :rules="[val=>validateEmail(val)||'Bitte gültige E-Mail eingeben!']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.password" type="password" label="Passwort"
|
||||||
|
autocomplete="current-password" :rules="[val=>val.length>=8||'Bitte gültiges Passwort eingeben!']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-btn
|
||||||
|
type="submit"
|
||||||
|
:outline="userAuthenticated"
|
||||||
|
:disabled="!loginValidation"
|
||||||
|
:loading="loading"
|
||||||
|
label="Login"
|
||||||
|
color="primary"
|
||||||
|
class="full-width"
|
||||||
|
unelevated
|
||||||
|
>
|
||||||
|
<template v-slot:loading>
|
||||||
|
<q-spinner-oval class="on-left"/>
|
||||||
|
wird eingeloggt...
|
||||||
|
</template>
|
||||||
|
</q-btn>
|
||||||
|
<div class="q-pt-md">
|
||||||
|
<q-btn
|
||||||
|
:disabled="userAuthenticated"
|
||||||
|
label="Registrieren"
|
||||||
|
color="primary"
|
||||||
|
class="full-width q-mt-md"
|
||||||
|
unelevated
|
||||||
|
to="/register"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
user: {
|
||||||
|
email: "",
|
||||||
|
password: "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.evalAuthentication();
|
||||||
|
console.log("created: initiated");
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
userAuthenticated() {
|
||||||
|
console.log("login: userAuthenticated()");
|
||||||
|
console.log(this.$store.state.auth.isAuthenticated);
|
||||||
|
return this.$store.state.auth.isAuthenticated;
|
||||||
|
},
|
||||||
|
loginValidation() {
|
||||||
|
if (this.validateEmail(this.user.email)
|
||||||
|
&& this.user.password.length>=8
|
||||||
|
&& !this.$store.state.auth.isAuthenticated) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
login: function () {
|
||||||
|
console.log("login called");
|
||||||
|
this.loading = true;
|
||||||
|
const data = {
|
||||||
|
email: this.user.email,
|
||||||
|
password: this.user.password
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("GET /api/login/ - json: " + JSON.stringify(data));
|
||||||
|
|
||||||
|
this.$axios.post(process.env.USER_API + '/account/login', data)
|
||||||
|
.then((response) => {
|
||||||
|
console.log("GET/POST /api/login/ - response: ");
|
||||||
|
console.log(response.data);
|
||||||
|
console.log("TOKEN");
|
||||||
|
console.log(response.data.token);
|
||||||
|
localStorage.setItem('userToken', JSON.stringify(response.data));
|
||||||
|
localStorage.setItem('userMail', JSON.stringify(data.email));
|
||||||
|
this.evalAuthentication();
|
||||||
|
this.$router.push({path: `/overview`})
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Fehler!";
|
||||||
|
msg = "E-Mail oder Passwort ist falsch. Bitte überprüfe deine Eingaben!";
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
this.evalAuthentication();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
evalAuthentication: function () {
|
||||||
|
this.$store.commit('auth/SET_AUTHENTICATED');
|
||||||
|
this.$store.commit('auth/SET_USER');
|
||||||
|
},
|
||||||
|
logout: function () {
|
||||||
|
console.log("logout()");
|
||||||
|
console.log(JSON.parse(localStorage.getItem('userToken')));
|
||||||
|
localStorage.removeItem('userToken');
|
||||||
|
this.evalAuthentication();
|
||||||
|
},
|
||||||
|
validateEmail(email) {
|
||||||
|
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(String(email).toLowerCase());
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
212
frontend/src/pages/MyCaches.vue
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="column no-wrap">
|
||||||
|
<div class="bg-red col col-shrink" style="">
|
||||||
|
<q-tabs
|
||||||
|
v-model="tab"
|
||||||
|
class="bg-grey-2"
|
||||||
|
inline-label
|
||||||
|
align="justify"
|
||||||
|
active-bg-color="bg-grey-1"
|
||||||
|
active-color="cyan-14"
|
||||||
|
indicator-color="cyan-14"
|
||||||
|
switch-indicator
|
||||||
|
>
|
||||||
|
<q-tab name="startedCaches" label="Angefangen" icon="playlist_play"/>
|
||||||
|
<q-tab name="finishedCaches" label="Beendet" icon="playlist_add_check"/>
|
||||||
|
</q-tabs>
|
||||||
|
<q-separator color="grey-4"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col flex column">
|
||||||
|
<q-tab-panels v-model="tab" animated swipeable class="col">
|
||||||
|
|
||||||
|
<q-tab-panel name="startedCaches" class=" fit">
|
||||||
|
<p class="text-grey-4 text-h5 text-center" v-if="startedCaches.filter(cache => cache.cacheAccesDefinition.id === 0).length === 0">keine Einträge</p>
|
||||||
|
<q-list>
|
||||||
|
<q-card
|
||||||
|
class="q-mb-md"
|
||||||
|
v-for="startedCache in startedCaches.filter(cache => cache.cacheAccesDefinition.id === 0)"
|
||||||
|
:key="startedCache.id"
|
||||||
|
>
|
||||||
|
<q-expansion-item
|
||||||
|
class=""
|
||||||
|
expand-separator
|
||||||
|
icon="location_on"
|
||||||
|
:label="startedCache.cache.name"
|
||||||
|
:caption="startedCache.cache.rankingPoints+' Punkte, '+startedCache.cache.stationen.length+' Stationen'"
|
||||||
|
>
|
||||||
|
<q-item class="q-pr-sm">
|
||||||
|
<q-item-section top avatar class="self-center"/>
|
||||||
|
<q-item-section class="">
|
||||||
|
<q-linear-progress rounded style="height: 15px" :value="startedCache.progress" color="primary" class=""/>
|
||||||
|
<q-item-label class="q-pt-xs" caption>
|
||||||
|
{{ Math.round(startedCache.progress * 100) }}% bereits geschafft
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
</q-item>
|
||||||
|
<q-item class="q-pr-sm reverse">
|
||||||
|
<q-btn @click="continueCache(startedCache.cache.id)" unelevated color="positive" stack
|
||||||
|
icon="arrow_forward"
|
||||||
|
label="Fortfahren" size="sm"/>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-card>
|
||||||
|
</q-list>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="finishedCaches" class="fit">
|
||||||
|
<p class="text-grey-4 text-h5 text-center" v-if="startedCaches.filter(cache => cache.cacheAccesDefinition.id === 1).length === 0">keine Einträge</p>
|
||||||
|
<q-list>
|
||||||
|
<q-card class="q-mb-md"
|
||||||
|
v-for="finishedCache in startedCaches.filter(cache => cache.cacheAccesDefinition.id === 1)"
|
||||||
|
:key="finishedCache.id">
|
||||||
|
<q-expansion-item
|
||||||
|
class=""
|
||||||
|
expand-separator
|
||||||
|
icon="location_on"
|
||||||
|
:label="finishedCache.cache.name"
|
||||||
|
:caption="finishedCache.cache.rankingPoints+' Punkte, '+finishedCache.cache.stationen.length+' Stationen'"
|
||||||
|
>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section top avatar class="self-center">
|
||||||
|
<!--<q-icon rounded color="cyan-14" name="location_on" size="3rem"/>-->
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label caption>{{'Erhaltene Punkte für diesen Cache: ' + finishedCache.cache.rankingPoints
|
||||||
|
}}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section side top class="self-center">
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="q-pr-sm reverse q-gutter-x-sm">
|
||||||
|
<q-btn @click="goToReward(finishedCache.cache.id)" unelevated color="primary" stack
|
||||||
|
icon="arrow_forward"
|
||||||
|
label="Belohnung ansehen" size="sm"/>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-card>
|
||||||
|
</q-list>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
</q-tab-panels>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
//import {dom} from 'quasar'
|
||||||
|
//const {height, width} = dom
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 'startedCaches',
|
||||||
|
startedCaches: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasAdminState() {
|
||||||
|
return this.$store.getters['auth/GET_ADMINSTATE'];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
// this.$store.commit('auth/SET_AUTHENTICATED');
|
||||||
|
this.fetchUserCaches();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
calculateProgress() {
|
||||||
|
console.log("calcProgress...")
|
||||||
|
console.log("this.startedCaches: ")
|
||||||
|
console.log(this.startedCaches)
|
||||||
|
this.startedCaches.forEach(startedCache => {
|
||||||
|
console.log(startedCache)
|
||||||
|
let stationCount = startedCache.cache.stationen.length;
|
||||||
|
let stationPos = 1 + startedCache.cache.stationen.findIndex(station => station.id === startedCache.aktuelleStation.id);
|
||||||
|
startedCache.progress = stationPos / stationCount;
|
||||||
|
console.log("progress: "+startedCache.progress)
|
||||||
|
});
|
||||||
|
console.log("calcProgress finished.");
|
||||||
|
console.log(this.startedCaches)
|
||||||
|
},
|
||||||
|
fetchUserCaches() {
|
||||||
|
console.log("fetchCaches...")
|
||||||
|
let token;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$axios.get('/api/getMyCaches', {params: {token}})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data)
|
||||||
|
this.startedCaches = response.data;
|
||||||
|
this.calculateProgress();
|
||||||
|
}).catch((error) => {
|
||||||
|
// Error
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
// The request was made and the server responded with a status code
|
||||||
|
title = "Problem with response!";
|
||||||
|
msg = error.response;
|
||||||
|
} else if (error.request) {
|
||||||
|
// The request was made but no response was received
|
||||||
|
title = "Problem with request!";
|
||||||
|
msg = "Error occured due to wrong server request!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
// Something happened in setting up the request that triggered an Error
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
}).finally(() => {
|
||||||
|
console.log("fetchCaches... finally")
|
||||||
|
});
|
||||||
|
},
|
||||||
|
continueCache(cacheID) {
|
||||||
|
console.log("cacheID");
|
||||||
|
console.log(cacheID);
|
||||||
|
let currentStationID;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
let params = {cacheID: cacheID};
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
this.$axios.get('/api/getCurrentStation', {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
currentStationID = response.data.aktuelleStation.id;
|
||||||
|
this.$router.push({path: `/station/${cacheID}/${currentStationID}`});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToReward(cacheID) {
|
||||||
|
console.log("cacheID");
|
||||||
|
console.log(cacheID);
|
||||||
|
let currentStationID;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
let params = {cacheID: cacheID};
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
this.$axios.get('/api/getCurrentStation', {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
currentStationID = response.data.aktuelleStation.id;
|
||||||
|
this.$router.push({path: `/CacheEnd/${params.cacheID}`});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
403
frontend/src/pages/Overview.vue
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="column">
|
||||||
|
<div class="bg-red col col-shrink" style="">
|
||||||
|
<q-tabs
|
||||||
|
v-model="tab"
|
||||||
|
class="bg-grey-2"
|
||||||
|
inline-label
|
||||||
|
align="justify"
|
||||||
|
active-bg-color="bg-grey-1"
|
||||||
|
active-color="cyan-14"
|
||||||
|
indicator-color="cyan-14"
|
||||||
|
switch-indicator
|
||||||
|
>
|
||||||
|
<q-tab name="list" label="Liste" icon="list"/>
|
||||||
|
<q-tab name="map" label="Karte" icon="map"/>
|
||||||
|
</q-tabs>
|
||||||
|
<q-separator color="grey-4"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col flex column">
|
||||||
|
<q-tab-panels v-model="tab" animated swipeable class="col">
|
||||||
|
|
||||||
|
<q-tab-panel name="list" class=" fit">
|
||||||
|
<q-spinner-oval
|
||||||
|
v-if="!render"
|
||||||
|
class="absolute-center"
|
||||||
|
color="primary"
|
||||||
|
size="10em"
|
||||||
|
/>
|
||||||
|
<p class="text-grey-4 text-h5 text-center" v-if="caches.length === 0">keine Einträge</p>
|
||||||
|
<q-list v-if="render">
|
||||||
|
<q-card class="q-mb-md" v-for="cache in caches" :key="cache.id">
|
||||||
|
<q-expansion-item
|
||||||
|
class=""
|
||||||
|
expand-separator
|
||||||
|
icon="location_on"
|
||||||
|
:header-class="'text-'+(getColorClass(cache) === 'primary' ? 'black' : getColorClass(cache))"
|
||||||
|
:label="cache.name"
|
||||||
|
:caption="cache.rankingPoints+' Punkte, '+cache.stationen.length+' Stationen'"
|
||||||
|
>
|
||||||
|
<q-item>
|
||||||
|
<q-item-section top avatar class="self-center">
|
||||||
|
<!--<q-icon rounded color="cyan-14" name="location_on" size="3rem"/>-->
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label caption v-html="cache.description"></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
|
||||||
|
<q-item-section side top class="self-center">
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item class="q-pr-sm reverse q-gutter-x-sm">
|
||||||
|
<q-btn v-if="(getCacheAccesDefinition(cache) === -1)" @click="startCache(cache.id)" unelevated
|
||||||
|
:color="getColorClass(cache)" stack icon="arrow_forward"
|
||||||
|
label="Starten" size="xs"/>
|
||||||
|
<q-btn v-if="(getCacheAccesDefinition(cache) === 0)" @click="continueCache(cache.id)" unelevated
|
||||||
|
:color="getColorClass(cache)" stack icon="arrow_forward"
|
||||||
|
label="Fortsetzen" size="xs"/>
|
||||||
|
<q-btn v-if="(getCacheAccesDefinition(cache) === 1)" @click="goToReward(cache.id)" unelevated
|
||||||
|
:color="getColorClass(cache)" stack icon="arrow_forward"
|
||||||
|
label="Belohnung" size="xs"/>
|
||||||
|
<q-btn v-if="hasAdminState" @click="confirmDelete(cache.id)" unelevated color="negative" stack
|
||||||
|
icon="delete" size="xs"/>
|
||||||
|
<q-btn v-if="hasAdminState" @click="editCache(cache.id)" unelevated color="primary" stack
|
||||||
|
icon="edit" size="xs"/>
|
||||||
|
<q-btn v-if="hasAdminState" @click="generateQrCodes(cache.id)" unelevated color="primary" stack
|
||||||
|
icon="image" label="QRCodes" size="xs"/>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-card>
|
||||||
|
<div v-if="hasAdminState" class="row">
|
||||||
|
<q-btn @click="addCache" unelevated color="primary" stack icon="add" label="Neuer Cache"
|
||||||
|
class="full-width"/>
|
||||||
|
</div>
|
||||||
|
</q-list>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
|
||||||
|
<q-tab-panel name="map" class="q-pa-none fit">
|
||||||
|
<!--<div class="full-width full-height absolute-full" style="background: url('statics/osm_mock.png'); background-size: cover">-->
|
||||||
|
<!--</div>-->
|
||||||
|
<div class="full-width full-height absolute-full">
|
||||||
|
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
|
||||||
|
data-projection="EPSG:4326">
|
||||||
|
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
|
||||||
|
|
||||||
|
<vl-geoloc @update:position="geolocPosition = $event">
|
||||||
|
<template slot-scope="geoloc">
|
||||||
|
<vl-feature v-if="geoloc.position" id="position-feature">
|
||||||
|
<vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
|
||||||
|
</vl-feature>
|
||||||
|
</template>
|
||||||
|
</vl-geoloc>
|
||||||
|
|
||||||
|
<vl-layer-tile id="osm">
|
||||||
|
<vl-source-osm></vl-source-osm>
|
||||||
|
</vl-layer-tile>
|
||||||
|
|
||||||
|
<vl-layer-vector>
|
||||||
|
<vl-source-vector :features.sync="features"></vl-source-vector>
|
||||||
|
<vl-style-box>
|
||||||
|
<vl-style-stroke color="green" :width="3"></vl-style-stroke>
|
||||||
|
<vl-style-fill color="rgba(255,255,255,0.5)"></vl-style-fill>
|
||||||
|
</vl-style-box>
|
||||||
|
</vl-layer-vector>
|
||||||
|
</vl-map>
|
||||||
|
</div>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
</q-tab-panels>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/*
|
||||||
|
.my-list-card-item
|
||||||
|
padding-left: 8px
|
||||||
|
*/
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueLayers from 'vuelayers'
|
||||||
|
import 'vuelayers/lib/style.css' // needs css-loader
|
||||||
|
|
||||||
|
Vue.use(VueLayers);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 'list',
|
||||||
|
caches: [],
|
||||||
|
stations: [],
|
||||||
|
pois: [],
|
||||||
|
features: [],
|
||||||
|
render: false,
|
||||||
|
zoom: 15,
|
||||||
|
center: [ 9.208858198755664, 49.14785422283188],
|
||||||
|
rotation: 0,
|
||||||
|
geolocPosition: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.initMap().then(features => {
|
||||||
|
this.features = features.map(Object.freeze)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasAdminState() {
|
||||||
|
return this.$store.getters['auth/GET_ADMINSTATE'];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
console.log("created(): " + this.caches);
|
||||||
|
this.loadData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadData() {
|
||||||
|
this.render = false;
|
||||||
|
await this.fetchAllCaches();
|
||||||
|
this.render = await this.fetchMyCaches();
|
||||||
|
},
|
||||||
|
getColorClass(cache) {
|
||||||
|
switch (this.getCacheAccesDefinition(cache)) {
|
||||||
|
case -1:
|
||||||
|
return "primary"
|
||||||
|
case 0:
|
||||||
|
return "amber"
|
||||||
|
case 1:
|
||||||
|
return "green"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getCacheAccesDefinition(cache) {
|
||||||
|
// console.log("getCacheAccessDefinition")
|
||||||
|
// console.log(cache.hasOwnProperty('cacheAccesDefinition') ? cache.cacheAccesDefinition : -1)
|
||||||
|
// can be -1 = no accessdefinition, 0 = started or 1 = finished
|
||||||
|
return cache.hasOwnProperty('cacheAccesDefinition') ? cache.cacheAccesDefinition : -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchAllCaches() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
this.caches = response.data;
|
||||||
|
//this.fetchMyCaches();
|
||||||
|
for (let cache in this.caches) {
|
||||||
|
|
||||||
|
this.stations[cache] = (this.caches[cache].stationen[0]);
|
||||||
|
const poiItem = {
|
||||||
|
Name: this.caches[cache].name,
|
||||||
|
CategoryID: 3,
|
||||||
|
Latitude: this.stations[cache].lattitude,
|
||||||
|
Longitude: this.stations[cache].longitude
|
||||||
|
};
|
||||||
|
this.pois.push(poiItem);
|
||||||
|
}
|
||||||
|
this.initMap();
|
||||||
|
resolve(true);
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Bitte Eingaben überprüfen!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchMyCaches() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
console.log("fetchMyCaches...");
|
||||||
|
if (this.$store.state.auth.isAuthenticated && localStorage.getItem('userToken')) {
|
||||||
|
const token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
this.$axios.get('/api/getMyCaches', {params: {token}})
|
||||||
|
.then((response) => {
|
||||||
|
console.log("process data...")
|
||||||
|
console.log(response.data)
|
||||||
|
for (let item of response.data) {
|
||||||
|
this.caches.forEach(cache => {
|
||||||
|
if (cache.id === item.cache.id) {
|
||||||
|
cache.cacheAccesDefinition = item.cacheAccesDefinition.id;
|
||||||
|
console.log("found accessdefinition: id: " + cache.id + " ad: " + cache.cacheAccesDefinition);
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
console.log(this.caches);
|
||||||
|
console.log("data processed.")
|
||||||
|
}).finally(() => resolve(true))
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmDelete(cacheID) {
|
||||||
|
this.$q.dialog({
|
||||||
|
title: 'Löschen...',
|
||||||
|
message: 'Willst du diesen Cache wirklich löschen?',
|
||||||
|
persistent: true,
|
||||||
|
cancel: true,
|
||||||
|
}).onOk(() => {
|
||||||
|
console.log('>>>> OK');
|
||||||
|
this.deleteCache(cacheID)
|
||||||
|
}).onCancel(() => {
|
||||||
|
console.log('>>>> Cancel')
|
||||||
|
}).onDismiss(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
addCache() {
|
||||||
|
this.$store.commit('cacheCollector/RESET_NEW_CACHE');
|
||||||
|
this.$router.push({path: `/cache`})
|
||||||
|
},
|
||||||
|
editCache(cacheID) {
|
||||||
|
this.$router.push({path: `/cache/${cacheID}`})
|
||||||
|
},
|
||||||
|
initMap() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => {
|
||||||
|
// generate GeoJSON random features
|
||||||
|
resolve([
|
||||||
|
|
||||||
|
{
|
||||||
|
type: "Feature",
|
||||||
|
id: 'fakerator.misc.uuid()',
|
||||||
|
geometry: {
|
||||||
|
type: "LineString",
|
||||||
|
coordinates: [
|
||||||
|
[
|
||||||
|
44.47265625,
|
||||||
|
-1.7575368113083125,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44.47265625,
|
||||||
|
-1.7575368113083125,
|
||||||
|
],
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteCache(id) {
|
||||||
|
console.log('delete cache: ' + id);
|
||||||
|
this.$axios.delete('/api/deleteCache', {params: {cacheID: id}})
|
||||||
|
.then((response) => {
|
||||||
|
this.loadData();
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Fehler!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
startCache(cacheID) {
|
||||||
|
console.log("startCache()");
|
||||||
|
this.$store.state.currentCache.cache = this.caches.find(cache => cache.id === Number(cacheID));
|
||||||
|
console.log(this.$store.state.currentCache.cache);
|
||||||
|
this.$store.state.currentCache.currentCacheID = Number(cacheID);
|
||||||
|
this.$router.push({path: `/CacheStart/${cacheID}`})
|
||||||
|
},
|
||||||
|
continueCache(cacheID) {
|
||||||
|
console.log("cacheID");
|
||||||
|
console.log(cacheID);
|
||||||
|
let currentStationID;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
let params = {cacheID: cacheID};
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
this.$axios.get('/api/getCurrentStation', {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
currentStationID = response.data.aktuelleStation.id;
|
||||||
|
this.$router.push({path: `/station/${cacheID}/${currentStationID}`});
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Fehler!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
goToReward(cacheID) {
|
||||||
|
console.log("cacheID");
|
||||||
|
console.log(cacheID);
|
||||||
|
let currentStationID;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
let params = {cacheID: cacheID};
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
this.$axios.get('/api/getCurrentStation', {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
currentStationID = response.data.aktuelleStation.id;
|
||||||
|
this.$router.push({path: `/CacheEnd/${params.cacheID}`});
|
||||||
|
}).catch((error) => {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Fehler!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
generateQrCodes(cacheID) {
|
||||||
|
this.$router.push({path: `/generator/${cacheID}`})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
612
frontend/src/pages/Profile.vue
Normal file
@ -0,0 +1,612 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="column no-wrap">
|
||||||
|
<div class="bg-red col col-shrink" style="">
|
||||||
|
<q-tabs
|
||||||
|
v-model="tab"
|
||||||
|
class="bg-grey-2"
|
||||||
|
inline-label
|
||||||
|
align="justify"
|
||||||
|
active-bg-color="bg-grey-1"
|
||||||
|
active-color="cyan-14"
|
||||||
|
indicator-color="cyan-14"
|
||||||
|
switch-indicator
|
||||||
|
>
|
||||||
|
<q-tab name="profile" label="Profil" icon="perm_identity"/>
|
||||||
|
<q-tab v-if="boolAlreadyInTeam" name="teams" label="Team" icon="group"/>
|
||||||
|
</q-tabs>
|
||||||
|
<q-separator color="grey-4"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col flex column">
|
||||||
|
<q-tab-panels v-model="tab" animated swipeable class="col">
|
||||||
|
|
||||||
|
<q-tab-panel name="profile" class="fit q-pa-none">
|
||||||
|
<div class="bg-primary q-pa-md">
|
||||||
|
<p class="text-h3 text-white"> {{userName}} </p>
|
||||||
|
<p class="text-white"> Meine Email: {{email}} </p>
|
||||||
|
<p class="text-white"> Mein Rang: {{userRanking}} </p>
|
||||||
|
<p class="text-white"> Mein Team: {{teamName}} </p>
|
||||||
|
</div>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<div v-show="!boolAlreadyInTeam">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="newTeamName"
|
||||||
|
type="text"
|
||||||
|
label="Teamname eingeben"
|
||||||
|
:rules="[val=>val.length>=2||'Name muss mindestens 5 Zeichen lang sein!']"/>
|
||||||
|
<q-btn
|
||||||
|
:disabled="!newTeamNameValidationSuccesful"
|
||||||
|
label="Team anlegen"
|
||||||
|
color="primary"
|
||||||
|
class="full-width"
|
||||||
|
@click="createTeam()"
|
||||||
|
unelevated
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="text-h5 q-mt-sm"> Teameinladungen </p>
|
||||||
|
<p class="text-grey-4 text-h5 text-center q-mt-xl" v-if="teamInvites.length === 0">keine Einträge</p>
|
||||||
|
<q-list>
|
||||||
|
<q-card class="q-mb-md" v-for="teamInvite in teamInvites" :key="teamInvite.id">
|
||||||
|
<q-expansion-item
|
||||||
|
class=""
|
||||||
|
expand-icon-toggle
|
||||||
|
expand-separator
|
||||||
|
icon="group"
|
||||||
|
:label="teamInvite.team.name"
|
||||||
|
:caption="teamInvite.team.teamStatus"
|
||||||
|
>
|
||||||
|
<q-item class="q-pr-sm reverse q-gutter-x-sm">
|
||||||
|
<q-btn :disabled="boolAlreadyInTeam" @click="joinTeam(teamInvite)" unelevated color="positive" stack
|
||||||
|
icon="arrow_forward"
|
||||||
|
label="Annehmen" size="sm"/>
|
||||||
|
<q-btn @click="deleteTeamInvite(teamInvite)" unelevated color="negative" stack icon="delete"
|
||||||
|
label="Ablehnen" size="sm"/>
|
||||||
|
</q-item>
|
||||||
|
</q-expansion-item>
|
||||||
|
</q-card>
|
||||||
|
</q-list>
|
||||||
|
<!--<div v-show="hasAdminState">-->
|
||||||
|
<!--<p class="text-h5 q-mt-sm"> Neuen Admin hinzufügen </p>-->
|
||||||
|
<!--<q-input lazy-rules outlined filled stack-label v-model="newAdminMail"-->
|
||||||
|
<!--type="text" label="Email des Nutzers" class="col-9"-->
|
||||||
|
<!--:rules="[val=>validateEmail(val)||'Bitte Email verwenden']"/>-->
|
||||||
|
<!--<q-btn class="col-3"-->
|
||||||
|
<!--icon="arrow_right_alt"-->
|
||||||
|
<!--@click="getUser()"-->
|
||||||
|
<!--:disabled="!adminInviteChecked"-->
|
||||||
|
<!--color="positive"-->
|
||||||
|
<!--type="submit"/>-->
|
||||||
|
<!--</div>-->
|
||||||
|
</div>
|
||||||
|
</q-tab-panel>
|
||||||
|
<q-tab-panel v-if="boolAlreadyInTeam" name="teams" class="q-pa-none">
|
||||||
|
<div class="bg-primary q-pa-md" v-show="boolAlreadyInTeam">
|
||||||
|
<p class="text-h3 text-white"> {{teamName}} </p>
|
||||||
|
<p class="text-white"> Unser Rang: {{teamRanking}} </p>
|
||||||
|
<p v-show="!boolStatus" class="text-white">
|
||||||
|
Unser Status: {{currentTeamStatus}}
|
||||||
|
<q-btn icon="create"
|
||||||
|
unelevated
|
||||||
|
class="on-right"
|
||||||
|
@click="updateStatus()"
|
||||||
|
color="amber"/>
|
||||||
|
</p>
|
||||||
|
<q-input v-show="boolStatus" lazy-rules outlined filled stack-label v-model="teamStatus"
|
||||||
|
type="text" label="Neuer Teamstatus" bg-color="white"
|
||||||
|
:rules="[val=>val.length<=160||'Status zu lang!']"/>
|
||||||
|
<div v-show="boolStatus" align="center">
|
||||||
|
<q-btn icon="done"
|
||||||
|
unelevated
|
||||||
|
class="on-left"
|
||||||
|
:disabled="!teamStatusChecked"
|
||||||
|
@click="setTeamStatus()"
|
||||||
|
color="positive"
|
||||||
|
type="submit"/>
|
||||||
|
<q-btn icon="clear"
|
||||||
|
unelevated
|
||||||
|
class="on-right"
|
||||||
|
@click="updateStatus()"
|
||||||
|
color="negative"
|
||||||
|
type="submit"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white q-pa-md" v-show="boolAlreadyInTeam">
|
||||||
|
<p class="text-h5"> Teammitglieder </p>
|
||||||
|
<q-table
|
||||||
|
v-if="boolTable"
|
||||||
|
name="teamDataTable"
|
||||||
|
:data="teamData"
|
||||||
|
:columns="teamColumns"
|
||||||
|
row-key="name"
|
||||||
|
hide-bottom
|
||||||
|
:pagination.sync="pagination"
|
||||||
|
/>
|
||||||
|
<br/>
|
||||||
|
<div align="center">
|
||||||
|
<q-btn label="Nutzer einladen"
|
||||||
|
unelevated
|
||||||
|
@click="activateInvite()"
|
||||||
|
color="primary"
|
||||||
|
class="full-width q-mt-sm"
|
||||||
|
type="submit"/>
|
||||||
|
</div>
|
||||||
|
<div v-if="inviteActivated" align="center" class="row q-mt-sm justify-between">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="inviteMail"
|
||||||
|
type="text" label="Email des Nutzers" class="col q-mr-sm"
|
||||||
|
:rules="[val=>validateEmail(val)||'Bitte Email verwenden']"/>
|
||||||
|
<div class="row" style="padding-bottom: 20px">
|
||||||
|
<q-btn label="Einladen"
|
||||||
|
unelevated
|
||||||
|
@click="sendTeamInvite()"
|
||||||
|
:disabled="!teamInviteChecked"
|
||||||
|
color="positive"
|
||||||
|
type="submit"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div align="center" class="q-mt-lg">
|
||||||
|
<q-btn label="Team verlassen"
|
||||||
|
unelevated
|
||||||
|
@click="confirmLeave()"
|
||||||
|
color="negative"
|
||||||
|
class="full-width"
|
||||||
|
type="submit"/>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
</q-tab-panels>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/*.my-custom th {*/
|
||||||
|
/* color: #027BE3 !important;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.my-custom tr {*/
|
||||||
|
/* color: #027BE3 !important;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.my-custom td {*/
|
||||||
|
/* color: #027BE3 !important;*/
|
||||||
|
/*}*/
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
//import {dom} from 'quasar'
|
||||||
|
//const {height, width} = dom
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 'profile',
|
||||||
|
inviteMail: "",
|
||||||
|
currentTeamStatus: "",
|
||||||
|
teamStatus: "",
|
||||||
|
userName: "",
|
||||||
|
email: null,
|
||||||
|
userRanking: null,
|
||||||
|
teamName: null,
|
||||||
|
boolAlreadyInTeam: false,
|
||||||
|
teamRanking: null,
|
||||||
|
newTeamName: "",
|
||||||
|
leaveTeamCheck: "",
|
||||||
|
teamMembers: [],
|
||||||
|
teamInvites: [],
|
||||||
|
inviteActivated: false,
|
||||||
|
boolStatus: false,
|
||||||
|
boolTable: true,
|
||||||
|
boolMemberTableUpdating: false,
|
||||||
|
teamColumns: [
|
||||||
|
{
|
||||||
|
name: 'desc',
|
||||||
|
label: 'Name',
|
||||||
|
required: true,
|
||||||
|
align: 'left',
|
||||||
|
field: row => row.name,
|
||||||
|
format: val => `${val}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ranking',
|
||||||
|
label: 'Rang',
|
||||||
|
required: true,
|
||||||
|
align: 'left',
|
||||||
|
field: 'ranking',
|
||||||
|
sortable: true,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
teamData: [],
|
||||||
|
pagination: {
|
||||||
|
sortBy: 'ranking',
|
||||||
|
descending: false,
|
||||||
|
page: 1,
|
||||||
|
rowsPerPage: 10
|
||||||
|
},
|
||||||
|
// newAdminMail: "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasAdminState() {
|
||||||
|
return this.$store.getters['auth/GET_ADMINSTATE'];
|
||||||
|
},
|
||||||
|
newTeamNameValidationSuccesful() {
|
||||||
|
if (this.newTeamName.length >= 5) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
teamInviteChecked() {
|
||||||
|
if (this.validateEmail(this.inviteMail)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
teamStatusChecked() {
|
||||||
|
if (this.teamStatus.length <= 160) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
leaveConfirmed() {
|
||||||
|
if (this.leaveTeamCheck === 'BESTÄTIGEN') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
adminInviteChecked() {
|
||||||
|
if (this.validateEmail(this.newAdminMail)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
// this.$store.commit('auth/SET_AUTHENTICATED');
|
||||||
|
// this.$store.commit('auth/SET_USER');
|
||||||
|
this.userName = JSON.parse(localStorage.getItem('userToken')).name;
|
||||||
|
this.email = JSON.parse(localStorage.getItem('userMail'));
|
||||||
|
this.getPersonalRanking();
|
||||||
|
this.updateTeamData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmLeave() {
|
||||||
|
this.$q.dialog({
|
||||||
|
title: 'Verlassen...',
|
||||||
|
message: 'Willst du dein aktuelles Team wirklich verlassen?',
|
||||||
|
persistent: true,
|
||||||
|
cancel: true,
|
||||||
|
}).onOk(() => {
|
||||||
|
console.log('>>>> OK');
|
||||||
|
this.leaveTeam();
|
||||||
|
}).onCancel(() => {
|
||||||
|
console.log('>>>> Cancel')
|
||||||
|
}).onDismiss(() => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
activateInvite() {
|
||||||
|
if (this.inviteActivated) {
|
||||||
|
this.inviteActivated = false;
|
||||||
|
} else {
|
||||||
|
this.inviteActivated = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateStatus() {
|
||||||
|
if (this.boolStatus) {
|
||||||
|
this.boolStatus = false;
|
||||||
|
} else {
|
||||||
|
this.boolStatus = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateTable() {
|
||||||
|
if (this.boolTable) {
|
||||||
|
this.boolTable = false;
|
||||||
|
} else {
|
||||||
|
this.boolTable = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
validateEmail(email) {
|
||||||
|
let re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(String(email).toLowerCase());
|
||||||
|
},
|
||||||
|
getPersonalRanking() {
|
||||||
|
let email = this.email;
|
||||||
|
this.$axios.get('/api/getRankingPlace', {params: {email}})
|
||||||
|
.then((response) => {
|
||||||
|
this.userRanking = response.data;
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async updateTeamData() {
|
||||||
|
await this.getTeamData();
|
||||||
|
this.boolTable = false;
|
||||||
|
this.boolTable = await this.fetchTeamMembers();
|
||||||
|
await this.fetchTeamInvites();
|
||||||
|
},
|
||||||
|
getTeamData() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let token;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$axios.get('/api/getTeamOfUser', {params: {token}})
|
||||||
|
.then(response => {
|
||||||
|
if (response.data === '') {
|
||||||
|
this.boolAlreadyInTeam = false;
|
||||||
|
this.teamName = "Aktuell in keinem Team";
|
||||||
|
this.currentTeamStatus = "";
|
||||||
|
this.teamRanking = "-";
|
||||||
|
} else {
|
||||||
|
this.boolAlreadyInTeam = true;
|
||||||
|
this.teamName = response.data.name;
|
||||||
|
this.currentTeamStatus = response.data.teamStatus;
|
||||||
|
this.teamRanking = "-";
|
||||||
|
}
|
||||||
|
console.log("getTeam: " + response);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
}).finally(() => resolve(true));
|
||||||
|
})
|
||||||
|
},
|
||||||
|
createTeam() {
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.name = this.newTeamName;
|
||||||
|
this.$axios.post('/api/createTeam', null, {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log("createTeam: " + response);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Das Team " + this.newTeamName + " wurde erfolgreich erstellt!",
|
||||||
|
title: "Teamerstellung",
|
||||||
|
color: "blue"
|
||||||
|
});
|
||||||
|
this.updateTeamData();
|
||||||
|
this.tab = 'teams';
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
leaveTeam() {
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$axios.put('/api/leaveTeam', null, {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log("createTeam: " + response);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Du hast das Team " + this.teamName + " verlassen!",
|
||||||
|
title: "Team verlassen",
|
||||||
|
color: "blue"
|
||||||
|
});
|
||||||
|
this.updateTeamData();
|
||||||
|
this.tab = 'profile';
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchTeamMembers() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.teamData = [];
|
||||||
|
let name = this.teamName;
|
||||||
|
this.$axios.get('/api/getTeamMembers', {params: {name}})
|
||||||
|
.then(async (response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
this.teamMembers = response.data;
|
||||||
|
for (var i = 0; i < this.teamMembers.length; i++) {
|
||||||
|
let member = {};
|
||||||
|
member.name = this.teamMembers[i].username;
|
||||||
|
member.email = this.teamMembers[i].email;
|
||||||
|
await this.getMemberRanking(member, member.email);
|
||||||
|
this.teamData.push(member);
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
}).finally(() => resolve(true))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getMemberRanking(member, email) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this.updateTable();
|
||||||
|
let ranking;
|
||||||
|
this.$axios.get('/api/getRankingPlace', {params: {email}})
|
||||||
|
.then((response) => {
|
||||||
|
ranking = response.data;
|
||||||
|
member.ranking = ranking;
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
}).finally(() => resolve(true))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchTeamInvites() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let token;
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$axios.get('/api/getMyTeamInvites', {params: {token}})
|
||||||
|
.then((response) => {
|
||||||
|
this.teamInvites = response.data;
|
||||||
|
console.log(this.teamInvites);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
}).finally(() => resolve(true))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
joinTeam(teamInvite) {
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.teamID = teamInvite.team.id;
|
||||||
|
this.$axios.put('/api/joinTeam', null, {params})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Du bist dem Team " + teamInvite.team.name + " erfolgreich beigetreten!",
|
||||||
|
title: "Teambeitrittsanfrage",
|
||||||
|
color: "blue"
|
||||||
|
});
|
||||||
|
this.updateTeamData();
|
||||||
|
this.tab = 'teams';
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteTeamInvite(teamInvite) {
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.teamInviteID = teamInvite.id;
|
||||||
|
this.$axios.delete('/api/deleteTeamInvite', {params})
|
||||||
|
.then((response) => {
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Du hast die Anfrage von Team " + teamInvite.team.name + " gelöscht!",
|
||||||
|
title: "Teambeitrittsanfrage",
|
||||||
|
color: "blue"
|
||||||
|
});
|
||||||
|
this.updateTeamData();
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
sendTeamInvite() {
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.invitedUserEmail = this.inviteMail;
|
||||||
|
this.$axios.post('/api/sendTeamInvite', null, {params})
|
||||||
|
.then((response) => {
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Du hast " + params.invitedUserEmail + " eingeladen!",
|
||||||
|
title: "Einladung ins Team",
|
||||||
|
color: "blue"
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setTeamStatus() {
|
||||||
|
let params = {};
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
params.token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.teamStatus = this.teamStatus;
|
||||||
|
this.$axios.put('/api/setTeamStatus', null, {params})
|
||||||
|
.then((response) => {
|
||||||
|
this.updateTeamData();
|
||||||
|
}).catch((error) => {
|
||||||
|
this.handleError(error);
|
||||||
|
});
|
||||||
|
this.updateStatus();
|
||||||
|
},
|
||||||
|
// getUser() {
|
||||||
|
// const data = {
|
||||||
|
// "email" : this.newAdminMail
|
||||||
|
// }
|
||||||
|
// JSON.stringify(data);
|
||||||
|
// const token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
// this.$axios.get(process.env.USER_API + '/account/email', data, {
|
||||||
|
// headers: {
|
||||||
|
// 'Authorization': 'Bearer ' + token,
|
||||||
|
// }
|
||||||
|
// }).then((response) => {
|
||||||
|
// let newAdminAccount = response.data;
|
||||||
|
// if (response.status === 200) {
|
||||||
|
// this.addAdmin(JSON.parse(response).data);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((error) => {
|
||||||
|
// let message;
|
||||||
|
// let header = "Unbekannter Fehler...";
|
||||||
|
// if (error.response) {
|
||||||
|
// console.log(error.response)
|
||||||
|
// if (error.response.status === 400) {
|
||||||
|
// message = "Es gibt keinen Nutzer mit dieser Email!";
|
||||||
|
// header = "Email überprüfen!";
|
||||||
|
// }
|
||||||
|
// } else if (error.request) {
|
||||||
|
// console.log(error.request);
|
||||||
|
// header = "Anfrage fehlgeschlagen!";
|
||||||
|
// message = "Die Verbindung zum Server ist gestört. Versuchen Sie es später noch einmal.";
|
||||||
|
// }
|
||||||
|
// this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: message, title: header});
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
// addAdmin(newAdmin) {
|
||||||
|
// const token = JSON.parse(localStorage.getItem('userToken')).token;
|
||||||
|
// newAdmin.roles.role = "ADMIN";
|
||||||
|
// JSON.stringify(newAdmin);
|
||||||
|
// this.$axios.patch(process.env.USER_API, '/account', newAdmin, {
|
||||||
|
// headers: {
|
||||||
|
// 'Authorization': 'Bearer ' + token,
|
||||||
|
// }
|
||||||
|
// }).then((response) => {
|
||||||
|
// if (response.status === 200) {
|
||||||
|
// this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
// message: "Du hast den Nutzer mit der Email " + this.newAdminMail + " als Admin freigeschalten!",
|
||||||
|
// title: "Nutzerverwaltung",
|
||||||
|
// color: "blue"
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .catch((error) => {
|
||||||
|
// let message;
|
||||||
|
// let header = "Unbekannter Fehler...";
|
||||||
|
// if (error.response) {
|
||||||
|
// console.log(error.response)
|
||||||
|
// if (error.response.status === 400) {
|
||||||
|
// message = JSON.parse(error).error;
|
||||||
|
// header = "Probleme mit dem zu bearbeitenden Nutzeraccount!";
|
||||||
|
// }
|
||||||
|
// } else if (error.request) {
|
||||||
|
// console.log(error.request);
|
||||||
|
// header = "Anfrage fehlgeschlagen!";
|
||||||
|
// message = "Die Verbindung zum Server ist gestört. Versuchen Sie es später noch einmal.";
|
||||||
|
// }
|
||||||
|
// this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: message, title: header});
|
||||||
|
// });
|
||||||
|
// },
|
||||||
|
handleError(error) {
|
||||||
|
let msg;
|
||||||
|
let title;
|
||||||
|
if (error.response) {
|
||||||
|
title = "Fehler!";
|
||||||
|
msg = error.response.data;
|
||||||
|
} else if (error.request) {
|
||||||
|
title = "Verbindungsfehler!";
|
||||||
|
msg = "Es konnte keine Verbindung zum Server aufgebaut werden. Versuchen Sie es später noch einmal!"
|
||||||
|
console.log(error.request);
|
||||||
|
} else {
|
||||||
|
title = "Error";
|
||||||
|
msg = error.message;
|
||||||
|
console.log('Error', error.message);
|
||||||
|
}
|
||||||
|
console.log(error.config);
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: msg, title: title,});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
217
frontend/src/pages/Register.vue
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<form class="register" autocomplete="off">
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<p class="text-h5 text-black"> Registrierung </p>
|
||||||
|
<div class="column q-gutter-lg" style="">
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.name" type="text" label="Vor- und Nachname"
|
||||||
|
:rules="[val=>validateUsername(val)||'Name muss mindestens 2 Zeichen lang sein und darf nur aus Buchstaben, Zahlen und Unterstrichen bestehen!']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.email" type="text" label="E-Mail"
|
||||||
|
:rules="[val=>validateEmail(val)||'Bitte gültige E-Mail angeben!']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.checkemail" type="text"
|
||||||
|
label="E-Mail erneut eingeben" placeholer="Email"
|
||||||
|
:rules="[val=>val===user.email||'E-Mail stimmt nicht überein!']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.password" type="password"
|
||||||
|
label="Passwort"
|
||||||
|
:rules="[val=>val.length>=8||'Passwort muss mindestens 8 Zeichen lang sein!']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-input lazy-rules outlined filled stack-label v-model="user.checkpassword" type="password"
|
||||||
|
label="Passwort erneut eingeben"
|
||||||
|
:rules="[val=>val===user.password||'Passwort stimmt nicht überein']"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="">
|
||||||
|
<div class="" style="max-width: 440px">
|
||||||
|
<q-btn
|
||||||
|
:disabled="!validationSuccesful"
|
||||||
|
label="Registrieren"
|
||||||
|
color="primary"
|
||||||
|
class="full-width"
|
||||||
|
@click="register()"
|
||||||
|
unelevated
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
checkemail: "",
|
||||||
|
password: "",
|
||||||
|
checkpassword: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validationSuccesful() {
|
||||||
|
if (this.user.name.length >= 2
|
||||||
|
&& this.validateEmail(this.user.email)
|
||||||
|
&& this.user.email === this.user.checkemail
|
||||||
|
&& this.user.password.length >= 8
|
||||||
|
&& this.user.password === this.user.checkpassword) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.login();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
validateEmail(email) {
|
||||||
|
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(String(email).toLowerCase());
|
||||||
|
},
|
||||||
|
register: function () {
|
||||||
|
|
||||||
|
if (this.user.email === this.user.checkemail && this.user.password === this.user.checkpassword) {
|
||||||
|
const data = {
|
||||||
|
name: this.user.name,
|
||||||
|
password: this.user.password,
|
||||||
|
email: this.user.email,
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
role: "CACHER",
|
||||||
|
domain: "geocaching.de"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("POST /api/register/ - json: " + JSON.stringify(data));
|
||||||
|
const token = JSON.parse(localStorage.getItem('registerToken')).token;
|
||||||
|
this.$axios.post(process.env.USER_API + '/account/register', data, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + token,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
console.log(response.data);
|
||||||
|
if (response.status === 201) {
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Deine Registrierung war erfolgreich! Du bist jetzt eingeloggt.",
|
||||||
|
title: "Registrierungsprozess",
|
||||||
|
color: "blue"
|
||||||
|
});
|
||||||
|
this.autoLogin();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
let message;
|
||||||
|
let header = "Unbekannter Fehler!";
|
||||||
|
if (error.response) {
|
||||||
|
console.log(error.response);
|
||||||
|
if (error.response.status === 409) {
|
||||||
|
message = "Die E-Mail-Adresse wird bereits verwendet!";
|
||||||
|
header = "Anmeldedaten überprüfen!";
|
||||||
|
}
|
||||||
|
if (error.response.status === 500) {
|
||||||
|
message= "Der Anmeldeserver konnte die Daten nicht verarbeiten.";
|
||||||
|
header= "Fehler!";
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
console.log(error.request);
|
||||||
|
header = "Anfrage fehlgeschlagen!";
|
||||||
|
message = "Die Verbindung zum Server ist gestört. Versuchen Sie es später noch einmal.";
|
||||||
|
}
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {message: message, title: header});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (this.user.email.toLowerCase() != this.user.checkemail.toLowerCase()) {
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Email stimmt nicht überein",
|
||||||
|
title: "Fehler",
|
||||||
|
});
|
||||||
|
} else if (this.user.password != this.user.checkpassword) {
|
||||||
|
this.$store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Passwort stimmt nicht überein",
|
||||||
|
title: "Fehler",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
login() {
|
||||||
|
console.log("MasterUser()")
|
||||||
|
const logindata = {
|
||||||
|
email: "register@bugageocaching.de",
|
||||||
|
password: "reguser2019"
|
||||||
|
};
|
||||||
|
this.$axios.post(process.env.USER_API + '/account/login', logindata)
|
||||||
|
.then((response) => {
|
||||||
|
localStorage.setItem('registerToken', JSON.stringify(response.data));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
autoLogin() {
|
||||||
|
console.log("autoLogin()");
|
||||||
|
const data = {
|
||||||
|
email: this.user.email,
|
||||||
|
password: this.user.password,
|
||||||
|
};
|
||||||
|
console.log(data);
|
||||||
|
this.$axios.post(process.env.USER_API + '/account/login', data)
|
||||||
|
.then((response) => {
|
||||||
|
localStorage.setItem('userToken', JSON.stringify(response.data));
|
||||||
|
localStorage.setItem('userMail', JSON.stringify(data.email));
|
||||||
|
// this.$store.commit('auth/SET_AUTHENTICATED');
|
||||||
|
// this.$store.commit('auth/SET_USER');
|
||||||
|
this.$router.push({path: `/overview`});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.$router.push("/login");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
validateUsername(fld) {
|
||||||
|
var illegalChars = /[^A-Za-z0-9_äÄöÖüÜß. -]/g;
|
||||||
|
if (fld === "") {
|
||||||
|
return false;
|
||||||
|
} else if (fld.length < 2) {
|
||||||
|
return false;
|
||||||
|
} else if (illegalChars.test(fld)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
137
frontend/src/pages/StationEdit.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-ma-md">
|
||||||
|
<p class="text-h5">Neue Station</p>
|
||||||
|
<q-editor
|
||||||
|
:toolbar="[
|
||||||
|
['bold', 'italic', 'strike', 'underline'],
|
||||||
|
['undo', 'redo'],
|
||||||
|
['hr', 'link'],
|
||||||
|
['fullscreen'],
|
||||||
|
]"
|
||||||
|
v-model="station.description" min-height="10rem"/>
|
||||||
|
<!--<q-input-->
|
||||||
|
<!--v-model="description"-->
|
||||||
|
<!--filled-->
|
||||||
|
<!--type="textarea"-->
|
||||||
|
<!--/>-->
|
||||||
|
<p class="text-h6 q-mt-md">Location</p>
|
||||||
|
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
|
||||||
|
data-projection="EPSG:4326">
|
||||||
|
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
|
||||||
|
|
||||||
|
<vl-geoloc @update:position="geolocPosition = $event">
|
||||||
|
<template slot-scope="geoloc">
|
||||||
|
<vl-feature v-if="geoloc.position" id="position-feature">
|
||||||
|
<vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
|
||||||
|
</vl-feature>
|
||||||
|
</template>
|
||||||
|
</vl-geoloc>
|
||||||
|
|
||||||
|
<vl-layer-tile id="osm">
|
||||||
|
<vl-source-osm></vl-source-osm>
|
||||||
|
</vl-layer-tile>
|
||||||
|
</vl-map>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<q-input class="col" dense stack-label filled v-model="latlang" @input="separateLatlang"
|
||||||
|
label="Breitengrad/Längengrad"/>
|
||||||
|
<div class="col-shrink">
|
||||||
|
<q-btn unelevated color="primary" class="full-height" icon="my_location"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <p class="text-h6 q-mt-md">Lösung</p>-->
|
||||||
|
<!-- <q-input class="col" dense stack-label filled v-model="station.solution" label="Lösung"/>-->
|
||||||
|
<!-- <q-input class="col q-mt-md" dense stack-label filled v-model="station.code" label="Code" readonly/>-->
|
||||||
|
<div class="row reverse q-mt-md q-gutter-x-md">
|
||||||
|
<q-btn @click="saveStation" unelevated color="primary" label="Speichern" icon-right="add"/>
|
||||||
|
<q-btn @click="dismiss" unelevated color="negative" label="verwerfen" icon-right="delete"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapGetters} from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Station",
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
description: "Rätsel, Aufgabe und Informationen zur Station.",
|
||||||
|
latlang: "",
|
||||||
|
station: {
|
||||||
|
description: "Rätsel, Aufgabe und Informationen zur Station.",
|
||||||
|
lattitude: 49.1474082,
|
||||||
|
longitude: 9.2065282,
|
||||||
|
solution: "",
|
||||||
|
code: ""
|
||||||
|
},
|
||||||
|
isNewStation: true,
|
||||||
|
// stationObject: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// props: [ 'stationObject' ],
|
||||||
|
created: function () {
|
||||||
|
this.isNewStation = (this.$route.params.pos === undefined);
|
||||||
|
console.log("neu: " + this.isNewStation);
|
||||||
|
console.log("pos: " + this.$route.params.pos);
|
||||||
|
console.log(this.$route);
|
||||||
|
console.log(this.station);
|
||||||
|
if (!this.isNewStation) {
|
||||||
|
this.station = JSON.parse(JSON.stringify(this.tempStation));
|
||||||
|
}
|
||||||
|
console.log(this.station);
|
||||||
|
},
|
||||||
|
beforeMount: function () {
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.concatLatlang();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
tempStation: 'cacheCollector/GET_TEMPSTATION'
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
separateLatlang() {
|
||||||
|
//console.log("separateLatlang()");
|
||||||
|
if (this.latlang.includes(',')) {
|
||||||
|
this.station.lattitude = this.latlang.substr(0, this.latlang.indexOf(',')).trim();
|
||||||
|
this.station.longitude = this.latlang.substr(this.latlang.indexOf(',') + 1, this.latlang.length).trim();
|
||||||
|
console.log(this.latlang);
|
||||||
|
console.log(this.station.lattitude + ", " + this.station.longitude);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
concatLatlang() {
|
||||||
|
this.latlang = this.station.lattitude + ", " + this.station.longitude;
|
||||||
|
},
|
||||||
|
saveStation() {
|
||||||
|
console.log("saveStation(): ");
|
||||||
|
console.log(this.station);
|
||||||
|
console.log(this.isNewStation);
|
||||||
|
console.log(this.$route.params.pos);
|
||||||
|
if (this.isNewStation) {
|
||||||
|
this.$store.commit('cacheCollector/ADD_STATION', this.station);
|
||||||
|
} else {
|
||||||
|
this.$store.commit('cacheCollector/EDIT_STATION', {index: this.$route.params.pos, station: this.station});
|
||||||
|
this.$store.commit('cacheCollector/SET_TEMPSTATION', null);
|
||||||
|
}
|
||||||
|
let cache = this.$store.getters['cacheCollector/GET_CACHE'];
|
||||||
|
if (cache.hasOwnProperty('id')) {
|
||||||
|
this.$router.push({path: `/cache/${cache.id}`});
|
||||||
|
} else {
|
||||||
|
this.$router.push({path: `/cache`});
|
||||||
|
}
|
||||||
|
console.log("station saved..");
|
||||||
|
},
|
||||||
|
dismiss() {
|
||||||
|
this.$store.commit('cacheCollector/SET_TEMPSTATION', null);
|
||||||
|
this.$router.push({path: `/cache`});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
97
frontend/src/pages/StationEndEdit.vue
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-ma-md">
|
||||||
|
<p class="text-h5">Endstation</p>
|
||||||
|
<p class="text-h6 q-mt-md">Location</p>
|
||||||
|
<q-img transition="fade"
|
||||||
|
class="q-mb-md "
|
||||||
|
:ratio="16/9"
|
||||||
|
src="https://www.buga2019.de/we-bilder/3.Gartenausstellung/Gelaendeplan/190320_Gelaendeplan-quadratisch.jpg"
|
||||||
|
></q-img>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<q-input class="col" dense stack-label filled v-model="latlang" @input="separateLatlang"
|
||||||
|
label="Breitengrad/Längengrad"/>
|
||||||
|
<div class="col-shrink">
|
||||||
|
<q-btn unelevated color="primary" class="full-height" icon="my_location"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <p class="text-h6 q-mt-md">Lösung</p>-->
|
||||||
|
<!-- <q-input class="col" dense stack-label filled v-model="station.solution" label="Lösung"/>-->
|
||||||
|
<!-- <q-input class="col q-mt-md" dense stack-label filled v-model="station.code" label="Code" readonly/>-->
|
||||||
|
<div class="row reverse q-mt-md q-gutter-x-md">
|
||||||
|
<q-btn @click="saveStation" unelevated color="primary" label="Speichern" icon-right="add"/>
|
||||||
|
<q-btn @click="dismiss" unelevated color="negative" label="verwerfen" icon-right="delete"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapGetters} from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Station",
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
description: "Rätsel, Aufgabe und Informationen zur Station.",
|
||||||
|
latlang: "",
|
||||||
|
station: {
|
||||||
|
description: "Beschreibung",
|
||||||
|
lattitude: 49.1474082,
|
||||||
|
longitude: 9.2065282,
|
||||||
|
solution: "",
|
||||||
|
code: ""
|
||||||
|
},
|
||||||
|
// stationObject: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// props: [ 'stationObject' ],
|
||||||
|
created: function () {
|
||||||
|
//this.isNewStation = (this.$route.params.pos === undefined);
|
||||||
|
console.log("pos: " + this.$route.params.pos);
|
||||||
|
console.log(this.$route);
|
||||||
|
console.log(this.station);
|
||||||
|
this.station = JSON.parse(JSON.stringify(this.tempStation));
|
||||||
|
console.log(this.station);
|
||||||
|
},
|
||||||
|
beforeMount: function () {
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.concatLatlang();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
tempStation: 'cacheCollector/GET_ENDSTATION'
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
separateLatlang() {
|
||||||
|
//console.log("separateLatlang()");
|
||||||
|
if (this.latlang.includes(',')) {
|
||||||
|
this.station.lattitude = this.latlang.substr(0, this.latlang.indexOf(',')).trim();
|
||||||
|
this.station.longitude = this.latlang.substr(this.latlang.indexOf(',') + 1, this.latlang.length).trim();
|
||||||
|
console.log(this.latlang);
|
||||||
|
console.log(this.station.lattitude + ", " + this.station.longitude);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
concatLatlang() {
|
||||||
|
this.latlang = this.station.lattitude + ", " + this.station.longitude;
|
||||||
|
},
|
||||||
|
saveStation() {
|
||||||
|
console.log("saveStation(): ");
|
||||||
|
console.log(this.station);
|
||||||
|
this.$store.commit('cacheCollector/SET_ENDSTATION', this.station);
|
||||||
|
this.$router.push({path: `/cache`});
|
||||||
|
console.log("station saved..");
|
||||||
|
},
|
||||||
|
dismiss() {
|
||||||
|
//this.$store.commit('cacheCollector/SET_ENDSTATION', null);
|
||||||
|
this.$router.push({path: `/cache`});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
128
frontend/src/pages/StationView.vue
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<vl-map :load-tiles-while-animating="true" :load-tiles-while-interacting="true"
|
||||||
|
data-projection="EPSG:4326" style="height: 200px" >
|
||||||
|
<vl-view :zoom.sync="zoom" :center.sync="center" :rotation.sync="rotation"></vl-view>
|
||||||
|
|
||||||
|
<vl-geoloc @update:position="geolocPosition = $event">
|
||||||
|
<template slot-scope="geoloc">
|
||||||
|
<vl-feature v-if="geoloc.position" id="position-feature">
|
||||||
|
<vl-geom-point :coordinates="geoloc.position"></vl-geom-point>
|
||||||
|
</vl-feature>
|
||||||
|
</template>
|
||||||
|
</vl-geoloc>
|
||||||
|
|
||||||
|
<vl-layer-tile id="osm">
|
||||||
|
<vl-source-osm></vl-source-osm>
|
||||||
|
</vl-layer-tile>
|
||||||
|
</vl-map>
|
||||||
|
<div class="q-ma-md" v-if="!cameraActive">
|
||||||
|
<p class="text-h4">{{ cache.name }}</p>
|
||||||
|
<p class="text-h5">Station {{ showCacheProgress }}</p>
|
||||||
|
<p>{{ station.description }}</p>
|
||||||
|
<div class="column q-gutter-y-md">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<qrscanner @result="updateResult($event)" @camera="updateCamera($event)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Vue from 'vue'
|
||||||
|
import VueLayers from 'vuelayers'
|
||||||
|
import 'vuelayers/lib/style.css' // needs css-loader
|
||||||
|
|
||||||
|
Vue.use(VueLayers);
|
||||||
|
import qrscanner from "../components/qrscanner";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Station",
|
||||||
|
components: { qrscanner },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cacheID: "",
|
||||||
|
cacheName: "",
|
||||||
|
cache: {
|
||||||
|
name: "",
|
||||||
|
stationen: [],
|
||||||
|
},
|
||||||
|
station: {},
|
||||||
|
cameraActive: false,
|
||||||
|
result: null,
|
||||||
|
zoom: 15,
|
||||||
|
center: [ 9.208858198755664, 49.14785422283188],
|
||||||
|
rotation: 0,
|
||||||
|
geolocPosition: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
console.log("beforeRouteUpdate: reset data and fetch");
|
||||||
|
this.cameraActive = false;
|
||||||
|
this.result = null;
|
||||||
|
this.cacheID = "";
|
||||||
|
this.cacheName = "";
|
||||||
|
this.cache = {
|
||||||
|
name: "",
|
||||||
|
stationen: [],
|
||||||
|
};
|
||||||
|
this.station = {};
|
||||||
|
this.fetchData();
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
console.log("StationView: created!");
|
||||||
|
console.log("'id' from url: " + this.$route.params.id);
|
||||||
|
console.log("'cache' from url: " + this.$route.params.cache);
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
beforeMount: function () {
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
},
|
||||||
|
updated: function () {
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
showCacheProgress() {
|
||||||
|
if (this.cache !== null) {
|
||||||
|
let stationCount = this.cache.stationen.length;
|
||||||
|
let stationPos = 1 + this.cache.stationen.findIndex(station => station.id === Number(this.$route.params.id));
|
||||||
|
return `${stationPos} von ${stationCount}`;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("/api/allCaches");
|
||||||
|
console.log(response.data);
|
||||||
|
const cache = response.data.find(cache => cache.id === Number(this.$route.params.cache));
|
||||||
|
this.cache = cache;
|
||||||
|
this.station = cache.stationen.find(station => station.id === Number(this.$route.params.id));
|
||||||
|
this.cacheName = cache.name;
|
||||||
|
this.cacheID = this.$route.params.cache;
|
||||||
|
console.log(JSON.stringify(this.cache));
|
||||||
|
console.log(JSON.stringify(this.station));
|
||||||
|
console.log(this.cache);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updateResult(event) {
|
||||||
|
console.log("updateResult()");
|
||||||
|
console.log(event);
|
||||||
|
this.result = event;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateCamera(event) {
|
||||||
|
console.log("updateCamera()");
|
||||||
|
this.cameraActive = event;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
59
frontend/src/pages/qr-generator.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div class="q-ma-md">
|
||||||
|
<p class="text-h4">QR-Generator</p>
|
||||||
|
<p class="text-h5">Cache: {{ cacheName }}</p>
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2 items-start" v-for="(station, index) in stationen" :key="index">
|
||||||
|
<p class="text-h6 q-my-sm">Station {{ index + 1 }}</p>
|
||||||
|
<q-card class="">
|
||||||
|
<img class="full-width q-pa-md bg-white" :src="imgBaseURL + cacheId +'/'+ station.id" alt="">
|
||||||
|
</q-card>
|
||||||
|
<p class="q-my-sm">{{ cacheId }}/{{ station.id }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-btn to="/overview" unelevated color="primary" stack
|
||||||
|
label="Schließen" class="full-width"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
// name: 'PageName',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cacheName: "",
|
||||||
|
cacheId: 0,
|
||||||
|
stationen: [],
|
||||||
|
imgBaseURL: "https://api.qrserver.com/v1/create-qr-code/?size=500x500&data="
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("/api/allCaches");
|
||||||
|
console.log(response.data);
|
||||||
|
const cache = response.data.find(cache => cache.id === Number(this.$route.params.cache));
|
||||||
|
this.cacheName = cache.name;
|
||||||
|
this.cacheId = cache.id;
|
||||||
|
this.stationen = cache.stationen;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeRouteUpdate(to, from, next) {
|
||||||
|
this.fetchData();
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
33
frontend/src/pages/qr-scanner.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="!cameraActive" class="q-ma-md">
|
||||||
|
<p class="text-h4">QR-Scanner</p>
|
||||||
|
<p class="">Mit diesem Scanner kannst du die nächste Station deines Caches einscannen
|
||||||
|
oder einen neuen Cache beginnen indem du eine der Startstationen einscannst.
|
||||||
|
Du findest die Startstationen über die Karte.</p>
|
||||||
|
</div>
|
||||||
|
<qrscanner @camera="cameraState($event)" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import qrscanner from "../components/qrscanner";
|
||||||
|
export default {
|
||||||
|
name: 'Scanner',
|
||||||
|
components: {qrscanner},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cameraActive: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
cameraState(event) {
|
||||||
|
this.cameraActive = event;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
114
frontend/src/pages/ranking.vue
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="column">
|
||||||
|
<div class="bg-red col col-shrink" style="">
|
||||||
|
<q-tabs
|
||||||
|
v-model="tab"
|
||||||
|
class="bg-grey-2"
|
||||||
|
inline-label
|
||||||
|
align="justify"
|
||||||
|
active-bg-color="bg-grey-1"
|
||||||
|
active-color="cyan-14"
|
||||||
|
indicator-color="cyan-14"
|
||||||
|
switch-indicator
|
||||||
|
>
|
||||||
|
<q-tab name="solo" label="Solo" icon="person"/>
|
||||||
|
<q-tab name="team" label="Team" icon="group"/>
|
||||||
|
</q-tabs>
|
||||||
|
<q-separator color="grey-4"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col flex column">
|
||||||
|
<q-tab-panels v-model="tab" animated swipeable class="col">
|
||||||
|
<q-tab-panel name="solo" class="q-pa-md fit">
|
||||||
|
<q-list>
|
||||||
|
<q-card class="q-mb-md" v-for="(user,index) in rankinglist" :key="user.id">
|
||||||
|
<q-item class="q-pr-sm ">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label><a class="text-black" style="text-decoration: none"><span>{{index+1}}. {{user.username}}</span></a></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side>
|
||||||
|
<span class="text-grey">{{user.rankingPointsSum}} Punkte </span>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-card>
|
||||||
|
</q-list>
|
||||||
|
</q-tab-panel>
|
||||||
|
|
||||||
|
<q-tab-panel name="team" class=" fit">
|
||||||
|
<q-list>
|
||||||
|
<q-card class="q-mb-md" v-for="(team,index) in teamRankinglist" :key="team.id">
|
||||||
|
<q-item class="q-pr-sm ">
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label><a class="text-black" style="text-decoration: none"><span>{{index+1}}. {{team.teamname}}</span></a></q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side>
|
||||||
|
<span class="text-grey">{{team.rankingPointsSum}} Punkte </span>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-card>
|
||||||
|
</q-list>
|
||||||
|
</q-tab-panel>
|
||||||
|
</q-tab-panels>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/*
|
||||||
|
.my-list-card-item
|
||||||
|
padding-left: 8px
|
||||||
|
*/
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import {dom} from 'quasar'
|
||||||
|
|
||||||
|
const {height, width} = dom
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 'solo',
|
||||||
|
rankinglist: [],
|
||||||
|
teamRankinglist: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: function() {
|
||||||
|
console.log("created(): " + this.rankinglist);
|
||||||
|
console.log("created(): " + this.teamRankinglist);
|
||||||
|
this.fetchRankinglist();
|
||||||
|
this.fetchTeamRankinglist();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchRankinglist() {
|
||||||
|
this.$axios.get('/api/getRankingList')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("Rankinglist: ");
|
||||||
|
console.log(response.data);
|
||||||
|
this.rankinglist = response.data;
|
||||||
|
}).catch((error) => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchTeamRankinglist() {
|
||||||
|
this.$axios.get('/api/getTeamRankingList')
|
||||||
|
.then((response) => {
|
||||||
|
console.log("Team-Rankinglist: ");
|
||||||
|
console.log(response.data);
|
||||||
|
this.teamRankinglist = response.data;
|
||||||
|
}).catch((error) => {
|
||||||
|
})
|
||||||
|
},
|
||||||
|
calculateRank() {
|
||||||
|
// TODO
|
||||||
|
// let rank = 0;
|
||||||
|
// let lastSum = null;
|
||||||
|
// const rankinglistCopy = this.rankinglist;
|
||||||
|
// for (let i = rankinglistCopy.length-1; i >= 0; i--) {
|
||||||
|
// if (lastSum === null || ranking.rankingPointsSum >= lastSum) {
|
||||||
|
// rank++;
|
||||||
|
// }
|
||||||
|
// lastSum = ranking.rankingPointsSum;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
93
frontend/src/router/index.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import VueRouter from "vue-router";
|
||||||
|
|
||||||
|
import routes from "./routes";
|
||||||
|
import {axiosInstance} from "../boot/axios";
|
||||||
|
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If not building with SSR mode, you can
|
||||||
|
* directly export the Router instantiation
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function ({store}/* { store, ssrContext } */) {
|
||||||
|
const Router = new VueRouter({
|
||||||
|
scrollBehavior: () => ({x: 0, y: 0}),
|
||||||
|
routes,
|
||||||
|
store,
|
||||||
|
// Leave these as is and change from quasar.conf.js instead!
|
||||||
|
// quasar.conf.js -> build -> vueRouterMode
|
||||||
|
// quasar.conf.js -> build -> publicPath
|
||||||
|
mode: process.env.VUE_ROUTER_MODE,
|
||||||
|
base: process.env.VUE_ROUTER_BASE
|
||||||
|
});
|
||||||
|
Router.beforeEach((to, from, next) => {
|
||||||
|
console.log("before Routing...");
|
||||||
|
const isPublic = to.matched.some(record => record.meta.public);
|
||||||
|
const onlyWhenLoggedOut = to.matched.some(record => record.meta.onlyWhenLoggedOut);
|
||||||
|
const onlyAdmin = to.matched.some(record => record.meta.onlyAdmin);
|
||||||
|
const loggedIn = localStorage.getItem('userToken')
|
||||||
|
? JSON.parse(localStorage.getItem('userToken'))
|
||||||
|
: false;
|
||||||
|
console.log(`isPublic: ${isPublic} \nonlyWhenLoggedOut: ${onlyWhenLoggedOut} \nonlyAdmin: ${onlyAdmin}`);
|
||||||
|
console.log("loggedIn");
|
||||||
|
console.log(loggedIn);
|
||||||
|
const isAdmin = loggedIn ? loggedIn.roles.find(x => x.role === "ADMIN" && x.domain === "geocaching.de") : false;
|
||||||
|
//const isAdmin = true;
|
||||||
|
if (!isPublic && !loggedIn) {
|
||||||
|
return next({
|
||||||
|
path: '/login',
|
||||||
|
query: {redirect: to.fullPath} // Store the full path to redirect the user to after login
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("!isPublic && !loggedIn")
|
||||||
|
if ((loggedIn && onlyWhenLoggedOut) || (loggedIn && onlyAdmin && !isAdmin)) {
|
||||||
|
return next('/')
|
||||||
|
}
|
||||||
|
console.log("(loggedIn && onlyWhenLoggedOut) || (loggedIn && onlyAdmin && !isAdmin)")
|
||||||
|
if (isPublic && !loggedIn) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
console.log("isPublic")
|
||||||
|
if (!onlyWhenLoggedOut && loggedIn) {
|
||||||
|
console.log("fetch data...")
|
||||||
|
axiosInstance.get('/api/getUser', {
|
||||||
|
params: {
|
||||||
|
token: loggedIn.token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
console.log("Token valid!");
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("Catch Block: ");
|
||||||
|
console.log("Token invalid!");
|
||||||
|
if (error.response) {
|
||||||
|
console.log(error.response.data);
|
||||||
|
console.log(error.response.status);
|
||||||
|
console.log(error.response.headers);
|
||||||
|
//store.commit('auth/SET_LOGOUT');
|
||||||
|
store.commit('dialog/NEW_MESSAGE_DIALOG', {
|
||||||
|
message: "Ihre Session ist abgelaufen. Bitte loggen Sie sich erneut ein.",
|
||||||
|
title: "Bitte erneut anmelden.",
|
||||||
|
color: "blue",
|
||||||
|
});
|
||||||
|
localStorage.removeItem('userToken');
|
||||||
|
return next('/Login');
|
||||||
|
} else {
|
||||||
|
console.log("unexpected behaviour");
|
||||||
|
console.log(error);
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
store.commit('auth/SET_AUTHENTICATED');
|
||||||
|
store.commit('auth/SET_USER');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(`isPublic: ${isPublic} \nonlyWhenLoggedOut: ${onlyWhenLoggedOut} \nonlyAdmin: ${onlyAdmin}`);
|
||||||
|
//return next();
|
||||||
|
});
|
||||||
|
return Router;
|
||||||
|
}
|
||||||
193
frontend/src/router/routes.js
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Index.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: true, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
|
||||||
|
path: "/overview/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Overview.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: true, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/cache/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Cache.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/qr-scanner/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/qr-scanner.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/cache/:id",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Cache.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/station/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/StationEdit.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/endstation/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/StationEndEdit.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tempstation/:pos",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{path: "", component: () => import("pages/StationEdit.vue")}],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/tempendstation/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{path: "", component: () => import("pages/StationEndEdit.vue")}],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/station/:cache/:id",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/StationView.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/ranking/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{path: "", component: () => import("pages/ranking.vue")}],
|
||||||
|
meta: {
|
||||||
|
public: true, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/login/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Login.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: true, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/CacheStart/:cache/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/CacheStart.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/CacheEnd/:cache/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/CacheEnd.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/generator/:cache/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/qr-generator.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/profile/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Profile.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/mycaches/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/MyCaches.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: false, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: false,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/register/",
|
||||||
|
component: () => import("layouts/MyLayout.vue"),
|
||||||
|
children: [{ path: "", component: () => import("pages/Register.vue") }],
|
||||||
|
meta: {
|
||||||
|
public: true, // Allow access to even if not logged in
|
||||||
|
onlyWhenLoggedOut: true,
|
||||||
|
onlyAdmin: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Always leave this as last one
|
||||||
|
if (process.env.MODE !== "ssr") {
|
||||||
|
routes.push({
|
||||||
|
path: "*",
|
||||||
|
component: () => import("pages/Error404.vue")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default routes;
|
||||||
35
frontend/src/statics/buga_logo.svg
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="103.187mm" height="99.871mm" viewBox="0 0 292.5 283.1">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.a, .e {
|
||||||
|
fill: #58c5c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.a, .b, .c, .g {
|
||||||
|
opacity: 0.65;
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.b, .f {
|
||||||
|
fill: #ec0090;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c, .d {
|
||||||
|
fill: #5ca038;
|
||||||
|
}
|
||||||
|
|
||||||
|
.g, .h {
|
||||||
|
fill: #accd6b;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<title>buga_logo</title>
|
||||||
|
<path class="a" d="M272.4,1H256.8V83.4c-.3,6.2-.4,11.7-1.8,17.8-2.2,11.1-10.2,32.9-31.5,33.2h0c30.2-.2,43.3-22.1,46.4-33.2,1.9-6.1,2.1-11.5,2.5-17.8ZM165.6,1V83.4c.4,6.3.6,11.7,2.5,17.8,3,11.1,16.3,33,46.5,33.2-21.4-.2-29.4-22.1-31.6-33.2-1.4-6.1-1.5-11.5-1.8-17.8V1Zm49,133.4Z"/>
|
||||||
|
<path class="b" d="M29.7,1H15.8V133.5H29.6V1ZM75.1,1H71.8c1.6,0,7.3.3,11.7,2.6C91.9,7.9,97.9,20,97.9,33.1c0,4.1-.7,14.2-6.7,21.9a36.431,36.431,0,0,1-5.1,5.3,20.877,20.877,0,0,1,7.8,3.4c8.2,5.7,12.7,18.1,12.7,31,0,4.5-.7,14.6-6,24-4.7,8.5-10.7,12.8-17.9,14.2-2.2.4-4.6.4-6.9.6h5.1c3.2-.2,6.6-.2,9.8-.6,10.2-1.3,18.7-5.7,25.3-14.2a39.435,39.435,0,0,0,8.5-24c0-12.9-6.4-25.4-17.9-31a38.989,38.989,0,0,0-11-3.4c3.6-2.4,5.7-3.8,7.2-5.3a29.422,29.422,0,0,0,9.4-21.9A31.582,31.582,0,0,0,91.8,3.6C85.5,1.2,76.8,1,75.1,1"/>
|
||||||
|
<path class="c" d="M223.4,157.9v.5L271.2,282H291Zm-8.6,0L147.2,282H167l47.8-123.5Z"/>
|
||||||
|
<path class="d" d="M271.2,282,223.4,158V282ZM167,282h47.8V158Z"/>
|
||||||
|
<path class="e" d="M256.7,83.4V1H223.5V134.4c21.3-.3,29.3-22.1,31.5-33.2,1.3-6.1,1.4-11.6,1.7-17.8m-42.1,51V1H181.2V83.4c.3,6.2.4,11.7,1.8,17.8,2.1,11.1,10.2,33,31.6,33.2"/>
|
||||||
|
<path class="f" d="M91.3,55c6-7.8,6.7-17.8,6.7-21.9C98,20,92,7.9,83.6,3.6,79,1.2,72.8,1,71.6,1H68V133.5h7.8c2.3-.2,4.7-.2,6.9-.6,7.2-1.3,13.2-5.7,17.9-14.2a50.913,50.913,0,0,0,6-24c0-12.9-4.5-25.3-12.7-31a22.143,22.143,0,0,0-7.7-3.4A31.991,31.991,0,0,0,91.3,55m-39,78.5h6.9V1H29.7V133.5Z"/>
|
||||||
|
<path class="g" d="M59.1,283.2h0c-8.3-1.1-16-5.5-22.4-12.2l-.1-.1c-1-1.1-2-2.2-3-3.4a3.039,3.039,0,0,1-.5-.7,37.117,37.117,0,0,1-2.3-3.2,14,14,0,0,0-1-1.6c-.5-.9-1.1-1.8-1.6-2.7-.4-.7-.7-1.5-1.1-2.2s-.8-1.6-1.2-2.4a4.875,4.875,0,0,1-.4-1.1c-.6-1.3-1.1-2.5-1.6-3.9,0-.1-.1-.2-.1-.3a73.839,73.839,0,0,1-2.9-10.2,89.8,89.8,0,0,1-1.5-10.4c-.2-2.5-.3-4.9-.3-7.5,0-7.2.8-25.1,10.3-40.5,7.6-12.4,18.5-21.1,29.9-22.6h0c-18.5,1.2-33.8,10.2-44.5,22.6C1.2,196.1,0,214,0,221.2c0,33,25.5,60,59.1,62m68-61.8H108.3c0,2.5-.1,4.9-.3,7.3a89.8,89.8,0,0,1-1.5,10.4,70.079,70.079,0,0,1-2.9,10.2c0,.1-.1.2-.1.3-.5,1.3-1,2.6-1.6,3.9-.1.4-.3.7-.4,1.1l-1.2,2.4c-.4.8-.7,1.5-1.1,2.2-.5.9-1.1,1.8-1.6,2.7a14,14,0,0,1-1,1.6,37.117,37.117,0,0,1-2.3,3.2,3.039,3.039,0,0,0-.5.7c-1,1.2-2,2.3-3,3.4l-.1.1c-6.4,6.7-14.1,11.1-22.4,12.2h0c33.3-1.9,58.7-28.7,58.8-61.7"/>
|
||||||
|
<path class="h" d="M59.1,283.2V158.1c-11.4,1.5-22.4,10.2-29.9,22.6-9.5,15.5-10.3,33.3-10.3,40.5-.1,33,18,59,40.2,62m49.2-61.8H68v61.7c22.1-2.9,40.2-28.7,40.3-61.7"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
BIN
frontend/src/statics/icons/apple-icon-152x152.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
frontend/src/statics/icons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/src/statics/icons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
frontend/src/statics/icons/icon-128x128.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
frontend/src/statics/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
frontend/src/statics/icons/icon-256x256.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
frontend/src/statics/icons/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
frontend/src/statics/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
frontend/src/statics/icons/ms-icon-144x144.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
frontend/src/statics/osm_mock.png
Normal file
|
After Width: | Height: | Size: 5.3 MiB |
4
frontend/src/store/auth/actions.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someAction (context) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
28
frontend/src/store/auth/getters.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export function GET_ADMINSTATE(state) {
|
||||||
|
console.log("GET_ADMINSTATE()");
|
||||||
|
if (state.userAuthenticated !== null) {
|
||||||
|
console.log(state.userAuthenticated.roles.find(x => x.name === "ADMIN") != null);
|
||||||
|
return state.userAuthenticated.roles.find(x => x.name === "ADMIN") != null;
|
||||||
|
} else {
|
||||||
|
console.log(state.userAuthenticated);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function GET_USER(state) {
|
||||||
|
console.log("GET_USER");
|
||||||
|
return state.userAuthenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @return {boolean}
|
||||||
|
// */
|
||||||
|
// export function IS_AUTHENTICATED(state) {
|
||||||
|
// console.log("IS_AUTHENTICATED()");
|
||||||
|
// console.log(JSON.parse(localStorage.getItem('userToken')));
|
||||||
|
// console.log(!(JSON.parse(localStorage.getItem('userToken')) === null));
|
||||||
|
// console.log(!(localStorage.getItem('userToken') === null));
|
||||||
|
// return !(localStorage.getItem('userToken') === null);
|
||||||
|
// }
|
||||||
12
frontend/src/store/auth/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import state from './state'
|
||||||
|
import * as getters from './getters'
|
||||||
|
import * as mutations from './mutations'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
||||||
41
frontend/src/store/auth/mutations.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { axiosInstance } from '../../boot/axios'
|
||||||
|
|
||||||
|
export const SET_AUTHENTICATED = (state) => {
|
||||||
|
console.log("SET_AUTHENTICATED()");
|
||||||
|
console.log(JSON.parse(localStorage.getItem('userToken')));
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
state.isAuthenticated = true;
|
||||||
|
} else {
|
||||||
|
state.isAuthenticated = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const SET_LOGOUT = (state) => {
|
||||||
|
console.log("SET_LOGOUT()");
|
||||||
|
localStorage.removeItem('userToken');
|
||||||
|
state.userAuthenticated = null;
|
||||||
|
state.isAuthenticated = false;
|
||||||
|
};
|
||||||
|
export const SET_USER = (state) => {
|
||||||
|
console.log("SET_USER()");
|
||||||
|
console.log(process.env.API)
|
||||||
|
if (localStorage.getItem('userToken')) {
|
||||||
|
axiosInstance.get('/api/getUser', {
|
||||||
|
params: {
|
||||||
|
token: JSON.parse(localStorage.getItem('userToken')).token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
console.log("GET /api/getUser");
|
||||||
|
state.userAuthenticated = response.data;
|
||||||
|
state.isAuthenticated = true;
|
||||||
|
if (state.userAuthenticated.hasOwnProperty('password')) delete state.userAuthenticated.password;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("error")
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
state.isAuthenticated = false;
|
||||||
|
state.userAuthenticated = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
19
frontend/src/store/auth/state.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export default {
|
||||||
|
isAuthenticated: false,
|
||||||
|
userAuthenticated: null,
|
||||||
|
// userAuthenticated: {
|
||||||
|
// id: 1,
|
||||||
|
// firstname: "Timo",
|
||||||
|
// lastname: "Volkmann",
|
||||||
|
// username: "moximoti",
|
||||||
|
// rankingPointsSum: 0,
|
||||||
|
// email: "test@user.com",
|
||||||
|
// password: "$2a$10$c3Fo5nuUG.nlwXP94qc7qO01/UC1OL2DebEm.5zYlisKJGRhXMnqq",
|
||||||
|
// roles: [
|
||||||
|
// {
|
||||||
|
// id: 0,
|
||||||
|
// name: "admin"
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
}
|
||||||
4
frontend/src/store/cacheCollector/actions.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someAction (context) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
20
frontend/src/store/cacheCollector/getters.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const GET_CACHE = (state) => {
|
||||||
|
console.log("GET_CACHE: retrieve cache from store. ");
|
||||||
|
return state.newCache;
|
||||||
|
};
|
||||||
|
export const GET_LOCK = (state) => {
|
||||||
|
console.log("GET_LOCK: ");
|
||||||
|
return state.locked;
|
||||||
|
};
|
||||||
|
export const GET_TEMPSTATION = (state) => {
|
||||||
|
console.log("GET_TEMPSTATION: retrieve cache from store. ");
|
||||||
|
return state.tempStation;
|
||||||
|
};
|
||||||
|
export const GET_ENDSTATION = (state) => {
|
||||||
|
console.log("GET_ENDSTATION: retrieve cache from store. ");
|
||||||
|
return state.endStation;
|
||||||
|
};
|
||||||
|
export const GET_STATIONS = (state) => {
|
||||||
|
console.log("GET_STATIONEN: retrieve cache from store. ");
|
||||||
|
return state.newCache.stationen;
|
||||||
|
};
|
||||||
12
frontend/src/store/cacheCollector/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import state from './state'
|
||||||
|
import * as getters from './getters'
|
||||||
|
import * as mutations from './mutations'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
||||||
79
frontend/src/store/cacheCollector/mutations.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
export const SET_CACHE = (state, cache) => {
|
||||||
|
console.log("SET_CACHE: save cache to store. ");
|
||||||
|
state.newCache = cache;
|
||||||
|
};
|
||||||
|
export const SET_LOCK = (state, bool) => {
|
||||||
|
console.log("SET_LOCK: ");
|
||||||
|
state.locked = bool;
|
||||||
|
};
|
||||||
|
export const SET_TEMPSTATION = (state, station) => {
|
||||||
|
console.log("SET_TEMPSTATION: add new station to cache: "+station);
|
||||||
|
state.tempStation = station;
|
||||||
|
};
|
||||||
|
export const SET_ENDSTATION = (state, station) => {
|
||||||
|
console.log("SET_ENDSTATION: "+station);
|
||||||
|
state.endStation = station;
|
||||||
|
};
|
||||||
|
export const SET_STATIONS = (state, station) => {
|
||||||
|
console.log("SET_STATIONEN: "+station);
|
||||||
|
state.newCache.stationen = station;
|
||||||
|
};
|
||||||
|
export const ADD_STATION = (state, station) => {
|
||||||
|
console.log("ADD_STATION: add new station to cache: "+station);
|
||||||
|
state.newCache.stationen.push(station);
|
||||||
|
};
|
||||||
|
export const EDIT_STATION = (state, indexStation) => {
|
||||||
|
let index, station;
|
||||||
|
index = indexStation.index;
|
||||||
|
station = indexStation.station;
|
||||||
|
console.log("EDIT_STATION: "+index+" "+station);
|
||||||
|
|
||||||
|
state.newCache.stationen[index] = station;
|
||||||
|
console.log("EDIT_STATION: "+JSON.stringify(state.newCache.stationen[index]));
|
||||||
|
};
|
||||||
|
export const REMOVE_STATION = (state, index) => {
|
||||||
|
console.log("REMOVE_STATION: "+index);
|
||||||
|
state.newCache.stationen.splice(index,1);
|
||||||
|
};
|
||||||
|
export const RESET_NEW_CACHE = (state) => {
|
||||||
|
state.newCache = {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
rankingPoints: 0,
|
||||||
|
reward: {
|
||||||
|
rewardDescription: "",
|
||||||
|
},
|
||||||
|
stationen: []
|
||||||
|
};
|
||||||
|
state.endStation = {
|
||||||
|
description: "Endstation",
|
||||||
|
lattitude: 49.1474082,
|
||||||
|
longitude: 9.2065282,
|
||||||
|
solution: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("resetted new Cache");
|
||||||
|
};
|
||||||
|
export const LOAD_REMOTE_CACHE = (state, id) => {
|
||||||
|
console.log("LOAD_REMOTE_CACHE: get caches from remote");
|
||||||
|
this.$axios.get('/api/allCaches')
|
||||||
|
.then((response) => {
|
||||||
|
const allCaches = JSON.parse(response.data);
|
||||||
|
console.log("Caches: " + allCaches.length);
|
||||||
|
if (response.data === undefined || allCaches.length === 0) {
|
||||||
|
console.log("aborted processing data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cacheToSet = null;
|
||||||
|
for (let cache in allCaches) {
|
||||||
|
console.log("Cachedata: ");
|
||||||
|
console.log(cache.id);
|
||||||
|
console.log(cache.name);
|
||||||
|
if (cache.id === id) {
|
||||||
|
cacheToSet = cache;
|
||||||
|
console.log("found matching ID: setted cache to store.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
19
frontend/src/store/cacheCollector/state.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export default {
|
||||||
|
locked: false,
|
||||||
|
newCache: {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
rankingPoints: 0,
|
||||||
|
reward: {
|
||||||
|
rewardDescription: "",
|
||||||
|
},
|
||||||
|
stationen: []
|
||||||
|
},
|
||||||
|
tempStation: {},
|
||||||
|
endStation: {
|
||||||
|
description: "Endstation",
|
||||||
|
lattitude: 49.1474082,
|
||||||
|
longitude: 9.2065282,
|
||||||
|
solution: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
4
frontend/src/store/currentCache/actions.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someAction (context) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
4
frontend/src/store/currentCache/getters.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someGetter (state) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
12
frontend/src/store/currentCache/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import state from './state'
|
||||||
|
import * as getters from './getters'
|
||||||
|
import * as mutations from './mutations'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
||||||
4
frontend/src/store/currentCache/mutations.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someMutation (state) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
5
frontend/src/store/currentCache/state.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
cache: {},
|
||||||
|
currentCacheID: null,
|
||||||
|
currentStationID: null,
|
||||||
|
}
|
||||||
4
frontend/src/store/dialog/actions.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someAction (context) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
4
frontend/src/store/dialog/getters.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someGetter (state) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
12
frontend/src/store/dialog/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import state from './state'
|
||||||
|
import * as getters from './getters'
|
||||||
|
import * as mutations from './mutations'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
||||||
51
frontend/src/store/dialog/mutations.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
export function NEW_MESSAGE_DIALOG (state, messageObject) {
|
||||||
|
// console.log("NEW_MESSAGE_DIALOG");
|
||||||
|
// console.log(messageObject);
|
||||||
|
if (messageObject == null) {
|
||||||
|
state.dialog.show = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// console.log(messageObject);
|
||||||
|
|
||||||
|
if (messageObject.hasOwnProperty('color')) {
|
||||||
|
switch (messageObject.color) {
|
||||||
|
case "red": {
|
||||||
|
let color = "red-9";
|
||||||
|
state.dialog.colorBackground = "bg-"+color+" text-white";
|
||||||
|
state.dialog.colorButton = color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "amber": {
|
||||||
|
let color = "amber";
|
||||||
|
state.dialog.colorBackground = "bg-"+color+" text-white";
|
||||||
|
state.dialog.colorButton = color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "blue": {
|
||||||
|
let color = "primary";
|
||||||
|
state.dialog.colorBackground = "bg-"+color+" text-white";
|
||||||
|
state.dialog.colorButton = color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageObject.hasOwnProperty('title')) {
|
||||||
|
state.dialog.title = messageObject.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageObject.hasOwnProperty('message')) {
|
||||||
|
state.dialog.message = messageObject.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.dialog.show = true;
|
||||||
|
}
|
||||||
|
export function RESET_MESSAGE_DIALOG (state) {
|
||||||
|
// console.log("RESET_MESSAGE_DIALOG");
|
||||||
|
state.dialog.colorBackground = "bg-red-9 text-white";
|
||||||
|
state.dialog.colorButton = "red-9";
|
||||||
|
state.dialog.message = "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es noch einmal.";
|
||||||
|
state.dialog.title = "Fehler";
|
||||||
|
state.dialog.show = false;
|
||||||
|
}
|
||||||
9
frontend/src/store/dialog/state.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export default {
|
||||||
|
dialog: {
|
||||||
|
colorBackground: "bg-red-9 text-white",
|
||||||
|
colorButton: "red-9",
|
||||||
|
show: false,
|
||||||
|
title: "Fehler",
|
||||||
|
message: "Ein unbekannter Fehler ist aufgetreten. Bitte versuchen Sie es noch einmal."
|
||||||
|
},
|
||||||
|
}
|
||||||
59
frontend/src/store/index.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import Vuex from "vuex";
|
||||||
|
import Axios from "axios";
|
||||||
|
import auth from "./auth"
|
||||||
|
import cacheCollector from "./cacheCollector"
|
||||||
|
import dialog from "./dialog"
|
||||||
|
import currentCache from "./currentCache"
|
||||||
|
|
||||||
|
// import example from './module-example'
|
||||||
|
|
||||||
|
Vue.use(Vuex, Axios);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If not building with SSR mode, you can
|
||||||
|
* directly export the Store instantiation
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default function (/* { ssrContext } */) {
|
||||||
|
const Store = new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
auth,
|
||||||
|
cacheCollector,
|
||||||
|
dialog,
|
||||||
|
currentCache
|
||||||
|
},
|
||||||
|
|
||||||
|
// enable strict mode (adds overhead!)
|
||||||
|
// for dev mode only
|
||||||
|
//strict: process.env.DEV
|
||||||
|
strict: false
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
if we want some HMR magic for it, we handle
|
||||||
|
the hot update like below. Notice we guard this
|
||||||
|
code with "process.env.DEV" -- so this doesn't
|
||||||
|
get into our production build (and it shouldn't).
|
||||||
|
*/
|
||||||
|
if (process.env.DEV && module.hot) {
|
||||||
|
module.hot.accept(['./auth'], () => {
|
||||||
|
const auth = require('./auth').default;
|
||||||
|
store.hotUpdate({ modules: { auth: newAuth } })
|
||||||
|
});
|
||||||
|
module.hot.accept(['./cacheCollector'], () => {
|
||||||
|
const cacheCollector = require('./cacheCollector').default;
|
||||||
|
store.hotUpdate({ modules: { cacheCollector: newCacheCollector } })
|
||||||
|
});
|
||||||
|
module.hot.accept(['./dialog'], () => {
|
||||||
|
const dialog = require('./dialog').default;
|
||||||
|
store.hotUpdate({ modules: { dialog: newDialog } })
|
||||||
|
});
|
||||||
|
module.hot.accept(['./currentCache'], () => {
|
||||||
|
const currentCache = require('./currentCache').default;
|
||||||
|
store.hotUpdate({ modules: { currentCache: newCurrentCache } })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Store
|
||||||
|
};
|
||||||
1
frontend/src/store/module-example/actions.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export function someAction(/* context */) {}
|
||||||
1
frontend/src/store/module-example/getters.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export function someGetter(/* state */) {}
|
||||||
12
frontend/src/store/module-example/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import state from "./state";
|
||||||
|
import * as getters from "./getters";
|
||||||
|
import * as mutations from "./mutations";
|
||||||
|
import * as actions from "./actions";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions,
|
||||||
|
state
|
||||||
|
};
|
||||||
1
frontend/src/store/module-example/mutations.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export function someMutation(/* state */) {}
|
||||||
3
frontend/src/store/module-example/state.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
//
|
||||||
|
};
|
||||||
7467
frontend/yarn.lock
Normal file
104
labswp_2019_sose_geocaching.ipr
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<option name="DEFAULT_COMPILER" value="Javac"/>
|
||||||
|
<resourceExtensions>
|
||||||
|
<entry name=".+\.(properties|xml|html|dtd|tld)"/>
|
||||||
|
<entry name=".+\.(gif|png|jpeg|jpg)"/>
|
||||||
|
</resourceExtensions>
|
||||||
|
<wildcardResourcePatterns>
|
||||||
|
<entry name="!?*.class"/>
|
||||||
|
<entry name="!?*.scala"/>
|
||||||
|
<entry name="!?*.groovy"/>
|
||||||
|
<entry name="!?*.java"/>
|
||||||
|
</wildcardResourcePatterns>
|
||||||
|
<annotationProcessing enabled="false" useClasspath="true"/>
|
||||||
|
<bytecodeTargetLevel target="1.8"/>
|
||||||
|
</component>
|
||||||
|
<component name="CopyrightManager" default="">
|
||||||
|
<module2copyright/>
|
||||||
|
</component>
|
||||||
|
<component name="DependencyValidationManager">
|
||||||
|
<option name="SKIP_IMPORT_STATEMENTS" value="false"/>
|
||||||
|
</component>
|
||||||
|
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"/>
|
||||||
|
<component name="GradleUISettings">
|
||||||
|
<setting name="root"/>
|
||||||
|
</component>
|
||||||
|
<component name="GradleUISettings2">
|
||||||
|
<setting name="root"/>
|
||||||
|
</component>
|
||||||
|
<component name="IdProvider" IDEtalkID="11DA1DB66DD62DDA1ED602B7079FE97C"/>
|
||||||
|
<component name="JavadocGenerationManager">
|
||||||
|
<option name="OUTPUT_DIRECTORY"/>
|
||||||
|
<option name="OPTION_SCOPE" value="protected"/>
|
||||||
|
<option name="OPTION_HIERARCHY" value="true"/>
|
||||||
|
<option name="OPTION_NAVIGATOR" value="true"/>
|
||||||
|
<option name="OPTION_INDEX" value="true"/>
|
||||||
|
<option name="OPTION_SEPARATE_INDEX" value="true"/>
|
||||||
|
<option name="OPTION_DOCUMENT_TAG_USE" value="false"/>
|
||||||
|
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false"/>
|
||||||
|
<option name="OPTION_DOCUMENT_TAG_VERSION" value="false"/>
|
||||||
|
<option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true"/>
|
||||||
|
<option name="OPTION_DEPRECATED_LIST" value="true"/>
|
||||||
|
<option name="OTHER_OPTIONS" value=""/>
|
||||||
|
<option name="HEAP_SIZE"/>
|
||||||
|
<option name="LOCALE"/>
|
||||||
|
<option name="OPEN_IN_BROWSER" value="true"/>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/labswp_2019_sose_geocaching.iml" filepath="$PROJECT_DIR$/labswp_2019_sose_geocaching.iml"/>
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" assert-keyword="false" jdk-15="true" project-jdk-type="JavaSDK" assert-jdk-15="false" project-jdk-name="1.10">
|
||||||
|
<output url="file://$PROJECT_DIR$/out"/>
|
||||||
|
</component>
|
||||||
|
<component name="SvnBranchConfigurationManager">
|
||||||
|
<option name="mySupportsUserInfoFilter" value="true"/>
|
||||||
|
</component>
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs=""/>
|
||||||
|
</component>
|
||||||
|
<component name="masterDetails">
|
||||||
|
<states>
|
||||||
|
<state key="ArtifactsStructureConfigurable.UI">
|
||||||
|
<UIState>
|
||||||
|
<splitter-proportions>
|
||||||
|
<SplitterProportionsDataImpl/>
|
||||||
|
</splitter-proportions>
|
||||||
|
<settings/>
|
||||||
|
</UIState>
|
||||||
|
</state>
|
||||||
|
<state key="Copyright.UI">
|
||||||
|
<UIState>
|
||||||
|
<splitter-proportions>
|
||||||
|
<SplitterProportionsDataImpl/>
|
||||||
|
</splitter-proportions>
|
||||||
|
</UIState>
|
||||||
|
</state>
|
||||||
|
<state key="ProjectJDKs.UI">
|
||||||
|
<UIState>
|
||||||
|
<splitter-proportions>
|
||||||
|
<SplitterProportionsDataImpl>
|
||||||
|
<option name="proportions">
|
||||||
|
<list>
|
||||||
|
<option value="0.2"/>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</SplitterProportionsDataImpl>
|
||||||
|
</splitter-proportions>
|
||||||
|
<last-edited>1.6</last-edited>
|
||||||
|
</UIState>
|
||||||
|
</state>
|
||||||
|
<state key="ScopeChooserConfigurable.UI">
|
||||||
|
<UIState>
|
||||||
|
<splitter-proportions>
|
||||||
|
<SplitterProportionsDataImpl/>
|
||||||
|
</splitter-proportions>
|
||||||
|
<settings/>
|
||||||
|
</UIState>
|
||||||
|
</state>
|
||||||
|
</states>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
18
src/main/java/hhn/labsw/bugageocaching/SwaggerMarkup.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package hhn.labsw.bugageocaching;
|
||||||
|
|
||||||
|
import io.github.swagger2markup.Swagger2MarkupConverter;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
public class SwaggerMarkup {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Path localSwaggerFile = Paths.get("swagger.json");
|
||||||
|
Path outputDirectory = Paths.get("build/asciidoc");
|
||||||
|
|
||||||
|
Swagger2MarkupConverter.from(localSwaggerFile)
|
||||||
|
.build()
|
||||||
|
.toFolder(outputDirectory);
|
||||||
|
}
|
||||||
|
}
|
||||||