Compare commits

...

42 Commits

Author SHA1 Message Date
fa51b31fe4 🚀Bumped version to v0.11.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-15 19:27:59 +01:00
b9624aa5ad Added footer to every page 2023-03-15 19:27:40 +01:00
09e48e771f Accept tos to accept privacy_policy 2023-03-15 19:22:35 +01:00
8364321a7c 🚀Bumped version to v0.11.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-18 17:27:22 +01:00
659fa77dca Profile: add "---" if no data set
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-18 17:26:54 +01:00
10c9127256 🚀Bumped version to v0.11.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 17:24:42 +01:00
a3a1e89aa1 pnpm migration
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 17:24:25 +01:00
5587175534 🚀Bumped version to v0.10.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 17:19:08 +01:00
8e2b355466 updated nginx config for cache busting
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 17:18:57 +01:00
eccce0795b text cleanups + footer
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 17:14:01 +01:00
54540be6e1 improved profile page padding + full width registration code for easier scanning
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 17:04:25 +01:00
3392a2e68e 🚀Bumped version to v0.9.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 16:52:50 +01:00
8928f841dc wip: registration confirmation ui
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 16:52:38 +01:00
9ac14e8a5d wip: error registration ui feedback
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 16:48:14 +01:00
4a5b9d2569 🚀Bumped version to v0.8.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 16:44:43 +01:00
123509d0a6 fix: registration code (broke in merge)
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 16:44:10 +01:00
51f8d0fb42 wip: fix registration code
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 16:41:42 +01:00
f4d1c7b053 🚀Bumped version to v0.8.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:51:54 +01:00
ab9b400fff /profile/ dont instantly show mail format error
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:51:23 +01:00
ac75828309 /profile/ move from anchor tag to button
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:51:09 +01:00
c7f3a893af /profile/ toast styles
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:50:54 +01:00
30fd7ead08 /profile/ autocomplete mail
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:50:40 +01:00
64e6ef8cec /profile/ text cleanups
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:50:27 +01:00
c35f943957 move /registered/ to props
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-03 13:46:18 +01:00
292e44057a 🚀Bumped version to v0.8.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-02 15:54:17 +01:00
20fca6794d Adjusted button text 2023-02-02 15:53:58 +01:00
8139d63715 Switched request login link api path to login (backend v13.0.0) 2023-02-02 15:52:33 +01:00
7051909bf9 🚀Bumped version to v0.8.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-02 00:16:29 +01:00
f7a0682c33 Merge pull request 'dont autologin on register -> require mail link' (#47) from feature/46-dont-autologin-on-register-require-mail-link into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #47
2023-02-01 23:16:11 +00:00
c63adf557b 🚀Bumped version to v0.7.11
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-02 00:15:58 +01:00
077b33f031 Profile: improved mobile responsiveness/ design + toast clear
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-02 00:15:12 +01:00
52a6b3dc77 fix: registration code download button style
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-02 00:10:48 +01:00
eb20b547e7 add /registered/?mail route
ref #46
2023-02-02 00:08:14 +01:00
8b5e1cac13 🚀Bumped version to v0.7.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-01 17:50:46 +01:00
20c6a420e9 Merge branch 'dev' of git.odit.services:lfk/selfservice into dev
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-01 17:48:52 +01:00
53800b4fa3 Added download for registration code 2023-02-01 17:42:58 +01:00
debbd9219c 🚀Bumped version to v0.7.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-01 17:40:23 +01:00
9b261bf200 fix codeconfig.height
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-01 17:40:09 +01:00
713dd15312 drop jsbarcode
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-01 17:39:59 +01:00
64382880c4 Pinned version 2023-02-01 17:39:15 +01:00
9e06c46411 Configureable barcode format via bwp-js 2023-02-01 17:39:03 +01:00
8cfddb5029 migrate to bwip-js
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-01 17:36:22 +01:00
14 changed files with 4173 additions and 296 deletions

View File

@@ -2,9 +2,110 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [0.11.2](https://git.odit.services/lfk/selfservice/compare/0.11.1...0.11.2)
- Added footer to every page [`b9624aa`](https://git.odit.services/lfk/selfservice/commit/b9624aa5ad57002852c84081fc710c2e977251bd)
- Accept tos to accept privacy_policy [`09e48e7`](https://git.odit.services/lfk/selfservice/commit/09e48e771f4c0db4a7dbb898b05b347f1aad2292)
#### [0.11.1](https://git.odit.services/lfk/selfservice/compare/0.11.0...0.11.1)
> 18 February 2023
- Profile: add "---" if no data set [`659fa77`](https://git.odit.services/lfk/selfservice/commit/659fa77dca3b527d896d752386e91a167a1b8f4f)
- 🚀Bumped version to v0.11.1 [`8364321`](https://git.odit.services/lfk/selfservice/commit/8364321a7cb6a0037ee0c5055b028c4cf883fb10)
#### [0.11.0](https://git.odit.services/lfk/selfservice/compare/0.10.0...0.11.0)
> 3 February 2023
- pnpm migration [`a3a1e89`](https://git.odit.services/lfk/selfservice/commit/a3a1e89aa14798e1d180697f91809e282a229f23)
- 🚀Bumped version to v0.11.0 [`10c9127`](https://git.odit.services/lfk/selfservice/commit/10c9127256edf2cbad1dc6690d5f4e87c32f6396)
#### [0.10.0](https://git.odit.services/lfk/selfservice/compare/0.9.0...0.10.0)
> 3 February 2023
- text cleanups + footer [`eccce07`](https://git.odit.services/lfk/selfservice/commit/eccce0795b6960aa3a2e9368de5ae4bdf80997d7)
- updated nginx config for cache busting [`8e2b355`](https://git.odit.services/lfk/selfservice/commit/8e2b355466e9a37510be8bf03311d6b28e1a0d9f)
- improved profile page padding + full width registration code for easier scanning [`54540be`](https://git.odit.services/lfk/selfservice/commit/54540be6e12ed85afc96061b8548cd4ec6700eec)
- 🚀Bumped version to v0.10.0 [`5587175`](https://git.odit.services/lfk/selfservice/commit/5587175534188e05ab43d0eed9745484e01edb63)
#### [0.9.0](https://git.odit.services/lfk/selfservice/compare/0.8.3...0.9.0)
> 3 February 2023
- wip: error registration ui feedback [`9ac14e8`](https://git.odit.services/lfk/selfservice/commit/9ac14e8a5d740fb4cd87645e69b2cfde73e4acc7)
- wip: registration confirmation ui [`8928f84`](https://git.odit.services/lfk/selfservice/commit/8928f841dcd370daf28416adbf1adbe16fa4d76f)
- 🚀Bumped version to v0.9.0 [`3392a2e`](https://git.odit.services/lfk/selfservice/commit/3392a2e68edb8fcf2aa6dd4b962abbf6ac27a414)
#### [0.8.3](https://git.odit.services/lfk/selfservice/compare/0.8.2...0.8.3)
> 3 February 2023
- fix: registration code (broke in merge) [`123509d`](https://git.odit.services/lfk/selfservice/commit/123509d0a6d15fe254e28f7a316bd6c260fe7bd2)
- 🚀Bumped version to v0.8.3 [`4a5b9d2`](https://git.odit.services/lfk/selfservice/commit/4a5b9d25698931158a602b2159b4e6c4fa129e8e)
- wip: fix registration code [`51f8d0f`](https://git.odit.services/lfk/selfservice/commit/51f8d0fb42d0dbba268799eb9385686d22fd2284)
#### [0.8.2](https://git.odit.services/lfk/selfservice/compare/0.8.1...0.8.2)
> 3 February 2023
- move /registered/ to props [`c35f943`](https://git.odit.services/lfk/selfservice/commit/c35f943957e5ba84361a437c1d945331248746d1)
- 🚀Bumped version to v0.8.2 [`f4d1c7b`](https://git.odit.services/lfk/selfservice/commit/f4d1c7b053d1e7210911772bde1b2d80a30ab225)
- /profile/ text cleanups [`64e6ef8`](https://git.odit.services/lfk/selfservice/commit/64e6ef8cec38a8193c4fb28c5f1b26ee0e4d5063)
- /profile/ move from anchor tag to button [`ac75828`](https://git.odit.services/lfk/selfservice/commit/ac75828309043532c6ab8aad63e0c40edf450459)
- /profile/ toast styles [`c7f3a89`](https://git.odit.services/lfk/selfservice/commit/c7f3a893af8705af12f2e7ae7e40197ca4c28666)
- /profile/ dont instantly show mail format error [`ab9b400`](https://git.odit.services/lfk/selfservice/commit/ab9b400fff1b421a41dd27479a81fb5e3740b9ef)
- /profile/ autocomplete mail [`30fd7ea`](https://git.odit.services/lfk/selfservice/commit/30fd7ead0833b0b3ab4e5509608aa92112151380)
#### [0.8.1](https://git.odit.services/lfk/selfservice/compare/0.8.0...0.8.1)
> 2 February 2023
- 🚀Bumped version to v0.8.1 [`292e440`](https://git.odit.services/lfk/selfservice/commit/292e44057aee9ef57a51aa9fa0372c3678b81de0)
- Adjusted button text [`20fca67`](https://git.odit.services/lfk/selfservice/commit/20fca6794dd7e0c714cd09c80a68b1d3592ab09c)
- Switched request login link api path to login (backend v13.0.0) [`8139d63`](https://git.odit.services/lfk/selfservice/commit/8139d637151c8c0184e4a98f151991b429d0a70c)
#### [0.8.0](https://git.odit.services/lfk/selfservice/compare/0.7.11...0.8.0)
> 2 February 2023
- 🚀Bumped version to v0.8.0 [`7051909`](https://git.odit.services/lfk/selfservice/commit/7051909bf960fb44b43e979ac4d304dff9ef2ec4)
- Merge pull request 'dont autologin on register -> require mail link' (#47) from feature/46-dont-autologin-on-register-require-mail-link into dev [`f7a0682`](https://git.odit.services/lfk/selfservice/commit/f7a0682c3392b8882be4a676882c8a49d55bd5fe)
- add /registered/?mail route [`eb20b54`](https://git.odit.services/lfk/selfservice/commit/eb20b547e79d352f3b7cd1b5ce7b7dbfcf8c19f7)
#### [0.7.11](https://git.odit.services/lfk/selfservice/compare/0.7.10...0.7.11)
> 2 February 2023
- Profile: improved mobile responsiveness/ design + toast clear [`077b33f`](https://git.odit.services/lfk/selfservice/commit/077b33f03180d0bd6c45becaaa63d3408c645deb)
- 🚀Bumped version to v0.7.11 [`c63adf5`](https://git.odit.services/lfk/selfservice/commit/c63adf557bcb29c8eccc05d5a83d476c75380d95)
- fix: registration code download button style [`52a6b3d`](https://git.odit.services/lfk/selfservice/commit/52a6b3dc776b806eaa8fee058a1c381ab63a8ea5)
#### [0.7.10](https://git.odit.services/lfk/selfservice/compare/0.7.9...0.7.10)
> 1 February 2023
- Configureable barcode format via bwp-js [`9e06c46`](https://git.odit.services/lfk/selfservice/commit/9e06c464118c5b5d0cd78c8b8379523bf3bfdbd4)
- Added download for registration code [`53800b4`](https://git.odit.services/lfk/selfservice/commit/53800b4fa355bb972e51e71b5b1f98772deed114)
- 🚀Bumped version to v0.7.10 [`8b5e1ca`](https://git.odit.services/lfk/selfservice/commit/8b5e1cac1353373b13cf9d570deb7cadcd437247)
- Pinned version [`6438288`](https://git.odit.services/lfk/selfservice/commit/64382880c40ba5c1e3c9004ce7fc65099849cd44)
#### [0.7.9](https://git.odit.services/lfk/selfservice/compare/0.7.8...0.7.9)
> 1 February 2023
- migrate to bwip-js [`8cfddb5`](https://git.odit.services/lfk/selfservice/commit/8cfddb502964be7edf45cdc524344ea2f7f20142)
- fix codeconfig.height [`9b261bf`](https://git.odit.services/lfk/selfservice/commit/9b261bf20023561a7c9691dff33c9a6d2b5c0cac)
- 🚀Bumped version to v0.7.9 [`debbd92`](https://git.odit.services/lfk/selfservice/commit/debbd9219cb53dbd48cf0cb7bee329b765ce4647)
- drop jsbarcode [`713dd15`](https://git.odit.services/lfk/selfservice/commit/713dd153126851e8cf1045bf5ba3ca702a39c738)
#### [0.7.8](https://git.odit.services/lfk/selfservice/compare/0.7.7...0.7.8) #### [0.7.8](https://git.odit.services/lfk/selfservice/compare/0.7.7...0.7.8)
> 1 February 2023
- add barcode to profile [`851190e`](https://git.odit.services/lfk/selfservice/commit/851190e6a7f8b9cccbf05e60f9b50b96c196959c) - add barcode to profile [`851190e`](https://git.odit.services/lfk/selfservice/commit/851190e6a7f8b9cccbf05e60f9b50b96c196959c)
- 🚀Bumped version to v0.7.8 [`e5a01bc`](https://git.odit.services/lfk/selfservice/commit/e5a01bcd7629164655cacd10dd1f014260c67c4b)
- certificate generation: success toast styling [`1603a09`](https://git.odit.services/lfk/selfservice/commit/1603a097f71ed85c901baf8da04cb06b86474649) - certificate generation: success toast styling [`1603a09`](https://git.odit.services/lfk/selfservice/commit/1603a097f71ed85c901baf8da04cb06b86474649)
#### [0.7.7](https://git.odit.services/lfk/selfservice/compare/0.7.6...0.7.7) #### [0.7.7](https://git.odit.services/lfk/selfservice/compare/0.7.6...0.7.7)

View File

@@ -1,9 +1,10 @@
FROM registry.odit.services/hub/library/node:15.9.0-alpine3.13 FROM registry.odit.services/hub/library/node:19.5.0-alpine3.17
WORKDIR /app WORKDIR /app
RUN npm i -g pnpm@7
COPY . . COPY . .
RUN yarn RUN pnpm i --frozen-lockfile
RUN yarn build RUN pnpm build
RUN yarn postbuild RUN pnpm postbuild
# final image # final image
FROM registry.odit.services/library/nginx-brotli:3.15 FROM registry.odit.services/library/nginx-brotli:3.15
COPY --from=0 /app/dist /usr/share/nginx/html COPY --from=0 /app/dist /usr/share/nginx/html

View File

@@ -7,15 +7,21 @@ http {
error_page 404 /index.html; error_page 404 /index.html;
root /usr/share/nginx/html; root /usr/share/nginx/html;
location / { location / {
rewrite /register/(.*) /$1 break; rewrite /register/(.*) /$1 break;
rewrite /profile/(.*) /$1 break; rewrite /profile/(.*) /$1 break;
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
add_header Last-Modified $date_gmt;
add_header Cache-Control 'private no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
if_modified_since off;
expires off;
etag off;
} }
location ~* \.(?:ico|css|gif|jpe?g|png)$ { location ~* \.(?:ico|css|gif|jpe?g|png)$ {
rewrite /register/(.*) /$1 break; rewrite /register/(.*) /$1 break;
rewrite /profile/(.*) /$1 break; rewrite /profile/(.*) /$1 break;
expires 1y; expires 1y;
add_header Pragma public; add_header Pragma public;
@@ -35,24 +41,24 @@ http {
gzip_buffers 16 8k; gzip_buffers 16 8k;
gzip_http_version 1.1; gzip_http_version 1.1;
gzip_types application/javascript gzip_types application/javascript
application/rss+xml application/rss+xml
application/vnd.ms-fontobject application/vnd.ms-fontobject
application/x-font application/x-font
application/x-font-opentype application/x-font-opentype
application/x-font-otf application/x-font-otf
application/x-font-truetype application/x-font-truetype
application/x-font-ttf application/x-font-ttf
application/x-javascript application/x-javascript
application/xhtml+xml application/xhtml+xml
application/xml application/xml
font/opentype font/opentype
font/otf font/otf
font/ttf font/ttf
image/svg+xml image/svg+xml
image/x-icon image/x-icon
text/css text/css
text/javascript text/javascript
text/plain text/plain
text/xml; text/xml;
} }
} }

View File

@@ -1,52 +1,52 @@
{ {
"name": "@odit/lfk-selfservice", "name": "@odit/lfk-selfservice",
"version": "0.7.8", "version": "0.11.2",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"release": "release-it", "release": "release-it",
"postbuild": "node env_fix.js" "postbuild": "node env_fix.js"
}, },
"dependencies": { "dependencies": {
"jsbarcode": "^3.11.5", "bwip-js": "3.2.2",
"marked": "2.0.3", "marked": "2.0.3",
"redaxios": "0.4.1", "redaxios": "0.4.1",
"toastify-js": "1.10.0", "toastify-js": "1.10.0",
"validator": "13.5.2", "validator": "13.5.2",
"vue-i18n": "9.1.4", "vue-i18n": "9.1.4",
"vue-toastification": "2.0.0-rc.1", "vue-toastification": "2.0.0-rc.1",
"vue": "3.0.9", "vue": "3.0.9",
"vue-router": "4.0.5" "vue-router": "4.0.5"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/jit": "0.1.18", "@tailwindcss/jit": "0.1.18",
"@tailwindcss/aspect-ratio": "0.2.0", "@tailwindcss/aspect-ratio": "0.2.0",
"@tailwindcss/forms": "0.3.2", "@tailwindcss/forms": "0.3.2",
"@tailwindcss/line-clamp": "0.2.0", "@tailwindcss/line-clamp": "0.2.0",
"@tailwindcss/typography": "0.4.0", "@tailwindcss/typography": "0.4.0",
"@vitejs/plugin-vue": "1.2.1", "@vitejs/plugin-vue": "1.2.1",
"@vue/compiler-sfc": "3.0.11", "@vue/compiler-sfc": "3.0.11",
"autoprefixer": "10.2.5", "autoprefixer": "10.2.5",
"tailwindcss": "2.1.1", "tailwindcss": "2.1.1",
"release-it": "14.6.1", "release-it": "14.6.1",
"vite": "2.1.5" "vite": "2.1.5"
}, },
"release-it": { "release-it": {
"git": { "git": {
"commit": true, "commit": true,
"requireCleanWorkingDir": false, "requireCleanWorkingDir": false,
"commitMessage": "🚀Bumped version to v${version}", "commitMessage": "🚀Bumped version to v${version}",
"requireBranch": "dev", "requireBranch": "dev",
"push": true, "push": true,
"tag": true, "tag": true,
"tagName": null, "tagName": null,
"tagAnnotation": "v${version}" "tagAnnotation": "v${version}"
}, },
"npm": { "npm": {
"publish": false "publish": false
}, },
"hooks": { "hooks": {
"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node order_i18n.js && git add src/locales" "after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node order_i18n.js && git add src/locales"
} }
} }
} }

3122
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,8 +7,10 @@ const config = {
baseurl_selfservice: '/selfservice/', baseurl_selfservice: '/selfservice/',
// full url (including fqdn) // full url (including fqdn)
baseurl_documentserver: 'http://localhost:4010/documents', baseurl_documentserver: 'http://localhost:4010/documents',
// optional, will fallback to code128
code_format: 'ean13',
// optional, will fallback to baseurl_selfservice/imprint // optional, will fallback to baseurl_selfservice/imprint
url_imprint: '', url_imprint: '',
// optional, will fallback to baseurl_selfservice/privacy // optional, will fallback to baseurl_selfservice/privacy
url_privacy: '' url_privacy: '',
}; };

View File

@@ -1,11 +1,11 @@
{ {
"access_is_only_provided_via_your_email_link": "Der Zugang erfolgt nur über den Link, den Sie bei der Registrierung erhalten haben.", "access_is_only_provided_via_your_email_link": "Der Zugang erfolgt über den Link, den Sie bei der Registrierung erhalten haben.",
"already_have_an_account": "Sie haben bereits einen Account?", "already_have_an_account": "Sie haben bereits einen Account?",
"amount_per_kilometer_in_eur": "Betrag pro Kilometer (in €)", "amount_per_kilometer_in_eur": "Betrag pro Kilometer (in €)",
"apartment_suite_etc": "Addresszeile 2", "apartment_suite_etc": "Addresszeile 2",
"cancel_keep_my_data": "Abbrechen, meine Daten behalten", "cancel_keep_my_data": "Abbrechen, meine Daten behalten",
"configuration_error": "Konfigurationsfehler", "configuration_error": "Konfigurationsfehler",
"confirm_delete_all_of_my_data": "Bestätigung, meine gesamten Daten löschen", "confirm_delete_all_of_my_data": "Bestätigen, meine Daten löschen",
"confirm_personal_data": "Hiermit bestätige ich die Vollständigkeit und Richtigkeit der oben genannten Angaben", "confirm_personal_data": "Hiermit bestätige ich die Vollständigkeit und Richtigkeit der oben genannten Angaben",
"current_total_amount_in_eur": "Aktueller Gesamtbetrag (in €)", "current_total_amount_in_eur": "Aktueller Gesamtbetrag (in €)",
"delete_all_of_my_data": "Meine gesamten Daten löschen", "delete_all_of_my_data": "Meine gesamten Daten löschen",
@@ -13,6 +13,7 @@
"deletion_in_progress": "Daten werden gelöscht...", "deletion_in_progress": "Daten werden gelöscht...",
"distance": "Distanz", "distance": "Distanz",
"download_certificate": "Urkunde herunterladen", "download_certificate": "Urkunde herunterladen",
"download_registrationcode": "Registrierungscode herunterladen",
"e_mail_adress": "E-Mail Adresse", "e_mail_adress": "E-Mail Adresse",
"go_to_login": "Zum Login", "go_to_login": "Zum Login",
"i_accept": "Ich habe die ", "i_accept": "Ich habe die ",
@@ -21,7 +22,7 @@
"imprint": "Impressum", "imprint": "Impressum",
"lap_time": "Rundenzeit", "lap_time": "Rundenzeit",
"lap_times": "Rundenzeiten", "lap_times": "Rundenzeiten",
"lost_your_registration_mail": "Haben Sie Ihre Registrierungsmail verloren?", "lost_your_registration_mail": "Brauchen Sie einen neuen Login-Link?",
"main_page_text": "Hier können Sie sich für den Lauf Für Kaya! registrieren oder ihr Läuferprofil verwalten.", "main_page_text": "Hier können Sie sich für den Lauf Für Kaya! registrieren oder ihr Läuferprofil verwalten.",
"mittelname": "Mittelname", "mittelname": "Mittelname",
"nachname": "Nachname", "nachname": "Nachname",
@@ -44,7 +45,7 @@
"register_now_small": "Jetzt registrieren", "register_now_small": "Jetzt registrieren",
"registrieren": "Registrieren", "registrieren": "Registrieren",
"registrierungscode": "Registrierungscode", "registrierungscode": "Registrierungscode",
"resend_the_registration_mail": "Registrierungsmail erneut versenden", "resend_the_registration_mail": "Login-Link anfordern",
"save_changes": "Änderungen speichern", "save_changes": "Änderungen speichern",
"sponsoring": "Sponsoring", "sponsoring": "Sponsoring",
"strasse": "Straße", "strasse": "Straße",

View File

@@ -13,6 +13,7 @@
"deletion_in_progress": "Deletion in progress...", "deletion_in_progress": "Deletion in progress...",
"distance": "Distance", "distance": "Distance",
"download_certificate": "Download certificate", "download_certificate": "Download certificate",
"download_registrationcode": "Download registrationcode",
"e_mail_adress": "mail address", "e_mail_adress": "mail address",
"go_to_login": "Go To Login", "go_to_login": "Go To Login",
"i_accept": "I have read and accepted the ", "i_accept": "I have read and accepted the ",
@@ -43,7 +44,7 @@
"register_now": "Register now!", "register_now": "Register now!",
"register_now_small": "Register now", "register_now_small": "Register now",
"registrieren": "Register Now", "registrieren": "Register Now",
"resend_the_registration_mail": "Resend the registration mail", "resend_the_registration_mail": "Send me a login link",
"save_changes": "Save changes", "save_changes": "Save changes",
"sponsoring": "Sponsoring", "sponsoring": "Sponsoring",
"strasse": "Street/ Block", "strasse": "Street/ Block",

View File

@@ -9,15 +9,18 @@
<div class="mx-auto prose" v-html="content"></div> <div class="mx-auto prose" v-html="content"></div>
</div> </div>
</section> </section>
<Footer></Footer>
</template> </template>
<style src="../simple.css"> <style src="../simple.css">
</style> </style>
<script> <script>
import marked from "marked"; import marked from "marked";
import Footer from "@/components/Footer.vue";
export default { export default {
components: {Footer},
data() { data() {
return { return {
content: "" content: "",
} }
}, },
async beforeMount() { async beforeMount() {

View File

@@ -9,12 +9,15 @@
<div class="mx-auto prose" v-html="content"></div> <div class="mx-auto prose" v-html="content"></div>
</div> </div>
</section> </section>
<Footer></Footer>
</template> </template>
<style src="../simple.css"> <style src="../simple.css">
</style> </style>
<script> <script>
import marked from "marked"; import marked from "marked";
import Footer from "@/components/Footer.vue";
export default { export default {
components: {Footer},
data() { data() {
return { return {
content: "" content: ""

View File

@@ -3,19 +3,82 @@
<section class="text-white body-font"> <section class="text-white body-font">
<div class="container mx-auto flex items-center md:flex-row flex-col"> <div class="container mx-auto flex items-center md:flex-row flex-col">
<div <div
class="flex flex-col md:pr-10 md:mb-0 mb-6 pr-0 w-full md:w-auto md:text-left text-center text-black dark:text-gray-200" class="
flex flex-col
md:pr-10 md:mb-0
mb-6
pr-0
w-full
md:w-auto md:text-left
text-center text-black
dark:text-gray-200
"
> >
<p <p
class="text-3xl font-bold whitespace-nowrap" class="text-3xl font-bold whitespace-nowrap"
v-text="(state.firstname || '') + ' ' + (state.middlename || '') + ' ' + (state.lastname || '')" v-text="
(state.firstname || '') +
' ' +
(state.middlename || '') +
' ' +
(state.lastname || '')
"
></p> ></p>
<p class="text-md whitespace-nowrap">{{ state.group }}</p> <p class="text-md whitespace-nowrap">{{ state.group }}</p>
</div> </div>
<div class="inline-flex md:ml-auto md:mr-0 mx-auto items-center"> <div class="inline-flex md:ml-auto md:mr-0 mx-auto items-center">
<div v-if="(state.delete_active === false)"> <div v-if="state.delete_active === false">
<button <button
type="button" type="button"
class="focus:border-black focus:ring-2 focus:ring-black text-white text-sm py-2.5 px-5 rounded-md bg-blue-500 hover:bg-blue-600 hover:shadow-lg" class="
focus:border-black focus:ring-2 focus:ring-black
text-white text-sm
py-2.5
px-5
rounded-md
bg-blue-500
hover:bg-blue-600 hover:shadow-lg
w-full
md:w-auto
mb-1
md:mr-1
"
@click="get_registration"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="feather feather-download"
style="display: inline; height: 1rem; vertical-align: sub"
>
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" />
</svg>
{{ $t("download_registrationcode") }}
</button>
<button
type="button"
class="
focus:border-black focus:ring-2 focus:ring-black
text-white text-sm
py-2.5
px-5
rounded-md
bg-blue-500
hover:bg-blue-600 hover:shadow-lg
w-full
md:w-auto
mb-1
md:mr-1
"
@click="get_certificate" @click="get_certificate"
> >
<svg <svg
@@ -29,18 +92,32 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-download" class="feather feather-download"
style="display: inline;height: 1rem;vertical-align: sub;" style="display: inline; height: 1rem; vertical-align: sub"
> >
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="7 10 12 15 17 10" /> <polyline points="7 10 12 15 17 10" />
<line x1="12" y1="15" x2="12" y2="3" /> <line x1="12" y1="15" x2="12" y2="3" />
</svg> </svg>
{{ $t('download_certificate') }} {{ $t("download_certificate") }}
</button> </button>
<button <button
type="button" type="button"
class="focus:border-black focus:ring-2 focus:ring-black text-white text-sm py-2.5 px-5 rounded-md bg-red-600 hover:bg-red-700 hover:shadow-lg ml-1" class="
@click="() => { state.delete_active = true }" focus:border-black focus:ring-2 focus:ring-black
text-white text-sm
py-2.5
px-5
rounded-md
bg-red-600
hover:bg-red-700 hover:shadow-lg
w-full
md:w-auto
"
@click="
() => {
state.delete_active = true;
}
"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -53,7 +130,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-download" class="feather feather-download"
style="display: inline;height: 1rem;vertical-align: sub;" style="display: inline; height: 1rem; vertical-align: sub"
> >
<path d="M0 0h24v24H0z" /> <path d="M0 0h24v24H0z" />
<path <path
@@ -61,14 +138,30 @@
d="M17 6h5v2h-2v13a1 1 0 01-1 1H5a1 1 0 01-1-1V8H2V6h5V3a1 1 0 011-1h8a1 1 0 011 1v3zm1 2H6v12h12V8zm-5 6l2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2 1 1-2 2zM9 4v2h6V4H9z" d="M17 6h5v2h-2v13a1 1 0 01-1 1H5a1 1 0 01-1-1V8H2V6h5V3a1 1 0 011-1h8a1 1 0 011 1v3zm1 2H6v12h12V8zm-5 6l2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2 1 1-2 2zM9 4v2h6V4H9z"
/> />
</svg> </svg>
{{ $t('delete_my_data') }} {{ $t("delete_my_data") }}
</button> </button>
</div> </div>
<div v-if="(state.delete_active === true)"> <div v-if="state.delete_active === true">
<button <button
type="button" type="button"
class="focus:border-black focus:ring-2 focus:ring-black text-white text-sm py-2.5 px-5 rounded-md bg-blue-500 hover:bg-blue-600 hover:shadow-lg" class="
@click="() => { state.delete_active = false }" focus:border-black focus:ring-2 focus:ring-black
text-white text-sm
py-2.5
px-5
rounded-md
mb-1
md:mb-auto
w-full
md:w-auto
bg-blue-500
hover:bg-blue-600 hover:shadow-lg
"
@click="
() => {
state.delete_active = false;
}
"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -81,16 +174,30 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-download" class="feather feather-download"
style="display: inline;height: 1rem;vertical-align: sub;" style="display: inline; height: 1rem; vertical-align: sub"
> >
<path fill="none" d="M0 0h24v24H0z" /> <path fill="none" d="M0 0h24v24H0z" />
<path fill="currentColor" d="M12 11l5-5 1 1-5 5 5 5-1 1-5-5-5 5-1-1 5-5-5-5 1-1z" /> <path
fill="currentColor"
d="M12 11l5-5 1 1-5 5 5 5-1 1-5-5-5 5-1-1 5-5-5-5 1-1z"
/>
</svg> </svg>
{{ $t('cancel_keep_my_data') }} {{ $t("cancel_keep_my_data") }}
</button> </button>
<button <button
type="button" type="button"
class="focus:border-black focus:ring-2 focus:ring-black text-white text-sm py-2.5 px-5 rounded-md bg-red-600 hover:bg-red-700 hover:shadow-lg ml-1" class="
focus:border-black focus:ring-2 focus:ring-black
text-white text-sm
py-2.5
px-5
rounded-md
w-full
md:w-auto
bg-red-600
hover:bg-red-700 hover:shadow-lg
md:ml-1
"
@click="delete_me" @click="delete_me"
> >
<svg <svg
@@ -104,7 +211,7 @@
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
class="feather feather-download" class="feather feather-download"
style="display: inline;height: 1rem;vertical-align: sub;" style="display: inline; height: 1rem; vertical-align: sub"
> >
<path d="M0 0h24v24H0z" /> <path d="M0 0h24v24H0z" />
<path <path
@@ -112,81 +219,187 @@
d="M17 6h5v2h-2v13a1 1 0 01-1 1H5a1 1 0 01-1-1V8H2V6h5V3a1 1 0 011-1h8a1 1 0 011 1v3zm1 2H6v12h12V8zm-5 6l2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2 1 1-2 2zM9 4v2h6V4H9z" d="M17 6h5v2h-2v13a1 1 0 01-1 1H5a1 1 0 01-1-1V8H2V6h5V3a1 1 0 011-1h8a1 1 0 011 1v3zm1 2H6v12h12V8zm-5 6l2 2-1 1-2-2-2 2-1-1 2-2-2-2 1-1 2 2 2-2 1 1-2 2zM9 4v2h6V4H9z"
/> />
</svg> </svg>
{{ $t('confirm_delete_all_of_my_data') }} {{ $t("confirm_delete_all_of_my_data") }}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<div class="flex flex-wrap"> <div class="flex flex-wrap">
<div class="w-full p-4"> <div class="w-full">
<div class="flex flex-wrap flex-col w-full tabs"> <div class="flex flex-wrap flex-col w-full tabs">
<div class="flex lg:flex-wrap flex-row lg:space-x-2"> <div class="flex lg:flex-wrap flex-row lg:space-x-2">
<div class="flex-none"> <div class="flex-none">
<button <button
@click="() => { state.activetab = 'profile' }" @click="
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'profile') }" () => {
state.activetab = 'profile';
}
"
:class="{
'tab-active border-b-2 font-medium border-blue-500':
state.activetab === 'profile',
}"
class="tab tab-underline py-4 px-6 block" class="tab tab-underline py-4 px-6 block"
type="button" type="button"
>{{ $t('profile') }}</button> >
{{ $t("profile") }}
</button>
</div> </div>
<div class="flex-none"> <div class="flex-none">
<button <button
@click="() => { state.activetab = 'laptimes' }" @click="
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'laptimes') }" () => {
state.activetab = 'laptimes';
}
"
:class="{
'tab-active border-b-2 font-medium border-blue-500':
state.activetab === 'laptimes',
}"
class="tab tab-underline py-4 px-6 block" class="tab tab-underline py-4 px-6 block"
type="button" type="button"
>{{ $t('lap_times') }}</button> >
{{ $t("lap_times") }}
</button>
</div> </div>
<div class="flex-none"> <div class="flex-none">
<button <button
@click="() => { state.activetab = 'sponsorings' }" @click="
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'sponsorings') }" () => {
state.activetab = 'sponsorings';
}
"
:class="{
'tab-active border-b-2 font-medium border-blue-500':
state.activetab === 'sponsorings',
}"
class="tab tab-underline py-4 px-6 block" class="tab tab-underline py-4 px-6 block"
type="button" type="button"
>{{ $t('sponsoring') }}</button> >
{{ $t("sponsoring") }}
</button>
</div> </div>
</div> </div>
<div v-if="(state.activetab === 'profile')" class="tab-content block"> <div v-if="state.activetab === 'profile'" class="tab-content block">
<div class="py-4 w-full"> <div class="py-4 w-full">
<div class="flex flex-col"> <div class="flex flex-col">
<form class="form flex flex-wrap w-full"> <form class="form flex flex-wrap w-full">
<div class="w-full"> <div class="w-full">
<div class="form-element"> <div class="form-element">
<div class="text-lg">{{ $t('registrierungscode') }}</div> <div class="text-lg">{{ $t("registrierungscode") }}</div>
<img alt="Registrierungscode" :src="state.barcode"> <img
<div class="text-lg">{{ $t('vorname') }}</div> class="w-full md:w-auto mb-2"
alt="Registrierungscode"
:src="state.barcode"
/>
<div class="text-lg">{{ $t("vorname") }}</div>
<p <p
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out" class="
v-text="state.firstname" h-10
w-full
dark:bg-gray-800
rounded
text-base
outline-none
dark:text-gray-100
text-gray-600
py-1
px-3
leading-8
transition-colors
duration-200
ease-in-out
"
v-text="state.firstname || '---'"
/> />
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="text-lg">{{ $t('mittelname') }}</div> <div class="text-lg">{{ $t("mittelname") }}</div>
<p <p
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out" class="
v-text="state.middlename" h-10
w-full
dark:bg-gray-800
rounded
text-base
outline-none
dark:text-gray-100
text-gray-600
py-1
px-3
leading-8
transition-colors
duration-200
ease-in-out
"
v-text="state.middlename || '---'"
/> />
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="text-lg">{{ $t('nachname') }}</div> <div class="text-lg">{{ $t("nachname") }}</div>
<p <p
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out" class="
v-text="state.lastname" h-10
w-full
dark:bg-gray-800
rounded
text-base
outline-none
dark:text-gray-100
text-gray-600
py-1
px-3
leading-8
transition-colors
duration-200
ease-in-out
"
v-text="state.lastname || '---'"
/> />
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="text-lg">{{ $t('e_mail_adress') }}</div> <div class="text-lg">{{ $t("e_mail_adress") }}</div>
<p <p
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out" class="
v-text="state.email" h-10
w-full
dark:bg-gray-800
rounded
text-base
outline-none
dark:text-gray-100
text-gray-600
py-1
px-3
leading-8
transition-colors
duration-200
ease-in-out
"
v-text="state.email || '---'"
/> />
</div> </div>
<div class="form-element"> <div class="form-element">
<div class="text-lg">{{ $t('phone_number') }}</div> <div class="text-lg">{{ $t("phone_number") }}</div>
<p <p
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out" class="
v-text="state.phone" h-10
w-full
dark:bg-gray-800
rounded
text-base
outline-none
dark:text-gray-100
text-gray-600
py-1
px-3
leading-8
transition-colors
duration-200
ease-in-out
"
v-text="state.phone || '---'"
/> />
</div> </div>
</div> </div>
@@ -194,7 +407,7 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="(state.activetab === 'laptimes')" class="tab-content block"> <div v-if="state.activetab === 'laptimes'" class="tab-content block">
<div class="py-4 w-full"> <div class="py-4 w-full">
<section class="text-gray-400 dark:bg-gray-900 body-font"> <section class="text-gray-400 dark:bg-gray-900 body-font">
<div class="container mx-auto"> <div class="container mx-auto">
@@ -204,15 +417,37 @@
class="table-auto w-full text-left whitespace-no-wrap" class="table-auto w-full text-left whitespace-no-wrap"
> >
<thead <thead
class="text-black bg-gray-300 dark:text-white text-sm dark:bg-gray-800" class="
text-black
bg-gray-300
dark:text-white
text-sm
dark:bg-gray-800
"
> >
<tr> <tr>
<th <th
class="px-4 py-3 title-font tracking-wider font-medium" class="
>{{ $t('distance') }}</th> px-4
py-3
title-font
tracking-wider
font-medium
"
>
{{ $t("distance") }}
</th>
<th <th
class="px-4 py-3 title-font tracking-wider font-medium" class="
>{{ $t('lap_time') }}</th> px-4
py-3
title-font
tracking-wider
font-medium
"
>
{{ $t("lap_time") }}
</th>
</tr> </tr>
</thead> </thead>
<tbody class="text-gray-900 dark:text-gray-50"> <tbody class="text-gray-900 dark:text-gray-50">
@@ -224,20 +459,32 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div v-else class="text-center font-bold text-black dark:text-white text-2xl"> <div
v-else
class="
text-center
font-bold
text-black
dark:text-white
text-2xl
"
>
<img <img
src="../assets/empty_laps.svg" src="../assets/empty_laps.svg"
style="height:25rem; margin:0 auto;" style="height: 25rem; margin: 0 auto"
:alt="[[$t('no_laps_scans_were_recorded_yet')]]" :alt="[[$t('no_laps_scans_were_recorded_yet')]]"
/> />
{{ $t('no_laps_scans_were_recorded_yet') }} {{ $t("no_laps_scans_were_recorded_yet") }}
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
</div> </div>
<div v-if="(state.activetab === 'sponsorings')" class="tab-content block"> <div
v-if="state.activetab === 'sponsorings'"
class="tab-content block"
>
<div class="py-4 w-full"> <div class="py-4 w-full">
<section class="text-gray-400 dark:bg-gray-900 body-font"> <section class="text-gray-400 dark:bg-gray-900 body-font">
<div class="container mx-auto"> <div class="container mx-auto">
@@ -247,16 +494,48 @@
class="table-auto w-full text-left whitespace-no-wrap" class="table-auto w-full text-left whitespace-no-wrap"
> >
<thead <thead
class="text-black bg-gray-300 dark:text-white text-sm dark:bg-gray-800" class="
text-black
bg-gray-300
dark:text-white
text-sm
dark:bg-gray-800
"
> >
<tr> <tr>
<th class="px-4 py-3 title-font tracking-wider font-medium">Name</th>
<th <th
class="px-4 py-3 title-font tracking-wider font-medium" class="
>{{ $t('amount_per_kilometer_in_eur') }}</th> px-4
py-3
title-font
tracking-wider
font-medium
"
>
Name
</th>
<th <th
class="px-4 py-3 title-font tracking-wider font-medium" class="
>{{ $t('current_total_amount_in_eur') }}</th> px-4
py-3
title-font
tracking-wider
font-medium
"
>
{{ $t("amount_per_kilometer_in_eur") }}
</th>
<th
class="
px-4
py-3
title-font
tracking-wider
font-medium
"
>
{{ $t("current_total_amount_in_eur") }}
</th>
</tr> </tr>
</thead> </thead>
<tbody class="text-gray-900 dark:text-gray-50"> <tbody class="text-gray-900 dark:text-gray-50">
@@ -270,51 +549,86 @@
</td> </td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span <span
v-text="(s.amountPerDistance / 100) v-text="
.toFixed(2) (s.amountPerDistance / 100)
.toLocaleString('de-DE', { valute: 'EUR' })" .toFixed(2)
></span>€ .toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>€
</td> </td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span <span
v-text="(s.amount / 100) v-text="
.toFixed(2) (s.amount / 100)
.toLocaleString('de-DE', { valute: 'EUR' })" .toFixed(2)
></span>€ .toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>€
</td> </td>
</tr> </tr>
</tbody> </tbody>
<tfoot class="text-gray-900 dark:text-gray-50"> <tfoot class="text-gray-900 dark:text-gray-50">
<tr> <tr>
<td class="px-4 py-3">{{ $t('total') }}</td> <td class="px-4 py-3">{{ $t("total") }}</td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span <span
v-text="(state.sponsorings.reduce(function(sum, current) { v-text="
return sum + current.amountPerDistance; (
}, 0) / 100) state.sponsorings.reduce(function (
.toFixed(2) sum,
.toLocaleString('de-DE', { valute: 'EUR' })" current
></span>€ ) {
return sum + current.amountPerDistance;
},
0) / 100
)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>€
</td> </td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span <span
v-text="(state.sponsorings.reduce(function(sum, current) { v-text="
return sum + current.amount; (
}, 0) / 100) state.sponsorings.reduce(function (
.toFixed(2) sum,
.toLocaleString('de-DE', { valute: 'EUR' })" current
></span>€ ) {
return sum + current.amount;
},
0) / 100
)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>€
</td> </td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div v-else class="text-center font-bold text-black dark:text-white text-2xl"> <div
v-else
class="
text-center
font-bold
text-black
dark:text-white
text-2xl
"
>
<img <img
src="../assets/empty_laps.svg" src="../assets/empty_laps.svg"
style="height:25rem; margin:0 auto;" style="height: 25rem; margin: 0 auto"
:alt="[[$t('no_sponsorings_for_you_were_recorded_yet')]]" :alt="[
[$t('no_sponsorings_for_you_were_recorded_yet')],
]"
/> />
{{ $t('no_sponsorings_for_you_were_recorded_yet') }} {{ $t("no_sponsorings_for_you_were_recorded_yet") }}
</div> </div>
</div> </div>
</div> </div>
@@ -324,6 +638,8 @@
</div> </div>
</div> </div>
</div> </div>
<!-- -->
<Footer></Footer>
</div> </div>
</template> </template>
@@ -331,11 +647,27 @@
import { reactive } from "vue"; import { reactive } from "vue";
import { TYPE, useToast } from "vue-toastification"; import { TYPE, useToast } from "vue-toastification";
import axios from "redaxios"; import axios from "redaxios";
import JsBarcode from "jsbarcode"; import bwipjs from "bwip-js";
import Footer from "@/components/Footer.vue";
function textToBase64Barcode(text){ function textToBase64Barcode(text) {
var canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
JsBarcode(canvas, text, {format: "CODE128", displayValue:false}); let codeconfig = {
bcid: config.code_format || "code39",
text: `${text}`,
scale: 3,
includetext: false,
textxalign: "center",
backgroundcolor: "ffffff",
};
if (
codeconfig.bcid === "code39" ||
codeconfig.bcid === "code128" ||
codeconfig.bcid === "ean13"
) {
codeconfig.height = 10;
}
bwipjs.toCanvas(canvas, codeconfig);
return canvas.toDataURL("image/png"); return canvas.toDataURL("image/png");
} }
@@ -351,14 +683,15 @@ const state = reactive({
group: "", group: "",
activetab: "profile", activetab: "profile",
delete_active: false, delete_active: false,
fullobject: {} fullobject: {},
}) });
const toast = useToast(); const toast = useToast();
const props = defineProps({ const props = defineProps({
token: String token: String,
}) });
const accesstoken = atob(props.token); const accesstoken = atob(props.token);
axios.get(`${config.baseurl}api/runners/me/${accesstoken}`) axios
.get(`${config.baseurl}api/runners/me/${accesstoken}`)
.then(({ data }) => { .then(({ data }) => {
state.phone = data.phone; state.phone = data.phone;
state.email = data.email; state.email = data.email;
@@ -368,66 +701,100 @@ axios.get(`${config.baseurl}api/runners/me/${accesstoken}`)
state.group = data.group; state.group = data.group;
state.sponsorings = data.distanceDonations; state.sponsorings = data.distanceDonations;
state.fullobject = data; state.fullobject = data;
state.barcode = textToBase64Barcode(state.fullobject.id); state.barcode = textToBase64Barcode(data.id ?? "???");
}).catch((error) => {
toast.error("Profil konnte nicht geladen werden...");
}) })
axios.get(`${config.baseurl}api/runners/me/${accesstoken}/scans`) .catch((error) => {
toast.clear();
toast.error("Profil konnte nicht geladen werden...");
});
axios
.get(`${config.baseurl}api/runners/me/${accesstoken}/scans`)
.then(({ data }) => { .then(({ data }) => {
data.map(function(s) { data.map(function (s) {
s.lapTime = Math.floor(s.lapTime / 60) + 'min ' + (Math.floor(s.lapTime % 60) + "").padStart(2, "0") + "s" s.lapTime =
s.distance = Math.floor(s.distance / 1000) + 'km ' + (Math.floor(s.distance % 1000) + "").padStart(3, "0") + "m" Math.floor(s.lapTime / 60) +
"min " +
(Math.floor(s.lapTime % 60) + "").padStart(2, "0") +
"s";
s.distance =
Math.floor(s.distance / 1000) +
"km " +
(Math.floor(s.distance % 1000) + "").padStart(3, "0") +
"m";
return s; return s;
}) });
data.filter(s => s.valid === true); data.filter((s) => s.valid === true);
state.scans = data; state.scans = data;
}).catch((error) => {
toast.error("Profil konnte nicht geladen werden...");
}) })
.catch((error) => {
toast.error("Profil konnte nicht geladen werden...");
});
function delete_me() { function delete_me() {
toast.clear();
toast("Profil wird gelöscht..."); toast("Profil wird gelöscht...");
let url = `${config.baseurl}api/runners/me/${accesstoken}?force=true` let url = `${config.baseurl}api/runners/me/${accesstoken}?force=true`;
axios.delete(url) axios
.delete(url)
.then(() => { .then(() => {
toast.clear();
toast("Alle Daten gelöscht!"); toast("Alle Daten gelöscht!");
location.replace(`${config.baseurl_selfservice}`); location.replace(`${config.baseurl_selfservice}`);
}) })
.catch((error) => { .catch((error) => {
toast.clear();
toast.error("Profil konnte nicht gelöscht werden..."); toast.error("Profil konnte nicht gelöscht werden...");
}); });
} }
function get_certificate() { function get_certificate() {
toast("Urkunde wird generiert..."); toast("Urkunde wird generiert...");
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2); const browserlocale = (
(navigator.languages && navigator.languages[0]) ||
""
).substr(0, 2);
let url = `${config.baseurl_documentserver}certificates?locale=${browserlocale}&download=true&key=${config.documentserver_key}`; let url = `${config.baseurl_documentserver}certificates?locale=${browserlocale}&download=true&key=${config.documentserver_key}`;
let postdata = Object.assign({}, state.fullobject); let postdata = Object.assign({}, state.fullobject);
postdata.group = { postdata.group = {
name: postdata.group name: postdata.group,
} };
postdata = [postdata] postdata = [postdata];
axios.post(url, postdata, { axios
responseType: "blob" .post(url, postdata, {
}) responseType: "blob",
})
.then((response) => { .then((response) => {
console.log(response) console.log(response);
if (response.status != "200") { if (response.status != "200") {
toast.error("Urkunde konnte nicht generiert werden..."); toast.error("Urkunde konnte nicht generiert werden...");
} else { } else {
var fileURL = window.URL.createObjectURL(new Blob([response.data], { type: 'application/pdf' })); var fileURL = window.URL.createObjectURL(
var fileLink = document.createElement('a'); new Blob([response.data], { type: "application/pdf" })
);
var fileLink = document.createElement("a");
fileLink.href = fileURL; fileLink.href = fileURL;
fileLink.setAttribute('download', 'Certificate.pdf'); fileLink.setAttribute("download", "Certificate.pdf");
document.body.appendChild(fileLink); document.body.appendChild(fileLink);
fileLink.click(); fileLink.click();
fileLink.remove(); fileLink.remove();
toast("Urkunde generiert!",{type:TYPE.SUCCESS}); toast.clear();
toast("Urkunde generiert!", { type: TYPE.SUCCESS });
} }
}) })
.catch((err) => { .catch((err) => {
console.error(err); console.error(err);
toast.error("An error occured while generating your certificate"); toast.clear();
toast.error("Urkunde konnte nicht generiert werden...");
}); });
} }
function get_registration() {
toast.clear();
toast("Registrierungscode wird generiert...");
var a = document.createElement("a");
a.href = state.barcode;
a.download = "LfK23_Registrierungscode.png";
a.click();
toast.clear();
toast("Registrierungscode generiert!", { type: TYPE.SUCCESS });
}
</script> </script>

View File

@@ -6,8 +6,8 @@
class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center" class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center"
>Lauf für Kaya! - {{ $t('profile') }}</h1> >Lauf für Kaya! - {{ $t('profile') }}</h1>
<p class="mx-auto leading-relaxed text-base text-center"> <p class="mx-auto leading-relaxed text-base text-center">
{{ $t('you_have_not_provided_a_valid_access_key') }} <!-- {{ $t('you_have_not_provided_a_valid_access_key') }}
<br /> <br /> -->
{{ $t('access_is_only_provided_via_your_email_link') }} {{ $t('access_is_only_provided_via_your_email_link') }}
</p> </p>
<div class="mt-6"> <div class="mt-6">
@@ -30,24 +30,24 @@
v-model="user_email" v-model="user_email"
name="email_address" name="email_address"
id="email_address" id="email_address"
autocomplete="off" autocomplete="email"
:placeholder="[[$t('e_mail_adress')]]" :placeholder="[[$t('e_mail_adress')]]"
type="email" type="email"
:class="{ 'border-red-500': (!isEmail(user_email)), 'border-green-300': (isEmail(user_email)) }" :class="{ 'border-red-500': (!isEmail(user_email)), 'border-green-300': (isEmail(user_email)) }"
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-2 bg-gray-50 text-gray-500 rounded-md p-2" class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-2 bg-gray-50 text-gray-500 rounded-md p-2"
/> />
<p <p
v-if="!isEmail(user_email)" v-if="!isEmail(user_email)&&user_email!==''"
class="text-sm" class="text-sm"
>{{ $t('please_provide_valid_mail') }}</p> >{{ $t('please_provide_valid_mail') }}</p>
</div> </div>
<div class="mt-2"> <div class="mt-2">
<a <button
:disabled="(!state.submit_enabled)" :disabled="(!state.submit_enabled)"
:class="{ 'opacity-50': (!state.submit_enabled), 'cursor-not-allowed': (!state.submit_enabled) }" :class="{ 'opacity-50': (!state.submit_enabled), 'cursor-not-allowed': (!state.submit_enabled) }"
@click="resendMail" @click="resendMail"
class="block w-full text-center py-2 px-3 border-2 border-gray-300 rounded-md p-1 dark:bg-gray-800 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm" class="block w-full text-center py-2 px-3 border-2 border-gray-300 rounded-md p-1 dark:bg-gray-800 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
>{{ $t('resend_the_registration_mail') }}</a> >{{ $t('resend_the_registration_mail') }}</button>
</div> </div>
</div> </div>
<div class="mt-12"> <div class="mt-12">
@@ -68,6 +68,7 @@
</div> </div>
</div> </div>
</div> </div>
<Footer></Footer>
</template> </template>
<script setup> <script setup>
@@ -76,7 +77,8 @@ import axios from "redaxios";
import isEmail from 'validator/es/lib/isEmail'; import isEmail from 'validator/es/lib/isEmail';
import isMobilePhone from 'validator/es/lib/isMobilePhone'; import isMobilePhone from 'validator/es/lib/isMobilePhone';
import isPostalCode from 'validator/es/lib/isPostalCode'; import isPostalCode from 'validator/es/lib/isPostalCode';
import { useToast } from "vue-toastification"; import { TYPE, useToast } from "vue-toastification";
import Footer from "@/components/Footer.vue";
let user_email = ref(""); let user_email = ref("");
// //
@@ -90,14 +92,14 @@ function resendMail() {
if (isEmail(user_email.value)) { if (isEmail(user_email.value)) {
toast("Login-Link wird angefordert..."); toast("Login-Link wird angefordert...");
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2); const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2);
axios.post(`${config.baseurl}api/runners/forgot?mail=${user_email.value}&locale=${browserlocale}`) axios.post(`${config.baseurl}api/runners/login?mail=${user_email.value}&locale=${browserlocale}`)
.then(({ data }) => { .then(({ data }) => {
console.log(data); console.log(data);
toast("Login-Link gesendet an " + user_email.value + "!"); toast("Login-Link gesendet an " + user_email.value + "!");
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
toast("Fehler beim Anfordern des Login-Links..."); toast("Fehler beim Anfordern des Login-Links...", { type: TYPE.ERROR });
}); });
} }
} }

View File

@@ -1,33 +1,73 @@
<template> <template>
<div class="min-h-screen flex items-center justify-center"> <div
class="min-h-screen flex items-center justify-center"
v-if="registrationState === 'registered'"
>
<div class="max-w-md w-full py-12 px-6"> <div class="max-w-md w-full py-12 px-6">
<img class="mx-auto h-24 w-auto" src="/favicon.png" alt /> <img class="mx-auto h-24 w-auto" src="/favicon.png" alt />
<h1 <h1 class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center">
class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center" Lauf für Kaya! - Registriert
>Lauf für Kaya! - {{ $t('registrieren') }}</h1> </h1>
<p class="mx-auto leading-relaxed text-base text-center">{{ $t('register.register_now') }}</p> <p class="mx-auto leading-relaxed text-base text-center">
Bitte klicken Sie zum Fortfahren auf den Link, den wir an
<b class="font-bold">{{ userdetails.mail }}</b> geschickt haben.
</p>
</div>
</div>
<div class="min-h-screen flex items-center justify-center" v-else>
<div class="max-w-md w-full py-12 px-6">
<img class="mx-auto h-24 w-auto" src="/favicon.png" alt />
<h1 class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center">
Lauf für Kaya! - {{ $t("registrieren") }}
</h1>
<p class="mx-auto leading-relaxed text-base text-center">
{{ $t("register.register_now") }}
</p>
<p <p
v-if="state.org_name !== ''" v-if="state.org_name !== ''"
class="mx-auto leading-relaxed text-base text-center" class="mx-auto leading-relaxed text-base text-center"
>{{ $t('organization') }}: {{ state.org_name }}</p> >
{{ $t("organization") }}: {{ state.org_name }}
</p>
<p <p
v-if="state.org_name !== '' && state.org_teams.length > 0" v-if="state.org_name !== '' && state.org_teams.length > 0"
class="mx-auto leading-relaxed text-base text-center" class="mx-auto leading-relaxed text-base text-center"
>Team:</p> >
Team:
</p>
<select <select
v-model="org_team" v-model="org_team"
v-if="state.org_name !== '' && state.org_teams.length > 0" v-if="state.org_name !== '' && state.org_teams.length > 0"
class="w-full border bg-white rounded px-3 py-2 outline-none block mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 form-select focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray" class="
w-full
border
bg-white
rounded
px-3
py-2
outline-none
block
mt-1
text-sm
dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700
form-select
focus:border-purple-400 focus:outline-none focus:shadow-outline-purple
dark:focus:shadow-outline-gray
"
> >
<option v-for="t in state.org_teams" :key="t.id" :value="t.id">{{ t.name }}</option> <option v-for="t in state.org_teams" :key="t.id" :value="t.id">
{{ t.name }}
</option>
</select> </select>
<p <p
v-if="state.org_name === ''" v-if="state.org_name === ''"
class="mx-auto leading-relaxed text-base text-center" class="mx-auto leading-relaxed text-base text-center"
>Bürgerlauf</p> >
Bürgerlauf
</p>
<div class="mt-4"> <div class="mt-4">
<label for="first_name" class="block font-medium"> <label for="first_name" class="block font-medium">
{{ $t('vorname') }} {{ $t("vorname") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
<input <input
@@ -37,11 +77,28 @@
autocomplete="off" autocomplete="off"
:placeholder="[[$t('vorname')]]" :placeholder="[[$t('vorname')]]"
type="text" type="text"
:class="{ 'border-red-500': (!userdetails.firstname.trim()), 'border-green-300': (userdetails.firstname.trim()) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500': !userdetails.firstname.trim(),
'border-green-300': userdetails.firstname.trim(),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
<!-- --> <!-- -->
<label for="middle_name" class="block font-medium">{{ $t('mittelname') }}</label> <label for="middle_name" class="block font-medium">{{
$t("mittelname")
}}</label>
<input <input
v-model="userdetails.middlename" v-model="userdetails.middlename"
name="middlename" name="middlename"
@@ -49,11 +106,23 @@
autocomplete="off" autocomplete="off"
:placeholder="[[$t('mittelname')]]" :placeholder="[[$t('mittelname')]]"
type="text" type="text"
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
<!-- --> <!-- -->
<label for="last_name" class="block font-medium"> <label for="last_name" class="block font-medium">
{{ $t('nachname') }} {{ $t("nachname") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
<input <input
@@ -63,12 +132,27 @@
autocomplete="off" autocomplete="off"
:placeholder="[[$t('nachname')]]" :placeholder="[[$t('nachname')]]"
type="text" type="text"
:class="{ 'border-red-500': (!userdetails.lastname.trim()), 'border-green-300': (userdetails.lastname.trim()) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500': !userdetails.lastname.trim(),
'border-green-300': userdetails.lastname.trim(),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
<!-- --> <!-- -->
<label for="email_address" class="block font-medium"> <label for="email_address" class="block font-medium">
{{ $t('e_mail_adress') }} {{ $t("e_mail_adress") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
<input <input
@@ -78,12 +162,31 @@
autocomplete="off" autocomplete="off"
:placeholder="[[$t('e_mail_adress')]]" :placeholder="[[$t('e_mail_adress')]]"
type="email" type="email"
:class="{ 'border-red-500': (!isEmail(userdetails.mail)), 'border-green-300': (isEmail(userdetails.mail)) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500': !isEmail(userdetails.mail),
'border-green-300': isEmail(userdetails.mail),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
<p v-if="!isEmail(userdetails.mail)" class="text-sm">{{ $t('please_provide_valid_mail') }}</p> <p v-if="!isEmail(userdetails.mail)" class="text-sm">
{{ $t("please_provide_valid_mail") }}
</p>
<!-- --> <!-- -->
<label for="phone" class="select-none block font-medium">{{ $t('phone_number') }}</label> <label for="phone" class="select-none block font-medium">{{
$t("phone_number")
}}</label>
<input <input
v-model="userdetails.phone" v-model="userdetails.phone"
name="phone" name="phone"
@@ -91,13 +194,32 @@
autocomplete="off" autocomplete="off"
:placeholder="[[$t('phone_number')]]" :placeholder="[[$t('phone_number')]]"
type="text" type="text"
:class="{ 'border-red-500': (!isMobilePhone(userdetails.phone) && userdetails.phone.trim()), 'border-green-300': (isMobilePhone(userdetails.phone) && userdetails.phone.trim()) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500':
!isMobilePhone(userdetails.phone) && userdetails.phone.trim(),
'border-green-300':
isMobilePhone(userdetails.phone) && userdetails.phone.trim(),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
<p <p
v-if="(!isMobilePhone(userdetails.phone) && userdetails.phone.trim())" v-if="!isMobilePhone(userdetails.phone) && userdetails.phone.trim()"
class="text-sm" class="text-sm"
>{{ $t('this_is_not_a_valid_international_phone_number') }}</p> >
{{ $t("this_is_not_a_valid_international_phone_number") }}
</p>
<!-- --> <!-- -->
<div class="grid grid-cols-6 mt-6"> <div class="grid grid-cols-6 mt-6">
<div class="col-span-6"></div> <div class="col-span-6"></div>
@@ -115,13 +237,14 @@
<label <label
for="address_activated" for="address_activated"
class="font-medium text-gray-400 select-none" class="font-medium text-gray-400 select-none"
>{{ $t('provide_address') }}</label> >{{ $t("provide_address") }}</label
>
</div> </div>
</div> </div>
<div v-if="provide_address === true" class="col-span-6"> <div v-if="provide_address === true" class="col-span-6">
<div class="col-span-6"> <div class="col-span-6">
<label for="street" class="block font-medium"> <label for="street" class="block font-medium">
{{ $t('strasse') }} {{ $t("strasse") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
<input <input
@@ -131,12 +254,29 @@
:placeholder="[[$t('strasse')]]" :placeholder="[[$t('strasse')]]"
id="street" id="street"
autocomplete="street-address" autocomplete="street-address"
:class="{ 'border-red-500': (!userdetails.address.street.trim()), 'border-green-300': (userdetails.address.street.trim()) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500': !userdetails.address.street.trim(),
'border-green-300': userdetails.address.street.trim(),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
</div> </div>
<div class="col-span-6"> <div class="col-span-6">
<label for="address2" class="block font-medium">{{ $t('apartment_suite_etc') }}</label> <label for="address2" class="block font-medium">{{
$t("apartment_suite_etc")
}}</label>
<input <input
v-model="userdetails.address.address2" v-model="userdetails.address.address2"
type="text" type="text"
@@ -144,12 +284,24 @@
:placeholder="[[$t('apartment_suite_etc')]]" :placeholder="[[$t('apartment_suite_etc')]]"
id="address2" id="address2"
autocomplete="street-address" autocomplete="street-address"
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
</div> </div>
<div class="col-span-6 sm:col-span-6 lg:col-span-2"> <div class="col-span-6 sm:col-span-6 lg:col-span-2">
<label for="city" class="block font-medium"> <label for="city" class="block font-medium">
{{ $t('ort') }} {{ $t("ort") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
<input <input
@@ -158,13 +310,28 @@
name="city" name="city"
:placeholder="[[$t('ort')]]" :placeholder="[[$t('ort')]]"
id="city" id="city"
:class="{ 'border-red-500': (!userdetails.address.city.trim()), 'border-green-300': (userdetails.address.city.trim()) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500': !userdetails.address.city.trim(),
'border-green-300': userdetails.address.city.trim(),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
</div> </div>
<div class="col-span-6 sm:col-span-3 lg:col-span-2"> <div class="col-span-6 sm:col-span-3 lg:col-span-2">
<label for="postal_code" class="block font-medium"> <label for="postal_code" class="block font-medium">
{{ $t('plz') }} {{ $t("plz") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
<input <input
@@ -174,14 +341,37 @@
:placeholder="[[$t('plz')]]" :placeholder="[[$t('plz')]]"
id="postal_code" id="postal_code"
autocomplete="postal-code" autocomplete="postal-code"
:class="{ 'border-red-500': (!isPostalCode(userdetails.address.zipcode, 'DE')), 'border-green-300': (isPostalCode(userdetails.address.zipcode, 'DE')) }" :class="{
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2" 'border-red-500': !isPostalCode(
userdetails.address.zipcode,
'DE'
),
'border-green-300': isPostalCode(
userdetails.address.zipcode,
'DE'
),
}"
class="
dark:bg-gray-800
mt-1
block
w-full
shadow-sm
sm:text-sm
border-gray-300 border-2
bg-gray-50
text-gray-500
rounded-md
p-2
"
/> />
</div> </div>
<p <p
v-if="!isPostalCode(userdetails.address.zipcode, 'DE')" v-if="!isPostalCode(userdetails.address.zipcode, 'DE')"
class="text-sm" class="text-sm"
>{{ $t('please_provide_a_valid_zipcode') }}</p> >
{{ $t("please_provide_a_valid_zipcode") }}
</p>
</div> </div>
</div> </div>
<div class="flex items-start mt-6"> <div class="flex items-start mt-6">
@@ -195,15 +385,19 @@
/> />
</div> </div>
<div class="ml-3 text-sm"> <div class="ml-3 text-sm">
<label for="agb_accepted" class="font-medium text-gray-400 select-none"> <label
{{ $t('i_accept', { tos: $t('tos') }) }} for="agb_accepted"
class="font-medium text-gray-400 select-none"
>
{{ $t("i_accept", { tos: $t("privacy_policy") }) }}
<a <a
target="_blank" target="_blank"
rel="noreferrer,noopener" rel="noreferrer,noopener"
href="https://lauf-fuer-kaya.de/datenschutz/" href="https://lauf-fuer-kaya.de/datenschutz/"
class="underline" class="underline"
>{{ $t('tos') }}</a> >{{ $t("privacy_policy") }}</a
{{ $t('i_accept_end') }} >
{{ $t("i_accept_end") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
</div> </div>
@@ -219,8 +413,11 @@
/> />
</div> </div>
<div class="ml-3 text-sm"> <div class="ml-3 text-sm">
<label for="data_confirmed" class="font-medium text-gray-400 select-none"> <label
{{ $t('confirm_personal_data') }} for="data_confirmed"
class="font-medium text-gray-400 select-none"
>
{{ $t("confirm_personal_data") }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
</div> </div>
@@ -228,29 +425,52 @@
<div class="mt-6"> <div class="mt-6">
<button <button
@click="login" @click="login"
:disabled="(!state.submit_enabled)" :disabled="!state.submit_enabled"
:class="{ 'opacity-50': (!state.submit_enabled), 'cursor-not-allowed': (!state.submit_enabled) }" :class="{
class="text-white block w-full text-center py-2 px-3 border-2 border-gray-300 rounded-md p-1 bg-blue-800 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm" 'opacity-50': !state.submit_enabled,
>{{ $t('registrieren') }}</button> 'cursor-not-allowed': !state.submit_enabled,
}"
class="
text-white
block
w-full
text-center
py-2
px-3
border-2 border-gray-300
rounded-md
p-1
bg-blue-800
font-medium
hover:border-gray-400
focus:outline-none focus:border-gray-400
sm:text-sm
"
>
{{ $t("registrieren") }}
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<Footer></Footer>
</template> </template>
<script setup> <script setup>
import { computed, ref, reactive, defineProps } from "vue"; import { computed, ref, reactive, defineProps } from "vue";
import axios from "redaxios"; import axios from "redaxios";
import isEmail from 'validator/es/lib/isEmail'; import isEmail from "validator/es/lib/isEmail";
import isMobilePhone from 'validator/es/lib/isMobilePhone'; import isMobilePhone from "validator/es/lib/isMobilePhone";
import isPostalCode from 'validator/es/lib/isPostalCode'; import isPostalCode from "validator/es/lib/isPostalCode";
import { useToast } from "vue-toastification"; import { TYPE, useToast } from "vue-toastification";
import Footer from "@/components/Footer.vue";
const props = defineProps({ const props = defineProps({
token: String token: String,
}) });
if (props.token) { if (props.token) {
axios.get(`${config.baseurl}api/organizations/selfservice/${props.token}`) axios
.get(`${config.baseurl}api/organizations/selfservice/${props.token}`)
.then(({ data }) => { .then(({ data }) => {
state.org_name = data.name; state.org_name = data.name;
state.org_teams = data.teams; state.org_teams = data.teams;
@@ -261,31 +481,51 @@ if (props.token) {
}); });
} }
let userdetails = ref({ firstname: "", lastname: "", middlename: "", mail: "", phone: "", address: { street: "", address2: "", city: "", zipcode: "" } }); let userdetails = ref({
firstname: "",
lastname: "",
middlename: "",
mail: "",
phone: "",
address: { street: "", address2: "", city: "", zipcode: "" },
});
let provide_address = ref(false); let provide_address = ref(false);
let agb_accepted = ref(false); let agb_accepted = ref(false);
let data_confirmed = ref(false); let data_confirmed = ref(false);
let org_team = ref(""); let org_team = ref("");
// let registrationState = ref("pending");
//
const state = reactive({ const state = reactive({
org_name: "", org_name: "",
org_teams: [], org_teams: [],
submit_enabled: computed(() => agb_accepted.value === true && data_confirmed.value === true && (isMobilePhone(userdetails.value.phone) || !userdetails.value.phone.trim()) && isEmail(userdetails.value.mail) submit_enabled: computed(
&& userdetails.value.firstname () =>
&& userdetails.value.lastname && (provide_address.value === false || provide_address.value === true && (userdetails.value.address.street.trim() && userdetails.value.address.city.trim() && isPostalCode(userdetails.value.address.zipcode, "DE")))) agb_accepted.value === true &&
}) data_confirmed.value === true &&
(isMobilePhone(userdetails.value.phone) ||
!userdetails.value.phone.trim()) &&
isEmail(userdetails.value.mail) &&
userdetails.value.firstname &&
userdetails.value.lastname &&
(provide_address.value === false ||
(provide_address.value === true &&
userdetails.value.address.street.trim() &&
userdetails.value.address.city.trim() &&
isPostalCode(userdetails.value.address.zipcode, "DE")))
),
});
const toast = useToast(); const toast = useToast();
function login() { function login() {
userdetails = userdetails.value; userdetails = userdetails.value;
if (userdetails?.phone === "" || isMobilePhone(userdetails.phone)) { if (userdetails?.phone === "" || isMobilePhone(userdetails.phone)) {
if (isEmail(userdetails.mail)) { if (isEmail(userdetails.mail)) {
let postdata = { let postdata = {
"email": userdetails.mail, email: userdetails.mail,
"firstname": userdetails.firstname, firstname: userdetails.firstname,
"middlename": userdetails.middlename, middlename: userdetails.middlename,
"lastname": userdetails.lastname, lastname: userdetails.lastname,
"address": {} address: {},
} };
if (isMobilePhone(userdetails.phone)) { if (isMobilePhone(userdetails.phone)) {
postdata.phone = userdetails.phone; postdata.phone = userdetails.phone;
} }
@@ -296,25 +536,31 @@ function login() {
city: userdetails.address.city, city: userdetails.address.city,
postalcode: userdetails.address.zipcode, postalcode: userdetails.address.zipcode,
country: "DE", country: "DE",
} };
} }
if (state.org_name !== '' && state.org_teams.length > 0) { if (state.org_name !== "" && state.org_teams.length > 0) {
postdata.team = org_team.value; postdata.team = org_team.value;
} }
toast("Registrierung läuft..."); toast("Registrierung läuft...");
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2); const browserlocale = (
(navigator.languages && navigator.languages[0]) ||
""
).substr(0, 2);
let url = `${config.baseurl}api/runners/register/?locale=${browserlocale}`; let url = `${config.baseurl}api/runners/register/?locale=${browserlocale}`;
if (props.token) { if (props.token) {
url = `${config.baseurl}api/runners/register/${props.token}/?locale=${browserlocale}` url = `${config.baseurl}api/runners/register/${props.token}/?locale=${browserlocale}`;
} }
axios.post(url, postdata) registrationState.value = "loading";
.then(({ data }) => { axios
const token = btoa(data.token); .post(url, postdata)
// alert(token); .then(() => {
location.replace(`${config.baseurl_selfservice}profile/${token}`); registrationState.value = "registered";
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
if (error.data.message === "E-Mail already registered") {
toast("bereits registriert...", { type: TYPE.ERROR });
}
}); });
} }
} }

22
src/views/Registered.vue Normal file
View File

@@ -0,0 +1,22 @@
<template>
<div class="min-h-screen flex items-center justify-center">
<div class="max-w-md w-full py-12 px-6">
<img class="mx-auto h-24 w-auto" src="/favicon.png" alt />
<h1 class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center">
Lauf für Kaya! - Registriert
</h1>
<p class="mx-auto leading-relaxed text-base text-center">
Bitte klicken Sie zum Fortfahren auf den Link, den wir an
<b class="font-bold">{{ $props.mail }}</b> geschickt haben.
</p>
</div>
</div>
<Footer></Footer>
</template>
<script setup>
import Footer from "@/components/Footer.vue";
const props = defineProps({
mail: String,
});
</script>