mirror of
https://github.com/goauthentik/authentik
synced 2026-05-07 07:32:23 +02:00
Compare commits
31 Commits
cleanup-fl
...
playwright
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
117bf97eaa | ||
|
|
7e4bdac093 | ||
|
|
5f16ea4718 | ||
|
|
d50a266d74 | ||
|
|
41ebfa24da | ||
|
|
8b7cc18988 | ||
|
|
e780b7d519 | ||
|
|
25894592ae | ||
|
|
5021d08c69 | ||
|
|
cb74b47674 | ||
|
|
aafd81ca09 | ||
|
|
a4f8e15f91 | ||
|
|
0c20169739 | ||
|
|
24ca89c439 | ||
|
|
0352d31af0 | ||
|
|
5bdbf06351 | ||
|
|
b3f1e7b1a2 | ||
|
|
2dfda8833d | ||
|
|
9094b30860 | ||
|
|
7e52e932fc | ||
|
|
b5fc28a3fd | ||
|
|
df49dd4ec8 | ||
|
|
3f4c58a05b | ||
|
|
46c9bfb0aa | ||
|
|
6d325d566c | ||
|
|
5689336f61 | ||
|
|
e04ca70cb2 | ||
|
|
6a5342f621 | ||
|
|
e250c8f514 | ||
|
|
a4e7aa0adc | ||
|
|
ac79acd2bc |
@@ -76,7 +76,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "GEOIPUPDATE_LICENSE_KEY_FILE=/run/secrets/GEOIPUPDATE_LICENSE_KEY /usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 4: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.8.3 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.8.4 AS uv
|
||||
# Stage 5: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.13.5-slim-bookworm-fips AS python-base
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
17
go.mod
17
go.mod
@@ -5,12 +5,12 @@ go 1.24.0
|
||||
require (
|
||||
beryju.io/ldap v0.1.0
|
||||
github.com/avast/retry-go/v4 v4.6.1
|
||||
github.com/coreos/go-oidc/v3 v3.14.1
|
||||
github.com/getsentry/sentry-go v0.34.1
|
||||
github.com/coreos/go-oidc/v3 v3.15.0
|
||||
github.com/getsentry/sentry-go v0.35.0
|
||||
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/go-openapi/runtime v0.28.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
github.com/gorilla/mux v1.8.1
|
||||
@@ -22,7 +22,7 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.8.1
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/redis/go-redis/v9 v9.11.0
|
||||
github.com/sethvargo/go-envconfig v1.3.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
@@ -69,18 +69,17 @@ require (
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
32
go.sum
32
go.sum
@@ -16,8 +16,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@@ -26,8 +26,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
|
||||
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/getsentry/sentry-go v0.34.1 h1:HSjc1C/OsnZttohEPrrqKH42Iud0HuLCXpv8cU1pWcw=
|
||||
github.com/getsentry/sentry-go v0.34.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
github.com/getsentry/sentry-go v0.35.0 h1:+FJNlnjJsZMG3g0/rmmP7GiKjQoUF5EXfEtBwtPtkzY=
|
||||
github.com/getsentry/sentry-go v0.35.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
@@ -67,8 +67,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
|
||||
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
@@ -140,14 +140,14 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
|
||||
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
@@ -211,8 +211,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
||||
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1022.0",
|
||||
"aws-cdk": "^2.1023.0",
|
||||
"cross-env": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -24,9 +24,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1022.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1022.0.tgz",
|
||||
"integrity": "sha512-GHCu+tDtYMqCiElCl7Fad2/Bt2GmtXEV3dynudoAsV9PlL5ETeLmEN7jflDQxhmr7KhKpQeZJo/PM0DoWCvoHw==",
|
||||
"version": "2.1023.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1023.0.tgz",
|
||||
"integrity": "sha512-DWMA+IrAsBUNF2RvH7ujpDp7wSJkqTkRL8yfK4AYpEjoGY1KMaKIfxz3M3+Nk3ogM7VhZiW3OGWEOgyDF47HOQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1022.0",
|
||||
"aws-cdk": "^2.1023.0",
|
||||
"cross-env": "^10.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
6
packages/docusaurus-config/package-lock.json
generated
6
packages/docusaurus-config/package-lock.json
generated
@@ -17958,9 +17958,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@@ -2728,9 +2728,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc-plugin-markdown": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.7.1.tgz",
|
||||
"integrity": "sha512-HN/fHLm2S6MD4HX8txfB4eWvVBzX/mEYy5U5s1KTAdh3E5uX5/lilswqTzZlPTT6fNZInAboAdFGpbAuBKnE4A==",
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.8.0.tgz",
|
||||
"integrity": "sha512-BQqXnT9PETe6WEFf8bcsvvGEGQHbwTo/BFyY+RUIsSB05Y0Wn56iF+fK1PY2OKJJIhV4kp4dp7osaP9Bm5a0Zw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -2741,9 +2741,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
448
packages/eslint-config/package-lock.json
generated
448
packages/eslint-config/package-lock.json
generated
@@ -500,93 +500,6 @@
|
||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
|
||||
"integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.38.0",
|
||||
"@typescript-eslint/type-utils": "8.38.0",
|
||||
"@typescript-eslint/utils": "8.38.0",
|
||||
"@typescript-eslint/visitor-keys": "8.38.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.38.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
|
||||
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.38.0",
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/typescript-estree": "8.38.0",
|
||||
"@typescript-eslint/visitor-keys": "8.38.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
|
||||
"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.38.0",
|
||||
"@typescript-eslint/types": "^8.38.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz",
|
||||
@@ -605,48 +518,6 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
|
||||
"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
|
||||
"integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/typescript-estree": "8.38.0",
|
||||
"@typescript-eslint/utils": "8.38.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz",
|
||||
@@ -661,98 +532,6 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
|
||||
"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.38.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.38.0",
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/visitor-keys": "8.38.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
|
||||
"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.38.0",
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/typescript-estree": "8.38.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
|
||||
@@ -4694,9 +4473,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -4731,6 +4510,227 @@
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz",
|
||||
"integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.10.0",
|
||||
"@typescript-eslint/scope-manager": "8.38.0",
|
||||
"@typescript-eslint/type-utils": "8.38.0",
|
||||
"@typescript-eslint/utils": "8.38.0",
|
||||
"@typescript-eslint/visitor-keys": "8.38.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^7.0.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.38.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz",
|
||||
"integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/typescript-estree": "8.38.0",
|
||||
"@typescript-eslint/utils": "8.38.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz",
|
||||
"integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.38.0",
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/typescript-estree": "8.38.0",
|
||||
"@typescript-eslint/visitor-keys": "8.38.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
|
||||
"integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.38.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.38.0",
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/visitor-keys": "8.38.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
|
||||
"integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.38.0",
|
||||
"@typescript-eslint/types": "^8.38.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
|
||||
"integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.38.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz",
|
||||
"integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.38.0",
|
||||
"@typescript-eslint/types": "8.38.0",
|
||||
"@typescript-eslint/typescript-estree": "8.38.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <5.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript-eslint/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
||||
|
||||
6
packages/prettier-config/package-lock.json
generated
6
packages/prettier-config/package-lock.json
generated
@@ -1711,9 +1711,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
||||
175
web/e2e/fixtures/FormFixture.ts
Normal file
175
web/e2e/fixtures/FormFixture.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import { PageFixture } from "#e2e/fixtures/PageFixture";
|
||||
import type { LocatorContext } from "#e2e/selectors/types";
|
||||
|
||||
import { expect, Page } from "@playwright/test";
|
||||
|
||||
export class FormFixture extends PageFixture {
|
||||
static fixtureName = "Form";
|
||||
|
||||
//#region Selector Methods
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Field Methods
|
||||
|
||||
/**
|
||||
* Set the value of a text input.
|
||||
*
|
||||
* @param fieldName The name of the form element.
|
||||
* @param value the value to set.
|
||||
*/
|
||||
public fill = async (
|
||||
fieldName: string,
|
||||
value: string,
|
||||
parent: LocatorContext = this.page,
|
||||
): Promise<void> => {
|
||||
const control = parent
|
||||
.getByRole("textbox", {
|
||||
name: fieldName,
|
||||
})
|
||||
.or(
|
||||
parent.getByRole("spinbutton", {
|
||||
name: fieldName,
|
||||
}),
|
||||
)
|
||||
.first();
|
||||
|
||||
await expect(control, `Field (${fieldName}) should be visible`).toBeVisible();
|
||||
|
||||
await control.fill(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the value of a radio or checkbox input.
|
||||
*
|
||||
* @param fieldName The name of the form element.
|
||||
* @param value the value to set.
|
||||
*/
|
||||
public setInputCheck = async (
|
||||
fieldName: string,
|
||||
value: boolean = true,
|
||||
parent: LocatorContext = this.page,
|
||||
): Promise<void> => {
|
||||
const control = parent.locator("ak-switch-input", {
|
||||
hasText: fieldName,
|
||||
});
|
||||
|
||||
await control.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(control, `Field (${fieldName}) should be visible`).toBeVisible();
|
||||
|
||||
const currentChecked = await control
|
||||
.getAttribute("checked")
|
||||
.then((value) => value !== null);
|
||||
|
||||
if (currentChecked === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
await control.click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the value of a radio or checkbox input.
|
||||
*
|
||||
* @param fieldName The name of the form element.
|
||||
* @param pattern the value to set.
|
||||
*/
|
||||
public setRadio = async (
|
||||
groupName: string,
|
||||
fieldName: string,
|
||||
parent: LocatorContext = this.page,
|
||||
): Promise<void> => {
|
||||
const group = parent.getByRole("group", { name: groupName });
|
||||
|
||||
await expect(group, `Field "${groupName}" should be visible`).toBeVisible();
|
||||
const control = parent.getByRole("radio", { name: fieldName });
|
||||
|
||||
await control.setChecked(true, {
|
||||
force: true,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the value of a search select input.
|
||||
*
|
||||
* @param fieldLabel The name of the search select element.
|
||||
* @param pattern The text to match against the search select entry.
|
||||
*/
|
||||
public selectSearchValue = async (
|
||||
fieldLabel: string,
|
||||
pattern: string | RegExp,
|
||||
parent: LocatorContext = this.page,
|
||||
): Promise<void> => {
|
||||
const control = parent.getByRole("textbox", { name: fieldLabel });
|
||||
|
||||
await expect(
|
||||
control,
|
||||
`Search select control (${fieldLabel}) should be visible`,
|
||||
).toBeVisible();
|
||||
|
||||
const fieldName = await control.getAttribute("name");
|
||||
|
||||
if (!fieldName) {
|
||||
throw new Error(`Unable to find name attribute on search select (${fieldLabel})`);
|
||||
}
|
||||
|
||||
// Find the search select input control and activate it.
|
||||
await control.click();
|
||||
|
||||
const button = this.page
|
||||
// ---
|
||||
.locator(`div[data-managed-for*="${fieldName}"] button`, {
|
||||
hasText: pattern,
|
||||
});
|
||||
|
||||
if (!button) {
|
||||
throw new Error(
|
||||
`Unable to find an ak-search-select entry matching ${fieldLabel}:${pattern.toString()}`,
|
||||
);
|
||||
}
|
||||
|
||||
await button.click();
|
||||
await this.page.keyboard.press("Tab");
|
||||
await control.blur();
|
||||
};
|
||||
|
||||
public setFormGroup = async (
|
||||
pattern: string | RegExp,
|
||||
value: boolean = true,
|
||||
parent: LocatorContext = this.page,
|
||||
) => {
|
||||
const control = parent
|
||||
.locator("ak-form-group", {
|
||||
hasText: pattern,
|
||||
})
|
||||
.first();
|
||||
|
||||
const currentOpen = await control.getAttribute("open").then((value) => value !== null);
|
||||
|
||||
if (currentOpen === value) {
|
||||
this.logger.debug(`Form group ${pattern} is already ${value ? "open" : "closed"}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug(`Toggling form group ${pattern} to ${value ? "open" : "closed"}`);
|
||||
|
||||
await control.click();
|
||||
|
||||
if (value) {
|
||||
await expect(control).toHaveAttribute("open");
|
||||
} else {
|
||||
await expect(control).not.toHaveAttribute("open");
|
||||
}
|
||||
};
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
constructor(page: Page, testName: string) {
|
||||
super({ page, testName });
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
30
web/e2e/fixtures/PageFixture.ts
Normal file
30
web/e2e/fixtures/PageFixture.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { ConsoleLogger, FixtureLogger } from "#logger/node";
|
||||
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export interface PageFixtureOptions {
|
||||
page: Page;
|
||||
testName: string;
|
||||
}
|
||||
|
||||
export abstract class PageFixture {
|
||||
/**
|
||||
* The name of the fixture.
|
||||
*
|
||||
* Used for logging.
|
||||
*/
|
||||
static fixtureName: string;
|
||||
|
||||
protected readonly logger: FixtureLogger;
|
||||
protected readonly page: Page;
|
||||
protected readonly testName: string;
|
||||
|
||||
constructor({ page, testName }: PageFixtureOptions) {
|
||||
this.page = page;
|
||||
this.testName = testName;
|
||||
|
||||
const Constructor = this.constructor as typeof PageFixture;
|
||||
|
||||
this.logger = ConsoleLogger.fixture(Constructor.fixtureName, this.testName);
|
||||
}
|
||||
}
|
||||
42
web/e2e/fixtures/PointerFixture.ts
Normal file
42
web/e2e/fixtures/PointerFixture.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { PageFixture } from "#e2e/fixtures/PageFixture";
|
||||
import type { LocatorContext } from "#e2e/selectors/types";
|
||||
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export type GetByRoleParameters = Parameters<Page["getByRole"]>;
|
||||
export type ARIARole = GetByRoleParameters[0];
|
||||
export type ARIAOptions = GetByRoleParameters[1];
|
||||
|
||||
export type ClickByName = (name: string) => Promise<void>;
|
||||
export type ClickByRole = (
|
||||
role: ARIARole,
|
||||
options?: ARIAOptions,
|
||||
context?: LocatorContext,
|
||||
) => Promise<void>;
|
||||
|
||||
export class PointerFixture extends PageFixture {
|
||||
public static fixtureName = "Pointer";
|
||||
|
||||
public click = (
|
||||
name: string,
|
||||
optionsOrRole?: ARIAOptions | ARIARole,
|
||||
context: LocatorContext = this.page,
|
||||
): Promise<void> => {
|
||||
if (typeof optionsOrRole === "string") {
|
||||
return context.getByRole(optionsOrRole, { name }).click();
|
||||
}
|
||||
|
||||
const options = {
|
||||
...optionsOrRole,
|
||||
name,
|
||||
};
|
||||
|
||||
return (
|
||||
context
|
||||
// ---
|
||||
.getByRole("button", options)
|
||||
.or(context.getByRole("link", options))
|
||||
.click()
|
||||
);
|
||||
};
|
||||
}
|
||||
119
web/e2e/fixtures/SessionFixture.ts
Normal file
119
web/e2e/fixtures/SessionFixture.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { PageFixture } from "#e2e/fixtures/PageFixture";
|
||||
|
||||
import { expect, Page } from "@playwright/test";
|
||||
|
||||
export const GOOD_USERNAME = "test-admin@goauthentik.io";
|
||||
export const GOOD_PASSWORD = "test-runner";
|
||||
|
||||
export const BAD_USERNAME = "bad-username@bad-login.io";
|
||||
export const BAD_PASSWORD = "-this-is-a-bad-password-";
|
||||
|
||||
export interface LoginInit {
|
||||
username?: string;
|
||||
password?: string;
|
||||
to?: URL | string;
|
||||
}
|
||||
|
||||
export class SessionFixture extends PageFixture {
|
||||
static fixtureName = "Session";
|
||||
|
||||
public static readonly pathname = "/if/flow/default-authentication-flow/";
|
||||
|
||||
//#region Selectors
|
||||
|
||||
public $identificationStage = this.page.locator("ak-stage-identification");
|
||||
|
||||
/**
|
||||
* The username field on the login page.
|
||||
*/
|
||||
public $usernameField = this.$identificationStage.locator('input[name="uidField"]');
|
||||
|
||||
/**
|
||||
* The button to continue with the login process,
|
||||
* typically to the password flow stage.
|
||||
*/
|
||||
public $submitUsernameStageButton = this.$identificationStage.locator('button[type="submit"]');
|
||||
|
||||
public $passwordStage = this.page.locator("ak-stage-password");
|
||||
public $passwordField = this.$passwordStage.locator('input[name="password"]');
|
||||
/**
|
||||
* The button to submit the the login flow,
|
||||
* typically redirecting to the authenticated interface.
|
||||
*/
|
||||
public $submitPasswordStageButton = this.$passwordStage.locator('button[type="submit"]');
|
||||
|
||||
/**
|
||||
* A possible authentication failure message.
|
||||
*/
|
||||
public $authFailureMessage = this.page.locator(".pf-m-error");
|
||||
|
||||
//#endregion
|
||||
|
||||
constructor(page: Page, testName: string) {
|
||||
super({ page, testName });
|
||||
}
|
||||
|
||||
//#region Specific interactions
|
||||
|
||||
public async submitUsernameStage(username: string) {
|
||||
this.logger.info("Submitting username stage", username);
|
||||
|
||||
await this.$usernameField.fill(username);
|
||||
|
||||
await expect(this.$submitUsernameStageButton).toBeEnabled();
|
||||
|
||||
await this.$submitUsernameStageButton.click();
|
||||
}
|
||||
|
||||
public async submitPasswordStage(password: string) {
|
||||
this.logger.info("Submitting password stage");
|
||||
|
||||
await this.$passwordField.fill(password);
|
||||
|
||||
await expect(this.$submitPasswordStageButton).toBeEnabled();
|
||||
|
||||
await this.$submitPasswordStageButton.click();
|
||||
}
|
||||
|
||||
public checkAuthenticated = async (): Promise<boolean> => {
|
||||
// TODO: Check if the user is authenticated via API
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Log into the application.
|
||||
*/
|
||||
public async login({
|
||||
username = GOOD_USERNAME,
|
||||
password = GOOD_PASSWORD,
|
||||
to = SessionFixture.pathname,
|
||||
}: LoginInit = {}) {
|
||||
this.logger.info("Logging in...");
|
||||
|
||||
const initialURL = new URL(this.page.url());
|
||||
|
||||
if (initialURL.pathname === SessionFixture.pathname) {
|
||||
this.logger.info("Skipping navigation because we're already in a authentication flow");
|
||||
} else {
|
||||
await this.page.goto(to.toString());
|
||||
}
|
||||
|
||||
await this.submitUsernameStage(username);
|
||||
|
||||
await this.$passwordField.waitFor({ state: "visible" });
|
||||
|
||||
await this.submitPasswordStage(password);
|
||||
|
||||
const expectedPathname = typeof to === "string" ? to : to.pathname;
|
||||
|
||||
await this.page.waitForURL(`**${expectedPathname}`);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Navigation
|
||||
|
||||
public async toLoginPage() {
|
||||
await this.page.goto(SessionFixture.pathname);
|
||||
}
|
||||
}
|
||||
56
web/e2e/index.ts
Normal file
56
web/e2e/index.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { createLocatorProxy, DeepLocatorProxy } from "#e2e/elements/proxy";
|
||||
import { FormFixture } from "#e2e/fixtures/FormFixture";
|
||||
import { PointerFixture } from "#e2e/fixtures/PointerFixture";
|
||||
import { SessionFixture } from "#e2e/fixtures/SessionFixture";
|
||||
import { createOUIDNameEngine } from "#e2e/selectors/ouid";
|
||||
|
||||
import { test as base } from "@playwright/test";
|
||||
|
||||
export { expect } from "@playwright/test";
|
||||
|
||||
type TestIDLocatorProxy = DeepLocatorProxy<TestIDSelectorMap>;
|
||||
|
||||
interface E2EFixturesTestScope {
|
||||
/**
|
||||
* A proxy to retrieve elements by test ID.
|
||||
*
|
||||
* ```ts
|
||||
* const $button = $.button;
|
||||
* ```
|
||||
*/
|
||||
$: TestIDLocatorProxy;
|
||||
session: SessionFixture;
|
||||
pointer: PointerFixture;
|
||||
form: FormFixture;
|
||||
}
|
||||
|
||||
interface E2EWorkerScope {
|
||||
selectorRegistration: void;
|
||||
}
|
||||
|
||||
export const test = base.extend<E2EFixturesTestScope, E2EWorkerScope>({
|
||||
selectorRegistration: [
|
||||
async ({ playwright }, use) => {
|
||||
await playwright.selectors.register("ouid", createOUIDNameEngine);
|
||||
await use();
|
||||
},
|
||||
{ auto: true, scope: "worker" },
|
||||
],
|
||||
|
||||
$: async ({ page }, use) => {
|
||||
await use(createLocatorProxy<TestIDSelectorMap>(page));
|
||||
},
|
||||
|
||||
session: async ({ page }, use, { title }) => {
|
||||
await use(new SessionFixture(page, title));
|
||||
},
|
||||
|
||||
form: async ({ page }, use, { title }) => {
|
||||
await use(new FormFixture(page, title));
|
||||
},
|
||||
|
||||
pointer: async ({ page }, use, { title }) => {
|
||||
await use(new PointerFixture({ page, testName: title }));
|
||||
},
|
||||
});
|
||||
44
web/e2e/selectors/ouid.ts
Normal file
44
web/e2e/selectors/ouid.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
type SelectorRoot = Document | ShadowRoot;
|
||||
|
||||
export function createOUIDNameEngine() {
|
||||
const attributeName = "data-ouid-component-name";
|
||||
|
||||
console.log("Creating OUID selector engine!!");
|
||||
return {
|
||||
// Returns all elements matching given selector in the root's subtree.
|
||||
queryAll(scope: SelectorRoot, componentName: string) {
|
||||
const result: Element[] = [];
|
||||
|
||||
const match = (element: Element) => {
|
||||
const name = element.getAttribute(attributeName);
|
||||
|
||||
if (name === componentName) {
|
||||
result.push(element);
|
||||
}
|
||||
};
|
||||
|
||||
const query = (root: Element | ShadowRoot | Document) => {
|
||||
const shadows: ShadowRoot[] = [];
|
||||
|
||||
if ((root as Element).shadowRoot) {
|
||||
shadows.push((root as Element).shadowRoot!);
|
||||
}
|
||||
|
||||
for (const element of root.querySelectorAll("*")) {
|
||||
match(element);
|
||||
|
||||
if (element.shadowRoot) {
|
||||
shadows.push(element.shadowRoot);
|
||||
}
|
||||
}
|
||||
|
||||
shadows.forEach(query);
|
||||
};
|
||||
|
||||
query(scope);
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
13
web/e2e/selectors/types.ts
Normal file
13
web/e2e/selectors/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Locator } from "@playwright/test";
|
||||
|
||||
export type LocatorContext = Pick<
|
||||
Locator,
|
||||
| "locator"
|
||||
| "getByRole"
|
||||
| "getByTestId"
|
||||
| "getByText"
|
||||
| "getByLabel"
|
||||
| "getByAltText"
|
||||
| "getByTitle"
|
||||
| "getByPlaceholder"
|
||||
>;
|
||||
60
web/e2e/utils/generators.ts
Normal file
60
web/e2e/utils/generators.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { IDGenerator } from "@goauthentik/core/id";
|
||||
|
||||
import {
|
||||
adjectives,
|
||||
colors,
|
||||
Config as NameConfig,
|
||||
uniqueNamesGenerator,
|
||||
} from "unique-names-generator";
|
||||
|
||||
/**
|
||||
* Given a dictionary of words, slice the dictionary to only include words that start with the given letter.
|
||||
*/
|
||||
export function alliterate(dictionary: string[], letter: string): string[] {
|
||||
let firstIndex = 0;
|
||||
|
||||
for (let i = 0; i < dictionary.length; i++) {
|
||||
if (dictionary[i][0] === letter) {
|
||||
firstIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let lastIndex = firstIndex;
|
||||
|
||||
for (let i = firstIndex; i < dictionary.length; i++) {
|
||||
if (dictionary[i][0] !== letter) {
|
||||
lastIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary.slice(firstIndex, lastIndex);
|
||||
}
|
||||
|
||||
export function createRandomName({
|
||||
seed = IDGenerator.randomID(),
|
||||
...config
|
||||
}: Partial<NameConfig> = {}) {
|
||||
const randomLetterIndex =
|
||||
typeof seed === "number"
|
||||
? seed
|
||||
: Array.from(seed).reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
||||
|
||||
const letter = adjectives[randomLetterIndex % adjectives.length][0];
|
||||
|
||||
const availableAdjectives = alliterate(adjectives, letter);
|
||||
|
||||
const availableColors = alliterate(colors, letter);
|
||||
|
||||
const name = uniqueNamesGenerator({
|
||||
dictionaries: [availableAdjectives, availableAdjectives, availableColors],
|
||||
style: "capital",
|
||||
separator: " ",
|
||||
length: 3,
|
||||
seed,
|
||||
...config,
|
||||
});
|
||||
|
||||
return name;
|
||||
}
|
||||
102
web/logger/node.js
Normal file
102
web/logger/node.js
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Application logger.
|
||||
*
|
||||
* @import { LoggerOptions, Logger, Level, ChildLoggerOptions } from "pino"
|
||||
* @import { PrettyOptions } from "pino-pretty"
|
||||
*/
|
||||
|
||||
import { pino } from "pino";
|
||||
|
||||
//#region Constants
|
||||
|
||||
/**
|
||||
* Default options for creating a Pino logger.
|
||||
*
|
||||
* @category Logger
|
||||
* @satisfies {LoggerOptions<never, false>}
|
||||
*/
|
||||
export const DEFAULT_PINO_LOGGER_OPTIONS = {
|
||||
enabled: true,
|
||||
level: "info",
|
||||
transport: {
|
||||
target: "./transport.js",
|
||||
options: /** @satisfies {PrettyOptions} */ ({
|
||||
colorize: true,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Functions
|
||||
|
||||
/**
|
||||
* Read the log level from the environment.
|
||||
* @return {Level}
|
||||
*/
|
||||
export function readLogLevel() {
|
||||
return process.env.AK_LOG_LEVEL || DEFAULT_PINO_LOGGER_OPTIONS.level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Logger} FixtureLogger
|
||||
*/
|
||||
|
||||
/**
|
||||
* @this {Logger}
|
||||
* @param {string} fixtureName
|
||||
* @param {string} [testName]
|
||||
* @param {ChildLoggerOptions} [options]
|
||||
* @returns {FixtureLogger}
|
||||
*/
|
||||
function createFixtureLogger(fixtureName, testName, options) {
|
||||
return this.child(
|
||||
{ name: fixtureName },
|
||||
{
|
||||
msgPrefix: `[${testName}] `,
|
||||
...options,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} CustomLoggerMethods
|
||||
* @property {typeof createFixtureLogger} fixture
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Logger & CustomLoggerMethods} ConsoleLogger
|
||||
*/
|
||||
|
||||
/**
|
||||
* A singleton logger instance for Node.js.
|
||||
*
|
||||
* ```js
|
||||
* import { ConsoleLogger } from "#logger/node";
|
||||
*
|
||||
* ConsoleLogger.info("Hello, world!");
|
||||
* ```
|
||||
*
|
||||
* @runtime node
|
||||
* @type {ConsoleLogger}
|
||||
*/
|
||||
export const ConsoleLogger = Object.assign(
|
||||
pino({
|
||||
...DEFAULT_PINO_LOGGER_OPTIONS,
|
||||
level: readLogLevel(),
|
||||
}),
|
||||
{ fixture: createFixtureLogger },
|
||||
);
|
||||
|
||||
/**
|
||||
* @typedef {ReturnType<ConsoleLogger['child']>} ChildConsoleLogger
|
||||
*/
|
||||
|
||||
//#region Aliases
|
||||
|
||||
export const info = ConsoleLogger.info.bind(ConsoleLogger);
|
||||
export const debug = ConsoleLogger.debug.bind(ConsoleLogger);
|
||||
export const warn = ConsoleLogger.warn.bind(ConsoleLogger);
|
||||
export const error = ConsoleLogger.error.bind(ConsoleLogger);
|
||||
|
||||
//#endregion
|
||||
22
web/logger/transport.js
Normal file
22
web/logger/transport.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @file Pretty transport for Pino
|
||||
*
|
||||
* @import { PrettyOptions } from "pino-pretty"
|
||||
*/
|
||||
|
||||
import PinoPretty from "pino-pretty";
|
||||
|
||||
/**
|
||||
* @param {PrettyOptions} options
|
||||
*/
|
||||
function prettyTransporter(options) {
|
||||
const pretty = PinoPretty({
|
||||
...options,
|
||||
ignore: "pid,hostname",
|
||||
translateTime: "SYS:HH:MM:ss",
|
||||
});
|
||||
|
||||
return pretty;
|
||||
}
|
||||
|
||||
export default prettyTransporter;
|
||||
1762
web/package-lock.json
generated
1762
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -91,7 +91,7 @@
|
||||
"@codemirror/legacy-modes": "^6.5.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.3",
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@floating-ui/dom": "^1.7.2",
|
||||
"@floating-ui/dom": "^1.7.3",
|
||||
"@formatjs/intl-listformat": "^7.7.11",
|
||||
"@fortawesome/fontawesome-free": "^7.0.0",
|
||||
"@goauthentik/api": "^2025.6.4-1753714826",
|
||||
@@ -113,12 +113,12 @@
|
||||
"@openlayers-elements/maps": "^0.4.0",
|
||||
"@patternfly/elements": "^4.1.0",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^9.42.1",
|
||||
"@sentry/browser": "^10.0.0",
|
||||
"@spotlightjs/spotlight": "^3.0.1",
|
||||
"@storybook/addon-docs": "^9.0.18",
|
||||
"@storybook/addon-links": "^9.0.18",
|
||||
"@storybook/web-components": "^9.0.18",
|
||||
"@storybook/web-components-vite": "^9.0.18",
|
||||
"@storybook/addon-docs": "^9.1.0",
|
||||
"@storybook/addon-links": "^9.1.0",
|
||||
"@storybook/web-components": "^9.1.0",
|
||||
"@storybook/web-components-vite": "^9.1.0",
|
||||
"@types/codemirror": "^5.60.16",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.3",
|
||||
@@ -137,7 +137,7 @@
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.5.0",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"chromedriver": "^138.0.4",
|
||||
"chromedriver": "^138.0.5",
|
||||
"codemirror": "^6.0.2",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.44.0",
|
||||
@@ -184,6 +184,8 @@
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.38.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vite": "^7.0.6",
|
||||
"vitest": "^3.2.4",
|
||||
"webcomponent-qr-code": "^1.3.0",
|
||||
"wireit": "^0.14.12",
|
||||
"yaml": "^2.8.0"
|
||||
@@ -192,9 +194,9 @@
|
||||
"@esbuild/darwin-arm64": "^0.25.4",
|
||||
"@esbuild/linux-arm64": "^0.25.4",
|
||||
"@esbuild/linux-x64": "^0.25.4",
|
||||
"@rollup/rollup-darwin-arm64": "^4.46.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.46.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.46.1"
|
||||
"@rollup/rollup-darwin-arm64": "^4.46.2",
|
||||
"@rollup/rollup-linux-arm64-gnu": "^4.46.2",
|
||||
"@rollup/rollup-linux-x64-gnu": "^4.46.2"
|
||||
},
|
||||
"wireit": {
|
||||
"build": {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup": "^4.46.1",
|
||||
"rollup": "^4.46.2",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
},
|
||||
|
||||
94
web/playwright.config.js
Normal file
94
web/playwright.config.js
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @file Playwright configuration.
|
||||
*
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*
|
||||
* @import { LogFn, Logger } from "pino"
|
||||
*/
|
||||
|
||||
import { ConsoleLogger } from "#logger/node";
|
||||
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const CI = !!process.env.CI;
|
||||
|
||||
/**
|
||||
* @type {Map<string, Logger>}
|
||||
*/
|
||||
const LoggerCache = new Map();
|
||||
|
||||
const baseURL = process.env.AK_TEST_RUNNER_PAGE_URL ?? "http://localhost:9000";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./test/browser",
|
||||
fullyParallel: true,
|
||||
forbidOnly: CI,
|
||||
retries: CI ? 2 : 0,
|
||||
workers: CI ? 1 : undefined,
|
||||
reporter: CI
|
||||
? "github"
|
||||
: [
|
||||
// ---
|
||||
["list", { printSteps: true }],
|
||||
["html", { open: "never" }],
|
||||
],
|
||||
use: {
|
||||
testIdAttribute: "data-test-id",
|
||||
baseURL,
|
||||
trace: "on-first-retry",
|
||||
launchOptions: {
|
||||
logger: {
|
||||
isEnabled() {
|
||||
return true;
|
||||
},
|
||||
log: (name, severity, message, args) => {
|
||||
let logger = LoggerCache.get(name);
|
||||
|
||||
if (!logger) {
|
||||
logger = ConsoleLogger.child({
|
||||
name: `Playwright ${name.toUpperCase()}`,
|
||||
});
|
||||
LoggerCache.set(name, logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {LogFn}
|
||||
*/
|
||||
let log;
|
||||
|
||||
switch (severity) {
|
||||
case "verbose":
|
||||
log = logger.debug;
|
||||
break;
|
||||
case "warning":
|
||||
log = logger.warn;
|
||||
break;
|
||||
case "error":
|
||||
log = logger.error;
|
||||
break;
|
||||
default:
|
||||
log = logger.info;
|
||||
break;
|
||||
}
|
||||
|
||||
if (name === "api") {
|
||||
log = logger.debug;
|
||||
}
|
||||
|
||||
log.call(logger, message.toString(), args);
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -10,25 +10,35 @@ import { html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-license-notice")
|
||||
export class AkLicenceNotice extends WithLicenseSummary(AKElement) {
|
||||
export class AKLicenceNotice extends WithLicenseSummary(AKElement) {
|
||||
static styles = [$PFBase];
|
||||
|
||||
@property()
|
||||
notice = msg("Enterprise only");
|
||||
public label = msg("Enterprise only");
|
||||
|
||||
@property()
|
||||
public description = msg("Learn more about the enterprise license.");
|
||||
|
||||
render() {
|
||||
return this.hasEnterpriseLicense
|
||||
? nothing
|
||||
: html`
|
||||
<ak-alert class="pf-c-radio__description" inline plain>
|
||||
<a href="#/enterprise/licenses">${this.notice}</a>
|
||||
</ak-alert>
|
||||
`;
|
||||
if (this.hasEnterpriseLicense) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ak-alert class="pf-c-radio__description" inline plain>
|
||||
<a
|
||||
aria-label="${this.label}"
|
||||
aria-description="${this.description}"
|
||||
href="#/enterprise/licenses"
|
||||
>${this.label}</a
|
||||
>
|
||||
</ak-alert>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-license-notice": AkLicenceNotice;
|
||||
"ak-license-notice": AKLicenceNotice;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ModelForm } from "#elements/forms/ModelForm";
|
||||
import { msg } from "@lit/localize";
|
||||
|
||||
export abstract class BaseProviderForm<T> extends ModelForm<T, number> {
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated provider.")
|
||||
: msg("Successfully created provider.");
|
||||
|
||||
@@ -29,32 +29,38 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-provider-list")
|
||||
export class ProviderListPage extends TablePage<Provider> {
|
||||
searchEnabled(): boolean {
|
||||
override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
override pageTitle(): string {
|
||||
return msg("Providers");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
override pageDescription(): string {
|
||||
return msg("Provide support for protocols like SAML and OAuth to assigned applications.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
override pageIcon(): string {
|
||||
return "pf-icon pf-icon-integration";
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
override checkbox = true;
|
||||
override clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public order = "name";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
|
||||
public searchLabel = msg("Provider name");
|
||||
public searchPlaceholder = msg("Search for providers…");
|
||||
|
||||
override async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersAllList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
override columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Application")),
|
||||
@@ -63,8 +69,9 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Provider(s)")}
|
||||
.objects=${this.selectedElements}
|
||||
@@ -85,7 +92,7 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
rowApp(item: Provider): TemplateResult {
|
||||
#rowApp(item: Provider): TemplateResult {
|
||||
if (item.assignedApplicationName) {
|
||||
return html`<i class="pf-icon pf-icon-ok pf-m-success"></i>
|
||||
${msg("Assigned to application ")}
|
||||
@@ -93,6 +100,7 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
>${item.assignedApplicationName}</a
|
||||
>`;
|
||||
}
|
||||
|
||||
if (item.assignedBackchannelApplicationName) {
|
||||
return html`<i class="pf-icon pf-icon-ok pf-m-success"></i>
|
||||
${msg("Assigned to application (backchannel) ")}
|
||||
@@ -100,15 +108,15 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
>${item.assignedBackchannelApplicationName}</a
|
||||
>`;
|
||||
}
|
||||
return html`<i class="pf-icon pf-icon-warning-triangle pf-m-warning"></i> ${msg(
|
||||
"Warning: Provider not assigned to any application.",
|
||||
)}`;
|
||||
|
||||
return html`<i aria-hidden="true" class="pf-icon pf-icon-warning-triangle pf-m-warning"></i>
|
||||
${msg("Warning: Provider not assigned to any application.")}`;
|
||||
}
|
||||
|
||||
row(item: Provider): TemplateResult[] {
|
||||
override row(item: Provider): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/core/providers/${item.pk}"> ${item.name} </a>`,
|
||||
this.rowApp(item),
|
||||
this.#rowApp(item),
|
||||
html`${item.verboseName}`,
|
||||
html`<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Update")} </span>
|
||||
@@ -121,16 +129,20 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
type=${item.component}
|
||||
>
|
||||
</ak-proxy-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
||||
<button
|
||||
aria-label=${msg("Edit provider")}
|
||||
slot="trigger"
|
||||
class="pf-c-button pf-m-plain"
|
||||
>
|
||||
<pf-tooltip position="top" content=${msg("Edit")}>
|
||||
<i class="fas fa-edit"></i>
|
||||
<i aria-hidden="true" class="fas fa-edit"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
override renderObjectCreate(): TemplateResult {
|
||||
return html`<ak-provider-wizard> </ak-provider-wizard> `;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
`;
|
||||
}
|
||||
|
||||
renderSidebarBefore(): TemplateResult {
|
||||
protected renderSidebarBefore(): TemplateResult {
|
||||
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__title">${msg("User folders")}</div>
|
||||
|
||||
@@ -35,11 +35,6 @@
|
||||
--ak-navbar--height: 7rem;
|
||||
}
|
||||
|
||||
.pf-c-form__group {
|
||||
--pf-c-form--m-horizontal__group-label--md--GridColumnWidth: minmax(max-content, 9.375rem);
|
||||
column-gap: var(--pf-global--spacer--md);
|
||||
}
|
||||
|
||||
@supports selector(::-webkit-scrollbar) {
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import "#elements/forms/HorizontalFormElement";
|
||||
|
||||
import { SlottedTemplateResult } from "../elements/types";
|
||||
|
||||
import { AKElement, type AKElementProps } from "#elements/Base";
|
||||
|
||||
import { IDGenerator } from "@goauthentik/core/id";
|
||||
|
||||
import { html, nothing, TemplateResult } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
|
||||
type HelpType = TemplateResult | typeof nothing;
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
export interface HorizontalLightComponentProps<T> extends AKElementProps {
|
||||
name: string;
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
help?: string;
|
||||
bighelp?: TemplateResult | TemplateResult[];
|
||||
bighelp?: SlottedTemplateResult | SlottedTemplateResult[];
|
||||
hidden?: boolean;
|
||||
invalid?: boolean;
|
||||
errorMessages?: string[];
|
||||
@@ -20,7 +23,10 @@ export interface HorizontalLightComponentProps<T> extends AKElementProps {
|
||||
inputHint?: string;
|
||||
}
|
||||
|
||||
export class HorizontalLightComponent<T> extends AKElement {
|
||||
export abstract class HorizontalLightComponent<T>
|
||||
extends AKElement
|
||||
implements HorizontalLightComponentProps<T>
|
||||
{
|
||||
// Render into the lightDOM. This effectively erases the shadowDOM nature of this component, but
|
||||
// we're not actually using that and, for the meantime, we need the form handlers to be able to
|
||||
// find the children of this component.
|
||||
@@ -46,7 +52,7 @@ export class HorizontalLightComponent<T> extends AKElement {
|
||||
* @attribute
|
||||
*/
|
||||
@property({ type: String, reflect: true })
|
||||
label = "";
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* @property
|
||||
@@ -104,16 +110,19 @@ export class HorizontalLightComponent<T> extends AKElement {
|
||||
* @attribute
|
||||
*/
|
||||
@property({ type: String, attribute: "input-hint" })
|
||||
inputHint = "";
|
||||
inputHint?: string;
|
||||
|
||||
protected renderControl() {
|
||||
throw new Error("Must be implemented in a subclass");
|
||||
}
|
||||
|
||||
renderHelp(): HelpType[] {
|
||||
const bigHelp: HelpType[] = Array.isArray(this.bighelp)
|
||||
protected fieldID = IDGenerator.elementID().toString();
|
||||
|
||||
protected renderHelp(): SlottedTemplateResult | SlottedTemplateResult[] {
|
||||
const bigHelp: SlottedTemplateResult[] = Array.isArray(this.bighelp)
|
||||
? this.bighelp
|
||||
: [this.bighelp ?? nothing];
|
||||
|
||||
return [
|
||||
this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing,
|
||||
...bigHelp,
|
||||
@@ -121,17 +130,16 @@ export class HorizontalLightComponent<T> extends AKElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
// prettier-ignore
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${this.label}
|
||||
fieldID=${this.fieldID}
|
||||
label=${ifDefined(this.label)}
|
||||
?required=${this.required}
|
||||
?hidden=${this.hidden}
|
||||
name=${this.name}
|
||||
.errorMessages=${this.errorMessages}
|
||||
?invalid=${this.invalid}
|
||||
>
|
||||
${this.renderControl()}
|
||||
${this.renderHelp()}
|
||||
>
|
||||
${this.renderControl()} ${this.renderHelp()}
|
||||
</ak-form-element-horizontal> `;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import {
|
||||
HorizontalLightComponentProps,
|
||||
} from "./HorizontalLightComponent.js";
|
||||
|
||||
import { bound } from "#elements/decorators/bound";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
@@ -24,6 +22,8 @@ export interface AkHiddenTextInputProps extends BaseProps {
|
||||
|
||||
export type InputLike = HTMLTextAreaElement | HTMLInputElement;
|
||||
|
||||
export type InputListener = (ev: InputEvent) => void;
|
||||
|
||||
/**
|
||||
* @element ak-hidden-text-input
|
||||
* @class AkHiddenTextInput
|
||||
@@ -72,6 +72,15 @@ export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
|
||||
@property({ type: String })
|
||||
public placeholder?: string;
|
||||
|
||||
/**
|
||||
* Text for when the input has no set value
|
||||
*
|
||||
* @property
|
||||
* @attribute
|
||||
*/
|
||||
@property({ type: String })
|
||||
public label?: string;
|
||||
|
||||
/**
|
||||
* Specify kind of help the browser should try to provide
|
||||
*
|
||||
@@ -98,28 +107,16 @@ export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
|
||||
@query("#main > input")
|
||||
protected inputField!: T;
|
||||
|
||||
@bound
|
||||
private handleToggleVisibility() {
|
||||
this.revealed = !this.revealed;
|
||||
|
||||
// Maintain focus on input after toggle
|
||||
this.updateComplete.then(() => {
|
||||
if (this.inputField && document.activeElement === this) {
|
||||
this.inputField.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Because of the peculiarities of how HorizontalLightComponent works, keeping its content
|
||||
// in the LightDom so the inner components actually inherit styling, the normal `css` options
|
||||
// aren't available. Embedding styles is bad styling, and we'll fix it in the next style
|
||||
// refresh.
|
||||
protected renderInputField(setValue: (ev: InputEvent) => void, code: boolean) {
|
||||
protected renderInputField(setValue: InputListener, code: boolean) {
|
||||
return html` <input
|
||||
style="flex: 1 1 auto; min-width: 0;"
|
||||
part="input"
|
||||
autocomplete=${ifDefined(this.autocomplete)}
|
||||
type=${this.revealed ? "text" : "password"}
|
||||
aria-label=${ifDefined(this.label)}
|
||||
@input=${setValue}
|
||||
value=${ifDefined(this.value)}
|
||||
placeholder=${ifDefined(this.placeholder)}
|
||||
@@ -134,12 +131,12 @@ export class AkHiddenTextInput<T extends InputLike = HTMLInputElement>
|
||||
|
||||
protected override renderControl() {
|
||||
const code = this.inputHint === "code";
|
||||
const setValue = (ev: InputEvent) => {
|
||||
const setValue: InputListener = (ev) => {
|
||||
this.value = (ev.target as T).value;
|
||||
};
|
||||
|
||||
return html` <div style="display: flex; gap: 0.25rem">
|
||||
${this.renderInputField(setValue, code)}
|
||||
<!-- -->
|
||||
<ak-visibility-toggle
|
||||
part="toggle"
|
||||
style="flex: 0 0 auto; align-self: flex-start"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { AkHiddenTextInput, type AkHiddenTextInputProps } from "./ak-hidden-text-input.js";
|
||||
import {
|
||||
AkHiddenTextInput,
|
||||
type AkHiddenTextInputProps,
|
||||
InputListener,
|
||||
} from "./ak-hidden-text-input.js";
|
||||
|
||||
import { html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
@@ -96,7 +100,7 @@ export class AkHiddenTextAreaInput
|
||||
// in the LightDom so the inner components actually inherit styling, the normal `css` options
|
||||
// aren't available. Embedding styles is bad styling, and we'll fix it in the next style
|
||||
// refresh.
|
||||
protected override renderInputField(setValue: (ev: InputEvent) => void, code: boolean) {
|
||||
protected override renderInputField(setValue: InputListener, code: boolean) {
|
||||
const wrap = this.revealed ? this.wrap : "soft";
|
||||
|
||||
return html`
|
||||
@@ -105,6 +109,7 @@ export class AkHiddenTextAreaInput
|
||||
part="textarea"
|
||||
@input=${setValue}
|
||||
placeholder=${ifDefined(this.placeholder)}
|
||||
aria-label=${ifDefined(this.label)}
|
||||
rows=${ifDefined(this.rows)}
|
||||
cols=${ifDefined(this.cols)}
|
||||
wrap=${ifDefined(wrap)}
|
||||
|
||||
@@ -175,6 +175,7 @@ export class NavigationButtons extends AKElement {
|
||||
return html`<img
|
||||
class="pf-c-page__header-tools-item pf-c-avatar pf-m-hidden pf-m-visible-on-xl"
|
||||
src=${ifDefined(this.me?.user.avatar)}
|
||||
aria-hidden="true"
|
||||
alt="${msg("Avatar image")}"
|
||||
/>`;
|
||||
}
|
||||
@@ -189,7 +190,7 @@ export class NavigationButtons extends AKElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<div class="pf-c-page__header-tools">
|
||||
return html`<div role="presentation" class="pf-c-page__header-tools">
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
${this.renderApiDrawerTrigger()}
|
||||
<!-- -->
|
||||
|
||||
@@ -21,6 +21,7 @@ export class AkNumberInput extends HorizontalLightComponent<number> {
|
||||
return html`<input
|
||||
type="number"
|
||||
@input=${setValue}
|
||||
aria-label=${ifDefined(this.label)}
|
||||
value=${ifDefined(this.value)}
|
||||
min=${ifDefined(this.min)}
|
||||
class="pf-c-form-control"
|
||||
|
||||
@@ -88,7 +88,7 @@ export class AKPageNavbar
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
|
||||
navbar {
|
||||
.main-content {
|
||||
border-bottom: var(--pf-global--BorderWidth--sm);
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: var(--pf-global--BorderColor--100);
|
||||
@@ -350,7 +350,12 @@ export class AKPageNavbar
|
||||
renderIcon() {
|
||||
if (this.icon) {
|
||||
if (this.iconImage && !this.icon.startsWith("fa://")) {
|
||||
return html`<img class="accent-icon pf-icon" src="${this.icon}" alt="page icon" />`;
|
||||
return html`<img
|
||||
aria-hidden="true"
|
||||
class="accent-icon pf-icon"
|
||||
src="${this.icon}"
|
||||
alt="page icon"
|
||||
/>`;
|
||||
}
|
||||
|
||||
const icon = this.icon.replaceAll("fa://", "fa ");
|
||||
@@ -362,9 +367,9 @@ export class AKPageNavbar
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <slot></slot>
|
||||
<navbar aria-label="Main" class="navbar">
|
||||
<aside class="brand ${this.open ? "" : "pf-m-collapsed"}">
|
||||
<a href="#/">
|
||||
<div role="banner" aria-label="Main" class="main-content">
|
||||
<aside role="presentation" class="brand ${this.open ? "" : "pf-m-collapsed"}">
|
||||
<a aria-label="${msg("Home")}" href="#/">
|
||||
<div class="logo">
|
||||
<img
|
||||
src=${themeImage(this.brandingLogo)}
|
||||
@@ -375,31 +380,35 @@ export class AKPageNavbar
|
||||
</a>
|
||||
</aside>
|
||||
<button
|
||||
aria-controls="global-nav"
|
||||
class="sidebar-trigger pf-c-button pf-m-plain"
|
||||
@click=${this.#toggleSidebar}
|
||||
aria-label=${msg("Toggle sidebar")}
|
||||
aria-label=${this.open ? msg("Collapse navigation") : msg("Expand navigation")}
|
||||
aria-expanded=${this.open ? "true" : "false"}
|
||||
>
|
||||
<i class="fas fa-bars"></i>
|
||||
<i aria-hidden="true" class="fas fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<section
|
||||
class="items primary pf-c-content ${this.description ? "block-sibling" : ""}"
|
||||
>
|
||||
<h1 class="page-title">
|
||||
<div class="items primary pf-c-content ${this.description ? "block-sibling" : ""}">
|
||||
<h1 aria-labelledby="page-navbar-heading" class="page-title">
|
||||
${this.hasIcon
|
||||
? html`<slot name="icon">${this.renderIcon()}</slot>`
|
||||
? html`<slot aria-hidden="true" name="icon">${this.renderIcon()}</slot>`
|
||||
: nothing}
|
||||
${this.header}
|
||||
<span id="page-navbar-heading">${this.header}</span>
|
||||
</h1>
|
||||
</section>
|
||||
</div>
|
||||
${this.description
|
||||
? html`<section class="items page-description pf-c-content">
|
||||
? html`<div
|
||||
role="heading"
|
||||
aria-level="2"
|
||||
aria-label="${this.description}"
|
||||
class="items page-description pf-c-content"
|
||||
>
|
||||
<p>${this.description}</p>
|
||||
</section>`
|
||||
</div>`
|
||||
: nothing}
|
||||
|
||||
<section class="items secondary">
|
||||
<div class="items secondary">
|
||||
<div class="pf-c-page__header-tools-group">
|
||||
<ak-nav-buttons .uiConfig=${this.uiConfig} .me=${this.session}>
|
||||
<a
|
||||
@@ -411,8 +420,8 @@ export class AKPageNavbar
|
||||
</a>
|
||||
</ak-nav-buttons>
|
||||
</div>
|
||||
</section>
|
||||
</navbar>`;
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -3,9 +3,11 @@ import "#elements/forms/Radio";
|
||||
import { HorizontalLightComponent } from "./HorizontalLightComponent.js";
|
||||
|
||||
import { RadioOption } from "#elements/forms/Radio";
|
||||
import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-radio-input")
|
||||
export class AkRadioInput<T> extends HorizontalLightComponent<T> {
|
||||
@@ -21,20 +23,20 @@ export class AkRadioInput<T> extends HorizontalLightComponent<T> {
|
||||
}
|
||||
}
|
||||
|
||||
renderHelp() {
|
||||
// This is weird, but Typescript says it's necessary?
|
||||
return [nothing as typeof nothing];
|
||||
protected override renderHelp(): SlottedTemplateResult {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
renderControl() {
|
||||
const helpText = this.help.trim();
|
||||
|
||||
return html`<ak-radio
|
||||
label=${ifDefined(this.label)}
|
||||
.options=${this.options}
|
||||
.value=${this.value}
|
||||
@input=${this.handleInput}
|
||||
></ak-radio>
|
||||
${this.help.trim()
|
||||
? html`<p class="pf-c-form__helper-radio">${this.help}</p>`
|
||||
: nothing}`;
|
||||
${helpText ? html`<p class="pf-c-form__helper-radio">${helpText}</p>` : nothing}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { AKElement } from "#elements/Base";
|
||||
|
||||
import { IDGenerator } from "@goauthentik/core/id";
|
||||
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-switch-input")
|
||||
export class AkSwitchInput extends AKElement {
|
||||
@@ -34,12 +37,21 @@ export class AkSwitchInput extends AKElement {
|
||||
@query("input.pf-c-switch__input[type=checkbox]")
|
||||
checkbox!: HTMLInputElement;
|
||||
|
||||
#fieldID: string = IDGenerator.randomID();
|
||||
|
||||
render() {
|
||||
const doCheck = this.checked ? this.checked : undefined;
|
||||
const helpText = this.help.trim();
|
||||
|
||||
return html` <ak-form-element-horizontal name=${this.name} ?required=${this.required}>
|
||||
<label class="pf-c-switch">
|
||||
<input class="pf-c-switch__input" type="checkbox" ?checked=${doCheck} />
|
||||
<label class="pf-c-switch" for="${this.#fieldID}">
|
||||
<input
|
||||
id="${this.#fieldID}"
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${doCheck}
|
||||
aria-label=${ifDefined(this.label)}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
@@ -47,7 +59,7 @@ export class AkSwitchInput extends AKElement {
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${this.label}</span>
|
||||
</label>
|
||||
${this.help.trim() ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing}
|
||||
${helpText ? html`<p class="pf-c-form__helper-text">${helpText}</p>` : nothing}
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ export class AkTextInput extends HorizontalLightComponent<string> {
|
||||
@property({ type: String, reflect: true })
|
||||
value = "";
|
||||
|
||||
@property({ type: String })
|
||||
autocomplete?: string;
|
||||
|
||||
@property({ type: String })
|
||||
placeholder?: string;
|
||||
|
||||
@@ -22,14 +25,17 @@ export class AkTextInput extends HorizontalLightComponent<string> {
|
||||
|
||||
return html` <input
|
||||
type="text"
|
||||
role="textbox"
|
||||
id=${ifDefined(this.fieldID)}
|
||||
@input=${setValue}
|
||||
value=${ifDefined(this.value)}
|
||||
class="${classMap({
|
||||
"pf-c-form-control": true,
|
||||
"pf-m-monospace": code,
|
||||
})}"
|
||||
autocomplete=${ifDefined(code ? "off" : undefined)}
|
||||
autocomplete=${ifDefined(code ? "off" : this.autocomplete)}
|
||||
spellcheck=${ifDefined(code ? "false" : undefined)}
|
||||
aria-label=${ifDefined(this.placeholder || this.label)}
|
||||
placeholder=${ifDefined(this.placeholder)}
|
||||
?required=${this.required}
|
||||
/>`;
|
||||
|
||||
@@ -44,12 +44,20 @@ const testOptions = [
|
||||
];
|
||||
|
||||
export const CheckboxGroup = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const displayChange = (ev: any) => {
|
||||
document.getElementById("check-message-pad")!.innerHTML = `
|
||||
<p>Values selected on target: ${ev.target.value.join(", ")}</p>
|
||||
<p>Values sent in event: ${ev.detail.join(", ")}</p>
|
||||
<p>Values present as data-ak-control: <kbd>${JSON.stringify(ev.target.json, null)}</kbd></p>`;
|
||||
const displayChange = (event: CustomEvent<string[]>) => {
|
||||
const target = event.target as AkCheckboxGroup;
|
||||
|
||||
document.getElementById("check-message-pad")!.innerHTML = /*html*/ `
|
||||
<p>
|
||||
Values selected on target: ${target.value.join(", ")}
|
||||
</p>
|
||||
<p>
|
||||
Values sent in event: ${event.detail.join(", ")}
|
||||
</p>
|
||||
<p>
|
||||
Values present as data-ak-control: <kbd>${JSON.stringify(target.json(), null)}</kbd>
|
||||
</p>
|
||||
`;
|
||||
};
|
||||
|
||||
return container(
|
||||
@@ -66,28 +74,32 @@ export const CheckboxGroup = () => {
|
||||
);
|
||||
};
|
||||
|
||||
type FDType = [string, string | FormDataEntryValue];
|
||||
type FDType = [key: string, value: string | FormDataEntryValue];
|
||||
|
||||
export const FormCheckboxGroup = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const displayChange = (ev: any) => {
|
||||
ev.preventDefault();
|
||||
const formData = new FormData(ev.target);
|
||||
const displayChange = (event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
const valList = Array.from(formData)
|
||||
.map(([_key, val]: FDType) => val)
|
||||
.join(", ");
|
||||
if (!(event.target instanceof HTMLFormElement)) {
|
||||
throw new Error("Expected target to be a form element");
|
||||
}
|
||||
|
||||
const fdList = Array.from(formData)
|
||||
.map(
|
||||
([key, val]: FDType) =>
|
||||
`${encodeURIComponent(key)}=${encodeURIComponent(val as string)}`,
|
||||
)
|
||||
.join("&");
|
||||
const formData = new FormData(event.target);
|
||||
|
||||
document.getElementById("check-message-pad")!.innerHTML = `
|
||||
<p>Values as seen in \`form.formData\`: ${valList}</p>
|
||||
<p>Values as seen in x-form-encoded format: <kbd>${fdList}</kbd></p>`;
|
||||
const valList = Array.from(formData.values()).join(", ");
|
||||
|
||||
const fdList = Array.from(formData, ([key, val]: FDType) => {
|
||||
return `${encodeURIComponent(key)}=${encodeURIComponent(val as string)}`;
|
||||
}).join("&");
|
||||
|
||||
document.getElementById("check-message-pad")!.innerHTML = /*html*/ `
|
||||
<p>
|
||||
Values as seen in ${"`form.formData`"}: ${valList}
|
||||
</p>
|
||||
<p>
|
||||
Values as seen in x-form-encoded format: <kbd>${fdList}</kbd>
|
||||
</p>
|
||||
`;
|
||||
};
|
||||
|
||||
return container(
|
||||
@@ -95,9 +107,9 @@ export const FormCheckboxGroup = () => {
|
||||
FormData example. This variant emits the same events and exhibits the same behavior
|
||||
as the above, but instead of monitoring for 'change' events on the checkbox group,
|
||||
we monitor for the user pressing the 'submit' button. What is displayed is the
|
||||
values as understood by the <form> object, via its internal \`formData\`
|
||||
field, to demonstrate that this component works with forms as if it were a native
|
||||
form element.
|
||||
values as understood by the <form> object, via its internal
|
||||
${"`form.formData`"} field, to demonstrate that this component works with forms as
|
||||
if it were a native form element.
|
||||
</p>
|
||||
|
||||
<form @submit=${displayChange}>
|
||||
|
||||
@@ -116,8 +116,8 @@ export abstract class ModalButton extends AKElement {
|
||||
* @abstract
|
||||
*/
|
||||
protected renderModal(): SlottedTemplateResult {
|
||||
return html`<div class="pf-c-backdrop" @click=${this.#backdropListener}>
|
||||
<div class="pf-l-bullseye">
|
||||
return html`<div class="pf-c-backdrop" @click=${this.#backdropListener} role="presentation">
|
||||
<div class="pf-l-bullseye" role="presentation">
|
||||
<div
|
||||
class="pf-c-modal-box ${this.size} ${this.locked ? "locked" : ""}"
|
||||
role="dialog"
|
||||
|
||||
@@ -20,7 +20,7 @@ import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||
type BulkDeleteMetadata = { key: string; value: string }[];
|
||||
|
||||
@customElement("ak-delete-objects-table")
|
||||
export class DeleteObjectsTable<T> extends Table<T> {
|
||||
export class DeleteObjectsTable<T extends object> extends Table<T> {
|
||||
paginated = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
@@ -246,7 +246,7 @@ export class DeleteBulkForm<T> extends ModalButton {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-delete-objects-table": DeleteObjectsTable<unknown>;
|
||||
"ak-forms-delete-bulk": DeleteBulkForm<unknown>;
|
||||
"ak-delete-objects-table": DeleteObjectsTable<object>;
|
||||
"ak-forms-delete-bulk": DeleteBulkForm<object>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +58,6 @@ export class HorizontalFormElement extends AKElement {
|
||||
grid-template-columns:
|
||||
var(--pf-c-form--m-horizontal__group-label--md--GridColumnWidth)
|
||||
var(--pf-c-form--m-horizontal__group-control--md--GridColumnWidth);
|
||||
|
||||
&[data-flow-direction="row"] {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.pf-c-form__group-label {
|
||||
|
||||
@@ -100,7 +100,7 @@ export class ModalForm extends ModalButton {
|
||||
);
|
||||
};
|
||||
|
||||
renderModalInner(): TemplateResult {
|
||||
protected renderModalInner(): TemplateResult {
|
||||
return html`${this.loading
|
||||
? html`<ak-loading-overlay topmost></ak-loading-overlay>`
|
||||
: nothing}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { randomId } from "../utils/randomId.js";
|
||||
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { CustomEmitterElement } from "#elements/utils/eventEmitter";
|
||||
|
||||
import { IDGenerator } from "@goauthentik/core/id";
|
||||
|
||||
import { css, CSSResult, html, nothing, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { map } from "lit/directives/map.js";
|
||||
@@ -22,15 +22,15 @@ export interface RadioOption<T> {
|
||||
@customElement("ak-radio")
|
||||
export class Radio<T> extends CustomEmitterElement(AKElement) {
|
||||
@property({ attribute: false })
|
||||
options: RadioOption<T>[] = [];
|
||||
public options: RadioOption<T>[] = [];
|
||||
|
||||
@property()
|
||||
name = "";
|
||||
public name = "";
|
||||
|
||||
@property({ attribute: false })
|
||||
value?: T;
|
||||
public value?: T;
|
||||
|
||||
internalId: string;
|
||||
#fieldID: string = this.name || IDGenerator.randomID();
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
@@ -46,16 +46,13 @@ export class Radio<T> extends CustomEmitterElement(AKElement) {
|
||||
.pf-c-radio span {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pf-c-radio__description {
|
||||
text-wrap: balance;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.renderRadio = this.renderRadio.bind(this);
|
||||
this.buildChangeHandler = this.buildChangeHandler.bind(this);
|
||||
this.internalId = this.name || `radio-${randomId(8)}`;
|
||||
}
|
||||
|
||||
// Set the value if it's not set already. Property changes inside the `willUpdate()` method do
|
||||
// not trigger an element update.
|
||||
willUpdate() {
|
||||
@@ -71,42 +68,48 @@ export class Radio<T> extends CustomEmitterElement(AKElement) {
|
||||
// radio loses its setting, and the selected radio gains its setting. We want radio buttons to
|
||||
// present a unified event interface, so we prevent the event from triggering if the value is
|
||||
// already set.
|
||||
buildChangeHandler(option: RadioOption<T>) {
|
||||
#buildChangeListener = (option: RadioOption<T>) => {
|
||||
return (ev: Event) => {
|
||||
// This is a controlled input. Stop the native event from escaping or affecting the
|
||||
// value. We'll do that ourselves.
|
||||
// value. We'll do that ourselves.
|
||||
ev.stopPropagation();
|
||||
|
||||
if (option.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.value = option.value;
|
||||
|
||||
this.dispatchCustomEvent("change", { value: option.value });
|
||||
this.dispatchCustomEvent("input", { value: option.value });
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
renderRadio(option: RadioOption<T>, index: number) {
|
||||
const elId = `${this.internalId}-${index}`;
|
||||
const handler = this.buildChangeHandler(option);
|
||||
return html`<div class="pf-c-radio" @click=${handler}>
|
||||
#renderRadio = (option: RadioOption<T>, index: number) => {
|
||||
const id = `${this.#fieldID}-${index}`;
|
||||
|
||||
const changeListener = this.#buildChangeListener(option);
|
||||
|
||||
return html`<div class="pf-c-radio" @click=${changeListener}>
|
||||
<input
|
||||
class="pf-c-radio__input"
|
||||
type="radio"
|
||||
name="${this.name}"
|
||||
id=${elId}
|
||||
aria-label=${option.label}
|
||||
id=${id}
|
||||
.checked=${option.value === this.value}
|
||||
.disabled=${option.disabled}
|
||||
/>
|
||||
<label class="pf-c-radio__label" for=${elId}>${option.label}</label>
|
||||
<label class="pf-c-radio__label" for=${id}>${option.label}</label>
|
||||
${option.description
|
||||
? html`<span class="pf-c-radio__description">${option.description}</span>`
|
||||
: nothing}
|
||||
</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return html`<div class="pf-c-form__group-control pf-m-stack">
|
||||
${map(this.options, this.renderRadio)}
|
||||
${map(this.options, this.#renderRadio)}
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import "#elements/chips/ChipGroup";
|
||||
import "#elements/table/TablePagination";
|
||||
import "#elements/table/TableSearch";
|
||||
|
||||
import { TableLike } from "./shared.js";
|
||||
import { TableColumn } from "./TableColumn.js";
|
||||
|
||||
import { EVENT_REFRESH } from "#common/constants";
|
||||
import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network";
|
||||
import { uiConfig } from "#common/ui/config";
|
||||
@@ -17,7 +20,7 @@ import { SlottedTemplateResult } from "#elements/types";
|
||||
|
||||
import { Pagination } from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
@@ -32,10 +35,8 @@ import PFToolbar from "@patternfly/patternfly/components/Toolbar/toolbar.css";
|
||||
import PFBullseye from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
export interface TableLike {
|
||||
order?: string;
|
||||
fetch: () => void;
|
||||
}
|
||||
export * from "./shared.js";
|
||||
export * from "./TableColumn.js";
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
pagination: Pagination;
|
||||
@@ -44,138 +45,10 @@ export interface PaginatedResponse<T> {
|
||||
results: Array<T>;
|
||||
}
|
||||
|
||||
export class TableColumn {
|
||||
title: string;
|
||||
orderBy?: string;
|
||||
|
||||
onClick?: () => void;
|
||||
|
||||
constructor(title: string, orderBy?: string) {
|
||||
this.title = title;
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
headerClickHandler(table: TableLike): void {
|
||||
if (!this.orderBy) {
|
||||
return;
|
||||
}
|
||||
table.order = table.order === this.orderBy ? `-${this.orderBy}` : this.orderBy;
|
||||
table.fetch();
|
||||
}
|
||||
|
||||
private getSortIndicator(table: TableLike): string {
|
||||
switch (table.order) {
|
||||
case this.orderBy:
|
||||
return "fa-long-arrow-alt-down";
|
||||
case `-${this.orderBy}`:
|
||||
return "fa-long-arrow-alt-up";
|
||||
default:
|
||||
return "fa-arrows-alt-v";
|
||||
}
|
||||
}
|
||||
|
||||
renderSortable(table: TableLike): TemplateResult {
|
||||
return html` <button
|
||||
class="pf-c-table__button"
|
||||
@click=${() => this.headerClickHandler(table)}
|
||||
>
|
||||
<div class="pf-c-table__button-content">
|
||||
<span class="pf-c-table__text">${this.title}</span>
|
||||
<span class="pf-c-table__sort-indicator">
|
||||
<i class="fas ${this.getSortIndicator(table)}"></i>
|
||||
</span>
|
||||
</div>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
render(table: TableLike): TemplateResult {
|
||||
const classes = {
|
||||
"pf-c-table__sort": !!this.orderBy,
|
||||
"pf-m-selected": table.order === this.orderBy || table.order === `-${this.orderBy}`,
|
||||
};
|
||||
|
||||
return html`<th role="columnheader" scope="col" class="${classMap(classes)}">
|
||||
${this.orderBy ? this.renderSortable(table) : html`${this.title}`}
|
||||
</th>`;
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Table<T> extends WithLicenseSummary(AKElement) implements TableLike {
|
||||
abstract apiEndpoint(): Promise<PaginatedResponse<T>>;
|
||||
abstract columns(): TableColumn[];
|
||||
abstract row(item: T): SlottedTemplateResult[];
|
||||
|
||||
private isLoading = false;
|
||||
|
||||
#pageParam = `${this.tagName.toLowerCase()}-page`;
|
||||
#searchParam = `${this.tagName.toLowerCase()}-search`;
|
||||
|
||||
@property({ type: Boolean })
|
||||
supportsQL: boolean = false;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
renderExpanded(_item: T): SlottedTemplateResult {
|
||||
if (this.expandable) {
|
||||
throw new Error("Expandable is enabled but renderExpanded is not overridden!");
|
||||
}
|
||||
|
||||
return nothing;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
data?: PaginatedResponse<T>;
|
||||
|
||||
@property({ type: Number })
|
||||
page = getURLParam(this.#pageParam, 1);
|
||||
|
||||
/**
|
||||
* Set if your `selectedElements` use of the selection box is to enable bulk-delete,
|
||||
* so that stale data is cleared out when the API returns a new list minus the deleted entries.
|
||||
*
|
||||
* @prop
|
||||
*/
|
||||
@property({ attribute: "clear-on-refresh", type: Boolean, reflect: true })
|
||||
clearOnRefresh = false;
|
||||
|
||||
@property({ type: String })
|
||||
order?: string;
|
||||
|
||||
@property({ type: String })
|
||||
search: string = "";
|
||||
|
||||
@property({ type: Boolean })
|
||||
checkbox = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
clickable = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
clickHandler: (item: T) => void = () => {};
|
||||
|
||||
@property({ type: Boolean })
|
||||
radioSelect = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
checkboxChip = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
selectedElements: T[] = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
paginated = true;
|
||||
|
||||
@property({ type: Boolean })
|
||||
expandable = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
expandedElements: T[] = [];
|
||||
|
||||
@state()
|
||||
error?: APIError;
|
||||
|
||||
export abstract class Table<T extends object>
|
||||
extends WithLicenseSummary(AKElement)
|
||||
implements TableLike
|
||||
{
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFTable,
|
||||
@@ -213,16 +86,124 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
`,
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener(EVENT_REFRESH, async () => {
|
||||
await this.fetch();
|
||||
});
|
||||
abstract apiEndpoint(): Promise<PaginatedResponse<T>>;
|
||||
abstract columns(): TableColumn[];
|
||||
abstract row(item: T): SlottedTemplateResult[];
|
||||
|
||||
#loading = false;
|
||||
|
||||
#pageParam = `${this.tagName.toLowerCase()}-page`;
|
||||
#searchParam = `${this.tagName.toLowerCase()}-search`;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public supportsQL: boolean = false;
|
||||
|
||||
//#region Properties
|
||||
|
||||
@property({ type: String })
|
||||
public toolbarLabel = msg("Table actions");
|
||||
|
||||
@property({ type: String })
|
||||
public label?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
public data?: PaginatedResponse<T>;
|
||||
|
||||
@property({ type: Number })
|
||||
public page = getURLParam(this.#pageParam, 1);
|
||||
|
||||
/**
|
||||
* Set if your `selectedElements` use of the selection box is to enable bulk-delete,
|
||||
* so that stale data is cleared out when the API returns a new list minus the deleted entries.
|
||||
*
|
||||
* @prop
|
||||
*/
|
||||
@property({ attribute: "clear-on-refresh", type: Boolean, reflect: true })
|
||||
public clearOnRefresh = false;
|
||||
|
||||
@property({ type: String })
|
||||
public order?: string;
|
||||
|
||||
@property({ type: String })
|
||||
public search: string = "";
|
||||
|
||||
@property({ type: Boolean })
|
||||
public checkbox = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public clickable = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public clickHandler: (item: T) => void = () => {};
|
||||
|
||||
@property({ type: Boolean })
|
||||
public radioSelect = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public checkboxChip = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public selectedElements: T[] = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
public paginated = true;
|
||||
|
||||
@property({ type: Boolean })
|
||||
public expandable = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public expandedElements: T[] = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
public searchLabel?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
public searchPlaceholder?: string;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
@state()
|
||||
protected error?: APIError;
|
||||
|
||||
#refreshListener = () => {
|
||||
return this.fetch();
|
||||
};
|
||||
|
||||
public override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.addEventListener(EVENT_REFRESH, this.#refreshListener);
|
||||
|
||||
if (this.searchEnabled()) {
|
||||
this.search = getURLParam(this.#searchParam, "");
|
||||
}
|
||||
}
|
||||
|
||||
public override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.removeEventListener(EVENT_REFRESH, this.#refreshListener);
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("page")) {
|
||||
updateURLParams({
|
||||
[this.#pageParam]: this.page,
|
||||
});
|
||||
}
|
||||
if (changedProperties.has("search")) {
|
||||
updateURLParams({
|
||||
[this.#searchParam]: this.search,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.fetch();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
async defaultEndpointConfig() {
|
||||
return {
|
||||
ordering: this.order,
|
||||
@@ -232,16 +213,12 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
};
|
||||
}
|
||||
|
||||
public groupBy(items: T[]): [SlottedTemplateResult, T[]][] {
|
||||
return groupBy(items, () => {
|
||||
return "";
|
||||
});
|
||||
}
|
||||
public fetch(): Promise<void> {
|
||||
if (this.#loading) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public async fetch(): Promise<void> {
|
||||
if (this.isLoading) return;
|
||||
|
||||
this.isLoading = true;
|
||||
this.#loading = true;
|
||||
|
||||
return this.apiEndpoint()
|
||||
.then((data) => {
|
||||
@@ -289,12 +266,14 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
this.error = await parseAPIResponseError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
this.#loading = false;
|
||||
this.requestUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
private renderLoading(): TemplateResult {
|
||||
//#region Render
|
||||
|
||||
protected renderLoading(): TemplateResult {
|
||||
return html`<tr role="row">
|
||||
<td role="cell" colspan="25">
|
||||
<div class="pf-l-bullseye">
|
||||
@@ -320,11 +299,21 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
</tbody>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): SlottedTemplateResult {
|
||||
/**
|
||||
* Render the create object button.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
protected renderObjectCreate(): SlottedTemplateResult {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
renderError(): SlottedTemplateResult {
|
||||
/**
|
||||
* Render the error state.
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
protected renderError(): SlottedTemplateResult {
|
||||
if (!this.error) return nothing;
|
||||
|
||||
return html`<ak-empty-state icon="fa-ban"
|
||||
@@ -333,11 +322,27 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
</ak-empty-state>`;
|
||||
}
|
||||
|
||||
//#region Rows
|
||||
/**
|
||||
* Render a row for a given item.
|
||||
*
|
||||
* @param item The item to render.
|
||||
*/
|
||||
protected rowLabel<T extends object>(item: T): string | typeof nothing {
|
||||
const name = "name" in item && typeof item.name === "string" ? item.name.trim() : null;
|
||||
|
||||
if (!name) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return msg(str`${name}`);
|
||||
}
|
||||
|
||||
private renderRows(): TemplateResult[] | undefined {
|
||||
if (this.error) {
|
||||
return [this.renderEmpty(this.renderError())];
|
||||
}
|
||||
if (!this.data || this.isLoading) {
|
||||
if (!this.data || this.#loading) {
|
||||
return [this.renderLoading()];
|
||||
}
|
||||
if (this.data.pagination.count === 0) {
|
||||
@@ -357,7 +362,23 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
});
|
||||
}
|
||||
|
||||
//#region Grouping
|
||||
|
||||
public groupBy(items: T[]): [SlottedTemplateResult, T[]][] {
|
||||
return groupBy(items, () => "");
|
||||
}
|
||||
|
||||
renderExpanded(_item: T): SlottedTemplateResult {
|
||||
if (this.expandable) {
|
||||
throw new Error("Expandable is enabled but renderExpanded is not overridden!");
|
||||
}
|
||||
|
||||
return nothing;
|
||||
}
|
||||
|
||||
private renderRowGroup(items: T[]): TemplateResult[] {
|
||||
const columns = this.columns();
|
||||
|
||||
return items.map((item) => {
|
||||
const itemSelectHandler = (ev: InputEvent | PointerEvent) => {
|
||||
const target = ev.target as HTMLElement;
|
||||
@@ -388,7 +409,7 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
};
|
||||
|
||||
const renderCheckbox = () =>
|
||||
html`<td class="pf-c-table__check" role="cell">
|
||||
html`<td aria-label="${msg("Select row")}" class="pf-c-table__check" role="button">
|
||||
<label class="ignore-click"
|
||||
><input
|
||||
type="checkbox"
|
||||
@@ -428,9 +449,9 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
</td>`;
|
||||
};
|
||||
|
||||
return html`<tbody role="rowgroup" class="${classMap(expandedClass)}">
|
||||
return html`<tbody class="${classMap(expandedClass)}">
|
||||
<tr
|
||||
role="row"
|
||||
aria-label="${this.rowLabel(item)}"
|
||||
class="${this.checkbox || this.clickable ? "pf-m-hoverable" : ""}"
|
||||
@click=${this.clickable
|
||||
? () => {
|
||||
@@ -441,7 +462,13 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
${this.checkbox ? renderCheckbox() : nothing}
|
||||
${this.expandable ? renderExpansion() : nothing}
|
||||
${this.row(item).map((column, columnIndex) => {
|
||||
return html`<td data-column-index="${columnIndex}" role="cell">
|
||||
const columnLabel = columns[columnIndex]?.title;
|
||||
|
||||
return html`<td
|
||||
aria-label=${ifDefined(columnLabel)}
|
||||
data-column-index="${columnIndex}"
|
||||
role="cell"
|
||||
>
|
||||
${column}
|
||||
</td>`;
|
||||
})}
|
||||
@@ -454,7 +481,11 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
});
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
//#endregion
|
||||
|
||||
//#region Toolbar
|
||||
|
||||
protected renderToolbar(): TemplateResult {
|
||||
return html` ${this.renderObjectCreate()}
|
||||
<ak-spinner-button
|
||||
.callAction=${() => {
|
||||
@@ -474,57 +505,64 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
return nothing;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("page")) {
|
||||
updateURLParams({
|
||||
[this.#pageParam]: this.page,
|
||||
});
|
||||
}
|
||||
if (changedProperties.has("search")) {
|
||||
updateURLParams({
|
||||
[this.#searchParam]: this.search,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderSearch(): TemplateResult {
|
||||
const runSearch = (value: string) => {
|
||||
this.search = value;
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
};
|
||||
const isQL = this.supportsQL && this.hasEnterpriseLicense;
|
||||
return !this.searchEnabled()
|
||||
? html``
|
||||
: html`<div class="pf-c-toolbar__group pf-m-search-filter ${isQL ? "ql" : ""}">
|
||||
<ak-table-search
|
||||
?supportsQL=${this.supportsQL}
|
||||
class="pf-c-toolbar__item pf-m-search-filter ${isQL ? "ql" : ""}"
|
||||
value=${ifDefined(this.search)}
|
||||
.onSearch=${runSearch}
|
||||
.apiResponse=${this.data}
|
||||
>
|
||||
</ak-table-search>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
protected renderToolbarContainer(): SlottedTemplateResult {
|
||||
return html`<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
return html`<header class="pf-c-toolbar" role="toolbar" aria-label="${this.toolbarLabel}">
|
||||
<div role="presentation" class="pf-c-toolbar__content">
|
||||
${this.renderSearch()}
|
||||
<div class="pf-c-toolbar__bulk-select">${this.renderToolbar()}</div>
|
||||
<div class="pf-c-toolbar__group">${this.renderToolbarAfter()}</div>
|
||||
<div class="pf-c-toolbar__group">${this.renderToolbarSelected()}</div>
|
||||
${this.paginated ? this.renderTablePagination() : html``}
|
||||
<div role="presentation" class="pf-c-toolbar__bulk-select">
|
||||
${this.renderToolbar()}
|
||||
</div>
|
||||
<div role="presentation" class="pf-c-toolbar__group">
|
||||
${this.renderToolbarAfter()}
|
||||
</div>
|
||||
<div role="presentation" class="pf-c-toolbar__group">
|
||||
${this.renderToolbarSelected()}
|
||||
</div>
|
||||
${this.paginated ? this.renderTablePagination() : nothing}
|
||||
</div>
|
||||
</header>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Search
|
||||
|
||||
#searchListener = (value: string) => {
|
||||
this.search = value;
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
};
|
||||
|
||||
protected searchEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected renderSearch(): SlottedTemplateResult {
|
||||
if (!this.searchEnabled()) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const isQL = this.supportsQL && this.hasEnterpriseLicense;
|
||||
|
||||
return html`<div class="pf-c-toolbar__group pf-m-search-filter ${isQL ? "ql" : ""}">
|
||||
<ak-table-search
|
||||
class="pf-c-toolbar__item pf-m-search-filter ${isQL ? "ql" : ""}"
|
||||
value=${ifDefined(this.search)}
|
||||
label=${ifDefined(this.searchLabel)}
|
||||
placeholder=${ifDefined(this.searchPlaceholder)}
|
||||
.onSearch=${this.#searchListener}
|
||||
>
|
||||
</ak-table-search>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.fetch();
|
||||
}
|
||||
//#endregion
|
||||
|
||||
/* The checkbox on the table header row that allows the user to "activate all on this page,"
|
||||
//#region Chips
|
||||
|
||||
/**
|
||||
* The checkbox on the table header row that allows the user to
|
||||
* "activate all on this page,"
|
||||
* "deactivate all on this page" with a single click.
|
||||
*/
|
||||
renderAllOnThisPageCheckbox(): TemplateResult {
|
||||
@@ -549,11 +587,13 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
</td>`;
|
||||
}
|
||||
|
||||
/* For very large tables where the user is selecting a limited number of entries, we provide a
|
||||
* chip-based subtable at the top that shows the list of selected entries. Long text result in
|
||||
* ellipsized chips, which is sub-optimal.
|
||||
/**
|
||||
* For very large tables where the user is selecting a limited number of entries,
|
||||
* we provide a chip-based subtable at the top that shows the list of selected entries.
|
||||
*
|
||||
* Long text result in ellipsized chips, which is sub-optimal.
|
||||
*/
|
||||
renderSelectedChip(_item: T): SlottedTemplateResult {
|
||||
protected renderSelectedChip(_item: T): SlottedTemplateResult {
|
||||
// Override this for chip-based displays
|
||||
return nothing;
|
||||
}
|
||||
@@ -570,7 +610,9 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
</ak-chip-group>`;
|
||||
}
|
||||
|
||||
/* A simple pagination display, shown at both the top and bottom of the page. */
|
||||
/**
|
||||
* A simple pagination display, shown at both the top and bottom of the page.
|
||||
*/
|
||||
protected renderTablePagination(): SlottedTemplateResult {
|
||||
const handler = (page: number) => {
|
||||
this.page = page;
|
||||
@@ -591,19 +633,22 @@ export abstract class Table<T> extends WithLicenseSummary(AKElement) implements
|
||||
const renderBottomPagination = () =>
|
||||
html`<div class="pf-c-pagination pf-m-bottom">${this.renderTablePagination()}</div>`;
|
||||
|
||||
return html`${this.needChipGroup ? this.renderChipGroup() : html``}
|
||||
return html`${this.needChipGroup ? this.renderChipGroup() : nothing}
|
||||
${this.renderToolbarContainer()}
|
||||
<table class="pf-c-table pf-m-compact pf-m-grid-md pf-m-expandable">
|
||||
<thead>
|
||||
<tr role="row" class="pf-c-table__header-row">
|
||||
${this.checkbox ? this.renderAllOnThisPageCheckbox() : html``}
|
||||
${this.expandable ? html`<td role="cell"></td>` : html``}
|
||||
<table
|
||||
aria-label=${this.label ? msg(str`Table of ${this.label}`) : msg("Table content")}
|
||||
class="pf-c-table pf-m-compact pf-m-grid-md pf-m-expandable"
|
||||
>
|
||||
<thead aria-label=${msg("Table actions")}>
|
||||
<tr role="presentation" class="pf-c-table__header-row">
|
||||
${this.checkbox ? this.renderAllOnThisPageCheckbox() : nothing}
|
||||
${this.expandable ? html`<td role="cell"></td>` : nothing}
|
||||
${this.columns().map((col) => col.render(this))}
|
||||
</tr>
|
||||
</thead>
|
||||
${this.renderRows()}
|
||||
</table>
|
||||
${this.paginated ? renderBottomPagination() : html``}`;
|
||||
${this.paginated ? renderBottomPagination() : nothing}`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
||||
81
web/src/elements/table/TableColumn.ts
Normal file
81
web/src/elements/table/TableColumn.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { TableLike } from "#elements/table/shared";
|
||||
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
|
||||
type ARIASort = "ascending" | "descending" | "none" | "other";
|
||||
|
||||
export class TableColumn {
|
||||
title: string;
|
||||
orderBy?: string;
|
||||
|
||||
onClick?: () => void;
|
||||
|
||||
constructor(title: string, orderBy?: string) {
|
||||
this.title = title;
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
//#region Sorting
|
||||
|
||||
#sortButtonListener(table: TableLike): void {
|
||||
if (!this.orderBy) {
|
||||
return;
|
||||
}
|
||||
|
||||
table.order = table.order === this.orderBy ? `-${this.orderBy}` : this.orderBy;
|
||||
table.fetch();
|
||||
}
|
||||
|
||||
private getSortIndicator(table: TableLike): string {
|
||||
switch (this.getARIASort(table)) {
|
||||
case "ascending":
|
||||
return "fa-long-arrow-alt-up";
|
||||
case "descending":
|
||||
return "fa-long-arrow-alt-down";
|
||||
default:
|
||||
return "fa-arrows-alt-v";
|
||||
}
|
||||
}
|
||||
|
||||
public getARIASort(table: TableLike): ARIASort {
|
||||
switch (table.order) {
|
||||
case this.orderBy:
|
||||
return "ascending";
|
||||
case `-${this.orderBy}`:
|
||||
return "descending";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
protected renderSortable(table: TableLike): TemplateResult {
|
||||
return html` <button
|
||||
class="pf-c-table__button"
|
||||
@click=${() => this.#sortButtonListener(table)}
|
||||
>
|
||||
<div class="pf-c-table__button-content">
|
||||
<span class="pf-c-table__text">${this.title}</span>
|
||||
<span class="pf-c-table__sort-indicator">
|
||||
<i aria-hidden="true" class="fas ${this.getSortIndicator(table)}"></i>
|
||||
</span>
|
||||
</div>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
public render(table: TableLike): TemplateResult {
|
||||
const classes = {
|
||||
"pf-c-table__sort": !!this.orderBy,
|
||||
"pf-m-selected": table.order === this.orderBy || table.order === `-${this.orderBy}`,
|
||||
};
|
||||
|
||||
return html`<th
|
||||
role="columnheader"
|
||||
scope="col"
|
||||
aria-sort=${this.getARIASort(table)}
|
||||
class="${classMap(classes)}"
|
||||
>
|
||||
${this.orderBy ? this.renderSortable(table) : html`${this.title}`}
|
||||
</th>`;
|
||||
}
|
||||
}
|
||||
@@ -49,10 +49,11 @@ export abstract class TableModal<T extends object> extends Table<T> {
|
||||
MODAL_BUTTON_STYLES,
|
||||
];
|
||||
|
||||
public async fetch(): Promise<void> {
|
||||
public override async fetch(): Promise<void> {
|
||||
if (!this.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
return super.fetch();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,25 @@ import { AKElement } from "#elements/Base";
|
||||
import { Pagination } from "@goauthentik/api";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { css, CSSResult, html, TemplateResult } from "lit";
|
||||
import { css, CSSResult, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
export type TablePageChangeListener = (page: number) => void;
|
||||
|
||||
@customElement("ak-table-pagination")
|
||||
export class TablePagination extends AKElement {
|
||||
@property({ type: String })
|
||||
label?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
pages?: Pagination;
|
||||
|
||||
@property({ attribute: false })
|
||||
pageChangeHandler: (page: number) => void = () => {
|
||||
return;
|
||||
};
|
||||
onPageChange?: TablePageChangeListener;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
@@ -35,28 +38,38 @@ export class TablePagination extends AKElement {
|
||||
`,
|
||||
];
|
||||
|
||||
render(): TemplateResult {
|
||||
#navigatePrevious = () => {
|
||||
this.onPageChange?.(this.pages?.previous || 0);
|
||||
};
|
||||
|
||||
#navigateNext = () => {
|
||||
this.onPageChange?.(this.pages?.next || 0);
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.pages) {
|
||||
return html``;
|
||||
return nothing;
|
||||
}
|
||||
return html` <div class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md">
|
||||
|
||||
return html` <nav
|
||||
aria-label=${this.label || msg("Table pagination")}
|
||||
class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md"
|
||||
>
|
||||
<div class="pf-c-pagination pf-m-compact pf-m-compact pf-m-hidden pf-m-visible-on-md">
|
||||
<div class="pf-c-options-menu">
|
||||
<div class="pf-c-options-menu__toggle pf-m-text pf-m-plain">
|
||||
<span class="pf-c-options-menu__toggle-text">
|
||||
<span role="heading" aria-level="4" class="pf-c-options-menu__toggle-text">
|
||||
${msg(
|
||||
str`${this.pages?.startIndex} - ${this.pages?.endIndex} of ${this.pages?.count}`,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="pf-c-pagination__nav" aria-label=${msg("Pagination")}>
|
||||
<div class="pf-c-pagination__nav">
|
||||
<div class="pf-c-pagination__nav-control pf-m-prev">
|
||||
<button
|
||||
class="pf-c-button pf-m-plain"
|
||||
@click=${() => {
|
||||
this.pageChangeHandler(this.pages?.previous || 0);
|
||||
}}
|
||||
@click=${this.#navigatePrevious}
|
||||
?disabled="${(this.pages?.previous || 0) < 1}"
|
||||
aria-label="${msg("Go to previous page")}"
|
||||
>
|
||||
@@ -66,18 +79,16 @@ export class TablePagination extends AKElement {
|
||||
<div class="pf-c-pagination__nav-control pf-m-next">
|
||||
<button
|
||||
class="pf-c-button pf-m-plain"
|
||||
@click=${() => {
|
||||
this.pageChangeHandler(this.pages?.next || 0);
|
||||
}}
|
||||
@click=${this.#navigateNext}
|
||||
?disabled="${(this.pages?.next || 0) <= 0}"
|
||||
aria-label="${msg("Go to next page")}"
|
||||
>
|
||||
<i class="fas fa-angle-right" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
</nav>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,16 +18,16 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-table-search")
|
||||
export class TableSearch extends WithLicenseSummary(AKElement) {
|
||||
@property()
|
||||
value?: string;
|
||||
public value?: string;
|
||||
|
||||
@property({ type: Boolean })
|
||||
supportsQL: boolean = false;
|
||||
public supportsQL: boolean = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
apiResponse?: PaginatedResponse<unknown>;
|
||||
public apiResponse?: PaginatedResponse<unknown>;
|
||||
|
||||
@property()
|
||||
onSearch?: (value: string) => void;
|
||||
public onSearch?: (value: string) => void;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
@@ -45,6 +45,29 @@ export class TableSearch extends WithLicenseSummary(AKElement) {
|
||||
`,
|
||||
];
|
||||
|
||||
public reset = () => {
|
||||
if (!this.onSearch) return;
|
||||
this.value = "";
|
||||
this.onSearch("");
|
||||
};
|
||||
|
||||
#submitListener = (event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!this.onSearch) return;
|
||||
|
||||
const form = event.target as HTMLFormElement;
|
||||
const data = new FormData(form);
|
||||
|
||||
const value = data.get("search")?.toString().trim();
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.onSearch(value);
|
||||
};
|
||||
|
||||
renderInput(): TemplateResult {
|
||||
if (this.supportsQL && this.hasEnterpriseLicense) {
|
||||
return html`<ak-search-ql
|
||||
@@ -57,51 +80,28 @@ export class TableSearch extends WithLicenseSummary(AKElement) {
|
||||
name="search"
|
||||
></ak-search-ql>`;
|
||||
}
|
||||
|
||||
return html`<input
|
||||
class="pf-c-form-control"
|
||||
name="search"
|
||||
type="search"
|
||||
placeholder=${msg("Search...")}
|
||||
value="${ifDefined(this.value)}"
|
||||
@search=${(ev: Event) => {
|
||||
if (!this.onSearch) return;
|
||||
this.onSearch((ev.target as HTMLInputElement).value);
|
||||
}}
|
||||
/>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<form
|
||||
class="pf-c-input-group"
|
||||
method="get"
|
||||
@submit=${(event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!this.onSearch) return;
|
||||
|
||||
const el = this.shadowRoot?.querySelector<HTMLInputElement | HTMLTextAreaElement>(
|
||||
"[name=search]",
|
||||
);
|
||||
|
||||
if (!el) return;
|
||||
if (el.value === "") return;
|
||||
|
||||
this.onSearch(el?.value);
|
||||
}}
|
||||
>
|
||||
return html`<form class="pf-c-input-group" method="get" @submit=${this.#submitListener}>
|
||||
${this.renderInput()}
|
||||
<button
|
||||
aria-label=${msg("Clear search")}
|
||||
class="pf-c-button pf-m-control"
|
||||
type="reset"
|
||||
@click=${() => {
|
||||
if (!this.onSearch) return;
|
||||
this.value = "";
|
||||
this.onSearch("");
|
||||
}}
|
||||
@click=${this.reset}
|
||||
>
|
||||
<i class="fas fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button class="pf-c-button pf-m-control" type="submit">
|
||||
<button aria-label=${msg("Search")} type="submit" class="pf-c-button pf-m-control">
|
||||
<i class="fas fa-search" aria-hidden="true"></i>
|
||||
</button>
|
||||
</form>`;
|
||||
|
||||
13
web/src/elements/table/shared.ts
Normal file
13
web/src/elements/table/shared.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Pagination } from "@goauthentik/api";
|
||||
|
||||
export interface TableLike {
|
||||
order?: string;
|
||||
fetch: () => void;
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
pagination: Pagination;
|
||||
autocomplete?: { [key: string]: string };
|
||||
|
||||
results: Array<T>;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import { WithBrandConfig } from "#elements/mixins/branding";
|
||||
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
|
||||
import { themeImage } from "#elements/utils/images";
|
||||
|
||||
import { BaseStage, StageHost, SubmitOptions } from "#flow/stages/base";
|
||||
import { StageHost, SubmitOptions } from "#flow/stages/base";
|
||||
|
||||
import {
|
||||
CapabilitiesEnum,
|
||||
@@ -34,15 +34,7 @@ import {
|
||||
} from "@goauthentik/api";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
MaybeCompiledTemplateResult,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { until } from "lit/directives/until.js";
|
||||
@@ -381,104 +373,167 @@ export class FlowExecutor
|
||||
}
|
||||
}
|
||||
|
||||
//#region Render Challenge
|
||||
|
||||
async #registerChallengeComponent(component: ChallengeTypes["component"]) {
|
||||
switch (component) {
|
||||
//#region Stages
|
||||
|
||||
case "ak-stage-access-denied":
|
||||
return import("#flow/stages/access_denied/AccessDeniedStage");
|
||||
case "ak-stage-identification":
|
||||
return import("#flow/stages/identification/IdentificationStage");
|
||||
case "ak-stage-password":
|
||||
return import("#flow/stages/password/PasswordStage");
|
||||
case "ak-stage-captcha":
|
||||
return import("#flow/stages/captcha/CaptchaStage");
|
||||
case "ak-stage-consent":
|
||||
return import("#flow/stages/consent/ConsentStage");
|
||||
case "ak-stage-dummy":
|
||||
return import("#flow/stages/dummy/DummyStage");
|
||||
case "ak-stage-email":
|
||||
return import("#flow/stages/email/EmailStage");
|
||||
case "ak-stage-autosubmit":
|
||||
return import("#flow/stages/autosubmit/AutosubmitStage");
|
||||
case "ak-stage-prompt":
|
||||
return import("#flow/stages/prompt/PromptStage");
|
||||
case "ak-stage-authenticator-totp":
|
||||
return import("#flow/stages/authenticator_totp/AuthenticatorTOTPStage");
|
||||
case "ak-stage-authenticator-duo":
|
||||
return import("#flow/stages/authenticator_duo/AuthenticatorDuoStage");
|
||||
case "ak-stage-authenticator-static":
|
||||
return import("#flow/stages/authenticator_static/AuthenticatorStaticStage");
|
||||
case "ak-stage-authenticator-email":
|
||||
return import("#flow/stages/authenticator_email/AuthenticatorEmailStage");
|
||||
case "ak-stage-authenticator-sms":
|
||||
return import("#flow/stages/authenticator_sms/AuthenticatorSMSStage");
|
||||
case "ak-stage-authenticator-validate":
|
||||
return import("#flow/stages/authenticator_validate/AuthenticatorValidateStage");
|
||||
case "ak-stage-user-login":
|
||||
return import("#flow/stages/user_login/UserLoginStage");
|
||||
case "ak-stage-session-end":
|
||||
return import("#flow/providers/SessionEnd");
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Providers
|
||||
|
||||
case "ak-provider-oauth2-device-code":
|
||||
return import("#flow/providers/oauth2/DeviceCode");
|
||||
case "ak-provider-oauth2-device-code-finish":
|
||||
return import("#flow/providers/oauth2/DeviceCodeFinish");
|
||||
|
||||
//#endregion
|
||||
}
|
||||
}
|
||||
|
||||
async renderChallenge(): Promise<MaybeCompiledTemplateResult | HTMLElement> {
|
||||
const { challenge } = this;
|
||||
|
||||
if (!challenge) {
|
||||
async renderChallenge(): Promise<TemplateResult> {
|
||||
if (!this.challenge) {
|
||||
return html`<ak-flow-card loading></ak-flow-card>`;
|
||||
}
|
||||
|
||||
const { component } = challenge;
|
||||
|
||||
await this.#registerChallengeComponent(component);
|
||||
|
||||
switch (component) {
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-access-denied":
|
||||
await import("#flow/stages/access_denied/AccessDeniedStage");
|
||||
return html`<ak-stage-access-denied
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-access-denied>`;
|
||||
case "ak-stage-identification":
|
||||
await import("#flow/stages/identification/IdentificationStage");
|
||||
return html`<ak-stage-identification
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-identification>`;
|
||||
case "ak-stage-password":
|
||||
await import("#flow/stages/password/PasswordStage");
|
||||
return html`<ak-stage-password
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-password>`;
|
||||
case "ak-stage-captcha":
|
||||
await import("#flow/stages/captcha/CaptchaStage");
|
||||
return html`<ak-stage-captcha
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-captcha>`;
|
||||
case "ak-stage-consent":
|
||||
await import("#flow/stages/consent/ConsentStage");
|
||||
return html`<ak-stage-consent
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-consent>`;
|
||||
case "ak-stage-dummy":
|
||||
await import("#flow/stages/dummy/DummyStage");
|
||||
return html`<ak-stage-dummy
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-dummy>`;
|
||||
case "ak-stage-email":
|
||||
await import("#flow/stages/email/EmailStage");
|
||||
return html`<ak-stage-email
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-email>`;
|
||||
case "ak-stage-autosubmit":
|
||||
await import("#flow/stages/autosubmit/AutosubmitStage");
|
||||
return html`<ak-stage-autosubmit
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-autosubmit>`;
|
||||
case "ak-stage-prompt":
|
||||
await import("#flow/stages/prompt/PromptStage");
|
||||
return html`<ak-stage-prompt
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-prompt>`;
|
||||
case "ak-stage-authenticator-totp":
|
||||
await import("#flow/stages/authenticator_totp/AuthenticatorTOTPStage");
|
||||
return html`<ak-stage-authenticator-totp
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-totp>`;
|
||||
case "ak-stage-authenticator-duo":
|
||||
await import("#flow/stages/authenticator_duo/AuthenticatorDuoStage");
|
||||
return html`<ak-stage-authenticator-duo
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-duo>`;
|
||||
case "ak-stage-authenticator-static":
|
||||
await import("#flow/stages/authenticator_static/AuthenticatorStaticStage");
|
||||
return html`<ak-stage-authenticator-static
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-static>`;
|
||||
case "ak-stage-authenticator-webauthn":
|
||||
return html`<ak-stage-authenticator-webauthn
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-webauthn>`;
|
||||
case "ak-stage-authenticator-email":
|
||||
await import("#flow/stages/authenticator_email/AuthenticatorEmailStage");
|
||||
return html`<ak-stage-authenticator-email
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-email>`;
|
||||
case "ak-stage-authenticator-sms":
|
||||
await import("#flow/stages/authenticator_sms/AuthenticatorSMSStage");
|
||||
return html`<ak-stage-authenticator-sms
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-sms>`;
|
||||
case "ak-stage-authenticator-validate":
|
||||
await import("#flow/stages/authenticator_validate/AuthenticatorValidateStage");
|
||||
return html`<ak-stage-authenticator-validate
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-authenticator-validate>`;
|
||||
case "ak-stage-user-login":
|
||||
await import("#flow/stages/user_login/UserLoginStage");
|
||||
return html`<ak-stage-user-login
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-user-login>`;
|
||||
// Sources
|
||||
case "ak-source-plex":
|
||||
return html`<ak-flow-source-plex
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-source-plex>`;
|
||||
case "ak-source-oauth-apple":
|
||||
return html`<ak-flow-source-oauth-apple
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-source-oauth-apple>`;
|
||||
// Providers
|
||||
case "ak-provider-oauth2-device-code":
|
||||
await import("#flow/providers/oauth2/DeviceCode");
|
||||
return html`<ak-flow-provider-oauth2-code
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-provider-oauth2-code>`;
|
||||
case "ak-provider-oauth2-device-code-finish":
|
||||
await import("#flow/providers/oauth2/DeviceCodeFinish");
|
||||
return html`<ak-flow-provider-oauth2-code-finish
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-flow-provider-oauth2-code-finish>`;
|
||||
case "ak-stage-session-end":
|
||||
await import("#flow/providers/SessionEnd");
|
||||
return html`<ak-stage-session-end
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-session-end>`;
|
||||
// Internal stages
|
||||
case "ak-stage-flow-error":
|
||||
return html`<ak-stage-flow-error
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></ak-stage-flow-error>`;
|
||||
case "xak-flow-redirect":
|
||||
return html`<ak-stage-redirect
|
||||
.host=${this}
|
||||
.challenge=${challenge}
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
?promptUser=${this.inspectorOpen}
|
||||
>
|
||||
</ak-stage-redirect>`;
|
||||
case "xak-flow-shell":
|
||||
return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;
|
||||
case "xak-flow-frame":
|
||||
return html`<xak-flow-frame
|
||||
.host=${this}
|
||||
.challenge=${challenge}
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></xak-flow-frame>`;
|
||||
case "xak-flow-shell":
|
||||
return html`${unsafeHTML((challenge as ShellChallenge).body)}`;
|
||||
default:
|
||||
return html`Invalid native challenge element`;
|
||||
}
|
||||
|
||||
const ElementConstructor = customElements.get(component);
|
||||
|
||||
if (!ElementConstructor) {
|
||||
return html`Invalid native challenge element "${component}"`;
|
||||
}
|
||||
|
||||
const element = document.createElement(component) as BaseStage<ChallengeTypes, unknown>;
|
||||
|
||||
element.host = this;
|
||||
element.challenge = this.challenge!;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
async renderInspector() {
|
||||
if (!this.inspectorOpen) {
|
||||
return nothing;
|
||||
@@ -525,7 +580,7 @@ export class FlowExecutor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${(this.inspectorAvailable ?? !this.inspectorOpen)
|
||||
${this.inspectorAvailable || !this.inspectorOpen
|
||||
? html`<button
|
||||
class="inspector-toggle pf-c-button pf-m-primary"
|
||||
@click=${() => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/// <reference types="vitest/config" />
|
||||
|
||||
import { createBundleDefinitions } from "#bundler/utils/node";
|
||||
import { inlineCSSPlugin } from "#bundler/vite-plugin-lit-css/node";
|
||||
|
||||
@@ -9,4 +11,41 @@ export default defineConfig({
|
||||
// ---
|
||||
inlineCSSPlugin(),
|
||||
],
|
||||
test: {
|
||||
dir: "./test",
|
||||
exclude: [
|
||||
"**/node_modules/**",
|
||||
"**/dist/**",
|
||||
"**/out/**",
|
||||
"**/.{idea,git,cache,output,temp}/**",
|
||||
"**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*",
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
test: {
|
||||
include: ["./unit/**/*.{test,spec}.ts", "**/*.unit.{test,spec}.ts"],
|
||||
name: "unit",
|
||||
environment: "node",
|
||||
},
|
||||
},
|
||||
{
|
||||
test: {
|
||||
setupFiles: ["./test/lit/setup.js"],
|
||||
|
||||
include: ["./browser/**/*.{test,spec}.ts", "**/*.browser.{test,spec}.ts"],
|
||||
name: "browser",
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: "playwright",
|
||||
|
||||
instances: [
|
||||
{
|
||||
browser: "chromium",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6620,7 +6620,7 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9667,10 +9667,6 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
<source>Number of previous passwords to check</source>
|
||||
<target>Anzahl der vorherigen Passwörter, die geprüft werden sollen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
<target>Seitenleiste umschalten</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
<target>Wähle einen Provider</target>
|
||||
@@ -9976,6 +9972,42 @@ Bindings zu Gruppen/Benutzern werden mit dem Benutzer des Ereignisses abgegliche
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -5299,7 +5299,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>Change password</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -7590,9 +7590,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -7853,6 +7850,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="es" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
@@ -596,9 +596,9 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>El URL "
|
||||
<x id="0" equiv-text="${this.url}"/>" no fue encontrado.</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>El URL "
|
||||
<x id="0" equiv-text="${this.url}"/>" no fue encontrado.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
@@ -1693,7 +1693,7 @@
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>Ingrese una URL completa, una ruta relativa o use 'fa: //fa-test' para usar el ícono Font Awesome «fa-test».</target>
|
||||
|
||||
</trans-unit>
|
||||
@@ -3732,10 +3732,10 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>¿Estás seguro de que deseas actualizar
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>"?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>"?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
@@ -4790,8 +4790,8 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>Un autenticador "roaming", como una YubiKey</target>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>Un autenticador "roaming", como una YubiKey</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sfffba7b23d8fb40c">
|
||||
@@ -5149,8 +5149,8 @@ no se aprueba cuando una o ambas de las opciones seleccionadas son iguales o sup
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>Si se establece en una duración mayor a 0, el usuario tendrá la opción de "mantener la sesión iniciada", lo que extenderá su sesión por el tiempo especificado aquí.</target>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>Si se establece en una duración mayor a 0, el usuario tendrá la opción de "mantener la sesión iniciada", lo que extenderá su sesión por el tiempo especificado aquí.</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s542a71bb8f41e057">
|
||||
@@ -6625,7 +6625,7 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -7398,7 +7398,7 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
|
||||
<target>Usuario creado correctamente y agregado al grupo <x id="0" equiv-text="${this.group.name}"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s824e0943a7104668">
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<source>This user will be added to the group "<x id="0" equiv-text="${this.targetGroup.name}"/>".</source>
|
||||
<target>Este usuario se agregará al grupo. &quot;<x id="0" equiv-text="${this.targetGroup.name}"/>&quot;.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s62e7f6ed7d9cb3ca">
|
||||
@@ -8660,7 +8660,7 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
|
||||
<target>Sincronizar Grupo</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${p.name}"/> ("<x id="1" equiv-text="${p.fieldKey}"/>", of type <x id="2" equiv-text="${p.type}"/>)</source>
|
||||
<target><x id="0" equiv-text="${p.name}"/> (&quot;<x id="1" equiv-text="${p.fieldKey}"/>&quot;, of type <x id="2" equiv-text="${p.type}"/>)</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s25bacc19d98b444e">
|
||||
@@ -8908,8 +8908,8 @@ Las vinculaciones a grupos/usuarios se verifican en función del usuario del eve
|
||||
<target>URI de redirección válidas tras un flujo de autorización exitoso. Especifique también aquí los orígenes de los flujos implícitos.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4c49d27de60a532b">
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>Para permitir cualquier URI de redirección, configure el modo en Expresión Regular y el valor en ".*". Tenga en cuenta las posibles implicaciones de seguridad que esto puede tener.</target>
|
||||
<source>To allow any redirect URI, set the mode to Regex and the value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>Para permitir cualquier URI de redirección, configure el modo en Expresión Regular y el valor en ".*". Tenga en cuenta las posibles implicaciones de seguridad que esto puede tener.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa52bf79fe1ccb13e">
|
||||
<source>Federated OIDC Sources</source>
|
||||
@@ -9655,8 +9655,8 @@ Si se deja vacío, AuthnContextClassRef se establecerá según los métodos de a
|
||||
<target>Cómo realizar la autenticación durante un flujo de solicitud de token de código de autorización</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s844baf19a6c4a9b4">
|
||||
<source>Enable "Remember me on this device"</source>
|
||||
<target>Habilita "Recordarme en este dispositivo"</target>
|
||||
<source>Enable "Remember me on this device"</source>
|
||||
<target>Habilita "Recordarme en este dispositivo"</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sfa72bca733f40692">
|
||||
<source>When enabled, the user can save their username in a cookie, allowing them to skip directly to entering their password.</source>
|
||||
@@ -9674,10 +9674,6 @@ Si se deja vacío, AuthnContextClassRef se establecerá según los métodos de a
|
||||
<source>Number of previous passwords to check</source>
|
||||
<target>Número de contraseñas anteriores a verificar</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
<target>Alternar barra lateral</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
<target>Elige un Proveedor</target>
|
||||
@@ -10024,7 +10020,43 @@ El valor de este campo se compara con el atributo de pertenencia del usuario.</t
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
||||
|
||||
@@ -6623,7 +6623,7 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9671,10 +9671,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>Number of previous passwords to check</source>
|
||||
<target>Nombre d'anciens mots de passe à vérifier</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
<target>Afficher/masquer la barre latérale</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
<target>Choisir un fournisseur</target>
|
||||
@@ -9994,6 +9990,42 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6624,7 +6624,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9672,10 +9672,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Number of previous passwords to check</source>
|
||||
<target>Numero di password precedenti da controllare</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
<target>Attiva/disattiva la barra laterale</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
<target>Scegli un Provider</target>
|
||||
@@ -9980,6 +9976,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6553,7 +6553,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9038,9 +9038,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9301,6 +9298,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6561,7 +6561,7 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -8942,9 +8942,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9205,6 +9202,42 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6625,7 +6625,7 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9359,9 +9359,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9622,6 +9619,42 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6584,7 +6584,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target><x id="0" equiv-text="${prompt.label}"/></target>
|
||||
|
||||
</trans-unit>
|
||||
@@ -9367,9 +9367,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9631,4 +9628,40 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
||||
@@ -6624,7 +6624,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9450,9 +9450,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9713,6 +9710,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6585,7 +6585,7 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9423,9 +9423,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9686,6 +9683,42 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -4617,7 +4617,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Change password</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s33f85f24c0f5f008">
|
||||
<source>Save</source>
|
||||
@@ -6214,9 +6214,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -6478,6 +6475,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
||||
@@ -6624,7 +6624,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9672,10 +9672,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>Number of previous passwords to check</source>
|
||||
<target>检查历史密码数量</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
<target>切换侧边栏</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
<target>选择提供程序</target>
|
||||
@@ -9981,6 +9977,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -5010,7 +5010,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<target>修改密码</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -7296,9 +7296,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -7559,6 +7556,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -6543,7 +6543,7 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="saf63d34c8601dd41">
|
||||
<source><x id="0" equiv-text="${prompt.label}"/></source>
|
||||
<source><x id="0" equiv-text="${name}"/></source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.label}"/>
|
||||
</target>
|
||||
@@ -9018,9 +9018,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s79b3fcd40dd63921">
|
||||
<source>Number of previous passwords to check</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sdd66c5a2e706fb81">
|
||||
<source>Toggle sidebar</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s7d4ec232535a36f0">
|
||||
<source>Choose a Provider</source>
|
||||
</trans-unit>
|
||||
@@ -9281,6 +9278,42 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5e13dff03b580216">
|
||||
<source>Previous executions logs</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s6abb1cd87fe0114e">
|
||||
<source>Home</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se58e6ed983bf34b0">
|
||||
<source>Collapse navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc6ef25894ed00175">
|
||||
<source>Expand navigation</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s148b5e365440a7c1">
|
||||
<source>Table pagination</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5d929ff1619ac0c9">
|
||||
<source>Search</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd2c2366d13599d8c">
|
||||
<source>Table actions</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s3d195621e562d805">
|
||||
<source>Select row</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s572d21b6a41e24fa">
|
||||
<source>Table of <x id="0" equiv-text="${this.label}"/></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sa25b60b4fac481aa">
|
||||
<source>Table content</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5eba8fa19126f70a">
|
||||
<source>Learn more about the enterprise license.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9db1679f3b234d4e">
|
||||
<source>Search for providers…</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s76790480b7b28ad2">
|
||||
<source>Edit provider</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -45,11 +45,11 @@
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "1.4.10",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.10",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.10",
|
||||
"@swc/core-darwin-arm64": "1.13.2",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.2",
|
||||
"@rspack/binding-darwin-arm64": "1.4.11",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.11",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.11",
|
||||
"@swc/core-darwin-arm64": "1.13.3",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.3",
|
||||
"@swc/core-linux-x64-gnu": "1.13.3",
|
||||
"@swc/html-darwin-arm64": "1.13.3",
|
||||
"@swc/html-linux-arm64-gnu": "1.13.3",
|
||||
|
||||
@@ -56,6 +56,14 @@ export default createDocusaurusConfig(
|
||||
path: "pages",
|
||||
},
|
||||
docs: {
|
||||
exclude: [
|
||||
/**
|
||||
* Exclude previously generated API docs.
|
||||
*
|
||||
* @expires 2025-12-01
|
||||
*/
|
||||
"**/developer-docs/api/reference/**",
|
||||
],
|
||||
routeBasePath: "/docs",
|
||||
path: ".",
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ To support the integration of Kimai with authentik, you need to create an applic
|
||||
- **Choose a Provider type**: select **SAML Provider** as the provider type.
|
||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
|
||||
- Set the **ACS URL** to `https://kimai.company/auth/saml/acs`.
|
||||
- Set the **Audience** to `https://kimai.companyauth/saml`.
|
||||
- Set the **Issuer** to `https://authentik.company`.
|
||||
- Set the **Service Provider Binding** to `Post`.
|
||||
- Set the **Audience** to `https://kimai.company/auth/saml`.
|
||||
- Under **Advanced protocol settings**, select an available signing certificate.
|
||||
- **Configure Bindings** _(optional)_: you can create a [binding](/docs/add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage the listing and access to applications on a user's **My applications** page.
|
||||
|
||||
|
||||
52
website/package-lock.json
generated
52
website/package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"@goauthentik/eslint-config": "^1.0.5",
|
||||
"@goauthentik/prettier-config": "^3.1.0",
|
||||
"@goauthentik/tsconfig": "^1.0.4",
|
||||
"@swc/html-linux-x64-gnu": "1.13.3",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.11",
|
||||
"@types/node": "^24.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
"@typescript-eslint/parser": "^8.38.0",
|
||||
@@ -33,11 +33,11 @@
|
||||
"node": ">=24"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "1.4.10",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.10",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.10",
|
||||
"@swc/core-darwin-arm64": "1.13.2",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.2",
|
||||
"@rspack/binding-darwin-arm64": "1.4.11",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.11",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.11",
|
||||
"@swc/core-darwin-arm64": "1.13.3",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.3",
|
||||
"@swc/core-linux-x64-gnu": "1.13.3",
|
||||
"@swc/html-darwin-arm64": "1.13.3",
|
||||
"@swc/html-linux-arm64-gnu": "1.13.3",
|
||||
@@ -77,11 +77,11 @@
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "1.4.10",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.10",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.10",
|
||||
"@swc/core-darwin-arm64": "1.13.2",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.2",
|
||||
"@rspack/binding-darwin-arm64": "1.4.11",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.11",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.11",
|
||||
"@swc/core-darwin-arm64": "1.13.3",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.3",
|
||||
"@swc/core-linux-x64-gnu": "1.13.3",
|
||||
"@swc/html-darwin-arm64": "1.13.3",
|
||||
"@swc/html-linux-arm64-gnu": "1.13.3",
|
||||
@@ -5776,9 +5776,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/binding-darwin-arm64": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.4.10.tgz",
|
||||
"integrity": "sha512-PraYGuVSzvEwdoYC8T70qI/8j1QeUe2sysiWmjSdxUpxJsDfw35hK9TfxULeAJULlAUAiiXs03hdZk29DBc3ow==",
|
||||
"version": "1.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.4.11.tgz",
|
||||
"integrity": "sha512-PrmBVhR8MC269jo6uQ+BMy1uwIDx0HAJYLQRQur8gXiehWabUBCRg/d4U9KR7rLzdaSScRyc5JWXR52T7/4MfA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5802,9 +5802,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-arm64-gnu": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.4.10.tgz",
|
||||
"integrity": "sha512-cs6yu250FzRU1hl+02VLoJRdzbAveTOqvREeHgqL5AiTc6q1dQo1IZ16/Qt4+g0DMjnvM66pELRIO2nphXL8aA==",
|
||||
"version": "1.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.4.11.tgz",
|
||||
"integrity": "sha512-ms6uwECUIcu+6e82C5HJhRMHnfsI+l33v7XQezntzRPN0+sG3EpikEoT7SGbgt4vDwaWLR7wS20suN4qd5r3GA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -5828,9 +5828,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rspack/binding-linux-x64-gnu": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.4.10.tgz",
|
||||
"integrity": "sha512-FcaBqMclADWiqX+Mez15kggwaVYZkoEqDiQwYRpYDbBMsiJEtfp41GnNRstTWxYxFbcmuWoZl2cYy+LepR21ag==",
|
||||
"version": "1.4.11",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.4.11.tgz",
|
||||
"integrity": "sha512-bHYFLxPPYBOSaHdQbEoCYGMQ1gOrEWj7Mro/DLfSHZi1a0okcQ2Q1y0i1DczReim3ZhLGNrK7k1IpFXCRbAobQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -6327,9 +6327,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-darwin-arm64": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.2.tgz",
|
||||
"integrity": "sha512-44p7ivuLSGFJ15Vly4ivLJjg3ARo4879LtEBAabcHhSZygpmkP8eyjyWxrH3OxkY1eRZSIJe8yRZPFw4kPXFPw==",
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.3.tgz",
|
||||
"integrity": "sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -6375,9 +6375,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.2.tgz",
|
||||
"integrity": "sha512-KJUSl56DBk7AWMAIEcU83zl5mg3vlQYhLELhjwRFkGFMvghQvdqQ3zFOYa4TexKA7noBZa3C8fb24rI5sw9Exg==",
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.3.tgz",
|
||||
"integrity": "sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
"typescript-eslint": "^8.38.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rspack/binding-darwin-arm64": "1.4.10",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.10",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.10",
|
||||
"@swc/core-darwin-arm64": "1.13.2",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.2",
|
||||
"@rspack/binding-darwin-arm64": "1.4.11",
|
||||
"@rspack/binding-linux-arm64-gnu": "1.4.11",
|
||||
"@rspack/binding-linux-x64-gnu": "1.4.11",
|
||||
"@swc/core-darwin-arm64": "1.13.3",
|
||||
"@swc/core-linux-arm64-gnu": "1.13.3",
|
||||
"@swc/core-linux-x64-gnu": "1.13.3",
|
||||
"@swc/html-darwin-arm64": "1.13.3",
|
||||
"@swc/html-linux-arm64-gnu": "1.13.3",
|
||||
|
||||
Reference in New Issue
Block a user