Compare commits

...

18 Commits

Author SHA1 Message Date
Jonas Plum
1b67d5e6cd chore: remove ticket tabs (#1177) 2026-01-11 10:04:01 +01:00
dependabot[bot]
f067a4289a build(deps): bump oven-sh/setup-bun from 1 to 2 (#1170)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 07:25:11 +01:00
dependabot[bot]
fc6e8c0174 build(deps): bump actions/setup-go from 5 to 6 (#1171)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 07:20:01 +01:00
dependabot[bot]
87ac03d3c4 build(deps): bump the gomod-backward-compatible group with 10 updates (#1173)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 07:13:16 +01:00
dependabot[bot]
9e41fa1d90 build(deps): bump codecov/codecov-action from 4 to 5 (#1174)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 07:12:26 +01:00
dependabot[bot]
c97945fa47 build(deps): bump amannn/action-semantic-pull-request from 5 to 6 (#1169)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 07:12:16 +01:00
dependabot[bot]
c94a0f4940 build(deps): bump actions/checkout from 4 to 6 (#1172)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-23 07:11:55 +01:00
Jonas Plum
f0a53d56a2 fix: setting details (#1175) 2025-12-23 07:10:40 +01:00
Jonas Plum
5f37e8a7d3 chore: configure Dependabot for gomod and GitHub Actions (#1168) 2025-12-23 05:57:06 +00:00
Jonas Plum
e148ae0e0c fix: add configVersion to bun.lock (#1166) 2025-11-23 20:29:15 +01:00
dependabot[bot]
2b6320c2d4 build(deps): bump golang.org/x/crypto from 0.41.0 to 0.45.0 (#1165)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-23 20:00:29 +01:00
Jonas Plum
a4a4baf88a fix: downgrade goreleaser-cross docker version (#1164) 2025-10-26 07:50:27 +00:00
Jonas Plum
148f625b00 fix: pin goreleaser-cross docker version (#1163) 2025-10-26 08:32:51 +01:00
Rumburaq2
97ebe9f01a feat: add image preview feature (#1161)
Co-authored-by: Jonas Plum <git@cugu.eu>
2025-10-26 07:38:09 +01:00
dependabot[bot]
770390c4ea build(deps): bump github.com/wneessen/go-mail from 0.6.2 to 0.7.1 (#1159)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-02 19:37:20 +02:00
Jonas Plum
4e03a52b71 fix: text alignment (#1158) 2025-09-21 16:58:46 +00:00
Jonas Plum
e475b38ea4 refactor: sanitize webhook auth payload (#1157) 2025-09-21 17:26:18 +02:00
Jonas Plum
e07afd0f3a chore: remove admin interface link from dashboard cards (#1156) 2025-09-21 17:25:31 +02:00
43 changed files with 595 additions and 363 deletions

22
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
version: 2
updates:
- package-ecosystem: gomod
open-pull-requests-limit: 10
directory: "/"
schedule:
interval: "monthly"
groups:
gomod-backward-compatible:
update-types: [minor, patch]
cooldown:
default-days: 7
- package-ecosystem: github-actions
open-pull-requests-limit: 5
directory: "/"
schedule:
interval: "monthly"
groups:
github-actions-backward-compatible:
update-types: [minor, patch]
cooldown:
default-days: 7

View File

@@ -9,8 +9,8 @@ jobs:
name: Generate Go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- run: make install-golangci-lint generate-go
- run: git diff --exit-code
@@ -19,8 +19,8 @@ jobs:
name: Generate UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: make install-ui generate-ui
- run: git diff --exit-code
@@ -28,8 +28,8 @@ jobs:
name: Fmt Go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- run: make install-golangci-lint fmt-go
- run: git diff --exit-code
@@ -38,16 +38,16 @@ jobs:
name: Fmt UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: make install-ui fmt-ui
lint-go:
name: Lint Go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- run: make install-golangci-lint lint-go
@@ -55,27 +55,27 @@ jobs:
name: Lint UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: make install-ui lint-ui
build-ui:
name: Build UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: make install-ui build-ui
test-go:
name: Test Go
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- run: make test-coverage
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
files: ./coverage.out
token: ${{ secrets.CODECOV_TOKEN }}
@@ -84,20 +84,20 @@ jobs:
name: Test UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v2
- run: make install-ui test-ui
test-playwright:
name: Test Playwright
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v2
- run: make install-ui build-ui install-playwright test-playwright
list-upgrade-test-folders:
@@ -106,7 +106,7 @@ jobs:
outputs:
folders: ${{ steps.set-dirs.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- id: set-dirs
run: |
FOLDERS=$(ls -d ./testing/data/*/ | xargs -n 1 basename | jq -R . | jq -c -s .)
@@ -120,10 +120,10 @@ jobs:
matrix:
folder: ${{ fromJson(needs.list-upgrade-test-folders.outputs.folders) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v2
- run: mkdir -p catalyst_data
- run: cp testing/data/${{ matrix.folder }}/data.db catalyst_data/data.db
- run: make install-ui build-ui install-playwright test-playwright

View File

@@ -14,12 +14,12 @@ jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-go@v5
- uses: actions/setup-go@v6
with: { go-version: '1.25' }
- uses: oven-sh/setup-bun@v1
- uses: oven-sh/setup-bun@v2
- run: make install-ui build-ui
@@ -36,5 +36,5 @@ jobs:
-w /go/src/github.com/SecurityBrewery/catalyst \
-e CGO_ENABLED=1 \
-e GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
ghcr.io/goreleaser/goreleaser-cross:latest \
release --clean
ghcr.io/goreleaser/goreleaser-cross:v1.25.1 \
release --clean

View File

@@ -16,7 +16,7 @@ jobs:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
- uses: amannn/action-semantic-pull-request@v6
with:
scopes: |
deps

View File

@@ -60,7 +60,7 @@ func runHook(ctx context.Context, queries *sqlc.Queries, collection, event strin
Action: event,
Collection: collection,
Record: record,
Auth: auth,
Auth: webhook.SanitizeUser(auth),
Admin: nil,
})
if err != nil {

View File

@@ -1881,7 +1881,7 @@ func marshal(state map[string]any) json.RawMessage {
func marshalPointer(state *map[string]any) json.RawMessage {
if state == nil {
return json.RawMessage("{}")
return nil
}
b, _ := json.Marshal(*state) //nolint:errchkjson

View File

@@ -68,7 +68,7 @@ func Test_marshalPointer(t *testing.T) {
m := map[string]any{"x": 1}
assert.JSONEq(t, `{"x":1}`, string(marshalPointer(&m)))
assert.JSONEq(t, "{}", string(marshalPointer(nil)))
assert.Nil(t, marshalPointer(nil))
}
func Test_generateID(t *testing.T) {
@@ -130,3 +130,30 @@ func TestService_DownloadFile_Errors(t *testing.T) {
_, err = s.DownloadFile(t.Context(), openapi.DownloadFileRequestObject{Id: "f_invalid_base64"})
require.Error(t, err)
}
func TestService_UpdateTicket_PreservesStateOnNil(t *testing.T) {
t.Parallel()
s := newTestService(t)
before, err := s.queries.Ticket(t.Context(), "test-ticket")
require.NoError(t, err)
open := false
resp, err := s.UpdateTicket(t.Context(), openapi.UpdateTicketRequestObject{
Id: "test-ticket",
Body: &openapi.TicketUpdate{
Open: &open,
},
})
require.NoError(t, err)
updated, ok := resp.(openapi.UpdateTicket200JSONResponse)
require.True(t, ok)
assert.Equal(t, unmarshal(before.State), updated.State)
after, err := s.queries.Ticket(t.Context(), "test-ticket")
require.NoError(t, err)
assert.Equal(t, before.State, after.State)
}

View File

@@ -8,6 +8,7 @@ import (
"io"
"log/slog"
"net/http"
"time"
"github.com/SecurityBrewery/catalyst/app/auth/usercontext"
"github.com/SecurityBrewery/catalyst/app/database"
@@ -35,11 +36,43 @@ func BindHooks(hooks *hook.Hooks, queries *sqlc.Queries) {
}
type Payload struct {
Action string `json:"action"`
Collection string `json:"collection"`
Record any `json:"record"`
Auth *sqlc.User `json:"auth,omitempty"`
Admin *sqlc.User `json:"admin,omitempty"`
Action string `json:"action"`
Collection string `json:"collection"`
Record any `json:"record"`
Auth *AuthUser `json:"auth,omitempty"`
Admin *AuthUser `json:"admin,omitempty"`
}
type AuthUser struct {
ID string `json:"id"`
Username string `json:"username"`
Active bool `json:"active"`
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
Avatar *string `json:"avatar,omitempty"`
Lastresetsentat *time.Time `json:"lastresetsentat,omitempty"`
Lastverificationsentat *time.Time `json:"lastverificationsentat,omitempty"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}
func SanitizeUser(user *sqlc.User) *AuthUser {
if user == nil {
return nil
}
return &AuthUser{
ID: user.ID,
Username: user.Username,
Active: user.Active,
Name: user.Name,
Email: user.Email,
Avatar: user.Avatar,
Lastresetsentat: user.Lastresetsentat,
Lastverificationsentat: user.Lastverificationsentat,
Created: user.Created,
Updated: user.Updated,
}
}
func event(ctx context.Context, queries *sqlc.Queries, event, collection string, record any) {
@@ -67,7 +100,7 @@ func event(ctx context.Context, queries *sqlc.Queries, event, collection string,
Action: event,
Collection: collection,
Record: record,
Auth: user,
Auth: SanitizeUser(user),
Admin: nil,
})
if err != nil {

32
go.mod
View File

@@ -8,20 +8,20 @@ tool (
)
require (
github.com/brianvoe/gofakeit/v7 v7.2.1
github.com/go-chi/chi/v5 v5.2.2
github.com/go-co-op/gocron/v2 v2.16.2
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/brianvoe/gofakeit/v7 v7.12.1
github.com/go-chi/chi/v5 v5.2.3
github.com/go-co-op/gocron/v2 v2.19.0
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/martian/v3 v3.3.3
github.com/mattn/go-sqlite3 v1.14.28
github.com/oapi-codegen/runtime v1.1.1
github.com/stretchr/testify v1.10.0
github.com/mattn/go-sqlite3 v1.14.32
github.com/oapi-codegen/runtime v1.1.2
github.com/stretchr/testify v1.11.1
github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
github.com/tus/tusd/v2 v2.8.0
github.com/urfave/cli/v3 v3.3.8
github.com/wneessen/go-mail v0.6.2
golang.org/x/crypto v0.39.0
github.com/urfave/cli/v3 v3.6.1
github.com/wneessen/go-mail v0.7.2
golang.org/x/crypto v0.46.0
)
require (
@@ -82,12 +82,12 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sync v0.15.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect

122
go.sum
View File

@@ -12,8 +12,8 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7D
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/brianvoe/gofakeit/v7 v7.2.1 h1:AGojgaaCdgq4Adzrd2uWdbGNDyX6MWNhHdQBraNfOHI=
github.com/brianvoe/gofakeit/v7 v7.2.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
github.com/brianvoe/gofakeit/v7 v7.12.1 h1:df1tiI4SL1dR5Ix4D/r6a3a+nXBJ/OBGU5jEKRBmmqg=
github.com/brianvoe/gofakeit/v7 v7.12.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cubicdaiya/gonp v1.0.4 h1:ky2uIAJh81WiLcGKBVD5R7KsM/36W6IqqTy6Bo6rGws=
github.com/cubicdaiya/gonp v1.0.4/go.mod h1:iWGuP/7+JVTn02OWhRemVbMmG1DOUnmrGTYYACpOI0I=
@@ -32,10 +32,10 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-co-op/gocron/v2 v2.16.2 h1:r08P663ikXiulLT9XaabkLypL/W9MoCIbqgQoAutyX4=
github.com/go-co-op/gocron/v2 v2.16.2/go.mod h1:4YTLGCCAH75A5RlQ6q+h+VacO7CgjkgP0EJ+BEOXRSI=
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-co-op/gocron/v2 v2.19.0 h1:OKf2y6LXPs/BgBI2fl8PxUpNAI1DA9Mg+hSeGOS38OU=
github.com/go-co-op/gocron/v2 v2.19.0/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -48,8 +48,8 @@ github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/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/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -59,7 +59,6 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
@@ -97,8 +96,8 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
@@ -107,8 +106,8 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI=
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
@@ -170,8 +169,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@@ -190,17 +189,16 @@ github.com/tus/tusd/v2 v2.8.0 h1:X2jGxQ05jAW4inDd2ogmOKqwnb4c/D0lw2yhgHayWyU=
github.com/tus/tusd/v2 v2.8.0/go.mod h1:3/zEOVQQIwmJhvNam8phV4x/UQt68ZmZiTzeuJUNhVo=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E=
github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=
github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk=
github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ=
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfPe7z7go8Dvv1AJQDI3eQ/5xith3q2mFlo=
github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM=
github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb h1:gQ+ZV4wJke/EBKYciZ2MshEouEHFuinB85dY3f5s1q8=
github.com/wasilibs/wazero-helpers v0.0.0-20250123031827-cd30c44769bb/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY=
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8=
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8=
github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
@@ -229,91 +227,35 @@ go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4=
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=

View File

@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "catalyst",
@@ -14,7 +15,7 @@
"@uppy/progress-bar": "^4.2.1",
"@uppy/tus": "^4.2.2",
"@uppy/vue": "^2.2.0",
"@vueuse/core": "^13.4.0",
"@vueuse/core": "^14.1.0",
"caniuse-lite": "^1.0.30001723",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -25,6 +26,7 @@
"lucide-vue-next": "^0.475.0",
"pinia": "^3.0.3",
"radix-vue": "^1.9.17",
"reka-ui": "^2.7.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2",
@@ -588,11 +590,11 @@
"@vue/tsconfig": ["@vue/tsconfig@0.7.0", "", { "peerDependencies": { "typescript": "5.x", "vue": "^3.4.0" }, "optionalPeers": ["typescript", "vue"] }, "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg=="],
"@vueuse/core": ["@vueuse/core@13.4.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "13.4.0", "@vueuse/shared": "13.4.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-OnK7zW3bTq/QclEk17+vDFN3tuAm8ONb9zQUIHrYQkkFesu3WeGUx/3YzpEp+ly53IfDAT9rsYXgGW6piNZC5w=="],
"@vueuse/core": ["@vueuse/core@14.1.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw=="],
"@vueuse/metadata": ["@vueuse/metadata@13.4.0", "", {}, "sha512-CPDQ/IgOeWbqItg1c/pS+Ulum63MNbpJ4eecjFJqgD/JUCJ822zLfpw6M9HzSvL6wbzMieOtIAW/H8deQASKHg=="],
"@vueuse/metadata": ["@vueuse/metadata@14.1.0", "", {}, "sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA=="],
"@vueuse/shared": ["@vueuse/shared@13.4.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-+AxuKbw8R1gYy5T21V5yhadeNM7rJqb4cPaRI9DdGnnNl3uqXh+unvQ3uCaA2DjYLbNr1+l7ht/B4qEsRegX6A=="],
"@vueuse/shared": ["@vueuse/shared@14.1.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw=="],
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
@@ -1230,6 +1232,8 @@
"object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
@@ -1354,6 +1358,8 @@
"reflect-metadata": ["reflect-metadata@0.2.2", "", {}, "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="],
"reka-ui": ["reka-ui@2.7.0", "", { "dependencies": { "@floating-ui/dom": "^1.6.13", "@floating-ui/vue": "^1.1.6", "@internationalized/date": "^3.5.0", "@internationalized/number": "^3.5.0", "@tanstack/vue-virtual": "^3.12.0", "@vueuse/core": "^12.5.0", "@vueuse/shared": "^12.5.0", "aria-hidden": "^1.2.4", "defu": "^6.1.4", "ohash": "^2.0.11" }, "peerDependencies": { "vue": ">= 3.2.0" } }, "sha512-m+XmxQN2xtFzBP3OAdIafKq7C8OETo2fqfxcIIxYmNN2Ch3r5oAf6yEYCIJg5tL/yJU2mHqF70dCCekUkrAnXA=="],
"remove-accents": ["remove-accents@0.5.0", "", {}, "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="],
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
@@ -1694,6 +1700,10 @@
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"reka-ui/@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="],
"reka-ui/@vueuse/shared": ["@vueuse/shared@12.8.2", "", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="],
"rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
@@ -1760,6 +1770,8 @@
"radix-vue/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@10.11.1", "", {}, "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="],
"reka-ui/@vueuse/core/@vueuse/metadata": ["@vueuse/metadata@12.8.2", "", {}, "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="],
"sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"sucrase/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],

View File

@@ -8,9 +8,8 @@
"baseColor": "slate",
"cssVariables": true
},
"framework": "vite",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
}

View File

@@ -29,7 +29,7 @@
"@uppy/progress-bar": "^4.2.1",
"@uppy/tus": "^4.2.2",
"@uppy/vue": "^2.2.0",
"@vueuse/core": "^13.4.0",
"@vueuse/core": "^14.1.0",
"caniuse-lite": "^1.0.30001723",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -40,6 +40,7 @@
"lucide-vue-next": "^0.475.0",
"pinia": "^3.0.3",
"radix-vue": "^1.9.17",
"reka-ui": "^2.7.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"v-calendar": "^3.1.2",

View File

@@ -62,7 +62,7 @@ watch(
<div v-for="(property, key) in schema?.properties" :key="key">
<FormField v-if="property.enum" :name="key" v-slot="{ componentField }" v-model="formdata[key]">
<FormItem>
<FormLabel :for="key" class="text-right">
<FormLabel :for="key" class="text-left">
{{ property.title }}
</FormLabel>
<Select :id="key" class="col-span-3" v-bind="componentField">
@@ -87,7 +87,7 @@ watch(
v-model="formdata[key]"
>
<FormItem>
<FormLabel :for="key" class="text-right">
<FormLabel :for="key" class="text-left">
{{ property.title }}
</FormLabel>
<Input :id="key" class="col-span-3" v-bind="componentField" />
@@ -120,7 +120,7 @@ watch(
v-model="formdata[key]"
>
<FormItem>
<FormLabel :for="key" class="text-right">
<FormLabel :for="key" class="text-left">
{{ property.title }}
</FormLabel>
<Input :id="key" class="col-span-3" type="number" v-bind="componentField" />

View File

@@ -133,7 +133,7 @@ const permissionItems = computed(() => config.value?.permissions || [])
<form @submit="onSubmit" class="flex w-full flex-col items-start gap-4">
<FormField name="name" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="name" class="text-right">Name</FormLabel>
<FormLabel for="name" class="text-left">Name</FormLabel>
<Input
id="name"
class="col-span-3"
@@ -152,7 +152,7 @@ const permissionItems = computed(() => config.value?.permissions || [])
>
<FormItem id="permissions" class="w-full">
<div class="space-y-0.5">
<FormLabel for="permissions" class="text-right">Permissions</FormLabel>
<FormLabel for="permissions" class="text-left">Permissions</FormLabel>
</div>
<FormControl>
<MultiSelect

View File

@@ -82,7 +82,7 @@ watch(
<form @submit="onSubmit" @change="change">
<FormField name="group" v-slot="{ componentField }">
<FormItem>
<FormLabel for="group" class="text-right"> Group</FormLabel>
<FormLabel for="group" class="text-left"> Group</FormLabel>
<Select id="group" v-bind="componentField">
<SelectTrigger>
<SelectValue placeholder="Select a group" />

View File

@@ -13,7 +13,7 @@ import {
<template>
<FormField name="actiondata.requirements" v-slot="{ componentField }">
<FormItem>
<FormLabel for="requirements" class="text-right">requirements.txt</FormLabel>
<FormLabel for="requirements" class="text-left">requirements.txt</FormLabel>
<FormControl>
<GrowTextarea id="requirements" class="col-span-3" v-bind="componentField" />
</FormControl>
@@ -23,7 +23,7 @@ import {
</FormField>
<FormField name="actiondata.script" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="script" class="text-right">Script</FormLabel>
<FormLabel for="script" class="text-left">Script</FormLabel>
<FormControl>
<GrowTextarea id="script" class="col-span-3" v-bind="componentField" />
</FormControl>

View File

@@ -14,7 +14,7 @@ import { Input } from '@/components/ui/input'
<template>
<FormField name="actiondata.headers" v-slot="{ value, handleChange }">
<FormItem>
<FormLabel for="headers" class="text-right">Headers</FormLabel>
<FormLabel for="headers" class="text-left">Headers</FormLabel>
<FormControl>
<GrowListTextarea
id="headers"
@@ -29,7 +29,7 @@ import { Input } from '@/components/ui/input'
</FormField>
<FormField name="actiondata.url" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="url" class="text-right">URL</FormLabel>
<FormLabel for="url" class="text-left">URL</FormLabel>
<FormControl>
<Input id="url" v-bind="componentField" placeholder="https://example.com/webhook" />
</FormControl>

View File

@@ -267,7 +267,7 @@ const curlExample = computed(() => {
<form @submit="onSubmit" class="flex w-full flex-col items-start gap-4">
<FormField name="name" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="name" class="text-right">Name</FormLabel>
<FormLabel for="name" class="text-left">Name</FormLabel>
<Input id="name" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -280,7 +280,7 @@ const curlExample = computed(() => {
<CardContent class="flex flex-col gap-4">
<FormField name="trigger" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="trigger" class="text-right">Type</FormLabel>
<FormLabel for="trigger" class="text-left">Type</FormLabel>
<FormControl>
<Select id="trigger" class="col-span-3" v-bind="componentField">
<SelectTrigger class="font-medium">
@@ -321,7 +321,7 @@ const curlExample = computed(() => {
<CardContent class="flex flex-col gap-4">
<FormField name="action" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="action" class="text-right">Type</FormLabel>
<FormLabel for="action" class="text-left">Type</FormLabel>
<FormControl>
<Select id="action" class="col-span-3" v-bind="componentField">
<SelectTrigger class="font-medium">

View File

@@ -14,7 +14,7 @@ import {
<template>
<FormField name="triggerdata.collections" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="collections" class="text-right">Collections</FormLabel>
<FormLabel for="collections" class="text-left">Collections</FormLabel>
<FormControl>
<TriggerHookFormFieldCollections id="collections" v-bind="componentField" />
</FormControl>
@@ -25,7 +25,7 @@ import {
<FormField name="triggerdata.events" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="events" class="text-right">Events</FormLabel>
<FormLabel for="events" class="text-left">Events</FormLabel>
<FormControl>
<TriggerHookFormFieldEvents id="events" v-bind="componentField" />
</FormControl>

View File

@@ -13,7 +13,7 @@ import { Input } from '@/components/ui/input'
<template>
<FormField name="triggerdata.expression" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="expression" class="text-right"> Cron Expression </FormLabel>
<FormLabel for="expression" class="text-left"> Cron Expression </FormLabel>
<FormControl>
<Input
id="expression"

View File

@@ -13,7 +13,7 @@ import { Input } from '@/components/ui/input'
<template>
<FormField name="triggerdata.token" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="token" class="text-right">Token</FormLabel>
<FormLabel for="token" class="text-left">Token</FormLabel>
<FormControl>
<Input id="token" class="col-span-3" v-bind="componentField" placeholder="Enter a token" />
</FormControl>
@@ -27,7 +27,7 @@ import { Input } from '@/components/ui/input'
<FormField name="triggerdata.path" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="path" class="text-right">Path</FormLabel>
<FormLabel for="path" class="text-left">Path</FormLabel>
<FormControl>
<Input id="path" class="col-span-3" v-bind="componentField" placeholder="Enter a path" />
</FormControl>

View File

@@ -225,7 +225,7 @@ const onSubmit = handleSubmit((vals) => {
<form @submit="onSubmit" class="flex w-full flex-col items-start gap-4">
<FormField name="meta.appName" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="meta.appName" class="text-right">App Name</FormLabel>
<FormLabel for="meta.appName" class="text-left">App Name</FormLabel>
<Input id="meta.appName" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -233,7 +233,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="meta.appUrl" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="meta.appUrl" class="text-right">App URL</FormLabel>
<FormLabel for="meta.appUrl" class="text-left">App URL</FormLabel>
<Input id="meta.appUrl" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -241,7 +241,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="meta.senderName" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="meta.senderName" class="text-right">Sender Name</FormLabel>
<FormLabel for="meta.senderName" class="text-left">Sender Name</FormLabel>
<Input id="meta.senderName" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -249,7 +249,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="meta.senderAddress" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="meta.senderAddress" class="text-right">Sender Address</FormLabel>
<FormLabel for="meta.senderAddress" class="text-left">Sender Address</FormLabel>
<Input id="meta.senderAddress" type="email" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -262,7 +262,7 @@ const onSubmit = handleSubmit((vals) => {
<CardContent class="flex flex-col gap-4">
<FormField name="maxDays" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="maxDays" class="text-right">Max Days</FormLabel>
<FormLabel for="maxDays" class="text-left">Max Days</FormLabel>
<Input id="maxDays" type="number" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -270,7 +270,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="logLevel" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="logLevel" class="text-right">Log Level</FormLabel>
<FormLabel for="logLevel" class="text-left">Log Level</FormLabel>
<FormControl>
<Select id="logLevel" class="col-span-3" v-bind="componentField">
<SelectTrigger class="font-medium">
@@ -328,7 +328,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="smtp.host" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="smtp.host" class="text-right">Host</FormLabel>
<FormLabel for="smtp.host" class="text-left">Host</FormLabel>
<Input id="smtp.host" class="col-span-3" v-bind="componentField" />
<FormMessage />
<FormDescription>SMTP server hostname (e.g., smtp.gmail.com)</FormDescription>
@@ -337,7 +337,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="smtp.port" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="smtp.port" class="text-right">Port</FormLabel>
<FormLabel for="smtp.port" class="text-left">Port</FormLabel>
<Input id="smtp.port" type="number" class="col-span-3" v-bind="componentField" />
<FormMessage />
<FormDescription>Common ports: 25, 465 (SSL), 587 (TLS)</FormDescription>
@@ -346,7 +346,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="smtp.username" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="smtp.username" class="text-right">Username</FormLabel>
<FormLabel for="smtp.username" class="text-left">Username</FormLabel>
<Input id="smtp.username" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -354,7 +354,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="smtp.password" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="smtp.password" class="text-right">Password</FormLabel>
<FormLabel for="smtp.password" class="text-left">Password</FormLabel>
<Input id="smtp.password" type="password" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -362,7 +362,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="smtp.authMethod" v-slot="{ componentField }" validate-on-input>
<FormItem>
<FormLabel for="smtp.authMethod" class="text-right"> Authentication Method </FormLabel>
<FormLabel for="smtp.authMethod" class="text-left"> Authentication Method </FormLabel>
<Select id="smtp.authMethod" class="col-span-3" v-bind="componentField">
<SelectTrigger class="font-medium">
<SelectValue placeholder="Select authentication method" />
@@ -392,7 +392,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="smtp.localName" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="smtp.localName" class="text-right">HELO domain</FormLabel>
<FormLabel for="smtp.localName" class="text-left">HELO domain</FormLabel>
<Input id="smtp.localName" class="col-span-3" v-bind="componentField" />
<FormDescription>Optional. Leave empty to use default hostname.</FormDescription>
<FormMessage />
@@ -408,7 +408,7 @@ const onSubmit = handleSubmit((vals) => {
<CardContent class="flex flex-col gap-4">
<FormField name="cron" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="cron" class="text-right">Cron Expression</FormLabel>
<FormLabel for="cron" class="text-left">Cron Expression</FormLabel>
<Input id="cron" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -416,7 +416,7 @@ const onSubmit = handleSubmit((vals) => {
<FormField name="cronMaxKeep" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="cronMaxKeep" class="text-right">Max Keep</FormLabel>
<FormLabel for="cronMaxKeep" class="text-left">Max Keep</FormLabel>
<Input id="cronMaxKeep" type="number" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>

View File

@@ -8,17 +8,21 @@ import StatusIcon from '@/components/ticket/StatusIcon.vue'
import TicketActionBar from '@/components/ticket/TicketActionBar.vue'
import TicketCloseBar from '@/components/ticket/TicketCloseBar.vue'
import TicketHeader from '@/components/ticket/TicketHeader.vue'
import TicketTab from '@/components/ticket/TicketTab.vue'
import TicketComments from '@/components/ticket/comment/TicketComments.vue'
import TicketFiles from '@/components/ticket/file/TicketFiles.vue'
import TicketLinks from '@/components/ticket/link/TicketLinks.vue'
import TicketTasks from '@/components/ticket/task/TicketTasks.vue'
import TicketTimeline from '@/components/ticket/timeline/TicketTimeline.vue'
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger
} from '@/components/ui/accordion'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { useToast } from '@/components/ui/toast/use-toast'
import { Edit } from 'lucide-vue-next'
@@ -166,51 +170,64 @@ const updateDescription = (value: string | undefined) => (message.value = value
class="min-h-14"
/>
</Card>
<Separator />
<Tabs default-value="timeline" class="flex flex-1 flex-col">
<TabsList>
<TabsTrigger value="timeline">
Timeline
<Badge
v-if="timeline && timeline.length > 0"
variant="outline"
class="ml-2 hidden sm:inline-flex"
>
{{ timeline.length }}
</Badge>
</TabsTrigger>
<TabsTrigger value="tasks">
Tasks
<Badge
v-if="tasks && tasks.length > 0"
variant="outline"
class="ml-2 hidden sm:inline-flex"
>
{{ tasks.length }}
<StatusIcon :status="taskStatus" class="size-6" />
</Badge>
</TabsTrigger>
<TabsTrigger value="comments">
Comments
<Badge
v-if="comments && comments.length > 0"
variant="outline"
class="ml-2 hidden sm:inline-flex"
>
{{ comments.length }}
</Badge>
</TabsTrigger>
</TabsList>
<TicketTab value="timeline">
<TicketTimeline :ticket="ticket" :timeline="timeline" />
</TicketTab>
<TicketTab value="tasks">
<TicketTasks :ticket="ticket" :tasks="tasks" />
</TicketTab>
<TicketTab value="comments">
<TicketComments :ticket="ticket" :comments="comments" />
</TicketTab>
</Tabs>
<Accordion
type="multiple"
:default-value="['tasks', 'comments', 'timeline']"
class="w-full divide-y rounded-md border"
>
<AccordionItem value="tasks" class="border-0">
<AccordionTrigger class="px-3 py-2 hover:no-underline">
<div class="flex items-center gap-2">
<span class="text-sm font-medium">Tasks</span>
<Badge
v-if="tasks && tasks.length > 0"
variant="outline"
class="hidden sm:inline-flex"
>
{{ tasks.length }}
<StatusIcon :status="taskStatus" class="size-6" />
</Badge>
</div>
</AccordionTrigger>
<AccordionContent class="px-3 pt-2">
<TicketTasks :ticket="ticket" :tasks="tasks" />
</AccordionContent>
</AccordionItem>
<AccordionItem value="comments" class="border-0">
<AccordionTrigger class="px-3 py-2 hover:no-underline">
<div class="flex items-center gap-2">
<span class="text-sm font-medium">Comments</span>
<Badge
v-if="comments && comments.length > 0"
variant="outline"
class="hidden sm:inline-flex"
>
{{ comments.length }}
</Badge>
</div>
</AccordionTrigger>
<AccordionContent class="px-3 pt-2">
<TicketComments :ticket="ticket" :comments="comments" />
</AccordionContent>
</AccordionItem>
<AccordionItem value="timeline" class="border-0">
<AccordionTrigger class="px-3 py-2 hover:no-underline">
<div class="flex items-center gap-2">
<span class="text-sm font-medium">Timeline</span>
<Badge
v-if="timeline && timeline.length > 0"
variant="outline"
class="hidden sm:inline-flex"
>
{{ timeline.length }}
</Badge>
</div>
</AccordionTrigger>
<AccordionContent class="px-3 pt-2">
<TicketTimeline :ticket="ticket" :timeline="timeline" />
</AccordionContent>
</AccordionItem>
</Accordion>
<Separator class="xl:hidden" />
</div>
<div class="flex flex-col gap-4 xl:w-96 xl:flex-initial">

View File

@@ -142,7 +142,7 @@ watch(
<form @submit="onSubmit">
<FormField name="name" v-slot="{ componentField }" v-model="name">
<FormItem>
<FormLabel for="name" class="text-right">Name</FormLabel>
<FormLabel for="name" class="text-left">Name</FormLabel>
<Input id="name" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -150,7 +150,7 @@ watch(
<FormField name="description" v-slot="{ componentField }" v-model="description">
<FormItem>
<FormLabel for="description" class="text-right">Description</FormLabel>
<FormLabel for="description" class="text-left">Description</FormLabel>
<Input id="description" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>

View File

@@ -0,0 +1,38 @@
<script setup lang="ts">
import { Button } from '@/components/ui/button'
import { X } from 'lucide-vue-next'
defineProps<{
modelValue: boolean
imageUrl: string
fileName: string
}>()
const emit = defineEmits(['update:modelValue'])
const closeModal = () => {
emit('update:modelValue', false)
}
</script>
<template>
<div
v-if="modelValue"
class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
@click.self="closeModal"
>
<div class="relative rounded-lg bg-white p-2 shadow-xl">
<img :src="imageUrl" :alt="fileName" class="max-h-[80vh] max-w-[80vw]" />
<Button
variant="ghost"
size="icon"
class="absolute -right-1 -top-1 h-8 w-8 rounded-full bg-white"
@click="closeModal"
>
<X class="h-5 w-5" />
<span class="sr-only">Close image modal</span>
</Button>
</div>
</div>
</template>

View File

@@ -5,6 +5,7 @@ import '@uppy/dashboard/dist/style.min.css'
import DeleteDialog from '@/components/common/DeleteDialog.vue'
import TicketPanel from '@/components/ticket/TicketPanel.vue'
import FileAddDialog from '@/components/ticket/file/FileAddDialog.vue'
import ImageModal from '@/components/ticket/file/ImageModal.vue'
import { Button } from '@/components/ui/button'
import { useToast } from '@/components/ui/toast/use-toast'
@@ -30,6 +31,36 @@ const props = defineProps<{
files: Array<ModelFile> | undefined
}>()
const isImageModalOpen = ref(false)
const selectedImageUrl = ref('')
const selectedImageName = ref('')
const isImage = (fileName: string) => {
const imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg']
const extension = fileName.split('.').pop()?.toLowerCase()
return extension ? imageExtensions.includes(extension) : false
}
const openImageModal = async (file: ModelFile) => {
if (!isImage(file.name)) return
try {
const response = await fetch(`/api/files/${file.id}/download`, {
headers: { Authorization: `Bearer ${authStore.token}` }
})
const blob = await response.blob()
selectedImageUrl.value = window.URL.createObjectURL(blob)
selectedImageName.value = file.name
isImageModalOpen.value = true
} catch (err) {
console.error('Error fetching image:', err)
toast({
title: 'Error',
description: 'Could not load image for preview.',
variant: 'destructive'
})
}
}
const downloadFile = (file: any) => {
fetch(`/api/files/${file.id}/download`, {
headers: { Authorization: `Bearer ${authStore.token}` }
@@ -44,6 +75,8 @@ const downloadFile = (file: any) => {
link.download = file.name
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(_url)
})
.catch((err) => {
console.log(err)
@@ -82,11 +115,24 @@ watch(
},
{ immediate: true }
)
watch(isImageModalOpen, (isOpen) => {
if (!isOpen && selectedImageUrl.value) {
window.URL.revokeObjectURL(selectedImageUrl.value)
selectedImageUrl.value = ''
selectedImageName.value = ''
}
})
</script>
<template>
<TicketPanel title="Files" @add="dialogOpen = true" :hideAdd="isDemo">
<FileAddDialog v-if="!isDemo" v-model="dialogOpen" :ticket="ticket" />
<ImageModal
v-model="isImageModalOpen"
:image-url="selectedImageUrl"
:file-name="selectedImageName"
/>
<div
v-if="!files || files.length === 0"
class="flex h-10 items-center p-4 text-muted-foreground"
@@ -99,7 +145,11 @@ watch(
:title="file.name"
class="flex w-full items-center border-t py-1 pl-2 pr-1 first:rounded-t first:border-none last:rounded-b"
>
<div class="flex flex-1 items-center overflow-hidden pr-2">
<div
class="flex flex-1 items-center overflow-hidden pr-2"
:class="isImage(file.name) ? 'cursor-pointer' : ''"
@click="openImageModal(file)"
>
{{ file.name }}
<div class="ml-1 flex-1 text-nowrap text-sm text-muted-foreground">

View File

@@ -86,7 +86,7 @@ const change = () => validate({ mode: 'silent' }).then((res) => (submitDisabled.
<form @submit="onSubmit" @change="change">
<FormField name="name" v-slot="{ componentField }">
<FormItem>
<FormLabel for="name" class="text-right"> Name</FormLabel>
<FormLabel for="name" class="text-left"> Name</FormLabel>
<Input id="name" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -94,7 +94,7 @@ const change = () => validate({ mode: 'silent' }).then((res) => (submitDisabled.
<FormField name="url" v-slot="{ componentField }" class="mt-2">
<FormItem>
<FormLabel for="url" class="text-right"> URL</FormLabel>
<FormLabel for="url" class="text-left"> URL</FormLabel>
<Input id="url" v-bind="componentField" />
<FormMessage />
</FormItem>

View File

@@ -138,7 +138,7 @@ const onSubmit = handleSubmit((values) => {
<form @submit="onSubmit" class="flex w-full flex-col items-start gap-4">
<FormField name="singular" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="singular" class="text-right">Singular</FormLabel>
<FormLabel for="singular" class="text-left">Singular</FormLabel>
<Input id="singular" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -146,7 +146,7 @@ const onSubmit = handleSubmit((values) => {
<FormField name="plural" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="plural" class="text-right">Plural</FormLabel>
<FormLabel for="plural" class="text-left">Plural</FormLabel>
<Input id="plural" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { AccordionRootEmits, AccordionRootProps } from 'reka-ui'
import { AccordionRoot, useForwardPropsEmits } from 'reka-ui'
const props = defineProps<AccordionRootProps>()
const emits = defineEmits<AccordionRootEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<AccordionRoot v-bind="forwarded">
<slot />
</AccordionRoot>
</template>

View File

@@ -0,0 +1,23 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { AccordionContentProps } from 'reka-ui'
import { AccordionContent } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionContentProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = reactiveOmit(props, 'class')
</script>
<template>
<AccordionContent
v-bind="delegatedProps"
class="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
>
<div :class="cn('pb-4 pt-0', props.class)">
<slot />
</div>
</AccordionContent>
</template>

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { reactiveOmit } from '@vueuse/core'
import type { AccordionItemProps } from 'reka-ui'
import { AccordionItem, useForwardProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionItemProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = reactiveOmit(props, 'class')
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<AccordionItem v-bind="forwardedProps" :class="cn('border-b', props.class)">
<slot />
</AccordionItem>
</template>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import { ChevronDown } from 'lucide-vue-next'
import { reactiveOmit } from '@vueuse/core'
import type { AccordionTriggerProps } from 'reka-ui'
import { AccordionHeader, AccordionTrigger } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<AccordionTriggerProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = reactiveOmit(props, 'class')
</script>
<template>
<AccordionHeader class="flex">
<AccordionTrigger
v-bind="delegatedProps"
:class="
cn(
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
props.class
)
"
>
<slot />
<slot name="icon">
<ChevronDown class="h-4 w-4 shrink-0 transition-transform duration-200" />
</slot>
</AccordionTrigger>
</AccordionHeader>
</template>

View File

@@ -0,0 +1,4 @@
export { default as Accordion } from './Accordion.vue'
export { default as AccordionContent } from './AccordionContent.vue'
export { default as AccordionItem } from './AccordionItem.vue'
export { default as AccordionTrigger } from './AccordionTrigger.vue'

View File

@@ -153,7 +153,7 @@ const onSubmit = handleSubmit((values) => emit('submit', values))
<form @submit="onSubmit" class="flex w-full flex-col items-start gap-4">
<FormField name="name" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="name" class="text-right">Name</FormLabel>
<FormLabel for="name" class="text-left">Name</FormLabel>
<Input id="name" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -161,7 +161,7 @@ const onSubmit = handleSubmit((values) => emit('submit', values))
<FormField name="username" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="username" class="text-right">Username</FormLabel>
<FormLabel for="username" class="text-left">Username</FormLabel>
<Input id="username" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -169,7 +169,7 @@ const onSubmit = handleSubmit((values) => emit('submit', values))
<FormField name="email" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="email" class="text-right">Email</FormLabel>
<FormLabel for="email" class="text-left">Email</FormLabel>
<Input id="email" type="email" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>

View File

@@ -111,7 +111,7 @@ const onSubmit = handleSubmit((values) => emit('submit', values))
<form @submit="onSubmit" class="flex w-full flex-col items-start gap-4">
<FormField name="password" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="password" class="text-right">Password</FormLabel>
<FormLabel for="password" class="text-left">Password</FormLabel>
<Input id="password" type="password" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>
@@ -119,7 +119,7 @@ const onSubmit = handleSubmit((values) => emit('submit', values))
<FormField name="passwordConfirm" v-slot="{ componentField }" validate-on-input>
<FormItem class="w-full">
<FormLabel for="passwordConfirm" class="text-right">Confirm Password</FormLabel>
<FormLabel for="passwordConfirm" class="text-left">Confirm Password</FormLabel>
<Input id="passwordConfirm" type="password" class="col-span-3" v-bind="componentField" />
<FormMessage />
</FormItem>

View File

@@ -82,7 +82,7 @@ watch(
<form @submit="onSubmit" @change="change">
<FormField name="user" v-slot="{ componentField }">
<FormItem>
<FormLabel for="user" class="text-right"> User</FormLabel>
<FormLabel for="user" class="text-left"> User</FormLabel>
<Select id="user" v-bind="componentField">
<SelectTrigger>
<SelectValue placeholder="Select a user" />

View File

@@ -58,14 +58,6 @@ const count = (id: string) => {
Open Catalyst Handbook
<ExternalLink class="ml-2 h-4 w-4" />
</a>
<a
href="/_/"
target="_blank"
class="flex items-center rounded border p-2 text-blue-500 hover:bg-accent"
>
Open Admin Interface
<ExternalLink class="ml-2 h-4 w-4" />
</a>
</CardContent>
</Card>
<Card>
@@ -118,14 +110,6 @@ const count = (id: string) => {
Open Catalyst Handbook
<ExternalLink class="ml-2 h-4 w-4" />
</a>
<a
href="/_/"
target="_blank"
class="flex items-center rounded border p-2 text-blue-500 hover:bg-accent"
>
Open Admin Interface
<ExternalLink class="ml-2 h-4 w-4" />
</a>
</CardContent>
</Card>
<Card>

View File

@@ -4,7 +4,7 @@ const typography = require('@tailwindcss/typography')
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['media'],
darkMode: ['media', 'class'],
safelist: ['dark', 'size-5', 'size-12'],
prefix: '',
@@ -16,90 +16,106 @@ module.exports = {
],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px'
}
},
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))'
}
},
borderRadius: {
xl: 'calc(var(--radius) + 4px)',
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
keyframes: {
'accordion-down': {
from: { height: 0 },
to: { height: 'var(--radix-accordion-content-height)' }
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: 0 }
},
'collapsible-down': {
from: { height: 0 },
to: { height: 'var(--radix-collapsible-content-height)' }
},
'collapsible-up': {
from: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 0 }
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
'collapsible-up': 'collapsible-up 0.2s ease-in-out'
}
}
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px'
}
},
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))'
}
},
borderRadius: {
xl: 'calc(var(--radius) + 4px)',
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
keyframes: {
'accordion-down': {
from: {
height: '0'
},
to: {
height: 'var(--reka-accordion-content-height)'
}
},
'accordion-up': {
from: {
height: 'var(--reka-accordion-content-height)'
},
to: {
height: '0'
}
},
'collapsible-down': {
from: {
height: 0
},
to: {
height: 'var(--radix-collapsible-content-height)'
}
},
'collapsible-up': {
from: {
height: 'var(--radix-collapsible-content-height)'
},
to: {
height: 0
}
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
'collapsible-up': 'collapsible-up 0.2s ease-in-out'
}
}
},
plugins: [animate, typography]
}

View File

@@ -15,10 +15,9 @@ test.describe('update a comment', () => {
{
field: 'message',
update: async (page) => {
await page.getByRole('tab', { name: 'Comments' }).click()
await page.getByRole('button', { name: 'More' }).click()
await page.getByRole('menuitem', { name: 'Edit' }).click()
await page.locator('textarea').nth(1).fill('Updated Comment')
await page.locator('.CodeMirror textarea').first().fill('Updated Comment')
await page.getByRole('button', { name: 'Save' }).click()
},
assert: async (page) => {
@@ -46,7 +45,6 @@ test('can delete a comment', async ({ page }) => {
await createTicket(page, ticketName)
const message = `comment-${randomUUID()}`
await createComment(page, message)
await page.getByRole('tab', { name: 'Comments' }).click()
await page.getByRole('button', { name: 'More' }).click()
await page.getByRole('menuitem', { name: 'Delete' }).click()
await page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click()

View File

@@ -17,9 +17,8 @@ test.describe('update a task', () => {
update: async (page, taskName: string) => {
await page.getByText("Toggle Sidebar").click()
await page.getByRole('tab', { name: 'Tasks' }).click()
await page.getByText(taskName).click()
await page.getByRole('tabpanel', { name: 'Tasks' }).getByRole('textbox').fill('Updated Task')
await page.locator('input[autofocus]').fill('Updated Task')
await page.keyboard.press('Enter')
},
assert: async (page) => {
@@ -29,7 +28,6 @@ test.describe('update a task', () => {
{
field: 'status',
update: async (page) => {
await page.getByRole('tab', { name: 'Tasks' }).click()
const cb = page.getByRole('checkbox').first()
await cb.click()
},
@@ -59,7 +57,6 @@ test('can delete a task', async ({ page }) => {
await createTicket(page, ticketName)
const taskName = `task-${randomUUID()}`
await createTask(page, taskName, false)
await page.getByRole('tab', { name: 'Tasks' }).click()
await page.locator('button', { hasText: 'Delete Task' }).click()
await page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click()
await expect(page.getByText(taskName)).toHaveCount(0)

View File

@@ -17,10 +17,9 @@ test.describe('update a timeline item', () => {
await createTicket(page, ticketName)
const msg = `timeline-${randomUUID()}`
await createTimeline(page, msg)
await page.getByRole('tab', { name: 'Timeline' }).click()
await page.getByRole('button', { name: 'More' }).click()
await page.getByRole('menuitem', { name: 'Edit' }).click()
await page.locator('textarea').nth(1).fill('Updated Timeline')
await page.locator('.CodeMirror textarea').first().fill('Updated Timeline')
await page.getByRole('button', { name: 'Save' }).click()
await expect(page.getByText('Updated Timeline')).toBeVisible()
})
@@ -32,7 +31,6 @@ test('can delete a timeline item', async ({ page }) => {
await createTicket(page, ticketName)
const msg = `timeline-${randomUUID()}`
await createTimeline(page, msg)
await page.getByRole('tab', { name: 'Timeline' }).click()
await page.getByRole('button', { name: 'More' }).click()
await page.getByRole('menuitem', { name: 'Delete' }).click()
await page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click()

View File

@@ -39,23 +39,26 @@ export const createTicket = async (page, name: string) => {
}
export const createTimeline = async (page, message: string) => {
await page.getByRole('tab', { name: 'Timeline' }).click()
await page.getByRole('button', { name: 'Add Timeline Item' }).click()
await page.getByRole('tabpanel', { name: 'Timeline' }).getByRole('textbox').fill(message)
const editor = page.locator('.EasyMDEContainer .CodeMirror').last()
await expect(editor).toBeVisible()
await editor.click()
await page.keyboard.type(message)
await page.getByRole('button', { name: 'Save' }).click()
await expect(page.getByText(message)).toBeVisible()
}
export const createComment = async (page, message: string) => {
await page.getByRole('tab', { name: 'Comments' }).click()
await page.getByRole('button', { name: 'Add Comment' }).click()
await page.getByRole('tabpanel', { name: 'Comments' }).getByRole('textbox').fill(message)
const editor = page.locator('.EasyMDEContainer .CodeMirror').last()
await expect(editor).toBeVisible()
await editor.click()
await page.keyboard.type(message)
await page.getByRole('button', { name: 'Save' }).click()
await expect(page.getByText(message)).toBeVisible()
}
export const createTask = async (page, name: string, done: boolean) => {
await page.getByRole('tab', { name: 'Tasks' }).click()
await page.getByRole('button', { name: 'Add Task' }).click()
await page.getByPlaceholder('Add a task...').fill(name)
await page.getByRole('button', { name: 'Save' }).click()