commit 1ed3b0c2d40a42521fa57f289924a6f58c977cc2 Author: Ron Rise Date: Wed Apr 16 12:47:04 2025 -0400 No changes made diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f227987 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +postgres/ +migrate \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/backend/.gitlab-ci.yml b/backend/.gitlab-ci.yml new file mode 100644 index 0000000..778b599 --- /dev/null +++ b/backend/.gitlab-ci.yml @@ -0,0 +1,35 @@ +stages: + - test + - build + +variables: + IMAGE_URL: siteworxpro/ + IMAGE_NAME: reloading-backend + + +Unit Tests: + stage: test + only: + - branches + image: siteworxpro/golang:1.24.0 + before_script: + - go mod tidy + script: + - go test ./... + +Build: + stage: build + dependencies: + - Unit Tests + only: + - master + image: docker:dind + before_script: + - docker login -u siteworxpro -p ${DOCKER_TOKEN} + script: + - TAG=${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA} + - docker build -t ${IMAGE_NAME} . + - docker tag ${IMAGE_NAME}:latest ${IMAGE_URL}${IMAGE_NAME}:latest + - docker tag ${IMAGE_URL}${IMAGE_NAME}:latest ${IMAGE_URL}${IMAGE_NAME}:${TAG} + - docker push ${IMAGE_URL}${IMAGE_NAME}:${TAG} + - docker push ${IMAGE_URL}${IMAGE_NAME}:latest \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..8d1f661 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,19 @@ +FROM siteworxpro/golang:1.24.0 AS build + +WORKDIR /app + +ARG CGO_ENABLED=0 +ARG GOOS=linux +ARG GOPROXY=direct +ARG GOPRIVATE=git.s.int + +ADD . . + +RUN go mod tidy +RUN go build -o app + +FROM scratch + +COPY --from=build /app/app . + +ENTRYPOINT ["/app"] \ No newline at end of file diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..9f36a8d --- /dev/null +++ b/backend/README.md @@ -0,0 +1,8 @@ +```shell +docker run -v $(pwd)/postgres:/var/lib/postgresql/data \ + -e POSTGRES_DB=loading \ + -e POSTGRES_PASSWORD=password \ + --rm --name postgres \ + -p 5432:5432 \ + -d scr.siteworxpro.com/library/postgres:17 +``` \ No newline at end of file diff --git a/backend/database/conn.go b/backend/database/conn.go new file mode 100644 index 0000000..c6fc78a --- /dev/null +++ b/backend/database/conn.go @@ -0,0 +1,63 @@ +package database + +import ( + "context" + "fmt" + "git.s.int/packages/go/utilities/Env" + "git.s.int/reloading-manager/backend/models/bullets" + "git.s.int/reloading-manager/backend/models/loads" + "git.s.int/reloading-manager/backend/models/manufacturer" + "git.s.int/reloading-manager/backend/models/powder" + "git.s.int/reloading-manager/backend/models/primers" + "github.com/jackc/pgx/v5" + "log" +) + +const ( + DbHost Env.EnvironmentVariable = "DB_HOST" + DbDatabase Env.EnvironmentVariable = "DB_DATABASE" + DbUser Env.EnvironmentVariable = "DB_USER" + DbPassword Env.EnvironmentVariable = "DB_PASSWORD" +) + +type Database struct { + Db *pgx.Conn + connected bool + databaseName string + Bullets *bullets.Queries + Manufacturer *manufacturer.Queries + Powder *powder.Queries + Primer *primers.Queries + Loads *loads.Queries +} + +func (*Database) DSN() string { + dbHost := DbHost.GetEnvString("localhost") + dbDatabase := DbDatabase.GetEnvString("loading") + dbUser := DbUser.GetEnvString("postgres") + dbPassword := DbPassword.GetEnvString("password") + + extraParams := "?sslmode=disable" + + return fmt.Sprintf("postgres://%s:%s@%s:5432/%s%s", dbUser, dbPassword, dbHost, dbDatabase, extraParams) +} + +func GetNewDatabase() *Database { + var dbSingleton Database + + conn, dbErr := pgx.Connect(context.Background(), dbSingleton.DSN()) + if dbErr != nil { + log.Fatal(dbErr) + } + + dbSingleton.Db = conn + dbSingleton.connected = true + dbSingleton.databaseName = DbDatabase.GetEnvString("loading") + dbSingleton.Bullets = bullets.New(dbSingleton.Db) + dbSingleton.Manufacturer = manufacturer.New(dbSingleton.Db) + dbSingleton.Powder = powder.New(dbSingleton.Db) + dbSingleton.Primer = primers.New(dbSingleton.Db) + dbSingleton.Loads = loads.New(dbSingleton.Db) + + return &dbSingleton +} diff --git a/backend/database/middleware.go b/backend/database/middleware.go new file mode 100644 index 0000000..d26993c --- /dev/null +++ b/backend/database/middleware.go @@ -0,0 +1,25 @@ +package database + +import ( + "github.com/labstack/echo/v4" +) + +type CustomContext struct { + echo.Context + Db *Database +} + +func CreateDatabaseMiddleware() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + cc := &CustomContext{ + Context: c, + Db: GetNewDatabase(), + } + + res := next(cc) + + return res + } + } +} diff --git a/backend/database/migrate.go b/backend/database/migrate.go new file mode 100644 index 0000000..59a04e2 --- /dev/null +++ b/backend/database/migrate.go @@ -0,0 +1,34 @@ +package database + +import ( + "database/sql" + _ "github.com/go-sql-driver/mysql" + "github.com/golang-migrate/migrate" + "github.com/golang-migrate/migrate/database/postgres" + _ "github.com/golang-migrate/migrate/source/file" + "log" +) + +func (db *Database) Migrate() { + sqlDB, err := sql.Open("postgres", db.DSN()) + + driver, err := postgres.WithInstance(sqlDB, &postgres.Config{}) + if err != nil { + log.Fatal(err) + } + + m, err := migrate.NewWithDatabaseInstance( + "file://./migrations", + db.databaseName, + driver, + ) + + if err != nil { + log.Fatal(err) + } + + err = m.Up() + if err != nil && err.Error() != "no change" { + log.Fatal(err) + } +} diff --git a/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..8e1902b --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,45 @@ +module git.s.int/reloading-manager/backend + +go 1.24.0 + +require ( + git.s.int/packages/go/utilities v1.2.2 + github.com/go-playground/validator v9.31.0+incompatible + github.com/go-sql-driver/mysql v1.9.1 + github.com/golang-migrate/migrate v3.5.4+incompatible + github.com/google/uuid v1.6.0 + github.com/jackc/pgx/v5 v5.7.4 + github.com/labstack/echo/v4 v4.13.3 + github.com/labstack/gommon v0.4.2 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.11.0 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..4c9db21 --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,152 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +git.s.int/packages/go/utilities v1.2.2 h1:IXKdrTgRc7tnDUB4sOWD/kjwgw9luUzvsaPzX+Dhm7Y= +git.s.int/packages/go/utilities v1.2.2/go.mod h1:1nIS3PzUaLiNBBkyme408XbI725PiureeTV7iBXfUI0= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +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= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= +github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator v9.31.0+incompatible h1:UA72EPEogEnq76ehGdEDp4Mit+3FDh548oRqwVgNsHA= +github.com/go-playground/validator v9.31.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI= +github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA= +github.com/golang-migrate/migrate v3.5.4+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.3 h1:PO1wNKj/bTAwxSJnO1Z4Ai8j4magtqg2SLNjEDzcXQo= +github.com/jackc/pgx/v5 v5.7.3/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/backend/handlers/bullets/handler.go b/backend/handlers/bullets/handler.go new file mode 100644 index 0000000..366b606 --- /dev/null +++ b/backend/handlers/bullets/handler.go @@ -0,0 +1,292 @@ +package bullets + +import ( + "context" + "encoding/json" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers" + "git.s.int/reloading-manager/backend/models/bullets" + "github.com/labstack/echo/v4" + "net/http" + "strconv" +) + +type BulletResponse struct { + Id string `json:"id"` + Name string `json:"name"` + Diameter int32 `json:"diameter"` + Weight int32 `json:"weight"` + Manufacturer handlers.Manufacturer `json:"manufacturer"` +} + +func Photo(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + byId, err := db.Bullets.GetBulletById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + mime := http.DetectContentType(byId.Photo) + + c.Response().Header().Set("Content-Type", mime) + c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + c.Response().Header().Set("Pragma", "no-cache") + c.Response().Header().Set("Expires", "0") + + _, err = c.Response().Write(byId.Photo) + + return err +} + +func Delete(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + err := db.Bullets.DeleteBullet(context.Background(), *uid) + if err != nil { + return err + } + + return c.JSON(http.StatusOK, handlers.Response[string]{}) +} + +func Put(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + byId, err := db.Bullets.GetBulletById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + file, err := c.FormFile("photo") + var fileData []byte + if err == nil { + handler, err := file.Open() + if err != nil { + return err + } + + buf := make([]byte, file.Size) + _, _ = handler.Read(buf) + fileData = buf + } else { + fileData = byId.Photo + } + + var meta json.RawMessage = []byte(c.FormValue("meta")) + if len(meta) == 0 { + meta = []byte("{}") + } + weight, _ := strconv.ParseInt(c.FormValue("weight"), 10, 32) + diameter, _ := strconv.ParseInt(c.FormValue("diameter"), 10, 32) + manufacturerId := c.FormValue("manufacturer_id") + manufacturerUid := handlers.ParseUuidOrBadRequest(c, manufacturerId) + if manufacturerUid == nil { + return nil + } + + name := c.FormValue("name") + if name == "" { + _ = c.JSON(http.StatusBadRequest, struct { + Message string `json:"message"` + }{ + Message: "Name is required.", + }) + + return nil + } + + manufacturer, err := db.Manufacturer.GetById(context.Background(), *manufacturerUid) + if err != nil { + _ = c.JSON(http.StatusBadRequest, struct { + Message string `json:"message"` + }{ + Message: "Invalid Manufacturer ID", + }) + + return nil + } + + err = db.Bullets.UpdateBullet(context.Background(), bullets.UpdateBulletParams{ + Name: name, + Weight: int32(weight), + Diameter: int32(diameter), + Photo: fileData, + Meta: meta, + ManufacturerID: manufacturer.ID, + ID: byId.ID, + }) + + if err != nil { + return err + } + + return nil +} + +func Get(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + byId, err := db.Bullets.GetBulletById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + response := handlers.Response[BulletResponse]{ + Status: http.StatusText(http.StatusOK), + Payload: BulletResponse{ + Id: uid.String(), + Name: byId.Name, + Diameter: byId.Diameter, + Weight: byId.Weight, + Manufacturer: handlers.Manufacturer{Id: byId.ManufactuererID.String(), Name: byId.ManufacutuerName, Url: byId.ManufacturerUrl.String}, + }, + } + + return c.JSON(http.StatusOK, response) + } + + bulletList, err := db.Bullets.ForPage(context.Background(), bullets.ForPageParams{ + Limit: 100, + Offset: 0, + }) + + if bulletList == nil { + bulletList = make([]bullets.ForPageRow, 0) + } + + var payload = make([]BulletResponse, 0) + for _, bullet := range bulletList { + payload = append(payload, BulletResponse{ + Id: bullet.ID.String(), + Name: bullet.Name, + Weight: bullet.Weight, + Diameter: bullet.Diameter, + Manufacturer: handlers.Manufacturer{Id: bullet.ManufacturerID.String(), Name: bullet.ManufacturerName, Url: bullet.ManufacturerUrl.String}, + }) + } + + if err != nil { + return err + } + err = c.JSON(http.StatusOK, handlers.Response[[]BulletResponse]{ + Payload: payload, + Status: "OK", + }) + if err != nil { + return err + } + + return nil +} + +func Post(c echo.Context) error { + + file, err := c.FormFile("photo") + if err != nil { + c.Logger().Error(err) + _ = c.JSON(http.StatusBadRequest, struct { + Message string `json:"message"` + }{ + Message: "No file provided", + }) + + return nil + } + handler, err := file.Open() + if err != nil { + return err + } + + buf := make([]byte, file.Size) + _, _ = handler.Read(buf) + + weight, _ := strconv.ParseInt(c.FormValue("weight"), 10, 32) + diameter, _ := strconv.ParseInt(c.FormValue("diameter"), 10, 32) + manufacturerId := c.FormValue("manufacturer_id") + name := c.FormValue("name") + if name == "" { + _ = c.JSON(http.StatusBadRequest, struct { + Message string `json:"message"` + }{ + Message: "Name is required.", + }) + + return nil + } + + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + manufacturerUid := handlers.ParseUuidOrBadRequest(c, manufacturerId) + if manufacturerUid == nil { + return nil + } + + manufacturer, err := db.Manufacturer.GetById(context.Background(), *manufacturerUid) + if err != nil { + _ = c.JSON(http.StatusBadRequest, struct { + Message string `json:"message"` + }{ + Message: "Invalid Manufacturer ID", + }) + + return nil + } + + var meta json.RawMessage = []byte(c.FormValue("meta")) + if len(meta) == 0 { + meta = []byte("{}") + } + + id, err := db.Bullets.AddBullet(context.Background(), bullets.AddBulletParams{ + Name: name, + Weight: int32(weight), + Diameter: int32(diameter), + Photo: buf, + Meta: meta, + ManufacturerID: manufacturer.ID, + }) + if err != nil { + return err + } + + err = c.JSON(http.StatusAccepted, handlers.Response[string]{ + Status: http.StatusText(http.StatusAccepted), + Payload: id.String(), + }) + if err != nil { + return err + } + + return nil +} diff --git a/backend/handlers/cartridge/handler.go b/backend/handlers/cartridge/handler.go new file mode 100644 index 0000000..1289a38 --- /dev/null +++ b/backend/handlers/cartridge/handler.go @@ -0,0 +1,96 @@ +package cartridge + +import ( + "context" + "encoding/json" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers" + "git.s.int/reloading-manager/backend/models/loads" + "github.com/labstack/echo/v4" + "net/http" +) + +type response struct { + Id string `json:"id"` + Name string `json:"name"` +} + +type postRequest struct { + Name string `json:"name"` + Meta string `json:"meta"` +} + +func Get(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + cartridges, err := db.Loads.GetCartridges(context.Background()) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + var res = make([]response, 0) + for _, cartridge := range cartridges { + res = append(res, response{ + Id: cartridge.ID.String(), + Name: cartridge.Name, + }) + } + + return c.JSON(http.StatusOK, handlers.Response[[]response]{ + Payload: res, + Status: http.StatusText(http.StatusOK), + }) +} + +func Post(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + req := postRequest{} + + err := c.Bind(&req) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + + if req.Name == "" { + return echo.NewHTTPError(http.StatusBadRequest, "name is required") + } + + var meta json.RawMessage + if req.Meta != "" { + meta = []byte(req.Meta) + } else { + meta = []byte("{}") + } + + retId, err := db.Loads.CreateCartridge(context.Background(), loads.CreateCartridgeParams{ + Name: req.Name, + Meta: meta, + }) + + if err != nil { + return handlers.BadRequest(c, "already exists") + } + + return c.JSON(http.StatusOK, map[string]string{"id": retId.String()}) +} + +func Delete(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + uid := handlers.ParseUuidOrBadRequest(c, c.Param("id")) + if uid == nil { + return nil + } + + err := db.Loads.DeleteCartridge(context.Background(), *uid) + + if err != nil { + return err + } + + return c.NoContent(http.StatusOK) +} diff --git a/backend/handlers/file.go b/backend/handlers/file.go new file mode 100644 index 0000000..96f3ace --- /dev/null +++ b/backend/handlers/file.go @@ -0,0 +1,29 @@ +package handlers + +import ( + "github.com/labstack/echo/v4" + "net/http" +) + +func ReadFile(c echo.Context, formName string) ([]byte, error) { + file, err := c.FormFile(formName) + if err != nil { + c.Logger().Error(err) + _ = c.JSON(http.StatusBadRequest, struct { + Message string `json:"message"` + }{ + Message: "No file provided", + }) + + return nil, err + } + handler, err := file.Open() + if err != nil { + return nil, err + } + + buf := make([]byte, file.Size) + _, _ = handler.Read(buf) + + return buf, nil +} diff --git a/backend/handlers/loads/handler.go b/backend/handlers/loads/handler.go new file mode 100644 index 0000000..d4d3168 --- /dev/null +++ b/backend/handlers/loads/handler.go @@ -0,0 +1,215 @@ +package loads + +import ( + "context" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers" + "git.s.int/reloading-manager/backend/handlers/bullets" + "git.s.int/reloading-manager/backend/handlers/primers" + "git.s.int/reloading-manager/backend/models/loads" + "github.com/labstack/echo/v4" + "net/http" +) + +type loadResponseResults struct { + Id string `json:"id"` + Cartridge string `json:"cartridge"` + Col float32 `json:"col"` + Powder handlers.Powder `json:"powder"` + PowderGr float32 `json:"powder_gr"` + Primer primers.PrimerResponse `json:"primer"` + Bullet bullets.BulletResponse `json:"bullet"` +} + +type loadResponse struct { + Total int `json:"total"` + Results []loadResponseResults `json:"results"` +} + +type ResultChan[T any] struct { + Result T + Err error +} + +func Post(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + cartridgeID, err := handlers.ParseUuid(c.FormValue("cartridge_id")) + if err != nil { + return handlers.BadRequest(c, "cartridge_id is not a valid UUID") + } + + powderId, err := handlers.ParseUuid(c.FormValue("powder_id")) + if err != nil { + return handlers.BadRequest(c, "powder_id is not a valid UUID") + } + + primerId, err := handlers.ParseUuid(c.FormValue("primer_id")) + if err != nil { + return handlers.BadRequest(c, "primer_id is not a valid UUID") + } + + bulletId, err := handlers.ParseUuid(c.FormValue("bullet_id")) + if err != nil { + return handlers.BadRequest(c, "bullet_id is not a valid UUID") + } + + file, err := handlers.ReadFile(c, "photo") + if err != nil { + return handlers.BadRequest(c, "photo is not valid") + } + + meta := c.FormValue("meta") + if meta == "" { + meta = "{}" + } + + powderGr := c.FormValue("powder_gr") + if powderGr == "" { + return handlers.BadRequest(c, "powder_gr is not valid") + } + + powderGrFl, err := handlers.ParseFloat32(powderGr) + if err != nil { + return handlers.BadRequest(c, "powder_gr is not valid") + } + + col := c.FormValue("col") + if col == "" { + return handlers.BadRequest(c, "col is not valid") + } + colFl, err := handlers.ParseFloat32(col) + if err != nil { + return handlers.BadRequest(c, "col is not valid") + } + + uid, err := db.Loads.CreateLoad(context.Background(), loads.CreateLoadParams{ + CartridgeID: *cartridgeID, + Col: colFl, + PowderID: *powderId, + PowderGr: powderGrFl, + PrimerID: *primerId, + BulletID: *bulletId, + Photo: file, + Meta: []byte(meta), + }) + + if err != nil { + return err + } + + return c.JSON(http.StatusCreated, handlers.Response[string]{Payload: uid.String()}) +} + +func Get(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + cTotal := make(chan ResultChan[int64]) + cResults := make(chan ResultChan[[]loadResponseResults]) + + go func(c chan ResultChan[int64]) { + db := database.GetNewDatabase() + defer db.Db.Close(context.Background()) + + total, err := db.Loads.TotalLoads(context.Background()) + + if err != nil { + c <- ResultChan[int64]{Err: err} + } else { + c <- ResultChan[int64]{Result: total} + } + + }(cTotal) + + go func(ch chan ResultChan[[]loadResponseResults]) { + + rows, err := db.Loads.GetLoads(context.Background(), loads.GetLoadsParams{ + Limit: handlers.ParseInt32WithDefault(c.QueryParam("limit"), 50), + Offset: handlers.ParseInt32WithDefault(c.QueryParam("offset"), 0), + + OrderBy: "cartridge_name asc", + + SearchCartridge: c.QueryParam("search_cartridge") != "", + Cartridge: c.QueryParam("search_cartridge"), + SearchBullet: c.QueryParam("search_bullet") != "", + Bullet: c.QueryParam("search_bullet") + "%", + SearchBulletManufacturer: c.QueryParam("search_bullet_manufacturer") != "", + BulletManufacturer: c.QueryParam("search_bullet_manufacturer") + "%", + SearchPrimer: c.QueryParam("search_primer") != "", + Primer: c.QueryParam("search_primer") + "%", + SearchPrimerManufacturer: c.QueryParam("search_primer_manufacturer") != "", + PrimeManufacturer: c.QueryParam("search_primer_manufacturer") + "%", + SearchPowder: c.QueryParam("search_powder") != "", + Powder: c.QueryParam("search_powder") + "%", + SearchPowderManufacturer: c.QueryParam("search_powder_manufacturer") != "", + PowderManufacturer: c.QueryParam("search_powder_manufacturer") + "%", + + SearchCartridgeID: c.QueryParam("search_cartridge_id") != "", + CartridgeID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_cartridge_id")), + SearchBulletID: c.QueryParam("search_bullet_id") != "", + BulletID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_bullet_id")), + SearchPrimerID: c.QueryParam("search_primer_id") != "", + PrimerID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_primer_id")), + SearchPowderID: c.QueryParam("search_powder_id") != "", + PowderID: *handlers.ParseUuidOrEmpty(c.QueryParam("search_powder_id")), + }) + if err != nil { + ch <- ResultChan[[]loadResponseResults]{Err: err} + + return + } + + results := make([]loadResponseResults, 0) + for _, row := range rows { + results = append(results, loadResponseResults{ + Id: row.ID.String(), + Cartridge: row.CartridgeName, + Col: row.Col, + Powder: handlers.Powder{ + Id: row.PowderID.String(), + Name: row.PowderName, + Meta: string(row.PowderMeta), + Manufacturer: handlers.Manufacturer{ + Name: row.PowderManufacturerName, + Url: row.PowderManufacturerUrl.String, + }, + }, + PowderGr: row.PowderGr, + Primer: primers.PrimerResponse{ + ID: row.PrimerID.String(), + Name: row.PrimerName, + Manufacturer: handlers.Manufacturer{ + Name: row.PrimerManufacturerName, + Url: row.PrimerManufacturerUrl.String, + }, + }, + Bullet: bullets.BulletResponse{ + Id: row.BulletID.String(), + Name: row.BulletName, + Diameter: row.BulletDiameter, + Weight: row.BulletWeight, + Manufacturer: handlers.Manufacturer{ + Name: row.BulletManufacturerName, + Url: row.BulletManufacturerUrl.String, + }, + }, + }) + } + + ch <- ResultChan[[]loadResponseResults]{Result: results} + }(cResults) + + total := <-cTotal + if total.Err != nil { + return total.Err + } + + results := <-cResults + if results.Err != nil { + return results.Err + } + + return c.JSON(http.StatusOK, handlers.Response[loadResponse]{Status: http.StatusText(http.StatusOK), Payload: loadResponse{Total: int(total.Result), Results: results.Result}}) +} diff --git a/backend/handlers/manufacturer/handler.go b/backend/handlers/manufacturer/handler.go new file mode 100644 index 0000000..86e15e3 --- /dev/null +++ b/backend/handlers/manufacturer/handler.go @@ -0,0 +1,167 @@ +package manufacturer + +import ( + "context" + "encoding/json" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers" + "git.s.int/reloading-manager/backend/models/manufacturer" + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + "github.com/labstack/echo/v4" + "net/http" +) + +type manufacturerResponse struct { + Id string `json:"id"` + Name string `json:"name"` + Url string `json:"url"` + CreatedAt string `json:"created_at"` +} + +func Get(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + byId, err := db.Manufacturer.GetById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + response := handlers.Response[manufacturerResponse]{ + Status: http.StatusText(http.StatusOK), + Payload: manufacturerResponse{ + Id: byId.ID.String(), + Name: byId.Name, + Url: byId.Url.String, + CreatedAt: byId.CreatedAt.Time.Format(handlers.DateFormat), + }, + } + + return c.JSON(http.StatusOK, response) + } + + all, err := db.Manufacturer.GetAll(context.Background()) + if err != nil { + return err + } + + var payload []manufacturerResponse + for _, m := range all { + payload = append(payload, manufacturerResponse{ + Id: m.ID.String(), + Name: m.Name, + Url: m.Url.String, + CreatedAt: m.CreatedAt.Time.Format(handlers.DateFormat), + }) + } + + response := handlers.Response[[]manufacturerResponse]{ + Status: http.StatusText(http.StatusOK), + Payload: payload, + } + + return c.JSON(http.StatusOK, response) +} + +func Delete(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + _, err := db.Manufacturer.GetById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + err = db.Manufacturer.DeleteById(context.Background(), *uid) + + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Cannot delete manufacturer") + } + + return c.NoContent(http.StatusNoContent) +} + +func Post(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + _, err := db.Manufacturer.GetById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + jsonMap := make(map[string]interface{}) + _ = json.NewDecoder(c.Request().Body).Decode(&jsonMap) + + if jsonMap["name"] == "" { + return handlers.BadRequest(c, "Name is required.") + } + + err = db.Manufacturer.UpdateManufacturer(context.Background(), manufacturer.UpdateManufacturerParams{ + Name: jsonMap["name"].(string), + Url: pgtype.Text{ + String: jsonMap["url"].(string), + Valid: true, + }, + ID: *uid, + }) + if err != nil { + return err + } + + return nil + } + + newUuid := uuid.New().String() + uid, _ := handlers.ParseUuid(newUuid) + + jsonMap := make(map[string]interface{}) + _ = json.NewDecoder(c.Request().Body).Decode(&jsonMap) + + if jsonMap["name"] == nil || jsonMap["name"] == "" { + return handlers.BadRequest(c, "Name is required.") + } + + var url string + if jsonMap["url"] != nil { + url = jsonMap["url"].(string) + } + + err := db.Manufacturer.CreateManufacturer(context.Background(), manufacturer.CreateManufacturerParams{ + ID: *uid, + Name: jsonMap["name"].(string), + Url: pgtype.Text{ + String: url, + Valid: true, + }, + }) + if err != nil { + return err + } + + return c.JSON(http.StatusAccepted, handlers.Response[string]{ + Status: http.StatusText(http.StatusAccepted), + Payload: newUuid, + }) +} diff --git a/backend/handlers/numbers.go b/backend/handlers/numbers.go new file mode 100644 index 0000000..646856b --- /dev/null +++ b/backend/handlers/numbers.go @@ -0,0 +1,21 @@ +package handlers + +import "strconv" + +func ParseFloat32(v string) (float32, error) { + fl, err := strconv.ParseFloat(v, 32) + if err != nil { + return 0, err + } + + return float32(fl), nil +} + +func ParseInt32WithDefault(s string, def int32) int32 { + sInt, err := strconv.ParseInt(s, 10, 32) + if err != nil { + return def + } + + return int32(sInt) +} diff --git a/backend/handlers/powder/handler.go b/backend/handlers/powder/handler.go new file mode 100644 index 0000000..26105d2 --- /dev/null +++ b/backend/handlers/powder/handler.go @@ -0,0 +1,217 @@ +package powder + +import ( + "context" + "encoding/json" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers" + "git.s.int/reloading-manager/backend/models/powder" + "github.com/google/uuid" + "github.com/labstack/echo/v4" + "net/http" +) + +func Photo(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "Invalid UUID") + } + + byId, err := db.Powder.GetPowderById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + mime := http.DetectContentType(byId.Photo) + + c.Response().Header().Set("Content-Type", mime) + c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + c.Response().Header().Set("Pragma", "no-cache") + c.Response().Header().Set("Expires", "0") + + _, err = c.Response().Write(byId.Photo) + + return err +} + +func Delete(c echo.Context) error { + id := c.Param("id") + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + uid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "Invalid UUID") + } + + err = db.Powder.DeletePowder(context.Background(), *uid) + if err != nil { + return err + } + + return c.JSON(http.StatusOK, handlers.Response[string]{}) +} + +func Post(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + + uid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "Invalid UUID") + } + + p, err := db.Powder.GetPowderById(context.Background(), *uid) + if err != nil { + return handlers.NotFound(c, "Not found") + } + + file, _ := c.FormFile("photo") + if file != nil { + handler, _ := file.Open() + buf := make([]byte, file.Size) + _, _ = handler.Read(buf) + p.Photo = buf + } + + if c.FormValue("name") != "" { + p.Name = c.FormValue("name") + } + + muid, err := handlers.ParseUuid(c.FormValue("manufacturer_id")) + if err != nil { + return handlers.BadRequest(c, "Invalid UUID") + } + + if c.FormValue("manufacturer_id") != "" { + p.ManufacturerID = *muid + } + + metaString := c.FormValue("meta") + if metaString == "" { + metaString = string(p.Meta) + } + + err = db.Powder.UpdatePowder(context.Background(), powder.UpdatePowderParams{ + ID: p.ID, + Name: p.Name, + ManufacturerID: p.ManufacturerID, + Photo: p.Photo, + Meta: []byte(metaString), + }) + + if err != nil { + return err + } + + return c.JSON(http.StatusOK, handlers.Response[string]{}) + } + + buf, err := handlers.ReadFile(c, "photo") + if err != nil { + return err + } + + newUuid := uuid.New().String() + pgUuid, _ := handlers.ParseUuid(newUuid) + mfUid, _ := handlers.ParseUuid(c.FormValue("manufacturer_id")) + + jsonMap := make(map[string]interface{}) + jsonMap["name"] = c.FormValue("name") + if jsonMap["name"] == nil || jsonMap["name"] == "" { + return handlers.BadRequest(c, "Name is required.") + } + + jsonMap["manufacturer_id"] = c.FormValue("manufacturer_id") + if jsonMap["manufacturer_id"] == nil || jsonMap["manufacturer_id"] == "" { + return handlers.BadRequest(c, "manufacturer_id is required.") + } + + metaString := c.FormValue("meta") + if metaString == "" { + metaString = "{}" + } + var meta json.RawMessage = []byte(metaString) + err = db.Powder.InsertPowder(context.Background(), powder.InsertPowderParams{ + ID: *pgUuid, + Name: jsonMap["name"].(string), + ManufacturerID: *mfUid, + Meta: meta, + Photo: buf, + }) + + if err != nil { + return err + } + + return c.JSON(http.StatusCreated, handlers.Response[struct { + Id string `json:"id"` + }]{ + Status: http.StatusText(http.StatusCreated), + Payload: struct { + Id string `json:"id"` + }{Id: newUuid}, + }) +} + +func Get(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + uid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "Invalid UUID") + } + + p, err := db.Powder.GetPowderById(context.Background(), *uid) + if err != nil { + return handlers.BadRequest(c, "Not found") + } + + response := handlers.Response[handlers.Powder]{ + Payload: handlers.Powder{ + Id: uid.String(), + Name: p.Name, + Manufacturer: handlers.Manufacturer{Id: p.ManufacturerID.String(), Name: p.ManufacturerName, Url: p.ManufacturerUrl.String}, + }, + } + + response.Status = http.StatusText(http.StatusOK) + + return c.JSON(http.StatusOK, response) + } + + powders, err := db.Powder.GetPowders(context.Background()) + if err != nil { + return err + } + + response := handlers.Response[[]handlers.Powder]{ + Payload: make([]handlers.Powder, 0), + } + + for _, p := range powders { + response.Payload = append(response.Payload, handlers.Powder{ + Id: p.ID.String(), + Name: p.Name, + Manufacturer: handlers.Manufacturer{ + Id: p.ManufacturerID.String(), + Name: p.ManufacturerName, + Url: p.ManufacturerUrl.String, + }, + }) + } + + response.Status = http.StatusText(http.StatusOK) + + return c.JSON(200, response) +} diff --git a/backend/handlers/primers/handler.go b/backend/handlers/primers/handler.go new file mode 100644 index 0000000..58fc033 --- /dev/null +++ b/backend/handlers/primers/handler.go @@ -0,0 +1,226 @@ +package primers + +import ( + "context" + "encoding/json" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers" + "git.s.int/reloading-manager/backend/models/primers" + "github.com/google/uuid" + "github.com/labstack/echo/v4" + "net/http" +) + +type PrimerResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Manufacturer handlers.Manufacturer `json:"manufacturer"` +} + +func Delete(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid, err := handlers.ParseUuid(id) + if err != nil { + return handlers.BadRequest(c, "Invalid UUID.") + } + + err = db.Primer.DeletePrimer(context.Background(), *uid) + if err != nil { + return err + } + + return c.JSON(http.StatusNoContent, handlers.Response[string]{}) +} + +func Post(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + p, err := db.Primer.GetPrimerById(context.Background(), *uid) + if err != nil { + return handlers.NotFound(c, "Primer not found.") + } + + if c.FormValue("name") != "" { + p.Name = c.FormValue("name") + } + + if c.FormValue("manufacturer_id") != "" { + mid := handlers.ParseUuidOrBadRequest(c, c.FormValue("manufacturer_id")) + if mid == nil { + return nil + } + + p.ManufacturerID = *mid + } + + if c.FormValue("meta") != "" { + p.Meta = json.RawMessage(c.Param("meta")) + } + + file, _ := c.FormFile("photo") + if file != nil { + photo, err := handlers.ReadFile(c, "photo") + if err != nil { + return err + } + p.Photo = photo + } + + err = db.Primer.UpdatePrimer(context.Background(), primers.UpdatePrimerParams{ + ID: p.ID, + Name: p.Name, + ManufacturerID: p.ManufacturerID, + Meta: p.Meta, + Photo: p.Photo, + }) + + if err != nil { + return err + } + + return c.JSON(http.StatusOK, handlers.Response[string]{Payload: p.ID.String(), Status: http.StatusText(http.StatusOK)}) + } + + buf, err := handlers.ReadFile(c, "photo") + if err != nil { + return err + } + + name := c.FormValue("name") + manufacturerId := c.FormValue("manufacturer_id") + metaString := c.FormValue("meta") + + if name == "" { + return handlers.BadRequest(c, "Name is required.") + } + + if manufacturerId == "" { + return handlers.BadRequest(c, "Manufacturer ID is required.") + } + + if metaString == "" { + metaString = "{}" + } + var meta json.RawMessage = []byte(metaString) + + newUuid := uuid.New().String() + uid, _ := handlers.ParseUuid(newUuid) + mid := handlers.ParseUuidOrBadRequest(c, manufacturerId) + if mid == nil { + return nil + } + + err = db.Primer.InsertPrimer(context.Background(), primers.InsertPrimerParams{ + ID: *uid, + Name: name, + ManufacturerID: *mid, + Meta: meta, + Photo: buf, + }) + + if err != nil { + return err + } + + return c.JSON(http.StatusCreated, handlers.Response[struct { + Id string `json:"id"` + }]{ + Status: http.StatusText(http.StatusCreated), + Payload: struct { + Id string `json:"id"` + }{Id: newUuid}, + }) +} + +func Photo(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + byId, err := db.Primer.GetPrimerById(context.Background(), *uid) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "Not found") + } + + mime := http.DetectContentType(byId.Photo) + + c.Response().Header().Set("Content-Type", mime) + c.Response().Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + c.Response().Header().Set("Pragma", "no-cache") + c.Response().Header().Set("Expires", "0") + + _, err = c.Response().Write(byId.Photo) + + return err +} + +func Get(c echo.Context) error { + db := c.(*database.CustomContext).Db + defer db.Db.Close(context.Background()) + + if c.Param("id") != "" { + id := c.Param("id") + uid := handlers.ParseUuidOrBadRequest(c, id) + if uid == nil { + return nil + } + + row, err := db.Primer.GetPrimerById(context.Background(), *uid) + if err != nil { + return err + } + + return c.JSON(http.StatusOK, handlers.Response[PrimerResponse]{ + Status: http.StatusText(http.StatusOK), + Payload: PrimerResponse{ + ID: row.ID.String(), + Name: row.Name, + Manufacturer: handlers.Manufacturer{ + Id: row.ManufacturerID.String(), + Name: row.ManufacturerName, + Url: row.ManufacturerUrl.String, + }, + }, + }) + } + + rows, err := db.Primer.GetPrimers(context.Background()) + if err != nil { + return err + } + + response := handlers.Response[[]PrimerResponse]{ + Status: "OK", + Payload: make([]PrimerResponse, 0), + } + + for _, row := range rows { + response.Payload = append(response.Payload, PrimerResponse{ + ID: row.ID.String(), + Name: row.Name, + Manufacturer: handlers.Manufacturer{ + Id: row.ManufacturerID.String(), + Name: row.ManufacturerName, + Url: row.ManufacturerUrl.String, + }, + }) + } + + return c.JSON(http.StatusOK, response) +} diff --git a/backend/handlers/response.go b/backend/handlers/response.go new file mode 100644 index 0000000..e2e7852 --- /dev/null +++ b/backend/handlers/response.go @@ -0,0 +1,42 @@ +package handlers + +import ( + "github.com/labstack/echo/v4" + "net/http" +) + +const DateFormat = "2006-01-02 15:04:05" + +type Response[T any] struct { + Status string `json:"status"` + Message string `json:"message,omitempty"` + Payload T `json:"payload"` +} + +func BadRequest(c echo.Context, message string) error { + return c.JSON(http.StatusBadRequest, Response[string]{ + Status: http.StatusText(http.StatusBadRequest), + Message: message, + }) +} + +func NotFound(c echo.Context, message string) error { + return c.JSON(http.StatusNotFound, Response[string]{ + Status: http.StatusText(http.StatusNotFound), + Message: message, + }) +} + +type Manufacturer struct { + Id string `json:"id"` + Name string `json:"name"` + Url string `json:"url"` +} + +type Powder struct { + Id string `json:"id"` + Name string `json:"name"` + Meta string `json:"meta"` + Photo string `json:"photo,omitempty"` + Manufacturer Manufacturer `json:"manufacturer"` +} diff --git a/backend/handlers/responses_test.go b/backend/handlers/responses_test.go new file mode 100644 index 0000000..0c3671b --- /dev/null +++ b/backend/handlers/responses_test.go @@ -0,0 +1,38 @@ +package handlers + +import ( + "github.com/labstack/echo/v4" + "testing" +) + +type TestContext struct { + echo.Context + t *testing.T +} + +func (t TestContext) JSON(code int, i interface{}) error { + + if code != 400 { + t.t.Fatal("expected 400") + } + + switch i.(type) { + case Response[string]: + break + default: + t.t.Fatal("expected response") + } + + return nil +} + +func TestBadRequest(t *testing.T) { + context := TestContext{ + t: t, + } + + err := BadRequest(context, "Something is wrong") + if err != nil { + t.Errorf("Should not have returned an error") + } +} diff --git a/backend/handlers/uuid.go b/backend/handlers/uuid.go new file mode 100644 index 0000000..07f842e --- /dev/null +++ b/backend/handlers/uuid.go @@ -0,0 +1,45 @@ +package handlers + +import ( + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + "github.com/labstack/echo/v4" +) + +func ParseUuidOrEmpty(s string) *pgtype.UUID { + ParsedUuid, err := uuid.Parse(s) + if err != nil { + return &pgtype.UUID{ + Valid: false, + } + } + + return &pgtype.UUID{ + Bytes: ParsedUuid, + Valid: true, + } +} + +func ParseUuid(s string) (*pgtype.UUID, error) { + uid, err := uuid.Parse(s) + if err != nil { + return nil, err + } + + return &pgtype.UUID{ + Bytes: uid, + Valid: true, + }, nil +} + +func ParseUuidOrBadRequest(c echo.Context, s string) *pgtype.UUID { + uid, err := ParseUuid(s) + if err != nil { + _ = BadRequest(c, "Invalid UUID.") + + return nil + } + + return uid + +} diff --git a/backend/main.go b/backend/main.go new file mode 100644 index 0000000..1ea560b --- /dev/null +++ b/backend/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "context" + "fmt" + "git.s.int/packages/go/utilities/Env" + "git.s.int/reloading-manager/backend/database" + "git.s.int/reloading-manager/backend/handlers/bullets" + "git.s.int/reloading-manager/backend/handlers/cartridge" + "git.s.int/reloading-manager/backend/handlers/loads" + "git.s.int/reloading-manager/backend/handlers/manufacturer" + "git.s.int/reloading-manager/backend/handlers/powder" + "git.s.int/reloading-manager/backend/handlers/primers" + "github.com/go-playground/validator" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "github.com/labstack/gommon/log" + "net/http" +) + +const ( + Port Env.EnvironmentVariable = "PORT" +) + +type ( + CustomValidator struct { + validator *validator.Validate + } +) + +func (cv *CustomValidator) Validate(i interface{}) error { + if err := cv.validator.Struct(i); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + return nil +} + +func main() { + e := echo.New() + + e.Validator = &CustomValidator{validator: validator.New()} + + e.HideBanner = true + e.HidePort = true + + e.Logger.SetLevel(log.DEBUG) + + e.Use(database.CreateDatabaseMiddleware()) + e.Use(middleware.Recover()) + e.Use(middleware.Logger()) + e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ + AllowOrigins: []string{"http://localhost:5173", "http://127.0.0.1:5173"}, + AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, + })) + + migrate(e) + + // Bullets + e.GET("/bullet", bullets.Get) + e.GET("/bullet/:id", bullets.Get) + e.GET("/bullet/:id/photo", bullets.Photo) + e.POST("/bullet", bullets.Post) + e.POST("/bullet/:id", bullets.Put) + e.DELETE("/bullet/:id", bullets.Delete) + + // Manufacturers + e.GET("/manufacturer", manufacturer.Get) + e.GET("/manufacturer/:id", manufacturer.Get) + e.POST("/manufacturer", manufacturer.Post) + e.POST("/manufacturer/:id", manufacturer.Post) + e.DELETE("/manufacturer/:id", manufacturer.Delete) + + // Powders + e.GET("/powder", powder.Get) + e.GET("/powder/:id", powder.Get) + e.GET("/powder/:id/photo", powder.Photo) + e.POST("/powder", powder.Post) + e.POST("/powder/:id", powder.Post) + e.DELETE("/powder/:id", powder.Delete) + + // Primers + e.GET("/primer", primers.Get) + e.GET("/primer/:id", primers.Get) + e.GET("/primer/:id/photo", primers.Photo) + e.POST("/primer", primers.Post) + e.POST("/primer/:id", primers.Post) + e.DELETE("/primer/:id", primers.Delete) + + // cartridge + e.GET("/cartridge", cartridge.Get) + e.POST("/cartridge", cartridge.Post) + e.DELETE("/cartridge/:id", cartridge.Delete) + + // loads + e.GET("/load", loads.Get) + e.POST("/load", loads.Post) + + addr := fmt.Sprintf("0.0.0.0:%s", Port.GetEnvString("8080")) + + e.Logger.Info("Starting Server...") + e.Logger.Infof("Listening on %v", addr) + e.Logger.Fatal(e.Start(addr)) +} + +func migrate(e *echo.Echo) { + e.Logger.Info("Starting migrations...") + + db := database.GetNewDatabase() + + defer db.Db.Close(context.Background()) + + db.Migrate() + e.Logger.Info("Done!") +} diff --git a/backend/migrations/000001_create_initial_tables.down.sql b/backend/migrations/000001_create_initial_tables.down.sql new file mode 100644 index 0000000..ca33961 --- /dev/null +++ b/backend/migrations/000001_create_initial_tables.down.sql @@ -0,0 +1,2 @@ +drop table if exists bullets; +drop table if exists manufacturers; \ No newline at end of file diff --git a/backend/migrations/000001_create_initial_tables.up.sql b/backend/migrations/000001_create_initial_tables.up.sql new file mode 100644 index 0000000..b16e4f3 --- /dev/null +++ b/backend/migrations/000001_create_initial_tables.up.sql @@ -0,0 +1,42 @@ +create table manufacturers +( + id uuid primary key default gen_random_uuid(), + name varchar(255) not null, + url varchar(255), + created_at timestamp default NOW() not null +); + +insert into manufacturers (name, url) +values ('Hornady', 'https://www.hornady.com/'), + ('CCI', 'https://www.cci-ammunition.com/'), + ('IMR Powders', 'https://shop.hodgdon.com/imr/'), + ('Accurate Powders', 'https://hodgdonpowderco.com/accurate/'), + ('Federal', 'https://www.federalpremium.com/'), + ('Remington', 'https://www.remington.com/'), + ('Winchester', 'https://www.winchester.com/'), + ('Speer', 'https://www.speer.com/'), + ('Nosler', 'https://www.nosler.com/'), + ('Sierra', 'https://www.sierrabullets.com/'), + ('Barnes', 'https://www.barnesbullets.com/'), + ('Berger', 'https://www.bergerbullets.com/'), + ('Lapua', 'https://www.lapua.com/'), + ('Norma', 'https://www.norma-ammunition.com/'), + ('Berrys Bullets', 'https://www.berrysmfg.com/'), + ('Starline Brass', 'https://www.starlinebrass.com/'), + ('Swift', 'https://www.swiftbullets.com/'); + +create table bullets +( + id uuid primary key default gen_random_uuid(), + name varchar(255) not null, + weight int not null, + diameter int not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null +); + +alter table bullets + add constraint bullets_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id); \ No newline at end of file diff --git a/backend/migrations/000002_create_powder_tables.down.sql b/backend/migrations/000002_create_powder_tables.down.sql new file mode 100644 index 0000000..7b5c2f2 --- /dev/null +++ b/backend/migrations/000002_create_powder_tables.down.sql @@ -0,0 +1 @@ +drop table if exists powders; diff --git a/backend/migrations/000002_create_powder_tables.up.sql b/backend/migrations/000002_create_powder_tables.up.sql new file mode 100644 index 0000000..022e140 --- /dev/null +++ b/backend/migrations/000002_create_powder_tables.up.sql @@ -0,0 +1,11 @@ +create table powders +( + id uuid primary key default gen_random_uuid(), + name varchar(255) not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null, + constraint powders_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id) +); \ No newline at end of file diff --git a/backend/migrations/000003_create_primers_table.down.sql b/backend/migrations/000003_create_primers_table.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/backend/migrations/000003_create_primers_table.up.sql b/backend/migrations/000003_create_primers_table.up.sql new file mode 100644 index 0000000..a1a2fbc --- /dev/null +++ b/backend/migrations/000003_create_primers_table.up.sql @@ -0,0 +1,11 @@ +create table primers +( + id uuid primary key default gen_random_uuid(), + name varchar(255) not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null, + constraint primers_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id) +); \ No newline at end of file diff --git a/backend/migrations/000004_create_loads_tables.down.sql b/backend/migrations/000004_create_loads_tables.down.sql new file mode 100644 index 0000000..d000ca6 --- /dev/null +++ b/backend/migrations/000004_create_loads_tables.down.sql @@ -0,0 +1,2 @@ +drop table if exists loads; +drop table if exists cartridges; \ No newline at end of file diff --git a/backend/migrations/000004_create_loads_tables.up.sql b/backend/migrations/000004_create_loads_tables.up.sql new file mode 100644 index 0000000..c919032 --- /dev/null +++ b/backend/migrations/000004_create_loads_tables.up.sql @@ -0,0 +1,32 @@ +create table cartridges +( + id uuid primary key default gen_random_uuid(), + name varchar(255) not null, + created_at timestamp default current_timestamp not null, + meta json not null, + constraint cartridges_name_uindex + unique (name) +); + +create table loads +( + id uuid primary key default gen_random_uuid(), + cartridge_id uuid not null, + col float4 not null, + powder_id uuid not null, + powder_gr float4 not null, + primer_id uuid not null, + bullet_id uuid not null, + photo bytea not null, + created_at timestamp default current_timestamp not null, + meta json not null, + constraint loads_bullets_id_fk + foreign key (bullet_id) references bullets (id), + constraint loads_powders_id_fk + foreign key (powder_id) references powders (id), + constraint loads_primers_id_fk + foreign key (primer_id) references primers (id), + constraint loads_cartridges_id_fk + foreign key (cartridge_id) references cartridges (id) +); + diff --git a/backend/models/bullets-schema.sql b/backend/models/bullets-schema.sql new file mode 100644 index 0000000..3888a72 --- /dev/null +++ b/backend/models/bullets-schema.sql @@ -0,0 +1,20 @@ +create table bullets +( + id uuid primary key, + name varchar(255) not null, + weight int not null, + diameter int not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null +); + + +create table manufacturers +( + id uuid primary key , + name varchar(255) not null, + url varchar(255), + created_at timestamp default NOW() not null +); \ No newline at end of file diff --git a/backend/models/bullets.sql b/backend/models/bullets.sql new file mode 100644 index 0000000..f4be255 --- /dev/null +++ b/backend/models/bullets.sql @@ -0,0 +1,63 @@ +-- name: GetBullets :many +select bullets.id as id, + bullets.name as name, + weight, + diameter, + bullets.created_at, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from bullets + join manufacturers m on m.id = bullets.manufacturer_id +order by manufacturer_name, bullets.name desc; + +-- name: GetBulletById :one +select bullets.id as id, + bullets.name, + bullets.diameter, + bullets.weight, + bullets.meta, + bullets.photo, + bullets.created_at, + m.id as manufactuerer_id, + m.name as manufacutuer_name, + m.url as manufacturer_url +from bullets + join manufacturers m on m.id = bullets.manufacturer_id +where bullets.id = $1; + +-- name: ForPage :many +select bullets.id as id, + bullets.name as name, + weight, + diameter, + bullets.created_at, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from bullets + join manufacturers m on m.id = bullets.manufacturer_id +order by manufacturer_name, bullets.name desc +limit $1 offset $2; + +-- name: DeleteBullet :exec +DELETE +from bullets +where id = $1; + +-- name: AddBullet :one +insert into bullets (name, weight, diameter, photo, meta, manufacturer_id) +values ($1, $2, $3, $4, $5, $6) +returning id; + +-- name: UpdateBullet :exec +UPDATE bullets +set name=$1, + weight=$2, + diameter=$3, + photo=$4, + meta=$5, + manufacturer_id=$6 +where id = $7; \ No newline at end of file diff --git a/backend/models/bullets/bullets.sql.go b/backend/models/bullets/bullets.sql.go new file mode 100644 index 0000000..8d422ca --- /dev/null +++ b/backend/models/bullets/bullets.sql.go @@ -0,0 +1,253 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: bullets.sql + +package bullets + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const addBullet = `-- name: AddBullet :one +insert into bullets (name, weight, diameter, photo, meta, manufacturer_id) +values ($1, $2, $3, $4, $5, $6) +returning id +` + +type AddBulletParams struct { + Name string `json:"name"` + Weight int32 `json:"weight"` + Diameter int32 `json:"diameter"` + Photo []byte `json:"photo"` + Meta []byte `json:"meta"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` +} + +func (q *Queries) AddBullet(ctx context.Context, arg AddBulletParams) (pgtype.UUID, error) { + row := q.db.QueryRow(ctx, addBullet, + arg.Name, + arg.Weight, + arg.Diameter, + arg.Photo, + arg.Meta, + arg.ManufacturerID, + ) + var id pgtype.UUID + err := row.Scan(&id) + return id, err +} + +const deleteBullet = `-- name: DeleteBullet :exec +DELETE +from bullets +where id = $1 +` + +func (q *Queries) DeleteBullet(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteBullet, id) + return err +} + +const forPage = `-- name: ForPage :many +select bullets.id as id, + bullets.name as name, + weight, + diameter, + bullets.created_at, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from bullets + join manufacturers m on m.id = bullets.manufacturer_id +order by manufacturer_name, bullets.name desc +limit $1 offset $2 +` + +type ForPageParams struct { + Limit int32 `json:"limit"` + Offset int32 `json:"offset"` +} + +type ForPageRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Weight int32 `json:"weight"` + Diameter int32 `json:"diameter"` + CreatedAt pgtype.Timestamp `json:"created_at"` + Meta []byte `json:"meta"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ManufacturerName string `json:"manufacturer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) ForPage(ctx context.Context, arg ForPageParams) ([]ForPageRow, error) { + rows, err := q.db.Query(ctx, forPage, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ForPageRow + for rows.Next() { + var i ForPageRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Weight, + &i.Diameter, + &i.CreatedAt, + &i.Meta, + &i.ManufacturerID, + &i.ManufacturerName, + &i.ManufacturerUrl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getBulletById = `-- name: GetBulletById :one +select bullets.id as id, + bullets.name, + bullets.diameter, + bullets.weight, + bullets.meta, + bullets.photo, + bullets.created_at, + m.id as manufactuerer_id, + m.name as manufacutuer_name, + m.url as manufacturer_url +from bullets + join manufacturers m on m.id = bullets.manufacturer_id +where bullets.id = $1 +` + +type GetBulletByIdRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Diameter int32 `json:"diameter"` + Weight int32 `json:"weight"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + CreatedAt pgtype.Timestamp `json:"created_at"` + ManufactuererID pgtype.UUID `json:"manufactuerer_id"` + ManufacutuerName string `json:"manufacutuer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) GetBulletById(ctx context.Context, id pgtype.UUID) (GetBulletByIdRow, error) { + row := q.db.QueryRow(ctx, getBulletById, id) + var i GetBulletByIdRow + err := row.Scan( + &i.ID, + &i.Name, + &i.Diameter, + &i.Weight, + &i.Meta, + &i.Photo, + &i.CreatedAt, + &i.ManufactuererID, + &i.ManufacutuerName, + &i.ManufacturerUrl, + ) + return i, err +} + +const getBullets = `-- name: GetBullets :many +select bullets.id as id, + bullets.name as name, + weight, + diameter, + bullets.created_at, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from bullets + join manufacturers m on m.id = bullets.manufacturer_id +order by manufacturer_name, bullets.name desc +` + +type GetBulletsRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Weight int32 `json:"weight"` + Diameter int32 `json:"diameter"` + CreatedAt pgtype.Timestamp `json:"created_at"` + Meta []byte `json:"meta"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ManufacturerName string `json:"manufacturer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) GetBullets(ctx context.Context) ([]GetBulletsRow, error) { + rows, err := q.db.Query(ctx, getBullets) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetBulletsRow + for rows.Next() { + var i GetBulletsRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Weight, + &i.Diameter, + &i.CreatedAt, + &i.Meta, + &i.ManufacturerID, + &i.ManufacturerName, + &i.ManufacturerUrl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateBullet = `-- name: UpdateBullet :exec +UPDATE bullets +set name=$1, + weight=$2, + diameter=$3, + photo=$4, + meta=$5, + manufacturer_id=$6 +where id = $7 +` + +type UpdateBulletParams struct { + Name string `json:"name"` + Weight int32 `json:"weight"` + Diameter int32 `json:"diameter"` + Photo []byte `json:"photo"` + Meta []byte `json:"meta"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateBullet(ctx context.Context, arg UpdateBulletParams) error { + _, err := q.db.Exec(ctx, updateBullet, + arg.Name, + arg.Weight, + arg.Diameter, + arg.Photo, + arg.Meta, + arg.ManufacturerID, + arg.ID, + ) + return err +} diff --git a/backend/models/bullets/db.go b/backend/models/bullets/db.go new file mode 100644 index 0000000..752a4aa --- /dev/null +++ b/backend/models/bullets/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package bullets + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/backend/models/bullets/models.go b/backend/models/bullets/models.go new file mode 100644 index 0000000..dc4cde5 --- /dev/null +++ b/backend/models/bullets/models.go @@ -0,0 +1,27 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package bullets + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type Bullet struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Weight int32 `json:"weight"` + Diameter int32 `json:"diameter"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type Manufacturer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Url pgtype.Text `json:"url"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} diff --git a/backend/models/loads-schema.sql b/backend/models/loads-schema.sql new file mode 100644 index 0000000..12fe400 --- /dev/null +++ b/backend/models/loads-schema.sql @@ -0,0 +1,73 @@ +create table cartridges +( + id uuid primary key, + name varchar(255) not null, + created_at timestamp default current_timestamp not null, + meta json not null +); + +create table loads +( + id uuid primary key, + cartridge_id uuid not null, + col float4 not null, + powder_id uuid not null, + powder_gr float4 not null, + primer_id uuid not null, + bullet_id uuid not null, + photo bytea not null, + created_at timestamp default current_timestamp not null, + meta json not null, + constraint loads_bullets_id_fk + foreign key (bullet_id) references bullets (id), + constraint loads_powders_id_fk + foreign key (powder_id) references powders (id), + constraint loads_primers_id_fk + foreign key (primer_id) references primers (id), + constraint loads_cartridges_id_fk + foreign key (cartridge_id) references cartridges (id) +); + +create table bullets +( + id uuid primary key, + name varchar(255) not null, + weight int not null, + diameter int not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null +); + + +create table manufacturers +( + id uuid primary key, + name varchar(255) not null, + url varchar(255), + created_at timestamp default NOW() not null +); + +create table powders +( + id uuid primary key, + name varchar(255) not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null, + constraint powders_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id) +); +create table primers +( + id uuid primary key, + name varchar(255) not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null, + constraint primers_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id) +); diff --git a/backend/models/loads.sql b/backend/models/loads.sql new file mode 100644 index 0000000..53b8df2 --- /dev/null +++ b/backend/models/loads.sql @@ -0,0 +1,100 @@ +-- name: GetCartridgeById :one +select c.id as id, c.name, c.meta +from cartridges c +where id = $1; + +-- name: GetCartridges :many +select c.id as id, c.name, c.meta +from cartridges c +order by c.name; + +-- name: CreateCartridge :one +insert into cartridges (name, meta) +values ($1, $2) +returning id; + +-- name: DeleteCartridge :exec +delete +from cartridges +where id = $1; + +-- name: CreateLoad :one +insert into loads (cartridge_id, col, powder_id, powder_gr, primer_id, bullet_id, photo, meta) +values ($1, $2, $3, $4, $5, $6, $7, $8) +returning id; + +-- name: GetLoadById :one +select l.id as id, + c.id as cartridge_id, + c.name as cartridge_name, + c.meta as cartridge_meta, + l.col, + p.id as powder_id, + p.name as powder_name, + p.meta as powder_meta, + l.powder_gr, + pr.id as primer_id, + pr.name as primer_name, + pr.meta as primer_meta +from public.loads l + join cartridges c on l.cartridge_id = c.id + join bullets b on l.bullet_id = b.id + join powders p on l.powder_id = p.id + join primers pr on l.primer_id = pr.id +where l.id = $1; + +-- name: TotalLoads :one +select count(*) +from loads; + +-- name: GetLoads :many +select l.id as id, + l.col, + l.powder_gr, + c.id as cartridge_id, + c.name as cartridge_name, + c.meta as cartridge_meta, + p.id as powder_id, + p.name as powder_name, + p.meta as powder_meta, + p_m.name as powder_manufacturer_name, + p_m.url as powder_manufacturer_url, + pr.id as primer_id, + pr.name as primer_name, + pr.meta as primer_meta, + pr_m.name as primer_manufacturer_name, + pr_m.url as primer_manufacturer_url, + b.id as bullet_id, + b.name as bullet_name, + b.meta as bullet_meta, + b.weight as bullet_weight, + b.diameter as bullet_diameter, + b_m.name as bullet_manufacturer_name, + b_m.url as bullet_manufacturer_url +from public.loads l + join cartridges c on l.cartridge_id = c.id + join bullets b on l.bullet_id = b.id + join manufacturers b_m on b.manufacturer_id = b_m.id + join powders p on l.powder_id = p.id + join manufacturers p_m on p.manufacturer_id = p_m.id + join primers pr on l.primer_id = pr.id + join manufacturers pr_m on pr.manufacturer_id = pr_m.id +where 1 = 1 + + -- Loose Search By Name + AND (CASE WHEN @search_cartridge::bool THEN lower(c.name) like lower(@cartridge) ELSE TRUE END) + AND (CASE WHEN @search_bullet::bool THEN lower(b.name) like lower(@bullet) ELSE TRUE END) + AND (CASE WHEN @search_bullet_manufacturer::bool THEN lower(b_m.name) like lower(@bullet_manufacturer) ELSE TRUE END) + AND (CASE WHEN @search_primer::bool THEN lower(pr.name) like lower(@primer) ELSE TRUE END) + AND (CASE WHEN @search_primer_manufacturer::bool THEN lower(pr_m.name) like lower(@prime_manufacturer) ELSE TRUE END) + AND (CASE WHEN @search_powder::bool THEN lower(p.name) like lower(@powder) ELSE TRUE END) + AND (CASE WHEN @search_powder_manufacturer::bool THEN lower(p_m.name) like lower(@powder_manufacturer) ELSE TRUE END) + + -- Exact Search By ID + AND (CASE WHEN @search_cartridge_id::bool THEN c.id = @cartridge_id::uuid ELSE TRUE END) + AND (CASE WHEN @search_bullet_id::bool THEN b.id = @bullet_id::uuid ELSE TRUE END) + AND (CASE WHEN @search_primer_id::bool THEN pr.id = @primer_id::uuid ELSE TRUE END) + AND (CASE WHEN @search_powder_id::bool THEN p.id = @powder_id::uuid ELSE TRUE END) + +order by @order_by +limit $1 offset $2; \ No newline at end of file diff --git a/backend/models/loads/db.go b/backend/models/loads/db.go new file mode 100644 index 0000000..36ef31c --- /dev/null +++ b/backend/models/loads/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package loads + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/backend/models/loads/loads.sql.go b/backend/models/loads/loads.sql.go new file mode 100644 index 0000000..4a1d7d7 --- /dev/null +++ b/backend/models/loads/loads.sql.go @@ -0,0 +1,370 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: loads.sql + +package loads + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createCartridge = `-- name: CreateCartridge :one +insert into cartridges (name, meta) +values ($1, $2) +returning id +` + +type CreateCartridgeParams struct { + Name string `json:"name"` + Meta []byte `json:"meta"` +} + +func (q *Queries) CreateCartridge(ctx context.Context, arg CreateCartridgeParams) (pgtype.UUID, error) { + row := q.db.QueryRow(ctx, createCartridge, arg.Name, arg.Meta) + var id pgtype.UUID + err := row.Scan(&id) + return id, err +} + +const createLoad = `-- name: CreateLoad :one +insert into loads (cartridge_id, col, powder_id, powder_gr, primer_id, bullet_id, photo, meta) +values ($1, $2, $3, $4, $5, $6, $7, $8) +returning id +` + +type CreateLoadParams struct { + CartridgeID pgtype.UUID `json:"cartridge_id"` + Col float32 `json:"col"` + PowderID pgtype.UUID `json:"powder_id"` + PowderGr float32 `json:"powder_gr"` + PrimerID pgtype.UUID `json:"primer_id"` + BulletID pgtype.UUID `json:"bullet_id"` + Photo []byte `json:"photo"` + Meta []byte `json:"meta"` +} + +func (q *Queries) CreateLoad(ctx context.Context, arg CreateLoadParams) (pgtype.UUID, error) { + row := q.db.QueryRow(ctx, createLoad, + arg.CartridgeID, + arg.Col, + arg.PowderID, + arg.PowderGr, + arg.PrimerID, + arg.BulletID, + arg.Photo, + arg.Meta, + ) + var id pgtype.UUID + err := row.Scan(&id) + return id, err +} + +const deleteCartridge = `-- name: DeleteCartridge :exec +delete +from cartridges +where id = $1 +` + +func (q *Queries) DeleteCartridge(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteCartridge, id) + return err +} + +const getCartridgeById = `-- name: GetCartridgeById :one +select c.id as id, c.name, c.meta +from cartridges c +where id = $1 +` + +type GetCartridgeByIdRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` +} + +func (q *Queries) GetCartridgeById(ctx context.Context, id pgtype.UUID) (GetCartridgeByIdRow, error) { + row := q.db.QueryRow(ctx, getCartridgeById, id) + var i GetCartridgeByIdRow + err := row.Scan(&i.ID, &i.Name, &i.Meta) + return i, err +} + +const getCartridges = `-- name: GetCartridges :many +select c.id as id, c.name, c.meta +from cartridges c +order by c.name +` + +type GetCartridgesRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` +} + +func (q *Queries) GetCartridges(ctx context.Context) ([]GetCartridgesRow, error) { + rows, err := q.db.Query(ctx, getCartridges) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetCartridgesRow + for rows.Next() { + var i GetCartridgesRow + if err := rows.Scan(&i.ID, &i.Name, &i.Meta); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getLoadById = `-- name: GetLoadById :one +select l.id as id, + c.id as cartridge_id, + c.name as cartridge_name, + c.meta as cartridge_meta, + l.col, + p.id as powder_id, + p.name as powder_name, + p.meta as powder_meta, + l.powder_gr, + pr.id as primer_id, + pr.name as primer_name, + pr.meta as primer_meta +from public.loads l + join cartridges c on l.cartridge_id = c.id + join bullets b on l.bullet_id = b.id + join powders p on l.powder_id = p.id + join primers pr on l.primer_id = pr.id +where l.id = $1 +` + +type GetLoadByIdRow struct { + ID pgtype.UUID `json:"id"` + CartridgeID pgtype.UUID `json:"cartridge_id"` + CartridgeName string `json:"cartridge_name"` + CartridgeMeta []byte `json:"cartridge_meta"` + Col float32 `json:"col"` + PowderID pgtype.UUID `json:"powder_id"` + PowderName string `json:"powder_name"` + PowderMeta []byte `json:"powder_meta"` + PowderGr float32 `json:"powder_gr"` + PrimerID pgtype.UUID `json:"primer_id"` + PrimerName string `json:"primer_name"` + PrimerMeta []byte `json:"primer_meta"` +} + +func (q *Queries) GetLoadById(ctx context.Context, id pgtype.UUID) (GetLoadByIdRow, error) { + row := q.db.QueryRow(ctx, getLoadById, id) + var i GetLoadByIdRow + err := row.Scan( + &i.ID, + &i.CartridgeID, + &i.CartridgeName, + &i.CartridgeMeta, + &i.Col, + &i.PowderID, + &i.PowderName, + &i.PowderMeta, + &i.PowderGr, + &i.PrimerID, + &i.PrimerName, + &i.PrimerMeta, + ) + return i, err +} + +const getLoads = `-- name: GetLoads :many +select l.id as id, + l.col, + l.powder_gr, + c.id as cartridge_id, + c.name as cartridge_name, + c.meta as cartridge_meta, + p.id as powder_id, + p.name as powder_name, + p.meta as powder_meta, + p_m.name as powder_manufacturer_name, + p_m.url as powder_manufacturer_url, + pr.id as primer_id, + pr.name as primer_name, + pr.meta as primer_meta, + pr_m.name as primer_manufacturer_name, + pr_m.url as primer_manufacturer_url, + b.id as bullet_id, + b.name as bullet_name, + b.meta as bullet_meta, + b.weight as bullet_weight, + b.diameter as bullet_diameter, + b_m.name as bullet_manufacturer_name, + b_m.url as bullet_manufacturer_url +from public.loads l + join cartridges c on l.cartridge_id = c.id + join bullets b on l.bullet_id = b.id + join manufacturers b_m on b.manufacturer_id = b_m.id + join powders p on l.powder_id = p.id + join manufacturers p_m on p.manufacturer_id = p_m.id + join primers pr on l.primer_id = pr.id + join manufacturers pr_m on pr.manufacturer_id = pr_m.id +where 1 = 1 + + -- Loose Search By Name + AND (CASE WHEN $3::bool THEN lower(c.name) like lower($4) ELSE TRUE END) + AND (CASE WHEN $5::bool THEN lower(b.name) like lower($6) ELSE TRUE END) + AND (CASE WHEN $7::bool THEN lower(b_m.name) like lower($8) ELSE TRUE END) + AND (CASE WHEN $9::bool THEN lower(pr.name) like lower($10) ELSE TRUE END) + AND (CASE WHEN $11::bool THEN lower(pr_m.name) like lower($12) ELSE TRUE END) + AND (CASE WHEN $13::bool THEN lower(p.name) like lower($14) ELSE TRUE END) + AND (CASE WHEN $15::bool THEN lower(p_m.name) like lower($16) ELSE TRUE END) + + -- Exact Search By ID + AND (CASE WHEN $17::bool THEN c.id = $18::uuid ELSE TRUE END) + AND (CASE WHEN $19::bool THEN b.id = $20::uuid ELSE TRUE END) + AND (CASE WHEN $21::bool THEN pr.id = $22::uuid ELSE TRUE END) + AND (CASE WHEN $23::bool THEN p.id = $24::uuid ELSE TRUE END) + +order by $25 +limit $1 offset $2 +` + +type GetLoadsParams struct { + Limit int32 `json:"limit"` + Offset int32 `json:"offset"` + SearchCartridge bool `json:"search_cartridge"` + Cartridge string `json:"cartridge"` + SearchBullet bool `json:"search_bullet"` + Bullet string `json:"bullet"` + SearchBulletManufacturer bool `json:"search_bullet_manufacturer"` + BulletManufacturer string `json:"bullet_manufacturer"` + SearchPrimer bool `json:"search_primer"` + Primer string `json:"primer"` + SearchPrimerManufacturer bool `json:"search_primer_manufacturer"` + PrimeManufacturer string `json:"prime_manufacturer"` + SearchPowder bool `json:"search_powder"` + Powder string `json:"powder"` + SearchPowderManufacturer bool `json:"search_powder_manufacturer"` + PowderManufacturer string `json:"powder_manufacturer"` + SearchCartridgeID bool `json:"search_cartridge_id"` + CartridgeID pgtype.UUID `json:"cartridge_id"` + SearchBulletID bool `json:"search_bullet_id"` + BulletID pgtype.UUID `json:"bullet_id"` + SearchPrimerID bool `json:"search_primer_id"` + PrimerID pgtype.UUID `json:"primer_id"` + SearchPowderID bool `json:"search_powder_id"` + PowderID pgtype.UUID `json:"powder_id"` + OrderBy interface{} `json:"order_by"` +} + +type GetLoadsRow struct { + ID pgtype.UUID `json:"id"` + Col float32 `json:"col"` + PowderGr float32 `json:"powder_gr"` + CartridgeID pgtype.UUID `json:"cartridge_id"` + CartridgeName string `json:"cartridge_name"` + CartridgeMeta []byte `json:"cartridge_meta"` + PowderID pgtype.UUID `json:"powder_id"` + PowderName string `json:"powder_name"` + PowderMeta []byte `json:"powder_meta"` + PowderManufacturerName string `json:"powder_manufacturer_name"` + PowderManufacturerUrl pgtype.Text `json:"powder_manufacturer_url"` + PrimerID pgtype.UUID `json:"primer_id"` + PrimerName string `json:"primer_name"` + PrimerMeta []byte `json:"primer_meta"` + PrimerManufacturerName string `json:"primer_manufacturer_name"` + PrimerManufacturerUrl pgtype.Text `json:"primer_manufacturer_url"` + BulletID pgtype.UUID `json:"bullet_id"` + BulletName string `json:"bullet_name"` + BulletMeta []byte `json:"bullet_meta"` + BulletWeight int32 `json:"bullet_weight"` + BulletDiameter int32 `json:"bullet_diameter"` + BulletManufacturerName string `json:"bullet_manufacturer_name"` + BulletManufacturerUrl pgtype.Text `json:"bullet_manufacturer_url"` +} + +func (q *Queries) GetLoads(ctx context.Context, arg GetLoadsParams) ([]GetLoadsRow, error) { + rows, err := q.db.Query(ctx, getLoads, + arg.Limit, + arg.Offset, + arg.SearchCartridge, + arg.Cartridge, + arg.SearchBullet, + arg.Bullet, + arg.SearchBulletManufacturer, + arg.BulletManufacturer, + arg.SearchPrimer, + arg.Primer, + arg.SearchPrimerManufacturer, + arg.PrimeManufacturer, + arg.SearchPowder, + arg.Powder, + arg.SearchPowderManufacturer, + arg.PowderManufacturer, + arg.SearchCartridgeID, + arg.CartridgeID, + arg.SearchBulletID, + arg.BulletID, + arg.SearchPrimerID, + arg.PrimerID, + arg.SearchPowderID, + arg.PowderID, + arg.OrderBy, + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetLoadsRow + for rows.Next() { + var i GetLoadsRow + if err := rows.Scan( + &i.ID, + &i.Col, + &i.PowderGr, + &i.CartridgeID, + &i.CartridgeName, + &i.CartridgeMeta, + &i.PowderID, + &i.PowderName, + &i.PowderMeta, + &i.PowderManufacturerName, + &i.PowderManufacturerUrl, + &i.PrimerID, + &i.PrimerName, + &i.PrimerMeta, + &i.PrimerManufacturerName, + &i.PrimerManufacturerUrl, + &i.BulletID, + &i.BulletName, + &i.BulletMeta, + &i.BulletWeight, + &i.BulletDiameter, + &i.BulletManufacturerName, + &i.BulletManufacturerUrl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const totalLoads = `-- name: TotalLoads :one +select count(*) +from loads +` + +func (q *Queries) TotalLoads(ctx context.Context) (int64, error) { + row := q.db.QueryRow(ctx, totalLoads) + var count int64 + err := row.Scan(&count) + return count, err +} diff --git a/backend/models/loads/models.go b/backend/models/loads/models.go new file mode 100644 index 0000000..6aae061 --- /dev/null +++ b/backend/models/loads/models.go @@ -0,0 +1,65 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package loads + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type Bullet struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Weight int32 `json:"weight"` + Diameter int32 `json:"diameter"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type Cartridge struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + CreatedAt pgtype.Timestamp `json:"created_at"` + Meta []byte `json:"meta"` +} + +type Load struct { + ID pgtype.UUID `json:"id"` + CartridgeID pgtype.UUID `json:"cartridge_id"` + Col float32 `json:"col"` + PowderID pgtype.UUID `json:"powder_id"` + PowderGr float32 `json:"powder_gr"` + PrimerID pgtype.UUID `json:"primer_id"` + BulletID pgtype.UUID `json:"bullet_id"` + Photo []byte `json:"photo"` + CreatedAt pgtype.Timestamp `json:"created_at"` + Meta []byte `json:"meta"` +} + +type Manufacturer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Url pgtype.Text `json:"url"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type Powder struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type Primer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} diff --git a/backend/models/manufacturer-schema.sql b/backend/models/manufacturer-schema.sql new file mode 100644 index 0000000..101db95 --- /dev/null +++ b/backend/models/manufacturer-schema.sql @@ -0,0 +1,7 @@ +create table manufacturers +( + id uuid primary key , + name varchar(255) not null, + url varchar(255), + created_at timestamp default NOW() not null +); diff --git a/backend/models/manufacturer.sql b/backend/models/manufacturer.sql new file mode 100644 index 0000000..78c1276 --- /dev/null +++ b/backend/models/manufacturer.sql @@ -0,0 +1,23 @@ +-- name: GetAll :many +select id, name, url, created_at +from manufacturers +order by name; + +-- name: CreateManufacturer :exec +insert into manufacturers (id, name, url) values ($1, $2, $3); + +-- name: UpdateManufacturer :exec +update manufacturers +set name=$1, + url=$2 +where id = $3; + +-- name: GetById :one +select id, name, url, created_at +from manufacturers +where id = $1; + +-- name: DeleteById :exec +delete +from manufacturers +where id = $1; \ No newline at end of file diff --git a/backend/models/manufacturer/db.go b/backend/models/manufacturer/db.go new file mode 100644 index 0000000..f4f84f6 --- /dev/null +++ b/backend/models/manufacturer/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package manufacturer + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/backend/models/manufacturer/manufacturer.sql.go b/backend/models/manufacturer/manufacturer.sql.go new file mode 100644 index 0000000..9d4d5cd --- /dev/null +++ b/backend/models/manufacturer/manufacturer.sql.go @@ -0,0 +1,105 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: manufacturer.sql + +package manufacturer + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createManufacturer = `-- name: CreateManufacturer :exec +insert into manufacturers (id, name, url) values ($1, $2, $3) +` + +type CreateManufacturerParams struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Url pgtype.Text `json:"url"` +} + +func (q *Queries) CreateManufacturer(ctx context.Context, arg CreateManufacturerParams) error { + _, err := q.db.Exec(ctx, createManufacturer, arg.ID, arg.Name, arg.Url) + return err +} + +const deleteById = `-- name: DeleteById :exec +delete +from manufacturers +where id = $1 +` + +func (q *Queries) DeleteById(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteById, id) + return err +} + +const getAll = `-- name: GetAll :many +select id, name, url, created_at +from manufacturers +order by name +` + +func (q *Queries) GetAll(ctx context.Context) ([]Manufacturer, error) { + rows, err := q.db.Query(ctx, getAll) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Manufacturer + for rows.Next() { + var i Manufacturer + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Url, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getById = `-- name: GetById :one +select id, name, url, created_at +from manufacturers +where id = $1 +` + +func (q *Queries) GetById(ctx context.Context, id pgtype.UUID) (Manufacturer, error) { + row := q.db.QueryRow(ctx, getById, id) + var i Manufacturer + err := row.Scan( + &i.ID, + &i.Name, + &i.Url, + &i.CreatedAt, + ) + return i, err +} + +const updateManufacturer = `-- name: UpdateManufacturer :exec +update manufacturers +set name=$1, + url=$2 +where id = $3 +` + +type UpdateManufacturerParams struct { + Name string `json:"name"` + Url pgtype.Text `json:"url"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateManufacturer(ctx context.Context, arg UpdateManufacturerParams) error { + _, err := q.db.Exec(ctx, updateManufacturer, arg.Name, arg.Url, arg.ID) + return err +} diff --git a/backend/models/manufacturer/models.go b/backend/models/manufacturer/models.go new file mode 100644 index 0000000..2203614 --- /dev/null +++ b/backend/models/manufacturer/models.go @@ -0,0 +1,16 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package manufacturer + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type Manufacturer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Url pgtype.Text `json:"url"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} diff --git a/backend/models/powder-schema.sql b/backend/models/powder-schema.sql new file mode 100644 index 0000000..aaa1843 --- /dev/null +++ b/backend/models/powder-schema.sql @@ -0,0 +1,19 @@ +create table powders +( + id uuid primary key, + name varchar(255) not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null, + constraint powders_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id) +); + +create table manufacturers +( + id uuid primary key , + name varchar(255) not null, + url varchar(255), + created_at timestamp default NOW() not null +); diff --git a/backend/models/powder.sql b/backend/models/powder.sql new file mode 100644 index 0000000..b2e0f65 --- /dev/null +++ b/backend/models/powder.sql @@ -0,0 +1,41 @@ +-- name: GetPowderById :one +select powders.id as id, + powders.name as name, + powders.created_at, + powders.photo, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from powders + join manufacturers m on m.id = powders.manufacturer_id +where powders.id = $1; + +-- name: GetPowders :many +select powders.id as id, + powders.name as name, + powders.created_at, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from powders + join manufacturers m on m.id = powders.manufacturer_id +order by powders.name desc; + +-- name: DeletePowder :exec +DELETE +FROM powders +WHERE id = $1; + +-- name: InsertPowder :exec +insert into powders (id, name, manufacturer_id, meta, photo) +values ($1, $2, $3, $4, $5); + +-- name: UpdatePowder :exec +update powders +set name = $1, + manufacturer_id = $2, + meta = $3, + photo = $4 +where id = $5; \ No newline at end of file diff --git a/backend/models/powder/db.go b/backend/models/powder/db.go new file mode 100644 index 0000000..4cc6e42 --- /dev/null +++ b/backend/models/powder/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package powder + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/backend/models/powder/models.go b/backend/models/powder/models.go new file mode 100644 index 0000000..cf42d90 --- /dev/null +++ b/backend/models/powder/models.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package powder + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type Manufacturer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Url pgtype.Text `json:"url"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type Powder struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} diff --git a/backend/models/powder/powder.sql.go b/backend/models/powder/powder.sql.go new file mode 100644 index 0000000..b47a5bc --- /dev/null +++ b/backend/models/powder/powder.sql.go @@ -0,0 +1,167 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: powder.sql + +package powder + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const deletePowder = `-- name: DeletePowder :exec +DELETE +FROM powders +WHERE id = $1 +` + +func (q *Queries) DeletePowder(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deletePowder, id) + return err +} + +const getPowderById = `-- name: GetPowderById :one +select powders.id as id, + powders.name as name, + powders.created_at, + powders.photo, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from powders + join manufacturers m on m.id = powders.manufacturer_id +where powders.id = $1 +` + +type GetPowderByIdRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + CreatedAt pgtype.Timestamp `json:"created_at"` + Photo []byte `json:"photo"` + Meta []byte `json:"meta"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ManufacturerName string `json:"manufacturer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) GetPowderById(ctx context.Context, id pgtype.UUID) (GetPowderByIdRow, error) { + row := q.db.QueryRow(ctx, getPowderById, id) + var i GetPowderByIdRow + err := row.Scan( + &i.ID, + &i.Name, + &i.CreatedAt, + &i.Photo, + &i.Meta, + &i.ManufacturerID, + &i.ManufacturerName, + &i.ManufacturerUrl, + ) + return i, err +} + +const getPowders = `-- name: GetPowders :many +select powders.id as id, + powders.name as name, + powders.created_at, + meta, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +from powders + join manufacturers m on m.id = powders.manufacturer_id +order by powders.name desc +` + +type GetPowdersRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + CreatedAt pgtype.Timestamp `json:"created_at"` + Meta []byte `json:"meta"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ManufacturerName string `json:"manufacturer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) GetPowders(ctx context.Context) ([]GetPowdersRow, error) { + rows, err := q.db.Query(ctx, getPowders) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetPowdersRow + for rows.Next() { + var i GetPowdersRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.CreatedAt, + &i.Meta, + &i.ManufacturerID, + &i.ManufacturerName, + &i.ManufacturerUrl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertPowder = `-- name: InsertPowder :exec +insert into powders (id, name, manufacturer_id, meta, photo) +values ($1, $2, $3, $4, $5) +` + +type InsertPowderParams struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` +} + +func (q *Queries) InsertPowder(ctx context.Context, arg InsertPowderParams) error { + _, err := q.db.Exec(ctx, insertPowder, + arg.ID, + arg.Name, + arg.ManufacturerID, + arg.Meta, + arg.Photo, + ) + return err +} + +const updatePowder = `-- name: UpdatePowder :exec +update powders +set name = $1, + manufacturer_id = $2, + meta = $3, + photo = $4 +where id = $5 +` + +type UpdatePowderParams struct { + Name string `json:"name"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdatePowder(ctx context.Context, arg UpdatePowderParams) error { + _, err := q.db.Exec(ctx, updatePowder, + arg.Name, + arg.ManufacturerID, + arg.Meta, + arg.Photo, + arg.ID, + ) + return err +} diff --git a/backend/models/primers-schema.sql b/backend/models/primers-schema.sql new file mode 100644 index 0000000..0b08e39 --- /dev/null +++ b/backend/models/primers-schema.sql @@ -0,0 +1,19 @@ +create table primers +( + id uuid primary key, + name varchar(255) not null, + meta json, + photo bytea, + manufacturer_id uuid not null, + created_at timestamp default NOW() not null, + constraint primers_manufacturers_id_fk + foreign key (manufacturer_id) references manufacturers (id) +); + +create table manufacturers +( + id uuid primary key , + name varchar(255) not null, + url varchar(255), + created_at timestamp default NOW() not null +); diff --git a/backend/models/primers.sql b/backend/models/primers.sql new file mode 100644 index 0000000..3a03c8c --- /dev/null +++ b/backend/models/primers.sql @@ -0,0 +1,41 @@ +-- name: GetPrimers :many +SELECT primers.id as id, + primers.name as name, + primers.meta as meta, + primers.created_at as created_at, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +FROM primers + join manufacturers m on m.id = primers.manufacturer_id +ORDER BY primers.name desc; + +-- name: GetPrimerById :one +SELECT primers.id as id, + primers.name as name, + primers.meta as meta, + primers.photo as photo, + primers.created_at as created_at, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +FROM primers + join manufacturers m on m.id = primers.manufacturer_id +WHERE primers.id = $1; + +-- name: InsertPrimer :exec +INSERT INTO primers (id, name, manufacturer_id, meta, photo) +VALUES ($1, $2, $3, $4, $5); + +-- name: DeletePrimer :exec +DELETE +FROM primers +WHERE id = $1; + +-- name: UpdatePrimer :exec +UPDATE primers +SET name = $1, + manufacturer_id = $2, + meta = $3, + photo = $4 +WHERE id = $5; diff --git a/backend/models/primers/db.go b/backend/models/primers/db.go new file mode 100644 index 0000000..d9f879e --- /dev/null +++ b/backend/models/primers/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package primers + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/backend/models/primers/models.go b/backend/models/primers/models.go new file mode 100644 index 0000000..2fc1e50 --- /dev/null +++ b/backend/models/primers/models.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package primers + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type Manufacturer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Url pgtype.Text `json:"url"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} + +type Primer struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + CreatedAt pgtype.Timestamp `json:"created_at"` +} diff --git a/backend/models/primers/primers.sql.go b/backend/models/primers/primers.sql.go new file mode 100644 index 0000000..400a5e8 --- /dev/null +++ b/backend/models/primers/primers.sql.go @@ -0,0 +1,167 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: primers.sql + +package primers + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const deletePrimer = `-- name: DeletePrimer :exec +DELETE +FROM primers +WHERE id = $1 +` + +func (q *Queries) DeletePrimer(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deletePrimer, id) + return err +} + +const getPrimerById = `-- name: GetPrimerById :one +SELECT primers.id as id, + primers.name as name, + primers.meta as meta, + primers.photo as photo, + primers.created_at as created_at, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +FROM primers + join manufacturers m on m.id = primers.manufacturer_id +WHERE primers.id = $1 +` + +type GetPrimerByIdRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + CreatedAt pgtype.Timestamp `json:"created_at"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ManufacturerName string `json:"manufacturer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) GetPrimerById(ctx context.Context, id pgtype.UUID) (GetPrimerByIdRow, error) { + row := q.db.QueryRow(ctx, getPrimerById, id) + var i GetPrimerByIdRow + err := row.Scan( + &i.ID, + &i.Name, + &i.Meta, + &i.Photo, + &i.CreatedAt, + &i.ManufacturerID, + &i.ManufacturerName, + &i.ManufacturerUrl, + ) + return i, err +} + +const getPrimers = `-- name: GetPrimers :many +SELECT primers.id as id, + primers.name as name, + primers.meta as meta, + primers.created_at as created_at, + m.id as manufacturer_id, + m.name as manufacturer_name, + m.url as manufacturer_url +FROM primers + join manufacturers m on m.id = primers.manufacturer_id +ORDER BY primers.name desc +` + +type GetPrimersRow struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Meta []byte `json:"meta"` + CreatedAt pgtype.Timestamp `json:"created_at"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + ManufacturerName string `json:"manufacturer_name"` + ManufacturerUrl pgtype.Text `json:"manufacturer_url"` +} + +func (q *Queries) GetPrimers(ctx context.Context) ([]GetPrimersRow, error) { + rows, err := q.db.Query(ctx, getPrimers) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetPrimersRow + for rows.Next() { + var i GetPrimersRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Meta, + &i.CreatedAt, + &i.ManufacturerID, + &i.ManufacturerName, + &i.ManufacturerUrl, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertPrimer = `-- name: InsertPrimer :exec +INSERT INTO primers (id, name, manufacturer_id, meta, photo) +VALUES ($1, $2, $3, $4, $5) +` + +type InsertPrimerParams struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` +} + +func (q *Queries) InsertPrimer(ctx context.Context, arg InsertPrimerParams) error { + _, err := q.db.Exec(ctx, insertPrimer, + arg.ID, + arg.Name, + arg.ManufacturerID, + arg.Meta, + arg.Photo, + ) + return err +} + +const updatePrimer = `-- name: UpdatePrimer :exec +UPDATE primers +SET name = $1, + manufacturer_id = $2, + meta = $3, + photo = $4 +WHERE id = $5 +` + +type UpdatePrimerParams struct { + Name string `json:"name"` + ManufacturerID pgtype.UUID `json:"manufacturer_id"` + Meta []byte `json:"meta"` + Photo []byte `json:"photo"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdatePrimer(ctx context.Context, arg UpdatePrimerParams) error { + _, err := q.db.Exec(ctx, updatePrimer, + arg.Name, + arg.ManufacturerID, + arg.Meta, + arg.Photo, + arg.ID, + ) + return err +} diff --git a/backend/models/sqlc.yaml b/backend/models/sqlc.yaml new file mode 100644 index 0000000..09fe002 --- /dev/null +++ b/backend/models/sqlc.yaml @@ -0,0 +1,47 @@ +version: "2" +sql: +- schema: "bullets-schema.sql" + engine: "postgresql" + queries: "bullets.sql" + gen: + go: + package: "bullets" + out: "bullets" + emit_json_tags: true + sql_package: "pgx/v5" +- schema: "manufacturer-schema.sql" + engine: "postgresql" + queries: "manufacturer.sql" + gen: + go: + package: "manufacturer" + out: "manufacturer" + emit_json_tags: true + sql_package: "pgx/v5" +- schema: "primers-schema.sql" + engine: "postgresql" + queries: "primers.sql" + gen: + go: + package: "primers" + out: "primers" + emit_json_tags: true + sql_package: "pgx/v5" +- schema: "powder-schema.sql" + engine: "postgresql" + queries: "powder.sql" + gen: + go: + package: "powder" + out: "powder" + emit_json_tags: true + sql_package: "pgx/v5" +- schema: "loads-schema.sql" + engine: "postgresql" + queries: "loads.sql" + gen: + go: + package: "loads" + out: "loads" + emit_json_tags: true + sql_package: "pgx/v5" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/frontend/.env b/frontend/.env new file mode 100644 index 0000000..257d47d --- /dev/null +++ b/frontend/.env @@ -0,0 +1 @@ +VITE_API=http://localhost:8080 \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 0000000..c5ddcef --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +v22.14.0 \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..ef72fd5 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,18 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..436cbcc --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2951 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@primeuix/themes": "^1.0.0", + "@tailwindcss/postcss": "^4.0.15", + "@tailwindcss/vite": "^4.0.15", + "@vuelidate/core": "^2.0.3", + "@vuelidate/validators": "^2.0.4", + "axios": "^1.8.4", + "primevue": "^4.3.2", + "sass": "^1.86.0", + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.21", + "tailwindcss": "^4.0.15", + "typescript": "^5.8.2", + "vite": "^6.2.2", + "vue-tsc": "^2.2.8" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@primeuix/styled": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.5.0.tgz", + "integrity": "sha512-k5CTQ+10cXIXxZTep7sktmYe8lJkjmUaFVDAc1OCsWTJR+bhBy/s6zWIatGljVtuf3RmTSxtlrHQeFLjPmdUNQ==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.5.0" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primeuix/styles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@primeuix/styles/-/styles-1.0.0.tgz", + "integrity": "sha512-j/TlbqihLNMP37zFNjxac5dTRaQEf5Ldrv0P7NwKigCCc/+MI5j4MddxDw1LnxkGhWCJ1Gjbt9uwyQteWtSv7A==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.5.0" + } + }, + "node_modules/@primeuix/themes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@primeuix/themes/-/themes-1.0.0.tgz", + "integrity": "sha512-fxUgcAP9H6FeytbE8c4QvRt8aBnoyZJqvtnnVwHT8PHr1dNSnC1nYKGrXpebcx3SpNy9Hp9oVidGsl6u61+pXQ==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.5.0" + } + }, + "node_modules/@primeuix/utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.5.1.tgz", + "integrity": "sha512-/bYirtF3gJOGrRQfQ5tUyQOLEria7wg/UCqvpIydTAxLmj/UWgWwh2kAjYVp49eldm1+2sk4+TDkbAz8XcPpew==", + "license": "MIT", + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primevue/core": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@primevue/core/-/core-4.3.2.tgz", + "integrity": "sha512-8ghF6PdYKw77rLFZRl8C8kLHMKFMPg8f7xiPQIvLG0Cxl+FKsBrGp0xQxnEXsp2+6NX3Pv1jysrQjxSnuufz4w==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.5.0", + "@primeuix/utils": "^0.5.1" + }, + "engines": { + "node": ">=12.11.0" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@primevue/icons": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@primevue/icons/-/icons-4.3.2.tgz", + "integrity": "sha512-sT9MZQ7GNmi/pfpMFdHejt3s3Q5kIN6pV/xi5n21KbpQsgsXnQBYq/fyx+sfKvbUfDvYYIpKDSWXj6RYR3zIdw==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.5.1", + "@primevue/core": "4.3.2" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz", + "integrity": "sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.36.0.tgz", + "integrity": "sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.36.0.tgz", + "integrity": "sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.36.0.tgz", + "integrity": "sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.36.0.tgz", + "integrity": "sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.36.0.tgz", + "integrity": "sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.36.0.tgz", + "integrity": "sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.36.0.tgz", + "integrity": "sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.36.0.tgz", + "integrity": "sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.36.0.tgz", + "integrity": "sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.36.0.tgz", + "integrity": "sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.36.0.tgz", + "integrity": "sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.36.0.tgz", + "integrity": "sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.36.0.tgz", + "integrity": "sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.36.0.tgz", + "integrity": "sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.36.0.tgz", + "integrity": "sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.36.0.tgz", + "integrity": "sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.36.0.tgz", + "integrity": "sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.36.0.tgz", + "integrity": "sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.0.15.tgz", + "integrity": "sha512-IODaJjNmiasfZX3IoS+4Em3iu0fD2HS0/tgrnkYfW4hyUor01Smnr5eY3jc4rRgaTDrJlDmBTHbFO0ETTDaxWA==", + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "tailwindcss": "4.0.15" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.0.15.tgz", + "integrity": "sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.0.15", + "@tailwindcss/oxide-darwin-arm64": "4.0.15", + "@tailwindcss/oxide-darwin-x64": "4.0.15", + "@tailwindcss/oxide-freebsd-x64": "4.0.15", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.15", + "@tailwindcss/oxide-linux-arm64-gnu": "4.0.15", + "@tailwindcss/oxide-linux-arm64-musl": "4.0.15", + "@tailwindcss/oxide-linux-x64-gnu": "4.0.15", + "@tailwindcss/oxide-linux-x64-musl": "4.0.15", + "@tailwindcss/oxide-win32-arm64-msvc": "4.0.15", + "@tailwindcss/oxide-win32-x64-msvc": "4.0.15" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.15.tgz", + "integrity": "sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.15.tgz", + "integrity": "sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.15.tgz", + "integrity": "sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.15.tgz", + "integrity": "sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.15.tgz", + "integrity": "sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.15.tgz", + "integrity": "sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.15.tgz", + "integrity": "sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.15.tgz", + "integrity": "sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.15.tgz", + "integrity": "sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.15.tgz", + "integrity": "sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.0.15.tgz", + "integrity": "sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.0.15.tgz", + "integrity": "sha512-qyrpoDKIO7wzkRbKCvGLo7gXRjT9/Njf7ZJiJhG4njrfZkvOhjwnaHpYbpxYeDysEg+9pB1R4jcd+vQ7ZUDsmQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.0.15", + "@tailwindcss/oxide": "4.0.15", + "lightningcss": "1.29.2", + "postcss": "^8.4.41", + "tailwindcss": "4.0.15" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.0.15.tgz", + "integrity": "sha512-JRexava80NijI8cTcLXNM3nQL5A0ptTHI8oJLLe8z1MpNB6p5J4WCdJJP8RoyHu8/eB1JzEdbpH86eGfbuaezQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.0.15", + "@tailwindcss/oxide": "4.0.15", + "lightningcss": "1.29.2", + "tailwindcss": "4.0.15" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.3.tgz", + "integrity": "sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.12.tgz", + "integrity": "sha512-RLrFdXEaQBWfSnYGVxvR2WrO6Bub0unkdHYIdC31HzIEqATIuuhRRzYu76iGPZ6OtA4Au1SnW0ZwIqPP217YhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.12" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.12.tgz", + "integrity": "sha512-bUFIKvn2U0AWojOaqf63ER0N/iHIBYZPpNGogfLPQ68F5Eet6FnLlyho7BS0y2HJ1jFhSif7AcuTx1TqsCzRzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.12.tgz", + "integrity": "sha512-HJB73OTJDgPc80K30wxi3if4fSsZZAOScbj2fcicMuOPoOkcf9NNAINb33o+DzhBdF9xTKC1gnPmIRDous5S0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.12", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/language-core": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.8.tgz", + "integrity": "sha512-rrzB0wPGBvcwaSNRriVWdNAbHQWSf0NlGqgKHK5mEkXpefjUlVRP62u03KvwZpvKVjRnBIQ/Lwre+Mx9N6juUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "~2.4.11", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "vue": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "license": "MIT" + }, + "node_modules/@vuelidate/core": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@vuelidate/core/-/core-2.0.3.tgz", + "integrity": "sha512-AN6l7KF7+mEfyWG0doT96z+47ljwPpZfi9/JrNMkOGLFv27XVZvKzRLXlmDPQjPl/wOB1GNnHuc54jlCLRNqGA==", + "license": "MIT", + "dependencies": { + "vue-demi": "^0.13.11" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vuelidate/core/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vuelidate/validators": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@vuelidate/validators/-/validators-2.0.4.tgz", + "integrity": "sha512-odTxtUZ2JpwwiQ10t0QWYJkkYrfd0SyFYhdHH44QQ1jDatlZgTh/KRzrWVmn/ib9Gq7H4hFD4e8ahoo5YlUlDw==", + "license": "MIT", + "dependencies": { + "vue-demi": "^0.13.11" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^2.0.0 || >=3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vuelidate/validators/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/alien-signals": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.6.tgz", + "integrity": "sha512-aITl4ODHNX9mqBqwZWr5oTYP74hemqVGV4KRLSQacjoZIdwNxbedHF656+c4zuGLtRtcowitoXdIfyrXgzniVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001706", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001706.tgz", + "integrity": "sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.123", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", + "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/immutable": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/primevue": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/primevue/-/primevue-4.3.2.tgz", + "integrity": "sha512-TCCLu66k0pW8LmL8ho4w8evPEwc1agCP4ojY5OIsr2pqYepzBqlsSP2GSSv+Kv5EwWqg4ufh0bor+XVJl9sIVw==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.5.0", + "@primeuix/styles": "^1.0.0", + "@primeuix/utils": "^0.5.1", + "@primevue/core": "4.3.2", + "@primevue/icons": "4.3.2" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rollup": { + "version": "4.36.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.36.0.tgz", + "integrity": "sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.36.0", + "@rollup/rollup-android-arm64": "4.36.0", + "@rollup/rollup-darwin-arm64": "4.36.0", + "@rollup/rollup-darwin-x64": "4.36.0", + "@rollup/rollup-freebsd-arm64": "4.36.0", + "@rollup/rollup-freebsd-x64": "4.36.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.36.0", + "@rollup/rollup-linux-arm-musleabihf": "4.36.0", + "@rollup/rollup-linux-arm64-gnu": "4.36.0", + "@rollup/rollup-linux-arm64-musl": "4.36.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.36.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.36.0", + "@rollup/rollup-linux-riscv64-gnu": "4.36.0", + "@rollup/rollup-linux-s390x-gnu": "4.36.0", + "@rollup/rollup-linux-x64-gnu": "4.36.0", + "@rollup/rollup-linux-x64-musl": "4.36.0", + "@rollup/rollup-win32-arm64-msvc": "4.36.0", + "@rollup/rollup-win32-ia32-msvc": "4.36.0", + "@rollup/rollup-win32-x64-msvc": "4.36.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/sass": { + "version": "1.86.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.86.0.tgz", + "integrity": "sha512-zV8vGUld/+mP4KbMLJMX7TyGCuUp7hnkOScgCMsWuHtns8CWBoz+vmEhoGMXsaJrbUP8gj+F1dLvVe79sK8UdA==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.15.tgz", + "integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", + "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", + "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.8.tgz", + "integrity": "sha512-jBYKBNFADTN+L+MdesNX/TB3XuDSyaWynKMDgR+yCSln0GQ9Tfb7JS2lr46s2LiFUT1WsmfWsSvIElyxzOPqcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "~2.4.11", + "@vue/language-core": "2.2.8" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..f39adb7 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,31 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@primeuix/themes": "^1.0.0", + "@tailwindcss/postcss": "^4.0.15", + "@tailwindcss/vite": "^4.0.15", + "@vuelidate/core": "^2.0.3", + "@vuelidate/validators": "^2.0.4", + "axios": "^1.8.4", + "primevue": "^4.3.2", + "sass": "^1.86.0", + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.21", + "tailwindcss": "^4.0.15", + "typescript": "^5.8.2", + "vite": "^6.2.2", + "vue-tsc": "^2.2.8" + } +} diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..8d828dd --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/frontend/src/assets/vue.svg b/frontend/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/frontend/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/axios/index.ts b/frontend/src/axios/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/TopMenubar.vue b/frontend/src/components/TopMenubar.vue new file mode 100644 index 0000000..adcb0ca --- /dev/null +++ b/frontend/src/components/TopMenubar.vue @@ -0,0 +1,109 @@ + + \ No newline at end of file diff --git a/frontend/src/lib/icons.ts b/frontend/src/lib/icons.ts new file mode 100644 index 0000000..912210e --- /dev/null +++ b/frontend/src/lib/icons.ts @@ -0,0 +1,8 @@ +export const icons = { + add: 'fa-thin fa-sharp fa-plus', + edit: 'fa-thin fa-sharp fa-pencil', + list: 'fa-thin fa-sharp fa-list', + upload: 'fa-thin fa-sharp fa-upload', + save: 'fa-thin fa-sharp fa-floppy-disk', + delete: 'fa-thin fa-sharp fa-trash', +} \ No newline at end of file diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..09ea8e4 --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,20 @@ +import { createApp } from 'vue' +import Aura from '@primeuix/themes/aura' + +import App from './App.vue' +import router from './router' +import PrimeVue from 'primevue/config' + +const app = createApp(App) +app.use(router) +app.use(PrimeVue, { + theme: { + preset: Aura, + }, +}) + +import ToastService from 'primevue/toastservice' + +app.use(ToastService) + +app.mount('#app') diff --git a/frontend/src/pages/Index.vue b/frontend/src/pages/Index.vue new file mode 100644 index 0000000..0eddd1d --- /dev/null +++ b/frontend/src/pages/Index.vue @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/bullets/Add.vue b/frontend/src/pages/bullets/Add.vue new file mode 100644 index 0000000..cf92919 --- /dev/null +++ b/frontend/src/pages/bullets/Add.vue @@ -0,0 +1,166 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/bullets/Edit.vue b/frontend/src/pages/bullets/Edit.vue new file mode 100644 index 0000000..3c4806c --- /dev/null +++ b/frontend/src/pages/bullets/Edit.vue @@ -0,0 +1,178 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/bullets/List.vue b/frontend/src/pages/bullets/List.vue new file mode 100644 index 0000000..4481896 --- /dev/null +++ b/frontend/src/pages/bullets/List.vue @@ -0,0 +1,48 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/loads/Add.vue b/frontend/src/pages/loads/Add.vue new file mode 100644 index 0000000..1b0bd3e --- /dev/null +++ b/frontend/src/pages/loads/Add.vue @@ -0,0 +1,297 @@ + + + \ No newline at end of file diff --git a/frontend/src/pages/loads/Search.vue b/frontend/src/pages/loads/Search.vue new file mode 100644 index 0000000..3084d7c --- /dev/null +++ b/frontend/src/pages/loads/Search.vue @@ -0,0 +1,140 @@ + + + diff --git a/frontend/src/pages/manufacturers/Add.vue b/frontend/src/pages/manufacturers/Add.vue new file mode 100644 index 0000000..8b8c4b4 --- /dev/null +++ b/frontend/src/pages/manufacturers/Add.vue @@ -0,0 +1,72 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/manufacturers/Edit.vue b/frontend/src/pages/manufacturers/Edit.vue new file mode 100644 index 0000000..f0586db --- /dev/null +++ b/frontend/src/pages/manufacturers/Edit.vue @@ -0,0 +1,100 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/manufacturers/List.vue b/frontend/src/pages/manufacturers/List.vue new file mode 100644 index 0000000..fee59ab --- /dev/null +++ b/frontend/src/pages/manufacturers/List.vue @@ -0,0 +1,56 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/powders/Add.vue b/frontend/src/pages/powders/Add.vue new file mode 100644 index 0000000..ba26378 --- /dev/null +++ b/frontend/src/pages/powders/Add.vue @@ -0,0 +1,95 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/powders/Edit.vue b/frontend/src/pages/powders/Edit.vue new file mode 100644 index 0000000..539d043 --- /dev/null +++ b/frontend/src/pages/powders/Edit.vue @@ -0,0 +1,133 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/powders/List.vue b/frontend/src/pages/powders/List.vue new file mode 100644 index 0000000..d5ac80f --- /dev/null +++ b/frontend/src/pages/powders/List.vue @@ -0,0 +1,57 @@ + + \ No newline at end of file diff --git a/frontend/src/pages/primers/Add.vue b/frontend/src/pages/primers/Add.vue new file mode 100644 index 0000000..5e350ed --- /dev/null +++ b/frontend/src/pages/primers/Add.vue @@ -0,0 +1,93 @@ + + diff --git a/frontend/src/pages/primers/Edit.vue b/frontend/src/pages/primers/Edit.vue new file mode 100644 index 0000000..51395e9 --- /dev/null +++ b/frontend/src/pages/primers/Edit.vue @@ -0,0 +1,142 @@ + + diff --git a/frontend/src/pages/primers/List.vue b/frontend/src/pages/primers/List.vue new file mode 100644 index 0000000..852efe3 --- /dev/null +++ b/frontend/src/pages/primers/List.vue @@ -0,0 +1,61 @@ + + \ No newline at end of file diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 0000000..0cef437 --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,71 @@ +import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router' + +const routes = [ + { + path: '/', + component: () => import('../pages/Index.vue'), + }, + { + path: '/bullets/add', + component: () => import('../pages/bullets/Add.vue'), + }, + { + path: '/bullets/edit/:id', + component: () => import('../pages/bullets/Edit.vue'), + }, + { + path: '/bullets', + component: () => import('../pages/bullets/List.vue'), + }, + { + path: '/powders', + component: () => import('../pages/powders/List.vue'), + }, + { + path: '/powders/add', + component: () => import('../pages/powders/Add.vue'), + }, + { + path: '/powders/edit/:id', + component: () => import('../pages/powders/Edit.vue'), + }, + { + path: '/primers', + component: () => import('../pages/primers/List.vue'), + }, + { + path: '/primers/add', + component: () => import('../pages/primers/Add.vue'), + }, + { + path: '/primers/edit/:id', + component: () => import('../pages/primers/Edit.vue'), + }, + { + path: '/manufacturers', + component: () => import('../pages/manufacturers/List.vue'), + }, + { + path: '/manufacturers/add', + component: () => import('../pages/manufacturers/Add.vue'), + }, + { + path: '/manufacturers/edit/:id', + component: () => import('../pages/manufacturers/Edit.vue'), + }, + { + path: '/loads/add', + component: () => import('../pages/loads/Add.vue'), + }, + { + path: '/loads/search', + component: () => import('../pages/loads/Search.vue'), + }, +] as RouteRecordRaw[] + +const router = createRouter({ + history: createWebHashHistory(), + routes, +}) + +export default router \ No newline at end of file diff --git a/frontend/src/services/manufacturers.ts b/frontend/src/services/manufacturers.ts new file mode 100644 index 0000000..745ae2b --- /dev/null +++ b/frontend/src/services/manufacturers.ts @@ -0,0 +1,10 @@ +import axios from 'axios' +import { Response } from '../types/Response' +import { Manufacturer } from '../types/manufacturer' + +export const fetchManufacturers = async () => { + const response = await axios.get>( + `${import.meta.env.VITE_API}/manufacturer`) + + return response.data +} \ No newline at end of file diff --git a/frontend/src/types/Response.d.ts b/frontend/src/types/Response.d.ts new file mode 100644 index 0000000..b50a666 --- /dev/null +++ b/frontend/src/types/Response.d.ts @@ -0,0 +1,8 @@ +import {AxiosResponse} from "axios"; + +export interface Response extends AxiosResponse { + data: { + payload: T + status: 'OK' | 'ERROR' + } +} \ No newline at end of file diff --git a/frontend/src/types/bullet.d.ts b/frontend/src/types/bullet.d.ts new file mode 100644 index 0000000..184a20a --- /dev/null +++ b/frontend/src/types/bullet.d.ts @@ -0,0 +1,8 @@ +import { Manufacturer } from './manufacturer' + +export interface Bullet { + id: string + name: string + weight: number + manufacturer: Manufacturer +} \ No newline at end of file diff --git a/frontend/src/types/manufacturer.d.ts b/frontend/src/types/manufacturer.d.ts new file mode 100644 index 0000000..5275930 --- /dev/null +++ b/frontend/src/types/manufacturer.d.ts @@ -0,0 +1,5 @@ +export interface Manufacturer { + id: string + name: string + url: string +} diff --git a/frontend/src/types/powder.d.ts b/frontend/src/types/powder.d.ts new file mode 100644 index 0000000..ce9a3c3 --- /dev/null +++ b/frontend/src/types/powder.d.ts @@ -0,0 +1,8 @@ +import { Manufacturer } from './manufacturer' + +export interface Powder { + id: string; + name: string; + meta: string; + manufacturer: Manufacturer; +} \ No newline at end of file diff --git a/frontend/src/types/primers.d.ts b/frontend/src/types/primers.d.ts new file mode 100644 index 0000000..9f32f77 --- /dev/null +++ b/frontend/src/types/primers.d.ts @@ -0,0 +1,7 @@ +import { Manufacturer } from './manufacturer' + +export interface Primers { + id: string + name: string + manufacturer: Manufacturer +} \ No newline at end of file diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..1829f5a --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1,10 @@ +/// + +import {ComponentCustomProperties as GCS} from "@vue/runtime-core"; +import {Account} from "appwrite"; + +declare module "@vue/runtime-core" { + interface ComponentCustomProperties extends GCS { + $account: Account + } +} \ No newline at end of file diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000..7849f91 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..e2def95 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "allowJs": true, + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..a645f8f --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..2e90844 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import tailwindcss from '@tailwindcss/vite' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue(), tailwindcss()], +})