Merge branch 'dev'
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Philipp Dormann 2023-02-02 00:21:22 +01:00
commit fe1bbc7816
Signed by: philipp
GPG Key ID: 3BB9ADD52DCA4314
19 changed files with 4767 additions and 740 deletions

View File

@ -1,109 +1,117 @@
--- ---
kind: secret kind: secret
name: docker_username name: docker_username
get: get:
path: odit-registry-builder path: odit-registry-builder
name: username name: username
--- ---
kind: secret kind: secret
name: docker_password name: docker_password
get: get:
path: odit-registry-builder path: odit-registry-builder
name: password name: password
--- ---
kind: secret kind: secret
name: git_ssh name: git_ssh
get: get:
path: odit-git-bot path: odit-git-bot
name: sshkey name: sshkey
--- ---
kind: pipeline kind: pipeline
type: kubernetes type: kubernetes
name: build:dev name: build:dev
steps: steps:
# - name: run full license export - name: build dev
# depends_on: ["clone"] image: plugins/docker
# image: node:alpine depends_on: [clone]
# commands: settings:
# - yarn username:
# - yarn licenses:export from_secret: docker_username
# - name: push new licenses file to repo password:
# depends_on: ["run full license export"] from_secret: docker_password
# image: appleboy/drone-git-push repo: registry.odit.services/lfk/selfservice
# settings: tags:
# branch: dev - dev
# commit: true registry: registry.odit.services
# commit_message: new license file version [CI SKIP] mtu: 1000
# author_email: bot@odit.services trigger:
# remote: git@git.odit.services:lfk/selfservice.git branch:
# ssh_key: - dev
# from_secret: git_ssh event:
- name: build dev - push
image: plugins/docker
depends_on: [clone] ---
settings: kind: pipeline
username: type: kubernetes
from_secret: docker_username name: build:testing
password:
from_secret: docker_password steps:
repo: registry.odit.services/lfk/selfservice - name: build testing
tags: image: plugins/docker
- dev depends_on: [clone]
registry: registry.odit.services settings:
mtu: 1000 username:
trigger: from_secret: docker_username
branch: password:
- dev from_secret: docker_password
event: repo: registry.odit.services/lfk/selfservice
- push tags:
- testing
--- registry: registry.odit.services
kind: pipeline mtu: 1000
type: kubernetes trigger:
name: build:latest branch:
- bugfix/31-env_linking
steps: event:
- name: build latest - push
image: plugins/docker
depends_on: [clone] ---
settings: kind: pipeline
username: type: kubernetes
from_secret: docker_username name: build:latest
password:
from_secret: docker_password steps:
repo: registry.odit.services/lfk/selfservice - name: build latest
tags: image: plugins/docker
- latest depends_on: [clone]
registry: registry.odit.services settings:
mtu: 1000 username:
trigger: from_secret: docker_username
branch: password:
- main from_secret: docker_password
event: repo: registry.odit.services/lfk/selfservice
- push tags:
- latest
--- registry: registry.odit.services
kind: pipeline mtu: 1000
type: kubernetes trigger:
name: build:tags branch:
steps: - main
- name: build $DRONE_TAG event:
image: plugins/docker - push
depends_on: [clone]
settings: ---
username: kind: pipeline
from_secret: docker_username type: kubernetes
password: name: build:tags
from_secret: docker_password steps:
repo: registry.odit.services/lfk/selfservice - name: build $DRONE_TAG
tags: image: plugins/docker
- '${DRONE_TAG}' depends_on: [clone]
registry: registry.odit.services settings:
mtu: 1000 username:
trigger: from_secret: docker_username
event: password:
from_secret: docker_password
repo: registry.odit.services/lfk/selfservice
tags:
- '${DRONE_TAG}'
registry: registry.odit.services
mtu: 1000
trigger:
event:
- tag - tag

View File

@ -2,9 +2,180 @@
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.8.0](https://git.odit.services/lfk/selfservice/compare/0.7.11...0.8.0)
- 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)
> 1 February 2023
- 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)
#### [0.7.7](https://git.odit.services/lfk/selfservice/compare/0.7.6...0.7.7)
> 31 January 2023
- 🚀Bumped version to v0.7.7 [`c2b6152`](https://git.odit.services/lfk/selfservice/commit/c2b615294e605db37695b13cec1158f535986911)
- fix: registration w/o phone [`c647628`](https://git.odit.services/lfk/selfservice/commit/c64762831f1e6dffc9cbc3f531e23435b455a5a9)
#### [0.7.6](https://git.odit.services/lfk/selfservice/compare/0.7.5...0.7.6)
> 31 January 2023
- update texts of pdf generation status toasts [`#44`](https://git.odit.services/lfk/selfservice/issues/44)
- 🚀Bumped version to v0.7.6 [`9b446ab`](https://git.odit.services/lfk/selfservice/commit/9b446abc1fa231bb1f5a78c545400c617eaa4af5)
- update release script [`5d974e5`](https://git.odit.services/lfk/selfservice/commit/5d974e562ed1ed5aeac579afe000c2dca945ff71)
#### [0.7.5](https://git.odit.services/lfk/selfservice/compare/0.7.4...0.7.5)
> 30 January 2023
- fix: relativ links [`917cb6b`](https://git.odit.services/lfk/selfservice/commit/917cb6be340844bcc2318bf73cec37c3c831fd5d)
- 🚀Bumped version to v0.7.5 [`1249248`](https://git.odit.services/lfk/selfservice/commit/1249248a9d3e0d72665bca6871a651f2491a4039)
- 2023 [`9812d79`](https://git.odit.services/lfk/selfservice/commit/9812d79d4de820ce791f69634c5861f4f04ad7f1)
- update nginx base [`0bd6d54`](https://git.odit.services/lfk/selfservice/commit/0bd6d543bf60b7a333b96d5d319269d4bf50db96)
- updated base node image [`92d7bfd`](https://git.odit.services/lfk/selfservice/commit/92d7bfd59407273f86809b53ffc9f67fb8ba0ec7)
- AGB link [`d159cb5`](https://git.odit.services/lfk/selfservice/commit/d159cb59be35f963abc89c42732ba6110d27830c)
#### [0.7.4](https://git.odit.services/lfk/selfservice/compare/0.7.3...0.7.4)
> 21 April 2021
- Updated dronefile [`9c9ceaa`](https://git.odit.services/lfk/selfservice/commit/9c9ceaa6664f6ea21a536a12f04e552156e81da4)
- 🚀Bumped version to v0.7.4 [`4663214`](https://git.odit.services/lfk/selfservice/commit/4663214ede3880386b5389885e32c953571290e6)
#### [0.7.3](https://git.odit.services/lfk/selfservice/compare/0.7.2...0.7.3)
> 21 April 2021
- 🚀Bumped version to v0.7.3 [`602d80b`](https://git.odit.services/lfk/selfservice/commit/602d80bd14c4bd00a24b746be1e74d9e41af0445)
- Merge pull request 'Button fixes bugfix/42-button_links' (#43) from bugfix/42-button_links into dev [`45ee4ab`](https://git.odit.services/lfk/selfservice/commit/45ee4ab81260adf5b938d9f5359d256cce879acb)
- Removed useless register now button [`1102d29`](https://git.odit.services/lfk/selfservice/commit/1102d29c0e174b7a34fc4d3e6fe32d2dfb276765)
- Fixed register button link [`020c310`](https://git.odit.services/lfk/selfservice/commit/020c310865912b8f0752069e1c7e2adf71ab9835)
#### [0.7.2](https://git.odit.services/lfk/selfservice/compare/0.7.1...0.7.2)
> 14 April 2021
- 🚀Bumped version to v0.7.2 [`1a3af20`](https://git.odit.services/lfk/selfservice/commit/1a3af200dd41c8cc4271690ed72bef911901ce54)
- Document generation hotfix 🐞 [`b74bea0`](https://git.odit.services/lfk/selfservice/commit/b74bea03401c672ae774aaddc6da5beb67e2890e)
#### [0.7.1](https://git.odit.services/lfk/selfservice/compare/0.7.0...0.7.1)
> 13 April 2021
- Merge pull request 'bugfix/31-env-js-linking-ci' (#41) from bugfix/31-env-js-linking-ci into dev [`#31`](https://git.odit.services/lfk/selfservice/issues/31)
- ⏫ dependency bump [`ad13bae`](https://git.odit.services/lfk/selfservice/commit/ad13bae068416bed10d00e6887a05d580a836482)
- 🚀Bumped version to v0.7.1 [`b0172c5`](https://git.odit.services/lfk/selfservice/commit/b0172c500b0613209ac44e61023043065b3854b0)
- added 'yarn postbuild' script for fixing env.js in dist/index.html [`51d058b`](https://git.odit.services/lfk/selfservice/commit/51d058bf966c3dcb064562e6bf696a748d0cd148)
#### [0.7.0](https://git.odit.services/lfk/selfservice/compare/0.6.1...0.7.0)
> 6 April 2021
- 🚀Bumped version to v0.7.0 [`152e741`](https://git.odit.services/lfk/selfservice/commit/152e74190d13d30110d494a9062f868390ca19b3)
- Merge pull request 'Donation list feature/39-donation_list' (#40) from feature/39-donation_list into dev [`4c83e2e`](https://git.odit.services/lfk/selfservice/commit/4c83e2e738a075354383dca4d500808f761247d2)
- Added total to bottom of page [`89820d4`](https://git.odit.services/lfk/selfservice/commit/89820d44501793365248b8e778522cdc188afa70)
- Sorted translations 🌍 [`cb1b9d3`](https://git.odit.services/lfk/selfservice/commit/cb1b9d330b28f11cceed9691aee4ccee5246d346)
- Added basic sponsoring table [`f141130`](https://git.odit.services/lfk/selfservice/commit/f141130db5ede60a623747f4e324f66259e5bc75)
- Added translations 🌍 [`d713fbe`](https://git.odit.services/lfk/selfservice/commit/d713fbef94e75fbbc62254ea8f0ca50dc4e93d79)
- Now w/ formatted currency amount [`815a36f`](https://git.odit.services/lfk/selfservice/commit/815a36f20271f47f49c3814b001c3404d43113cd)
- Fixed spaces in name [`baa6da3`](https://git.odit.services/lfk/selfservice/commit/baa6da3dd06c493fccc61945b9bcbd9b2e79d910)
- Updated env description [`5a123b0`](https://git.odit.services/lfk/selfservice/commit/5a123b0cf89f49d450becbbc03b28c5bb6416b7c)
- added distance formatting [`121022c`](https://git.odit.services/lfk/selfservice/commit/121022c8434484a363a3f2ea68aba4d8ef7cad2d)
#### [0.6.1](https://git.odit.services/lfk/selfservice/compare/0.6.0...0.6.1)
> 6 April 2021
- 🚀Bumped version to v0.6.1 [`f5ae214`](https://git.odit.services/lfk/selfservice/commit/f5ae2145df07413329f60c229d9571fd1de2ca79)
- Fixed imprint/privacy default links [`96c0e56`](https://git.odit.services/lfk/selfservice/commit/96c0e5698697361e65c5aa80c9a0aada5c3d5f30)
#### [0.6.0](https://git.odit.services/lfk/selfservice/compare/0.5.1...0.6.0)
> 3 April 2021
- 🚀Bumped version to v0.6.0 [`bc1de2a`](https://git.odit.services/lfk/selfservice/commit/bc1de2acdcbf8f3319026887cb6b3d594fcd63dc)
- first part of certificate generation with manual data [`f633439`](https://git.odit.services/lfk/selfservice/commit/f6334397dc99cd38d31ef524bc26ba7d386b33dd)
- Renoved fixed data [`f0a7f35`](https://git.odit.services/lfk/selfservice/commit/f0a7f35dec07508a07d4c11abda297ad0bf91187)
- Revert "🚀Bumped version to v0.6.0" [`145b499`](https://git.odit.services/lfk/selfservice/commit/145b49906bdf2d75abf93e1779f188a691a3a909)
- 🚀Bumped version to v0.6.0 [`0a62e8f`](https://git.odit.services/lfk/selfservice/commit/0a62e8f5d1929bfe9a53a9709a3031f06a2487c3)
- Merge pull request 'Certificate download feature/19-runner_certficates' (#38) from feature/19-runner_certficates into dev [`53eeb6b`](https://git.odit.services/lfk/selfservice/commit/53eeb6be3ddf4735a11b6c4636c28b4d16aa58ee)
- Fixed blob handling [`b938cfc`](https://git.odit.services/lfk/selfservice/commit/b938cfc49e269e76761a752f8257ebbab88f6959)
- Reverted relative linking fix [`c3b2b93`](https://git.odit.services/lfk/selfservice/commit/c3b2b93d90102b4e31cfce15220acfc8fe48a7c5)
#### [0.5.1](https://git.odit.services/lfk/selfservice/compare/0.5.0...0.5.1)
> 3 April 2021
- Merge pull request 'feature/30-profile-forgot-link' (#35) from feature/30-profile-forgot-link into dev [`#30`](https://git.odit.services/lfk/selfservice/issues/30)
- 🚀Bumped version to v0.5.1 [`1a3c9ed`](https://git.odit.services/lfk/selfservice/commit/1a3c9edeb3987907ffe2223da8f3be079a1c80d2)
- Merge pull request 'Env linking bugfix bugfix/31-env_linking' (#37) from bugfix/31-env_linking into dev [`fd6bd88`](https://git.odit.services/lfk/selfservice/commit/fd6bd88d4238cca2a2755ab1bc51ff7870bef947)
- basic ProfileNone layout [`863568d`](https://git.odit.services/lfk/selfservice/commit/863568d7d0e132dd1b7d13a2cb1afe8db02c472c)
- add forgot view + action [`30f3a51`](https://git.odit.services/lfk/selfservice/commit/30f3a51ef82ad966225abc6fd95508af56106924)
- Added building pipeline for current branch [`544542a`](https://git.odit.services/lfk/selfservice/commit/544542ac1e9717506676c1495c2d530eca833d31)
- Removed useless code [`713db5e`](https://git.odit.services/lfk/selfservice/commit/713db5e1e8fed5f38b3a5d19cfe870eff16eeda1)
- Now manually linking env and main [`67c0dae`](https://git.odit.services/lfk/selfservice/commit/67c0dae537df5842e89c0ed040c0fef02df01b69)
- Fixed post url and body [`0795ea3`](https://git.odit.services/lfk/selfservice/commit/0795ea318d80b764eee89462ad87da86aa7ce0ae)
- 🐞 fix home routes [`60dea51`](https://git.odit.services/lfk/selfservice/commit/60dea511b9fbb6d206ee531618e788e686fa6b5a)
#### [0.5.0](https://git.odit.services/lfk/selfservice/compare/0.4.5...0.5.0)
> 1 April 2021
- 🚀Bumped version to v0.5.0 [`676f0e8`](https://git.odit.services/lfk/selfservice/commit/676f0e83393be823ccb612846650a9a98bf7f0b3)
- Revert "🚀Bumped version to v0.5.0" [`27fd8f9`](https://git.odit.services/lfk/selfservice/commit/27fd8f9a9106294ae6522f2fff42dfbf84f06f82)
- 🚀Bumped version to v0.5.0 [`b8c1b8c`](https://git.odit.services/lfk/selfservice/commit/b8c1b8c43b6d050d239d836bbb6ace2504fdcd21)
- Merge pull request 'Runner self deletion feature/21-user_deletion' (#36) from feature/21-user_deletion into dev [`bd512e0`](https://git.odit.services/lfk/selfservice/commit/bd512e0651363f1f2543136d354d9ada9990d83d)
- Sorted translations [`cf0437c`](https://git.odit.services/lfk/selfservice/commit/cf0437c3a738d42154ed1336d6020b88661b462f)
- Added translations 🌍 [`ba3dedb`](https://git.odit.services/lfk/selfservice/commit/ba3dedb1643e2d5f1c2ab92224a11c1c8a82ae71)
- Added confirmation for profile deletion [`8773239`](https://git.odit.services/lfk/selfservice/commit/8773239244c064a888a4ad1f66298f2811fcc2ae)
- Added confirmation for profile deletion [`85b0549`](https://git.odit.services/lfk/selfservice/commit/85b0549a11927758c579cd08bc8015ae00105a84)
- Updated icons [`d710d4b`](https://git.odit.services/lfk/selfservice/commit/d710d4b68ff0e9515eb4a0a4ea0def7b3552e599)
- Added data deletion button [`619078c`](https://git.odit.services/lfk/selfservice/commit/619078cce8281bef520b05bf3e735c26d0752375)
- Content i18n run 🌍 [`175bb16`](https://git.odit.services/lfk/selfservice/commit/175bb168ae134009c26072b76cd74948aecc189f)
- Implemented basic deletion [`fc04e71`](https://git.odit.services/lfk/selfservice/commit/fc04e713110df161c481cb9a3b64e78b30d4f79b)
- Added translations [`8a24505`](https://git.odit.services/lfk/selfservice/commit/8a24505d64832001d6a44800f61f7e0705892046)
- Updated toasts and removed useless code [`7440fc4`](https://git.odit.services/lfk/selfservice/commit/7440fc404ea2a8e68e7ce896e8369700069b2021)
- Added error toast [`9751c2e`](https://git.odit.services/lfk/selfservice/commit/9751c2e9fd99650efab4d5edce3909c907ea9391)
- Merge pull request 'RELEASE 0.3.0' (#25) from dev into main [`1c19657`](https://git.odit.services/lfk/selfservice/commit/1c196579356a70f9d761f7f69d79033157570076)
#### [0.4.5](https://git.odit.services/lfk/selfservice/compare/0.4.4...0.4.5) #### [0.4.5](https://git.odit.services/lfk/selfservice/compare/0.4.4...0.4.5)
> 1 April 2021
- Merge pull request 'feature/32-registration-language' (#33) from feature/32-registration-language into dev [`#32`](https://git.odit.services/lfk/selfservice/issues/32) - Merge pull request 'feature/32-registration-language' (#33) from feature/32-registration-language into dev [`#32`](https://git.odit.services/lfk/selfservice/issues/32)
- 🚀Bumped version to v0.4.5 [`efcb7f5`](https://git.odit.services/lfk/selfservice/commit/efcb7f56745aa3f347eb98399ee1bcb68a157e9d)
- 🐞 fix /profile/ router on register [`52a0828`](https://git.odit.services/lfk/selfservice/commit/52a082841d47d1ed9c0997776a0784285a3f6ba7) - 🐞 fix /profile/ router on register [`52a0828`](https://git.odit.services/lfk/selfservice/commit/52a082841d47d1ed9c0997776a0784285a3f6ba7)
- pass browser locale to api/runners/register/ [`e3f7497`](https://git.odit.services/lfk/selfservice/commit/e3f7497af5860b2472b6cdfd33090b9d68b9fb49) - pass browser locale to api/runners/register/ [`e3f7497`](https://git.odit.services/lfk/selfservice/commit/e3f7497af5860b2472b6cdfd33090b9d68b9fb49)

View File

@ -1,9 +1,10 @@
FROM node:15.9.0-alpine3.13 FROM registry.odit.services/hub/library/node:15.9.0-alpine3.13
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN yarn RUN yarn
RUN yarn build RUN yarn build
RUN yarn postbuild
# final image # final image
FROM fholzer/nginx-brotli:v1.19.1 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
COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./nginx.conf /etc/nginx/nginx.conf

6
env_fix.js Normal file
View File

@ -0,0 +1,6 @@
const fs = require('fs');
if (fs.existsSync('./dist/index.html')) {
const content = fs.readFileSync('./dist/index.html', { encoding: 'utf8' });
const newcontent = content.replace(`"/env.js"`, `"./env.js"`);
fs.writeFileSync('./dist/index.html', newcontent);
}

View File

@ -1,17 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lauf für Kaya! - Selfservice</title> <title>Lauf für Kaya! - Selfservice</title>
</head> </head>
<body class="dark:bg-gray-900 text-black dark:text-white p-0"> <body class="dark:bg-gray-900 text-black dark:text-white p-0">
<div id="app"></div> <div id="app"></div>
<script src="/env.js"></script> <script src="/env.js"></script>
<script defer type="module" src="/src/main.js"></script> <script defer type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

View File

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

3129
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -2,7 +2,7 @@
<footer class="text-gray-400 bg-gray-900 body-font"> <footer class="text-gray-400 bg-gray-900 body-font">
<div class="container px-5 py-8 mx-auto flex items-center sm:flex-row flex-col"> <div class="container px-5 py-8 mx-auto flex items-center sm:flex-row flex-col">
<p class="text-sm text-gray-400 sm:ml-4 sm:pl-4 sm:py-2 sm:mt-0 mt-4"> <p class="text-sm text-gray-400 sm:ml-4 sm:pl-4 sm:py-2 sm:mt-0 mt-4">
Lauf für Kaya! Selfservice - Copyright © 2021 + proudly powered by Lauf für Kaya! Selfservice - Copyright © 2023 + proudly powered by
<a <a
class="underline" class="underline"
target="_blank" target="_blank"
@ -31,8 +31,8 @@
export default { export default {
data() { data() {
return { return {
imprint_url: config.url_imprint || "/imprint/" imprint_url: config.url_imprint || config.baseurl_selfservice + "imprint"
, privacy_url: config.url_privacy || "/privacy/" , privacy_url: config.url_privacy || config.baseurl_selfservice + "privacy"
} }
}, },
} }

View File

@ -29,7 +29,7 @@
placeholder="Passwort" placeholder="Passwort"
/> />
<div class="absolute inset-y-0 right-0 pr-3 flex items-center text-sm"> <div class="absolute inset-y-0 right-0 pr-3 flex items-center text-sm">
<a href="./reset" class="text-gray-900 underline">Passwort vergessen?</a> <a href="/selfservice/reset" class="text-gray-900 underline">Passwort vergessen?</a>
</div> </div>
</div> </div>
</div> </div>
@ -80,7 +80,7 @@
</div> </div>
<div class="mt-6"> <div class="mt-6">
<a <a
href="./register" href="/selfservice/register"
class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md 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 border-gray-300 rounded-md font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
>Account erstellen</a> >Account erstellen</a>
</div> </div>
@ -101,7 +101,7 @@ function login() {
axios.get("").then((res) => { axios.get("").then((res) => {
loading.value = false; loading.value = false;
Toastify({ Toastify({
text: "This is a toast", text: "Login läuft...",
duration: 3000, duration: 3000,
}).showToast(); }).showToast();
}); });

View File

@ -29,7 +29,7 @@
placeholder="Passwort" placeholder="Passwort"
/> />
<div class="absolute inset-y-0 right-0 pr-3 flex items-center text-sm"> <div class="absolute inset-y-0 right-0 pr-3 flex items-center text-sm">
<a href="./reset" class="text-gray-900 underline">Passwort vergessen?</a> <a href="/selfservice/reset" class="text-gray-900 underline">Passwort vergessen?</a>
</div> </div>
</div> </div>
</div> </div>
@ -80,7 +80,7 @@
</div> </div>
<div class="mt-6"> <div class="mt-6">
<a <a
href="./register" href="/selfservice/register"
class="block w-full text-center py-2 px-3 border border-gray-300 rounded-md 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 border-gray-300 rounded-md font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
>Account erstellen</a> >Account erstellen</a>
</div> </div>
@ -101,7 +101,7 @@ function login() {
axios.get("").then((res) => { axios.get("").then((res) => {
loading.value = false; loading.value = false;
Toastify({ Toastify({
text: "This is a toast", text: "Login läuft...",
duration: 3000, duration: 3000,
}).showToast(); }).showToast();
}); });

View File

@ -1,10 +1,19 @@
{ {
"access_is_only_provided_via_your_email_link": "Der Zugang erfolgt nur ü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 €)",
"apartment_suite_etc": "Addresszeile 2", "apartment_suite_etc": "Addresszeile 2",
"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_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 €)",
"delete_all_of_my_data": "Meine gesamten Daten löschen",
"delete_my_data": "Meine Daten löschen",
"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 ",
@ -13,10 +22,14 @@
"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?",
"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",
"no_laps_scans_were_recorded_yet": "Es wurden noch keine Runden / Scans aufgezeichnet ...", "no_laps_scans_were_recorded_yet": "Es wurden noch keine Runden / Scans aufgezeichnet ...",
"no_sponsorings_for_you_were_recorded_yet": "Es gibt noch keine Sponsorings für dich",
"not_registered_yet": "Noch nicht registriert?",
"organization": "Organisation",
"ort": "Ort", "ort": "Ort",
"phone_number": "Telefonnummer", "phone_number": "Telefonnummer",
"please_provide_a_valid_zipcode": "Bitte geben Sie eine gültige Postleitzahl an...", "please_provide_a_valid_zipcode": "Bitte geben Sie eine gültige Postleitzahl an...",
@ -26,16 +39,22 @@
"profile": "Profil", "profile": "Profil",
"provide_address": "Adresse angeben?", "provide_address": "Adresse angeben?",
"register": { "register": {
"register_now": "Jetzt für den Lauf für Kaya! 2021 registrieren." "register_now": "Jetzt für den Lauf für Kaya! 2023 registrieren."
}, },
"register_now": "Jetzt registrieren!", "register_now": "Jetzt registrieren!",
"register_now_small": "Jetzt registrieren",
"registrieren": "Registrieren", "registrieren": "Registrieren",
"registrierungscode": "Registrierungscode",
"resend_the_registration_mail": "Registrierungsmail erneut versenden",
"save_changes": "Änderungen speichern", "save_changes": "Änderungen speichern",
"sponsoring": "Sponsoring", "sponsoring": "Sponsoring",
"strasse": "Straße", "strasse": "Straße",
"the_system_is_not_properly_configured_please_contact_the_system_administrator_for_help": "Das System ist nicht richtig konfiguriert. Bitte wenden Sie sich an den Systemadministrator, um Hilfe zu erhalten.", "the_system_is_not_properly_configured_please_contact_the_system_administrator_for_help": "Das System ist nicht richtig konfiguriert. Bitte wenden Sie sich an den Systemadministrator, um Hilfe zu erhalten.",
"this_is_not_a_valid_international_phone_number": "Dies ist keine gültige internationale Telefonnummer", "this_is_not_a_valid_international_phone_number": "Dies ist keine gültige internationale Telefonnummer",
"tos": "AGBs", "tos": "AGBs",
"total": "Gesamt",
"view_my_data": "Meine Läuferdaten einsehen", "view_my_data": "Meine Läuferdaten einsehen",
"vorname": "Vorname" "vorname": "Vorname",
"you_have_been_registered": "Sie wurden registriert!",
"you_have_not_provided_a_valid_access_key": "Sie haben keinen gültigen Zugangsschlüssel angegeben..."
} }

View File

@ -1,10 +1,19 @@
{ {
"access_is_only_provided_via_your_email_link": "Access is only provided via the link you received upon registration.",
"all_data_deleted": "All Data deleted!",
"already_have_an_account": "Already have an account?", "already_have_an_account": "Already have an account?",
"amount_per_kilometer_in_eur": "Amount per kilometer (in €)",
"apartment_suite_etc": "Apartment, suite, etc.", "apartment_suite_etc": "Apartment, suite, etc.",
"cancel_keep_my_data": "Cancel, keep my data",
"configuration_error": "Configuration error", "configuration_error": "Configuration error",
"confirm_delete_all_of_my_data": "Confirm, delete all of my data",
"confirm_personal_data": "I hereby confirm that the above information is complete and correct", "confirm_personal_data": "I hereby confirm that the above information is complete and correct",
"current_total_amount_in_eur": "Current total amount (in €)",
"delete_my_data": "Delete my data",
"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 ",
@ -13,10 +22,13 @@
"imprint": "Imprint", "imprint": "Imprint",
"lap_time": "Lap time", "lap_time": "Lap time",
"lap_times": "Lap times", "lap_times": "Lap times",
"lost_your_registration_mail": "Lost your registration mail?",
"main_page_text": "Here you can register for the Lauf Für Kaya! or manage your runner profile.", "main_page_text": "Here you can register for the Lauf Für Kaya! or manage your runner profile.",
"mittelname": "Middlename", "mittelname": "Middlename",
"nachname": "Lastname", "nachname": "Lastname",
"no_laps_scans_were_recorded_yet": "No laps/ scans were recorded yet...", "no_laps_scans_were_recorded_yet": "No laps/ scans were recorded yet...",
"no_sponsorings_for_you_were_recorded_yet": "No sponsorings for you were recorded yet...",
"not_registered_yet": "Not registered yet?",
"organization": "Organization", "organization": "Organization",
"ort": "City", "ort": "City",
"phone_number": "Phone Number", "phone_number": "Phone Number",
@ -27,17 +39,21 @@
"profile": "Profile", "profile": "Profile",
"provide_address": "Provide a postal address?", "provide_address": "Provide a postal address?",
"register": { "register": {
"register_now": "Register now for Lauf für Kaya! 2021." "register_now": "Register now for Lauf für Kaya! 2023."
}, },
"register_now": "Register now!", "register_now": "Register now!",
"register_now_small": "Register now",
"registrieren": "Register Now", "registrieren": "Register Now",
"resend_the_registration_mail": "Resend the registration mail",
"save_changes": "Save changes", "save_changes": "Save changes",
"sponsoring": "Sponsoring", "sponsoring": "Sponsoring",
"strasse": "Street/ Block", "strasse": "Street/ Block",
"the_system_is_not_properly_configured_please_contact_the_system_administrator_for_help": "The system is not properly configured. Please contact the system administrator for help.", "the_system_is_not_properly_configured_please_contact_the_system_administrator_for_help": "The system is not properly configured. Please contact the system administrator for help.",
"this_is_not_a_valid_international_phone_number": "This is not a valid international phone number", "this_is_not_a_valid_international_phone_number": "This is not a valid international phone number",
"tos": "Terms of Service", "tos": "Terms of Service",
"total": "Total",
"view_my_data": "View my data", "view_my_data": "View my data",
"vorname": "Firstname", "vorname": "Firstname",
"you_have_been_registered": "You have been registered!" "you_have_been_registered": "You have been registered!",
"you_have_not_provided_a_valid_access_key": "You have not provided a valid access key..."
} }

View File

@ -3,6 +3,7 @@ import Home from './views/Home.vue';
import Imprint from './views/Imprint.vue'; import Imprint from './views/Imprint.vue';
import Privacy from './views/Privacy.vue'; import Privacy from './views/Privacy.vue';
import Register from './views/Register.vue'; import Register from './views/Register.vue';
import Registered from './views/Registered.vue';
import Profile from './views/Profile.vue'; import Profile from './views/Profile.vue';
import ProfileNone from './views/ProfileNone.vue'; import ProfileNone from './views/ProfileNone.vue';
@ -17,6 +18,7 @@ export const routes = [
{ path: config.baseurl_selfservice + 'privacy/', component: Privacy }, { path: config.baseurl_selfservice + 'privacy/', component: Privacy },
{ path: config.baseurl_selfservice + 'register', component: Register }, { path: config.baseurl_selfservice + 'register', component: Register },
{ path: config.baseurl_selfservice + 'register/', component: Register }, { path: config.baseurl_selfservice + 'register/', component: Register },
{ path: config.baseurl_selfservice + 'registered/', component: Registered },
{ path: config.baseurl_selfservice + 'register/:token', component: Register, props: true }, { path: config.baseurl_selfservice + 'register/:token', component: Register, props: true },
{ path: config.baseurl_selfservice + 'profile', component: Profile }, { path: config.baseurl_selfservice + 'profile', component: Profile },
{ path: config.baseurl_selfservice + 'profile/', component: ProfileNone }, { path: config.baseurl_selfservice + 'profile/', component: ProfileNone },

View File

@ -11,10 +11,10 @@
<div class="mt-6 sm:flex place-content-center"> <div class="mt-6 sm:flex place-content-center">
<a <a
class="w-full sm:w-auto inline-flex px-6 py-3 border border-transparent text-base font-semibold rounded-md text-gray-900 bg-white shadow-sm hover:text-gray-600 focus:outline-none focus:text-gray-600 transition ease-in-out duration-150 xl:text-lg xl:py-4" class="w-full sm:w-auto inline-flex px-6 py-3 border border-transparent text-base font-semibold rounded-md text-gray-900 bg-white shadow-sm hover:text-gray-600 focus:outline-none focus:text-gray-600 transition ease-in-out duration-150 xl:text-lg xl:py-4"
href="./register" href="/selfservice/register/"
>{{ $t('register_now') }}</a> >{{ $t('register_now') }}</a>
<a <a
href="./profile" href="/selfservice/profile/"
class="mt-4 sm:ml-4 sm:mt-0 w-full sm:w-auto inline-flex px-6 py-3 border border-transparent text-base font-semibold rounded-md text-white bg-gray-800 shadow-sm hover:bg-gray-700 focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150 xl:text-lg xl:py-4" class="mt-4 sm:ml-4 sm:mt-0 w-full sm:w-auto inline-flex px-6 py-3 border border-transparent text-base font-semibold rounded-md text-white bg-gray-800 shadow-sm hover:bg-gray-700 focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150 xl:text-lg xl:py-4"
>{{ $t('view_my_data') }}</a> >{{ $t('view_my_data') }}</a>
</div> </div>

View File

@ -1,209 +1,781 @@
<template> <template>
<div class="min-h-screen w-full p-4"> <div class="min-h-screen w-full p-4">
<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
<p md:pr-10 md:mb-0
class="text-3xl font-bold whitespace-nowrap" mb-6
v-text="(state.firstname || '') + ' ' + (state.middlename || '') + ' ' + (state.lastname || '')" pr-0
></p> w-full
<p class="text-md whitespace-nowrap">{{ state.group }}</p> md:w-auto md:text-left
</div> text-center text-black
<div class="inline-flex md:ml-auto md:mr-0 mx-auto items-center"> dark:text-gray-200
<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" <p
> class="text-3xl font-bold whitespace-nowrap"
<svg v-text="
xmlns="http://www.w3.org/2000/svg" (state.firstname || '') +
width="24" ' ' +
height="24" (state.middlename || '') +
viewBox="0 0 24 24" ' ' +
fill="none" (state.lastname || '')
stroke="currentColor" "
stroke-width="2" ></p>
stroke-linecap="round" <p class="text-md whitespace-nowrap">{{ state.group }}</p>
stroke-linejoin="round" </div>
class="feather feather-download" <div class="inline-flex md:ml-auto md:mr-0 mx-auto items-center">
style="display: inline;height: 1rem;vertical-align: sub;" <div v-if="state.delete_active === false">
> <button
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> type="button"
<polyline points="7 10 12 15 17 10" /> class="
<line x1="12" y1="15" x2="12" y2="3" /> focus:border-black focus:ring-2 focus:ring-black
</svg> text-white text-sm
{{ $t('download_certificate') }} py-2.5
</button> px-5
</div> rounded-md
</div> bg-blue-500
</section> hover:bg-blue-600 hover:shadow-lg
<div class="flex flex-wrap"> w-full
<div class="w-full p-4"> md:w-auto
<div class="flex flex-wrap flex-col w-full tabs"> mb-1
<div class="flex lg:flex-wrap flex-row lg:space-x-2"> md:mr-1
<div class="flex-none"> "
<button @click="get_registration"
@click="() => { state.activetab = 'profile' }" >
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'profile') }" <svg
class="tab tab-underline py-4 px-6 block" xmlns="http://www.w3.org/2000/svg"
type="button" width="24"
>{{ $t('profile') }}</button> height="24"
</div> viewBox="0 0 24 24"
<div class="flex-none"> fill="none"
<button stroke="currentColor"
@click="() => { state.activetab = 'laptimes' }" stroke-width="2"
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'laptimes') }" stroke-linecap="round"
class="tab tab-underline py-4 px-6 block" stroke-linejoin="round"
type="button" class="feather feather-download"
>{{ $t('lap_times') }}</button> style="display: inline; height: 1rem; vertical-align: sub"
</div> >
<div class="flex-none"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<button <polyline points="7 10 12 15 17 10" />
@click="() => { state.activetab = 'sponsorings' }" <line x1="12" y1="15" x2="12" y2="3" />
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'sponsorings') }" </svg>
class="tab tab-underline py-4 px-6 block" {{ $t("download_registrationcode") }}
type="button" </button>
>{{ $t('sponsoring') }}</button> <button
</div> type="button"
</div> class="
<div v-if="(state.activetab === 'profile')" class="tab-content block"> focus:border-black focus:ring-2 focus:ring-black
<div class="py-4 w-full"> text-white text-sm
<div class="flex flex-col"> py-2.5
<form class="form flex flex-wrap w-full"> px-5
<div class="w-full"> rounded-md
<div class="form-element"> bg-blue-500
<div class="text-lg">{{ $t('vorname') }}</div> hover:bg-blue-600 hover:shadow-lg
<p w-full
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" md:w-auto
v-text="state.firstname" mb-1
/> md:mr-1
</div> "
<div class="form-element"> @click="get_certificate"
<div class="text-lg">{{ $t('mittelname') }}</div> >
<p <svg
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" xmlns="http://www.w3.org/2000/svg"
v-text="state.middlename" width="24"
/> height="24"
</div> viewBox="0 0 24 24"
<div class="form-element"> fill="none"
<div class="text-lg">{{ $t('nachname') }}</div> stroke="currentColor"
<p stroke-width="2"
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" stroke-linecap="round"
v-text="state.lastname" stroke-linejoin="round"
/> class="feather feather-download"
</div> style="display: inline; height: 1rem; vertical-align: sub"
<div class="form-element"> >
<div class="text-lg">{{ $t('e_mail_adress') }}</div> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<p <polyline points="7 10 12 15 17 10" />
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" <line x1="12" y1="15" x2="12" y2="3" />
v-text="state.email" </svg>
/> {{ $t("download_certificate") }}
</div> </button>
<div class="form-element"> <button
<div class="text-lg">{{ $t('phone_number') }}</div> type="button"
<p class="
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" focus:border-black focus:ring-2 focus:ring-black
v-text="state.phone" text-white text-sm
/> py-2.5
</div> px-5
</div> rounded-md
</form> bg-red-600
</div> hover:bg-red-700 hover:shadow-lg
</div> w-full
</div> md:w-auto
<div v-if="(state.activetab === 'laptimes')" class="tab-content block"> "
<div class="py-4 w-full"> @click="
<section class="text-gray-400 dark:bg-gray-900 body-font"> () => {
<div class="container mx-auto"> state.delete_active = true;
<div class="lg:w-2/3 w-full mx-auto overflow-auto"> }
<table "
v-if="state.scans.length > 0" >
class="table-auto w-full text-left whitespace-no-wrap" <svg
> xmlns="http://www.w3.org/2000/svg"
<thead width="24"
class="text-black bg-gray-300 dark:text-white text-sm dark:bg-gray-800" height="24"
> viewBox="0 0 24 24"
<tr> fill="none"
<th stroke="none"
class="px-4 py-3 title-font tracking-wider font-medium" stroke-width="2"
>{{ $t('distance') }}</th> stroke-linecap="round"
<th stroke-linejoin="round"
class="px-4 py-3 title-font tracking-wider font-medium" class="feather feather-download"
>{{ $t('lap_time') }}</th> style="display: inline; height: 1rem; vertical-align: sub"
</tr> >
</thead> <path d="M0 0h24v24H0z" />
<tbody class="text-gray-900 dark:text-gray-50"> <path
<tr v-for="s in state.scans" :key="s.id"> fill="currentColor"
<td class="px-4 py-3"> 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"
<span v-text="s.distance"></span>m />
</td> </svg>
<td class="px-4 py-3" v-text="s.lapTime"></td> {{ $t("delete_my_data") }}
</tr> </button>
</tbody> </div>
</table> <div v-if="state.delete_active === true">
<div v-else class="text-center font-bold text-black dark:text-white text-2xl"> <button
<img type="button"
src="../assets/empty_laps.svg" class="
style="height:25rem; margin:0 auto;" focus:border-black focus:ring-2 focus:ring-black
:alt="[[$t('no_laps_scans_were_recorded_yet')]]" text-white text-sm
/> py-2.5
{{ $t('no_laps_scans_were_recorded_yet') }} px-5
</div> rounded-md
</div> bg-blue-500
</div> hover:bg-blue-600 hover:shadow-lg
</section> "
</div> @click="
</div> () => {
<div v-if="(state.activetab === 'sponsorings')" class="tab-content block"> state.delete_active = false;
<div class="py-4 w-full">coming soon...</div> }
</div> "
</div> >
</div> <svg
</div> xmlns="http://www.w3.org/2000/svg"
</div> width="24"
</template> height="24"
viewBox="0 0 24 24"
<script setup> fill="none"
import { reactive } from "vue"; stroke="none"
import { useToast } from "vue-toastification"; stroke-width="2"
import axios from "redaxios"; stroke-linecap="round"
stroke-linejoin="round"
const state = reactive({ class="feather feather-download"
phone: "", style="display: inline; height: 1rem; vertical-align: sub"
email: "", >
firstname: "", <path fill="none" d="M0 0h24v24H0z" />
middlename: "", <path
lastname: "", fill="currentColor"
scans: [], d="M12 11l5-5 1 1-5 5 5 5-1 1-5-5-5 5-1-1 5-5-5-5 1-1z"
group: "", />
activetab: "profile", </svg>
}) {{ $t("cancel_keep_my_data") }}
const toast = useToast(); </button>
const props = defineProps({ <button
token: String type="button"
}) class="
const accesstoken = atob(props.token); focus:border-black focus:ring-2 focus:ring-black
axios.get(`${config.baseurl}api/runners/me/${accesstoken}`) text-white text-sm
.then(({ data }) => { py-2.5
state.phone = data.phone; px-5
state.email = data.email; rounded-md
state.firstname = data.firstname; bg-red-600
state.middlename = data.middlename; hover:bg-red-700 hover:shadow-lg
state.lastname = data.lastname; ml-1
state.group = data.group; "
}).catch((error) => { @click="delete_me"
toast.error("An error occured while loading your profile data"); >
}) <svg
axios.get(`${config.baseurl}api/runners/me/${accesstoken}/scans`) xmlns="http://www.w3.org/2000/svg"
.then(({ data }) => { width="24"
data.map(function(s) { height="24"
s.lapTime = Math.floor(s.lapTime / 60) + 'min ' + (Math.floor(s.lapTime % 60) + "").padStart(2, "0") + "s" viewBox="0 0 24 24"
return s; fill="none"
}) stroke="none"
data.filter(s => s.valid === true); stroke-width="2"
state.scans = data; stroke-linecap="round"
}).catch((error) => { stroke-linejoin="round"
toast.error("An error occured while loading your profile data"); class="feather feather-download"
}) style="display: inline; height: 1rem; vertical-align: sub"
>
<path d="M0 0h24v24H0z" />
<path
fill="currentColor"
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>
{{ $t("confirm_delete_all_of_my_data") }}
</button>
</div>
</div>
</div>
</section>
<div class="flex flex-wrap">
<div class="w-full p-4">
<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-none">
<button
@click="
() => {
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"
type="button"
>
{{ $t("profile") }}
</button>
</div>
<div class="flex-none">
<button
@click="
() => {
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"
type="button"
>
{{ $t("lap_times") }}
</button>
</div>
<div class="flex-none">
<button
@click="
() => {
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"
type="button"
>
{{ $t("sponsoring") }}
</button>
</div>
</div>
<div v-if="state.activetab === 'profile'" class="tab-content block">
<div class="py-4 w-full">
<div class="flex flex-col">
<form class="form flex flex-wrap w-full">
<div class="w-full">
<div class="form-element">
<div class="text-lg">{{ $t("registrierungscode") }}</div>
<img alt="Registrierungscode" :src="state.barcode" />
<div class="text-lg">{{ $t("vorname") }}</div>
<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
"
v-text="state.firstname"
/>
</div>
<div class="form-element">
<div class="text-lg">{{ $t("mittelname") }}</div>
<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
"
v-text="state.middlename"
/>
</div>
<div class="form-element">
<div class="text-lg">{{ $t("nachname") }}</div>
<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
"
v-text="state.lastname"
/>
</div>
<div class="form-element">
<div class="text-lg">{{ $t("e_mail_adress") }}</div>
<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
"
v-text="state.email"
/>
</div>
<div class="form-element">
<div class="text-lg">{{ $t("phone_number") }}</div>
<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
"
v-text="state.phone"
/>
</div>
</div>
</form>
</div>
</div>
</div>
<div v-if="state.activetab === 'laptimes'" class="tab-content block">
<div class="py-4 w-full">
<section class="text-gray-400 dark:bg-gray-900 body-font">
<div class="container mx-auto">
<div class="lg:w-2/3 w-full mx-auto overflow-auto">
<table
v-if="state.scans.length > 0"
class="table-auto w-full text-left whitespace-no-wrap"
>
<thead
class="
text-black
bg-gray-300
dark:text-white
text-sm
dark:bg-gray-800
"
>
<tr>
<th
class="
px-4
py-3
title-font
tracking-wider
font-medium
"
>
{{ $t("distance") }}
</th>
<th
class="
px-4
py-3
title-font
tracking-wider
font-medium
"
>
{{ $t("lap_time") }}
</th>
</tr>
</thead>
<tbody class="text-gray-900 dark:text-gray-50">
<tr v-for="s in state.scans" :key="s.id">
<td class="px-4 py-3">
<span v-text="s.distance"></span>
</td>
<td class="px-4 py-3" v-text="s.lapTime"></td>
</tr>
</tbody>
</table>
<div
v-else
class="
text-center
font-bold
text-black
dark:text-white
text-2xl
"
>
<img
src="../assets/empty_laps.svg"
style="height: 25rem; margin: 0 auto"
:alt="[[$t('no_laps_scans_were_recorded_yet')]]"
/>
{{ $t("no_laps_scans_were_recorded_yet") }}
</div>
</div>
</div>
</section>
</div>
</div>
<div
v-if="state.activetab === 'sponsorings'"
class="tab-content block"
>
<div class="py-4 w-full">
<section class="text-gray-400 dark:bg-gray-900 body-font">
<div class="container mx-auto">
<div class="lg:w-2/3 w-full mx-auto overflow-auto">
<table
v-if="state.sponsorings.length > 0"
class="table-auto w-full text-left whitespace-no-wrap"
>
<thead
class="
text-black
bg-gray-300
dark:text-white
text-sm
dark:bg-gray-800
"
>
<tr>
<th
class="
px-4
py-3
title-font
tracking-wider
font-medium
"
>
Name
</th>
<th
class="
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>
</thead>
<tbody class="text-gray-900 dark:text-gray-50">
<tr v-for="s in state.sponsorings" :key="s.id">
<td class="px-4 py-3">
<span v-text="s.donor.firstname"></span>
<span v-if="s.donor.middlename">
<span v-text="s.donor.middlename"></span>
</span>
<span v-text="s.donor.lastname"></span>
</td>
<td class="px-4 py-3">
<span
v-text="
(s.amountPerDistance / 100)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>
</td>
<td class="px-4 py-3">
<span
v-text="
(s.amount / 100)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>
</td>
</tr>
</tbody>
<tfoot class="text-gray-900 dark:text-gray-50">
<tr>
<td class="px-4 py-3">{{ $t("total") }}</td>
<td class="px-4 py-3">
<span
v-text="
(
state.sponsorings.reduce(function (
sum,
current
) {
return sum + current.amountPerDistance;
},
0) / 100
)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>
</td>
<td class="px-4 py-3">
<span
v-text="
(
state.sponsorings.reduce(function (
sum,
current
) {
return sum + current.amount;
},
0) / 100
)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })
"
></span
>
</td>
</tr>
</tfoot>
</table>
<div
v-else
class="
text-center
font-bold
text-black
dark:text-white
text-2xl
"
>
<img
src="../assets/empty_laps.svg"
style="height: 25rem; margin: 0 auto"
:alt="[
[$t('no_sponsorings_for_you_were_recorded_yet')],
]"
/>
{{ $t("no_sponsorings_for_you_were_recorded_yet") }}
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive } from "vue";
import { TYPE, useToast } from "vue-toastification";
import axios from "redaxios";
import bwipjs from "bwip-js";
function textToBase64Barcode(text) {
var canvas = document.createElement("canvas");
bwipjs.toCanvas(canvas, {
bcid: config.codeformat || "code39",
text: text,
scale: 3,
height: 10,
// width: 10,
includetext: true,
textxalign: "center",
backgroundcolor: "ffffff",
});
return canvas.toDataURL("image/png");
}
const state = reactive({
barcode: "",
phone: "",
email: "",
firstname: "",
middlename: "",
lastname: "",
scans: [],
sponsorings: [],
group: "",
activetab: "profile",
delete_active: false,
fullobject: {},
});
const toast = useToast();
const props = defineProps({
token: String,
});
const accesstoken = atob(props.token);
axios
.get(`${config.baseurl}api/runners/me/${accesstoken}`)
.then(({ data }) => {
state.phone = data.phone;
state.email = data.email;
state.firstname = data.firstname;
state.middlename = data.middlename;
state.lastname = data.lastname;
state.group = data.group;
state.sponsorings = data.distanceDonations;
state.fullobject = data;
state.barcode = textToBase64Barcode(data.id ?? "???");
})
.catch((error) => {
toast.clear();
toast.error("Profil konnte nicht geladen werden...");
});
axios
.get(`${config.baseurl}api/runners/me/${accesstoken}/scans`)
.then(({ data }) => {
data.map(function (s) {
s.lapTime =
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;
});
data.filter((s) => s.valid === true);
state.scans = data;
})
.catch((error) => {
toast.error("Profil konnte nicht geladen werden...");
});
function delete_me() {
toast.clear();
toast("Profil wird gelöscht...");
let url = `${config.baseurl}api/runners/me/${accesstoken}?force=true`;
axios
.delete(url)
.then(() => {
toast.clear();
toast("Alle Daten gelöscht!");
location.replace(`${config.baseurl_selfservice}`);
})
.catch((error) => {
toast.clear();
toast.error("Profil konnte nicht gelöscht werden...");
});
}
function get_certificate() {
toast("Urkunde wird generiert...");
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 postdata = Object.assign({}, state.fullobject);
postdata.group = {
name: postdata.group,
};
postdata = [postdata];
axios
.post(url, postdata, {
responseType: "blob",
})
.then((response) => {
console.log(response);
if (response.status != "200") {
toast.error("Urkunde konnte nicht generiert werden...");
} else {
var fileURL = window.URL.createObjectURL(
new Blob([response.data], { type: "application/pdf" })
);
var fileLink = document.createElement("a");
fileLink.href = fileURL;
fileLink.setAttribute("download", "Certificate.pdf");
document.body.appendChild(fileLink);
fileLink.click();
fileLink.remove();
toast.clear();
toast("Urkunde generiert!", { type: TYPE.SUCCESS });
}
})
.catch((err) => {
console.error(err);
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

@ -1 +1,104 @@
<template>You have not provided a valid access key...</template> <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! - {{ $t('profile') }}</h1>
<p class="mx-auto leading-relaxed text-base text-center">
{{ $t('you_have_not_provided_a_valid_access_key') }}
<br />
{{ $t('access_is_only_provided_via_your_email_link') }}
</p>
<div class="mt-6">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span
class="px-2 bg-white dark:bg-gray-900"
>{{ $t('lost_your_registration_mail') }}</span>
</div>
</div>
<div class="mt-4">
<label for="email_address" class="block font-medium">
{{ $t('e_mail_adress') }}
<span class="font-bold">*</span>
</label>
<input
v-model="user_email"
name="email_address"
id="email_address"
autocomplete="off"
:placeholder="[[$t('e_mail_adress')]]"
type="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"
/>
<p
v-if="!isEmail(user_email)"
class="text-sm"
>{{ $t('please_provide_valid_mail') }}</p>
</div>
<div class="mt-2">
<a
:disabled="(!state.submit_enabled)"
:class="{ 'opacity-50': (!state.submit_enabled), 'cursor-not-allowed': (!state.submit_enabled) }"
@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"
>{{ $t('resend_the_registration_mail') }}</a>
</div>
</div>
<div class="mt-12">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white dark:bg-gray-900">{{ $t('not_registered_yet') }}</span>
</div>
</div>
<div class="mt-2">
<a
href="/selfservice/register/"
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('register_now_small') }}</a>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed, ref, reactive, defineProps } from "vue";
import axios from "redaxios";
import isEmail from 'validator/es/lib/isEmail';
import isMobilePhone from 'validator/es/lib/isMobilePhone';
import isPostalCode from 'validator/es/lib/isPostalCode';
import { useToast } from "vue-toastification";
let user_email = ref("");
//
const state = reactive({
org_name: "",
org_teams: [],
submit_enabled: computed(() => isEmail(user_email.value))
})
const toast = useToast();
function resendMail() {
if (isEmail(user_email.value)) {
toast("Login-Link wird angefordert...");
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2);
axios.post(`${config.baseurl}api/runners/forgot?mail=${user_email.value}&locale=${browserlocale}`)
.then(({ data }) => {
console.log(data);
toast("Login-Link gesendet an " + user_email.value + "!");
})
.catch((error) => {
console.log(error);
toast("Fehler beim Anfordern des Login-Links...");
});
}
}
</script>

View File

@ -1,338 +1,320 @@
<template> <template>
<div class="min-h-screen flex items-center justify-center"> <div class="min-h-screen flex items-center justify-center">
<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! - {{ $t('registrieren') }}</h1> >Lauf für Kaya! - {{ $t('registrieren') }}</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">{{ $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
v-model="userdetails.firstname" v-model="userdetails.firstname"
name="firstname" name="firstname"
id="first_name" id="first_name"
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="{ '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" 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"
id="middle_name" id="middle_name"
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
v-model="userdetails.lastname" v-model="userdetails.lastname"
name="lastname" name="lastname"
id="last_name" id="last_name"
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="{ '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" 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
v-model="userdetails.mail" v-model="userdetails.mail"
name="email_address" name="email_address"
id="email_address" id="email_address"
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="{ '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" 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"
id="phone" id="phone"
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="{ '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" 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>
<div class="flex items-start col-span-6"> <div class="flex items-start col-span-6">
<div class="flex items-center h-5"> <div class="flex items-center h-5">
<input <input
v-model="provide_address" v-model="provide_address"
id="address_activated" id="address_activated"
name="address_activated" name="address_activated"
type="checkbox" type="checkbox"
class="h-4 w-4 text-indigo-600 border-gray-300 rounded" class="h-4 w-4 text-indigo-600 border-gray-300 rounded"
/> />
</div> </div>
<div class="ml-3 text-sm"> <div class="ml-3 text-sm">
<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
v-model="userdetails.address.street" v-model="userdetails.address.street"
type="text" type="text"
name="street" name="street"
: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="{ '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" 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"
name="address2" name="address2"
: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
v-model="userdetails.address.city" v-model="userdetails.address.city"
type="text" type="text"
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="{ '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" 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
v-model="userdetails.address.zipcode" v-model="userdetails.address.zipcode"
type="text" type="text"
name="postal_code" name="postal_code"
: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="{ '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" 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">
<div class="flex items-center h-5"> <div class="flex items-center h-5">
<input <input
v-model="agb_accepted" v-model="agb_accepted"
id="agb_accepted" id="agb_accepted"
name="agb_accepted" name="agb_accepted"
type="checkbox" type="checkbox"
class="h-4 w-4 text-indigo-600 border-gray-300 rounded" class="h-4 w-4 text-indigo-600 border-gray-300 rounded"
/> />
</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 for="agb_accepted" class="font-medium text-gray-400 select-none">
{{ $t('i_accept', { tos: $t('tos') }) }} {{ $t('i_accept', { tos: $t('tos') }) }}
<a <a
target="_blank" target="_blank"
rel="noreferrer,noopener" rel="noreferrer,noopener"
href href="https://lauf-fuer-kaya.de/datenschutz/"
class="underline" class="underline"
>{{ $t('tos') }}</a> >{{ $t('tos') }}</a>
{{ $t('i_accept_end') }} {{ $t('i_accept_end') }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
</div> </div>
</div> </div>
<div class="flex items-start mt-6"> <div class="flex items-start mt-6">
<div class="flex items-center h-5"> <div class="flex items-center h-5">
<input <input
v-model="data_confirmed" v-model="data_confirmed"
id="data_confirmed" id="data_confirmed"
name="data_confirmed" name="data_confirmed"
type="checkbox" type="checkbox"
class="h-4 w-4 text-indigo-600 border-gray-300 rounded" class="h-4 w-4 text-indigo-600 border-gray-300 rounded"
/> />
</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 for="data_confirmed" class="font-medium text-gray-400 select-none">
{{ $t('confirm_personal_data') }} {{ $t('confirm_personal_data') }}
<span class="font-bold">*</span> <span class="font-bold">*</span>
</label> </label>
</div> </div>
</div> </div>
<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="{ 'opacity-50': (!state.submit_enabled), '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" 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> >{{ $t('registrieren') }}</button>
</div> </div>
</div> </div>
<div class="mt-6"> </div>
<div class="relative"> </div>
<div class="absolute inset-0 flex items-center"> </template>
<div class="w-full border-t border-gray-300"></div>
</div> <script setup>
<div class="relative flex justify-center text-sm"> import { computed, ref, reactive, defineProps } from "vue";
<span class="px-2 bg-white dark:bg-gray-900">{{ $t('already_have_an_account') }}</span> import axios from "redaxios";
</div> import isEmail from 'validator/es/lib/isEmail';
</div> import isMobilePhone from 'validator/es/lib/isMobilePhone';
<div class="mt-6"> import isPostalCode from 'validator/es/lib/isPostalCode';
<a import { useToast } from "vue-toastification";
href="./login"
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" const props = defineProps({
>{{ $t('go_to_login') }}</a> token: String
</div> })
</div> if (props.token) {
</div> axios.get(`${config.baseurl}api/organizations/selfservice/${props.token}`)
</div> .then(({ data }) => {
</template> state.org_name = data.name;
state.org_teams = data.teams;
<script setup> org_team.value = data.teams[0]?.id;
import { computed, ref, reactive, defineProps } from "vue"; })
import axios from "redaxios"; .catch((error) => {
import isEmail from 'validator/es/lib/isEmail'; console.log(error);
import isMobilePhone from 'validator/es/lib/isMobilePhone'; });
import isPostalCode from 'validator/es/lib/isPostalCode'; }
import { useToast } from "vue-toastification";
let userdetails = ref({ firstname: "", lastname: "", middlename: "", mail: "", phone: "", address: { street: "", address2: "", city: "", zipcode: "" } });
const props = defineProps({ let provide_address = ref(false);
token: String let agb_accepted = ref(false);
}) let data_confirmed = ref(false);
if (props.token) { let org_team = ref("");
axios.get(`${config.baseurl}api/organizations/selfservice/${props.token}`) //
.then(({ data }) => { const state = reactive({
state.org_name = data.name; org_name: "",
state.org_teams = data.teams; org_teams: [],
org_team.value = data.teams[0]?.id; submit_enabled: computed(() => agb_accepted.value === true && data_confirmed.value === true && (isMobilePhone(userdetails.value.phone) || !userdetails.value.phone.trim()) && isEmail(userdetails.value.mail)
}) && userdetails.value.firstname
.catch((error) => { && 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"))))
console.log(error); })
}); const toast = useToast();
} function login() {
userdetails = userdetails.value;
let userdetails = ref({ firstname: "", lastname: "", middlename: "", mail: "", phone: "", address: { street: "", address2: "", city: "", zipcode: "" } }); if (userdetails?.phone === "" || isMobilePhone(userdetails.phone)) {
let provide_address = ref(false); if (isEmail(userdetails.mail)) {
let agb_accepted = ref(false); let postdata = {
let data_confirmed = ref(false); "email": userdetails.mail,
let org_team = ref(""); "firstname": userdetails.firstname,
// "middlename": userdetails.middlename,
const state = reactive({ "lastname": userdetails.lastname,
org_name: "", "address": {}
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) if (isMobilePhone(userdetails.phone)) {
&& userdetails.value.firstname postdata.phone = userdetails.phone;
&& 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")))) }
}) if (provide_address.value === true) {
const toast = useToast(); postdata.address = {
function login() { address1: userdetails.address.street,
userdetails = userdetails.value; address2: userdetails.address.address2 || "",
if (userdetails.phone === "" || isMobilePhone(userdetails.phone)) { city: userdetails.address.city,
if (isEmail(userdetails.mail)) { postalcode: userdetails.address.zipcode,
let postdata = { country: "DE",
"email": userdetails.mail, }
"firstname": userdetails.firstname, }
"middlename": userdetails.middlename, if (state.org_name !== '' && state.org_teams.length > 0) {
"lastname": userdetails.lastname, postdata.team = org_team.value;
"address": {} }
} toast("Registrierung läuft...");
if (isMobilePhone(userdetails.phone)) { const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2);
postdata.phone = userdetails.phone; let url = `${config.baseurl}api/runners/register/?locale=${browserlocale}`;
} if (props.token) {
if (provide_address.value === true) { url = `${config.baseurl}api/runners/register/${props.token}/?locale=${browserlocale}`
postdata.address = { }
address1: userdetails.address.street, axios.post(url, postdata)
address2: userdetails.address.address2 || "", .then(() => {
city: userdetails.address.city, location.replace(`${config.baseurl_selfservice}registered/?mail=${encodeURIComponent(postdata.email)}`);
postalcode: userdetails.address.zipcode, })
country: "DE", .catch((error) => {
} console.log(error);
} });
if (state.org_name !== '' && state.org_teams.length > 0) { }
postdata.team = org_team.value; }
} }
toast("registration in progress...");
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2);
let url = `${config.baseurl}api/runners/register/?locale=${browserlocale}`;
if (props.token) {
url = `${config.baseurl}api/runners/register/${props.token}/?locale=${browserlocale}`
}
axios.post(url, postdata)
.then(({ data }) => {
const token = btoa(data.token);
// alert(token);
location.replace(`${config.baseurl_selfservice}profile/${token}`);
})
.catch((error) => {
console.log(error);
});
}
}
}
</script> </script>

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

@ -0,0 +1,11 @@
<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">{{ this.$route.query.mail }}</b> geschickt haben.</p>
</div>
</div>
</template>