1
0
Fork 0
forked from Kispi/Core

feat(core): add employer portal
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Simon Giesel 2023-07-09 21:27:45 +02:00
parent a2994e50e6
commit c70dba5ab3
20 changed files with 625 additions and 106 deletions

View file

@ -6,19 +6,25 @@ steps:
image: node:lts-alpine
commands:
- cd webapp
- corepack pnpm@8.6.0 install
- corepack pnpm@8.6.6 install
- name: lint-webapp
image: node:lts-alpine
commands:
- cd webapp
- corepack pnpm@8.6.0 lint
- corepack pnpm@8.6.6 lint
- name: version-webapp
image: node:lts-alpine
commands:
- cd webapp
- corepack pnpm@8.6.6 generate-version
- name: build-webapp
image: node:lts-alpine
commands:
- cd webapp
- corepack pnpm@8.6.0 build
- corepack pnpm@8.6.6 build
- name: deploy
image: plugins/docker

View file

@ -5,7 +5,7 @@ go 1.20
require (
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198
github.com/pocketbase/pocketbase v0.16.6
github.com/pocketbase/pocketbase v0.16.8
github.com/pterm/pterm v0.12.62
)
@ -15,7 +15,7 @@ require (
atomicgo.dev/schedule v0.0.2 // indirect
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.44.294 // indirect
github.com/aws/aws-sdk-go v1.44.298 // indirect
github.com/aws/aws-sdk-go-v2 v1.18.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
github.com/aws/aws-sdk-go-v2/config v1.18.27 // indirect
@ -48,7 +48,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gookit/color v1.5.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@ -70,21 +70,21 @@ require (
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opencensus.io v0.24.0 // indirect
gocloud.dev v0.30.0 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/image v0.8.0 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/oauth2 v0.9.0 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/term v0.9.0 // indirect
golang.org/x/text v0.10.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/image v0.9.0 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.12.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect
golang.org/x/tools v0.11.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.129.0 // indirect
google.golang.org/api v0.130.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
google.golang.org/grpc v1.56.1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
google.golang.org/grpc v1.56.2 // indirect
google.golang.org/protobuf v1.31.0 // indirect
lukechampine.com/uint128 v1.3.0 // indirect
modernc.org/cc/v3 v3.41.0 // indirect

View file

@ -187,6 +187,7 @@ cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IK
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
cloud.google.com/go/compute v1.20.0 h1:cUOcywWuowO9It2i1KX1lIb0HH7gLv6nENKuZGnlcSo=
cloud.google.com/go/compute v1.20.0/go.mod h1:kn5BhC++qUWR/AM3Dn21myV7QbgqejW04cAOrtppaQI=
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@ -791,6 +792,8 @@ github.com/aws/aws-sdk-go v1.44.245/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8
github.com/aws/aws-sdk-go v1.44.284/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.294 h1:3x7GaEth+pDU9HwFcAU0awZlEix5CEdyIZvV08SlHa8=
github.com/aws/aws-sdk-go v1.44.294/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.298 h1:5qTxdubgV7PptZJmp/2qDwD2JL187ePL7VOxsSh1i3g=
github.com/aws/aws-sdk-go v1.44.298/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo=
github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
@ -1300,6 +1303,7 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
@ -1451,6 +1455,8 @@ github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+Sb
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
@ -1933,6 +1939,8 @@ github.com/pocketbase/dbx v1.10.0 h1:58VIT7r6T+BnVbYVosvGBsPjQEic3/VFRYGT823vWSQ
github.com/pocketbase/dbx v1.10.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
github.com/pocketbase/pocketbase v0.16.6 h1:zE+9SPXlhimZpuiD4KrmY+cp4b2lyL8gLAbkjj5YqFY=
github.com/pocketbase/pocketbase v0.16.6/go.mod h1:xXXL26RVy0vGxDHOffoaeEv7CvZSJHivnfAeygmEfD8=
github.com/pocketbase/pocketbase v0.16.8 h1:uILzBla2DNMFGZFRu0cStIv3AVjYgicOQs2F1AeXpGw=
github.com/pocketbase/pocketbase v0.16.8/go.mod h1:vmajDvROzq//BiQ4RoNqHmjHf5i8RMgK665zQ14oo/g=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
@ -2345,6 +2353,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -2379,6 +2389,8 @@ golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeap
golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.8.0 h1:agUcRXV/+w6L9ryntYYsF2x9fQTMd4T8fiiYXAVW6Jg=
golang.org/x/image v0.8.0/go.mod h1:PwLxp3opCYg4WR2WO9P0L6ESnsD6bLTWcw8zanLMVFM=
golang.org/x/image v0.9.0 h1:QrzfX26snvCM20hIhBwuHI/ThTg18b/+kcKdXHvnR+g=
golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -2413,6 +2425,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -2509,6 +2523,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -2544,6 +2560,8 @@ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=
golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -2723,6 +2741,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -2738,6 +2758,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -2756,6 +2778,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -2864,6 +2888,8 @@ golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -2954,6 +2980,8 @@ google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvy
google.golang.org/api v0.128.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
google.golang.org/api v0.129.0 h1:2XbdjjNfFPXQyufzQVwPf1RRnHH8Den2pfNE2jw7L8w=
google.golang.org/api v0.129.0/go.mod h1:dFjiXlanKwWE3612X97llhsoI36FAoIiRj3aTl5b/zE=
google.golang.org/api v0.130.0 h1:A50ujooa1h9iizvfzA4rrJr2B7uRmWexwbekQ2+5FPQ=
google.golang.org/api v0.130.0/go.mod h1:J/LCJMYSDFvAVREGCbrESb53n4++NMBDetSHGL5I5RY=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -3126,6 +3154,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@ -3176,6 +3206,8 @@ google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGO
google.golang.org/grpc v1.56.0/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=

2
webapp/.env.example Normal file
View file

@ -0,0 +1,2 @@
# The app version is dynamically set by the build script
VITE_APP_VERSION="dev"

View file

@ -9,13 +9,14 @@
"lint": "eslint -c .eslintrc.js src/",
"preview": "vite preview",
"typegen": "pocketbase-typegen --json ../server/pb_schema.json --out ./src/types/pocketbase.types.ts",
"optimize-svg": "svgo -f ./src/assets/svg/ --config=./src/assets/svg/svgo.config.js"
"optimize-svg": "svgo -f ./src/assets/svg/ --config=./src/assets/svg/svgo.config.js",
"generate-version": "node ./scripts/generate-version.js"
},
"dependencies": {
"@heroicons/vue": "^2.0.18",
"@vueuse/core": "^10.2.1",
"canvas-confetti": "^1.6.0",
"daisyui": "^3.1.9",
"daisyui": "^3.2.1",
"pocketbase": "^0.15.2",
"vue": "^3.3.4",
"vue-router": "4.2.4"
@ -32,13 +33,14 @@
"eslint-plugin-tailwindcss": "^3.13.0",
"eslint-plugin-vue": "^9.15.1",
"eslint-plugin-vue-scoped-css": "^2.5.0",
"git-commit-info": "^2.0.2",
"pocketbase-typegen": "^1.1.11",
"postcss": "^8.4.25",
"sass": "^1.63.6",
"svgo": "^3.0.2",
"tailwindcss": "^3.3.2",
"typescript": "^5.1.6",
"vite": "^4.4.1",
"vite": "^4.4.2",
"vue-eslint-parser": "^9.3.1",
"vue-tsc": "^1.8.4"
}

View file

@ -15,8 +15,8 @@ dependencies:
specifier: ^1.6.0
version: 1.6.0
daisyui:
specifier: ^3.1.9
version: 3.1.9(postcss@8.4.25)
specifier: ^3.2.1
version: 3.2.1
pocketbase:
specifier: ^0.15.2
version: 0.15.2
@ -45,7 +45,7 @@ devDependencies:
version: 5.61.0(eslint@8.44.0)(typescript@5.1.6)
'@vitejs/plugin-vue':
specifier: ^4.2.3
version: 4.2.3(vite@4.4.1)(vue@3.3.4)
version: 4.2.3(vite@4.4.2)(vue@3.3.4)
autoprefixer:
specifier: ^10.4.14
version: 10.4.14(postcss@8.4.25)
@ -61,6 +61,9 @@ devDependencies:
eslint-plugin-vue-scoped-css:
specifier: ^2.5.0
version: 2.5.0(eslint@8.44.0)(vue-eslint-parser@9.3.1)
git-commit-info:
specifier: ^2.0.2
version: 2.0.2
pocketbase-typegen:
specifier: ^1.1.11
version: 1.1.11
@ -80,8 +83,8 @@ devDependencies:
specifier: ^5.1.6
version: 5.1.6
vite:
specifier: ^4.4.1
version: 4.4.1(sass@1.63.6)
specifier: ^4.4.2
version: 4.4.2(sass@1.63.6)
vue-eslint-parser:
specifier: ^9.3.1
version: 9.3.1(eslint@8.44.0)
@ -645,14 +648,14 @@ packages:
eslint-visitor-keys: 3.4.1
dev: true
/@vitejs/plugin-vue@4.2.3(vite@4.4.1)(vue@3.3.4):
/@vitejs/plugin-vue@4.2.3(vite@4.4.2)(vue@3.3.4):
resolution: {integrity: sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.0.0
vue: ^3.2.25
dependencies:
vite: 4.4.1(sass@1.63.6)
vite: 4.4.2(sass@1.63.6)
vue: 3.3.4
dev: true
@ -1129,6 +1132,14 @@ packages:
- encoding
dev: true
/cross-spawn@5.1.0:
resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
dependencies:
lru-cache: 4.1.5
shebang-command: 1.2.0
which: 1.3.1
dev: true
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@ -1199,11 +1210,9 @@ packages:
/csstype@3.1.2:
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
/daisyui@3.1.9(postcss@8.4.25):
resolution: {integrity: sha512-GkQR3iLnTfxNsQz7EZXS5bPY65iJM7jdiHtQZWDpRVmBrMlq8Nmrc+t8XmjGdOmJljAPFjtMSD9E9zAnhXF7qg==}
/daisyui@3.2.1:
resolution: {integrity: sha512-gIqE6wiqoJt9G8+n3R/SwLeUnpNCE2eDhT73rP0yZYVaM7o6zVcakBH3aEW5RGpx3UkonPiLuvcgxRcb2lE8TA==}
engines: {node: '>=16.9.0'}
peerDependencies:
postcss: ^8
dependencies:
colord: 2.9.3
css-selector-tokenizer: 0.8.0
@ -1327,6 +1336,12 @@ packages:
dev: true
optional: true
/end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
once: 1.4.0
dev: true
/entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@ -1566,6 +1581,34 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/execa@0.6.3:
resolution: {integrity: sha512-/teX3MDLFBdYUhRk8WCBYboIMUmqeizu0m9Z3YF3JWrbEh/SlZg00vLJSaAGWw3wrZ9tE0buNw79eaAPYhUuvg==}
engines: {node: '>=4'}
dependencies:
cross-spawn: 5.1.0
get-stream: 3.0.0
is-stream: 1.1.0
npm-run-path: 2.0.2
p-finally: 1.0.0
signal-exit: 3.0.7
strip-eof: 1.0.0
dev: true
/execa@4.1.0:
resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
engines: {node: '>=10'}
dependencies:
cross-spawn: 7.0.3
get-stream: 5.2.0
human-signals: 1.1.1
is-stream: 2.0.1
merge-stream: 2.0.0
npm-run-path: 4.0.1
onetime: 5.1.2
signal-exit: 3.0.7
strip-final-newline: 2.0.0
dev: true
/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
@ -1697,6 +1740,26 @@ packages:
dev: true
optional: true
/get-stream@3.0.0:
resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
engines: {node: '>=4'}
dev: true
/get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
engines: {node: '>=8'}
dependencies:
pump: 3.0.0
dev: true
/git-commit-info@2.0.2:
resolution: {integrity: sha512-QOgPqeTkFiIQr3TW/ekG2U6s1x7OkNdR+2XsapmXHiGImO5Qx2cilx2HmGPrihIqmV0WWCo5H+B938lQ/vAAfA==}
dependencies:
execa: 4.1.0
is-git-repository: 1.1.1
path-is-absolute: 1.0.1
dev: true
/glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@ -1805,6 +1868,11 @@ packages:
- supports-color
dev: true
/human-signals@1.1.1:
resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
engines: {node: '>=8.12.0'}
dev: true
/humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
dependencies:
@ -1887,6 +1955,13 @@ packages:
engines: {node: '>=8'}
dev: true
/is-git-repository@1.1.1:
resolution: {integrity: sha512-hxLpJytJnIZ5Og5QsxSkzmb8Qx8rGau9bio1JN/QtXcGEFuSsQYau0IiqlsCwftsfVYjF1mOq6uLdmwNSspgpA==}
dependencies:
execa: 0.6.3
path-is-absolute: 1.0.1
dev: true
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
@ -1907,6 +1982,16 @@ packages:
engines: {node: '>=8'}
dev: true
/is-stream@1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
dev: true
/is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
dev: true
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
@ -1972,6 +2057,13 @@ packages:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: true
/lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
dependencies:
pseudomap: 1.0.2
yallist: 2.1.2
dev: true
/lru-cache@6.0.0:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
@ -2026,6 +2118,10 @@ packages:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
dev: true
/merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@ -2049,6 +2145,11 @@ packages:
mime-db: 1.52.0
dev: true
/mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
dev: true
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@ -2224,6 +2325,20 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/npm-run-path@2.0.2:
resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
engines: {node: '>=4'}
dependencies:
path-key: 2.0.1
dev: true
/npm-run-path@4.0.1:
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
engines: {node: '>=8'}
dependencies:
path-key: 3.1.1
dev: true
/npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
dependencies:
@ -2263,6 +2378,13 @@ packages:
dependencies:
wrappy: 1.0.2
/onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
dependencies:
mimic-fn: 2.1.0
dev: true
/optionator@0.9.3:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
@ -2275,6 +2397,11 @@ packages:
type-check: 0.4.0
dev: true
/p-finally@1.0.0:
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
engines: {node: '>=4'}
dev: true
/p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@ -2313,6 +2440,11 @@ packages:
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
/path-key@2.0.1:
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
engines: {node: '>=4'}
dev: true
/path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@ -2487,6 +2619,17 @@ packages:
dev: true
optional: true
/pseudomap@1.0.2:
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
dev: true
/pump@3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
dev: true
/punycode@2.1.1:
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
engines: {node: '>=6'}
@ -2597,6 +2740,13 @@ packages:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: true
/shebang-command@1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
engines: {node: '>=0.10.0'}
dependencies:
shebang-regex: 1.0.0
dev: true
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@ -2604,6 +2754,11 @@ packages:
shebang-regex: 3.0.0
dev: true
/shebang-regex@1.0.0:
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
engines: {node: '>=0.10.0'}
dev: true
/shebang-regex@3.0.0:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
@ -2719,6 +2874,16 @@ packages:
ansi-regex: 5.0.1
dev: true
/strip-eof@1.0.0:
resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
engines: {node: '>=0.10.0'}
dev: true
/strip-final-newline@2.0.0:
resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
engines: {node: '>=6'}
dev: true
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@ -2916,8 +3081,8 @@ packages:
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
/vite@4.4.1(sass@1.63.6):
resolution: {integrity: sha512-8mmwPlsy7t+ZcTme7vdbVGuZ3Tri+lrLXr6hsF5UHdyYyARPPPMtM16QlqC9TZuCd5j3NmWs1rwka3cVSRHZTw==}
/vite@4.4.2(sass@1.63.6):
resolution: {integrity: sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@ -3033,6 +3198,13 @@ packages:
webidl-conversions: 3.0.1
dev: true
/which@1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
hasBin: true
dependencies:
isexe: 2.0.0
dev: true
/which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@ -3055,6 +3227,10 @@ packages:
engines: {node: '>=12'}
dev: true
/yallist@2.1.2:
resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
dev: true
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true

View file

@ -0,0 +1,7 @@
const fs = require('fs');
const gitCommitInfo = require('git-commit-info');
const packageJson = require('../package.json');
const version = `${packageJson.version}+${gitCommitInfo().shortHash}`;
fs.writeFileSync('.env', `VITE_APP_VERSION=${version}`);

View file

@ -1,13 +1,16 @@
<template>
<footer class="footer footer-center bg-base-100 p-4 text-base-content">
<div>
<p>Version: {{ version }}</p>
<p>Made with by Simon Giesel</p>
<p>Copyright © 2023 - All right reserved</p>
<p>Copyright © 2023 - All rights reserved</p>
</div>
</footer>
</template>
<script lang="ts" setup>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const version = (import.meta as any).env.VITE_APP_VERSION as string;
</script>

View file

@ -16,7 +16,7 @@
type="password"
required
/>
<button class="btn gap-2 self-end">
<button class="btn-primary btn gap-2 self-end">
<ArrowRightOnRectangleIcon class="h-6 w-6" />
Login
</button>

View file

@ -1,16 +1,22 @@
<template>
<div
v-if="settings"
class="h-full overflow-x-auto rounded-lg"
class="rounded-lg"
>
<table class="table-pin-rows table-zebra table">
<thead class="uppercase">
<thead
v-if="!hideHeader"
class="uppercase"
>
<tr>
<th v-for="header in tableHeaders">{{ header.title }}</th>
</tr>
</thead>
<tbody class="bg-base-100">
<tr v-for="entry in data">
<tr
v-for="entry in data"
@click="$emit('click', entry.id)"
>
<td
v-for="header in tableHeaders"
:class="{
@ -29,6 +35,22 @@
<span v-else-if="header.type === TableHeaderType.CURRENCY">
{{ CurrencyService.toString(entry[header.key]) }}
</span>
<span v-else-if="header.type === TableHeaderType.SHIFT">
<div class="dropdown-bottom dropdown">
<label
tabindex="0"
class="btn bg-base-300 normal-case"
>{{ entry[header.key] }} <ChevronDownIcon class="h-4 w-4" /></label>
<ul
tabindex="0"
class="dropdown-content menu rounded-box z-[1] w-52 bg-base-100 p-2 shadow"
>
<li @click="$emit('selectShift', {id: entry.id, shift: Shifts.NONE}); removeFocus();"><a>{{ Shifts.NONE }}</a></li>
<li><a @click="$emit('selectShift', {id: entry.id, shift: Shifts.EARLY}); removeFocus();">{{ Shifts.EARLY }}</a></li>
<li><a @click="$emit('selectShift', {id: entry.id, shift: Shifts.LATE}); removeFocus();">{{ Shifts.LATE }}</a></li>
</ul>
</div>
</span>
<RouterLink
v-if="header.type === TableHeaderType.BUTTON_ACCOUNT"
:to="`/bank?accountNumber=${entry.accountNumber}`"
@ -41,10 +63,20 @@
:for="contactModalId"
class="flex items-center gap-2"
>
<!-- @click="$emit('open-contact-modal', entry.id)" -->
<span>{{ CurrencyService.toString(entry[header.key] * settings.minWage) }} / h</span>
<div class="btn-ghost btn-sm btn p-1">
<PencilSquareIcon class="h-5 w-5" />
<div class="dropdown-bottom dropdown">
<label
tabindex="0"
class="btn bg-base-300 normal-case"
>{{ CurrencyService.toString(entry[header.key] * settings.minWage) }} / h <ChevronDownIcon class="h-4 w-4" /></label>
<ul
tabindex="0"
class="dropdown-content menu rounded-box z-[1] w-52 bg-base-100 p-2 shadow"
>
<li
v-for="i of settings.maxWageFactor"
@click="$emit('selectWage', {id: entry.id, wageFactor: i}); removeFocus();"
><a>{{ CurrencyService.toString(settings.minWage * i ) }} / h</a></li>
</ul>
</div>
</span>
</td>
@ -56,11 +88,12 @@
<script lang="ts" setup>
import { PropType, onMounted, ref } from 'vue';
import { SettingsResponse } from '../../types/pocketbase.types';
import { DateService } from '../../services/date.service';
import { CurrencyService } from '../../services/currency.service';
import { CurrencyDollarIcon, PencilSquareIcon } from '@heroicons/vue/24/outline';
import { SettingsService } from '../../services/settings.service';
import { SettingsResponse } from '../../types/pocketbase.types';
import { Shifts } from '../../enums/shift.enum';
import { ChevronDownIcon, CurrencyDollarIcon } from '@heroicons/vue/24/outline';
const settings = ref<SettingsResponse>();
@ -80,11 +113,26 @@ defineProps({
required: false,
default: '',
},
hideHeader: {
type: Boolean,
required: false,
default: false,
},
});
onMounted(async () => {
settings.value = await SettingsService.getSettings();
});
defineEmits<{
click: [id: string],
selectShift: [{id: string, shift: Shifts}],
selectWage: [{id: string, wageFactor: number}],
}>();
function removeFocus() {
(document.activeElement as HTMLElement).blur();
}
</script>
<script lang="ts">
@ -95,6 +143,7 @@ export enum TableHeaderType {
DATETIME,
BUTTON_ACCOUNT,
WAGE,
SHIFT,
}
</script>

View file

@ -0,0 +1,86 @@
<template>
<AtomModal
:id="id"
ref="modal"
:title="title"
>
<div class="flex flex-col gap-4">
<p>{{ description }}</p>
<AtomInput
v-model="modelValue"
placeholder="Passwort"
type="password"
@keydown.prevent.enter="$emit('login')"
/>
<p
v-if="error"
class="alert alert-error"
>
{{ error }}
</p>
</div>
<template #action>
<button
class="btn gap-2"
:for="id"
@click="modal?.close()"
>
<XCircleIcon class="h-6 w-6" />
Schließen
</button>
<button
class="btn gap-2"
:for="id"
@click.prevent="$emit('login')"
>
<ArrowRightOnRectangleIcon class="h-6 w-6" />
Login
</button>
</template>
</AtomModal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { ArrowRightOnRectangleIcon, XCircleIcon } from '@heroicons/vue/24/outline';
import AtomModal from '../atoms/AtomModal.vue';
import AtomInput from '../atoms/AtomInput.vue';
const modal = ref<InstanceType<typeof AtomModal>>();
defineProps({
id: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
error: {
type: String,
default: '',
},
});
defineExpose({
show: () => modal.value?.show(),
close: () => modal.value?.close(),
isOpen: () => modal.value?.isOpen(),
});
/* global defineModel */
const modelValue = defineModel();
defineEmits<{
login: [],
}>();
</script>
<style lang="scss" scoped>
</style>

View file

@ -38,7 +38,7 @@
</div>
<slot />
</div>
<div class="drawer-side !fixed">
<div class="drawer-side !fixed z-50">
<label
:for="drawerId"
class="drawer-overlay"
@ -47,7 +47,7 @@
<li class="pointer-events-none">
<AtomLogo class="w-full" />
</li>
<template v-if="isAuthenticated">
<template v-if="isAdmin">
<li class="pointer-events-none rounded-md bg-warning uppercase text-warning-content">
<span>
<ExclamationTriangleIcon class="h-6 w-6" />
@ -102,7 +102,7 @@ import { ArrowRightOnRectangleIcon, Bars3Icon, ExclamationTriangleIcon, MoonIcon
import AtomLogo from '../atoms/AtomLogo.vue';
import AtomSwap from '../atoms/AtomSwap.vue';
const isAuthenticated = ref(false);
const isAdmin = ref(false);
const hideNavigation = ref(false);
const router = useRouter();
const drawerCheckbox = ref();
@ -126,11 +126,11 @@ function getNavigationEntryClass(navigationEntry: INavigationEntry) {
};
}
useEventBus<boolean>('isAuthenticated').on(state => (isAuthenticated.value = state));
useEventBus<boolean>('isAdmin').on(state => (isAdmin.value = state));
useEventBus<boolean>('hideNavigation').on(state => (hideNavigation.value = state));
onMounted(() => {
isAuthenticated.value = AuthService.isAuthenticated();
isAdmin.value = AuthService.isAdmin();
});
router.afterEach(() => {

View file

@ -1,5 +1,5 @@
<template>
<MoleculeAuthDialog v-if="!isAuthenticated" />
<MoleculeAuthDialog v-if="!isAdmin" />
<div v-else>
<slot />
</div>
@ -11,11 +11,11 @@ import { useEventBus } from '@vueuse/core';
import { AuthService } from '../../services/auth.service';
import MoleculeAuthDialog from '../molecules/MoleculeAuthDialog.vue';
const isAuthenticated = ref(false);
const isAdmin = ref(false);
useEventBus<boolean>('isAuthenticated').on((state: boolean) => {
isAuthenticated.value = state;
if(isAuthenticated.value) {
useEventBus<boolean>('isAdmin').on((state: boolean) => {
isAdmin.value = state;
if(isAdmin.value) {
emit('authChange', true);
} else {
emit('authChange', false);
@ -23,8 +23,8 @@ useEventBus<boolean>('isAuthenticated').on((state: boolean) => {
});
onMounted(() => {
isAuthenticated.value = AuthService.isAuthenticated();
if(isAuthenticated.value) {
isAdmin.value = AuthService.isAdmin();
if(isAdmin.value) {
emit('authChange', true);
}
});

View file

@ -1,5 +1,5 @@
<template>
<MoleculeAuthDialog v-if="!isAuthenticated" />
<MoleculeAuthDialog v-if="!isAdmin" />
<div
v-else
class="flex h-full flex-col p-6"
@ -31,7 +31,7 @@ import AtomInput from '../atoms/AtomInput.vue';
import MoleculeDataTable, { TableHeaderType } from '../molecules/MoleculeDataTable.vue';
import MoleculeAuthDialog from '../molecules/MoleculeAuthDialog.vue';
const isAuthenticated = ref(false);
const isAdmin = ref(false);
const accountsSubscription = ref<UnsubscribeFunc>();
const transactionsSubscription = ref<UnsubscribeFunc>();
@ -71,9 +71,9 @@ const tableHeaders = [
},
];
useEventBus<boolean>('isAuthenticated').on(state => {
isAuthenticated.value = state;
if(isAuthenticated.value) {
useEventBus<boolean>('isAdmin').on(state => {
isAdmin.value = state;
if(isAdmin.value) {
getData();
}
});
@ -100,8 +100,8 @@ function search(value: string) {
}
onMounted(async () => {
isAuthenticated.value = AuthService.isAuthenticated();
if(isAuthenticated.value) {
isAdmin.value = AuthService.isAdmin();
if(isAdmin.value) {
getData();
}
accountsSubscription.value = await AccountService.subscribeToAccountChanges(async () => {

View file

@ -1,34 +1,98 @@
<template>
<MoleculeAuthDialog v-if="!isAuthenticated" />
<div
v-else
class="flex h-full flex-col p-6"
v-if="!isAuthenticated"
class="flex min-h-full flex-col p-6"
>
<h1 class="mb-4 text-4xl">Musterfirma</h1>
<h2 class="mb-4 text-3xl">Kontostand: 500 Batzen</h2>
<AtomInput
v-model="searchQuery"
placeholder="Firmenname eingeben"
class="mb-4"
/>
<MoleculeDataTable
v-if="companies"
class="cursor-pointer"
:table-headers="companyTableHeaders"
:data="companies"
hide-header
@click="showLoginDialog"
/>
<MoleculeLoginModal
id="login-modal"
ref="loginModal"
v-model="password"
:title="selectedCompany?.name ?? ''"
description="Bitte gib dein Passwort ein, um das Firmenportal anzuzeigen."
:error="error"
@login="login"
@close="password = ''; error = ''"
/>
</div>
<div
v-else-if="company"
class="flex min-h-full flex-col p-6"
>
<div class="mb-4 flex flex-col gap-4">
<div class="flex items-center justify-between">
<h1 class="text-4xl">{{ company.name }}</h1>
<button
class="btn-primary btn w-48"
@click="logout()"
>
<ArrowLeftOnRectangleIcon class="h-6 w-6" />
Abmelden
</button>
</div>
<div class="flex items-center justify-between">
<h2 class="text-2xl">500,00 Batzen</h2>
<button class="btn-primary btn-disabled btn w-48">
<BanknotesIcon class="h-6 w-6" />
Transaktionen
</button>
</div>
</div>
<MoleculeDataTable
v-if="accounts"
:table-headers="tableHeaders"
:table-headers="accountTableHeaders"
:data="accounts"
@select-shift="selectShift"
@select-wage="selectWage"
/>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue';
import { useEventBus } from '@vueuse/core';
import { AccountsListResponse } from '../../types/pocketbase.types';
import { onMounted, onUnmounted, ref, watch } from 'vue';
import { RecordSubscription, UnsubscribeFunc } from 'pocketbase';
import { AccountsResponse, CompaniesResponse } from '../../types/pocketbase.types';
import { AccountService } from '../../services/account.service';
import { AuthService } from '../../services/auth.service';
import { CompanyService } from '../../services/company.service';
import { Shifts } from '../../enums/shift.enum';
import { ArrowLeftOnRectangleIcon, BanknotesIcon } from '@heroicons/vue/24/outline';
import AtomInput from '../atoms/AtomInput.vue';
import MoleculeDataTable, { TableHeaderType } from '../molecules/MoleculeDataTable.vue';
import MoleculeAuthDialog from '../molecules/MoleculeAuthDialog.vue';
import MoleculeLoginModal from '../molecules/MoleculeLoginModal.vue';
const isAuthenticated = ref(false);
const accounts = ref<AccountsListResponse<number, string>[]>([]);
const initAccounts = ref<AccountsListResponse<number, string>[]>([]);
const loginModal = ref<InstanceType<typeof MoleculeLoginModal>>();
const searchQuery = ref('');
const tableHeaders = [
const password = ref('');
const companies = ref<CompaniesResponse[]>([]);
const initCompanies = ref<CompaniesResponse[]>([]);
const selectedCompany = ref<CompaniesResponse>();
const company = ref<CompaniesResponse>();
const accounts = ref<AccountsResponse[]>([]);
const isAuthenticated = ref(false);
const accountsSubscription = ref<UnsubscribeFunc>();
const error = ref('');
const companyTableHeaders = [
{
title: '',
key: 'name',
type: TableHeaderType.STRING,
},
];
const accountTableHeaders = [
{
title: 'Vorname',
key: 'firstName',
@ -40,9 +104,9 @@ const tableHeaders = [
type: TableHeaderType.STRING,
},
{
title: 'Schicht (bearbeitbar)',
title: 'Schicht',
key: 'shift',
type: TableHeaderType.STRING,
type: TableHeaderType.SHIFT,
},
{
title: 'Lohn',
@ -51,16 +115,9 @@ const tableHeaders = [
},
];
useEventBus<boolean>('isAuthenticated').on(state => {
isAuthenticated.value = state;
if(isAuthenticated.value) {
getData();
}
});
async function getData() {
initAccounts.value = await AccountService.getAccountsByCompanyId('c09ncb3l2pi6i0u');
accounts.value = initAccounts.value;
initCompanies.value = await CompanyService.getCompanies();
companies.value = initCompanies.value;
}
watch(
@ -70,20 +127,77 @@ watch(
},
);
function search(value: string) {
if(initAccounts.value) {
accounts.value = initAccounts.value.filter((account) =>
account.name?.toLocaleLowerCase().includes(value.toLocaleLowerCase()),
function search(value: string): void {
if(initCompanies.value) {
companies.value = initCompanies.value.filter((company) =>
company.name?.toLocaleLowerCase().includes(value.toLocaleLowerCase()),
);
}
}
onMounted(async () => {
isAuthenticated.value = AuthService.isAuthenticated();
isAuthenticated.value = CompanyService.isAuthenticated();
if(isAuthenticated.value) {
init();
} else {
getData();
}
});
async function init() {
company.value = await CompanyService.getCompany();
accounts.value = await AccountService.getAccountsByCompanyId(company.value.id);
accountsSubscription.value = await AccountService.subscribeToAccountChanges(async (data: RecordSubscription<AccountsResponse>) => {
if(data.action === 'update') {
accounts.value = accounts.value.map((account) => {
if(account.id === data.record.id) {
return data.record;
}
return account;
});
}
});
}
function showLoginDialog(id: string): void {
selectedCompany.value = companies.value.find((company) => company.id === id);
loginModal.value?.show();
}
async function login(): Promise<void> {
if(selectedCompany.value) {
try {
await CompanyService.login(selectedCompany.value?.username, password.value);
} catch {
error.value = 'Fehler beim Login. Bitte Passwort überprüfen.';
return;
}
isAuthenticated.value = true;
await init();
loginModal.value?.close();
}
}
async function selectShift(shiftChange: {id: string, shift: Shifts}): Promise<void> {
await AccountService.updateShift(shiftChange.id, shiftChange.shift);
}
async function selectWage(wageChange: {id: string, wageFactor: number}): Promise<void> {
await AccountService.updateWage(wageChange.id, wageChange.wageFactor);
}
function logout() {
CompanyService.logout();
isAuthenticated.value = false;
selectedCompany.value = undefined;
company.value = undefined;
accounts.value = [];
getData();
}
onUnmounted(() => {
accountsSubscription.value?.();
});
</script>
<style lang="scss" scoped>

View file

@ -162,6 +162,7 @@ import { SettingsResponse } from '../../types/pocketbase.types';
import { AccountService } from '../../services/account.service';
import { CompanyService } from '../../services/company.service';
import { SettingsService } from '../../services/settings.service';
import { Shifts } from '../../enums/shift.enum';
import {
BriefcaseIcon,
ExclamationTriangleIcon,
@ -214,7 +215,7 @@ async function importAccounts(file: File): Promise<void> {
lastName: lastName.trim(),
grade: grade.trim(),
company: company?.id,
shift: 'Keine Schicht',
shift: Shifts.NONE,
wageFactor: 1,
}));
}

View file

@ -0,0 +1,5 @@
export enum Shifts {
NONE = 'Keine Schicht',
EARLY = 'Frühschicht',
LATE = 'Spätschicht'
}

View file

@ -16,12 +16,18 @@ export class AccountService {
public static async getAccounts(): Promise<AccountsListResponse<number, string>[]> {
return VIEW.getFullList();
}
public static getAccountsByCompanyId(companyId: string): Promise<AccountsListResponse<number, string>[]> {
public static getAccountsByCompanyId(companyId: string): Promise<AccountsResponse[]> {
return COLLECTION.getFullList({ filter: `company.id = "${companyId}"` });
}
public static async addAccount(account: AccountsRecord): Promise<AccountsResponse> {
return COLLECTION.create(account, { $cancelKey: `${account.firstName}.${account.lastName}.${account.grade}` });
}
public static async updateShift(accountId: string, shift: string): Promise<AccountsResponse> {
return COLLECTION.update(accountId, { shift });
}
public static async updateWage(accountId: string, wageFactor: number): Promise<AccountsResponse> {
return COLLECTION.update(accountId, { wageFactor });
}
public static async subscribeToAccountChanges(
callback: (data: RecordSubscription<AccountsResponse>)=> void,

View file

@ -5,15 +5,15 @@ import { useEventBus } from '@vueuse/core';
const API = PocketbaseService.getApi();
export class AuthService {
public static isAuthenticated(): boolean {
if(API.authStore.isValid) {
return true;
public static isAdmin(): boolean {
if(API.authStore.isValid && API.authStore.model) {
return API.authStore.model.constructor.name === 'Admin';
}
this.logout();
return false;
}
public static getAdmin(): Admin {
if(this.isAuthenticated()) {
if(this.isAdmin()) {
return API.authStore.model as Admin;
}
throw new Error('Not authenticated');
@ -21,13 +21,13 @@ export class AuthService {
public static async loginAsAdmin(email: string, password: string): Promise<Admin> {
const response = await API.admins.authWithPassword(email, password);
if(response) {
useEventBus<boolean>('isAuthenticated').emit(true);
useEventBus<boolean>('isAdmin').emit(true);
return response.admin;
}
throw new Error('Invalid credentials');
}
public static logout(): void {
API.authStore.clear();
useEventBus<boolean>('isAuthenticated').emit(false);
useEventBus<boolean>('isAdmin').emit(false);
}
}

View file

@ -1,13 +1,43 @@
import { RecordAuthResponse } from 'pocketbase';
import { Collections, CompaniesRecord, CompaniesResponse } from '../types/pocketbase.types';
import { PocketbaseService } from './pocketbase.service';
import { useEventBus } from '@vueuse/core';
import { AuthService } from './auth.service';
const COLLECTION = PocketbaseService.getApi().collection(Collections.Companies);
const API = PocketbaseService.getApi();
const COLLECTION = API.collection(Collections.Companies);
export class CompanyService {
public static async addCompany(company: CompaniesRecord & {password: string}): Promise<CompaniesResponse> {
return COLLECTION.create({ ...company, passwordConfirm: company.password }, { $cancelKey: `${company.name}` });
}
public static async getCompanies(): Promise<CompaniesResponse[]> {
return COLLECTION.getFullList();
return COLLECTION.getFullList({ sort: 'name' });
}
public static async login(username: string, password: string): Promise<RecordAuthResponse<CompaniesResponse>> {
try {
const response = await COLLECTION.authWithPassword<CompaniesResponse>(username, password);
useEventBus<boolean>('isAdmin').emit(AuthService.isAdmin());
return response;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
throw new Error(error);
}
}
public static isAuthenticated(): boolean {
if(API.authStore.isValid && API.authStore.model) {
return API.authStore.model.constructor.name === 'Record';
}
this.logout();
return false;
}
public static logout(): void {
API.authStore.clear();
}
public static async getCompany(): Promise<CompaniesResponse> {
if(this.isAuthenticated()) {
return API.authStore.model as unknown as CompaniesResponse;
}
throw new Error('Not authenticated');
}
}