Compare commits
No commits in common. "main" and "feature-scss" have entirely different histories.
main
...
feature-sc
@ -1,11 +1,2 @@
|
|||||||
.browserslistrc
|
|
||||||
.dockerignore
|
|
||||||
.drone.yml
|
|
||||||
.editorconfig
|
|
||||||
.git
|
.git
|
||||||
.gitignore
|
|
||||||
.reuse
|
|
||||||
Dockerfile
|
|
||||||
LICENSES
|
|
||||||
README.md
|
|
||||||
node_modules
|
node_modules
|
||||||
|
109
.drone.yml
@ -4,115 +4,14 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
kind: pipeline
|
kind: pipeline
|
||||||
type: docker
|
type: docker
|
||||||
name: qa
|
name: default
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
branch:
|
|
||||||
- main
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: reuse
|
|
||||||
image: fsfe/reuse:5.0.2-debian@sha256:7928d25ed14a1bc22758d917ebc6aecbb8bcd1a4da7aa748d7179c9011bbfb0b
|
|
||||||
- name: lint
|
|
||||||
image: node:20.18.0-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9
|
|
||||||
commands:
|
|
||||||
- npm ci
|
|
||||||
- npm run lint -- --no-fix
|
|
||||||
- name: audit
|
|
||||||
image: node:20.18.0-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9
|
|
||||||
commands:
|
|
||||||
- npm install -g better-npm-audit
|
|
||||||
- better-npm-audit audit --production --level=moderate
|
|
||||||
- name: docker-dry-run
|
|
||||||
image: plugins/docker:20.18.4@sha256:a8d3d86853c721492213264815f1d00d3ed13f42f5c1855a02f47fa4d5f1e042
|
|
||||||
settings:
|
|
||||||
registry: git.wtf-eg.de
|
|
||||||
repo: git.wtf-eg.de/kompetenzinventar/frontend
|
|
||||||
target: ki-frontend
|
|
||||||
dry_run: true
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: build
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
branch:
|
|
||||||
- main
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- qa
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: docker-publish
|
- name: docker-publish
|
||||||
image: plugins/docker:20.18.4@sha256:a8d3d86853c721492213264815f1d00d3ed13f42f5c1855a02f47fa4d5f1e042
|
image: plugins/docker
|
||||||
settings:
|
settings:
|
||||||
registry: git.wtf-eg.de
|
registry: registry.wtf-eg.net
|
||||||
repo: git.wtf-eg.de/kompetenzinventar/frontend
|
repo: registry.wtf-eg.net/ki-frontend
|
||||||
target: ki-frontend
|
|
||||||
auto_tag: true
|
|
||||||
username:
|
|
||||||
from_secret: "docker_username"
|
|
||||||
password:
|
|
||||||
from_secret: "docker_password"
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: deploy
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
branch:
|
|
||||||
- main
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: deploy-dev
|
|
||||||
image: appleboy/drone-ssh:1.7.5@sha256:995677e073454912f26d4c0fdd2f9df2e1f5a30d6603d3f2ece667311b6babb3
|
|
||||||
settings:
|
|
||||||
host:
|
|
||||||
- dev01.wtf-eg.net
|
|
||||||
username: drone_deployment
|
|
||||||
key:
|
|
||||||
from_secret: "dev01_deployment_key"
|
|
||||||
command_timeout: 2m
|
|
||||||
script:
|
|
||||||
- echo "Executing forced command..."
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: tag-release
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: reuse
|
|
||||||
image: fsfe/reuse:5.0.2-debian@sha256:7928d25ed14a1bc22758d917ebc6aecbb8bcd1a4da7aa748d7179c9011bbfb0b
|
|
||||||
- name: lint
|
|
||||||
image: node:20.18.0-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9
|
|
||||||
commands:
|
|
||||||
- npm ci
|
|
||||||
- npm run lint -- --no-fix
|
|
||||||
- name: docker-publish
|
|
||||||
image: plugins/docker:20.18.4@sha256:a8d3d86853c721492213264815f1d00d3ed13f42f5c1855a02f47fa4d5f1e042
|
|
||||||
settings:
|
|
||||||
registry: git.wtf-eg.de
|
|
||||||
repo: git.wtf-eg.de/kompetenzinventar/frontend
|
|
||||||
target: ki-frontend
|
target: ki-frontend
|
||||||
auto_tag: true
|
auto_tag: true
|
||||||
username:
|
username:
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.{json,js,vue}]
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.{yml,yaml}]
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.html]
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.{css,scss}]
|
|
||||||
indent_size = 2
|
|
@ -8,13 +8,10 @@ module.exports = {
|
|||||||
'eslint:recommended'
|
'eslint:recommended'
|
||||||
],
|
],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: '@babel/eslint-parser'
|
parser: 'babel-eslint'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
'vue/no-useless-template-attributes': 'off',
|
|
||||||
'vue/no-reserved-component-names': 'off'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
.gitignore
vendored
@ -23,4 +23,3 @@ pnpm-debug.log*
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
jsconfig.json
|
|
||||||
|
10
.reuse/dep5
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: ki-frontend
|
||||||
|
Upstream-Contact: Scammo <kontakt@samuelbrinkmann.de>
|
||||||
|
Source: https://git.wtf-eg.de/kompetenzinventar/ki-frontend
|
||||||
|
|
||||||
|
# Sample paragraph, commented out:
|
||||||
|
#
|
||||||
|
# Files: src/*
|
||||||
|
# Copyright: $YEAR $NAME <$CONTACT>
|
||||||
|
# License: ...
|
26
Dockerfile
@ -1,28 +1,10 @@
|
|||||||
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
FROM node:14-alpine as builder
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
FROM node:20.18.0-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 as builder
|
COPY . ./
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
COPY .eslintrc.js .
|
|
||||||
COPY babel.config.js .
|
|
||||||
COPY public public
|
|
||||||
COPY src src
|
|
||||||
|
|
||||||
RUN npm ci && npm run build
|
RUN npm ci && npm run build
|
||||||
|
|
||||||
|
|
||||||
FROM nginx:1.27-alpine@sha256:2140dad235c130ac861018a4e13a6bc8aea3a35f3a40e20c1b060d51a7efd250 as ki-frontend
|
FROM nginx as ki-frontend
|
||||||
|
|
||||||
LABEL org.opencontainers.image.source=https://git.wtf-eg.de/kompetenzinventar/ki-frontend.git
|
COPY --from=builder /dist/ /usr/share/nginx/html/
|
||||||
LABEL org.opencontainers.image.url=https://git.wtf-eg.de/kompetenzinventar/ki-frontend
|
|
||||||
LABEL org.opencontainers.image.documentation=https://git.wtf-eg.de/kompetenzinventar/ki-frontend#docker
|
|
||||||
LABEL org.opencontainers.image.vendor="WTF Kooperative eG"
|
|
||||||
|
|
||||||
WORKDIR /usr/share/nginx/html
|
|
||||||
|
|
||||||
COPY etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf
|
|
||||||
COPY --from=builder /dist .
|
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Alle Rechte vorbehalten
|
|
9
LICENSES/MIT.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,43 +0,0 @@
|
|||||||
SIL OPEN FONT LICENSE
|
|
||||||
|
|
||||||
Version 1.1 - 26 February 2007
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
|
|
||||||
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting, or substituting — in part or in whole — any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
|
|
||||||
This license becomes null and void if any of the above conditions are not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
43
README.md
@ -1,54 +1,14 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
# ki-frontend
|
# ki-frontend
|
||||||
|
|
||||||
[![Build Status](https://drone.wtf-eg.de/api/badges/kompetenzinventar/ki-frontend/status.svg)](https://drone.wtf-eg.de/kompetenzinventar/ki-frontend)
|
[![Build Status](https://drone.wtf-eg.de/api/badges/kompetenzinventar/ki-frontend/status.svg)](https://drone.wtf-eg.de/kompetenzinventar/ki-frontend)
|
||||||
[![REUSE status](https://api.reuse.software/badge/git.wtf-eg.de/kompetenzinventar/ki-frontend)](https://api.reuse.software/info/git.wtf-eg.de/kompetenzinventar/ki-frontend)
|
|
||||||
|
|
||||||
## Über
|
Vue3 Projekt:
|
||||||
|
|
||||||
Dieses Repo enthält das Frontend des Projekts Kompentenzinventar - einer Webapplikation zur Erfassung von Userprofilen für die WTF eG.
|
|
||||||
|
|
||||||
Implementiert ist das Frontend mit Vue3.
|
|
||||||
|
|
||||||
### Mitmachen
|
|
||||||
|
|
||||||
Du kannst gerne bei der Entwicklung des Kompetenzinventars mitmachen.
|
|
||||||
|
|
||||||
- Fehler oder fehlende Funktionen erfassen. Bitte direkt über die [Issues](https://git.wtf-eg.de/kompetenzinventar/ki-frontend/issues) in Gitea.
|
|
||||||
- Dokumentation oder Implementierung verbessern. Bitte forke hierzu das Projekt, branche von `main` ab und erstelle dann einen [Pull Request](https://git.wtf-eg.de/kompetenzinventar/ki-frontend/pulls).
|
|
||||||
|
|
||||||
### Kommunikation
|
|
||||||
|
|
||||||
Folgende Kanäle gibt es für die Kommunikation über das Kompetenzinventar:
|
|
||||||
|
|
||||||
- Die [Issues](https://git.wtf-eg.de/kompetenzinventar/ki-frontend/issues) im WTF Gitea.
|
|
||||||
- Den Bereich [AG Entwicklung](https://forum.wtf-eg.de/c/interna/ag-entwicklung/21) im WTF Forum.
|
|
||||||
- Einen Raum in Matrix. Zutritt per Einladung, frlan lädt ein, eine einfache PN im Forum reicht.
|
|
||||||
|
|
||||||
### Repos
|
|
||||||
|
|
||||||
* [ki-backend](https://git.wtf-eg.de/kompetenzinventar/ki-backend) enthält das Backend
|
|
||||||
* **[ki-frontend](https://git.wtf-eg.de/kompetenzinventar/ki-frontend)** (dieses Repo) enthält das Frontend
|
|
||||||
* Weitere Repositories befinden sich in der Gitea Organisation [Kompetenzinventar](https://git.wtf-eg.de/kompetenzinventar).
|
|
||||||
|
|
||||||
## Project setup
|
## Project setup
|
||||||
```
|
```
|
||||||
npm ci
|
npm ci
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pre requirements
|
|
||||||
|
|
||||||
* Node 20
|
|
||||||
* Wenn du eine andere node version installiert hast, kannst du [nvm](https://github.com/nvm-sh/nvm) benutzen um schnell zwischen node version zu wechseln
|
|
||||||
* NPM
|
|
||||||
* (KI-backend)[https://git.wtf-eg.de/kompetenzinventar/ki-backend] muss lokal laufen
|
|
||||||
|
|
||||||
|
|
||||||
### Konfigurationsdatei anpassen
|
### Konfigurationsdatei anpassen
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -56,6 +16,7 @@ cp public/config.js.dev public/config.js
|
|||||||
vi public/config.js
|
vi public/config.js
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Compiles and hot-reloads for development
|
### Compiles and hot-reloads for development
|
||||||
```
|
```
|
||||||
npm run serve
|
npm run serve
|
||||||
|
40
REUSE.toml
@ -1,40 +0,0 @@
|
|||||||
version = 1
|
|
||||||
SPDX-PackageName = "ki-frontend"
|
|
||||||
SPDX-PackageSupplier = "Scammo <kontakt@samuelbrinkmann.de>"
|
|
||||||
SPDX-PackageDownloadLocation = "https://git.wtf-eg.de/kompetenzinventar/ki-frontend"
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = ["package.json", "package-lock.json", "renovate.json"]
|
|
||||||
precedence = "aggregate"
|
|
||||||
SPDX-FileCopyrightText = "WTF Kooperative eG <https://wtf-eg.de/>"
|
|
||||||
SPDX-License-Identifier = "AGPL-3.0-or-later"
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = [".browserslistrc", ".dockerignore", ".eslintrc.js", ".gitignore", "REUSE.toml"]
|
|
||||||
precedence = "aggregate"
|
|
||||||
SPDX-FileCopyrightText = "WTF Kooperative eG <https://wtf-eg.de/>"
|
|
||||||
SPDX-License-Identifier = "AGPL-3.0-or-later"
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = "src/assets/img/wtf**"
|
|
||||||
precedence = "aggregate"
|
|
||||||
SPDX-FileCopyrightText = "WTF Kooperative eG <https://wtf-eg.de/>"
|
|
||||||
SPDX-License-Identifier = "LicenseRef-WTF"
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = ["src/assets/language_level.json", "src/assets/skill_level.json"]
|
|
||||||
precedence = "aggregate"
|
|
||||||
SPDX-FileCopyrightText = "WTF Kooperative eG <https://wtf-eg.de/>"
|
|
||||||
SPDX-License-Identifier = "AGPL-3.0-or-later"
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = "public/img/bootstrap-icons-1.5.0/**"
|
|
||||||
precedence = "aggregate"
|
|
||||||
SPDX-FileCopyrightText = "Copyright (c) 2019-2020 The Bootstrap Authors"
|
|
||||||
SPDX-License-Identifier = "MIT"
|
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
path = "public/fonts/Lato**"
|
|
||||||
precedence = "aggregate"
|
|
||||||
SPDX-FileCopyrightText = "2010-2015, Łukasz Dziedzic (dziedzic@typoland.com)"
|
|
||||||
SPDX-License-Identifier = "OFL-1.1-RFN"
|
|
@ -1,7 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
'@vue/cli-plugin-babel/preset'
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
listen [::]:80;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
#access_log /var/log/nginx/host.access.log main;
|
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
|
|
||||||
# routes without dots serve the index.html without caching
|
|
||||||
location / {
|
|
||||||
add_header Cache-Control "no-cache";
|
|
||||||
try_files $uri $uri/index.html /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# static js and css files that get replaced instead of updated
|
|
||||||
location ~ \.(js|css) {
|
|
||||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
# cache other static files for 30 days
|
|
||||||
location ~ \.(?!html) {
|
|
||||||
add_header Cache-Control "public, max-age=2592000";
|
|
||||||
try_files $uri =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
#error_page 404 /404.html;
|
|
||||||
|
|
||||||
# redirect server error pages to the static page /50x.html
|
|
||||||
#
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root /usr/share/nginx/html;
|
|
||||||
}
|
|
||||||
|
|
||||||
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
|
|
||||||
#
|
|
||||||
#location ~ \.php$ {
|
|
||||||
# proxy_pass http://127.0.0.1;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
|
|
||||||
#
|
|
||||||
#location ~ \.php$ {
|
|
||||||
# root html;
|
|
||||||
# fastcgi_pass 127.0.0.1:9000;
|
|
||||||
# fastcgi_index index.php;
|
|
||||||
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
|
|
||||||
# include fastcgi_params;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# deny access to .htaccess files, if Apache's document root
|
|
||||||
# concurs with nginx's one
|
|
||||||
#
|
|
||||||
#location ~ /\.ht {
|
|
||||||
# deny all;
|
|
||||||
#}
|
|
||||||
}
|
|
22844
package-lock.json
generated
37
package.json
@ -1,31 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "@wtf/ki-frontend",
|
"name": "@wtf/ki-frontend",
|
||||||
"version": "1.1.0",
|
"version": "0.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service build",
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "7.25.9",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
"@vue/cli-plugin-babel": "5.0.8",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"@vue/cli-plugin-eslint": "5.0.8",
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
"@vue/cli-plugin-router": "5.0.8",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"@vue/cli-service": "5.0.8",
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"@vue/compiler-sfc": "3.5.6",
|
"axios": "^0.21.1",
|
||||||
"bootstrap": "5.3.3",
|
"babel-eslint": "^10.1.0",
|
||||||
"bootstrap-icons": "1.11.3",
|
"bootstrap": "^5.0.1",
|
||||||
"core-js": "3.39.0",
|
"core-js": "^3.6.5",
|
||||||
"eslint": "8.57.1",
|
"eslint": "^6.7.2",
|
||||||
"eslint-plugin-vue": "9.30.0",
|
"eslint-plugin-vue": "^7.0.0",
|
||||||
"sass": "1.80.4",
|
"sass": "^1.37.5",
|
||||||
"sass-loader": "16.0.3",
|
"sass-loader": "^10.2.0",
|
||||||
"v-tooltip": "4.0.0-beta.17",
|
"vue": "^3.0.0",
|
||||||
"vue": "3.5.6",
|
"vue-router": "^4.0.0-0"
|
||||||
"vue-router": "4.4.5",
|
|
||||||
"vuex": "4.1.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"sass-embedded": "1.80.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
public/apple-touch-icon.png
(Stored with Git LFS)
Normal file
@ -1,7 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
window.ki = {
|
window.ki = {
|
||||||
apiUrl: 'http://localhost:5000'
|
apiUrl: 'http://localhost:5000'
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
window.ki = {
|
window.ki = {
|
||||||
apiUrl: 'http://localhost:13338'
|
apiUrl: 'http://localhost:13338'
|
||||||
}
|
}
|
||||||
|
BIN
public/favicon-96x96.png
(Stored with Git LFS)
Normal file
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
1
public/img/bootstrap-icons-1.5.0/pencil.license
Normal file
@ -0,0 +1 @@
|
|||||||
|
SPDX-License-Identifier: MIT
|
3
public/img/bootstrap-icons-1.5.0/pencil.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil" viewBox="0 0 16 16">
|
||||||
|
<path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 548 B |
3
public/img/bootstrap-icons-1.5.0/plus-lg.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16">
|
||||||
|
<path d="M8 0a1 1 0 0 1 1 1v6h6a1 1 0 1 1 0 2H9v6a1 1 0 1 1-2 0V9H1a1 1 0 0 1 0-2h6V1a1 1 0 0 1 1-1z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 238 B |
1
public/img/bootstrap-icons-1.5.0/plus-lg.svg.license
Normal file
@ -0,0 +1 @@
|
|||||||
|
SPDX-License-Identifier: MIT
|
3
public/img/bootstrap-icons-1.5.0/search.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-search" viewBox="0 0 16 16">
|
||||||
|
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 331 B |
1
public/img/bootstrap-icons-1.5.0/search.svg.license
Normal file
@ -0,0 +1 @@
|
|||||||
|
SPDX-License-Identifier: MIT
|
4
public/img/bootstrap-icons-1.5.0/trash.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
|
||||||
|
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
|
||||||
|
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 573 B |
1
public/img/bootstrap-icons-1.5.0/trash.svg.license
Normal file
@ -0,0 +1 @@
|
|||||||
|
SPDX-License-Identifier: MIT
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -1,9 +1,3 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
||||||
"extends": [
|
|
||||||
"config:best-practices",
|
|
||||||
"group:linters",
|
|
||||||
"group:test",
|
|
||||||
"npm:unpublishSafe",
|
|
||||||
":disableDependencyDashboard",
|
|
||||||
":maintainLockFilesWeekly",
|
|
||||||
":pinAllExceptPeerDependencies",
|
|
||||||
":separateMultipleMajorReleases"
|
|
||||||
],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchPackageNames": [
|
|
||||||
"node"
|
|
||||||
],
|
|
||||||
"allowedVersions": "/^[1-9][02468]\\./"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
123
src/App.vue
@ -1,25 +1,114 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="alert alert-success text-center mb-0" role="alert">
|
||||||
|
Willkommen in der Alpha Version! <a href="https://git.wtf-eg.de/kompetenzinventar/ki-frontend/issues/new">Problem? Einfach ein neues Issue Anlegen.</a>
|
||||||
|
</div>
|
||||||
<header v-if="this.$route.path.includes('/s/')">
|
<header v-if="this.$route.path.includes('/s/')">
|
||||||
<Navbar />
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">KI</a>
|
||||||
|
<button
|
||||||
|
@click="showMobileNavbar = !showMobileNavbar"
|
||||||
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbarSupportedContent"
|
||||||
|
aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="collapse navbar-collapse"
|
||||||
|
:class="{
|
||||||
|
show: showMobileNavbar,
|
||||||
|
}"
|
||||||
|
id="navbarSupportedContent"
|
||||||
|
>
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link
|
||||||
|
class="nav-link"
|
||||||
|
:to="{ path: `/s/search` }"
|
||||||
|
active-class="active"
|
||||||
|
>Suche</router-link
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link
|
||||||
|
class="nav-link"
|
||||||
|
:to="{ path: `/s/profile/${memberId}` }"
|
||||||
|
active-class="active"
|
||||||
|
>Mein Profil</router-link
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<router-link
|
||||||
|
class="nav-link"
|
||||||
|
:to="{ path: `/s/profile-edit` }"
|
||||||
|
active-class="active"
|
||||||
|
>Bearbeiten</router-link
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="btn btn-outline-danger" @click="logout()">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
<a class="nav-link active" aria-current="page" href="#"></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<form class="d-flex" @submit.prevent="searchRedirect()">
|
||||||
|
<input
|
||||||
|
class="form-control me-2"
|
||||||
|
v-model="searchText"
|
||||||
|
type="search"
|
||||||
|
placeholder="Direkt Suchen"
|
||||||
|
aria-label="Search"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/img/bootstrap-icons-1.5.0/search.svg"
|
||||||
|
alt="Suche Icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<router-view :key="$route.fullPath" />
|
<router-view />
|
||||||
<Footer />
|
<footer>
|
||||||
|
<a href="https://wtf-eg.de/impressum/" class="m-4">Impressum</a>
|
||||||
|
<a href="https://wtf-eg.de/datenschutz/" class="m-4">Datenschutz</a>
|
||||||
|
<a href="https://git.wtf-eg.de/kompetenzinventar" class="m-4">git(ea)</a>
|
||||||
|
<a href="https://git.wtf-eg.de/kompetenzinventar/ki-frontend/issues/new" class="m-4">Problem Melden</a>
|
||||||
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Footer from '@/components/Footer'
|
|
||||||
import Navbar from '@/components/Navbar'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: "App",
|
||||||
components: {
|
data() {
|
||||||
Footer,
|
return {
|
||||||
Navbar,
|
showMobileNavbar: false,
|
||||||
}
|
memberId: null,
|
||||||
|
searchText: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.memberId = localStorage.getItem("user_id");
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
localStorage.clear();
|
||||||
|
this.$router.push({ path: "/" });
|
||||||
|
},
|
||||||
|
searchRedirect() {
|
||||||
|
this.$router.push({ path: `/s/search?text`, query: { query: this.searchText } } );
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,23 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@import "variables";
|
@import "variables";
|
||||||
@import "bootstrap/scss/bootstrap";
|
@import "bootstrap/scss/bootstrap";
|
||||||
|
|
||||||
.bg-wtf {
|
.container{
|
||||||
background-image: url(../assets/img/wtf-header-bg.jpg);
|
min-height: calc(100vh - 70px - 24px);
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-card{
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 107 KiB |
@ -1,100 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg width="100%" height="100%" viewBox="0 0 1476 617" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
|
||||||
<g id="Background" transform="matrix(1,0,0,1,-184.291,-861.078)">
|
|
||||||
<rect x="184.291" y="861.078" width="1475.62" height="616.639" style="fill:none;"/>
|
|
||||||
</g>
|
|
||||||
<g id="Logo" transform="matrix(1,0,0,1,-184.236,-860.997)">
|
|
||||||
<g id="WTF-Koorperative-eG" serif:id="WTF Koorperative eG">
|
|
||||||
<g id="eG" transform="matrix(4.16667,0,0,4.16667,-389.557,-181.762)">
|
|
||||||
<g transform="matrix(15.962,0,0,15.962,431.653,372.57)">
|
|
||||||
<path d="M0.288,-0.434C0.271,-0.434 0.255,-0.431 0.241,-0.424C0.227,-0.417 0.214,-0.407 0.202,-0.395C0.191,-0.383 0.181,-0.368 0.172,-0.351C0.164,-0.334 0.157,-0.315 0.152,-0.295C0.193,-0.3 0.227,-0.306 0.252,-0.313C0.277,-0.32 0.296,-0.327 0.31,-0.335C0.324,-0.343 0.333,-0.352 0.337,-0.361C0.342,-0.37 0.344,-0.38 0.344,-0.39C0.344,-0.395 0.343,-0.4 0.341,-0.405C0.339,-0.41 0.335,-0.415 0.331,-0.419C0.326,-0.423 0.32,-0.427 0.313,-0.43C0.306,-0.433 0.298,-0.434 0.288,-0.434ZM0.142,-0.209C0.142,-0.128 0.176,-0.088 0.244,-0.088C0.259,-0.088 0.272,-0.089 0.283,-0.092C0.294,-0.095 0.304,-0.098 0.313,-0.103C0.321,-0.107 0.329,-0.111 0.336,-0.116C0.343,-0.121 0.349,-0.126 0.355,-0.13C0.361,-0.134 0.366,-0.137 0.372,-0.14C0.378,-0.143 0.384,-0.144 0.391,-0.144C0.395,-0.144 0.398,-0.143 0.402,-0.141C0.406,-0.139 0.41,-0.136 0.413,-0.133L0.444,-0.095C0.426,-0.077 0.409,-0.062 0.392,-0.049C0.375,-0.036 0.358,-0.025 0.34,-0.017C0.322,-0.009 0.304,-0.003 0.284,0.002C0.264,0.006 0.243,0.008 0.22,0.008C0.19,0.008 0.163,0.002 0.139,-0.008C0.114,-0.018 0.093,-0.032 0.076,-0.051C0.059,-0.069 0.045,-0.092 0.036,-0.118C0.026,-0.144 0.022,-0.173 0.022,-0.206C0.022,-0.233 0.024,-0.259 0.03,-0.285C0.036,-0.311 0.045,-0.336 0.056,-0.359C0.067,-0.382 0.08,-0.404 0.096,-0.424C0.112,-0.443 0.13,-0.46 0.15,-0.475C0.17,-0.489 0.193,-0.5 0.217,-0.508C0.241,-0.516 0.267,-0.52 0.295,-0.52C0.321,-0.52 0.345,-0.516 0.365,-0.509C0.385,-0.501 0.402,-0.491 0.415,-0.479C0.429,-0.467 0.439,-0.453 0.446,-0.438C0.452,-0.423 0.456,-0.408 0.456,-0.394C0.456,-0.371 0.451,-0.35 0.442,-0.331C0.433,-0.312 0.416,-0.295 0.393,-0.28C0.369,-0.265 0.337,-0.252 0.297,-0.241C0.256,-0.23 0.204,-0.222 0.142,-0.216L0.142,-0.209Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(15.962,0,0,15.962,440.065,372.57)">
|
|
||||||
<path d="M0.41,-0.361L0.635,-0.361L0.599,-0.07C0.562,-0.043 0.522,-0.024 0.48,-0.011C0.438,0.002 0.389,0.008 0.335,0.008C0.291,0.008 0.25,0 0.214,-0.016C0.178,-0.032 0.147,-0.054 0.121,-0.083C0.095,-0.111 0.076,-0.145 0.062,-0.184C0.048,-0.223 0.041,-0.266 0.041,-0.313C0.041,-0.353 0.045,-0.392 0.054,-0.428C0.063,-0.465 0.076,-0.498 0.092,-0.53C0.109,-0.561 0.129,-0.588 0.152,-0.613C0.175,-0.638 0.201,-0.659 0.23,-0.677C0.259,-0.694 0.29,-0.708 0.323,-0.717C0.357,-0.726 0.392,-0.731 0.429,-0.731C0.458,-0.731 0.484,-0.728 0.508,-0.723C0.532,-0.718 0.554,-0.711 0.573,-0.702C0.592,-0.693 0.61,-0.683 0.625,-0.671C0.64,-0.659 0.654,-0.646 0.666,-0.633L0.624,-0.579C0.617,-0.569 0.609,-0.563 0.6,-0.561C0.59,-0.559 0.58,-0.561 0.57,-0.568C0.56,-0.574 0.551,-0.581 0.541,-0.587C0.531,-0.593 0.521,-0.599 0.509,-0.604C0.498,-0.608 0.485,-0.612 0.47,-0.616C0.455,-0.619 0.438,-0.62 0.419,-0.62C0.383,-0.62 0.349,-0.613 0.32,-0.598C0.29,-0.584 0.264,-0.563 0.242,-0.537C0.22,-0.51 0.204,-0.478 0.192,-0.441C0.18,-0.404 0.174,-0.363 0.174,-0.317C0.174,-0.282 0.178,-0.251 0.187,-0.224C0.196,-0.197 0.208,-0.173 0.224,-0.155C0.24,-0.135 0.259,-0.121 0.282,-0.111C0.304,-0.101 0.329,-0.095 0.357,-0.096C0.384,-0.095 0.408,-0.098 0.429,-0.103C0.45,-0.108 0.47,-0.116 0.489,-0.126L0.505,-0.263L0.425,-0.263C0.417,-0.263 0.41,-0.265 0.406,-0.27C0.402,-0.274 0.4,-0.28 0.401,-0.287L0.41,-0.361Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g id="Kooperative" transform="matrix(4.16667,0,0,4.16667,896.411,1370.07)">
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,0,0)">
|
|
||||||
<path d="M0.786,-0.365C0.786,-0.312 0.776,-0.263 0.758,-0.218C0.74,-0.172 0.715,-0.133 0.682,-0.099C0.649,-0.066 0.609,-0.04 0.562,-0.021C0.516,-0.002 0.464,0.008 0.408,0.008C0.351,0.008 0.299,-0.002 0.253,-0.021C0.206,-0.04 0.166,-0.066 0.133,-0.099C0.1,-0.133 0.074,-0.172 0.056,-0.218C0.038,-0.263 0.029,-0.312 0.029,-0.365C0.029,-0.417 0.038,-0.466 0.056,-0.512C0.074,-0.557 0.1,-0.596 0.133,-0.63C0.166,-0.663 0.206,-0.689 0.253,-0.708C0.299,-0.727 0.351,-0.737 0.408,-0.737C0.464,-0.737 0.516,-0.727 0.562,-0.708C0.609,-0.689 0.649,-0.662 0.682,-0.629C0.715,-0.596 0.74,-0.556 0.758,-0.511C0.776,-0.466 0.786,-0.417 0.786,-0.365ZM0.613,-0.365C0.613,-0.401 0.608,-0.433 0.599,-0.462C0.589,-0.491 0.576,-0.515 0.558,-0.535C0.541,-0.555 0.519,-0.57 0.494,-0.581C0.469,-0.592 0.44,-0.597 0.408,-0.597C0.375,-0.597 0.346,-0.592 0.32,-0.581C0.295,-0.57 0.273,-0.555 0.256,-0.535C0.238,-0.515 0.225,-0.491 0.216,-0.462C0.206,-0.433 0.202,-0.401 0.202,-0.365C0.202,-0.328 0.206,-0.296 0.216,-0.267C0.225,-0.238 0.238,-0.214 0.256,-0.194C0.273,-0.173 0.295,-0.158 0.32,-0.148C0.346,-0.137 0.375,-0.132 0.408,-0.132C0.44,-0.132 0.469,-0.137 0.494,-0.148C0.519,-0.158 0.541,-0.173 0.558,-0.194C0.576,-0.214 0.589,-0.238 0.599,-0.267C0.608,-0.296 0.613,-0.328 0.613,-0.365Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,14.6911,0)">
|
|
||||||
<path d="M0.786,-0.365C0.786,-0.312 0.776,-0.263 0.758,-0.218C0.74,-0.172 0.715,-0.133 0.682,-0.099C0.649,-0.066 0.609,-0.04 0.562,-0.021C0.516,-0.002 0.464,0.008 0.408,0.008C0.351,0.008 0.299,-0.002 0.253,-0.021C0.206,-0.04 0.166,-0.066 0.133,-0.099C0.1,-0.133 0.074,-0.172 0.056,-0.218C0.038,-0.263 0.029,-0.312 0.029,-0.365C0.029,-0.417 0.038,-0.466 0.056,-0.512C0.074,-0.557 0.1,-0.596 0.133,-0.63C0.166,-0.663 0.206,-0.689 0.253,-0.708C0.299,-0.727 0.351,-0.737 0.408,-0.737C0.464,-0.737 0.516,-0.727 0.562,-0.708C0.609,-0.689 0.649,-0.662 0.682,-0.629C0.715,-0.596 0.74,-0.556 0.758,-0.511C0.776,-0.466 0.786,-0.417 0.786,-0.365ZM0.613,-0.365C0.613,-0.401 0.608,-0.433 0.599,-0.462C0.589,-0.491 0.576,-0.515 0.558,-0.535C0.541,-0.555 0.519,-0.57 0.494,-0.581C0.469,-0.592 0.44,-0.597 0.408,-0.597C0.375,-0.597 0.346,-0.592 0.32,-0.581C0.295,-0.57 0.273,-0.555 0.256,-0.535C0.238,-0.515 0.225,-0.491 0.216,-0.462C0.206,-0.433 0.202,-0.401 0.202,-0.365C0.202,-0.328 0.206,-0.296 0.216,-0.267C0.225,-0.238 0.238,-0.214 0.256,-0.194C0.273,-0.173 0.295,-0.158 0.32,-0.148C0.346,-0.137 0.375,-0.132 0.408,-0.132C0.44,-0.132 0.469,-0.137 0.494,-0.148C0.519,-0.158 0.541,-0.173 0.558,-0.194C0.576,-0.214 0.589,-0.238 0.599,-0.267C0.608,-0.296 0.613,-0.328 0.613,-0.365Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,29.3823,0)">
|
|
||||||
<path d="M0.328,-0.371C0.372,-0.371 0.403,-0.382 0.423,-0.403C0.442,-0.424 0.452,-0.453 0.452,-0.491C0.452,-0.507 0.449,-0.522 0.444,-0.536C0.439,-0.55 0.431,-0.561 0.421,-0.571C0.411,-0.581 0.398,-0.589 0.383,-0.594C0.367,-0.599 0.349,-0.602 0.328,-0.602L0.24,-0.602L0.24,-0.371L0.328,-0.371ZM0.328,-0.729C0.379,-0.729 0.423,-0.722 0.46,-0.71C0.497,-0.698 0.527,-0.681 0.551,-0.66C0.575,-0.639 0.592,-0.614 0.604,-0.585C0.615,-0.556 0.621,-0.524 0.621,-0.491C0.621,-0.454 0.615,-0.421 0.603,-0.391C0.591,-0.36 0.574,-0.334 0.55,-0.313C0.526,-0.291 0.495,-0.274 0.458,-0.262C0.421,-0.25 0.378,-0.243 0.328,-0.244L0.24,-0.244L0.24,-0L0.071,-0L0.071,-0.729L0.328,-0.729Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,40.8538,0)">
|
|
||||||
<path d="M0.241,-0.599L0.241,-0.429L0.47,-0.429L0.47,-0.304L0.241,-0.304L0.241,-0.13L0.54,-0.13L0.54,-0L0.071,-0L0.071,-0.729L0.54,-0.729L0.54,-0.599L0.241,-0.599Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,51.189,0)">
|
|
||||||
<path d="M0.308,-0.386C0.331,-0.386 0.35,-0.389 0.366,-0.395C0.382,-0.401 0.396,-0.409 0.406,-0.419C0.416,-0.429 0.424,-0.441 0.429,-0.455C0.433,-0.469 0.436,-0.484 0.436,-0.5C0.436,-0.532 0.425,-0.557 0.404,-0.575C0.383,-0.593 0.351,-0.602 0.308,-0.602L0.24,-0.602L0.24,-0.386L0.308,-0.386ZM0.667,-0L0.514,-0C0.485,-0 0.465,-0.011 0.453,-0.032L0.332,-0.244C0.326,-0.253 0.32,-0.259 0.313,-0.263C0.306,-0.267 0.296,-0.27 0.283,-0.27L0.24,-0.27L0.24,-0L0.071,-0L0.071,-0.729L0.308,-0.729C0.36,-0.729 0.405,-0.723 0.442,-0.712C0.479,-0.701 0.51,-0.686 0.533,-0.667C0.557,-0.648 0.574,-0.625 0.585,-0.599C0.595,-0.572 0.601,-0.544 0.601,-0.513C0.601,-0.489 0.597,-0.467 0.591,-0.446C0.585,-0.425 0.575,-0.405 0.563,-0.387C0.551,-0.369 0.535,-0.354 0.517,-0.34C0.499,-0.326 0.478,-0.314 0.455,-0.305C0.466,-0.299 0.476,-0.292 0.486,-0.284C0.495,-0.275 0.504,-0.265 0.512,-0.254L0.667,-0Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,63.0483,0)">
|
|
||||||
<path d="M0.456,-0.279L0.388,-0.478C0.383,-0.491 0.378,-0.506 0.372,-0.523C0.366,-0.54 0.361,-0.559 0.355,-0.579C0.35,-0.558 0.345,-0.539 0.339,-0.522C0.333,-0.505 0.328,-0.49 0.323,-0.477L0.256,-0.279L0.456,-0.279ZM0.724,-0L0.593,-0C0.578,-0 0.567,-0.003 0.558,-0.01C0.549,-0.017 0.542,-0.026 0.537,-0.037L0.494,-0.164L0.217,-0.164L0.174,-0.037C0.17,-0.027 0.164,-0.019 0.154,-0.011C0.145,-0.004 0.133,-0 0.119,-0L-0.013,-0L0.269,-0.729L0.442,-0.729L0.724,-0Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,74.592,0)">
|
|
||||||
<path d="M0.594,-0.595L0.389,-0.595L0.389,-0L0.22,-0L0.22,-0.595L0.015,-0.595L0.015,-0.729L0.594,-0.729L0.594,-0.595Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,85.5765,0)">
|
|
||||||
<rect x="0.071" y="-0.729" width="0.17" height="0.729" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,91.1861,0)">
|
|
||||||
<path d="M0.724,-0.729L0.432,-0L0.279,-0L-0.013,-0.729L0.123,-0.729C0.138,-0.729 0.15,-0.725 0.159,-0.718C0.168,-0.711 0.174,-0.703 0.179,-0.692L0.32,-0.302C0.327,-0.285 0.333,-0.266 0.34,-0.246C0.346,-0.226 0.352,-0.205 0.358,-0.183C0.362,-0.205 0.367,-0.226 0.373,-0.246C0.379,-0.266 0.385,-0.285 0.392,-0.302L0.532,-0.692C0.536,-0.701 0.542,-0.71 0.552,-0.717C0.561,-0.725 0.573,-0.729 0.587,-0.729L0.724,-0.729Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,104.01,0)">
|
|
||||||
<path d="M0.241,-0.599L0.241,-0.429L0.47,-0.429L0.47,-0.304L0.241,-0.304L0.241,-0.13L0.54,-0.13L0.54,-0L0.071,-0L0.071,-0.729L0.54,-0.729L0.54,-0.599L0.241,-0.599Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(18.037,0,0,18.037,-13.068,0)">
|
|
||||||
<path d="M0.24,-0.434L0.267,-0.434C0.292,-0.434 0.31,-0.441 0.321,-0.457L0.485,-0.695C0.494,-0.708 0.505,-0.716 0.516,-0.721C0.527,-0.726 0.541,-0.729 0.558,-0.729L0.705,-0.729L0.489,-0.434C0.473,-0.414 0.457,-0.399 0.439,-0.39C0.452,-0.385 0.463,-0.379 0.474,-0.371C0.484,-0.362 0.494,-0.351 0.503,-0.338L0.723,-0L0.572,-0C0.562,-0 0.554,-0.001 0.547,-0.002C0.54,-0.003 0.534,-0.005 0.529,-0.008C0.524,-0.011 0.52,-0.014 0.516,-0.018C0.512,-0.022 0.509,-0.026 0.506,-0.031L0.341,-0.285C0.335,-0.294 0.327,-0.3 0.318,-0.304C0.309,-0.308 0.296,-0.31 0.281,-0.31L0.24,-0.31L0.24,-0L0.071,-0L0.071,-0.729L0.24,-0.729L0.24,-0.434Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g id="WTF" transform="matrix(4.16667,0,0,4.16667,-363.557,-181.762)">
|
|
||||||
<g transform="matrix(63.848,0,0,63.848,353.279,347.753)">
|
|
||||||
<path d="M0.59,-0.605L0.378,-0.605L0.378,-0L0.227,-0L0.227,-0.605L0.015,-0.605L0.015,-0.726L0.59,-0.726L0.59,-0.605Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(63.848,0,0,63.848,391.843,347.753)">
|
|
||||||
<path d="M0.227,-0.608L0.227,-0.408L0.488,-0.408L0.488,-0.29L0.227,-0.29L0.227,-0L0.076,-0L0.076,-0.726L0.538,-0.726L0.538,-0.608L0.227,-0.608Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
<g transform="matrix(63.848,0,0,63.848,282.658,347.753)">
|
|
||||||
<path d="M1.052,-0.726L0.827,-0L0.692,-0L0.543,-0.475C0.541,-0.481 0.538,-0.489 0.536,-0.497C0.534,-0.505 0.532,-0.513 0.53,-0.522C0.528,-0.513 0.525,-0.505 0.523,-0.497C0.521,-0.489 0.518,-0.481 0.516,-0.475L0.366,-0L0.23,-0L0.006,-0.726L0.132,-0.726C0.145,-0.725 0.155,-0.723 0.164,-0.717C0.173,-0.711 0.179,-0.703 0.182,-0.693L0.294,-0.284C0.297,-0.272 0.299,-0.26 0.302,-0.247C0.305,-0.234 0.308,-0.22 0.311,-0.205C0.314,-0.22 0.317,-0.234 0.32,-0.247C0.323,-0.26 0.327,-0.272 0.331,-0.284L0.462,-0.693C0.465,-0.701 0.47,-0.708 0.479,-0.715C0.488,-0.722 0.499,-0.725 0.512,-0.726L0.556,-0.726C0.569,-0.725 0.579,-0.722 0.587,-0.716C0.595,-0.71 0.602,-0.702 0.606,-0.693L0.736,-0.284C0.74,-0.273 0.744,-0.261 0.747,-0.249C0.75,-0.236 0.753,-0.223 0.757,-0.209C0.759,-0.223 0.762,-0.236 0.764,-0.249C0.767,-0.261 0.769,-0.273 0.772,-0.284L0.885,-0.693C0.887,-0.701 0.893,-0.709 0.902,-0.716C0.911,-0.722 0.921,-0.725 0.934,-0.726L1.052,-0.726Z" style="fill:#fff;fill-rule:nonzero;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<g id="Einhorn" transform="matrix(1,0,0,1,-31.2941,-563.989)">
|
|
||||||
<path d="M450.529,1662.06L400.757,1697.42L401.385,1712.12C401.385,1712.12 401.392,1712.51 401.318,1712.69C401.189,1713.12 400.944,1713.39 400.944,1713.39L450.529,1662.06Z" style="fill:#fff;"/>
|
|
||||||
<path d="M736.631,1698.47L756.815,1695.93C756.815,1695.93 757.296,1695.91 757.696,1695.65C758.083,1695.4 758.264,1695.06 758.264,1695.06L750.801,1710.64C750.801,1710.64 750.463,1711.42 749.582,1711.56C748.632,1711.72 748.087,1711.18 748.087,1711.18L743.517,1707.05L745.991,1710.95C745.991,1710.95 746.486,1711.78 746.06,1712.62C745.565,1713.62 744.582,1713.64 744.582,1713.64L734.515,1714.3C734.515,1714.3 735.304,1714.06 735.548,1713.76C735.8,1713.44 736.025,1712.42 736.025,1712.42L736.631,1698.47Z" style="fill:#fff;"/>
|
|
||||||
<g transform="matrix(-1,0,0,1,1133.78,-0.317371)">
|
|
||||||
<path d="M736.631,1698.47L756.815,1695.93C756.815,1695.93 757.296,1695.91 757.696,1695.65C758.083,1695.4 758.264,1695.06 758.264,1695.06L750.801,1710.64C750.801,1710.64 750.463,1711.42 749.582,1711.56C748.632,1711.72 748.087,1711.18 748.087,1711.18L743.517,1707.05L745.991,1710.95C745.991,1710.95 746.486,1711.78 746.06,1712.62C745.565,1713.62 744.582,1713.64 744.582,1713.64L734.515,1714.3C734.515,1714.3 735.304,1714.06 735.548,1713.76C735.8,1713.44 736.025,1712.42 736.025,1712.42L736.631,1698.47Z" style="fill:#fff;"/>
|
|
||||||
</g>
|
|
||||||
<path d="M392.458,1653.33L445.559,1660.94L398.452,1694.68L377.352,1692.04C377.352,1692.04 376.875,1691.98 376.441,1692.17C375.813,1692.44 375.559,1692.97 375.559,1692.97L392.458,1653.33Z" style="fill:#fff;"/>
|
|
||||||
<g transform="matrix(-1,0,0,1,1133.82,0.344083)">
|
|
||||||
<path d="M392.458,1653.33L445.559,1660.94L398.452,1694.68L377.352,1692.04C377.352,1692.04 376.875,1691.98 376.441,1692.17C375.813,1692.44 375.559,1692.97 375.559,1692.97L392.458,1653.33Z" style="fill:#fff;"/>
|
|
||||||
</g>
|
|
||||||
<path d="M671.12,1573.37L702.593,1592.58L707.544,1588.92L698.064,1578.83L687.348,1576.59L689.224,1568.43C689.224,1568.43 689.271,1568.29 689.281,1568.09C689.288,1567.93 689.255,1567.77 689.255,1567.77L686.724,1550.74C686.724,1550.74 686.529,1549.58 685.362,1549.35C684.059,1549.08 683.457,1550.14 683.457,1550.14L674.166,1565.5C674.166,1565.5 674.065,1565.68 674.02,1565.84C673.954,1566.06 673.947,1566.24 673.947,1566.24L673.835,1574.13L671.12,1573.37Z" style="fill:#fff;"/>
|
|
||||||
<g transform="matrix(-1,0,0,1,1133.81,-0.623595)">
|
|
||||||
<path d="M670.409,1573.25L702.616,1592.94L707.454,1589.14L698.064,1578.83L687.348,1576.59L689.224,1568.43C689.224,1568.43 689.271,1568.29 689.281,1568.09C689.288,1567.93 689.255,1567.77 689.255,1567.77L686.724,1550.74C686.724,1550.74 686.529,1549.58 685.362,1549.35C684.059,1549.08 683.457,1550.14 683.457,1550.14L674.166,1565.5C674.166,1565.5 674.065,1565.68 674.02,1565.84C673.954,1566.06 673.947,1566.24 673.947,1566.24L673.84,1573.91L670.409,1573.25Z" style="fill:#fff;"/>
|
|
||||||
</g>
|
|
||||||
<path d="M427.91,1594.32L407.278,1606.88L315.202,1511.83C315.202,1511.83 314.118,1510.66 315.278,1509.41C316.302,1508.31 317.631,1509.24 317.631,1509.24L427.91,1594.32Z" style="fill:#fff;"/>
|
|
||||||
<g transform="matrix(-1,0,0,1,1133.84,0.346916)">
|
|
||||||
<path d="M427.91,1594.32L407.278,1606.88L315.202,1511.83C315.202,1511.83 314.118,1510.66 315.278,1509.41C316.302,1508.31 317.631,1509.24 317.631,1509.24L427.91,1594.32Z" style="fill:#fff;"/>
|
|
||||||
</g>
|
|
||||||
<path d="M352.629,1973.55L412.821,1957.45L564.149,1813.8L502.768,1776.15L352.629,1973.55Z" style="fill:#fff;"/>
|
|
||||||
<path d="M724.493,1958.33L791.81,1974.99L636.355,1779.22L570.066,1813.9L724.493,1958.33Z" style="fill:#fff;"/>
|
|
||||||
<path d="M567.109,1811.42L634.096,1776.37L566.931,1691.79L504.928,1773.31L567.109,1811.42Z" style="fill:#fff;"/>
|
|
||||||
<path d="M501.388,1771.76L565.007,1688.47L507.888,1603.52L471.991,1672.38L501.388,1771.76Z" style="fill:#fff;"/>
|
|
||||||
<path d="M568.964,1688.62L636.117,1772.79L662.194,1673.36L625.972,1603.85L568.964,1688.62Z" style="fill:#fff;"/>
|
|
||||||
<path d="M628.44,1600.9L663.644,1574.69L681.595,1659.26L664.468,1669.99L628.44,1600.9Z" style="fill:#fff;"/>
|
|
||||||
<path d="M452.254,1658.94L469.378,1669.67L505.406,1600.56L470.221,1574.36L452.254,1658.94Z" style="fill:#fff;"/>
|
|
||||||
<path d="M409.728,1609.57L466.44,1574.95L449.265,1655.87L409.728,1609.57ZM419.698,1608.4C418.738,1609.21 418.917,1610.78 420.06,1611.35L425.353,1613.99C426.003,1614.31 426.771,1614.21 427.32,1613.75L432.618,1609.17C433.604,1608.31 433.359,1606.71 432.159,1606.19C425.3,1602.76 425.728,1603.15 420.361,1607.83C420.143,1608.01 419.68,1608.37 419.698,1608.4Z" style="fill:#fff;"/>
|
|
||||||
<g transform="matrix(-1,0,0,1,1133.87,0.365109)">
|
|
||||||
<path d="M450.529,1662.06L400.757,1697.42L401.402,1712.11C401.402,1712.11 401.412,1712.43 401.327,1712.69C401.198,1713.08 400.954,1713.36 400.954,1713.36L450.529,1662.06Z" style="fill:#fff;"/>
|
|
||||||
</g>
|
|
||||||
<path d="M667.407,1575.3L724.122,1609.91L684.599,1656.21L667.407,1575.3ZM707.873,1603.98C707.655,1603.99 707.437,1604.03 707.227,1604.13L701.695,1606.53C700.477,1607.05 700.242,1608.66 701.232,1609.52L706.538,1614.1C707.075,1614.57 707.86,1614.67 708.506,1614.34L713.79,1611.69C714.933,1611.11 715.121,1609.57 714.156,1608.74L709.095,1604.41C708.754,1604.12 708.323,1603.97 707.873,1603.98Z" style="fill:#fff;"/>
|
|
||||||
<path d="M687.949,1657.75L740.377,1650.89C740.377,1650.89 731.48,1619.35 729.222,1611.36C729.041,1610.72 728.918,1610.57 728.71,1610.53C728.604,1610.5 728.323,1610.47 727.692,1611.21C721.177,1618.84 687.949,1657.75 687.949,1657.75Z" style="fill:#fff;"/>
|
|
||||||
<g transform="matrix(-1,0,0,1,1133.99,0.137922)">
|
|
||||||
<path d="M688.136,1657.23L740.413,1650.04C740.413,1650.04 731.949,1620.01 729.672,1611.95C729.314,1610.68 728.761,1610.52 728.548,1610.52C728.308,1610.52 728.093,1610.44 727.596,1611.02C721.135,1618.59 688.136,1657.23 688.136,1657.23Z" style="fill:#fff;"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 20 KiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"1": "Keine Angabe",
|
"1": "Keine Angabe",
|
||||||
"2": "Grundkenntnisse",
|
"2": "Grundkentnisse",
|
||||||
"3": "Gut",
|
"3": "Gut",
|
||||||
"4": "Fließend",
|
"4": "Fließend",
|
||||||
"5": "Muttersprache"
|
"5": "Muttersprache"
|
||||||
|
@ -1,22 +1,7 @@
|
|||||||
{
|
{
|
||||||
"1": {
|
"1": "bis 6 Monate",
|
||||||
"short": "≤ 6M",
|
"2": "bis 1 Jahr",
|
||||||
"long": "bis 6 Monate"
|
"3": "bis 3 Jahre",
|
||||||
},
|
"4": "bis 5 Jahre",
|
||||||
"2":{
|
"5": "mehr als 5 Jahre"
|
||||||
"short": "≤ 1J",
|
|
||||||
"long": "bis 1 Jahr"
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"short": "≤ 3J",
|
|
||||||
"long": "bis 3 Jahre"
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"short": "≤ 5J",
|
|
||||||
"long": "bis 5 Jahre"
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"short": "> 5J",
|
|
||||||
"long": "mehr als 5 Jahre"
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,18 +1 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
$gray-100: #edefeb;
|
|
||||||
$gray-800: #344b5d;
|
|
||||||
|
|
||||||
$primary: #0790a9;
|
$primary: #0790a9;
|
||||||
$dark: $gray-800;
|
|
||||||
|
|
||||||
$body-bg: $gray-100;
|
|
||||||
|
|
||||||
$link-decoration: none;
|
|
||||||
$link-hover-decoration: underline;
|
|
||||||
|
|
||||||
$spinner-animation-speed: 1s;
|
|
||||||
|
@ -1,73 +1,108 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<profile-list
|
<div>
|
||||||
:values="values"
|
<label for="searchText" class="form-label fw-bold">{{ label }}</label>
|
||||||
:type="type"
|
<div class="row mb-2">
|
||||||
:editable="true"
|
<div class="col">
|
||||||
:show-secondary="showSecondary"
|
|
||||||
@remove-value="removeValue($event)"
|
|
||||||
@update-values="this.$emit('update-values', this.values)"
|
|
||||||
>
|
|
||||||
</profile-list>
|
|
||||||
<div v-bind="$attrs" class="card-body bg-white">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-md-4 col-lg-3 col-xl-2">
|
|
||||||
<div class="form-control-plaintext form-control-sm">Eintrag hinzufügen:</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-6">
|
|
||||||
<input
|
<input
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control form-control-sm"
|
class="form-control"
|
||||||
id="searchText"
|
id="searchText"
|
||||||
:maxlength="maxlength"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
@input="search()"
|
@keyup="search()"
|
||||||
@keyup.enter="addResult()"
|
|
||||||
/>
|
/>
|
||||||
<div v-if="searchResults">
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div v-if="selectedResult">
|
||||||
|
<div v-if="type === 'contacttype'">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="contactContent"
|
||||||
|
v-model="contactContent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="type === 'language'">
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
aria-label="Selektiere dein Level"
|
||||||
|
v-model="level"
|
||||||
|
>
|
||||||
|
<option v-for="(value, key) in languagesSelection" :value="key" :key="key">{{value}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
aria-label="Selektiere dein Level"
|
||||||
|
v-model="level"
|
||||||
|
>
|
||||||
|
<option v-for="(value, key) in levelSelection" :value="key" :key="key">{{value}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<button
|
||||||
|
v-if="selectedResult && !selectedResult.edit"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-success"
|
||||||
|
aria-label="Hinzufügen"
|
||||||
|
@click="addSelectedResult()"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/img/bootstrap-icons-1.5.0/plus-lg.svg"
|
||||||
|
alt="Hinzufügen Icon"
|
||||||
|
/>
|
||||||
|
Hinzufügen
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="selectedResult && selectedResult.edit"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-success"
|
||||||
|
aria-label="Hinzufügen"
|
||||||
|
@click="editSelectedResult()"
|
||||||
|
>
|
||||||
|
Änderung Speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="" v-if="searchResults">
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
<li
|
<li
|
||||||
class="list-group-item bg-white"
|
class="list-group-item"
|
||||||
v-for="result in searchResults"
|
v-for="result in searchResults"
|
||||||
:key="result.id"
|
:key="result.id"
|
||||||
@click="addResult(result)"
|
:class="{
|
||||||
|
'bg-info':
|
||||||
|
selectedResult &&
|
||||||
|
selectedResult.id &&
|
||||||
|
result.id === selectedResult.id,
|
||||||
|
}"
|
||||||
|
@click="selectedResult = result"
|
||||||
>
|
>
|
||||||
{{ result.name }}
|
{{ result.name }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<profile-list
|
||||||
<div class="col-md-2">
|
:values="values"
|
||||||
<button
|
:type="type"
|
||||||
v-if="searchText != ''"
|
:editable="true"
|
||||||
type="button"
|
@edit-value="editValue($event)"
|
||||||
class="btn btn-outline-success"
|
@remove-value="removeValue($event)"
|
||||||
aria-label="Hinzufügen"
|
></profile-list>
|
||||||
@click="addResult()"
|
|
||||||
>
|
|
||||||
<i clas="bi-plus-lg"></i>
|
|
||||||
Hinzufügen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
import ProfileList from "@/components/ProfileList";
|
||||||
|
|
||||||
import RequestMixin from '@/mixins/request.mixin'
|
import levelJson from "@/assets/skill_level.json"
|
||||||
import ProfileList from '@/components/ProfileList';
|
import languagesJson from "@/assets/language_level.json"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AutoComplete',
|
name: "AutoComplete",
|
||||||
mixins: [RequestMixin],
|
|
||||||
components: {
|
components: {
|
||||||
ProfileList,
|
ProfileList,
|
||||||
},
|
},
|
||||||
@ -81,66 +116,103 @@ export default {
|
|||||||
values: {
|
values: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
showSecondary: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
iconUrl: this.apiUrl,
|
iconUrl: this.apiUrl,
|
||||||
searchText: '',
|
searchText: "",
|
||||||
|
contactContent: "",
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
showErrorMessage: false,
|
showErrorMessage: false,
|
||||||
|
level: "1",
|
||||||
|
selectedResult: null,
|
||||||
|
levelSelection: levelJson,
|
||||||
|
languagesSelection: languagesJson,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
methods: {
|
||||||
...mapState(['currentUserId']),
|
async search() {
|
||||||
maxlength() {
|
try {
|
||||||
return this.type === 'skill' ? 50 : 25
|
const request = await this.axios.get(
|
||||||
|
`${this.apiUrl}/${this.type}s?search=${this.searchText}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (request.status === 200) {
|
||||||
|
this.searchResults = request.data[`${this.type}s`];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error();
|
||||||
|
this.showErrorMessage = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
addSelectedResult() {
|
||||||
addResult(result = false) {
|
|
||||||
if (!result) result = this.searchResults[0];
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.values.map((item) => item[this.type].name).includes(result.name)
|
this.values
|
||||||
|
.map((item) => item[this.type].id)
|
||||||
|
.includes(this.selectedResult.id)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let changeValues = Object.assign(this.values);
|
let changeValues = Object.assign(this.values);
|
||||||
let newValue = {
|
let newValue = {
|
||||||
profile_id: this.currentUserId,
|
profile_id: localStorage.getItem("user_id"),
|
||||||
};
|
};
|
||||||
|
if (this.type != "contacttype") {
|
||||||
if (this.type != 'contacttype') {
|
newValue.level = this.level;
|
||||||
newValue.level = 1;
|
|
||||||
} else {
|
} else {
|
||||||
newValue.content = '';
|
newValue.content = this.contactContent;
|
||||||
}
|
}
|
||||||
|
newValue[this.type] = this.selectedResult;
|
||||||
newValue[this.type] = result;
|
changeValues.push(newValue);
|
||||||
changeValues.unshift(newValue);
|
this.searchText = "";
|
||||||
|
|
||||||
this.searchText = '';
|
|
||||||
this.searchResults = [];
|
this.searchResults = [];
|
||||||
this.$emit('update-values', changeValues);
|
this.selectedResult = null;
|
||||||
|
this.level = 1;
|
||||||
|
this.$emit("update-values", changeValues);
|
||||||
},
|
},
|
||||||
removeValue(valueName) {
|
editSelectedResult() {
|
||||||
|
this.values.map((item) => {
|
||||||
|
if (item[this.type].name == this.selectedResult.name) {
|
||||||
|
if (this.type != "contacttype") {
|
||||||
|
item.level = this.level;
|
||||||
|
} else {
|
||||||
|
item.content = this.contactContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$emit("update-values", this.values);
|
||||||
|
},
|
||||||
|
removeValue(valueId) {
|
||||||
const newValues = this.values.filter((value) => {
|
const newValues = this.values.filter((value) => {
|
||||||
if (valueName === value[this.type].name) {
|
if (valueId === value[this.type].id) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.$emit('update-values', newValues);
|
this.$emit("update-values", newValues);
|
||||||
|
},
|
||||||
|
editValue(value) {
|
||||||
|
this.searchResults = [value[this.type]];
|
||||||
|
this.selectedResult = {
|
||||||
|
...value[this.type],
|
||||||
|
level: value.level,
|
||||||
|
edit: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.type != "contacttype") {
|
||||||
|
this.level = value.level;
|
||||||
|
} else {
|
||||||
|
this.contactContent = value.content;
|
||||||
|
}
|
||||||
|
this.level = value.level;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="avatar bg-dark">
|
|
||||||
{{ avatarLetters }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
name: String
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
avatarLetters() {
|
|
||||||
return this.name.substr(0, 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.avatar {
|
|
||||||
align-items: center;
|
|
||||||
border-radius: .25rem;
|
|
||||||
color: white;
|
|
||||||
display: flex;
|
|
||||||
font-weight: bold;
|
|
||||||
height: 2.5rem;
|
|
||||||
justify-content: center;
|
|
||||||
width: 2.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,47 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<footer class="bg-dark py-5">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row text-center text-sm-start">
|
|
||||||
<div class="col-sm-6 d-sm-flex justify-content-center mb-3 mb-sm-0">
|
|
||||||
<div class="d-inline-block">
|
|
||||||
<div class="fw-bold text-white mb-2">Kompetenzinventar</div>
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li><a href="https://git.wtf-eg.de/kompetenzinventar">Quellcode</a></li>
|
|
||||||
<li><a href="https://git.wtf-eg.de/kompetenzinventar/ki-doku/issues/new/choose">Problem melden</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 d-sm-flex justify-content-center">
|
|
||||||
<div class="d-inline-block">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
<li><a href="https://wtf-eg.de/impressum/">Impressum</a></li>
|
|
||||||
<li><a href="https://wtf-eg.de/datenschutz/">Datenschutzerklärung</a></li>
|
|
||||||
</ul>
|
|
||||||
<img class="wtf-logo wtf-logo--footer" src="@/assets/img/wtf_logo_white.svg">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</template>
|
|
||||||
<style scoped>
|
|
||||||
a {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wtf-logo--footer {
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,110 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-white">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<router-link
|
|
||||||
class="navbar-brand"
|
|
||||||
:to="{ path:'/s/search' }"
|
|
||||||
>
|
|
||||||
<img class="wtf-logo wtf-logo--navbar" src="@/assets/img/wtf_logo.svg">
|
|
||||||
<span class="d-none d-sm-inline">Kompetenzinventar</span>
|
|
||||||
<span class="d-sm-none">KI</span>
|
|
||||||
</router-link>
|
|
||||||
<button
|
|
||||||
@click="toggleMobileNav"
|
|
||||||
class="navbar-toggler"
|
|
||||||
type="button"
|
|
||||||
aria-controls="navbarSupportedContent"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-label="Toggle navigation"
|
|
||||||
>
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
class="collapse navbar-collapse d-lg-flex"
|
|
||||||
:class="{ show: showMobileNav }"
|
|
||||||
id="navbarSupportedContent"
|
|
||||||
>
|
|
||||||
<ul class="navbar-nav mb-2 mb-lg-0 me-auto">
|
|
||||||
<li class="nav-item">
|
|
||||||
<router-link
|
|
||||||
class="nav-link"
|
|
||||||
:to="{ path: `/s/search` }"
|
|
||||||
active-class="active"
|
|
||||||
>Suche</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<router-link
|
|
||||||
class="nav-link"
|
|
||||||
:to="{ path: `/s/profile/${currentUserId}` }"
|
|
||||||
active-class="active"
|
|
||||||
>Mein Profil</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<router-link
|
|
||||||
class="nav-link"
|
|
||||||
:to="{ path: `/s/profile-edit` }"
|
|
||||||
active-class="active"
|
|
||||||
>Bearbeiten</router-link
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
<li class="nav-item">
|
|
||||||
<button class="btn btn-danger w-100" @click="logout()">
|
|
||||||
<i class="bi bi-box-arrow-right"></i>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import RequestMixin from '@/mixins/request.mixin'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Navbar',
|
|
||||||
mixins: [RequestMixin],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showMobileNav: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState(['currentUserId'])
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleMobileNav() {
|
|
||||||
this.showMobileNav = !this.showMobileNav
|
|
||||||
},
|
|
||||||
logout() {
|
|
||||||
this.$store.dispatch('clear')
|
|
||||||
this.$router.push({ path: '/' });
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
|
|
||||||
.wtf-logo--navbar {
|
|
||||||
height: 40px;
|
|
||||||
margin-bottom: -5px;
|
|
||||||
margin-top: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,50 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ul class="pagination">
|
|
||||||
<li
|
|
||||||
class="page-item"
|
|
||||||
:class="{ active: page === current }"
|
|
||||||
v-for="page in pages"
|
|
||||||
:key="page"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="page-link pointer"
|
|
||||||
:class="{ 'bg-white': page !== current }"
|
|
||||||
@click="onPageClicked(page)"
|
|
||||||
>
|
|
||||||
{{ page }}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Paginator',
|
|
||||||
props: {
|
|
||||||
page: Number,
|
|
||||||
current: Number,
|
|
||||||
pages: Number
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onPageClicked(page) {
|
|
||||||
if (page == this.current) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('page', page)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,123 +1,75 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ul class="list-group list-group-flush">
|
<ul>
|
||||||
<li
|
<li v-for="value in values" :key="value.id">
|
||||||
class="list-group-item bg-white"
|
<img
|
||||||
v-for="(value, valueKey) in values"
|
style="max-width: 64px"
|
||||||
:key="value.id"
|
|
||||||
>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-12 col-md-5 d-flex align-items-center">
|
|
||||||
<div
|
|
||||||
class="list-icon me-1"
|
|
||||||
:style="{ backgroundImage: `url('${iconBaseUrl + value[type].icon_url}'` }"
|
|
||||||
v-if="value[type].icon_url"
|
v-if="value[type].icon_url"
|
||||||
|
:src="iconUrl + value[type].icon_url"
|
||||||
|
:alt="`${value[type].name} Logo`"
|
||||||
/>
|
/>
|
||||||
<div>
|
|
||||||
{{ value[type].name }}
|
{{ value[type].name }}
|
||||||
</div>
|
<span v-if="value.level">
|
||||||
</div>
|
<span v-if="type == 'skill'">({{ levelSelection[value.level] }})</span>
|
||||||
<div class="col-10 col-md-5">
|
<span v-if="type == 'language'">({{ languagesSelection[value.level] }})</span>
|
||||||
<div v-if="type == 'skill' && showSecondary">
|
</span>
|
||||||
<select
|
<span v-if="value.content">:
|
||||||
class="form-select form-select-sm"
|
<span v-if="value[type].name === 'E-Mail'">
|
||||||
aria-label="Selektiere dein Level"
|
<a :href="`mailto:${value.content}`">{{ value.content }}</a>
|
||||||
v-model="editableValues[valueKey].level"
|
</span>
|
||||||
@input="$emit('update-values', editableValues)"
|
<span v-else>
|
||||||
>
|
{{ value.content }}
|
||||||
<option
|
</span>
|
||||||
v-for="(value, key) in levelSelection"
|
</span>
|
||||||
:value="key"
|
|
||||||
:key="key"
|
|
||||||
>
|
|
||||||
{{ value.long || value }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="type == 'language'">
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label="Selektiere dein Level"
|
|
||||||
v-model="editableValues[valueKey].level"
|
|
||||||
@input="$emit('update-values', editableValues)"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="(value, key) in languagesSelection"
|
|
||||||
:value="key"
|
|
||||||
:key="key"
|
|
||||||
>
|
|
||||||
{{ value }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="type == 'contacttype'">
|
|
||||||
<input
|
|
||||||
class="form-control form-control-sm"
|
|
||||||
maxlength="200"
|
|
||||||
v-model="editableValues[valueKey].content"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-2 text-end">
|
|
||||||
<button
|
<button
|
||||||
|
v-if="this.editable"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm btn-light"
|
class="btn btn-outline-warning m-2"
|
||||||
aria-label="Löschen"
|
aria-label="Bearbeiten"
|
||||||
@click="$emit('removeValue', value[type].name)"
|
@click="this.$emit('editValue', value)"
|
||||||
>
|
>
|
||||||
<i class="text-danger bi bi-x-circle"></i>
|
<img
|
||||||
|
src="/img/bootstrap-icons-1.5.0/pencil.svg"
|
||||||
|
alt="Bearbeiten Icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="this.editable"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-danger m-2"
|
||||||
|
aria-label="Löschen"
|
||||||
|
@click="this.$emit('removeValue', value[type].id)"
|
||||||
|
>
|
||||||
|
<img src="/img/bootstrap-icons-1.5.0/trash.svg" alt="Löschen Icon" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import levelJson from '@/assets/skill_level.json';
|
import levelJson from "@/assets/skill_level.json"
|
||||||
import languagesJson from '@/assets/language_level.json';
|
import languagesJson from "@/assets/language_level.json"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ProfileList',
|
name: "ProfileList",
|
||||||
props: {
|
props: {
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
values: {
|
values: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
showSecondary: {
|
editable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
iconBaseUrl: this.apiUrl,
|
iconUrl: this.apiUrl,
|
||||||
levelSelection: levelJson,
|
levelSelection: levelJson,
|
||||||
languagesSelection: languagesJson,
|
languagesSelection: languagesJson,
|
||||||
editableValues: this.values,
|
}
|
||||||
};
|
}
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
values: function (newVal) {
|
|
||||||
this.editableValues = newVal;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scope>
|
|
||||||
.list-icon {
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<router-link
|
|
||||||
class="text-decoration-none d-flex"
|
|
||||||
:to="{ path: `/s/profile/${profile.user_id}` }"
|
|
||||||
>
|
|
||||||
<div class="card w-100 bg-white">
|
|
||||||
<div class="card-body d-flex">
|
|
||||||
<div class="d-flex align-items-center justify-content-center me-3">
|
|
||||||
<Avatar :name="profile.nickname"/>
|
|
||||||
</div>
|
|
||||||
<div class="text-body">
|
|
||||||
<h5 class="card-title mb-1 lh-1">
|
|
||||||
{{ profile.nickname}}
|
|
||||||
<span v-if="profile.pronouns"> ({{ profile.pronouns }})</span>
|
|
||||||
</h5>
|
|
||||||
<small
|
|
||||||
class="card-text lh-1 text-dark"
|
|
||||||
v-if="profile.skills && profile.skills.length > 0"
|
|
||||||
>
|
|
||||||
Top-Fähigkeiten: {{ topSkills }}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</router-link>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Avatar from '@/components/Avatar'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Avatar,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
profile: Object
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
topSkills() {
|
|
||||||
return this.profile.skills.slice(0, 5).map(s => s.skill.name).join(', ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.card:hover {
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,81 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="skill rounded me-2">
|
|
||||||
<div class="skill__left p-2">
|
|
||||||
<div class="skill__icon" :style="{ backgroundImage: iconUrl }"></div>
|
|
||||||
</div>
|
|
||||||
<div class="skill__right d-flex align-items-center rounded-end px-2">
|
|
||||||
<div>
|
|
||||||
<div class="skill__name fw-bold me-1">
|
|
||||||
{{ profileSkill.skill.name }}
|
|
||||||
</div>
|
|
||||||
<small class="skill__level" v-if="showLevel" :title="levelTitle">
|
|
||||||
{{ level }}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import levels from '@/assets/skill_level.json';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
profileSkill: Object,
|
|
||||||
showLevel: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
levels
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
iconUrl() {
|
|
||||||
return `url("${window.ki.apiUrl}/${this.profileSkill.skill.icon_url}")`
|
|
||||||
},
|
|
||||||
level() {
|
|
||||||
return levels[this.profileSkill.level].short
|
|
||||||
},
|
|
||||||
levelTitle() {
|
|
||||||
return levels[this.profileSkill.level].long
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.skill {
|
|
||||||
align-items: stretch;
|
|
||||||
border: 1px solid #acacac;
|
|
||||||
display: inline-flex;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill__icon {
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill__right {
|
|
||||||
background-color: #edefeb;
|
|
||||||
color: #202020;
|
|
||||||
}
|
|
||||||
|
|
||||||
.skill__name,
|
|
||||||
.skill__level {
|
|
||||||
display: inline-block;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,9 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="spinner-grow" role="status"></div>
|
|
||||||
</template>
|
|
@ -1,39 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="container text-center py-5">
|
|
||||||
<template v-if="notFound">
|
|
||||||
<div v-if="isOwnProfile">
|
|
||||||
<div class="fs-1 lh-1">nullptr :/</div>
|
|
||||||
<div class="fs-3 mb-4">Du hast noch kein Profil</div>
|
|
||||||
<router-link :to="{ name: 'ProfileEdit' }" class="btn btn-primary" >
|
|
||||||
Jetzt Profil erstellen
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<div class="fs-1 mb-3">nullptr :/</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
Profil nicht gefunden
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="fs-1 mb-3">Kernel panic :/</div>
|
|
||||||
Das Profil konnte nicht geladen werden
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
isOwnProfile: Boolean,
|
|
||||||
notFound: Boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="contact rounded d-inline-flex align-items-center">
|
|
||||||
<div class="contact__left px-2">
|
|
||||||
{{ profileContact.contacttype.name }}
|
|
||||||
</div>
|
|
||||||
<div class="contact__right d-flex align-items-center rounded-end px-2">
|
|
||||||
{{ profileContact.content }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
profileContact: Object,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.contact {
|
|
||||||
align-items: stretch;
|
|
||||||
border: 1px solid #acacac;
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact__right {
|
|
||||||
background-color: #edefeb;
|
|
||||||
color: #202020;
|
|
||||||
font-weight: bold;
|
|
||||||
height: 32px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,79 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="bg-wtf py-3">
|
|
||||||
<div class="container">
|
|
||||||
<div class="d-flex align-items-center mb-3">
|
|
||||||
<Avatar class="me-3" :name="profile.nickname" />
|
|
||||||
<div class="text-white fs-3">
|
|
||||||
<span class="fs-3">{{ profile.nickname }}</span>
|
|
||||||
<span v-if="profile.pronouns" class="fs-5">
|
|
||||||
({{ profile.pronouns }})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="profile?.address?.name">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<i class="fs-4 bi bi-person-fill text-dark mx-2"></i>
|
|
||||||
<div class="text-white">
|
|
||||||
a.k.a. {{ profile.address.name }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="location">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<i class="fs-4 bi bi-geo-alt-fill text-dark mx-2"></i>
|
|
||||||
<div class="text-white">
|
|
||||||
{{ location }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Avatar from '@/components/Avatar'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ProfileHeader',
|
|
||||||
components: {
|
|
||||||
Avatar
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
profile: Object
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
location() {
|
|
||||||
if (!this.profile.address) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = []
|
|
||||||
|
|
||||||
if (this.profile.address.postcode) {
|
|
||||||
parts.push(this.profile.address.postcode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.profile.address.city) {
|
|
||||||
parts.push(this.profile.address.city)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.profile.address.country) {
|
|
||||||
parts.push(this.profile.address.country)
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join(', ')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.content {
|
|
||||||
min-height: calc(100vh - 60px);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,69 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="language rounded me-2">
|
|
||||||
<div class="language__left px-2">
|
|
||||||
<div class="language__icon" :style="{ backgroundImage: iconUrl }"></div>
|
|
||||||
</div>
|
|
||||||
<div class="language__right d-flex align-items-center rounded-end px-2">
|
|
||||||
<div>
|
|
||||||
<div class="language__name me-1">{{ profileLanguage.language.name }}</div>
|
|
||||||
<small class="language__level">{{ level }}</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import levels from '@/assets/language_level.json';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
profileLanguage: Object,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
levels
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
iconUrl() {
|
|
||||||
return `url("${window.ki.apiUrl}/${this.profileLanguage.language.icon_url}")`
|
|
||||||
},
|
|
||||||
level() {
|
|
||||||
return levels[this.profileLanguage.level]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.language {
|
|
||||||
align-items: stretch;
|
|
||||||
border: 1px solid #acacac;
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language__icon {
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: contain;
|
|
||||||
height: 32px;
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language__right {
|
|
||||||
background-color: #edefeb;
|
|
||||||
color: #202020;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language__name {
|
|
||||||
display: inline-block;
|
|
||||||
font-weight: bold;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,28 +0,0 @@
|
|||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="container mb-5">
|
|
||||||
<h3 class="text-center">
|
|
||||||
{{ title }}
|
|
||||||
</h3>
|
|
||||||
<div class="card w-100">
|
|
||||||
<slot name="card-body">
|
|
||||||
<div class="card-body bg-white">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
title: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
12
src/main.js
@ -1,23 +1,17 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import { createApp } from 'vue/dist/vue.esm-bundler'
|
import { createApp } from 'vue/dist/vue.esm-bundler'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from '@/store'
|
|
||||||
import VTooltipPlugin from 'v-tooltip'
|
|
||||||
|
|
||||||
import 'v-tooltip/dist/v-tooltip.css'
|
import axios from 'axios'
|
||||||
import 'bootstrap-icons/font/bootstrap-icons.css'
|
|
||||||
import './assets/global.scss'
|
import './assets/global.scss'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(VTooltipPlugin)
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(store)
|
|
||||||
|
|
||||||
|
app.config.globalProperties.axios=axios
|
||||||
app.config.globalProperties.apiUrl = window.ki.apiUrl
|
app.config.globalProperties.apiUrl = window.ki.apiUrl
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
import store from '@/store'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
methods: {
|
|
||||||
async submitLogin() {
|
|
||||||
this.showErrorMessage = false;
|
|
||||||
try {
|
|
||||||
const data = JSON.stringify({
|
|
||||||
username: this.username,
|
|
||||||
password: this.password,
|
|
||||||
})
|
|
||||||
|
|
||||||
const response = await fetch(`${this.apiUrl}/users/login`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: data
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
this.showErrorMessage = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
store.commit('setCurrentUserId', parseInt(responseData.user_id, 10))
|
|
||||||
store.commit('setToken', responseData.token)
|
|
||||||
this.$router.push({ path: '/s/search' });
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
this.showErrorMessage = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async search() {
|
|
||||||
if (!this.searchText) {
|
|
||||||
this.searchResults = []
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${this.apiUrl}/${this.type}s?search=${this.searchText}`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${store.state.token}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.error();
|
|
||||||
this.showErrorMessage = true;
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
const searchResults = responseData[`${this.type}s`];
|
|
||||||
|
|
||||||
if (
|
|
||||||
!searchResults.map((item) => item.name.toLowerCase())
|
|
||||||
.includes(this.searchText.toLowerCase())
|
|
||||||
) {
|
|
||||||
searchResults.unshift({ name: this.searchText });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.searchResults = searchResults
|
|
||||||
} catch (error) {
|
|
||||||
console.error();
|
|
||||||
this.showErrorMessage = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async initEditPage() {
|
|
||||||
const userId = store.state.currentUserId
|
|
||||||
const url = `${this.apiUrl}/users/${userId}/profile`
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${store.state.token}`
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
|
|
||||||
this.profile = responseData.profile;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async submitFormEdit(isProfileVisible) {
|
|
||||||
this.showErrorMessage = false
|
|
||||||
this.showSuccessMessage = false
|
|
||||||
const userId = store.state.currentUserId
|
|
||||||
this.profile.visible = isProfileVisible;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const body = JSON.stringify(this.profile)
|
|
||||||
const response = await fetch(
|
|
||||||
`${this.apiUrl}/users/${userId}/profile`,
|
|
||||||
{
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${store.state.token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
this.showErrorMessage = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showSuccessMessage = true
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
this.showErrorMessage = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async initViewPage() {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${this.apiUrl}/users/${this.$route.params.memberId}/profile`, {
|
|
||||||
headers: { Authorization: `Bearer ${store.state.token}` },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
store.commit('setCurrentProfile', responseData.profile)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async submitSearch() {
|
|
||||||
this.showErrorMessage = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const url = new URL(`${this.apiUrl}/users/profiles`)
|
|
||||||
|
|
||||||
if (this.searchText != "") {
|
|
||||||
url.searchParams.append('nickname', this.searchText)
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${store.state.token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
this.showErrorMessage = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
|
|
||||||
this.searchResults = responseData.profiles;
|
|
||||||
this.searchTotal = responseData.total;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
this.showErrorMessage = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,11 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
import store from '@/store'
|
|
||||||
|
|
||||||
import Index from '@/views/Index.vue'
|
import Index from '@/views/Index.vue'
|
||||||
import Search from '@/views/Search.vue'
|
import Search from '@/views/Search.vue'
|
||||||
import Edit from '@/views/profile/Edit.vue'
|
import Edit from '@/views/profile/Edit.vue'
|
||||||
@ -19,7 +13,10 @@ const routes = [
|
|||||||
template: "<router-view/>",
|
template: "<router-view/>",
|
||||||
},
|
},
|
||||||
beforeEnter: (to, from, next) => {
|
beforeEnter: (to, from, next) => {
|
||||||
if (store.state.token){
|
console.log('test')
|
||||||
|
console.log('token', localStorage.getItem('token'))
|
||||||
|
if(localStorage.getItem('token') !== null){
|
||||||
|
console.log('next')
|
||||||
next()
|
next()
|
||||||
}else{
|
}else{
|
||||||
next({path: '/', query: {url: to.fullPath, access: false}})
|
next({path: '/', query: {url: to.fullPath, access: false}})
|
||||||
@ -46,14 +43,7 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
component: Index,
|
component: Index
|
||||||
beforeEnter: (_to, _from, next) => {
|
|
||||||
if (store.state.token) {
|
|
||||||
next({name: 'Search'})
|
|
||||||
} else {
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
import { createStore } from 'vuex'
|
|
||||||
|
|
||||||
import profile from './profile'
|
|
||||||
import search from './search'
|
|
||||||
|
|
||||||
const localStorageKeys = {
|
|
||||||
currentUserId: 'ki_current_user_id',
|
|
||||||
token: 'ki_token',
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createStore({
|
|
||||||
modules: {
|
|
||||||
profile,
|
|
||||||
search,
|
|
||||||
},
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
currentUserId: JSON.parse(localStorage.getItem(localStorageKeys.currentUserId)),
|
|
||||||
token: JSON.parse(localStorage.getItem(localStorageKeys.token)),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
clearCurrentUserId(state) {
|
|
||||||
state.currentUserId = null
|
|
||||||
localStorage.removeItem(localStorageKeys.currentUserId)
|
|
||||||
},
|
|
||||||
setCurrentUserId(state, currentUserId) {
|
|
||||||
state.currentUserId = currentUserId
|
|
||||||
localStorage.setItem(
|
|
||||||
localStorageKeys.currentUserId,
|
|
||||||
JSON.stringify(currentUserId)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
clearToken(state) {
|
|
||||||
state.token = null
|
|
||||||
localStorage.removeItem(localStorageKeys.token)
|
|
||||||
},
|
|
||||||
setToken(state, token) {
|
|
||||||
state.token = token
|
|
||||||
localStorage.setItem(localStorageKeys.token, JSON.stringify(token))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
clear(context) {
|
|
||||||
context.commit('clearCurrentUserId')
|
|
||||||
context.commit('clearToken')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
@ -1,120 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
export default {
|
|
||||||
namespaced: true,
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
showSpinner: false,
|
|
||||||
profileId: null,
|
|
||||||
profile: null,
|
|
||||||
isOwnProfile: false,
|
|
||||||
error: false,
|
|
||||||
notFound: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
setProfileId(state, profileId) {
|
|
||||||
state.profileId = profileId
|
|
||||||
},
|
|
||||||
clearProfileId(state) {
|
|
||||||
state.profileId = null
|
|
||||||
},
|
|
||||||
setProfile(state, profile) {
|
|
||||||
state.profile = profile
|
|
||||||
},
|
|
||||||
clearProfile(state) {
|
|
||||||
state.profile = null
|
|
||||||
},
|
|
||||||
setLoading(state) {
|
|
||||||
state.loading = true
|
|
||||||
},
|
|
||||||
setNotLoading(state) {
|
|
||||||
state.loading = false
|
|
||||||
},
|
|
||||||
setError(state) {
|
|
||||||
state.error = true
|
|
||||||
},
|
|
||||||
clearError(state) {
|
|
||||||
state.error = false
|
|
||||||
},
|
|
||||||
showSpinner(state) {
|
|
||||||
state.showSpinner = true
|
|
||||||
},
|
|
||||||
hideSpinner(state) {
|
|
||||||
state.showSpinner = false
|
|
||||||
},
|
|
||||||
setNotFound(state, notFound) {
|
|
||||||
state.notFound = notFound
|
|
||||||
},
|
|
||||||
setIsOwnProfile(state, isOwnProfile) {
|
|
||||||
state.isOwnProfile = isOwnProfile
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
onError({commit, dispatch}) {
|
|
||||||
commit('setError')
|
|
||||||
dispatch('clear')
|
|
||||||
},
|
|
||||||
onNotFound({commit, dispatch}) {
|
|
||||||
dispatch('onError')
|
|
||||||
commit('setNotFound', true)
|
|
||||||
},
|
|
||||||
clear({commit}) {
|
|
||||||
commit('clearProfileId')
|
|
||||||
commit('clearProfile')
|
|
||||||
commit('hideSpinner')
|
|
||||||
commit('setNotLoading')
|
|
||||||
},
|
|
||||||
async load({state, commit, dispatch, rootState}, profileId) {
|
|
||||||
if (state.loading) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
commit('setProfileId', profileId)
|
|
||||||
commit('setIsOwnProfile', rootState.currentUserId === profileId)
|
|
||||||
commit('setLoading')
|
|
||||||
|
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
commit('showSpinner')
|
|
||||||
commit('clearProfile')
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
commit('clearError')
|
|
||||||
commit('setNotFound', false)
|
|
||||||
|
|
||||||
const url = new URL(`${window.ki.apiUrl}/users/${profileId}/profile`)
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Bearer ${rootState.token}`
|
|
||||||
}
|
|
||||||
|
|
||||||
let response
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await fetch(url, {headers})
|
|
||||||
} catch {
|
|
||||||
dispatch('onError')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
clearTimeout(timeoutId)
|
|
||||||
|
|
||||||
if (response.status === 404) {
|
|
||||||
dispatch('onNotFound')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
dispatch('onError')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
commit('setProfile', responseData.profile)
|
|
||||||
commit('hideSpinner')
|
|
||||||
commit('setNotLoading')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
|
|
||||||
export default {
|
|
||||||
namespaced: true,
|
|
||||||
state() {
|
|
||||||
return {
|
|
||||||
searching: false,
|
|
||||||
showSpinner: false,
|
|
||||||
searched: false,
|
|
||||||
profiles: [],
|
|
||||||
error: false,
|
|
||||||
errorMessage: '',
|
|
||||||
pages: 1,
|
|
||||||
query: {
|
|
||||||
search: '',
|
|
||||||
page: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
setSearching(state, searching) {
|
|
||||||
state.searching = searching
|
|
||||||
},
|
|
||||||
showSpinner(state) {
|
|
||||||
state.showSpinner = true
|
|
||||||
},
|
|
||||||
hideSpinner(state) {
|
|
||||||
state.showSpinner = false
|
|
||||||
},
|
|
||||||
clearProfiles(state) {
|
|
||||||
state.profiles = []
|
|
||||||
},
|
|
||||||
setProfiles(state, profiles) {
|
|
||||||
state.profiles = profiles
|
|
||||||
},
|
|
||||||
setError(state, error) {
|
|
||||||
state.error = error
|
|
||||||
},
|
|
||||||
setErrorMessage(state, errorMessage) {
|
|
||||||
state.errorMessage = errorMessage
|
|
||||||
},
|
|
||||||
setQuerySearch(state, search) {
|
|
||||||
state.query = {...state.query, search}
|
|
||||||
},
|
|
||||||
setPages(state, pages) {
|
|
||||||
state.pages = pages
|
|
||||||
},
|
|
||||||
setQueryPage(state, page) {
|
|
||||||
state.query = {...state.query, page}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
async search({state, commit, rootState, dispatch}) {
|
|
||||||
if (state.searching) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
commit('setSearching', true)
|
|
||||||
|
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
commit('showSpinner')
|
|
||||||
commit('clearProfiles')
|
|
||||||
}, 100)
|
|
||||||
|
|
||||||
commit('setError', false)
|
|
||||||
commit('setErrorMessage', '')
|
|
||||||
|
|
||||||
const url = new URL(`${window.ki.apiUrl}/users/profiles`)
|
|
||||||
|
|
||||||
if (state.query.search) {
|
|
||||||
url.searchParams.append('search', state.query.search)
|
|
||||||
}
|
|
||||||
|
|
||||||
url.searchParams.append('page', state.query.page)
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Bearer ${rootState.token}`,
|
|
||||||
}
|
|
||||||
|
|
||||||
let response
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await fetch(url, {headers})
|
|
||||||
} catch {
|
|
||||||
commit('setError', true)
|
|
||||||
commit('clearProfiles')
|
|
||||||
commit('setSearching', false)
|
|
||||||
commit('hideSpinner')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(response.ok)
|
|
||||||
console.log(response.status)
|
|
||||||
console.log(state.query.page)
|
|
||||||
|
|
||||||
clearTimeout(timeoutId)
|
|
||||||
|
|
||||||
if (!response.ok && response.status === 404 && state.query.page != 1) {
|
|
||||||
commit('setQueryPage', 1)
|
|
||||||
commit('setSearching', false)
|
|
||||||
await dispatch('search')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
commit('setError', true)
|
|
||||||
commit('clearProfiles')
|
|
||||||
commit('setSearching', false)
|
|
||||||
commit('hideSpinner')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseData = await response.json()
|
|
||||||
commit('setProfiles', responseData.profiles)
|
|
||||||
commit('setPages', responseData.pages)
|
|
||||||
commit('setSearching', false)
|
|
||||||
commit('hideSpinner')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +1,11 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="bg-wtf">
|
<div class="container">
|
||||||
<div class="container pt-5">
|
<h1>Kompetenz Inventar by WTF eG</h1>
|
||||||
<div class="text-center mb-5">
|
<form @submit.prevent="submitLogin()">
|
||||||
<img class="wtf-logo wtf-logo--index" src="@/assets/img/wtf_logo.svg">
|
|
||||||
<h1 class="text-white">Kompetenzinventar</h1>
|
|
||||||
</div>
|
|
||||||
<form @submit.prevent="submitLogin()" class="card bg-white login-form">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="exampleInputusername1" class="form-label" >
|
<label for="exampleInputusername1" class="form-label"
|
||||||
WTF-Benutzername:
|
>E-Mail Aresse:
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="username"
|
type="username"
|
||||||
@ -23,7 +13,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
|||||||
id="exampleInputusername1"
|
id="exampleInputusername1"
|
||||||
v-model="username"
|
v-model="username"
|
||||||
required
|
required
|
||||||
autofocus
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -36,27 +25,28 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Login</button>
|
<button type="submit" class="btn btn-primary mb-4">Login</button>
|
||||||
<a class="btn btn-link" href="https://resetpw.wtf-eg.de/">Passwort vergessen?</a>
|
</form>
|
||||||
|
<a href="https://resetpw.wtf-eg.de/">Globales WTF Passwort zurücksetzen</a>
|
||||||
<div
|
<div
|
||||||
class="alert alert-danger mt-3 mb-0"
|
class="alert alert-danger mb-4 mt-4"
|
||||||
role="alert"
|
role="alert"
|
||||||
v-if="showErrorMessage"
|
v-if="showErrorMessage"
|
||||||
>
|
>
|
||||||
Dein Benutzername oder Passwort ist falsch.<br>Versuche es noch einmal.
|
Mit deinen Login Daten ist ein Fehler aufgetreten. Versuch es nochmal oder
|
||||||
</div>
|
<a href="https://resetpw.wtf-eg.de/">erzeuge ein neues Passwort</a>.
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p>
|
||||||
|
Das Login gilt nur für WTF eG Mitglieder. Du kannst dein Ldap Passwort
|
||||||
|
hier ändern.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RequestMixin from "@/mixins/request.mixin"
|
import axios from "axios";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Index",
|
name: "Index",
|
||||||
mixins: [RequestMixin],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showErrorMessage: false,
|
showErrorMessage: false,
|
||||||
@ -64,21 +54,35 @@ export default {
|
|||||||
password: "",
|
password: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
async submitLogin() {
|
||||||
|
this.showErrorMessage = false;
|
||||||
|
try {
|
||||||
|
console.log(this.apiUrl)
|
||||||
|
const loginResult = await axios.post(
|
||||||
|
`${this.apiUrl}/users/login`,
|
||||||
|
{
|
||||||
|
username: this.username,
|
||||||
|
password: this.password,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log(loginResult);
|
||||||
|
console.log(loginResult.status);
|
||||||
|
if (loginResult.status === 200) {
|
||||||
|
console.log('if true')
|
||||||
|
this.showErrorMessage = false;
|
||||||
|
//success login
|
||||||
|
localStorage.setItem("token", loginResult.data.token);
|
||||||
|
localStorage.setItem("user_id", loginResult.data.user_id);
|
||||||
|
this.$router.push({ path: "/s/search" });
|
||||||
|
} else {
|
||||||
|
this.showErrorMessage = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
this.showErrorMessage = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
.container {
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form {
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wtf-logo--index {
|
|
||||||
max-width: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,186 +1,102 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="content">
|
|
||||||
<div class="bg-wtf text-white pt-3 pb-4">
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="fs-3 text-center lh-1 mb-3">Finde WTF Member</div>
|
<h1>Suche</h1>
|
||||||
<div class="card mx-auto bg-white">
|
<form @submit.prevent="submitSearch()">
|
||||||
<div class="card-body">
|
<div class="row">
|
||||||
<form @submit.prevent="handleSubmit">
|
<div class="col">
|
||||||
<fieldset class="d-flex" :disabled="searching">
|
|
||||||
<div class="flex-grow-1 me-3">
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="searchText"
|
id="searchText"
|
||||||
v-model="searchText"
|
v-model="searchText"
|
||||||
placeholder="Nick, Name, Fähigkeit, Sprache"
|
|
||||||
ref="searchTextInput"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="col">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary mb-4">
|
||||||
<i class="bi-search"></i>
|
Suche Starten
|
||||||
<span class="d-none d-md-inline"> Suchen</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container pt-4 pb-3">
|
|
||||||
<div class="text-center" v-if="showSpinner">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="fs-2 text-danger text-center"
|
class="alert alert-danger mb-4 mt-4"
|
||||||
role="alert"
|
role="alert"
|
||||||
v-if="error"
|
v-if="showErrorMessage"
|
||||||
>
|
>
|
||||||
<div class="fs-1 mb-3">Kernel panic :/</div>
|
Bei der Suche ist ein Fehler aufgetreten
|
||||||
Bei der Suche ist ein Fehler aufgetreten.
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="showNoResults" class="fs-2 text-black-50 text-center">
|
<div v-if="searchTotal == 0">
|
||||||
<div class="fs-1 mb-3">nullptr :/</div>
|
|
||||||
Es wurde kein Suchergebnis gefunden.
|
Es wurde kein Suchergebnis gefunden.
|
||||||
<p v-if="searchText !== ''">Probiere eine andere Suche.</p>
|
<p v-if="searchText !== ''">Am besten suchst du weniger Spezifisch.</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="showResults">
|
<div v-else>
|
||||||
<div class="d-flex justify-content-around">
|
<div class="row">
|
||||||
<Paginator
|
<div
|
||||||
:pages="pages"
|
class="col-4 p-2"
|
||||||
:current="currentPage"
|
v-for="result in searchResults"
|
||||||
@page="handlePageSelected"
|
:key="result.user_id"
|
||||||
/>
|
>
|
||||||
|
<router-link
|
||||||
|
class="text-decoration-none"
|
||||||
|
:to="{ path: `/s/profile/${result.user_id}` }"
|
||||||
|
>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">
|
||||||
|
{{ result.nickname
|
||||||
|
}}<span v-if="result.pronouns"> ({{ result.pronouns }})</span>
|
||||||
|
</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
|
||||||
|
<p class="card-text" v-if="result.skills">
|
||||||
|
Fähigkeiten:
|
||||||
|
<span v-for="skill in result.skills" :key="skill.skill.name"
|
||||||
|
>{{ skill.skill.name }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<SearchResult
|
</div>
|
||||||
v-for="profile in profiles"
|
</router-link>
|
||||||
:key="profile.user_id"
|
|
||||||
class="mb-3"
|
|
||||||
:profile="profile"
|
|
||||||
/>
|
|
||||||
<div class="d-flex justify-content-around">
|
|
||||||
<Paginator
|
|
||||||
:pages="pages"
|
|
||||||
:current="currentPage"
|
|
||||||
@page="handlePageSelected"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Paginator from '@/components/Paginator'
|
|
||||||
import SearchResult from '@/components/SearchResult'
|
|
||||||
import Spinner from '@/components/Spinner'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Search',
|
name: "Search",
|
||||||
components: {
|
|
||||||
Paginator,
|
|
||||||
SearchResult,
|
|
||||||
Spinner,
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
textChanged: false
|
showErrorMessage: false,
|
||||||
}
|
searchText: "",
|
||||||
},
|
searchResults: null,
|
||||||
computed: {
|
searchTotal: 0,
|
||||||
...mapState({
|
};
|
||||||
searching: state => state.search.searching,
|
|
||||||
profiles: state => state.search.profiles,
|
|
||||||
error: state => state.search.error,
|
|
||||||
showSpinner: state => state.search.showSpinner,
|
|
||||||
pages: state => state.search.pages,
|
|
||||||
}),
|
|
||||||
searchText: {
|
|
||||||
get() {
|
|
||||||
return this.$store.state.search.query.search
|
|
||||||
},
|
|
||||||
set(text) {
|
|
||||||
this.$store.commit('search/setQuerySearch', text)
|
|
||||||
this.textChanged = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
currentPage: {
|
|
||||||
get() {
|
|
||||||
return this.$store.state.search.query.page
|
|
||||||
},
|
|
||||||
set(page) {
|
|
||||||
this.$store.commit('search/setQueryPage', page)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showNoResults() {
|
|
||||||
return !this.searching && (!this.profiles || this.profiles.length === 0)
|
|
||||||
},
|
|
||||||
showResults() {
|
|
||||||
return !this.error && this.profiles && this.profiles.length > 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
searching(value) {
|
|
||||||
if (!value) {
|
|
||||||
if (this.$refs.searchTextInput) {
|
|
||||||
this.focusSearchText()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleSubmit() {
|
|
||||||
if (this.textChanged === true) {
|
|
||||||
this.$store.commit('search/setQueryPage', 1)
|
|
||||||
}
|
|
||||||
this.pushState()
|
|
||||||
this.$store.dispatch('search/search')
|
|
||||||
},
|
|
||||||
focusSearchText() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.searchTextInput.focus()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
handlePageSelected(page) {
|
|
||||||
this.currentPage = page
|
|
||||||
this.pushState()
|
|
||||||
this.$store.dispatch('search/search')
|
|
||||||
},
|
|
||||||
pushState() {
|
|
||||||
this.$router.push({ query: { query: this.searchText, page: this.currentPage }})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.$route.query.query) {
|
if (this.$route.query.query) this.searchText = this.$route.query.query;
|
||||||
this.searchText = this.$route.query.query
|
this.submitSearch();
|
||||||
this.$store.commit('search/clearProfiles')
|
},
|
||||||
|
methods: {
|
||||||
|
async submitSearch() {
|
||||||
|
this.showErrorMessage = false;
|
||||||
|
try {
|
||||||
|
let url = `${this.apiUrl}/users/profiles`;
|
||||||
|
if (this.searchText != "") {
|
||||||
|
url += `?nickname=${this.searchText}`;
|
||||||
}
|
}
|
||||||
|
const result = await this.axios.get(url, {
|
||||||
if (this.$route.query.page) {
|
headers: {
|
||||||
this.currentPage = parseInt(this.$route.query.page, 10)
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||||
this.$store.commit('search/clearProfiles')
|
},
|
||||||
}
|
});
|
||||||
|
this.searchResults = result.data.profiles;
|
||||||
this.$store.dispatch('search/search')
|
this.searchTotal = result.data.total;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
this.showErrorMessage = true;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
.container {
|
|
||||||
max-width: 768px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
min-height: calc(100vh - 60px);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,253 +1,173 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
|
||||||
<div class="bg-wtf py-3 mb-4">
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h3 class="text-white text-center mb-0">Profil bearbeiten</h3>
|
<h1>Profil Ändern</h1>
|
||||||
|
<form @submit.prevent="submitForm()">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<input type="radio" id="false" :value="false" v-model="profile.visible" class="mr-2"/>
|
||||||
|
<label for="false" class="m-2 fw-bold"> Nicht Öffentlich</label>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input type="radio" id="true" :value="true" v-model="profile.visible" />
|
||||||
|
<label for="true" class="m-2 fw-bold"> Öffentlich</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div id="visibilityHelp" class="form-text">
|
||||||
<form @submit.prevent="submitFormEdit(false)">
|
Erst wenn du dein Profil Öffentlich stellst, können andere Genoss:innen darauf zugreifen oder es in der Suche finden.
|
||||||
<Section title="Grunddaten">
|
</div>
|
||||||
<div class="row mb-4">
|
<div class="row">
|
||||||
<div class="col-12 col-md-4 mb-3 mb-md-0">
|
<div class="col-6 col-xs-12">
|
||||||
<label class="form-label">Nickname</label>
|
<label for="nickname" class="form-label fw-bold">Nickname:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="nickname"
|
id="nickname"
|
||||||
maxlength="25"
|
|
||||||
v-model="profile.nickname"
|
v-model="profile.nickname"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4 mb-3 mb-md-0">
|
<div class="col-6 col-xs-12">
|
||||||
<label class="form-label">Klarname (optional)</label>
|
<label for="pronouns" class="form-label fw-bold">Pronomen:</label>
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="realname"
|
|
||||||
maxlength="25"
|
|
||||||
v-model="profile.address.name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
<label class="form-label">
|
|
||||||
Pronomen
|
|
||||||
<i class="bi bi-info-circle" v-tooltip="pronounsTooltip"></i>
|
|
||||||
</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="pronouns"
|
id="pronouns"
|
||||||
maxlength="25"
|
|
||||||
v-model="profile.pronouns"
|
v-model="profile.pronouns"
|
||||||
/>
|
/>
|
||||||
|
<div for="pronouns" class="form-text">
|
||||||
|
Z.B.: Er/Ihn, Sie/Ihr, Es etc..
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="form-label">Anschrift</label>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-4 mb-3 mb-md-0">
|
<div class="col-12 col-xs-12">
|
||||||
<input
|
<label for="freetext" class="form-label fw-bold">Freitext Vorstellung:</label>
|
||||||
type="text"
|
<textarea
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="postcode"
|
id="freetext"
|
||||||
maxlength="10"
|
rows="3"
|
||||||
placeholder="Postleitzahl"
|
v-model="profile.freetext"
|
||||||
v-model="profile.address.postcode"
|
></textarea>
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4 mb-3 mb-md-0">
|
<div class="col-12 col-xs-12">
|
||||||
<input
|
<label for="volunteerwork" class="form-label fw-bold"
|
||||||
type="text"
|
>Ehrentamtliche Arbeit:</label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="city"
|
id="volunteerwork"
|
||||||
maxlength="25"
|
rows="3"
|
||||||
placeholder="Ort"
|
v-model="profile.volunteerwork"
|
||||||
v-model="profile.address.city"
|
></textarea>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="col-12 col-md-4">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
id="country"
|
|
||||||
maxlength="25"
|
|
||||||
placeholder="Land"
|
|
||||||
v-model="profile.address.country"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Meine Fähigkeiten">
|
|
||||||
<template v-slot:card-body>
|
|
||||||
<auto-complete
|
<auto-complete
|
||||||
type="skill"
|
type="skill"
|
||||||
|
label="Deine Fähigkeiten (Autocomplete)"
|
||||||
:values="profile.skills"
|
:values="profile.skills"
|
||||||
placeholder="z.B. Python, JavaScript, Linux"
|
|
||||||
@update-values="profile.skills = $event"
|
@update-values="profile.skills = $event"
|
||||||
></auto-complete>
|
></auto-complete>
|
||||||
</template>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Meine Sprachkenntnisse">
|
|
||||||
<template v-slot:card-body>
|
|
||||||
<auto-complete
|
<auto-complete
|
||||||
type="language"
|
type="language"
|
||||||
|
label="Deine Sprachen (Autocomplete)"
|
||||||
:values="profile.languages"
|
:values="profile.languages"
|
||||||
placeholder="z.B. Deutsch, Englisch, Französisch"
|
|
||||||
@update-values="profile.languages = $event"
|
@update-values="profile.languages = $event"
|
||||||
></auto-complete>
|
></auto-complete>
|
||||||
</template>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Ich suche für mich Projekte/Aufträge in folgenden Bereichen">
|
|
||||||
<template v-slot:card-body>
|
|
||||||
<auto-complete
|
<auto-complete
|
||||||
type="skill"
|
type="skill"
|
||||||
label="Ich suche für mich Projekte/Aufträge in folgenden Bereichen"
|
label="Ich Suche"
|
||||||
:values="profile.searchtopics"
|
:values="profile.searchtopics"
|
||||||
:showSecondary="false"
|
|
||||||
placeholder="z.B. Python, JavaScript, Linux"
|
|
||||||
@update-values="profile.searchtopics = $event"
|
@update-values="profile.searchtopics = $event"
|
||||||
></auto-complete>
|
></auto-complete>
|
||||||
</template>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Verfügbarkeit">
|
<div class="col-12 col-xs-12">
|
||||||
<div class="form-check mb-3">
|
<label for="availability" class="form-label fw-bold"
|
||||||
<input
|
>Ich bin für Anfragen verfügbar:</label
|
||||||
v-model="profile.availability_status"
|
>
|
||||||
class="form-check-input me-2"
|
|
||||||
type="checkbox"
|
|
||||||
id="availability_status">
|
|
||||||
<label class="form-check-label" for="availability_status">
|
|
||||||
Ich bin aktuell verfügbar
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3" v-if="profile.availability_status">
|
|
||||||
<label class="form-label">
|
|
||||||
Stunden pro Woche
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-model="profile.availability_hours_per_week"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
max="168"
|
|
||||||
class="form-control">
|
|
||||||
</div>
|
|
||||||
<label for="availability" class="form-label">
|
|
||||||
Anmerkungen
|
|
||||||
</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="availability"
|
id="availability"
|
||||||
rows="3"
|
rows="3"
|
||||||
maxlength="4000"
|
v-model="profile.availability"
|
||||||
v-model="profile.availability_text"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
</Section>
|
</div>
|
||||||
|
|
||||||
<Section title="Meine Kontaktmöglichkeiten">
|
|
||||||
<template v-slot:card-body>
|
|
||||||
<auto-complete
|
<auto-complete
|
||||||
type="contacttype"
|
type="contacttype"
|
||||||
|
label="Kontaktmöglichkeiten"
|
||||||
:values="profile.contacts"
|
:values="profile.contacts"
|
||||||
placeholder="z.B. E-Mail, Mobiltelefon, Matrix, Web"
|
|
||||||
@update-values="profile.contacts = $event"
|
@update-values="profile.contacts = $event"
|
||||||
></auto-complete>
|
></auto-complete>
|
||||||
</template>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Sonstiges">
|
<div class="row">
|
||||||
<div class="mb-3 bg-white">
|
<div class="col">
|
||||||
<label class="form-label">Über mich</label>
|
<label for="pzl" class="form-label fw-bold">PLZ</label>
|
||||||
<textarea
|
<input
|
||||||
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
rows="3"
|
id="pzl"
|
||||||
maxlength="4000"
|
v-model="profile.address.postcode"
|
||||||
v-model="profile.freetext"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="col">
|
||||||
<label class="form-label">Ehrenamtliche Arbeit</label>
|
<label for="city" class="form-label fw-bold">Stadt</label>
|
||||||
<textarea
|
<input
|
||||||
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
rows="3"
|
id="city"
|
||||||
maxlength="4000"
|
v-model="profile.address.city"
|
||||||
v-model="profile.volunteerwork"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
<div class="col">
|
||||||
<input type="submit" class="d-none">
|
<label for="country" class="form-label fw-bold">Land</label>
|
||||||
</form>
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="country"
|
||||||
|
v-model="profile.address.country"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="savebar bg-white border-top py-3">
|
</div>
|
||||||
<div class="container d-flex align-items-center justify-content-end">
|
|
||||||
|
<button type="submit" class="btn btn-outline-success mb-4 mt-4 col-12">Speichern</button>
|
||||||
<div
|
<div
|
||||||
class="text-danger"
|
class="alert alert-danger mb-4 mt-4"
|
||||||
|
role="alert"
|
||||||
v-if="showErrorMessage"
|
v-if="showErrorMessage"
|
||||||
>
|
>
|
||||||
<i class="bi bi-bug"></i>
|
Es ist Fehler aufgetreten
|
||||||
Beim Speichern ist ein Fehler aufgetreten.
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-success"
|
class="alert alert-success mb-4 mt-4"
|
||||||
|
role="alert"
|
||||||
v-if="showSuccessMessage"
|
v-if="showSuccessMessage"
|
||||||
>
|
>
|
||||||
<i class="bi bi-check-lg"></i>
|
Deine Änderungen wurden erfolgreich gespeichert
|
||||||
Gespeichert
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary ms-3"
|
|
||||||
@click="submitFormEdit(false)"
|
|
||||||
>
|
|
||||||
Entwurf Speichern
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary ms-3"
|
|
||||||
@click="submitFormEdit(true)"
|
|
||||||
>
|
|
||||||
Speichern und Veröffentlichen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import RequestMixin from "@/mixins/request.mixin"
|
import axios from "axios";
|
||||||
|
|
||||||
import AutoComplete from "@/components/AutoComplete";
|
import AutoComplete from "@/components/AutoComplete";
|
||||||
import Section from '@/components/profile/Section'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "profileEdit",
|
name: "profileEdit",
|
||||||
mixins: [RequestMixin],
|
|
||||||
components: {
|
components: {
|
||||||
AutoComplete,
|
AutoComplete,
|
||||||
Section,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showErrorMessage: false,
|
showErrorMessage: false,
|
||||||
showSuccessMessage: false,
|
showSuccessMessage: false,
|
||||||
clearMessagesHandle: null,
|
|
||||||
profile: {
|
profile: {
|
||||||
visible: false,
|
visible: false,
|
||||||
nickname: "",
|
nickname: "",
|
||||||
pronouns: "",
|
pronouns: "",
|
||||||
volunteerwork: "",
|
volunteerwork: "",
|
||||||
freetext: "",
|
freetext: "",
|
||||||
availability_status: false,
|
availability: "",
|
||||||
availability_hours_per_week: null,
|
|
||||||
availability_text: "",
|
|
||||||
address: {
|
address: {
|
||||||
postcode: "",
|
postcode: "",
|
||||||
city: "",
|
city: "",
|
||||||
@ -258,49 +178,49 @@ export default {
|
|||||||
searchtopics: [],
|
searchtopics: [],
|
||||||
contacts: [],
|
contacts: [],
|
||||||
},
|
},
|
||||||
pronounsTooltip: {
|
|
||||||
content: 'Wie möchtest du angesprochen werden?<br>Zum Beispiel "er/ihn" oder "sie/ihre".',
|
|
||||||
html: true
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.initEditPage();
|
try {
|
||||||
},
|
const userProfile = await axios.get(
|
||||||
unmounted() {
|
`${this.apiUrl}/users/${localStorage.getItem(
|
||||||
this.cancelClearMessages()
|
"user_id"
|
||||||
|
)}/profile`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.profile = userProfile.data.profile;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cancelClearMessages() {
|
async submitForm() {
|
||||||
if (this.clearMessagesHandle) {
|
try {
|
||||||
window.clearTimeout(this.clearMessagesHandle)
|
const formSubmitResult = await axios.post(
|
||||||
|
`${this.apiUrl}/users/${localStorage.getItem(
|
||||||
|
"user_id"
|
||||||
|
)}/profile`,
|
||||||
|
this.profile,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (formSubmitResult.status === 200) {
|
||||||
|
// success
|
||||||
|
this.showSuccessMessage = true;
|
||||||
|
} else {
|
||||||
|
// failure
|
||||||
|
this.showErrorMessage = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
this.showErrorMessage = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scheduleClearMessages() {
|
|
||||||
this.cancelClearMessages()
|
|
||||||
this.clearMessagesHandle = window.setTimeout(() => {
|
|
||||||
this.showErrorMessage = false
|
|
||||||
this.showSuccessMessage = false
|
|
||||||
}, 2500)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
showErrorMessage(curr) {
|
|
||||||
if (curr) {
|
|
||||||
this.scheduleClearMessages()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showSuccessMessage(curr) {
|
|
||||||
if (curr) {
|
|
||||||
this.scheduleClearMessages()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
.savebar {
|
|
||||||
bottom: 0;
|
|
||||||
position: sticky;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,160 +1,59 @@
|
|||||||
<!--
|
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
||||||
SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div v-if="profile" class="container">
|
||||||
<template v-if="error">
|
<h1>{{profile.nickname}}({{profile.pronouns}})</h1>
|
||||||
<ViewError :isOwnProfile="isOwnProfile" :notFound="notFound" />
|
<p><label class="fw-bold">Vorstellung: </label> {{profile.freetext}}</p>
|
||||||
</template>
|
<p><label class="fw-bold">Ehrentamtliche Arbeit: </label> {{profile.volunteerwork}}</p>
|
||||||
<template
|
<p><label class="fw-bold">Verfügbarkeit: </label> {{profile.availability}}</p>
|
||||||
v-else-if="profile"
|
<h3>Meine Skills:</h3>
|
||||||
class="container">
|
<profile-list
|
||||||
<ProfileHeader
|
:values="profile.skills"
|
||||||
class="mb-4"
|
type="skill"
|
||||||
:profile="profile" />
|
></profile-list>
|
||||||
|
<h3>Ich Suche:</h3>
|
||||||
<Section
|
<profile-list
|
||||||
v-if="profile.skills && profile.skills.length > 0"
|
:values="profile.searchtopics"
|
||||||
title="Das kann ich">
|
type="skill"
|
||||||
<div style="margin-bottom: -.5rem;">
|
></profile-list>
|
||||||
<Skill
|
<h3>Meine Kontaktmöglichkeiten:</h3>
|
||||||
class="me-2 mb-2"
|
<profile-list
|
||||||
v-for="skill in profile.skills"
|
:values="profile.contacts"
|
||||||
:key="skill.skill.id"
|
type="contacttype"
|
||||||
:profileSkill="skill"
|
></profile-list>
|
||||||
:showLevel="true" />
|
<h3>Ich Spreche Folgende Sprachen:</h3>
|
||||||
</div>
|
<profile-list
|
||||||
</Section>
|
:values="profile.languages"
|
||||||
|
type="language"
|
||||||
<Section
|
></profile-list>
|
||||||
v-if="profile.searchtopics && profile.searchtopics.length > 0"
|
<h3>Meine Location:</h3>
|
||||||
title="Ich suche für mich Projekte/Aufträge in folgenden Bereichen">
|
{{profile.address.city}} ({{profile.address.postcode}}), {{profile.address.country}}
|
||||||
<div style="margin-bottom: -.5rem;">
|
|
||||||
<Skill
|
|
||||||
class="me-2 mb-2"
|
|
||||||
v-for="skill in profile.searchtopics"
|
|
||||||
:key="skill.skill.id"
|
|
||||||
:profileSkill="skill"
|
|
||||||
:showLevel="false" />
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section
|
|
||||||
v-if="profile.languages && profile.languages.length > 0"
|
|
||||||
title="Ich spreche diese Sprachen">
|
|
||||||
<div style="margin-bottom: -.5rem;">
|
|
||||||
<Language
|
|
||||||
class="me-2 mb-2"
|
|
||||||
v-for="language in profile.languages"
|
|
||||||
:key="language.language.id"
|
|
||||||
:profileLanguage="language"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section title="Verfügbarkeit">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div v-if="profile.availability_status">
|
|
||||||
<i class="bi bi-check-square me-1"></i>
|
|
||||||
ja
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<i class="bi bi-x-square me-1"></i>
|
|
||||||
nein
|
|
||||||
</div>
|
|
||||||
<span
|
|
||||||
class="ms-3"
|
|
||||||
v-if="profile.availability_status && profile.availability_hours_per_week">
|
|
||||||
({{ profile.availability_hours_per_week }} Stunden pro Woche)
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="profile.availability_text" class="mt-3">
|
|
||||||
<label class="form-label fw-bold">
|
|
||||||
Anmerkungen
|
|
||||||
</label>
|
|
||||||
<div class="line-break-text">{{ profile.availability_text }}</div>
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section
|
|
||||||
v-if="profile.contacts && profile.contacts.length > 0"
|
|
||||||
title="Meine Kontaktmöglichkeiten"
|
|
||||||
>
|
|
||||||
<div style="margin-bottom: -.5rem;">
|
|
||||||
<Contact
|
|
||||||
class="me-2 mb-2"
|
|
||||||
v-for="profileContact in profile.contacts"
|
|
||||||
:key="profileContact.id"
|
|
||||||
:profileContact="profileContact"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<Section
|
|
||||||
v-if="profile.volunteerwork || profile.freetext"
|
|
||||||
title="Sonstiges">
|
|
||||||
<div v-if="profile.freetext" :class="{ 'lh-base': true, 'mb-4': profile.volunteerwork }">
|
|
||||||
<h5>Über mich</h5>
|
|
||||||
<div class="line-break-text">{{ profile.freetext }}</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="profile.volunteerwork" class="lh-base">
|
|
||||||
<h5>Ehrenamtliche Arbeit</h5>
|
|
||||||
<div class="line-break-text">{{ profile.volunteerwork }}</div>
|
|
||||||
</div>
|
|
||||||
</Section>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState, mapActions } from 'vuex'
|
import ProfileList from "@/components/ProfileList";
|
||||||
|
|
||||||
import ViewError from '@/components/ViewError'
|
|
||||||
import ProfileHeader from '@/components/profile/Header'
|
|
||||||
import Section from '@/components/profile/Section'
|
|
||||||
import Contact from '@/components/profile/Contact'
|
|
||||||
import Language from '@/components/profile/Language'
|
|
||||||
import Skill from '@/components/Skill'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'profileView',
|
name: "profileView",
|
||||||
components: {
|
components: {
|
||||||
Contact,
|
ProfileList,
|
||||||
Language,
|
|
||||||
ProfileHeader,
|
|
||||||
Section,
|
|
||||||
Skill,
|
|
||||||
ViewError,
|
|
||||||
},
|
},
|
||||||
methods: {
|
data() {
|
||||||
...mapActions({
|
return {
|
||||||
loadProfile: 'profile/load',
|
profile: null
|
||||||
clearStore: 'profile/clear',
|
};
|
||||||
})
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
profile: state => state.profile.profile,
|
|
||||||
error: state => state.profile.error,
|
|
||||||
notFound: state => state.profile.notFound,
|
|
||||||
isOwnProfile: state => state.profile.isOwnProfile,
|
|
||||||
showSpinner: state => state.profile.showSpinner
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const id = parseInt(this.$route.params.memberId, 10)
|
try {
|
||||||
this.loadProfile(id)
|
const userProfile = await this.axios.get(
|
||||||
},
|
`${this.apiUrl}/users/${this.$route.params.memberId}/profile`,
|
||||||
unmounted() {
|
{
|
||||||
this.clearStore()
|
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.profile = userProfile.data.profile;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.line-break-text{
|
|
||||||
white-space: pre-line;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|