Added Asciidoctor for Backend Dokumentation

This commit is contained in:
Maximilian Leopold 2019-06-13 12:56:47 +02:00
parent b42968b4d7
commit 0ef3b891e1
85 changed files with 24085 additions and 1 deletions

View File

@ -1,6 +1,7 @@
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
id 'org.asciidoctor.jvm.convert' version '2.2.0'
}
apply plugin: 'io.spring.dependency-management'
@ -13,6 +14,8 @@ sourceCompatibility = '1.8'
repositories {
mavenCentral()
jcenter()
maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' }
}
bootWar{
@ -73,6 +76,6 @@ dependencies {
testCompile group: 'junit', name: 'junit', version: '4.+'
compile "io.github.swagger2markup:swagger2markup:1.3.1"
}

9
frontend/.editorconfig Normal file
View 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
View File

@ -0,0 +1,2 @@
/dist
quasar.conf.js

41
frontend/.eslintrc.js Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
# Quasar App
> WIP

5
frontend/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@quasar/babel-preset-app'
]
}

10204
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

41
frontend/package.json Normal file
View 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
View 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
View File

@ -0,0 +1,13 @@
<template>
<div id="q-app">
<router-view />
</div>
</template>
<script>
export default {
name: "App"
};
</script>
<style></style>

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

View 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
View 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);
}

View 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);
}

View File

View 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>

View 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>

View File

@ -0,0 +1 @@
// app global css

View 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

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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;
}

View 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;

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 MiB

View File

@ -0,0 +1,4 @@
/*
export function someAction (context) {
}
*/

View 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);
// }

View 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
}

View 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;
}
};

View 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"
// }
// ]
// },
}

View File

@ -0,0 +1,4 @@
/*
export function someAction (context) {
}
*/

View 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;
};

View 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
}

View 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;
}
}
});
};

View 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: "",
},
}

View File

@ -0,0 +1,4 @@
/*
export function someAction (context) {
}
*/

View File

@ -0,0 +1,4 @@
/*
export function someGetter (state) {
}
*/

View 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
}

View File

@ -0,0 +1,4 @@
/*
export function someMutation (state) {
}
*/

View File

@ -0,0 +1,5 @@
export default {
cache: {},
currentCacheID: null,
currentStationID: null,
}

View File

@ -0,0 +1,4 @@
/*
export function someAction (context) {
}
*/

View File

@ -0,0 +1,4 @@
/*
export function someGetter (state) {
}
*/

View 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
}

View 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;
}

View 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."
},
}

View 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
};

View File

@ -0,0 +1 @@
export function someAction(/* context */) {}

View File

@ -0,0 +1 @@
export function someGetter(/* state */) {}

View 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
};

View File

@ -0,0 +1 @@
export function someMutation(/* state */) {}

View File

@ -0,0 +1,3 @@
export default {
//
};

7467
frontend/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View 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>

View 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);
}
}

1676
swagger.json Normal file

File diff suppressed because it is too large Load Diff