Compare commits

...

171 Commits

Author SHA1 Message Date
d2428a6f75 Merge pull request 'chore(deps): update fsfe/reuse docker tag to v5' (!144) from renovate/fsfe-reuse-5.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#144
2024-11-18 13:27:00 +01:00
511750cbab chore(deps): update fsfe/reuse docker tag to v5
All checks were successful
continuous-integration/drone/pr Build is passing
2024-11-14 10:36:45 +00:00
5664ede980 Merge pull request 'chore(deps): update linters' (!133) from renovate/linters into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#133
2024-11-04 16:33:32 +01:00
e03064852b chore(deps): update linters
All checks were successful
continuous-integration/drone/pr Build is passing
2024-11-04 16:30:45 +01:00
a159fd7b04 Merge pull request 'chore(deps): update dependency core-js to v3.39.0' (!138) from renovate/core-js-3.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#138
2024-11-04 16:29:53 +01:00
1cd0d4dc27 Merge pull request 'chore(deps): update dependency sass-loader to v16.0.3' (!139) from renovate/sass-loader-16.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#139
2024-11-04 16:29:29 +01:00
f969d2d061 chore(deps): update dependency sass-loader to v16.0.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-11-04 00:39:22 +00:00
b1a3852b86 chore(deps): update dependency core-js to v3.39.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-11-03 00:40:10 +00:00
958725ad73 Merge pull request 'chore(deps): update node.js to v20.18.0' (!127) from renovate/node-20.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#127
2024-10-29 11:05:26 +01:00
b14c4389d6 Merge pull request 'chore(deps): update dependency sass to v1.80.4' (!122) from renovate/sass-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#122
2024-10-28 22:19:13 +01:00
a39970216c Merge pull request 'chore(deps): update dependency sass-embedded to v1.80.4' (!131) from renovate/sass-embedded-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#131
2024-10-28 22:09:58 +01:00
c3beea3814 chore(deps): update dependency sass-embedded to v1.80.4
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-10-26 23:50:20 +00:00
6a4026e7cb chore(deps): update dependency sass to v1.80.4
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-10-26 23:43:20 +00:00
7ceaf18d53 Merge pull request 'chore(deps): update dependency sass-embedded to v1.79.4' (!123) from renovate/sass-embedded-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#123
2024-10-14 18:47:04 +02:00
6dabb6ea81 Merge pull request 'chore(deps): update nginx:1.27-alpine docker digest to 2140dad' (!126) from renovate/nginx-1.27-alpine into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#126
2024-10-14 18:03:19 +02:00
062c47be14 Merge pull request 'chore(deps): update dependency sass-loader to v16.0.2' (!121) from renovate/sass-loader-16.x into main
Some checks are pending
continuous-integration/drone/push Build is pending
Reviewed-on: kompetenzinventar/ki-frontend#121
2024-10-14 18:01:54 +02:00
6ef06e7b27 Merge pull request 'chore(deps): update linters' (!128) from renovate/linters into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#128
2024-10-14 18:01:35 +02:00
ceabff143b chore(deps): update linters
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-10-14 06:47:58 +00:00
df437fad47 chore(deps): update node.js to v20.18.0
All checks were successful
continuous-integration/drone/pr Build is passing
2024-10-05 01:36:13 +00:00
dcf5c81029 chore(deps): update nginx:1.27-alpine docker digest to 2140dad
All checks were successful
continuous-integration/drone/pr Build is passing
2024-10-03 06:36:00 +00:00
8582ef41e5 chore(deps): update dependency sass-embedded to v1.79.4
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-10-01 03:36:23 +00:00
5167716f85 chore(deps): update dependency sass-loader to v16.0.2
All checks were successful
continuous-integration/drone/pr Build is passing
renovate/stability-days Updates have met minimum release age requirement
2024-09-20 12:59:29 +00:00
0957580308 Merge pull request 'chore(deps): update vue monorepo to v3.5.6' (!119) from renovate/vue-monorepo into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#119
2024-09-19 21:38:42 +02:00
47294f9e3e Merge pull request 'chore(deps): update dependency eslint to v8.57.1' (!120) from renovate/linters into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#120
2024-09-19 20:15:33 +02:00
62aec412dd chore(deps): update dependency eslint to v8.57.1
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-19 15:36:14 +00:00
0d738885e8 chore(deps): update vue monorepo to v3.5.6
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-19 08:36:14 +00:00
7d19f57fb0 Merge pull request 'chore(deps): update dependency vue-router to v4.4.5' (!117) from renovate/vue-router-4.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#117
2024-09-16 16:17:04 +02:00
2a40a203cf chore(deps): update dependency vue-router to v4.4.5
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-16 07:36:00 +00:00
808d155a85 Merge pull request 'chore(deps): update vue monorepo to v3.5.4' (!110) from renovate/vue-monorepo into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#110
2024-09-13 12:15:15 +02:00
5aee683180 chore(deps): update vue monorepo to v3.5.4
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-13 09:36:13 +00:00
fc352864cc Merge pull request 'chore(deps): update dependency sass-embedded to v1.78.0' (!112) from renovate/sass-embedded-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#112
2024-09-10 13:10:05 +02:00
cc22f6767f Merge pull request 'chore(deps): update dependency sass to v1.78.0' (!111) from renovate/sass-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#111
2024-09-10 12:58:15 +02:00
99fe35dee4 Merge pull request 'Speedup CI runs' (!116) from ci-speedup into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#116
2024-09-10 12:53:34 +02:00
f6673d5819 Merge pull request 'chore(deps): update nginx:1.27-alpine docker digest to a5127da' (!113) from renovate/nginx-1.27-alpine into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#113
2024-09-10 12:48:34 +02:00
c58c727ff9 chore(deps): update dependency sass to v1.78.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-10 10:36:03 +00:00
41f079fc03
Switch to alpine image of Node.js in CI
All checks were successful
continuous-integration/drone/pr Build is passing
2024-09-10 12:30:19 +02:00
31b4e21034
Only run Docker dry-run on Pull Requests 2024-09-10 12:27:33 +02:00
acb5ab5f40 Merge pull request 'chore(deps): update node.js to 2d07db0' (!114) from renovate/node-20.17.0-alpine into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#114
2024-09-10 12:18:44 +02:00
66bf9d9a98 Merge pull request 'chore(deps): update node.js to 48db4f6' (!108) from renovate/node-20.17.0 into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#108
2024-09-10 12:18:30 +02:00
32f9d792e4 Merge pull request 'chore(deps): update dependency eslint-plugin-vue to v9.28.0' (!109) from renovate/linters into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#109
2024-09-10 12:16:22 +02:00
8e395d9c16 chore(deps): update nginx:1.27-alpine docker digest to a5127da
All checks were successful
continuous-integration/drone/pr Build is passing
2024-09-07 15:35:54 +00:00
86db7774ea chore(deps): update node.js to 2d07db0
All checks were successful
continuous-integration/drone/pr Build is passing
2024-09-07 10:35:52 +00:00
b0b29c77df chore(deps): update dependency sass-embedded to v1.78.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-06 23:36:17 +00:00
d2075123a2 chore(deps): update node.js to 48db4f6
All checks were successful
continuous-integration/drone/pr Build is passing
2024-09-06 04:35:47 +00:00
71ca40dc61 chore(deps): update dependency eslint-plugin-vue to v9.28.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-09-06 02:36:06 +00:00
ee6097bc10 Merge pull request 'chore(deps): update node.js to 4bc7ea5' (!107) from renovate/node-20.17.0 into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#107
2024-09-05 11:30:02 +02:00
d90bc32c53 chore(deps): update node.js to 4bc7ea5
All checks were successful
continuous-integration/drone/pr Build is passing
2024-09-05 06:35:44 +00:00
b25e0c2ce5 Merge pull request 'Run linter in CI without fixing issues' (!105) from lint-no-fix into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#105
2024-08-28 15:22:13 +02:00
bdaec983d8
Run linter in CI without fixing issues
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-28 15:00:45 +02:00
4850d79f54 Merge pull request 'chore(deps): update fsfe/reuse docker tag to v4' (!102) from renovate/fsfe-reuse-4.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#102
2024-08-27 18:42:00 +02:00
df8b79bc53
Specify license for REUSE.toml
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-27 18:36:30 +02:00
348b589d6b
Convert .reuse/dep5 to REUSE.toml
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-27 18:32:07 +02:00
312acee0f2 Merge pull request 'chore(deps): lock file maintenance' (!104) from renovate/lock-file-maintenance into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#104
2024-08-27 11:42:27 +02:00
f5b7fe8f12 chore(deps): lock file maintenance
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-26 00:36:14 +00:00
9bf644161d Merge pull request 'chore(deps): update plugins/docker docker tag to v20.18.4' (!103) from renovate/plugins-docker-20.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#103
2024-08-22 19:46:33 +02:00
4cd12bfe59 chore(deps): update plugins/docker docker tag to v20.18.4
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 15:36:04 +00:00
f5b7fd3dee chore(deps): update fsfe/reuse docker tag to v4
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 14:36:29 +00:00
bd4242a7e1 Merge pull request 'chore(deps): update dependency sass-loader to v16' (!101) from renovate/sass-loader-16.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#101
2024-08-22 15:52:51 +02:00
93c4fac780
chore(deps): Add optional (preferred) dependency
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 15:48:05 +02:00
b2b295df43 Merge pull request 'chore(deps): update dependency eslint to v8' (!98) from renovate/major-8-linters into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#98
2024-08-22 15:40:54 +02:00
cac4ceb173 chore(deps): update dependency sass-loader to v16
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-08-22 13:36:33 +00:00
aed94f4237 chore(deps): update dependency eslint to v8
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-08-22 12:36:22 +00:00
efd3d5bca5 Merge pull request 'chore(deps): update plugins/docker docker tag to v20.18.3' (!97) from renovate/plugins-docker-20.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#97
2024-08-22 13:56:10 +02:00
cf5eb08db6 Merge pull request 'chore(deps): update node.js to v20.17.0' (!96) from renovate/node-20.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#96
2024-08-22 13:44:57 +02:00
a4891afa1d chore(deps): update plugins/docker docker tag to v20.18.3
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 11:36:05 +00:00
087adf71c1 chore(deps): update node.js to v20.17.0
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 11:35:56 +00:00
83164e3f54 Merge pull request 'chore(deps): update nginx docker tag to v1.27' (!95) from renovate/nginx-1.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#95
2024-08-22 13:21:20 +02:00
27f399b0f3 Merge pull request 'chore(deps): update linters' (!94) from renovate/linters into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#94
2024-08-22 13:12:38 +02:00
da20c22ba4 chore(deps): update nginx docker tag to v1.27
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 10:36:17 +00:00
9a056b6977 chore(deps): update linters
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
continuous-integration/drone/pr Build is passing
2024-08-22 10:36:06 +00:00
fccf9a55b6 Merge pull request 'chore(deps): pin dependencies' (!92) from renovate/pin-dependencies into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#92
2024-08-22 11:56:38 +02:00
775cf303d1 chore(deps): pin dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-22 09:36:14 +00:00
abd1e0ee31 Merge pull request 'chore: Configure Renovate' (!91) from renovate/configure into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#91
2024-08-22 11:26:15 +02:00
563f492bc3
Disable Dependency Dashboard because of external issues
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 22:12:43 +02:00
72d6756749
Update Renovate config
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 22:01:01 +02:00
d1fdde6d10 Add renovate.json
Some checks failed
continuous-integration/drone/pr Build is failing
2024-08-21 19:35:10 +00:00
a1c758a4b6 Merge pull request 'Upgrade dependencies' (!90) from upgrade-dependencies into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#90
2024-08-21 20:28:45 +02:00
c463c2f1ad
Use Bootstrap classes
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 20:25:28 +02:00
eb5de926d0
chore(deps): Update dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 19:49:40 +02:00
5c4c76356a
Merge remote-tracking branch 'origin/main' into upgrade-dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
2024-08-21 19:40:45 +02:00
61a6115071
Pin Docker images in CI 2024-08-21 19:33:50 +02:00
d46aaa0f31 Merge pull request 'Fix Dockerfile' (!89) from fix-dockerfile into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#89
2024-04-23 23:07:43 +02:00
81dcf5ebe7
Try to build Docker image for Pull Requests
All checks were successful
continuous-integration/drone/pr Build is passing
2024-04-23 22:48:01 +02:00
7433d25716
Add missing trailing slash 2024-04-23 22:47:54 +02:00
7feee07d35 Merge pull request '♻️ Optimize Dockerfile' (!85) from optimize-dockerfile into main
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: kompetenzinventar/ki-frontend#85
Reviewed-by: srsh <srsh@noreply.git.wtf-eg.de>
2024-04-23 22:26:14 +02:00
f728f1f72b Merge branch 'main' into feature/upgrade-dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
2024-02-21 20:21:57 +01:00
663db94cb8 ISSUE-72| Move audit to qa in drone
All checks were successful
continuous-integration/drone/pr Build is passing
2024-02-21 09:42:31 +01:00
a49bd3feef ISSUE-72| Update more deps
All checks were successful
continuous-integration/drone/pr Build is passing
2024-02-21 06:40:53 +01:00
6f12f577d4 ISSUE-72| Update more deps
Some checks failed
continuous-integration/drone/pr Build is failing
2024-02-21 06:31:13 +01:00
872ff894b5 ISSUE-72| Update coreks
All checks were successful
continuous-integration/drone/pr Build is passing
2024-02-20 21:07:53 +01:00
0b02df26e8 ISSUE-72| Update eslint
All checks were successful
continuous-integration/drone/pr Build is passing
2024-02-20 21:01:15 +01:00
17e43d94c4 ISSUE-72| Update eslint
Some checks failed
continuous-integration/drone/pr Build is failing
2024-02-20 20:58:50 +01:00
c79ada2123 ISSUE-58| Move profile visiblity (!87)
All checks were successful
continuous-integration/drone/push Build is passing
Move switch profile visibility button to save bar
Rename Save button to save as draft and offer save and publish

Fixes kompetenzinventar/ki-doku#58

Reviewed-on: kompetenzinventar/ki-frontend#87
Reviewed-by: srsh <srsh@noreply.git.wtf-eg.de>
Co-authored-by: Nikolai Gotzmann <self@ngotzmann.net>
Co-committed-by: Nikolai Gotzmann <self@ngotzmann.net>
2024-02-20 20:26:54 +01:00
7b0e38296f Add pre requirements to readme
All checks were successful
continuous-integration/drone/pr Build is passing
2024-02-11 15:09:35 +01:00
bc1a079503 KI-frontend update
* Add better npm audit to pipeline
* pipeline will fail if vulneralabity is found higher then moderate
2024-02-11 11:52:11 +01:00
cd36d39fad KI Frontend update| Update vue-compiler-sfc 2024-02-11 10:06:03 +01:00
ba701b2ac8 KI Frontend update| Update sass 2024-02-11 10:02:50 +01:00
9d35989ba9 Updating ki-frontend
* Frontend works with node 20
* vue dependencies are updated
2024-02-11 09:48:56 +01:00
f8e78d1cc8 ♻️ Optimize Dockerfile
All checks were successful
continuous-integration/drone/pr Build is passing
- add all non-essential files to .dockerignore
- improve cachability by layers
- re-order lines to reduce layer generation
2024-01-12 18:42:27 +01:00
c2552f3c3a Merge pull request 'Push image to Gitea registry' (!84) from gitea-registry into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#84
2023-12-10 18:47:54 +01:00
687454afdb
Push image to Gitea registry
All checks were successful
continuous-integration/drone/pr Build is passing
2023-12-04 20:09:52 +01:00
a7c8774cc4 Merge pull request 'Add labels to Docker images' (!83) from docker-labels into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#83
2023-11-16 11:18:44 +01:00
b63e5a6c2d Merge pull request 'Rewrite Drone config' (!82) from drone-config into main
Some checks are pending
continuous-integration/drone/push Build is pending
Reviewed-on: kompetenzinventar/ki-frontend#82
2023-11-16 11:18:35 +01:00
1b221ab180 Merge pull request 'Improve SPA cacheing' (!81) from nginx-cacheing into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#81
2023-11-13 16:51:10 +01:00
e2b101eb89
Add labels to Docker images
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-11-07 14:54:40 +01:00
654169c383
Rewrite Drone config
All checks were successful
continuous-integration/drone/pr Build is passing
- Split PR and "push to main" actions
- Deploy latest main to dev system
- Build image for Git tags
2023-11-06 23:33:33 +01:00
26edf1d4b2
Improve SPA cacheing
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-11-06 18:35:05 +01:00
e7ff487aeb Merge pull request '"Problem melden"-Link korrigiert' (!78) from brain-patch-1 into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#78
2023-10-27 15:12:55 +02:00
c5bda80f11 Merge pull request 'Klarnamen in der Suche berücksichtigen' (!80) from zeitschlag/ki-frontend:feature/48-search-address-name into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#80
2023-10-27 15:12:06 +02:00
379ddaf5b9 Change placeholder (kompetenzinventar/ki-doku#48)
All checks were successful
continuous-integration/drone/pr Build is passing
2023-04-20 18:14:07 +02:00
13fb15e033 Fix link for new issues 2022-07-20 18:26:39 +02:00
aaa0883692 Merge pull request 'add tag trigger' (!77) from feature/tag-trigger into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#77
2022-02-28 19:32:33 +01:00
e404bef2a9
add tag trigger
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-28 19:32:04 +01:00
cbbcfd0f37 Merge pull request 'add address.name to frontend' (!76) from feature/realname into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#76
2022-01-26 22:03:08 +01:00
5c5f157a77
add address.name to frontend
All checks were successful
continuous-integration/drone/push Build is passing
2022-01-25 21:04:17 +01:00
3017c001b2 Merge pull request 'Paginierung' (!74) from feature/pagination into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#74
2022-01-24 19:46:59 +01:00
fefe9a034d Merge pull request 'Aktualisierung NPM Pakete' (!75) from fix/npm-update into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#75
2022-01-24 19:11:50 +01:00
bac8731e17
add pagination
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-01-23 20:14:52 +01:00
dc883ac302
update npm packages
All checks were successful
continuous-integration/drone/push Build is passing
2022-01-23 19:56:14 +01:00
16feb41f8a kompetenzinventar/ki-doku#18 line-break new text (!72)
All checks were successful
continuous-integration/drone/push Build is passing
Co-authored-by: scammo <samuel.brinkmann@googlemail.com>
Reviewed-on: kompetenzinventar/ki-frontend#72
Reviewed-by: weeman <weeman@noreply.git.wtf-eg.de>
Co-authored-by: scammo <scammo@noreply.git.wtf-eg.de>
Co-committed-by: scammo <scammo@noreply.git.wtf-eg.de>
2022-01-17 14:44:13 +01:00
04cb8a7217 Merge pull request '#64 "Ich Suche" besser definiert' (#71) from profile-page into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#71
2022-01-14 13:10:10 +01:00
e3115f9944 Merge branch 'main' into profile-page
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2022-01-14 13:09:46 +01:00
scammo
122b13b6a2 #64 ich besser definiert
All checks were successful
continuous-integration/drone/push Build is passing
2022-01-14 12:53:23 +01:00
872d544075 Merge pull request 'Fix typo for language levels' (#67) from zeitschlag/ki-frontend:typo into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#67
2021-12-17 20:41:45 +01:00
fba33d20a7 Fix typo for language levels 2021-12-17 18:29:54 +01:00
e035fa3289
extend skill length to 50
All checks were successful
continuous-integration/drone/push Build is passing
2021-11-22 20:14:45 +01:00
324203216a
fix typo 2021-11-22 18:48:13 +01:00
b270a5d56a Merge pull request 'Weitere Arbeiten Profilseite' (#53) from profile-page into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#53
2021-10-25 14:15:38 +02:00
6ecf80f34c
improve profile page #36
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-24 18:16:19 +02:00
016a1bd959 Merge pull request 'Profile bearbeiten Ansicht' (#52) from feature/profile-view into main
Reviewed-on: kompetenzinventar/ki-frontend#52
2021-10-18 20:47:23 +02:00
4e8390cf96
update profile edit view 2021-10-18 20:45:18 +02:00
04d59d5520 Merge pull request 'Verfügbarkeit: Status, Stunden pro Woche und Text' (#50) from availability into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#50
2021-10-11 18:57:38 +02:00
0f0d3cd861
implement advanced availability logic
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-11 18:55:19 +02:00
2b63603957 Merge pull request 'fix profile display' (#49) from fix/48-profile into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#49
2021-10-11 09:43:07 +02:00
9a51b416e5
run drone only for main branch
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-10 19:59:07 +02:00
3ea7eb48b4
fix profile display
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-10-10 19:41:45 +02:00
8c8021bedc Merge pull request 'Verschiedenes seit Freitag' (#47) from freitag into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#47
2021-10-04 17:42:09 +02:00
46fcaa2db6
implement profile view
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-10-03 20:05:08 +02:00
2d700c77dc
redirect users with token #38 2021-10-03 20:05:07 +02:00
c1d78fa8c1
implement seach view autofocus #41 2021-10-03 20:05:07 +02:00
7c8a1bb423
add login page name field autofocus #42 2021-10-03 20:05:06 +02:00
8e42c7fdbe Merge pull request 'Suchparameter in URL' (#46) from feature/44-suchparameter-in-url into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#46
2021-09-27 18:02:23 +02:00
scammo
c25639b40c Suchparameter in URL
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-09-27 17:50:01 +02:00
a2048d0eb9 Merge pull request 'Überarbeitung Suchansicht' (#37) from feature-search into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#37
2021-09-23 16:57:31 +02:00
scammo
b19a770d61 no more search in navbar
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-09-23 16:57:09 +02:00
scammo
8098c54c06 Merge branch 'feature-search' of git.wtf-eg.de:kompetenzinventar/ki-frontend into feature-search 2021-09-22 00:09:27 +02:00
73847022e2
implement search layout #32 #33 #35
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-09-22 00:01:17 +02:00
93cb302ca7
implement search layout #32 #33 #35
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-09-21 23:58:42 +02:00
fcbf3ee796 Merge branch 'sonntag' into main
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2021-09-20 16:45:54 +02:00
scammo
fee786f1c8 bessere anzeige search
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-20 16:44:44 +02:00
b190a3764b Merge pull request 'Sonntagsarbeiten' (#30) from sonntag into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#30
2021-09-20 11:15:20 +02:00
a408cae686
introduce vuex
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-09-19 17:31:46 +02:00
94d2a98b96
apply navbar style #15
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-19 16:38:43 +02:00
a1546849c8
apply login view design #29
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-19 16:01:45 +02:00
a13314327c
replace axios by fetch #13 2021-09-19 15:55:18 +02:00
ebb0783103
add reuse compliance #5
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-19 13:04:03 +02:00
6a5b468bb2
fix vue 404 #26 2021-09-19 12:19:40 +02:00
ec8bc47ec7
style footer #14 2021-09-19 11:50:30 +02:00
c73534f448
update boostrap 2021-09-19 10:42:59 +02:00
scammo
94b1d52fbb #17 fix anzeige
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-15 21:15:09 +02:00
scammo
d1026fafbb [BUG] Auch leere Kontakte können editiert sein
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-15 21:06:53 +02:00
scammo
46d4c596e9 Neuer Tab für Issues
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-15 21:02:51 +02:00
scammo
96cfd18d83 #27
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-15 20:54:08 +02:00
scammo
4cb045d29f Merge branch 'main' of git.wtf-eg.de:kompetenzinventar/ki-frontend into main
All checks were successful
continuous-integration/drone/push Build is passing
2021-09-13 18:40:57 +02:00
scammo
2cf781b11d Anzeige mein Profil 2021-09-13 18:40:50 +02:00
b1f28e5d55 Merge pull request 'Mehr Prosa über die Aufteilung und wie man mitmachen kann' (#25) from 64bit/ki-frontend:mitmachen into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#25
2021-09-13 11:07:41 +02:00
Benedikt Brückmann
b40913b331 Mehr Prosa über die Aufteilung und wie man mitmachen kann
All checks were successful
continuous-integration/drone/pr Build is passing
2021-09-13 10:17:45 +02:00
scammo
5b3252ec98 Merge branch 'main' of git.wtf-eg.de:kompetenzinventar/ki-frontend into main
All checks were successful
continuous-integration/drone/push Build is passing
2021-08-18 22:59:50 +02:00
scammo
f0ef4e7f13 #13 2021-08-18 22:59:44 +02:00
3fbc64d9f8 Merge pull request 'editorconfig' (#24) from feature-editorconfig into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: kompetenzinventar/ki-frontend#24
2021-08-18 14:52:17 +02:00
scammo
513d204d58 #17 #16
All checks were successful
continuous-integration/drone/push Build is passing
2021-08-18 14:34:28 +02:00
62 changed files with 13023 additions and 24584 deletions

View File

@ -1,2 +1,11 @@
.browserslistrc
.dockerignore
.drone.yml
.editorconfig
.git .git
.gitignore
.reuse
Dockerfile
LICENSES
README.md
node_modules node_modules

View File

@ -4,14 +4,115 @@
# 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: default name: qa
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 image: plugins/docker:20.18.4@sha256:a8d3d86853c721492213264815f1d00d3ed13f42f5c1855a02f47fa4d5f1e042
settings: settings:
registry: registry.wtf-eg.net registry: git.wtf-eg.de
repo: registry.wtf-eg.net/ki-frontend repo: git.wtf-eg.de/kompetenzinventar/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:

View File

@ -8,10 +8,13 @@ module.exports = {
'eslint:recommended' 'eslint:recommended'
], ],
parserOptions: { parserOptions: {
parser: 'babel-eslint' parser: '@babel/eslint-parser'
}, },
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
View File

@ -23,3 +23,4 @@ pnpm-debug.log*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
jsconfig.json

View File

@ -1,10 +0,0 @@
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: ...

View File

@ -1,10 +1,28 @@
FROM node:14-alpine as builder # SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
COPY . ./ FROM node:20.18.0-alpine@sha256:c13b26e7e602ef2f1074aef304ce6e9b7dd284c419b35d89fcf3cc8e44a8def9 as builder
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 as ki-frontend FROM nginx:1.27-alpine@sha256:2140dad235c130ac861018a4e13a6bc8aea3a35f3a40e20c1b060d51a7efd250 as ki-frontend
COPY --from=builder /dist/ /usr/share/nginx/html/ LABEL org.opencontainers.image.source=https://git.wtf-eg.de/kompetenzinventar/ki-frontend.git
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 .

View File

@ -0,0 +1 @@
Alle Rechte vorbehalten

View File

@ -1,9 +0,0 @@
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.

43
LICENSES/OFL-1.1-RFN.txt Normal file
View File

@ -0,0 +1,43 @@
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.

View File

@ -1,14 +1,54 @@
<!--
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)
Vue3 Projekt: ## Über
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
``` ```
@ -16,7 +56,6 @@ 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 Normal file
View File

@ -0,0 +1,40 @@
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"

View File

@ -1,3 +1,7 @@
// 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'

View File

@ -0,0 +1,63 @@
# 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;
#}
}

34301
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,31 @@
{ {
"name": "@wtf/ki-frontend", "name": "@wtf/ki-frontend",
"version": "0.1.0", "version": "1.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": {
"@vue/cli-plugin-babel": "~4.5.0", "@babel/eslint-parser": "7.25.9",
"@vue/cli-plugin-eslint": "~4.5.0", "@vue/cli-plugin-babel": "5.0.8",
"@vue/cli-plugin-router": "~4.5.0", "@vue/cli-plugin-eslint": "5.0.8",
"@vue/cli-service": "~4.5.0", "@vue/cli-plugin-router": "5.0.8",
"@vue/compiler-sfc": "^3.0.0", "@vue/cli-service": "5.0.8",
"axios": "^0.21.1", "@vue/compiler-sfc": "3.5.6",
"babel-eslint": "^10.1.0", "bootstrap": "5.3.3",
"bootstrap": "^5.0.1", "bootstrap-icons": "1.11.3",
"core-js": "^3.6.5", "core-js": "3.39.0",
"eslint": "^6.7.2", "eslint": "8.57.1",
"eslint-plugin-vue": "^7.0.0", "eslint-plugin-vue": "9.30.0",
"sass": "^1.37.5", "sass": "1.80.4",
"sass-loader": "^10.2.0", "sass-loader": "16.0.3",
"vue": "^3.0.0", "v-tooltip": "4.0.0-beta.17",
"vue-router": "^4.0.0-0" "vue": "3.5.6",
"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)

Binary file not shown.

View File

@ -1,4 +1,7 @@
// 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'
} }

View File

@ -1,4 +1,7 @@
// 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)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +0,0 @@
SPDX-License-Identifier: MIT

View File

@ -1,3 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 548 B

View File

@ -1,3 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 238 B

View File

@ -1 +0,0 @@
SPDX-License-Identifier: MIT

View File

@ -1,3 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 331 B

View File

@ -1 +0,0 @@
SPDX-License-Identifier: MIT

View File

@ -1,4 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 573 B

View File

@ -1 +0,0 @@
SPDX-License-Identifier: MIT

View File

@ -1,3 +1,9 @@
<!--
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>

21
renovate.json Normal file
View File

@ -0,0 +1,21 @@
{
"$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]\\./"
}
]
}

View File

@ -1,115 +1,25 @@
<!-- 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 beim Alpha-Test!
<a href="https://git.wtf-eg.de/kompetenzinventar/ki-frontend/issues/new">Problem gefunden? Bitte hier ein Issue anlegen.</a>
</div>
<header v-if="this.$route.path.includes('/s/')"> <header v-if="this.$route.path.includes('/s/')">
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <Navbar />
<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="Profile durchsuchen"
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 /> <router-view :key="$route.fullPath" />
<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',
data() { components: {
return { Footer,
showMobileNavbar: false, Navbar,
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>

View File

@ -1,10 +1,23 @@
/* /*
* 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";
.container{ .bg-wtf {
min-height: calc(100vh - 70px - 24px); background-image: url(../assets/img/wtf-header-bg.jpg);
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
}
.btn-primary {
color: #fff !important;
}
.search-card{
height: 100%;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,100 @@
<?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>

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,6 +1,6 @@
{ {
"1": "Keine Angabe", "1": "Keine Angabe",
"2": "Grundkentnisse", "2": "Grundkenntnisse",
"3": "Gut", "3": "Gut",
"4": "Fließend", "4": "Fließend",
"5": "Muttersprache" "5": "Muttersprache"

View File

@ -1,7 +1,22 @@
{ {
"1": "bis 6 Monate", "1": {
"2": "bis 1 Jahr", "short": "≤ 6M",
"3": "bis 3 Jahre", "long": "bis 6 Monate"
"4": "bis 5 Jahre", },
"5": "mehr als 5 Jahre" "2":{
"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"
}
} }

View File

@ -1 +1,18 @@
/*
* 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;

View File

@ -1,108 +1,73 @@
<!-- 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> <profile-list
<label for="searchText" class="form-label fw-bold">{{ label }}</label> :values="values"
<div class="row mb-2"> :type="type"
<div class="col"> :editable="true"
: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" class="form-control form-control-sm"
id="searchText" id="searchText"
:maxlength="maxlength"
:placeholder="placeholder"
v-model="searchText" v-model="searchText"
@keyup="search()" @input="search()"
@keyup.enter="addResult()"
/> />
</div> <div v-if="searchResults">
<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" class="list-group-item bg-white"
v-for="result in searchResults" v-for="result in searchResults"
:key="result.id" :key="result.id"
:class="{ @click="addResult(result)"
'bg-info':
selectedResult &&
selectedResult.id &&
result.id === selectedResult.id,
}"
@click="selectedResult = result"
> >
{{ result.name }} {{ result.name }}
</li> </li>
</ul> </ul>
</div> </div>
<profile-list </div>
:values="values" <div class="col-md-2">
:type="type" <button
:editable="true" v-if="searchText != ''"
@edit-value="editValue($event)" type="button"
@remove-value="removeValue($event)" class="btn btn-outline-success"
></profile-list> aria-label="Hinzufügen"
@click="addResult()"
>
<i clas="bi-plus-lg"></i>
Hinzufügen
</button>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import ProfileList from "@/components/ProfileList"; import { mapState } from 'vuex'
import levelJson from "@/assets/skill_level.json" import RequestMixin from '@/mixins/request.mixin'
import languagesJson from "@/assets/language_level.json" import ProfileList from '@/components/ProfileList';
export default { export default {
name: "AutoComplete", name: 'AutoComplete',
mixins: [RequestMixin],
components: { components: {
ProfileList, ProfileList,
}, },
@ -116,103 +81,66 @@ 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: {
...mapState(['currentUserId']),
maxlength() {
return this.type === 'skill' ? 50 : 25
}
},
methods: { methods: {
async search() { addResult(result = false) {
try { if (!result) result = this.searchResults[0];
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;
}
},
addSelectedResult() {
if ( if (
this.values this.values.map((item) => item[this.type].name).includes(result.name)
.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: localStorage.getItem("user_id"), profile_id: this.currentUserId,
}; };
if (this.type != "contacttype") {
newValue.level = this.level;
} else {
newValue.content = this.contactContent;
}
newValue[this.type] = this.selectedResult;
changeValues.push(newValue);
this.searchText = "";
this.searchResults = [];
this.selectedResult = null;
this.level = 1;
this.$emit("update-values", changeValues);
},
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); if (this.type != 'contacttype') {
newValue.level = 1;
} else {
newValue.content = '';
}
newValue[this.type] = result;
changeValues.unshift(newValue);
this.searchText = '';
this.searchResults = [];
this.$emit('update-values', changeValues);
}, },
removeValue(valueId) { removeValue(valueName) {
const newValues = this.values.filter((value) => { const newValues = this.values.filter((value) => {
if (valueId === value[this.type].id) { if (valueName === value[this.type].name) {
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;
}, },
}, },
}; };

37
src/components/Avatar.vue Normal file
View File

@ -0,0 +1,37 @@
<!--
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>

47
src/components/Footer.vue Normal file
View File

@ -0,0 +1,47 @@
<!--
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>

110
src/components/Navbar.vue Normal file
View File

@ -0,0 +1,110 @@
<!--
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>

View File

@ -0,0 +1,50 @@
<!--
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>

View File

@ -1,75 +1,123 @@
<!-- SPDX-License-Identifier: AGPL-3.0-or-later --> <!--
<template> SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
<ul>
<li v-for="value in values" :key="value.id">
<img
style="max-width: 64px"
v-if="value[type].icon_url"
:src="iconUrl + value[type].icon_url"
:alt="`${value[type].name} Logo`"
/>
{{ value[type].name }}
<span v-if="value.level">
<span v-if="type == 'skill'">({{ levelSelection[value.level] }})</span>
<span v-if="type == 'language'">({{ languagesSelection[value.level] }})</span>
</span>
<span v-if="value.content">:
<span v-if="value[type].name === 'E-Mail'">
<a :href="`mailto:${value.content}`">{{ value.content }}</a>
</span>
<span v-else>
{{ value.content }}
</span>
</span>
<button SPDX-License-Identifier: AGPL-3.0-or-later
v-if="this.editable" -->
type="button"
class="btn btn-outline-warning m-2" <template>
aria-label="Bearbeiten" <ul class="list-group list-group-flush">
@click="this.$emit('editValue', value)" <li
class="list-group-item bg-white"
v-for="(value, valueKey) in values"
:key="value.id"
> >
<img <div class="row">
src="/img/bootstrap-icons-1.5.0/pencil.svg" <div class="col-12 col-md-5 d-flex align-items-center">
alt="Bearbeiten Icon" <div
class="list-icon me-1"
:style="{ backgroundImage: `url('${iconBaseUrl + value[type].icon_url}'` }"
v-if="value[type].icon_url"
/> />
</button> <div>
<button {{ value[type].name }}
v-if="this.editable" </div>
type="button" </div>
class="btn btn-outline-danger m-2" <div class="col-10 col-md-5">
aria-label="Löschen" <div v-if="type == 'skill' && showSecondary">
@click="this.$emit('removeValue', value[type].id)" <select
class="form-select form-select-sm"
aria-label="Selektiere dein Level"
v-model="editableValues[valueKey].level"
@input="$emit('update-values', editableValues)"
> >
<img src="/img/bootstrap-icons-1.5.0/trash.svg" alt="Löschen Icon" /> <option
v-for="(value, key) in levelSelection"
: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
type="button"
class="btn btn-sm btn-light"
aria-label="Löschen"
@click="$emit('removeValue', value[type].name)"
>
<i class="text-danger bi bi-x-circle"></i>
</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,
}, },
editable: { showSecondary: {
type: Boolean, type: Boolean,
default: false, default: true
}, }
}, },
data() { data() {
return { return {
iconUrl: this.apiUrl, iconBaseUrl: 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>

View File

@ -0,0 +1,54 @@
<!--
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>

81
src/components/Skill.vue Normal file
View File

@ -0,0 +1,81 @@
<!--
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>

View File

@ -0,0 +1,9 @@
<!--
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>

View File

@ -0,0 +1,39 @@
<!--
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>

View File

@ -0,0 +1,39 @@
<!--
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>

View File

@ -0,0 +1,79 @@
<!--
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>

View File

@ -0,0 +1,69 @@
<!--
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>

View File

@ -0,0 +1,28 @@
<!--
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>

View File

@ -1,17 +1,23 @@
// 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 axios from 'axios' import 'v-tooltip/dist/v-tooltip.css'
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')

177
src/mixins/request.mixin.js Normal file
View File

@ -0,0 +1,177 @@
// 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;
}
},
},
}

View File

@ -1,5 +1,11 @@
// 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'
@ -13,7 +19,7 @@ const routes = [
template: "<router-view/>", template: "<router-view/>",
}, },
beforeEnter: (to, from, next) => { beforeEnter: (to, from, next) => {
if(localStorage.getItem('token') !== null){ if (store.state.token){
next() next()
} else { } else {
next({path: '/', query: {url: to.fullPath, access: false}}) next({path: '/', query: {url: to.fullPath, access: false}})
@ -40,7 +46,14 @@ 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()
}
}
}, },
] ]

53
src/store/index.js Normal file
View File

@ -0,0 +1,53 @@
// 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')
}
}
})

120
src/store/profile.js Normal file
View File

@ -0,0 +1,120 @@
// 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')
}
}
}

122
src/store/search.js Normal file
View File

@ -0,0 +1,122 @@
// 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')
}
}
}

View File

@ -1,11 +1,21 @@
<!-- 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="container"> <div class="bg-wtf">
<h1>WTF Kompetenzinventar</h1> <div class="container pt-5">
<form @submit.prevent="submitLogin()"> <div class="text-center mb-5">
<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" >
>Benutzername: WTF-Benutzername:
</label> </label>
<input <input
type="username" type="username"
@ -13,6 +23,7 @@
id="exampleInputusername1" id="exampleInputusername1"
v-model="username" v-model="username"
required required
autofocus
/> />
</div> </div>
<div class="mb-3"> <div class="mb-3">
@ -25,28 +36,27 @@
required required
/> />
</div> </div>
<button type="submit" class="btn btn-primary mb-4">Login</button> <button type="submit" class="btn btn-primary">Login</button>
</form> <a class="btn btn-link" href="https://resetpw.wtf-eg.de/">Passwort vergessen?</a>
<a href="https://resetpw.wtf-eg.de/">Globales WTF Passwort zurücksetzen</a>
<div <div
class="alert alert-danger mb-4 mt-4" class="alert alert-danger mt-3 mb-0"
role="alert" role="alert"
v-if="showErrorMessage" v-if="showErrorMessage"
> >
Mit deinen Login Daten ist ein Fehler aufgetreten. Versuch es nochmal oder Dein Benutzername oder Passwort ist falsch.<br>Versuche es noch einmal.
<a href="https://resetpw.wtf-eg.de/">hast du dein Passwort vergessen?</a>. </div>
</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 axios from "axios"; import RequestMixin from "@/mixins/request.mixin"
export default { export default {
name: "Index", name: "Index",
mixins: [RequestMixin],
data() { data() {
return { return {
showErrorMessage: false, showErrorMessage: false,
@ -54,35 +64,21 @@ 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>

View File

@ -1,102 +1,186 @@
<!-- 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">
<h1>Suche</h1> <div class="fs-3 text-center lh-1 mb-3">Finde WTF Member</div>
<form @submit.prevent="submitSearch()"> <div class="card mx-auto bg-white">
<div class="row"> <div class="card-body">
<div class="col"> <form @submit.prevent="handleSubmit">
<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="col"> <div class="">
<button type="submit" class="btn btn-primary mb-4"> <button type="submit" class="btn btn-primary">
Suche starten <i class="bi-search"></i>
<span class="d-none d-md-inline"> Suchen</span>
</button> </button>
</div> </div>
</div> </fieldset>
</form> </form>
<div
class="alert alert-danger mb-4 mt-4"
role="alert"
v-if="showErrorMessage"
>
Bei der Suche ist ein Fehler aufgetreten
</div> </div>
<div v-if="searchTotal == 0"> </div>
</div>
</div>
<div class="container pt-4 pb-3">
<div class="text-center" v-if="showSpinner">
<Spinner />
</div>
<div
class="fs-2 text-danger text-center"
role="alert"
v-if="error"
>
<div class="fs-1 mb-3">Kernel panic :/</div>
Bei der Suche ist ein Fehler aufgetreten.
</div>
<div v-else-if="showNoResults" class="fs-2 text-black-50 text-center">
<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 !== ''">Probiere eine andere Suche.</p>
</div> </div>
<div v-else> <div v-else-if="showResults">
<div class="row"> <div class="d-flex justify-content-around">
<div <Paginator
class="col-4 p-2" :pages="pages"
v-for="result in searchResults" :current="currentPage"
:key="result.user_id" @page="handlePageSelected"
> />
<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>
</div> <SearchResult
</router-link> v-for="profile in profiles"
: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 {
showErrorMessage: false, textChanged: false
searchText: "", }
searchResults: null,
searchTotal: 0,
};
}, },
created() { computed: {
if (this.$route.query.query) this.searchText = this.$route.query.query; ...mapState({
this.submitSearch(); 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: { methods: {
async submitSearch() { handleSubmit() {
this.showErrorMessage = false; if (this.textChanged === true) {
try { this.$store.commit('search/setQueryPage', 1)
let url = `${this.apiUrl}/users/profiles`;
if (this.searchText != "") {
url += `?nickname=${this.searchText}`;
} }
const result = await this.axios.get(url, { this.pushState()
headers: { this.$store.dispatch('search/search')
Authorization: `Bearer ${localStorage.getItem("token")}`,
}, },
}); focusSearchText() {
this.searchResults = result.data.profiles; this.$nextTick(() => {
this.searchTotal = result.data.total; this.$refs.searchTextInput.focus()
} catch (error) { })
console.error(error); },
this.showErrorMessage = true; handlePageSelected(page) {
this.currentPage = page
this.pushState()
this.$store.dispatch('search/search')
},
pushState() {
this.$router.push({ query: { query: this.searchText, page: this.currentPage }})
} }
}, },
}, created() {
if (this.$route.query.query) {
this.searchText = this.$route.query.query
this.$store.commit('search/clearProfiles')
}
if (this.$route.query.page) {
this.currentPage = parseInt(this.$route.query.page, 10)
this.$store.commit('search/clearProfiles')
}
this.$store.dispatch('search/search')
}
}; };
</script> </script>
<style scoped>
.container {
max-width: 768px;
}
.content {
min-height: calc(100vh - 60px);
}
</style>

View File

@ -1,173 +1,253 @@
<!-- 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">
<h1>Profil bearbeiten</h1> <h3 class="text-white text-center mb-0">Profil bearbeiten</h3>
<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 id="visibilityHelp" class="form-text"> <div class="container">
Erst wenn du dein Profil Öffentlich stellst, können andere Genoss:innen darauf zugreifen oder es in der Suche finden. <form @submit.prevent="submitFormEdit(false)">
</div> <Section title="Grunddaten">
<div class="row"> <div class="row mb-4">
<div class="col-6 col-xs-12"> <div class="col-12 col-md-4 mb-3 mb-md-0">
<label for="nickname" class="form-label fw-bold">Nickname:</label> <label class="form-label">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-6 col-xs-12"> <div class="col-12 col-md-4 mb-3 mb-md-0">
<label for="pronouns" class="form-label fw-bold">Pronomen:</label> <label class="form-label">Klarname (optional)</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-xs-12"> <div class="col-12 col-md-4 mb-3 mb-md-0">
<label for="freetext" class="form-label fw-bold">Vorstellung:</label>
<textarea
class="form-control"
id="freetext"
rows="3"
v-model="profile.freetext"
></textarea>
</div>
<div class="col-12 col-xs-12">
<label for="volunteerwork" class="form-label fw-bold"
>Ehrenamtliche Arbeit:</label
>
<textarea
class="form-control"
id="volunteerwork"
rows="3"
v-model="profile.volunteerwork"
></textarea>
</div>
</div>
<auto-complete
type="skill"
label="Deine Fähigkeiten"
:values="profile.skills"
@update-values="profile.skills = $event"
></auto-complete>
<auto-complete
type="language"
label="Deine Sprachen"
:values="profile.languages"
@update-values="profile.languages = $event"
></auto-complete>
<auto-complete
type="skill"
label="Ich suche"
:values="profile.searchtopics"
@update-values="profile.searchtopics = $event"
></auto-complete>
<div class="col-12 col-xs-12">
<label for="availability" class="form-label fw-bold"
>Ich bin für Anfragen verfügbar:</label
>
<textarea
class="form-control"
id="availability"
rows="3"
v-model="profile.availability"
></textarea>
</div>
<auto-complete
type="contacttype"
label="Kontaktmöglichkeiten"
:values="profile.contacts"
@update-values="profile.contacts = $event"
></auto-complete>
<div class="row">
<div class="col">
<label for="pzl" class="form-label fw-bold">PLZ</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="pzl" id="postcode"
maxlength="10"
placeholder="Postleitzahl"
v-model="profile.address.postcode" v-model="profile.address.postcode"
/> />
</div> </div>
<div class="col"> <div class="col-12 col-md-4 mb-3 mb-md-0">
<label for="city" class="form-label fw-bold">Stadt</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="city" id="city"
maxlength="25"
placeholder="Ort"
v-model="profile.address.city" v-model="profile.address.city"
/> />
</div> </div>
<div class="col"> <div class="col-12 col-md-4">
<label for="country" class="form-label fw-bold">Land</label>
<input <input
type="text" type="text"
class="form-control" class="form-control"
id="country" id="country"
maxlength="25"
placeholder="Land"
v-model="profile.address.country" v-model="profile.address.country"
/> />
</div> </div>
</div> </div>
</Section>
<button type="submit" class="btn btn-outline-success mb-4 mt-4 col-12">Speichern</button> <Section title="Meine Fähigkeiten">
<template v-slot:card-body>
<auto-complete
type="skill"
:values="profile.skills"
placeholder="z.B. Python, JavaScript, Linux"
@update-values="profile.skills = $event"
></auto-complete>
</template>
</Section>
<Section title="Meine Sprachkenntnisse">
<template v-slot:card-body>
<auto-complete
type="language"
:values="profile.languages"
placeholder="z.B. Deutsch, Englisch, Französisch"
@update-values="profile.languages = $event"
></auto-complete>
</template>
</Section>
<Section title="Ich suche für mich Projekte/Aufträge in folgenden Bereichen">
<template v-slot:card-body>
<auto-complete
type="skill"
label="Ich suche für mich Projekte/Aufträge in folgenden Bereichen"
:values="profile.searchtopics"
:showSecondary="false"
placeholder="z.B. Python, JavaScript, Linux"
@update-values="profile.searchtopics = $event"
></auto-complete>
</template>
</Section>
<Section title="Verfügbarkeit">
<div class="form-check mb-3">
<input
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
class="form-control"
id="availability"
rows="3"
maxlength="4000"
v-model="profile.availability_text"
></textarea>
</Section>
<Section title="Meine Kontaktmöglichkeiten">
<template v-slot:card-body>
<auto-complete
type="contacttype"
:values="profile.contacts"
placeholder="z.B. E-Mail, Mobiltelefon, Matrix, Web"
@update-values="profile.contacts = $event"
></auto-complete>
</template>
</Section>
<Section title="Sonstiges">
<div class="mb-3 bg-white">
<label class="form-label">Über mich</label>
<textarea
class="form-control"
rows="3"
maxlength="4000"
v-model="profile.freetext"
/>
</div>
<div>
<label class="form-label">Ehrenamtliche Arbeit</label>
<textarea
class="form-control"
rows="3"
maxlength="4000"
v-model="profile.volunteerwork"
/>
</div>
</Section>
<input type="submit" class="d-none">
</form>
</div>
<div class="savebar bg-white border-top py-3">
<div class="container d-flex align-items-center justify-content-end">
<div <div
class="alert alert-danger mb-4 mt-4" class="text-danger"
role="alert"
v-if="showErrorMessage" v-if="showErrorMessage"
> >
Es ist Fehler aufgetreten <i class="bi bi-bug"></i>
Beim Speichern ist ein Fehler aufgetreten.
</div> </div>
<div <div
class="alert alert-success mb-4 mt-4" class="text-success"
role="alert"
v-if="showSuccessMessage" v-if="showSuccessMessage"
> >
Deine Änderungen wurden erfolgreich gespeichert <i class="bi bi-check-lg"></i>
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 axios from "axios"; import RequestMixin from "@/mixins/request.mixin"
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: "", availability_status: false,
availability_hours_per_week: null,
availability_text: "",
address: { address: {
postcode: "", postcode: "",
city: "", city: "",
@ -178,49 +258,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() {
try { await this.initEditPage();
const userProfile = await axios.get( },
`${this.apiUrl}/users/${localStorage.getItem( unmounted() {
"user_id" this.cancelClearMessages()
)}/profile`,
{
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
}
);
this.profile = userProfile.data.profile;
} catch (error) {
console.error(error);
}
}, },
methods: { methods: {
async submitForm() { cancelClearMessages() {
try { if (this.clearMessagesHandle) {
const formSubmitResult = await axios.post( window.clearTimeout(this.clearMessagesHandle)
`${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>

View File

@ -1,64 +1,160 @@
<!-- 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 v-if="profile" class="container"> <div>
<h1> <template v-if="error">
{{profile.nickname}} <ViewError :isOwnProfile="isOwnProfile" :notFound="notFound" />
<span v-if="profile.pronouns">({{profile.pronouns}})</span> </template>
</h1> <template
<p><label class="fw-bold">Vorstellung: </label> {{profile.freetext}}</p> v-else-if="profile"
<p><label class="fw-bold">Ehrentamtliche Arbeit: </label> {{profile.volunteerwork}}</p> class="container">
<p><label class="fw-bold">Verfügbarkeit: </label> {{profile.availability}}</p> <ProfileHeader
<h3>Das kann ich:</h3> class="mb-4"
<profile-list :profile="profile" />
:values="profile.skills"
type="skill" <Section
></profile-list> v-if="profile.skills && profile.skills.length > 0"
<h3>Das suche ich:</h3> title="Das kann ich">
<profile-list <div style="margin-bottom: -.5rem;">
:values="profile.searchtopics" <Skill
type="skill" class="me-2 mb-2"
></profile-list> v-for="skill in profile.skills"
<h3>Meine Kontaktmöglichkeiten:</h3> :key="skill.skill.id"
<profile-list :profileSkill="skill"
:values="profile.contacts" :showLevel="true" />
type="contacttype"
></profile-list>
<h3>Ich Spreche Folgende Sprachen:</h3>
<profile-list
:values="profile.languages"
type="language"
></profile-list>
<div v-if="profile.address">
<h3>Meine Location:</h3>
{{profile.address.city}} ({{profile.address.postcode}}), {{profile.address.country}}
</div> </div>
</Section>
<Section
v-if="profile.searchtopics && profile.searchtopics.length > 0"
title="Ich suche für mich Projekte/Aufträge in folgenden Bereichen">
<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 ProfileList from "@/components/ProfileList"; import { mapState, mapActions } from 'vuex'
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: {
ProfileList, Contact,
Language,
ProfileHeader,
Section,
Skill,
ViewError,
}, },
data() { methods: {
return { ...mapActions({
profile: null loadProfile: 'profile/load',
}; 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() {
try { const id = parseInt(this.$route.params.memberId, 10)
const userProfile = await this.axios.get( this.loadProfile(id)
`${this.apiUrl}/users/${this.$route.params.memberId}/profile`, },
{ unmounted() {
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, this.clearStore()
}
);
this.profile = userProfile.data.profile;
} catch (error) {
console.error(error);
}
}, },
}; };
</script> </script>
<style>
.line-break-text{
white-space: pre-line;
}
</style>