diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml deleted file mode 100644 index 629e749..0000000 --- a/.github/workflows/go.yml +++ /dev/null @@ -1,33 +0,0 @@ -# This workflow will build a golang project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go - -name: Go - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - - build: - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./backend - - steps: - - uses: actions/checkout@v3 - - - name: Set up Go - uses: actions/setup-go@v4 - with: - go-version: '1.21' - - - name: Build - run: go build -v ./... - - - name: Test - run: go test -v ./... diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml deleted file mode 100644 index 64587eb..0000000 --- a/.github/workflows/golangci-lint.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: golangci-lint -on: - push: - branches: [ "master" ] - pull_request: - -permissions: - contents: read - # Optional: allow read access to pull request. Use with `only-new-issues` option. - # pull-requests: read - -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - cache: false - - name: golangci-lint - uses: golangci/golangci-lint-action@v4 - with: - # Require: The version of golangci-lint to use. - # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. - # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. - version: v1.54 - - # Optional: working directory, useful for monorepos - working-directory: ./backend - - # Optional: golangci-lint command line arguments. - # - # Note: By default, the `.golangci.yml` file should be at the root of the repository. - # The location of the configuration file can be changed by using `--config=` - # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true, then all caching functionality will be completely disabled, - # takes precedence over all other caching options. - # skip-cache: true - - # Optional: if set to true, then the action won't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. - # skip-build-cache: true - - # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. - # install-mode: "goinstall" diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 49f8c3b..0000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,37 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: Node.js CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./frontend - - strategy: - matrix: - node-version: [20.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - cache-dependency-path: '**/package-lock.json' - - run: npm ci - - run: npm run lint - - run: npm run build --if-present - - run: npm test diff --git a/.github/workflows/sqlite3-migrations.yml b/.github/workflows/sqlite3-migrations.yml deleted file mode 100644 index 0e53b98..0000000 --- a/.github/workflows/sqlite3-migrations.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: SQLite3 Migrations - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - defaults: - run: - working-directory: ./backend - - steps: - - uses: actions/checkout@v3 - - name: Install SQLite3 - run: sudo apt-get install sqlite3 - - name: Install Make - run: sudo apt-get install make - - name: Run Migrations - run: make migrate diff --git a/.gitignore b/.gitignore index c50fe24..063a7e7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,19 +6,7 @@ *.dylib bin -database.txt -plantuml.jar db.sqlite3 -db.sqlite3-journal -diagram.puml -backend/*.png -backend/*.jpg -backend/*.svg -__pycache__ - -/go.work.sum -/package-lock.json -/backend/docs/swagger.json # Test binary, built with `go test -c` *.test @@ -42,7 +30,6 @@ dist/ .vscode/ .idea/ .DS_Store -.go.work.sum # Ignore configuration files .env @@ -61,6 +48,5 @@ dist/ *.7z *.bak -backend/backups -config.toml + diff --git a/Justfile b/Justfile index 90fabf6..eadfabd 100644 --- a/Justfile +++ b/Justfile @@ -11,29 +11,19 @@ start-release: build-container-release remove-podman-containers # Removes and stops any containers related to the project [private] remove-podman-containers: - podman container rm -fi ttime + podman container rm -f ttime # Saves the release container to a tarball, pigz is just gzip but multithreaded save-release: build-container-release - podman save --format=oci-archive ttime-server | pigz -9 > ttime-server_`date -I`_`git rev-parse --short HEAD`.tar.gz + podman save --format=oci-archive ttime-server | pigz -9 > ttime-server.tar.gz # Loads the release container from a tarball load-release file: podman load --input {{file}} -# Tests every part of the project -testall: - cd frontend && npm install - cd frontend && npm test - cd frontend && npm run lint - cd frontend && npm run build - cd backend && make test - cd backend && make lint - cd backend && make itest - # Cleans up everything related to the project clean: remove-podman-containers - podman image rm -fi ttime-server + podman image rm -f ttime-server rm -rf frontend/dist rm -rf frontend/node_modules rm -f ttime-server.tar.gz @@ -43,7 +33,4 @@ clean: remove-podman-containers # Cleans up everything related to podman, not just the project. Make sure you understand what this means. [confirm] podman-clean: - podman system reset --force - -install-linter: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.56.2 + podman system reset --force \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 51fb206..0000000 --- a/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# Builds a release container -build-container-release: - podman build -t ttime-server -f container/Containerfile . - -# Builds a release container and runs it -start-release: build-container-release remove-podman-containers - podman run -d -e DATABASE_URL=sqlite:release.db -p 8080:8080 --name ttime ttime-server - @echo "Started production ttime-server on http://localhost:8080" - -# Removes and stops any containers related to the project -remove-podman-containers: - podman container rm -fi ttime - -# Tests every part of the project -testall: - cd frontend && npm install - cd frontend && npm test - cd frontend && npm run lint - cd frontend && npm run build - cd backend && make test - cd backend && make lint - cd backend && make itest - -# Cleans up everything related to the project -clean: remove-podman-containers - podman image rm -fi ttime-server - rm -rf frontend/dist - rm -rf frontend/node_modules - rm -f ttime-server.tar.gz - cd backend && make clean - @echo "Cleaned up!" - -.PHONY: itest -itest: - python testing.py - -# Cleans up everything related to podman, not just the project. Make sure you understand what this means. -podman-clean: - podman system reset --force - -# Installs the linter, which is not included in the ubuntu repo -install-linter: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.56.2 - -# This installs just, a make alternative, which is slightly more ergonomic to use -install-just: - @echo "Installing just" - @curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin \ No newline at end of file diff --git a/README.md b/README.md index 062c54f..fc55126 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,12 @@ Dependencies: - [Podman](https://podman.io/) / [Docker](https://www.docker.com/) - [Just](https://github.com/casey/just) (Optional) -### Fedora/Red Hat - If you're on [Fedora](https://fedoraproject.org/)/Red Hat derivatives, this is as simple as: ```bash sudo dnf install -y make golang nodejs podman just ``` -### Debian/Ubuntu - Any [Debian](https://www.debian.org/)/[Ubuntu](https://ubuntu.com/desktop)-based distro: ```bash @@ -40,43 +36,19 @@ sudo apt install -y make golang nodejs podman sudo apt install -y just # For Ubuntu ``` -### Arch Linux - [Arch Linux](https://archlinux.org/) & derivatives: ```bash sudo pacman -S make go nodejs npm podman just ``` -### MacOS - [MacOS](https://www.apple.com/macos/): (Requires [Homebrew](https://brew.sh/)) (Untested) ```bash brew install make go nodejs npm podman just ``` -### Windows - -#### Vanilla Windows - -The project should now build properly on Windows, given the dependencies: - -- [Go](https://go.dev/) -- [Node & npm](https://nodejs.org/en/) - -With chocolatey, you can install these dependencies with the following commands: - -```powershell -choco install -y golang nodejs -``` - -Note that none of the convenience tools (Make, Podman, Just*) are available on Windows. - -*Just is available, but the targets are written for a Unix-like environment. - -#### Windows Subsystem for Linux (WSL) - +[Windows](https://www.microsoft.com/en-us/windows): Unfortunately, [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install) is required for the build process. Running any form of containerized workload on windows is currently unsupported. More info [here](https://podman.io/docs/installation#windows). From my understanding, WSL also requires virtualization extensions to be enabled in the BIOS, which is not always the case for all users. It is possible to run the code on Windows, but this will be without the use of containers or any other build tools that are not available on Windows. @@ -87,22 +59,15 @@ My recommendation would be to make WSL your primary development environment if y You should consult the [WSL documentation](https://docs.microsoft.com/en-us/windows/wsl/install), but for any recent version of windows, installation essentially boils down to running the following command in **PowerShell as an administrator**: ```powershell -wsl --install -d Ubuntu-22.04 # To get a somewhat recent version of Go +wsl --install ``` -After this, you can open a (wsl) terminal and run the commands: +If you get any errors related to virtualization, you will need to enable virtualization in the BIOS. This is a common issue, and you can find a guide for your specific motherboard online. This is a one-time operation and will not affect your windows installation. This setting is usually called "VT-x" or "AMD-V" and is usually found in the CPU settings. If you can't find it, shoot me a message and I'll find it for you. -```bash -sudo apt update && sudo apt upgrade -sudo apt install -y make podman +If you're **still dead set** on using a vanilla Windows environment, you will need the following: -sudo add-apt-repository ppa:longsleep/golang-backports -sudo apt update -sudo apt install golang-go +- [Go](https://go.dev/) +- [Node & npm](https://nodejs.org/en/) +- [MariaDB](https://mariadb.org/) / [MySQL](https://www.mysql.com/) / [PostgreSQL](https://www.postgresql.org/) (This is undecided so far) -# For a recent version of node: -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash -nvm install node -``` - -If you get any errors related to virtualization, you will need to enable virtualization in the BIOS. This is a common issue, and you can find a guide for your specific motherboard online. This is a one-time operation and will not affect your windows installation. This setting is usually called "VT-x" or "AMD-V" and is usually found in the CPU settings. +With some grit and determination, you can get it to work. It's not recommended, but I (Imbus) will try to help you. diff --git a/backend/Makefile b/backend/Makefile index 15a550c..0482c03 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -8,47 +8,29 @@ GOGET = $(GOCMD) get # SQLite database filename DB_FILE = db.sqlite3 -PROC_NAME = ttime_server - # Directory containing migration SQL scripts -MIGRATIONS_DIR = internal/database/migrations -SAMPLE_DATA_DIR = internal/database/sample_data +MIGRATIONS_DIR = migrations # Build target build: - $(GOBUILD) -o bin/$(PROC_NAME) main.go + $(GOBUILD) -o bin/server cmd/*.go # Run target run: build - ./bin/$(PROC_NAME) + ./bin/server watch: build - watchexec -c -w . -r make run + watchexec -w . -r make run # Clean target clean: $(GOCLEAN) rm -rf bin rm -f db.sqlite3 - rm -f diagram* - rm -f plantuml.jar - rm -f erd.png - rm -f config.toml - rm -f database.txt # Test target -test: db.sqlite3 - $(GOTEST) ./... -count=1 - -# Integration test target -.PHONY: itest -itest: - pgrep $(PROC_NAME) && echo "Server already running" && exit 1 || true - make build - ./bin/$(PROC_NAME) >/dev/null 2>&1 & - sleep 1 # Adjust if needed - python ../testing/testing.py || pkill $(PROC_NAME) - pkill $(PROC_NAME) +test: + $(GOTEST) ./... # Get dependencies target deps: @@ -61,105 +43,11 @@ update: # Migration target migrate: - @echo "If this ever fails, run make clean and try again" @echo "Migrating database $(DB_FILE) using SQL scripts in $(MIGRATIONS_DIR)" @for file in $(wildcard $(MIGRATIONS_DIR)/*.sql); do \ echo "Applying migration: $$file"; \ sqlite3 $(DB_FILE) < $$file; \ done -sampledata: - @echo "If this ever fails, run make clean and try again" - @echo "Migrating database $(DB_FILE) using SQL scripts in $(SAMPLE_DATA_DIR)" - @for file in $(wildcard $(SAMPLE_DATA_DIR)/*.sql); do \ - echo "Applying migration: $$file"; \ - sqlite3 $(DB_FILE) < $$file; \ - done - -# Target added primarily for CI/CD to ensure that the database is created before running tests -db.sqlite3: - make migrate - -dbdump: - sqlite3 $(DB_FILE) .dump > database.txt - -backup: - mkdir -p backups - sqlite3 $(DB_FILE) .dump | gzip -9 > ./backups/BACKUP_$(DB_FILE)_$(shell date +"%Y-%m-%d_%H:%M:%S").sql.gz - # Restore with: - # gzip -cd BACKUP_FILE.sql.gz | sqlite3 $(DB_FILE) - -# Format -fmt: - $(GOCMD) fmt ./... - -# Lint -lint: - golangci-lint run ./... - # Default target -default: build - -# Generate swagger docs -.PHONY: docs -docs: - swag init -outputTypes go - -api: ./docs/swagger.json - rm ../frontend/src/API/GenApi.ts - npx swagger-typescript-api \ - --api-class-name GenApi \ - --path ./docs/swagger.json \ - --output ../frontend/src/API \ - --name GenApi.ts \ - -./docs/swagger.json: - swag init -outputTypes json - -.PHONY: docfmt -docfmt: - swag fmt - -# Generate ERD -# Requires eralchemy2 -.PHONY: erd -erd: - eralchemy2 -i sqlite:///db.sqlite3 -o erd.png - -install-swag: - @echo "Installing swag" - @go get -u github.com/swaggo/swag/cmd/swag - -# Convenience target to install golangci-lint -install-lint: - @echo "Installing golangci-lint" - @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.42.1 - -# Fetches the latest plantuml.jar and checks its SHA256 hash -plantuml.jar: - curl -sSfL https://github.com/plantuml/plantuml/releases/download/v1.2024.3/plantuml.jar -o plantuml.jar \ - && echo "519a4a7284c6a0357c369e4bb0caf72c4bfbbde851b8c6d6bbdb7af3c01fc82f plantuml.jar" | sha256sum -c - -# Generate UML diagrams diagral.png & diagram.svg -.PHONY: uml -uml: plantuml.jar - goplantuml -recursive . > diagram.puml - java -jar plantuml.jar -tpng diagram.puml - java -jar plantuml.jar -tsvg diagram.puml - -# Convenience target to install just (requires sudo privileges) -install-just: - @echo "Installing just" - @curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin - -.PHONY: types -types: - tygo generate - -.PHONY: install-golds -install-golds: - go install go101.org/golds@latest - -.PHONY: golds -golds: - golds -port 6060 -nouses -plainsrc -wdpkgs-listing=promoted ./... +default: build \ No newline at end of file diff --git a/backend/cmd/main.go b/backend/cmd/main.go new file mode 100644 index 0000000..699ebf5 --- /dev/null +++ b/backend/cmd/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "ttime/internal/database" + + _ "github.com/mattn/go-sqlite3" +) + +// The button state as represented in memory +type ButtonState struct { + PressCount int `json:"pressCount"` +} + +// This is what a handler with a receiver looks like +// Keep in mind that concurrent state access is not (usually) safe +// And will in practice be guarded by a mutex +func (b *ButtonState) pressHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == "POST" { + b.PressCount++ + } + + response, err := json.Marshal(b) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + fmt.Println("Request received") + io.WriteString(w, string(response)) +} + +// This is what a handler looks like +func handler(w http.ResponseWriter, r *http.Request) { + fmt.Println("Request received") + io.WriteString(w, "This is my website!\n") +} + +func main() { + database.DbConnect() + b := &ButtonState{PressCount: 0} + + // Mounting the handlers + fs := http.FileServer(http.Dir("static")) + http.Handle("/", fs) + http.HandleFunc("/hello", handler) + http.HandleFunc("/api/button", b.pressHandler) + + // Start the server on port 8080 + println("Currently listening on http://localhost:8080") + println("Visit http://localhost:8080/hello to see the hello handler in action") + println("Visit http://localhost:8080/button to see the button handler in action") + println("Press Ctrl+C to stop the server") + err := http.ListenAndServe(":8080", nil) + if err != nil { + panic(err) + } +} diff --git a/backend/docs/docs.go b/backend/docs/docs.go deleted file mode 100644 index c8b020d..0000000 --- a/backend/docs/docs.go +++ /dev/null @@ -1,404 +0,0 @@ -// Package docs Code generated by swaggo/swag. DO NOT EDIT -package docs - -import "github.com/swaggo/swag" - -const docTemplate = `{ - "schemes": {{ marshal .Schemes }}, - "swagger": "2.0", - "info": { - "description": "{{escape .Description}}", - "title": "{{.Title}}", - "contact": {}, - "license": { - "name": "AGPL", - "url": "https://www.gnu.org/licenses/agpl-3.0.html" - }, - "version": "{{.Version}}" - }, - "host": "{{.Host}}", - "basePath": "{{.BasePath}}", - "paths": { - "/login": { - "post": { - "description": "Logs in a user and returns a JWT token", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Auth" - ], - "summary": "Login", - "parameters": [ - { - "description": "User credentials", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.NewUser" - } - } - ], - "responses": { - "200": { - "description": "JWT token", - "schema": { - "$ref": "#/definitions/types.Token" - } - }, - "400": { - "description": "Bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, - "/loginrenew": { - "post": { - "security": [ - { - "JWT": [] - } - ], - "description": "Renews the users token.", - "produces": [ - "application/json" - ], - "tags": [ - "Auth" - ], - "summary": "LoginRenews", - "responses": { - "200": { - "description": "Successfully signed token for user", - "schema": { - "$ref": "#/definitions/types.Token" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, - "/promote/{projectName}": { - "put": { - "security": [ - { - "JWT": [] - } - ], - "description": "Promote a user to project manager", - "consumes": [ - "text/plain" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "Auth" - ], - "summary": "Promote to project manager", - "parameters": [ - { - "type": "string", - "description": "Project name", - "name": "projectName", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "User name", - "name": "userName", - "in": "query", - "required": true - } - ], - "responses": { - "403": { - "description": "Forbidden", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, - "/promoteToAdmin": { - "post": { - "security": [ - { - "JWT": [] - } - ], - "description": "Promote chosen user to site admin", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "User" - ], - "summary": "PromoteToAdmin", - "parameters": [ - { - "description": "user info", - "name": "NewUser", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.NewUser" - } - } - ], - "responses": { - "200": { - "description": "Successfully promoted user", - "schema": { - "$ref": "#/definitions/types.Token" - } - }, - "400": { - "description": "Bad request", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, - "/register": { - "post": { - "description": "Register a new user", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "Auth" - ], - "summary": "Register", - "parameters": [ - { - "description": "User to register", - "name": "NewUser", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/types.NewUser" - } - } - ], - "responses": { - "200": { - "description": "User added", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad request", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, - "/userdelete/{username}": { - "delete": { - "security": [ - { - "JWT": [] - } - ], - "description": "UserDelete deletes a user from the database", - "consumes": [ - "application/json" - ], - "produces": [ - "text/plain" - ], - "tags": [ - "User" - ], - "summary": "UserDelete", - "responses": { - "200": { - "description": "User deleted", - "schema": { - "type": "string" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "403": { - "description": "You can only delete yourself", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - }, - "/users/all": { - "get": { - "security": [ - { - "JWT": [] - } - ], - "description": "lists all users", - "produces": [ - "application/json" - ], - "tags": [ - "User" - ], - "summary": "ListsAllUsers", - "responses": { - "200": { - "description": "Successfully returned all users", - "schema": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "type": "string" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "type": "string" - } - } - } - } - } - }, - "definitions": { - "types.NewUser": { - "type": "object", - "properties": { - "password": { - "type": "string", - "example": "password123" - }, - "username": { - "type": "string", - "example": "username123" - } - } - }, - "types.Token": { - "type": "object", - "properties": { - "token": { - "type": "string" - } - } - } - }, - "securityDefinitions": { - "JWT": { - "description": "Use the JWT token provided by the login endpoint to authenticate requests. **Prefix the token with \"Bearer \".**", - "type": "apiKey", - "name": "Authorization", - "in": "header" - } - }, - "externalDocs": { - "description": "OpenAPI", - "url": "https://swagger.io/resources/open-api/" - } -}` - -// SwaggerInfo holds exported Swagger Info so clients can modify it -var SwaggerInfo = &swag.Spec{ - Version: "0.0.1", - Host: "localhost:8080", - BasePath: "/api", - Schemes: []string{}, - Title: "TTime API", - Description: "This is the API for TTime, a time tracking application.", - InfoInstanceName: "swagger", - SwaggerTemplate: docTemplate, - LeftDelim: "{{", - RightDelim: "}}", -} - -func init() { - swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) -} diff --git a/backend/go.mod b/backend/go.mod index 4f71031..57c5c73 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,54 +3,8 @@ module ttime go 1.21.1 require ( - github.com/BurntSushi/toml v1.3.2 - github.com/gofiber/swagger v1.0.0 github.com/jmoiron/sqlx v1.3.5 - github.com/swaggo/swag v1.16.3 - modernc.org/sqlite v1.29.5 + github.com/mattn/go-sqlite3 v1.14.22 ) -require ( - github.com/KyleBanks/depth v1.2.1 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/spec v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/ncruces/go-strftime v0.1.9 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/swaggo/files/v2 v2.0.0 // indirect - golang.org/x/tools v0.19.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect - modernc.org/libc v1.49.1 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect -) - -require ( - github.com/MicahParks/keyfunc/v2 v2.1.0 // indirect - github.com/gofiber/contrib/jwt v1.0.8 - github.com/golang-jwt/jwt/v5 v5.2.1 -) - -// These are all for fiber -require ( - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/gofiber/fiber/v2 v2.52.4 - github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.17.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.52.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/sys v0.19.0 // indirect -) +require github.com/BurntSushi/toml v1.3.2 // indirect diff --git a/backend/go.sum b/backend/go.sum index aa8ee65..afab06f 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,122 +1,11 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= -github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/MicahParks/keyfunc/v2 v2.1.0 h1:6ZXKb9Rp6qp1bDbJefnG7cTH8yMN1IC/4nf+GVjO99k= -github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= -github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/gofiber/contrib/jwt v1.0.8 h1:/GeOsm/Mr1OGr0GTy+RIVSz5VgNNyP3ZgK4wdqxF/WY= -github.com/gofiber/contrib/jwt v1.0.8/go.mod h1:gWWBtBiLmKXRN7xy6a96QO0KGvPEyxdh8x496Ujtg84= -github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM= -github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= -github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc= -github.com/gofiber/swagger v1.0.0/go.mod h1:QrYNF1Yrc7ggGK6ATsJ6yfH/8Zi5bu9lA7wB8TmCecg= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= -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/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= -github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -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-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/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= -github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= -github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= -github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= -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/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= -github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk= -modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.15.0 h1:uwfCZOkKhaNNotgYW7kxkJwrkQC1HfGitt/7ousudJE= -modernc.org/ccgo/v4 v4.15.0/go.mod h1:XVITcYGiI+O97UNDLMsnZ9ZjJOhC+ACX+TfxpsWWyRc= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b h1:BnN1t+pb1cy61zbvSUV7SeI0PwosMhlAEi/vBY4qxp8= -modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= -modernc.org/libc v1.49.1 h1:r4UaWllkYXRPA7Mq/KzmassZBvNJiH9egF4O/KV/gdE= -modernc.org/libc v1.49.1/go.mod h1:Hx2rWfza47GSzCluTU7Vf0Qx3z9rWCVORL6RNgq+Xog= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= -modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 5bebc34..aec7512 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -22,7 +22,7 @@ type Config struct { } // WriteConfigToFile writes a Config to a file -func (c *Config) WriteConfigToFile(filename string) error { +func WriteConfigToFile(c *Config, filename string) error { f, err := os.Create(filename) if err != nil { return err diff --git a/backend/internal/config/config_test.go b/backend/internal/config/config_test.go index e8ddce8..cc462ff 100644 --- a/backend/internal/config/config_test.go +++ b/backend/internal/config/config_test.go @@ -5,12 +5,8 @@ import ( "testing" ) -// TestNewConfig tests the creation of a new configuration object func TestNewConfig(t *testing.T) { - // Arrange c := NewConfig() - - // Act & Assert if c.Port != 8080 { t.Errorf("Expected port to be 8080, got %d", c.Port) } @@ -28,15 +24,9 @@ func TestNewConfig(t *testing.T) { } } -// TestWriteConfig tests the function to write the configuration to a file func TestWriteConfig(t *testing.T) { - // Arrange c := NewConfig() - - //Act - err := c.WriteConfigToFile("test.toml") - - // Assert + err := WriteConfigToFile(c, "test.toml") if err != nil { t.Errorf("Expected no error, got %s", err) } @@ -45,23 +35,14 @@ func TestWriteConfig(t *testing.T) { _ = os.Remove("test.toml") } -// TestReadConfig tests the function to read the configuration from a file func TestReadConfig(t *testing.T) { - // Arrange c := NewConfig() - - // Act - err := c.WriteConfigToFile("test.toml") - - // Assert + err := WriteConfigToFile(c, "test.toml") if err != nil { t.Errorf("Expected no error, got %s", err) } - // Act c2, err := ReadConfigFromFile("test.toml") - - // Assert if err != nil { t.Errorf("Expected no error, got %s", err) } diff --git a/backend/internal/database/db.go b/backend/internal/database/db.go index 12b0ee1..d99efde 100644 --- a/backend/internal/database/db.go +++ b/backend/internal/database/db.go @@ -1,685 +1,32 @@ package database import ( - "embed" - "encoding/json" - "errors" - "path/filepath" - "ttime/internal/types" + "os" - "github.com/gofiber/fiber/v2/log" "github.com/jmoiron/sqlx" - _ "modernc.org/sqlite" + _ "github.com/mattn/go-sqlite3" ) -// Interface for the database -type Database interface { - // Insert a new user into the database, password should be hashed before calling - AddUser(username string, password string) error - CheckUser(username string, password string) bool - RemoveUser(username string) error - RemoveUserFromProject(username string, projectname string) error - PromoteToAdmin(username string) error - GetUserId(username string) (int, error) - AddProject(name string, description string, username string) error - DeleteProject(name string, username string) error - GetProjectId(projectname string) (int, error) - AddWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error - AddUserToProject(username string, projectname string, role string) error - ChangeUserRole(username string, projectname string, role string) error - ChangeUserName(username string, newname string) error - GetAllUsersProject(projectname string) ([]UserProjectMember, error) - GetAllUsersApplication() ([]string, error) - GetProjectsForUser(username string) ([]types.Project, error) - GetAllProjects() ([]types.Project, error) - GetProject(projectId int) (types.Project, error) - GetUserRole(username string, projectname string) (string, error) - GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) - GetAllWeeklyReports(username string, projectname string) ([]types.WeeklyReportList, error) - GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) - SignWeeklyReport(reportId int, projectManagerId int) error - IsSiteAdmin(username string) (bool, error) - IsProjectManager(username string, projectname string) (bool, error) - ReportStatistics(username string, projectName string) (*types.Statistics, error) - GetProjectTimes(projectName string) (map[string]int, error) - UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error - RemoveProject(projectname string) error - GetUserName(id int) (string, error) - UnsignWeeklyReport(reportId int, projectManagerId int) error - DeleteReport(reportID int) error - ChangeProjectName(projectName string, newProjectName string) error - ChangeUserPassword(username string, password string) error -} +func DbConnect() *sqlx.DB { + // Check for the environment variable + dbpath := os.Getenv("SQLITE_DB_PATH") -// This struct is a wrapper type that holds the database connection -// Internally DB holds a connection pool, so it's safe for concurrent use -type Db struct { - *sqlx.Tx -} + // Default to something reasonable + if dbpath == "" { + dbpath = "./db.sqlite3" + } -type UserProjectMember struct { - Username string `db:"username"` - UserRole string `db:"p_role"` -} - -//go:embed migrations -var scripts embed.FS - -//go:embed sample_data -var sampleData embed.FS - -// TODO: Possibly break these out into separate files bundled with the embed package? -const userInsert = "INSERT INTO users (username, password) VALUES (?, ?)" -const projectInsert = "INSERT INTO projects (name, description, owner_user_id) VALUES (?, ?, (SELECT id FROM users WHERE username = ?))" -const promoteToAdmin = "INSERT INTO site_admin (admin_id) SELECT id FROM users WHERE username = ?" -const addWeeklyReport = `WITH UserLookup AS (SELECT id FROM users WHERE username = ?), - ProjectLookup AS (SELECT id FROM projects WHERE name = ?) - INSERT INTO weekly_reports (project_id, user_id, week, development_time, meeting_time, admin_time, own_work_time, study_time, testing_time) - VALUES ((SELECT id FROM ProjectLookup), (SELECT id FROM UserLookup),?, ?, ?, ?, ?, ?, ?);` -const addUserToProject = `INSERT OR IGNORE INTO user_roles (user_id, project_id, p_role) - VALUES ((SELECT id FROM users WHERE username = ?), - (SELECT id FROM projects WHERE name = ?), ?)` -const changeUserRole = "UPDATE user_roles SET p_role = ? WHERE user_id = (SELECT id FROM users WHERE username = ?) AND project_id = (SELECT id FROM projects WHERE name = ?)" -const getProjectsForUser = `SELECT p.id, p.name, p.description FROM projects p - JOIN user_roles ur ON p.id = ur.project_id - JOIN users u ON ur.user_id = u.id - WHERE u.username = ?` -const deleteProject = `DELETE FROM projects - WHERE id = ? AND owner_username = ?` - -const isProjectManagerQuery = `SELECT COUNT(*) > 0 FROM user_roles - JOIN users ON user_roles.user_id = users.id - JOIN projects ON user_roles.project_id = projects.id - WHERE users.username = ? AND projects.name = ? AND user_roles.p_role = 'project_manager'` - -const removeUserFromProjectQuery = `DELETE FROM user_roles - WHERE user_id = (SELECT id FROM users WHERE username = ?) - AND project_id = (SELECT id FROM projects WHERE name = ?)` - -const reportStatistics = `SELECT SUM(development_time) AS total_development_time, - SUM(meeting_time) AS total_meeting_time, - SUM(admin_time) AS total_admin_time, - SUM(own_work_time) AS total_own_work_time, - SUM(study_time) AS total_study_time, - SUM(testing_time) AS total_testing_time - FROM weekly_reports - WHERE user_id = (SELECT id FROM users WHERE username = ?) - AND project_id = (SELECT id FROM projects WHERE name = ?) - GROUP BY user_id, project_id` - -// DbConnect connects to the database -func DbConnect(dbpath string) sqlx.DB { // Open the database - db, err := sqlx.Connect("sqlite", dbpath) + // db, err := sqlx.Connect("sqlite3", ":memory:") + db, err := sqlx.Connect("sqlite3", dbpath) if err != nil { panic(err) } - // Ping forces the connection to be established err = db.Ping() if err != nil { panic(err) } - return *db -} - -func (d *Db) ReportStatistics(username string, projectName string) (*types.Statistics, error) { - var result types.Statistics - - err := d.Get(&result, reportStatistics, username, projectName) - if err != nil { - return nil, err - } - - serialized, err := json.Marshal(result) - if err != nil { - return nil, err - } - - log.Info(string(serialized)) - - return &result, nil -} - -func (d *Db) CheckUser(username string, password string) bool { - var dbPassword string - err := d.Get(&dbPassword, "SELECT password FROM users WHERE username = ?", username) - if err != nil { - return false - } - return dbPassword == password -} - -// GetProjectsForUser retrieves all projects associated with a specific user. -func (d *Db) GetProjectsForUser(username string) ([]types.Project, error) { - var projects []types.Project - err := d.Select(&projects, getProjectsForUser, username) - return projects, err -} - -// GetAllProjects retrieves all projects from the database. -func (d *Db) GetAllProjects() ([]types.Project, error) { - var projects []types.Project - err := d.Select(&projects, "SELECT * FROM projects") - return projects, err -} - -// GetProject retrieves a specific project by its ID. -func (d *Db) GetProject(projectId int) (types.Project, error) { - var project types.Project - err := d.Get(&project, "SELECT * FROM projects WHERE id = ?", projectId) - if err != nil { - println("Error getting project: ", err) - } - return project, err -} - -func (d *Db) AddWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error { - _, err := d.Exec(addWeeklyReport, userName, projectName, week, developmentTime, meetingTime, adminTime, ownWorkTime, studyTime, testingTime) - return err -} - -// AddUserToProject adds a user to a project with a specified role. -func (d *Db) AddUserToProject(username string, projectname string, role string) error { - _, err := d.Exec(addUserToProject, username, projectname, role) - return err -} - -func (d *Db) RemoveUserFromProject(username string, projectname string) error { - _, err := d.Exec(removeUserFromProjectQuery, username, projectname) - return err -} - -// ChangeUserRole changes the role of a user within a project. -func (d *Db) ChangeUserRole(username string, projectname string, role string) error { - // Execute the SQL query to change the user's role - _, err := d.Exec(changeUserRole, role, username, projectname) - return err -} - -// ChangeUserName changes the username of a user. -func (d *Db) ChangeUserName(username string, newname string) error { - // Execute the SQL query to update the username - _, err := d.Exec("UPDATE users SET username = ? WHERE username = ?", newname, username) - return err -} - -// GetUserRole retrieves the role of a user within a project. -func (d *Db) GetUserRole(username string, projectname string) (string, error) { - var role string - err := d.Get(&role, "SELECT p_role FROM user_roles WHERE user_id = (SELECT id FROM users WHERE username = ?) AND project_id = (SELECT id FROM projects WHERE name = ?)", username, projectname) - return role, err -} - -// AddUser adds a user to the database -func (d *Db) AddUser(username string, password string) error { - _, err := d.Exec(userInsert, username, password) - return err -} - -// Removes a user from the database -func (d *Db) RemoveUser(username string) error { - _, err := d.Exec("DELETE FROM users WHERE username = ?", username) - return err -} - -func (d *Db) PromoteToAdmin(username string) error { - _, err := d.Exec(promoteToAdmin, username) - return err -} - -func (d *Db) GetUserId(username string) (int, error) { - var id int - err := d.Get(&id, "SELECT id FROM users WHERE username = ?", username) // Borde det inte vara "user" i singular - return id, err -} - -func (d *Db) GetProjectId(projectname string) (int, error) { - var id int - err := d.Get(&id, "SELECT id FROM projects WHERE name = ?", projectname) - return id, err -} - -// Creates a new project in the database, associated with a user -func (d *Db) AddProject(name string, description string, username string) error { - // Insert the project into the database - _, err := d.Exec(projectInsert, name, description, username) - if err != nil { - return err - } - - // Add creator to project as project manager - _, err = d.Exec(addUserToProject, username, name, "project_manager") - if err != nil { - return err - } - - return err -} - -func (d *Db) DeleteProject(projectID string, username string) error { - _, err := d.Exec(deleteProject, projectID, username) - - return err -} - -func (d *Db) GetAllUsersProject(projectname string) ([]UserProjectMember, error) { - // Define the SQL query to fetch users and their roles for a given project - query := ` - SELECT u.username, ur.p_role - FROM users u - INNER JOIN user_roles ur ON u.id = ur.user_id - INNER JOIN projects p ON ur.project_id = p.id - WHERE p.name = ? - ` - - // Execute the query - rows, err := d.Queryx(query, projectname) - if err != nil { - return nil, err - } - defer rows.Close() - - // Iterate over the rows and populate the result slice - var users []UserProjectMember - for rows.Next() { - var user UserProjectMember - if err := rows.StructScan(&user); err != nil { - return nil, err - } - users = append(users, user) - } - if err := rows.Err(); err != nil { - return nil, err - } - - return users, nil -} - -// GetAllUsersApplication retrieves all usernames from the database -func (d *Db) GetAllUsersApplication() ([]string, error) { - // Define the SQL query to fetch all usernames - query := ` - SELECT username FROM users - ` - - // Execute the query - rows, err := d.Queryx(query) - if err != nil { - return nil, err - } - defer rows.Close() - - // Iterate over the rows and populate the result slice - var usernames []string - for rows.Next() { - var username string - if err := rows.Scan(&username); err != nil { - return nil, err - } - usernames = append(usernames, username) - } - if err := rows.Err(); err != nil { - return nil, err - } - - return usernames, nil -} - -func (d *Db) GetWeeklyReport(username string, projectName string, week int) (types.WeeklyReport, error) { - var report types.WeeklyReport - query := ` - SELECT - report_id, - user_id, - project_id, - week, - development_time, - meeting_time, - admin_time, - own_work_time, - study_time, - testing_time, - signed_by - FROM - weekly_reports - WHERE - user_id = (SELECT id FROM users WHERE username = ?) - AND project_id = (SELECT id FROM projects WHERE name = ?) - AND week = ? - ` - err := d.Get(&report, query, username, projectName, week) - return report, err -} - -// SignWeeklyReport signs a weekly report by updating the signed_by field -// with the provided project manager's ID, but only if the project manager -// is in the same project as the report -func (d *Db) SignWeeklyReport(reportId int, projectManagerId int) error { - // Retrieve the project ID associated with the report - var reportProjectID int - err := d.Get(&reportProjectID, "SELECT project_id FROM weekly_reports WHERE report_id = ?", reportId) - if err != nil { - return err - } - - managerQuery := `SELECT project_id FROM user_roles - WHERE user_id = ? - AND project_id = (SELECT project_id FROM weekly_reports WHERE report_id = ?) - AND p_role = 'project_manager'` - - // Retrieve the project ID associated with the project manager - var managerProjectID int - err = d.Get(&managerProjectID, managerQuery, projectManagerId, reportId) - if err != nil { - return err - } - - // Check if the project manager is in the same project as the report - if reportProjectID != managerProjectID { - return errors.New("project manager doesn't have permission to sign the report") - } - - // Update the signed_by field of the specified report - _, err = d.Exec("UPDATE weekly_reports SET signed_by = ? WHERE report_id = ?", projectManagerId, reportId) - return err -} - -func (d *Db) UnsignWeeklyReport(reportId int, projectManagerId int) error { - // Retrieve the project ID associated with the report - var reportProjectID int - err := d.Get(&reportProjectID, "SELECT project_id FROM weekly_reports WHERE report_id = ?", reportId) - if err != nil { - return err - } - - managerQuery := `SELECT project_id FROM user_roles - WHERE user_id = ? - AND project_id = (SELECT project_id FROM weekly_reports WHERE report_id = ?) - AND p_role = 'project_manager'` - - // Retrieve the project ID associated with the project manager - var managerProjectID int - err = d.Get(&managerProjectID, managerQuery, projectManagerId, reportId) - if err != nil { - return err - } - - // Check if the project manager is in the same project as the report - if reportProjectID != managerProjectID { - return errors.New("project manager doesn't have permission to unsign the report") - } - - // Update the signed_by field of the specified report - _, err = d.Exec("UPDATE weekly_reports SET signed_by = NULL WHERE report_id = ?;", reportId) - return err -} - -func (d *Db) GetUnsignedWeeklyReports(projectName string) ([]types.WeeklyReport, error) { - // Define the SQL query to fetch unsigned reports for a given user - query := ` - SELECT - report_id, - user_id, - project_id, - week, - development_time, - meeting_time, - admin_time, - own_work_time, - study_time, - testing_time, - signed_by - FROM - weekly_reports - WHERE - signed_by IS NULL - AND project_id = (SELECT id FROM projects WHERE name = ?) - ` - - // Execute the query - rows, err := d.Queryx(query, projectName) - if err != nil { - return nil, err - } - defer rows.Close() - - // Iterate over the rows and populate the result slice - var reports []types.WeeklyReport - for rows.Next() { - var report types.WeeklyReport - if err := rows.StructScan(&report); err != nil { - return nil, err - } - reports = append(reports, report) - } - if err := rows.Err(); err != nil { - return nil, err - } - - return reports, nil -} - -// IsSiteAdmin checks if a given username is a site admin -func (d *Db) IsSiteAdmin(username string) (bool, error) { - // Define the SQL query to check if the user is a site admin - query := ` - SELECT COUNT(*) FROM site_admin - JOIN users ON site_admin.admin_id = users.id - WHERE users.username = ? - ` - - // Execute the query - var count int - err := d.Get(&count, query, username) - if err != nil { - return false, err - } - - // If count is greater than 0, the user is a site admin - return count > 0, nil -} - -// Reads a directory of migration files and applies them to the database. -// This will eventually be used on an embedded directory -func Migrate(db sqlx.DB) error { - // Read the embedded scripts directory - files, err := scripts.ReadDir("migrations") - if err != nil { - return err - } - - if len(files) == 0 { - println("No migration files found") - return nil - } - - tr := db.MustBegin() - - // Iterate over each SQL file and execute it - for _, file := range files { - if file.IsDir() || filepath.Ext(file.Name()) != ".sql" { - continue - } - - // This is perhaps not the most elegant way to do this - sqlBytes, err := scripts.ReadFile("migrations/" + file.Name()) - if err != nil { - return err - } - - sqlQuery := string(sqlBytes) - _, err = tr.Exec(sqlQuery) - if err != nil { - return err - } - } - - if tr.Commit() != nil { - return err - } - - return nil -} - -// GetAllWeeklyReports retrieves weekly reports for a specific user and project. -func (d *Db) GetAllWeeklyReports(username string, projectName string) ([]types.WeeklyReportList, error) { - query := ` - SELECT - wr.week, - wr.development_time, - wr.meeting_time, - wr.admin_time, - wr.own_work_time, - wr.study_time, - wr.testing_time, - wr.signed_by - FROM - weekly_reports wr - INNER JOIN - users u ON wr.user_id = u.id - INNER JOIN - projects p ON wr.project_id = p.id - WHERE - u.username = ? AND p.name = ? - ` - - var reports []types.WeeklyReportList - if err := d.Select(&reports, query, username, projectName); err != nil { - return nil, err - } - - return reports, nil -} - -// IsProjectManager checks if a given username is a project manager for the specified project -func (d *Db) IsProjectManager(username string, projectname string) (bool, error) { - var manager bool - err := d.Get(&manager, isProjectManagerQuery, username, projectname) - return manager, err -} - -func (d *Db) UpdateWeeklyReport(projectName string, userName string, week int, developmentTime int, meetingTime int, adminTime int, ownWorkTime int, studyTime int, testingTime int) error { - query := ` - UPDATE weekly_reports - SET - development_time = ?, - meeting_time = ?, - admin_time = ?, - own_work_time = ?, - study_time = ?, - testing_time = ? - WHERE - user_id = (SELECT id FROM users WHERE username = ?) - AND project_id = (SELECT id FROM projects WHERE name = ?) - AND week = ? - ` - - _, err := d.Exec(query, developmentTime, meetingTime, adminTime, ownWorkTime, studyTime, testingTime, userName, projectName, week) - return err -} - -// MigrateSampleData applies sample data to the database. -func MigrateSampleData(db sqlx.DB) error { - // Insert sample data - files, err := sampleData.ReadDir("sample_data") - if err != nil { - return err - } - - if len(files) == 0 { - println("No sample data files found") - } - tr := db.MustBegin() - - // Iterate over each SQL file and execute it - for _, file := range files { - if file.IsDir() || filepath.Ext(file.Name()) != ".sql" { - continue - } - - // This is perhaps not the most elegant way to do this - sqlBytes, err := sampleData.ReadFile("sample_data/" + file.Name()) - if err != nil { - return err - } - - sqlQuery := string(sqlBytes) - _, err = tr.Exec(sqlQuery) - if err != nil { - return err - } - } - - if tr.Commit() != nil { - return err - } - - return nil -} - -// GetProjectTimes retrieves a map with times per "Activity" for a given project -func (d *Db) GetProjectTimes(projectName string) (map[string]int, error) { - query := ` - SELECT development_time, meeting_time, admin_time, own_work_time, study_time, testing_time - FROM weekly_reports - JOIN projects ON weekly_reports.project_id = projects.id - WHERE projects.name = ? - ` - - rows, err := d.Query(query, projectName) - if err != nil { - return nil, err - } - defer rows.Close() - - totalTime := make(map[string]int) - - for rows.Next() { - var developmentTime, meetingTime, adminTime, ownWorkTime, studyTime, testingTime int - if err := rows.Scan(&developmentTime, &meetingTime, &adminTime, &ownWorkTime, &studyTime, &testingTime); err != nil { - return nil, err - } - - totalTime["development"] += developmentTime - totalTime["meeting"] += meetingTime - totalTime["admin"] += adminTime - totalTime["own_work"] += ownWorkTime - totalTime["study"] += studyTime - totalTime["testing"] += testingTime - } - - if err := rows.Err(); err != nil { - return nil, err - } - - return totalTime, nil -} - -func (d *Db) RemoveProject(projectname string) error { - _, err := d.Exec("DELETE FROM projects WHERE name = ?", projectname) - return err -} - -func (d *Db) GetUserName(id int) (string, error) { - var username string - err := d.Get(&username, "SELECT username FROM users WHERE id = ?", id) - return username, err -} - -func (d *Db) DeleteReport(reportID int) error { - _, err := d.Exec("DELETE FROM weekly_reports WHERE report_id = ?", reportID) - return err -} - -// ChangeProjectName is a handler that changes the name of a project -func (d *Db) ChangeProjectName(projectName string, newProjectName string) error { - _, err := d.Exec("UPDATE projects SET name = ? WHERE name = ?", newProjectName, projectName) - return err -} - -func (d *Db) ChangeUserPassword(username string, password string) error { - _, err := d.Exec("UPDATE users SET password = ? WHERE username = ?", password, username) - return err + return db } diff --git a/backend/internal/database/db_test.go b/backend/internal/database/db_test.go index 7b599f2..d7b26c9 100644 --- a/backend/internal/database/db_test.go +++ b/backend/internal/database/db_test.go @@ -4,1141 +4,7 @@ import ( "testing" ) -// Tests are not guaranteed to be sequential - -// setupState initializes a database instance with necessary setup for testing -func setupState() (Database, error) { - db := DbConnect(":memory:") - err := Migrate(db) - if err != nil { - return nil, err - } - - db_iface := Db{db.MustBegin()} - return &db_iface, nil -} - -// This is a more advanced setup that includes more data in the database. -// This is useful for more complex testing scenarios. -func setupAdvancedState() (Database, error) { - db, err := setupState() - if err != nil { - return nil, err - } - - // Add a user - if err = db.AddUser("demouser", "password"); err != nil { - return nil, err - } - - // Add a project - if err = db.AddProject("projecttest", "description", "demouser"); err != nil { - return nil, err - } - - // Add a weekly report - if err = db.AddWeeklyReport("projecttest", "demouser", 1, 1, 1, 1, 1, 1, 1); err != nil { - return nil, err - } - - return db, nil -} - -// TestDbConnect tests the connection to the database func TestDbConnect(t *testing.T) { - db := DbConnect(":memory:") + db := DbConnect() _ = db } - -func TestSetupAdvancedState(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupAdvancedState failed:", err) - } - - // Check if the user was added - if _, err = db.GetUserId("demouser"); err != nil { - t.Error("GetUserId failed:", err) - } - - // Check if the project was added - projects, err := db.GetAllProjects() - if err != nil { - t.Error("GetAllProjects failed:", err) - } - if len(projects) != 1 { - t.Error("GetAllProjects failed: expected 1, got", len(projects)) - } - - // To be continued... -} - -// TestDbAddUser tests the AddUser function of the database -func TestDbAddUser(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - err = db.AddUser("test", "password") - if err != nil { - t.Error("AddUser failed:", err) - } -} - -// TestDbGetUserId tests the GetUserID function of the database -func TestDbGetUserId(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - if db.AddUser("test", "password") != nil { - t.Error("AddUser failed") - } - - var id int - - id, err = db.GetUserId("test") - if err != nil { - t.Error("GetUserId failed:", err) - } - if id != 1 { - t.Error("GetUserId failed: expected 1, got", id) - } -} - -// TestDbAddProject tests the AddProject function of the database -func TestDbAddProject(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddProject("test", "description", "demouser") - if err != nil { - t.Error("AddProject failed:", err) - } -} - -// TestDbRemoveUser tests the RemoveUser function of the database -func TestDbRemoveUser(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.RemoveUser("test") - if err != nil { - t.Error("RemoveUser failed:", err) - } -} - -// TestPromoteToAdmin tests the PromoteToAdmin function of the database -func TestPromoteToAdmin(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("test", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.PromoteToAdmin("test") - if err != nil { - t.Error("PromoteToAdmin failed:", err) - } -} - -// TestAddWeeklyReport tests the AddWeeklyReport function of the database -func TestAddWeeklyReport(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } -} - -// TestAddUserToProject tests the AddUseToProject function of the database -func TestAddUserToProject(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - err = db.AddUserToProject("testuser", "testproject", "user") - if err != nil { - t.Error("AddUserToProject failed:", err) - } -} - -// TestChangeUserRole tests the ChangeUserRole function of the database -func TestChangeUserRole(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - role, err := db.GetUserRole("testuser", "testproject") - if err != nil { - t.Error("GetUserRole failed:", err) - } - if role != "project_manager" { - t.Error("GetUserRole failed: expected project_manager, got", role) - } - - err = db.ChangeUserRole("testuser", "testproject", "member") - if err != nil { - t.Error("ChangeUserRole failed:", err) - } - - role, err = db.GetUserRole("testuser", "testproject") - if err != nil { - t.Error("GetUserRole failed:", err) - } - if role != "member" { - t.Error("GetUserRole failed: expected member, got", role) - } - -} - -// TestGetAllUsersProject tests the GetAllUsersProject function of the database -func TestGetAllUsersProject(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser1", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddUser("testuser2", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser1") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddUserToProject("testuser1", "testproject", "project_manager") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - err = db.AddUserToProject("testuser2", "testproject", "user") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - users, err := db.GetAllUsersProject("testproject") - if err != nil { - t.Error("GetAllUsersProject failed:", err) - } - - // Check if both users are returned with their roles - if len(users) != 2 { - t.Errorf("Expected 2 users, got %d", len(users)) - } - - // Check if testuser1 has project manager role - foundProjectManager := false - for _, user := range users { - if user.Username == "testuser1" && user.UserRole == "project_manager" { - foundProjectManager = true - break - } - } - if !foundProjectManager { - t.Error("Project Manager user not found") - } - - // Check if testuser2 has user role - foundUser := false - for _, user := range users { - if user.Username == "testuser2" && user.UserRole == "user" { - foundUser = true - break - } - } - if !foundUser { - t.Error("User user not found") - } -} - -// TestGetAllUsersApplication tests the GetAllUsersApplicsation function of the database -func TestGetAllUsersApplication(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser1", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddUser("testuser2", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - users, err := db.GetAllUsersApplication() - if err != nil { - t.Error("GetAllUsersApplication failed:", err) - } - - // Check if both users are returned - if len(users) != 2 { - t.Errorf("Expected 2 users, got %d", len(users)) - } - - // Check if the test users are included in the list - foundTestUser1 := false - foundTestUser2 := false - for _, user := range users { - if user == "testuser1" { - foundTestUser1 = true - } - if user == "testuser2" { - foundTestUser2 = true - } - } - - if !foundTestUser1 { - t.Error("testuser1 not found") - } - if !foundTestUser2 { - t.Error("testuser2 not found") - } -} - -// TestGetProjectsForUser tests the GetProjectsForUser function of the database -func TestGetProjectsForUser(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddUserToProject("testuser", "testproject", "user") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - projects1, err := db.GetAllProjects() - if err != nil { - t.Error("GetAllProjects failed:", err) - } - - if len(projects1) != 1 { - t.Error("GetAllProjects failed: expected 1, got", len(projects1)) - } - - projects, err := db.GetProjectsForUser("testuser") - if err != nil { - t.Error("GetProjectsForUser failed:", err) - } - - if len(projects) != 1 { - t.Error("GetProjectsForUser failed: expected 1, got", len(projects)) - } -} - -// TestAddProject tests AddProject function of the database -func TestAddProject(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Retrieve the added project to verify its existence - projects, err := db.GetAllProjects() - if err != nil { - t.Error("GetAllProjects failed:", err) - } - - // Check if the project was added successfully - found := false - for _, project := range projects { - if project.Name == "testproject" { - found = true - break - } - } - if !found { - t.Error("Added project not found") - } -} - -// TestGetWeeklyReport tests GetWeeklyReport function of the database -func TestGetWeeklyReport(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - report, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Check if the retrieved report matches the expected values - if report.UserId != 1 { - t.Errorf("Expected UserId to be 1, got %d", report.UserId) - } - if report.ProjectId != 1 { - t.Errorf("Expected ProjectId to be 1, got %d", report.ProjectId) - } - if report.Week != 1 { - t.Errorf("Expected Week to be 1, got %d", report.Week) - } - // Check other fields similarly -} - -func TestGetUnsignedWeeklyReports(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddUser("testuser1", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser1", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - reports, err := db.GetUnsignedWeeklyReports("testproject") - if err != nil { - t.Error("GetUnsignedWeeklyReports failed:", err) - } - - if reports == nil { - t.Error("Expected non-nil reports, got nil") - } -} - -// TestSignWeeklyReport tests SignWeeklyReport function of the database -func TestSignWeeklyReport(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add project manager - err = db.AddUser("projectManager", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a regular user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add project - err = db.AddProject("testproject", "description", "projectManager") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Add both regular users as members to the project - err = db.AddUserToProject("testuser", "testproject", "member") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - err = db.AddUserToProject("projectManager", "testproject", "project_manager") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - // Add a weekly report for one of the regular users - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - // Retrieve the added report - report, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Print project manager's ID - projectManagerID, err := db.GetUserId("projectManager") - if err != nil { - t.Error("GetUserId failed:", err) - } - - // Sign the report with the project manager - err = db.SignWeeklyReport(report.ReportId, projectManagerID) - if err != nil { - t.Error("SignWeeklyReport failed:", err) - } - - // Retrieve the report again to check if it's signed - signedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Ensure the report is signed by the project manager - if *signedReport.SignedBy != projectManagerID { - t.Errorf("Expected SignedBy to be %d, got %d", projectManagerID, *signedReport.SignedBy) - } -} - -func TestUnsignWeeklyReport(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add project manager - err = db.AddUser("projectManager", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a regular user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add project - err = db.AddProject("testproject", "description", "projectManager") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Add both regular users as members to the project - err = db.AddUserToProject("testuser", "testproject", "member") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - err = db.AddUserToProject("projectManager", "testproject", "project_manager") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - // Add a weekly report for one of the regular users - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - // Retrieve the added report - report, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Print project manager's ID - projectManagerID, err := db.GetUserId("projectManager") - if err != nil { - t.Error("GetUserId failed:", err) - } - - // Sign the report with the project manager - err = db.SignWeeklyReport(report.ReportId, projectManagerID) - if err != nil { - t.Error("SignWeeklyReport failed:", err) - } - - // Retrieve the report again to check if it's signed - signedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Ensure the report is signed by the project manager - if *signedReport.SignedBy != projectManagerID { - t.Errorf("Expected SignedBy to be %d, got %d", projectManagerID, *signedReport.SignedBy) - } - - // Unsign the report - err = db.UnsignWeeklyReport(report.ReportId, projectManagerID) - if err != nil { - t.Error("UnsignWeeklyReport failed:", err) - } - - // Retrieve the report again to check if it's unsigned - unsignedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Ensure the report is unsigned - if unsignedReport.SignedBy != nil { - t.Error("Expected SignedBy to be nil, got", unsignedReport.SignedBy) - } -} - -// TestSignWeeklyReportByAnotherProjectManager tests the scenario where a project manager attempts to sign a weekly report for a user who is not assigned to their project -func TestSignWeeklyReportByAnotherProjectManager(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add project manager - err = db.AddUser("projectManager", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a regular user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add project, projectManager is the owner - err = db.AddProject("testproject", "description", "projectManager") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Add the regular user as a member to the project - err = db.AddUserToProject("testuser", "testproject", "member") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - // Add a weekly report for the regular user - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - // Retrieve the added report - report, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - managerID, err := db.GetUserId("projectManager") - if err != nil { - t.Error("GetUserId failed:", err) - } - - err = db.SignWeeklyReport(report.ReportId, managerID) - if err != nil { - t.Error("SignWeeklyReport failed:", err) - } - - // Retrieve the report again to check if it's signed - signedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Ensure the report is signed by the project manager - if *signedReport.SignedBy != managerID { - t.Errorf("Expected SignedBy to be %d, got %d", managerID, *signedReport.SignedBy) - } -} - -// TestGetProject tests GetProject function of the database -func TestGetProject(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add a user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a project - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Retrieve the added project - project, err := db.GetProject(1) - if err != nil { - t.Error("GetProject failed:", err) - } - - // Check if the retrieved project matches the expected values - if project.Name != "testproject" { - t.Errorf("Expected Name to be testproject, got %s", project.Name) - } -} - -func TestGetWeeklyReportsUser(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - err = db.AddWeeklyReport("testproject", "testuser", 2, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - reports, err := db.GetAllWeeklyReports("testuser", "testproject") - if err != nil { - t.Error("GetWeeklyReportsUser failed:", err) - } - - // Check if the retrieved reports match the expected values - if len(reports) != 2 { - t.Errorf("Expected 1 report, got %d", len(reports)) - } -} - -func TestIsProjectManager(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add a project manager - err = db.AddUser("projectManager", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a regular user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add project - err = db.AddProject("testproject", "description", "projectManager") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Add both regular users as members to the project - err = db.AddUserToProject("testuser", "testproject", "member") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - err = db.AddUserToProject("projectManager", "testproject", "project_manager") - if err != nil { - t.Error("AddUserToProject failed:", err) - } - - // Check if the regular user is not a project manager - isManager, err := db.IsProjectManager("testuser", "testproject") - if err != nil { - t.Error("IsProjectManager failed:", err) - } - if isManager { - t.Error("Expected testuser not to be a project manager, but it is.") - } - - // Check if the project manager is indeed a project manager - isManager, err = db.IsProjectManager("projectManager", "testproject") - if err != nil { - t.Error("IsProjectManager failed:", err) - } - if !isManager { - t.Error("Expected projectManager to be a project manager, but it's not.") - } -} - -func TestGetProjectTimes(t *testing.T) { - // Initialize - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - return - } - - // Create a user - user := "TeaUser" - password := "Vanilla" - err = db.AddUser(user, password) - if err != nil { - t.Error("AddUser failed:", err) - return - } - - // Create a project - projectName := "ProjectVanilla" - projectDescription := "When tea tastes its best" - err = db.AddProject(projectName, projectDescription, user) // Fix the variable name here - if err != nil { - t.Error("AddProject failed:", err) - return - } - - // Tests the func in db.go - totalTime, err := db.GetProjectTimes(projectName) - if err != nil { - t.Error("GetTotalTimePerActivity failed:", err) - return - } - - // Check if the totalTime map is not nil - if totalTime == nil { - t.Error("Expected non-nil totalTime map, got nil") - return - } - - // Define the expected valeus - expectedTotalTime := map[string]int{ - "development": 0, - "meeting": 0, - "admin": 0, - "own_work": 0, - "study": 0, - "testing": 0, - } - - // Compare the expectedTotalTime with the totalTime retrieved from the database - for activity, expectedTime := range expectedTotalTime { - if totalTime[activity] != expectedTime { - t.Errorf("Expected %s time to be %d, got %d", activity, expectedTime, totalTime[activity]) - } - } - - // Insert some data into the database for different activities - err = db.AddWeeklyReport(projectName, user, 1, 1, 3, 2, 1, 4, 5) - if err != nil { - t.Error("Failed to insert data into the database:", err) - return - } - - newTotalTime, err := db.GetProjectTimes(projectName) - if err != nil { - t.Error("GetTotalTimePerActivity failed:", err) - return - } - - newExpectedTotalTime := map[string]int{ - "development": 1, - "meeting": 3, - "admin": 2, - "own_work": 1, - "study": 4, - "testing": 5, - } - - for activity, newExpectedTime := range newExpectedTotalTime { - if newTotalTime[activity] != newExpectedTime { - t.Errorf("Expected %s time to be %d, got %d", activity, newExpectedTime, newTotalTime[activity]) - } - } -} -func TestEnsureManagerOfCreatedProject(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add a user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a project - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Set user to a project manager - // err = db.AddUserToProject("testuser", "testproject", "project_manager") - // if err != nil { - // t.Error("AddUserToProject failed:", err) - // } - - managerState, err := db.IsProjectManager("testuser", "testproject") - if err != nil { - t.Error("IsProjectManager failed:", err) - } - - if !managerState { - t.Error("Expected testuser to be a project manager, but it's not.") - } -} - -// TestUpdateWeeklyReport tests the UpdateWeeklyReport function of the database -func TestUpdateWeeklyReport(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add a user - err = db.AddUser("testuser", "password") - if err != nil { - t.Error("AddUser failed:", err) - } - - // Add a project - err = db.AddProject("testproject", "description", "testuser") - if err != nil { - t.Error("AddProject failed:", err) - } - - // Add a weekly report - err = db.AddWeeklyReport("testproject", "testuser", 1, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - // Update the weekly report - err = db.UpdateWeeklyReport("testproject", "testuser", 1, 2, 2, 2, 2, 2, 2) - if err != nil { - t.Error("UpdateWeeklyReport failed:", err) - } - - // Retrieve the updated report - updatedReport, err := db.GetWeeklyReport("testuser", "testproject", 1) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Check if the report was updated correctly - if updatedReport.DevelopmentTime != 2 || - updatedReport.MeetingTime != 2 || - updatedReport.AdminTime != 2 || - updatedReport.OwnWorkTime != 2 || - updatedReport.StudyTime != 2 || - updatedReport.TestingTime != 2 { - t.Error("UpdateWeeklyReport failed: report not updated correctly") - } -} - -func TestRemoveProject(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Promote user to Admin - err = db.PromoteToAdmin("demouser") - if err != nil { - t.Error("PromoteToAdmin failed:", err) - } - - // Remove project - err = db.RemoveProject("projecttest") - if err != nil { - t.Error("RemoveProject failed:", err) - } - - // Check if the project was removed - projects, err := db.GetAllProjects() - if err != nil { - t.Error("GetAllProjects failed:", err) - } - if len(projects) != 0 { - t.Error("RemoveProject failed: expected 0, got", len(projects)) - } - -} - -func TestDeleteReport(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Promote user to Admin - err = db.PromoteToAdmin("demouser") - if err != nil { - t.Error("PromoteToAdmin failed:", err) - } - - // create a weekly report - err = db.AddWeeklyReport("projecttest", "demouser", 16, 1, 1, 1, 1, 1, 1) - if err != nil { - t.Error("AddWeeklyReport failed:", err) - } - - // Check if the report was added - report, err := db.GetWeeklyReport("demouser", "projecttest", 16) - if err != nil { - t.Error("GetWeeklyReport failed:", err) - } - - // Remove report - err = db.DeleteReport(report.ReportId) - if err != nil { - t.Error("RemoveReport failed:", err) - } - - // Check if the report was removed - report, err = db.GetWeeklyReport("demouser", "projecttest", 16) - if err == nil { - t.Error("RemoveReport failed: report not removed") - } - -} - -func TestChangeProjectName(t *testing.T) { - db, err := setupAdvancedState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Promote user to Admin - err = db.PromoteToAdmin("demouser") - if err != nil { - t.Error("PromoteToAdmin failed:", err) - } - - // Change project name - err = db.ChangeProjectName("projecttest", "newprojectname") - if err != nil { - t.Error("ChangeProjectName failed:", err) - } - - // Check if the project name was changed - projects, err := db.GetAllProjects() - if err != nil { - t.Error("GetAllProjects failed:", err) - } - if projects[0].Name != "newprojectname" { - t.Error("ChangeProjectName failed: expected newprojectname, got", projects[0].Name) - } -} - -func TestChangeUserPassword(t *testing.T) { - db, err := setupState() - if err != nil { - t.Error("setupState failed:", err) - } - - // Add a user - _ = db.AddUser("testuser", "password") - - // Change user password - err = db.ChangeUserPassword("testuser", "newpassword") - if err != nil { - t.Error("ChangeUserPassword failed:", err) - } - - // Check if the password was changed - if !db.CheckUser("testuser", "newpassword") { - t.Error("ChangeUserPassword failed: password not changed") - } - -} diff --git a/backend/internal/database/middleware.go b/backend/internal/database/middleware.go deleted file mode 100644 index b73a42f..0000000 --- a/backend/internal/database/middleware.go +++ /dev/null @@ -1,34 +0,0 @@ -package database - -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/jmoiron/sqlx" -) - -// Simple middleware that provides a transaction as a local key "db" -func DbMiddleware(db *sqlx.DB) func(c *fiber.Ctx) error { - return func(c *fiber.Ctx) error { - tx := db.MustBegin() - - defer func() { - if err := tx.Commit(); err != nil { - if err = tx.Rollback(); err != nil { - log.Error("Failed to rollback transaction: ", err) - } - return - } - }() - - var db_iface Database = &Db{tx} - - c.Locals("db", &db_iface) - return c.Next() - } -} - -// Helper function to get the database from the context, without fiddling with casts -func GetDb(c *fiber.Ctx) Database { - // Dereference a pointer to a local, casted to a pointer to a Database - return *c.Locals("db").(*Database) -} diff --git a/backend/internal/database/migrations/0010_users.sql b/backend/internal/database/migrations/0010_users.sql deleted file mode 100644 index 15b1373..0000000 --- a/backend/internal/database/migrations/0010_users.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Id is a surrogate key for in ternal use --- userId is what is used for external id --- username is what is used for login --- password is the hashed password -CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username VARCHAR(255) NOT NULL UNIQUE, - password VARCHAR(255) NOT NULL -); - --- Users are commonly searched by username and userId -CREATE INDEX IF NOT EXISTS users_username_index ON users (username); diff --git a/backend/internal/database/migrations/0020_projects.sql b/backend/internal/database/migrations/0020_projects.sql deleted file mode 100644 index 99bce86..0000000 --- a/backend/internal/database/migrations/0020_projects.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE IF NOT EXISTS projects ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name VARCHAR(255) NOT NULL UNIQUE, - description TEXT NOT NULL, - owner_user_id INTEGER NOT NULL, - FOREIGN KEY (owner_user_id) REFERENCES users (id) -); - -CREATE INDEX IF NOT EXISTS projects_user_id_index ON projects (owner_user_id); \ No newline at end of file diff --git a/backend/internal/database/migrations/0035_weekly_report.sql b/backend/internal/database/migrations/0035_weekly_report.sql deleted file mode 100644 index b0cbe82..0000000 --- a/backend/internal/database/migrations/0035_weekly_report.sql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE TABLE IF NOT EXISTS weekly_reports ( - report_id INTEGER PRIMARY KEY AUTOINCREMENT, - user_id INTEGER NOT NULL, - project_id INTEGER NOT NULL, - week INTEGER NOT NULL, - development_time INTEGER, - meeting_time INTEGER, - admin_time INTEGER, - own_work_time INTEGER, - study_time INTEGER, - testing_time INTEGER, - signed_by INTEGER, - UNIQUE(user_id, project_id, week), - FOREIGN KEY (user_id) REFERENCES users(id), - FOREIGN KEY (project_id) REFERENCES projects(id), - FOREIGN KEY (signed_by) REFERENCES users(id) -); \ No newline at end of file diff --git a/backend/internal/database/migrations/0049_project_role.sql b/backend/internal/database/migrations/0049_project_role.sql deleted file mode 100644 index f7e7151..0000000 --- a/backend/internal/database/migrations/0049_project_role.sql +++ /dev/null @@ -1,9 +0,0 @@ --- This table represents the possible role a user can have in a project. --- It has nothing to do with the site admin table. -CREATE TABLE IF NOT EXISTS project_role ( - p_role TEXT PRIMARY KEY -); - --- Insert the possible roles a user can have in a project. -INSERT OR IGNORE INTO project_role (p_role) VALUES ('project_manager'); -INSERT OR IGNORE INTO project_role (p_role) VALUES ('member'); diff --git a/backend/internal/database/migrations/0050_user_roles.sql b/backend/internal/database/migrations/0050_user_roles.sql deleted file mode 100644 index d3e614d..0000000 --- a/backend/internal/database/migrations/0050_user_roles.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE IF NOT EXISTS user_roles ( - user_id INTEGER NOT NULL, - project_id INTEGER NOT NULL, - p_role TEXT NOT NULL, -- 'project_manager' or 'member' - FOREIGN KEY (user_id) REFERENCES users (id) - FOREIGN KEY (project_id) REFERENCES projects (id) - FOREIGN KEY (p_role) REFERENCES project_role (p_role) - PRIMARY KEY (user_id, project_id) -); \ No newline at end of file diff --git a/backend/internal/database/migrations/0060_site_admin.sql b/backend/internal/database/migrations/0060_site_admin.sql deleted file mode 100644 index 14de60d..0000000 --- a/backend/internal/database/migrations/0060_site_admin.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE IF NOT EXISTS site_admin ( - admin_id INTEGER PRIMARY KEY, - FOREIGN KEY (admin_id) REFERENCES users (id) ON DELETE CASCADE -) \ No newline at end of file diff --git a/backend/internal/database/migrations/README.md b/backend/internal/database/migrations/README.md deleted file mode 100644 index 8a0c271..0000000 --- a/backend/internal/database/migrations/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Database migrations - -This directory contains all the database migrations for the backend. - -[!WARNING] -Keep in mind that these migrations are **not yet stable**. -In practice, this means that the database schema may change at any time, and that the migrations may not be backwards compatible. - -## Running migrations - -In the root of the backend directory, run: - -```bash -make migrate -``` diff --git a/backend/internal/database/sample_data/0010_sample_data.sql b/backend/internal/database/sample_data/0010_sample_data.sql deleted file mode 100644 index f519608..0000000 --- a/backend/internal/database/sample_data/0010_sample_data.sql +++ /dev/null @@ -1,220 +0,0 @@ -INSERT OR IGNORE INTO users(username, password) -VALUES ("admin", "123"), - ("user", "123"), - ("user2", "123"), - ("John", "123"), - ("Emma", "123"), - ("Michael", "123"), - ("Liam", "123"), - ("Oliver", "123"), - ("Amelia", "123"), - ("Benjamin", "123"), - ("Mia", "123"), - ("Elijah", "123"), - ("Charlotte", "123"), - ("Henry", "123"), - ("Harper", "123"), - ("Lucas", "123"), - ("Emily", "123"), - ("Alexander", "123"), - ("Daniel", "123"), - ("Ella", "123"), - ("Matthew", "123"), - ("Madison", "123"), - ("Samuel", "123"), - ("Avery", "123"), - ("Sofia", "123"), - ("David", "123"), - ("Victoria", "123"), - ("Jackson", "123"), - ("Abigail", "123"), - ("Gabriel", "123"), - ("Luna", "123"), - ("Wyatt", "123"), - ("Chloe", "123"), - ("Nora", "123"), - ("Joshua", "123"), - ("Hazel", "123"), - ("Riley", "123"), - ("Scarlett", "123"), - ("Aria", "123"), - ("Carter", "123"), - ("Grace", "123"), - ("Jayden", "123"), - ("Hannah", "123"), - ("Zoe", "123"), - ("Luke", "123"), - ("Sophia", "123"), - ("Jack", "123"), - ("Isabella", "123"), - ("William", "123"), - ("Mason", "123"), - ("Evelyn", "123"), - ("James", "123"), - ("Cynthia", "123"), - ("Abraham", "123"), - ("Ava", "123"), - ("Aiden", "123"), - ("Natalie", "123"), - ("Lily", "123"), - ("Olivia", "123"), - ("Alexander", "123"), - ("Ethan", "123"), - ("Mila", "123"), - ("Evelyn", "123"), - ("Logan", "123"), - ("Riley", "123"), - ("Grace", "123"), - ("Arnold", "123"), - ("Connor", "123"), - ("Samantha", "123"), - ("Emma", "123"), - ("Sarah", "123"), - ("Nathan", "123"), - ("Layla", "123"), - ("Ryan", "123"), - ("Zoey", "123"), - ("Megan", "123"), - ("Christian", "123"), - ("Eva", "123"), - ("Isaac", "123"), - ("Michaela", "123"), - ("Caroline", "123"), - ("Elijah", "123"), - ("Elena", "123"), - ("Julian", "123"), - ("Sophie", "123"), - ("Gabriella", "123"), - ("Cole", "123"), - ("Hannah", "123"), - ("Lucy", "123"), - ("Katherine", "123"), - ("Benjamin", "123"), - ("Ella", "123"), - ("Evan", "123"); - -INSERT OR IGNORE INTO projects(name, description, owner_user_id) -VALUES ("projecttest1", "Description for projecttest1", 1), - ("projecttest2", "Description for projecttest2", 1), - ("projecttest3", "Description for projecttest3", 1), - ("projecttest4", "Description for projecttest4", 1), - ("projecttest5", "Description for projecttest5", 1), - ("projecttest6", "Description for projecttest6", 1), - ("projecttest7", "Description for projecttest7", 1), - ("projecttest8", "Description for projecttest8", 1), - ("projecttest9", "Description for projecttest9", 1), - ("projecttest10", "Description for projecttest10", 1), - ("projecttest11", "Description for projecttest11", 1), - ("projecttest12", "Description for projecttest12", 1), - ("projecttest13", "Description for projecttest13", 1), - ("projecttest14", "Description for projecttest14", 1), - ("projecttest15", "Description for projecttest15", 1); - -INSERT OR IGNORE INTO user_roles(user_id,project_id,p_role) -VALUES (1,1,"project_manager"), - (1,2,"project_manager"), - (1,3,"project_manager"), - (1,4,"project_manager"), - (1,5,"project_manager"), - (1,6,"project_manager"), - (1,7,"project_manager"), - (1,8,"project_manager"), - (1,9,"project_manager"), - (1,10,"project_manager"), - (1,11,"project_manager"), - (1,12,"project_manager"), - (1,13,"project_manager"), - (1,14,"project_manager"), - (1,15,"project_manager"), - (2,1,"project_manager"), - (2,2,"member"), - (2,3,"member"), - (2,4,"member"), - (2,5,"member"), - (2,6,"member"), - (2,7,"member"), - (2,8,"member"), - (2,9,"member"), - (2,10,"member"), - (2,11,"member"), - (2,12,"member"), - (2,13,"member"), - (2,14,"member"), - (2,15,"member"), - (3,1,"member"), - (3,2,"member"), - (3,3,"member"), - (3,4,"member"), - (3,5,"member"), - (3,6,"member"), - (3,7,"member"), - (3,8,"member"), - (3,9,"member"), - (3,10,"member"), - (3,11,"member"), - (3,12,"member"), - (3,13,"member"), - (3,14,"member"), - (3,15,"member"), - (4,1,"member"), - (4,2,"member"), - (4,3,"member"), - (4,4,"member"), - (4,5,"member"), - (4,6,"member"), - (4,7,"member"), - (4,8,"member"), - (4,9,"member"), - (4,10,"member"), - (4,11,"member"), - (4,12,"member"), - (4,13,"member"), - (4,14,"member"), - (4,15,"member"), - (5,1,"member"), - (5,2,"member"), - (5,3,"member"), - (5,4,"member"), - (5,5,"member"), - (5,6,"member"), - (5,7,"member"), - (5,8,"member"), - (5,9,"member"), - (5,10,"member"), - (5,11,"member"), - (5,12,"member"), - (5,13,"member"), - (5,14,"member"), - (5,15,"member"); - -INSERT OR IGNORE INTO weekly_reports (user_id, project_id, week, development_time, meeting_time, admin_time, own_work_time, study_time, testing_time, signed_by) -VALUES (2, 1, 12, 100, 50, 30, 150, 80, 20, NULL), - (3, 1, 12, 200, 80, 20, 200, 100, 30, NULL), - (3, 1, 14, 150, 70, 40, 180, 90, 25, NULL), - (3, 2, 12, 120, 60, 35, 160, 85, 15, NULL), - (3, 3, 12, 180, 90, 25, 190, 110, 40, NULL), - (2, 1, 13, 130, 70, 40, 170, 95, 35, NULL), - (3, 1, 15, 140, 60, 50, 200, 120, 30, NULL), - (2, 2, 11, 110, 50, 45, 140, 70, 25, NULL), - (3, 3, 14, 170, 80, 30, 180, 100, 35, NULL), - (3, 3, 15, 200, 100, 20, 220, 130, 45, NULL), - (2, 4, 12, 120, 60, 40, 160, 80, 30, NULL), - (3, 5, 14, 150, 70, 30, 180, 90, 25, NULL), - (3, 5, 15, 180, 90, 20, 190, 110, 35, NULL), - (2, 6, 11, 100, 50, 35, 130, 60, 20, NULL), - (3, 7, 14, 170, 80, 25, 180, 100, 30, NULL), - (2, 8, 12, 130, 70, 30, 170, 90, 25, NULL), - (2, 8, 13, 150, 80, 20, 180, 110, 35, NULL), - (3, 9, 12, 140, 60, 40, 180, 100, 30, NULL), - (3, 10, 11, 120, 50, 45, 150, 70, 25, NULL), - (2, 11, 13, 110, 60, 35, 140, 80, 30, NULL), - (3, 12, 12, 160, 70, 30, 180, 100, 35, NULL), - (3, 12, 13, 180, 90, 25, 190, 110, 40, NULL), - (3, 12, 14, 200, 100, 20, 220, 130, 45, NULL), - (2, 13, 11, 100, 50, 45, 130, 60, 20, NULL), - (2, 13, 12, 120, 60, 40, 160, 80, 30, NULL), - (3, 14, 13, 140, 70, 30, 160, 90, 35, NULL), - (3, 15, 12, 150, 80, 25, 180, 100, 30, NULL), - (3, 15, 13, 170, 90, 20, 190, 110, 35, NULL); - -INSERT OR IGNORE INTO site_admin VALUES (1); \ No newline at end of file diff --git a/backend/internal/handlers/projects/AddUserToProject.go b/backend/internal/handlers/projects/AddUserToProject.go deleted file mode 100644 index 3195314..0000000 --- a/backend/internal/handlers/projects/AddUserToProject.go +++ /dev/null @@ -1,42 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// AddUserToProjectHandler is a handler that adds a user to a project with a specified role -func AddUserToProjectHandler(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - pm_name := claims["name"].(string) - - project := c.Params("projectName") - username := c.Query("userName") - - // Check if the user is a project manager - isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isPM { - log.Info("User: ", pm_name, " is not a project manager in project: ", project) - return c.Status(403).SendString("User is not a project manager") - } - - // Add the user to the project with the specified role - err = db.GetDb(c).AddUserToProject(username, project, "member") - if err != nil { - log.Info("Error adding user to project:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return success message - log.Info("User : ", username, " added to project: ", project) - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/projects/ChangeProjectName.go b/backend/internal/handlers/projects/ChangeProjectName.go deleted file mode 100644 index f6831db..0000000 --- a/backend/internal/handlers/projects/ChangeProjectName.go +++ /dev/null @@ -1,43 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// ChangeProjectName is a handler that changes the name of a project -func ChangeProjectName(c *fiber.Ctx) error { - - //check token and get username of current user - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract the necessary parameters from the request - projectName := c.Params("projectName") - newProjectName := c.Query("newProjectName") - - // Check if user is site admin - issiteadmin, err := db.GetDb(c).IsSiteAdmin(username) - if err != nil { - log.Warn("Error checking if siteadmin:", err) - return c.Status(500).SendString(err.Error()) - } else if !issiteadmin { - log.Warn("User is not siteadmin") - return c.Status(401).SendString("User is not siteadmin") - } - - - // Perform the project name change - err = db.GetDb(c).ChangeProjectName(projectName, newProjectName) - if err != nil { - log.Warn("Error changing project name:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return a success message - return c.Status(200).SendString("Project name changed successfully") -} diff --git a/backend/internal/handlers/projects/CreateProject.go b/backend/internal/handlers/projects/CreateProject.go deleted file mode 100644 index cef2f2b..0000000 --- a/backend/internal/handlers/projects/CreateProject.go +++ /dev/null @@ -1,30 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/golang-jwt/jwt/v5" -) - -// CreateProject is a simple handler that creates a new project -func CreateProject(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - - p := new(types.NewProject) - if err := c.BodyParser(p); err != nil { - return c.Status(400).SendString(err.Error()) - } - - // Get the username from the token and set it as the owner of the project - // This is ugly but - claims := user.Claims.(jwt.MapClaims) - owner := claims["name"].(string) - - if err := db.GetDb(c).AddProject(p.Name, p.Description, owner); err != nil { - return c.Status(500).SendString(err.Error()) - } - - return c.Status(200).SendString("Project added") -} diff --git a/backend/internal/handlers/projects/DeleteProject.go b/backend/internal/handlers/projects/DeleteProject.go deleted file mode 100644 index 415424a..0000000 --- a/backend/internal/handlers/projects/DeleteProject.go +++ /dev/null @@ -1,19 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" -) - -func DeleteProject(c *fiber.Ctx) error { - - projectID := c.Params("projectID") - username := c.Params("username") - - if err := db.GetDb(c).DeleteProject(projectID, username); err != nil { - return c.Status(500).SendString((err.Error())) - } - - return c.Status(200).SendString("Project deleted") -} diff --git a/backend/internal/handlers/projects/GetProject.go b/backend/internal/handlers/projects/GetProject.go deleted file mode 100644 index 03333ce..0000000 --- a/backend/internal/handlers/projects/GetProject.go +++ /dev/null @@ -1,38 +0,0 @@ -package projects - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" -) - -// GetProject retrieves a specific project by its ID -func GetProject(c *fiber.Ctx) error { - // Extract the project ID from the request parameters or body - projectID := c.Params("projectID") - if projectID == "" { - log.Info("No project ID provided") - return c.Status(400).SendString("No project ID provided") - } - log.Info("Getting project with ID: ", projectID) - - // Parse the project ID into an integer - projectIDInt, err := strconv.Atoi(projectID) - if err != nil { - log.Info("Invalid project ID") - return c.Status(400).SendString("Invalid project ID") - } - - // Get the project from the database by its ID - project, err := db.GetDb(c).GetProject(projectIDInt) - if err != nil { - log.Info("Error getting project:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return the project as JSON - log.Info("Returning project: ", project.Name) - return c.JSON(project) -} diff --git a/backend/internal/handlers/projects/GetProjectTimes.go b/backend/internal/handlers/projects/GetProjectTimes.go deleted file mode 100644 index 573a95e..0000000 --- a/backend/internal/handlers/projects/GetProjectTimes.go +++ /dev/null @@ -1,63 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func GetProjectTimesHandler(c *fiber.Ctx) error { - // Get the username from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Get project - projectName := c.Params("projectName") - if projectName == "" { - log.Info("No project name provided") - return c.Status(400).SendString("No project name provided") - } - - // Get all users in the project and roles - userProjects, err := db.GetDb(c).GetAllUsersProject(projectName) - if err != nil { - log.Info("Error getting users in project:", err) - return c.Status(500).SendString(err.Error()) - } - - // If the user is member - isMember := false - for _, userProject := range userProjects { - if userProject.Username == username { - isMember = true - break - } - } - - // If the user is admin - if !isMember { - isAdmin, err := db.GetDb(c).IsSiteAdmin(username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - if !isAdmin { - log.Info("User is neither a project member nor a site admin:", username) - return c.Status(403).SendString("User is neither a project member nor a site admin") - } - } - - // Get project times - projectTimes, err := db.GetDb(c).GetProjectTimes(projectName) - if err != nil { - log.Info("Error getting project times:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return project times as JSON - log.Info("Returning project times for project:", projectName) - return c.JSON(projectTimes) -} diff --git a/backend/internal/handlers/projects/GetUserProject.go b/backend/internal/handlers/projects/GetUserProject.go deleted file mode 100644 index 6c80515..0000000 --- a/backend/internal/handlers/projects/GetUserProject.go +++ /dev/null @@ -1,26 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" -) - -// GetUserProjects returns all projects that the user is a member of -func GetUserProjects(c *fiber.Ctx) error { - username := c.Params("username") - if username == "" { - log.Info("No username provided") - return c.Status(400).SendString("No username provided") - } - - // Then dip into the database to get the projects - projects, err := db.GetDb(c).GetProjectsForUser(username) - if err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Return a json serialized list of projects - return c.JSON(projects) -} diff --git a/backend/internal/handlers/projects/IsProjectManager.go b/backend/internal/handlers/projects/IsProjectManager.go deleted file mode 100644 index 678fad5..0000000 --- a/backend/internal/handlers/projects/IsProjectManager.go +++ /dev/null @@ -1,32 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// IsProjectManagerHandler is a handler that checks if a user is a project manager for a given project -func IsProjectManagerHandler(c *fiber.Ctx) error { - // Get the username from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract necessary parameters from the request query string - projectName := c.Params("projectName") - - log.Info("Checking if user ", username, " is a project manager for project ", projectName) - - // Check if the user is a project manager for the specified project - isManager, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking project manager status:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return the result as JSON - return c.JSON(fiber.Map{"isProjectManager": isManager}) -} diff --git a/backend/internal/handlers/projects/ListAllUserProjects.go b/backend/internal/handlers/projects/ListAllUserProjects.go deleted file mode 100644 index e0bcaf5..0000000 --- a/backend/internal/handlers/projects/ListAllUserProjects.go +++ /dev/null @@ -1,55 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func ListAllUsersProject(c *fiber.Ctx) error { - // Extract the project name from the request parameters or body - projectName := c.Params("projectName") - if projectName == "" { - log.Info("No project name provided") - return c.Status(400).SendString("No project name provided") - } - - // Get the user token - userToken := c.Locals("user").(*jwt.Token) - claims := userToken.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Check if the user is a project manager for the specified project - isManager, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking project manager status:", err) - return c.Status(500).SendString(err.Error()) - } - - // If the user is not a project manager, check if the user is a site admin - if !isManager { - isAdmin, err := db.GetDb(c).IsSiteAdmin(username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - if !isAdmin { - log.Info("User is neither a project manager nor a site admin:", username) - return c.Status(403).SendString("User is neither a project manager nor a site admin") - } - } - - // Get all users associated with the project from the database - users, err := db.GetDb(c).GetAllUsersProject(projectName) - if err != nil { - log.Info("Error getting users for project:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning users for project: ", projectName) - - // Return the list of users as JSON - return c.JSON(users) -} diff --git a/backend/internal/handlers/projects/ProjectRoleChange.go b/backend/internal/handlers/projects/ProjectRoleChange.go deleted file mode 100644 index 6c5d455..0000000 --- a/backend/internal/handlers/projects/ProjectRoleChange.go +++ /dev/null @@ -1,51 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// ProjectRoleChange is a handler that changes a user's role within a project -func ProjectRoleChange(c *fiber.Ctx) error { - - //check token and get username of current user - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract the necessary parameters from the request - data := new(types.RoleChange) - if err := c.BodyParser(data); err != nil { - log.Info("error parsing username, project or role") - return c.Status(400).SendString(err.Error()) - } - - // Check if user is trying to change its own role - if username == data.UserName { - log.Info("Can't change your own role") - return c.Status(403).SendString("Can't change your own role") - } - - log.Info("Changing role for user: ", data.UserName, " in project: ", data.Projectname, " to: ", data.Role) - - // Dubble diping and checcking if current user is - if ismanager, err := db.GetDb(c).IsProjectManager(username, data.Projectname); err != nil { - log.Warn("Error checking if projectmanager:", err) - return c.Status(500).SendString(err.Error()) - } else if !ismanager { - log.Warn("User is not projectmanager") - return c.Status(401).SendString("User is not projectmanager") - } - - // Change the user's role within the project in the database - if err := db.GetDb(c).ChangeUserRole(data.UserName, data.Projectname, data.Role); err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Return a success message - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/projects/PromoteToPm.go b/backend/internal/handlers/projects/PromoteToPm.go deleted file mode 100644 index c587b65..0000000 --- a/backend/internal/handlers/projects/PromoteToPm.go +++ /dev/null @@ -1,55 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// @Summary Promote to project manager -// @Description Promote a user to project manager -// @Tags Auth -// @Security JWT -// @Accept plain -// @Produce plain -// @Param projectName path string true "Project name" -// @Param userName query string true "User name" -// @Failure 500 {string} string "Internal server error" -// @Failure 403 {string} string "Forbidden" -// @Router /promote/{projectName} [put] -// -// Login logs in a user and returns a JWT token -// Promote to project manager -func PromoteToPm(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - pm_name := claims["name"].(string) - - project := c.Params("projectName") - new_pm_name := c.Query("userName") - - // Check if the user is a project manager - isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isPM { - log.Info("User: ", pm_name, " is not a project manager in project: ", project) - return c.Status(403).SendString("User is not a project manager") - } - - // Add the user to the project with the specified role - err = db.GetDb(c).ChangeUserRole(new_pm_name, project, "project_manager") - if err != nil { - log.Info("Error promoting user to project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return success message - log.Info("User : ", new_pm_name, " promoted to project manager in project: ", project) - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/projects/RemoveProject.go b/backend/internal/handlers/projects/RemoveProject.go deleted file mode 100644 index 7b140dd..0000000 --- a/backend/internal/handlers/projects/RemoveProject.go +++ /dev/null @@ -1,35 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func RemoveProject(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Check if the user is a site admin - isAdmin, err := db.GetDb(c).IsSiteAdmin(username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isAdmin { - log.Info("User is not a site admin:", username) - return c.Status(403).SendString("User is not a site admin") - } - - projectName := c.Params("projectName") - - if err := db.GetDb(c).RemoveProject(projectName); err != nil { - return c.Status(500).SendString((err.Error())) - } - - return c.Status(200).SendString("Project deleted") -} diff --git a/backend/internal/handlers/projects/RemoveUserFromProject.go b/backend/internal/handlers/projects/RemoveUserFromProject.go deleted file mode 100644 index 7aefcf8..0000000 --- a/backend/internal/handlers/projects/RemoveUserFromProject.go +++ /dev/null @@ -1,40 +0,0 @@ -package projects - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func RemoveUserFromProject(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - pm_name := claims["name"].(string) - - project := c.Params("projectName") - username := c.Query("userName") - - // Check if the user is a project manager - isPM, err := db.GetDb(c).IsProjectManager(pm_name, project) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !isPM { - log.Info("User: ", pm_name, " is not a project manager in project: ", project) - return c.Status(403).SendString("User is not a project manager") - } - - // Remove the user from the project - if err = db.GetDb(c).RemoveUserFromProject(username, project); err != nil { - log.Info("Error removing user from project:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return success message - log.Info("User : ", username, " removed from project: ", project) - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/reports/DeleteReport.go b/backend/internal/handlers/reports/DeleteReport.go deleted file mode 100644 index b3ae7de..0000000 --- a/backend/internal/handlers/reports/DeleteReport.go +++ /dev/null @@ -1,22 +0,0 @@ -package reports - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" -) - -func DeleteReport(c *fiber.Ctx) error { - reportID := c.Params("reportID") - reportIDInt, err := strconv.Atoi(reportID) - if err != nil { - return c.Status(400).SendString("Invalid report ID") - } - - if err := db.GetDb(c).DeleteReport(reportIDInt); err != nil { - return c.Status(500).SendString((err.Error())) - } - - return c.Status(200).SendString("Weekly report deleted") -} diff --git a/backend/internal/handlers/reports/GetAllWeeklyReports.go b/backend/internal/handlers/reports/GetAllWeeklyReports.go deleted file mode 100644 index d9778b8..0000000 --- a/backend/internal/handlers/reports/GetAllWeeklyReports.go +++ /dev/null @@ -1,56 +0,0 @@ -package reports - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// GetAllWeeklyReports retrieves all weekly reports for a user in a specific project -func GetAllWeeklyReports(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract project name and week from query parameters - projectName := c.Params("projectName") - target_user := c.Query("targetUser") // The user whose reports are being requested - - // If the target user is not empty, use it as the username - if target_user == "" { - target_user = username - } - - log.Info(username, " trying to get all weekly reports for: ", target_user) - - if projectName == "" { - log.Info("Missing project name") - return c.Status(400).SendString("Missing project name") - } - - // If the user is not a project manager, they can only view their own reports - pm, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !(pm || target_user == username) { - log.Info("Unauthorized access") - return c.Status(403).SendString("Unauthorized access") - } - - // Retrieve weekly reports for the user in the project from the database - reports, err := db.GetDb(c).GetAllWeeklyReports(target_user, projectName) - if err != nil { - log.Error("Error getting weekly reports for user:", target_user, "in project:", projectName, ":", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning weekly report") - // Return the retrieved weekly report - return c.JSON(reports) -} diff --git a/backend/internal/handlers/reports/GetUnsignedReports.go b/backend/internal/handlers/reports/GetUnsignedReports.go deleted file mode 100644 index 9525f55..0000000 --- a/backend/internal/handlers/reports/GetUnsignedReports.go +++ /dev/null @@ -1,45 +0,0 @@ -package reports - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func GetUnsignedReports(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - projectManagerUsername := claims["name"].(string) - - // Extract project name and week from query parameters - projectName := c.Params("projectName") - - log.Info("Getting unsigned reports for") - - if projectName == "" { - log.Info("Missing project name") - return c.Status(400).SendString("Missing project name") - } - - // Get the project manager's ID - isProjectManager, err := db.GetDb(c).IsProjectManager(projectManagerUsername, projectName) - if err != nil { - log.Info("Failed to get project manager ID") - return c.Status(500).SendString("Failed to get project manager ID") - } - log.Info("User is Project Manager: ", isProjectManager) - - // Call the database function to get the unsigned weekly reports - reports, err := db.GetDb(c).GetUnsignedWeeklyReports(projectName) - if err != nil { - log.Info("Error getting unsigned weekly reports:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning unsigned reports") - // Return the list of unsigned reports - return c.JSON(reports) -} diff --git a/backend/internal/handlers/reports/GetWeeklyReport.go b/backend/internal/handlers/reports/GetWeeklyReport.go deleted file mode 100644 index 2b6827e..0000000 --- a/backend/internal/handlers/reports/GetWeeklyReport.go +++ /dev/null @@ -1,65 +0,0 @@ -package reports - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// Handler for retrieving weekly report -func GetWeeklyReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the request - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract project name and week from query parameters - projectName := c.Query("projectName") - week := c.Query("week") - target_user := c.Query("targetUser") // The user whose report is being requested - - // If the target user is not empty, use it as the username - if target_user == "" { - target_user = username - } - - log.Info(username, " trying to get weekly report for: ", target_user) - - if projectName == "" || week == "" { - log.Info("Missing project name or week number") - return c.Status(400).SendString("Missing project name or week number") - } - - // Convert week to integer - weekInt, err := strconv.Atoi(week) - if err != nil { - log.Info("Invalid week number") - return c.Status(400).SendString("Invalid week number") - } - - // If the token user is not an admin, check if the target user is the same as the token user - pm, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - if !(pm || target_user == username) { - log.Info("Unauthorized access") - return c.Status(403).SendString("Unauthorized access") - } - - // Call the database function to get the weekly report - report, err := db.GetDb(c).GetWeeklyReport(target_user, projectName, weekInt) - if err != nil { - log.Info("Error getting weekly report from db:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning weekly report") - // Return the retrieved weekly report - return c.JSON(report) -} diff --git a/backend/internal/handlers/reports/SignReport.go b/backend/internal/handlers/reports/SignReport.go deleted file mode 100644 index a486ecc..0000000 --- a/backend/internal/handlers/reports/SignReport.go +++ /dev/null @@ -1,41 +0,0 @@ -package reports - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func SignReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - projectManagerUsername := claims["name"].(string) - - // Extract report ID from the path - reportId, err := strconv.Atoi(c.Params("reportId")) - if err != nil { - log.Info("Invalid report ID") - return c.Status(400).SendString("Invalid report ID") - } - - // Get the project manager's ID - projectManagerID, err := db.GetDb(c).GetUserId(projectManagerUsername) - if err != nil { - log.Info("Failed to get project manager ID for user: ", projectManagerUsername) - return c.Status(500).SendString("Failed to get project manager ID") - } - - // Call the database function to sign the weekly report - err = db.GetDb(c).SignWeeklyReport(reportId, projectManagerID) - if err != nil { - log.Info("Error signing weekly report:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Project manager ID: ", projectManagerID, " signed report ID: ", reportId) - return c.Status(200).SendString("Weekly report signed successfully") -} diff --git a/backend/internal/handlers/reports/Statistics.go b/backend/internal/handlers/reports/Statistics.go deleted file mode 100644 index dac017d..0000000 --- a/backend/internal/handlers/reports/Statistics.go +++ /dev/null @@ -1,56 +0,0 @@ -package reports - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func GetStatistics(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Extract project name from query parameters - projectName := c.Query("projectName") - userNameParam := c.Query("userName") - - log.Info(username, " trying to get statistics for project: ", projectName) - - if projectName == "" { - log.Info("Missing project name") - return c.Status(400).SendString("Missing project name") - } - - // Check if the user is a project manager - pm, err := db.GetDb(c).IsProjectManager(username, projectName) - if err != nil { - log.Info("Error checking if user is project manager:", err) - return c.Status(500).SendString(err.Error()) - } - - // Bail if the user is not a PM or checking its own statistics - if !pm && userNameParam != "" && userNameParam != username { - log.Info("Unauthorized access for user: ", username, "trying to access project: ", projectName, "statistics for user: ", userNameParam) - return c.Status(403).SendString("Unauthorized access") - } - - if pm && userNameParam != "" { - username = userNameParam - } - - // Retrieve statistics for the project from the database - statistics, err := db.GetDb(c).ReportStatistics(username, projectName) - if err != nil { - log.Error("Error getting statistics for project:", projectName, ":", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning statistics") - // Return the retrieved statistics - return c.JSON(statistics) - -} diff --git a/backend/internal/handlers/reports/SubmitWeeklyReport.go b/backend/internal/handlers/reports/SubmitWeeklyReport.go deleted file mode 100644 index 900aa03..0000000 --- a/backend/internal/handlers/reports/SubmitWeeklyReport.go +++ /dev/null @@ -1,41 +0,0 @@ -package reports - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func SubmitWeeklyReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - report := new(types.NewWeeklyReport) - if err := c.BodyParser(report); err != nil { - log.Info("Error parsing weekly report") - return c.Status(400).SendString(err.Error()) - } - - // Make sure all the fields of the report are valid - if report.Week < 1 || report.Week > 52 { - log.Info("Invalid week number") - return c.Status(400).SendString("Invalid week number") - } - if report.DevelopmentTime < 0 || report.MeetingTime < 0 || report.AdminTime < 0 || report.OwnWorkTime < 0 || report.StudyTime < 0 || report.TestingTime < 0 { - log.Info("Invalid time report") - return c.Status(400).SendString("Invalid time report") - } - - if err := db.GetDb(c).AddWeeklyReport(report.ProjectName, username, report.Week, report.DevelopmentTime, report.MeetingTime, report.AdminTime, report.OwnWorkTime, report.StudyTime, report.TestingTime); err != nil { - log.Info("Error adding weekly report to db:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Weekly report added") - return c.Status(200).SendString("Time report added") -} diff --git a/backend/internal/handlers/reports/UnsignReport.go b/backend/internal/handlers/reports/UnsignReport.go deleted file mode 100644 index 45943de..0000000 --- a/backend/internal/handlers/reports/UnsignReport.go +++ /dev/null @@ -1,41 +0,0 @@ -package reports - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func UnsignReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - projectManagerUsername := claims["name"].(string) - - // Extract report ID from the path - reportId, err := strconv.Atoi(c.Params("reportId")) - if err != nil { - log.Info("Invalid report ID") - return c.Status(400).SendString("Invalid report ID") - } - - // Get the project manager's ID - projectManagerID, err := db.GetDb(c).GetUserId(projectManagerUsername) - if err != nil { - log.Info("Failed to get project manager ID for user: ", projectManagerUsername) - return c.Status(500).SendString("Failed to get project manager ID") - } - - // Call the database function to sign the weekly report - err = db.GetDb(c).UnsignWeeklyReport(reportId, projectManagerID) - if err != nil { - log.Info("Error Unsigning weekly report:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Project manager ID: ", projectManagerID, " unsigned report ID: ", reportId) - return c.Status(200).SendString("Weekly report unsigned successfully") -} diff --git a/backend/internal/handlers/reports/UpdateWeeklyReport.go b/backend/internal/handlers/reports/UpdateWeeklyReport.go deleted file mode 100644 index 3ab835d..0000000 --- a/backend/internal/handlers/reports/UpdateWeeklyReport.go +++ /dev/null @@ -1,44 +0,0 @@ -package reports - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -func UpdateWeeklyReport(c *fiber.Ctx) error { - // Extract the necessary parameters from the token - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - username := claims["name"].(string) - - // Parse the request body into an UpdateWeeklyReport struct - var updateReport types.UpdateWeeklyReport - if err := c.BodyParser(&updateReport); err != nil { - log.Info("Error parsing weekly report") - return c.Status(400).SendString(err.Error()) - } - - // Make sure all the fields of the report are valid - if updateReport.Week < 1 || updateReport.Week > 52 { - log.Info("Invalid week number") - return c.Status(400).SendString("Invalid week number") - } - - if updateReport.DevelopmentTime < 0 || updateReport.MeetingTime < 0 || updateReport.AdminTime < 0 || updateReport.OwnWorkTime < 0 || updateReport.StudyTime < 0 || updateReport.TestingTime < 0 { - log.Info("Invalid time report") - return c.Status(400).SendString("Invalid time report") - } - - // Update the weekly report in the database - if err := db.GetDb(c).UpdateWeeklyReport(updateReport.ProjectName, username, updateReport.Week, updateReport.DevelopmentTime, updateReport.MeetingTime, updateReport.AdminTime, updateReport.OwnWorkTime, updateReport.StudyTime, updateReport.TestingTime); err != nil { - log.Info("Error updating weekly report in db:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("Weekly report updated") - return c.Status(200).SendString("Weekly report updated") -} diff --git a/backend/internal/handlers/users/ChangeUserName.go b/backend/internal/handlers/users/ChangeUserName.go deleted file mode 100644 index 75032e4..0000000 --- a/backend/internal/handlers/users/ChangeUserName.go +++ /dev/null @@ -1,44 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// ChangeUserName changes a user's username in the database -func ChangeUserName(c *fiber.Ctx) error { - // Check token and get username of current user - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - adminUsername := claims["name"].(string) - log.Info(adminUsername) - - // Extract the necessary parameters from the request - data := new(types.StrNameChange) - if err := c.BodyParser(data); err != nil { - log.Info("Error parsing username") - return c.Status(400).SendString(err.Error()) - } - - // Check if the current user is an admin - isAdmin, err := db.GetDb(c).IsSiteAdmin(adminUsername) - if err != nil { - log.Warn("Error checking if admin:", err) - return c.Status(500).SendString(err.Error()) - } else if !isAdmin { - log.Warn("Tried changing name when not admin") - return c.Status(401).SendString("You cannot change name unless you are an admin") - } - - // Change the user's name in the database - if err := db.GetDb(c).ChangeUserName(data.PrevName, data.NewName); err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Return a success message - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/users/ChangeUserPassword.go b/backend/internal/handlers/users/ChangeUserPassword.go deleted file mode 100644 index 1596247..0000000 --- a/backend/internal/handlers/users/ChangeUserPassword.go +++ /dev/null @@ -1,42 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// ChangeUserPassword is a handler that changes the password of a user -func ChangeUserPassword(c *fiber.Ctx) error { - - //Check token and get username of current user - user := c.Locals("user").(*jwt.Token) - claims := user.Claims.(jwt.MapClaims) - admin := claims["name"].(string) - - // Extract the necessary parameters from the request - username := c.Params("username") - newPassword := c.Query("newPassword") - - // Check if user is site admin - issiteadmin, err := db.GetDb(c).IsSiteAdmin(admin) - if err != nil { - log.Warn("Error checking if siteadmin:", err) - return c.Status(500).SendString(err.Error()) - } else if !issiteadmin { - log.Warn("User is not siteadmin") - return c.Status(401).SendString("User is not siteadmin") - } - - // Perform the password change - err = db.GetDb(c).ChangeUserPassword(username, newPassword) - if err != nil { - log.Warn("Error changing password:", err) - return c.Status(500).SendString(err.Error()) - } - - // Return a success message - return c.Status(200).SendString("Password changed successfully") -} diff --git a/backend/internal/handlers/users/GetUserName.go b/backend/internal/handlers/users/GetUserName.go deleted file mode 100644 index 82b6cc8..0000000 --- a/backend/internal/handlers/users/GetUserName.go +++ /dev/null @@ -1,32 +0,0 @@ -package users - -import ( - "strconv" - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" -) - -// Return the username of a user given their user id -func GetUserName(c *fiber.Ctx) error { - // Check the query params for userId - user_id_string := c.Query("userId") - if user_id_string == "" { - return c.Status(400).SendString("Missing user id") - } - - // Convert to int - user_id, err := strconv.Atoi(user_id_string) - if err != nil { - return c.Status(400).SendString("Invalid user id") - } - - // Get the username from the database - username, err := db.GetDb(c).GetUserName(user_id) - if err != nil { - return c.Status(500).SendString(err.Error()) - } - - // Send the nuclear launch codes to north korea - return c.JSON(fiber.Map{"username": username}) -} diff --git a/backend/internal/handlers/users/GetUsersProjects.go b/backend/internal/handlers/users/GetUsersProjects.go deleted file mode 100644 index 10a6ec6..0000000 --- a/backend/internal/handlers/users/GetUsersProjects.go +++ /dev/null @@ -1,22 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" -) - -func GetAllUsersProject(c *fiber.Ctx) error { - // Get all users from a project - projectName := c.Params("projectName") - users, err := db.GetDb(c).GetAllUsersProject(projectName) - if err != nil { - log.Info("Error getting users from project:", err) // Debug print - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning all users") - // Return the list of users as JSON - return c.JSON(users) -} diff --git a/backend/internal/handlers/users/ListAllUsers.go b/backend/internal/handlers/users/ListAllUsers.go deleted file mode 100644 index 5ac5df0..0000000 --- a/backend/internal/handlers/users/ListAllUsers.go +++ /dev/null @@ -1,32 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" -) - -// @Summary ListsAllUsers -// @Description lists all users -// @Tags User -// @Produce json -// @Security JWT -// @Success 200 {array} string "Successfully returned all users" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" -// @Router /users/all [get] -// -// ListAllUsers returns a list of all users in the application database -func ListAllUsers(c *fiber.Ctx) error { - // Get all users from the database - users, err := db.GetDb(c).GetAllUsersApplication() - if err != nil { - log.Info("Error getting users from db:", err) // Debug print - return c.Status(500).SendString(err.Error()) - } - - log.Info("Returning all users") - // Return the list of users as JSON - return c.JSON(users) -} diff --git a/backend/internal/handlers/users/Login.go b/backend/internal/handlers/users/Login.go deleted file mode 100644 index 42c52a5..0000000 --- a/backend/internal/handlers/users/Login.go +++ /dev/null @@ -1,66 +0,0 @@ -package users - -import ( - "time" - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// @Summary Login -// @Description Logs in a user and returns a JWT token -// @Tags Auth -// @Accept json -// @Produce json -// @Param body body types.NewUser true "User credentials" -// @Success 200 {object} types.Token "JWT token" -// @Failure 400 {string} string "Bad request" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" -// @Router /login [post] -// -// Login logs in a user and returns a JWT token -func Login(c *fiber.Ctx) error { - // The body type is identical to a NewUser - - u := new(types.NewUser) - if err := c.BodyParser(u); err != nil { - log.Warn("Error parsing body") - return c.Status(400).SendString(err.Error()) - } - - log.Info("Username logging in:", u.Username) - if !db.GetDb(c).CheckUser(u.Username, u.Password) { - log.Info("User not found") - return c.SendStatus(fiber.StatusUnauthorized) - } - - isAdmin, err := db.GetDb(c).IsSiteAdmin(u.Username) - if err != nil { - log.Info("Error checking admin status:", err) - return c.Status(500).SendString(err.Error()) - } - // Create the Claims - claims := jwt.MapClaims{ - "name": u.Username, - "admin": isAdmin, - "exp": time.Now().Add(time.Hour * 72).Unix(), - } - - // Create token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - log.Info("Token created for user:", u.Username) - - // Generate encoded token and send it as response. - t, err := token.SignedString([]byte("secret")) - if err != nil { - log.Warn("Error signing token") - return c.SendStatus(fiber.StatusInternalServerError) - } - - println("Successfully signed token for user:", u.Username) - return c.JSON(types.Token{Token: t}) -} diff --git a/backend/internal/handlers/users/LoginRenew.go b/backend/internal/handlers/users/LoginRenew.go deleted file mode 100644 index 3926ce4..0000000 --- a/backend/internal/handlers/users/LoginRenew.go +++ /dev/null @@ -1,50 +0,0 @@ -package users - -import ( - "time" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// @Summary LoginRenews -// @Description Renews the users token. -// @Tags Auth -// @Produce json -// @Security JWT -// @Success 200 {object} types.Token "Successfully signed token for user" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" -// @Router /loginrenew [post] -// -// LoginRenew renews the users token -func LoginRenew(c *fiber.Ctx) error { - user := c.Locals("user").(*jwt.Token) - - log.Info("Renewing token for user:", user.Claims.(jwt.MapClaims)["name"]) - - // Renewing the token means we trust whatever is already in the token - claims := user.Claims.(jwt.MapClaims) - - // 72 hour expiration time - claims["exp"] = time.Now().Add(time.Hour * 72).Unix() - - // Create token with old claims, but new expiration time - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "name": claims["name"], - "admin": claims["admin"], - "exp": claims["exp"], - }) - - // Sign it with top secret key - t, err := token.SignedString([]byte("secret")) - if err != nil { - log.Warn("Error signing token") - return c.SendStatus(fiber.StatusInternalServerError) // 500 - } - - log.Info("Successfully renewed token for user:", user.Claims.(jwt.MapClaims)["name"]) - return c.JSON(types.Token{Token: t}) -} diff --git a/backend/internal/handlers/users/PromoteToAdmin.go b/backend/internal/handlers/users/PromoteToAdmin.go deleted file mode 100644 index 3f0a6d3..0000000 --- a/backend/internal/handlers/users/PromoteToAdmin.go +++ /dev/null @@ -1,45 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" -) - -// @Summary PromoteToAdmin -// @Description Promote chosen user to site admin -// @Tags User -// @Accept json -// @Produce plain -// @Security JWT -// @Param NewUser body types.NewUser true "user info" -// @Success 200 {object} types.Token "Successfully promoted user" -// @Failure 400 {string} string "Bad request" -// @Failure 401 {string} string "Unauthorized" -// @Failure 500 {string} string "Internal server error" -// @Router /promoteToAdmin [post] -// -// PromoteToAdmin promotes a user to a site admin -func PromoteToAdmin(c *fiber.Ctx) error { - // Extract the username from the request body - var newUser types.NewUser - if err := c.BodyParser(&newUser); err != nil { - return c.Status(400).SendString("Bad request") - } - username := newUser.Username - - log.Info("Promoting user to admin:", username) // Debug print - - // Promote the user to a site admin in the database - if err := db.GetDb(c).PromoteToAdmin(username); err != nil { - log.Info("Error promoting user to admin:", err) // Debug print - return c.Status(500).SendString(err.Error()) - } - - log.Info("User promoted to admin successfully:", username) // Debug print - - // Return a success message - return c.SendStatus(fiber.StatusOK) -} diff --git a/backend/internal/handlers/users/Register.go b/backend/internal/handlers/users/Register.go deleted file mode 100644 index b9e0c78..0000000 --- a/backend/internal/handlers/users/Register.go +++ /dev/null @@ -1,38 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - "ttime/internal/types" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" -) - -// @Summary Register -// @Description Register a new user -// @Tags Auth -// @Accept json -// @Produce plain -// @Param NewUser body types.NewUser true "User to register" -// @Success 200 {string} string "User added" -// @Failure 400 {string} string "Bad request" -// @Failure 500 {string} string "Internal server error" -// @Router /register [post] -// -// Register is a simple handler that registers a new user -func Register(c *fiber.Ctx) error { - u := new(types.NewUser) - if err := c.BodyParser(u); err != nil { - log.Warn("Error parsing body") - return c.Status(400).SendString(err.Error()) - } - - log.Info("Adding user:", u.Username) - if err := db.GetDb(c).AddUser(u.Username, u.Password); err != nil { - log.Warn("Error adding user:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("User added:", u.Username) - return c.Status(200).SendString("User added") -} diff --git a/backend/internal/handlers/users/UserDelete.go b/backend/internal/handlers/users/UserDelete.go deleted file mode 100644 index 491a1b3..0000000 --- a/backend/internal/handlers/users/UserDelete.go +++ /dev/null @@ -1,43 +0,0 @@ -package users - -import ( - db "ttime/internal/database" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/log" - "github.com/golang-jwt/jwt/v5" -) - -// @Summary UserDelete -// @Description UserDelete deletes a user from the database -// @Tags User -// @Accept json -// @Produce plain -// @Security JWT -// @Success 200 {string} string "User deleted" -// @Failure 403 {string} string "You can only delete yourself" -// @Failure 500 {string} string "Internal server error" -// @Failure 401 {string} string "Unauthorized" -// @Router /userdelete/{username} [delete] -// -// UserDelete deletes a user from the database -func UserDelete(c *fiber.Ctx) error { - // Read from path parameters - username := c.Params("username") - - // Read username from Locals - auth_username := c.Locals("user").(*jwt.Token).Claims.(jwt.MapClaims)["name"].(string) - - if username == auth_username { - log.Info("User tried to delete itself") - return c.Status(403).SendString("You can't delete yourself") - } - - if err := db.GetDb(c).RemoveUser(username); err != nil { - log.Warn("Error deleting user:", err) - return c.Status(500).SendString(err.Error()) - } - - log.Info("User deleted:", username) - return c.Status(200).SendString("User deleted") -} diff --git a/backend/internal/types/WeeklyReport.go b/backend/internal/types/WeeklyReport.go deleted file mode 100644 index 5550b3f..0000000 --- a/backend/internal/types/WeeklyReport.go +++ /dev/null @@ -1,97 +0,0 @@ -package types - -// This is what should be submitted to the server, the username will be derived from the JWT token -type NewWeeklyReport struct { - // The name of the project, as it appears in the database - ProjectName string `json:"projectName"` - // The week number - Week int `json:"week"` - // Total time spent on development - DevelopmentTime int `json:"developmentTime"` - // Total time spent in meetings - MeetingTime int `json:"meetingTime"` - // Total time spent on administrative tasks - AdminTime int `json:"adminTime"` - // Total time spent on personal projects - OwnWorkTime int `json:"ownWorkTime"` - // Total time spent on studying - StudyTime int `json:"studyTime"` - // Total time spent on testing - TestingTime int `json:"testingTime"` -} - -type WeeklyReportList struct { - // The name of the project, as it appears in the database - ProjectName string `json:"projectName" db:"project_name"` - // The week number - Week int `json:"week" db:"week"` - // Total time spent on development - DevelopmentTime int `json:"developmentTime" db:"development_time"` - // Total time spent in meetings - MeetingTime int `json:"meetingTime" db:"meeting_time"` - // Total time spent on administrative tasks - AdminTime int `json:"adminTime" db:"admin_time"` - // Total time spent on personal projects - OwnWorkTime int `json:"ownWorkTime" db:"own_work_time"` - // Total time spent on studying - StudyTime int `json:"studyTime" db:"study_time"` - // Total time spent on testing - TestingTime int `json:"testingTime" db:"testing_time"` - // The project manager who signed it - SignedBy *int `json:"signedBy" db:"signed_by"` -} - -type WeeklyReport struct { - // The ID of the report - ReportId int `json:"reportId" db:"report_id"` - // The user id of the user who submitted the report - UserId int `json:"userId" db:"user_id"` - // The name of the project, as it appears in the database - ProjectId int `json:"projectId" db:"project_id"` - // The week number - Week int `json:"week" db:"week"` - // Total time spent on development - DevelopmentTime int `json:"developmentTime" db:"development_time"` - // Total time spent in meetings - MeetingTime int `json:"meetingTime" db:"meeting_time"` - // Total time spent on administrative tasks - AdminTime int `json:"adminTime" db:"admin_time"` - // Total time spent on personal projects - OwnWorkTime int `json:"ownWorkTime" db:"own_work_time"` - // Total time spent on studying - StudyTime int `json:"studyTime" db:"study_time"` - // Total time spent on testing - TestingTime int `json:"testingTime" db:"testing_time"` - // The project manager who signed it - SignedBy *int `json:"signedBy" db:"signed_by"` -} - -type Statistics struct { - TotalDevelopmentTime int `json:"totalDevelopmentTime" db:"total_development_time"` - TotalMeetingTime int `json:"totalMeetingTime" db:"total_meeting_time"` - TotalAdminTime int `json:"totalAdminTime" db:"total_admin_time"` - TotalOwnWorkTime int `json:"totalOwnWorkTime" db:"total_own_work_time"` - TotalStudyTime int `json:"totalStudyTime" db:"total_study_time"` - TotalTestingTime int `json:"totalTestingTime" db:"total_testing_time"` -} - -type UpdateWeeklyReport struct { - // The name of the project, as it appears in the database - ProjectName string `json:"projectName"` - // The name of the user - UserName string `json:"userName"` - // The week number - Week int `json:"week"` - // Total time spent on development - DevelopmentTime int `json:"developmentTime"` - // Total time spent in meetings - MeetingTime int `json:"meetingTime"` - // Total time spent on administrative tasks - AdminTime int `json:"adminTime"` - // Total time spent on personal projects - OwnWorkTime int `json:"ownWorkTime"` - // Total time spent on studying - StudyTime int `json:"studyTime"` - // Total time spent on testing - TestingTime int `json:"testingTime"` -} diff --git a/backend/internal/types/project.go b/backend/internal/types/project.go deleted file mode 100644 index 2e26eb9..0000000 --- a/backend/internal/types/project.go +++ /dev/null @@ -1,29 +0,0 @@ -package types - -// Project is a struct that holds the information about a project -type Project struct { - ID int `json:"id" db:"id"` - Name string `json:"name" db:"name"` - Description string `json:"description" db:"description"` - Owner string `json:"owner" db:"owner_user_id"` -} - -// As it arrives from the client, Owner is derived from the JWT token -type NewProject struct { - Name string `json:"name"` - Description string `json:"description"` -} - -// Used to change the role of a user in a project. -// If name is identical to the name contained in the token, the role can be changed. -// If the name is different, only a project manager can change the role. -type RoleChange struct { - UserName string `json:"username"` - Role string `json:"role" tstype:"'project_manager' | 'user'"` - Projectname string `json:"projectname"` -} - -type NameChange struct { - ID int `json:"id" db:"id"` - Name string `json:"name" db:"name"` -} diff --git a/backend/internal/types/users.go b/backend/internal/types/users.go deleted file mode 100644 index 37cc8c2..0000000 --- a/backend/internal/types/users.go +++ /dev/null @@ -1,39 +0,0 @@ -package types - -// User struct represents a user in the system -type User struct { - UserId string `json:"userId"` - Username string `json:"username"` - Password string `json:"password"` -} - -// If the user needs to be served over the api, we dont want to send the password -// ToPublicUser converts a User to a PublicUser -func (u *User) ToPublicUser() (*PublicUser, error) { - return &PublicUser{ - UserId: u.UserId, - Username: u.Username, - }, nil -} - -// Should be used when registering, for example -type NewUser struct { - Username string `json:"username" example:"username123"` - Password string `json:"password" example:"password123"` -} - -// PublicUser represents a user that is safe to send over the API (no password) -type PublicUser struct { - UserId string `json:"userId"` - Username string `json:"username"` -} - -// wrapper type for token -type Token struct { - Token string `json:"token"` -} - -type StrNameChange struct { - PrevName string `json:"prevName" db:"prevName"` - NewName string `json:"newName" db:"newName"` -} diff --git a/backend/internal/types/users_test.go b/backend/internal/types/users_test.go deleted file mode 100644 index 811839d..0000000 --- a/backend/internal/types/users_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package types - -import "testing" - -// NewUser returns a new User -func TestNewUser(t *testing.T) { - u := User{ - UserId: "123", - Username: "test", - Password: "password", - } - - if u.UserId != "123" { - t.Errorf("Expected user id to be 123, got %s", u.UserId) - } - if u.Username != "test" { - t.Errorf("Expected username to be test, got %s", u.Username) - } - if u.Password != "password" { - t.Errorf("Expected password to be password, got %s", u.Password) - } -} - -func TestToPublicUser(t *testing.T) { - u := User{ - UserId: "123", - Username: "test", - Password: "password", - } - - _, err := u.ToPublicUser() - if err != nil { - t.Errorf("Expected no error, got %s", err) - } -} diff --git a/backend/main.go b/backend/main.go deleted file mode 100644 index 0c66369..0000000 --- a/backend/main.go +++ /dev/null @@ -1,148 +0,0 @@ -package main - -import ( - "fmt" - "os" - _ "ttime/docs" - "ttime/internal/config" - "ttime/internal/database" - "ttime/internal/handlers/projects" - "ttime/internal/handlers/reports" - "ttime/internal/handlers/users" - - "github.com/BurntSushi/toml" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/swagger" - - jwtware "github.com/gofiber/contrib/jwt" -) - -// @title TTime API -// @version 0.0.1 -// @description This is the API for TTime, a time tracking application. - -// @license.name AGPL -// @license.url https://www.gnu.org/licenses/agpl-3.0.html - -// @securityDefinitions.apikey JWT -// @in header -// @name Authorization -// @description Use the JWT token provided by the login endpoint to authenticate requests. **Prefix the token with "Bearer ".** - -// @host localhost:8080 -// @BasePath /api - -// @externalDocs.description OpenAPI -// @externalDocs.url https://swagger.io/resources/open-api/ - -/** -Main function for starting the server and initializing configurations. -Reads configuration from file, pretty prints it, connects to the database, -migrates it, and sets up routes for the server. -*/ - -func main() { - conf, err := config.ReadConfigFromFile("config.toml") - if err != nil { - conf = config.NewConfig() - _ = conf.WriteConfigToFile("config.toml") - } - - // Pretty print the current config with toml - _ = toml.NewEncoder(os.Stdout).Encode(conf) - - fmt.Printf("Starting server on http://localhost:%d\n", conf.Port) - fmt.Printf("For documentation, go to http://localhost:%d/swagger/index.html\n", conf.Port) - - // Connect to the database - db := database.DbConnect(conf.DbPath) - - // Migrate the database - if err = database.Migrate(db); err != nil { - fmt.Println("Error migrating database: ", err) - os.Exit(1) - } - - // Migrate sample data, should not be used in production - if err = database.MigrateSampleData(db); err != nil { - fmt.Println("Error migrating sample data: ", err) - os.Exit(1) - } - - // Create the server - server := fiber.New() - - // We want some logs - server.Use(logger.New()) - - // Sets up db middleware, accessed as Local "db" key - server.Use(database.DbMiddleware(&db)) - - // Mounts the swagger documentation, this is available at /swagger/index.html - server.Get("/swagger/*", swagger.HandlerDefault) - - // Mount our static files (Beware of the security implications of this!) - // This will likely be replaced by an embedded filesystem in the future - server.Static("/", "./static") - - // Create a group for our API - api := server.Group("/api") - - // Register our unprotected routes - api.Post("/register", users.Register) - api.Post("/login", users.Login) - - // Every route from here on will require a valid - // JWT bearer token authentication in the header - server.Use(jwtware.New(jwtware.Config{ - SigningKey: jwtware.SigningKey{Key: []byte("secret")}, - })) - - // All user related routes - // userGroup := api.Group("/user") // Not currently in use - api.Get("/users/all", users.ListAllUsers) - api.Get("/project/getAllUsers", users.GetAllUsersProject) - api.Get("/username", users.GetUserName) - api.Post("/login", users.Login) - api.Post("/register", users.Register) - api.Post("/loginrenew", users.LoginRenew) - api.Post("/promoteToAdmin", users.PromoteToAdmin) - api.Put("/changeUserName", users.ChangeUserName) - api.Delete("/userdelete/:username", users.UserDelete) // Perhaps just use POST to avoid headaches - api.Put("/changeUserPassword/:username", users.ChangeUserPassword) - - // All project related routes - // projectGroup := api.Group("/project") // Not currently in use - api.Get("/getProjectTimes/:projectName", projects.GetProjectTimesHandler) - api.Get("/getUserProjects/:username", projects.GetUserProjects) - api.Get("/project/:projectId", projects.GetProject) - api.Get("/checkIfProjectManager/:projectName", projects.IsProjectManagerHandler) - api.Get("/getUsersProject/:projectName", projects.ListAllUsersProject) - api.Post("/project", projects.CreateProject) - api.Post("/ProjectRoleChange", projects.ProjectRoleChange) - api.Put("/promoteToPm/:projectName", projects.PromoteToPm) - api.Put("/addUserToProject/:projectName", projects.AddUserToProjectHandler) - api.Delete("/removeUserFromProject/:projectName", projects.RemoveUserFromProject) - api.Delete("/removeProject/:projectName", projects.RemoveProject) - api.Delete("/project/:projectID", projects.DeleteProject) - api.Put("/changeProjectName/:projectName", projects.ChangeProjectName) - - // All report related routes - // reportGroup := api.Group("/report") // Not currently in use - api.Get("/getWeeklyReport", reports.GetWeeklyReport) - api.Get("/getUnsignedReports/:projectName", reports.GetUnsignedReports) - api.Get("/getAllWeeklyReports/:projectName", reports.GetAllWeeklyReports) - api.Get("/getStatistics", reports.GetStatistics) - api.Post("/submitWeeklyReport", reports.SubmitWeeklyReport) - api.Put("/signReport/:reportId", reports.SignReport) - api.Put("/updateWeeklyReport", reports.UpdateWeeklyReport) - api.Put("/unsignReport/:reportId", reports.UnsignReport) - api.Delete("/deleteReport/:reportId", reports.DeleteReport) - - // Announce the port we are listening on and start the server - err = server.Listen(fmt.Sprintf(":%d", conf.Port)) - if err != nil { - fmt.Println("Error starting server: ", err) - } -} diff --git a/backend/migrations/0010.sql b/backend/migrations/0010.sql new file mode 100644 index 0000000..df314b1 --- /dev/null +++ b/backend/migrations/0010.sql @@ -0,0 +1,50 @@ +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY, + username VARCHAR(255) NOT NULL, + password VARCHAR(255) NOT NULL +); + +CREATE TABLE IF NOT EXISTS projects ( + id INTEGER PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) +); + +CREATE TABLE IF NOT EXISTS time_reports ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + start DATETIME NOT NULL, + end DATETIME NOT NULL, + FOREIGN KEY (project_id) REFERENCES projects (id) +); + +CREATE TRIGGER IF NOT EXISTS time_reports_start_before_end + BEFORE INSERT ON time_reports + FOR EACH ROW + BEGIN + SELECT + CASE + WHEN NEW.start >= NEW.end THEN + RAISE (ABORT, 'start must be before end') + END; + END; + +CREATE TABLE IF NOT EXISTS report_collection ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + date DATE NOT NULL, + signed_by INTEGER, -- NULL if not signed + FOREIGN KEY (signed_by) REFERENCES users (id) +); + +CREATE TABLE IF NOT EXISTS user_roles ( + user_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + role STRING NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (id) + FOREIGN KEY (project_id) REFERENCES projects (id) +); \ No newline at end of file diff --git a/backend/tygo.yaml b/backend/tygo.yaml deleted file mode 100644 index 54c1e8f..0000000 --- a/backend/tygo.yaml +++ /dev/null @@ -1,9 +0,0 @@ -packages: - - path: "ttime/internal/types" - output_path: "../frontend/src/Types/goTypes.ts" - type_mappings: - time.Time: "string /* RFC3339 */" - null.String: "null | string" - null.Bool: "null | boolean" - uuid.UUID: "string /* uuid */" - uuid.NullUUID: "null | string /* uuid */" diff --git a/container/Containerfile b/container/Containerfile index f9cb39d..23d177c 100644 --- a/container/Containerfile +++ b/container/Containerfile @@ -13,6 +13,7 @@ FROM docker.io/golang:alpine as go RUN apk add gcompat RUN apk add gcc RUN apk add musl-dev +RUN apk add make RUN apk add sqlite WORKDIR /build ADD backend/go.mod backend/go.sum ./ @@ -23,8 +24,10 @@ RUN go mod download # Add the source code ADD backend . -RUN go build -o server -RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o ./server ./main.go +RUN make migrate + +# RUN go build -o server +RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o ./server ./cmd/ # Strip the binary for a smaller image RUN strip ./server @@ -34,7 +37,6 @@ FROM docker.io/alpine:latest as runner RUN adduser -D nonroot RUN addgroup nonroot nonroot WORKDIR /app -RUN chown nonroot:nonroot /app # Copy the frontend SPA build into public COPY --from=client /build/dist static @@ -42,6 +44,9 @@ COPY --from=client /build/dist static # Copy the server binary COPY --from=go /build/server server +# Copy the database +COPY --from=go /build/db.sqlite3 db.sqlite3 + # Expose port 8080 EXPOSE 8080 @@ -49,4 +54,4 @@ EXPOSE 8080 USER nonroot:nonroot # Run the server -CMD ["./server"] +CMD ["./server"] \ No newline at end of file diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 1051c03..6606c58 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -9,7 +9,7 @@ module.exports = { 'plugin:react-hooks/recommended', 'plugin:prettier/recommended', ], - ignorePatterns: ['dist', '.eslintrc.cjs', 'tailwind.config.js', 'postcss.config.js', 'jest.config.cjs', 'goTypes.ts'], + ignorePatterns: ['dist', '.eslintrc.cjs'], parser: '@typescript-eslint/parser', plugins: ['react-refresh', 'prettier'], rules: { diff --git a/frontend/.prettierignore b/frontend/.prettierignore deleted file mode 100644 index c49d006..0000000 --- a/frontend/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -goTypes.ts -GenApi.ts \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 573ba58..e4b78ea 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - TTIME + Vite + React + TS
diff --git a/frontend/jest.config.cjs b/frontend/jest.config.cjs deleted file mode 100644 index 28ea68a..0000000 --- a/frontend/jest.config.cjs +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - transform: { - '^.+\\.(t|j)sx?$': '@swc/jest', - }, -} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 323ef11..fc12e8b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,34 +8,22 @@ "name": "tmp", "version": "0.0.0", "dependencies": { - "localforage": "^1.10.0", - "match-sorter": "^6.3.4", "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.22.2", - "sort-by": "^0.0.2" + "react-dom": "^18.2.0" }, "devDependencies": { - "@swc/core": "^1.4.2", - "@swc/jest": "^0.2.36", - "@types/jest": "^29.5.12", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react-swc": "^3.5.0", - "autoprefixer": "^10.4.17", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", - "jest": "^29.7.0", - "postcss": "^8.4.35", "prettier": "3.2.5", - "react-test-renderer": "^18.2.0", - "tailwindcss": "^3.4.1", "typescript": "^5.2.2", "vite": "^5.1.0" } @@ -49,625 +37,10 @@ "node": ">=0.10.0" } }, - "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==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.24.2", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", - "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.4", - "@babel/parser": "^7.24.4", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", - "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", - "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "cpu": [ "ppc64" ], @@ -681,9 +54,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "cpu": [ "arm" ], @@ -697,9 +70,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "cpu": [ "arm64" ], @@ -713,9 +86,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "cpu": [ "x64" ], @@ -729,9 +102,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ "arm64" ], @@ -745,9 +118,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], @@ -761,9 +134,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "cpu": [ "arm64" ], @@ -777,9 +150,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], @@ -793,9 +166,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "cpu": [ "arm" ], @@ -809,9 +182,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], @@ -825,9 +198,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], @@ -841,9 +214,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], @@ -857,9 +230,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], @@ -873,9 +246,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], @@ -889,9 +262,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], @@ -905,9 +278,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], @@ -921,9 +294,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], @@ -937,9 +310,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], @@ -953,9 +326,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], @@ -969,9 +342,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "cpu": [ "x64" ], @@ -985,9 +358,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], @@ -1001,9 +374,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], @@ -1017,9 +390,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -1102,9 +475,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1160,553 +533,11 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/create-cache-key-function": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", - "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1742,16 +573,6 @@ "node": ">= 8" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -1764,18 +585,10 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", - "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz", + "integrity": "sha512-/MeDQmcD96nVoRumKUljsYOLqfv1YFJps+0pTrb2Z9Nl/w5qNUysMaWQsrd1mvAlNT4yza1iVyIu4Q4AgF6V3A==", "cpu": [ "arm" ], @@ -1786,9 +599,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", - "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.10.0.tgz", + "integrity": "sha512-lvu0jK97mZDJdpZKDnZI93I0Om8lSDaiPx3OiCk0RXn3E8CMPJNS/wxjAvSJJzhhZpfjXsjLWL8LnS6qET4VNQ==", "cpu": [ "arm64" ], @@ -1799,9 +612,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", - "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.10.0.tgz", + "integrity": "sha512-uFpayx8I8tyOvDkD7X6n0PriDRWxcqEjqgtlxnUA/G9oS93ur9aZ8c8BEpzFmsed1TH5WZNG5IONB8IiW90TQg==", "cpu": [ "arm64" ], @@ -1812,9 +625,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", - "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.10.0.tgz", + "integrity": "sha512-nIdCX03qFKoR/MwQegQBK+qZoSpO3LESurVAC6s6jazLA1Mpmgzo3Nj3H1vydXp/JM29bkCiuF7tDuToj4+U9Q==", "cpu": [ "x64" ], @@ -1825,9 +638,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", - "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.10.0.tgz", + "integrity": "sha512-Fz7a+y5sYhYZMQFRkOyCs4PLhICAnxRX/GnWYReaAoruUzuRtcf+Qnw+T0CoAWbHCuz2gBUwmWnUgQ67fb3FYw==", "cpu": [ "arm" ], @@ -1838,9 +651,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", - "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.10.0.tgz", + "integrity": "sha512-yPtF9jIix88orwfTi0lJiqINnlWo6p93MtZEoaehZnmCzEmLL0eqjA3eGVeyQhMtxdV+Mlsgfwhh0+M/k1/V7Q==", "cpu": [ "arm64" ], @@ -1851,9 +664,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", - "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.10.0.tgz", + "integrity": "sha512-9GW9yA30ib+vfFiwjX+N7PnjTnCMiUffhWj4vkG4ukYv1kJ4T9gHNg8zw+ChsOccM27G9yXrEtMScf1LaCuoWQ==", "cpu": [ "arm64" ], @@ -1863,23 +676,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", - "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", - "cpu": [ - "ppc64le" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", - "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.10.0.tgz", + "integrity": "sha512-X1ES+V4bMq2ws5fF4zHornxebNxMXye0ZZjUrzOrf7UMx1d6wMQtfcchZ8SqUnQPPHdOyOLW6fTcUiFgHFadRA==", "cpu": [ "riscv64" ], @@ -1889,23 +689,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", - "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", - "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.10.0.tgz", + "integrity": "sha512-w/5OpT2EnI/Xvypw4FIhV34jmNqU5PZjZue2l2Y3ty1Ootm3SqhI+AmfhlUYGBTd9JnpneZCDnt3uNOiOBkMyw==", "cpu": [ "x64" ], @@ -1916,9 +703,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", - "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.10.0.tgz", + "integrity": "sha512-q/meftEe3QlwQiGYxD9rWwB21DoKQ9Q8wA40of/of6yGHhZuGfZO0c3WYkN9dNlopHlNT3mf5BPsUSxoPuVQaw==", "cpu": [ "x64" ], @@ -1929,9 +716,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", - "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.10.0.tgz", + "integrity": "sha512-NrR6667wlUfP0BHaEIKgYM/2va+Oj+RjZSASbBMnszM9k+1AmliRjHc3lJIiOehtSSjqYiO7R6KLNrWOX+YNSQ==", "cpu": [ "arm64" ], @@ -1942,9 +729,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", - "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.10.0.tgz", + "integrity": "sha512-FV0Tpt84LPYDduIDcXvEC7HKtyXxdvhdAOvOeWMWbQNulxViH2O07QXkT/FffX4FqEI02jEbCJbr+YcuKdyyMg==", "cpu": [ "ia32" ], @@ -1955,9 +742,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", - "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.10.0.tgz", + "integrity": "sha512-OZoJd+o5TaTSQeFFQ6WjFCiltiYVjIdsXxwu/XZ8qRpsvMQr4UsVrE5UyT9RIvsnuF47DqkJKhhVZ2Q9YW9IpQ==", "cpu": [ "x64" ], @@ -1967,38 +754,14 @@ "win32" ] }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, "node_modules/@swc/core": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.12.tgz", - "integrity": "sha512-QljRxTaUajSLB9ui93cZ38/lmThwIw/BPxjn+TphrYN6LPU3vu9/ykjgHtlpmaXDDcngL4K5i396E7iwwEUxYg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.0.tgz", + "integrity": "sha512-wc5DMI5BJftnK0Fyx9SNJKkA0+BZSJQx8430yutWmsILkHMBD3Yd9GhlMaxasab9RhgKqZp7Ht30hUYO5ZDvQg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@swc/counter": "^0.1.2", + "@swc/counter": "^0.1.1", "@swc/types": "^0.1.5" }, "engines": { @@ -2009,16 +772,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.12", - "@swc/core-darwin-x64": "1.4.12", - "@swc/core-linux-arm-gnueabihf": "1.4.12", - "@swc/core-linux-arm64-gnu": "1.4.12", - "@swc/core-linux-arm64-musl": "1.4.12", - "@swc/core-linux-x64-gnu": "1.4.12", - "@swc/core-linux-x64-musl": "1.4.12", - "@swc/core-win32-arm64-msvc": "1.4.12", - "@swc/core-win32-ia32-msvc": "1.4.12", - "@swc/core-win32-x64-msvc": "1.4.12" + "@swc/core-darwin-arm64": "1.4.0", + "@swc/core-darwin-x64": "1.4.0", + "@swc/core-linux-arm-gnueabihf": "1.4.0", + "@swc/core-linux-arm64-gnu": "1.4.0", + "@swc/core-linux-arm64-musl": "1.4.0", + "@swc/core-linux-x64-gnu": "1.4.0", + "@swc/core-linux-x64-musl": "1.4.0", + "@swc/core-win32-arm64-msvc": "1.4.0", + "@swc/core-win32-ia32-msvc": "1.4.0", + "@swc/core-win32-x64-msvc": "1.4.0" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -2030,9 +793,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.12.tgz", - "integrity": "sha512-BZUUq91LGJsLI2BQrhYL3yARkcdN4TS3YGNS6aRYUtyeWrGCTKHL90erF2BMU2rEwZLLkOC/U899R4o4oiSHfA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.0.tgz", + "integrity": "sha512-UTJ/Vz+s7Pagef6HmufWt6Rs0aUu+EJF4Pzuwvr7JQQ5b1DZeAAUeUtkUTFx/PvCbM8Xfw4XdKBUZfrIKCfW8A==", "cpu": [ "arm64" ], @@ -2046,9 +809,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.12.tgz", - "integrity": "sha512-Wkk8rq1RwCOgg5ybTlfVtOYXLZATZ+QjgiBNM7pIn03A5/zZicokNTYd8L26/mifly2e74Dz34tlIZBT4aTGDA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.0.tgz", + "integrity": "sha512-f8v58u2GsGak8EtZFN9guXqE0Ep10Suny6xriaW2d8FGqESPyNrnBzli3aqkSeQk5gGqu2zJ7WiiKp3XoUOidA==", "cpu": [ "x64" ], @@ -2062,9 +825,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.12.tgz", - "integrity": "sha512-8jb/SN67oTQ5KSThWlKLchhU6xnlAlnmnLCCOKK1xGtFS6vD+By9uL+qeEY2krV98UCRTf68WSmC0SLZhVoz5A==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.0.tgz", + "integrity": "sha512-q2KAkBzmPcTnRij/Y1fgHCKAGevUX/H4uUESrw1J5gmUg9Qip6onKV80lTumA1/aooGJ18LOsB31qdbwmZk9OA==", "cpu": [ "arm" ], @@ -2078,9 +841,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.12.tgz", - "integrity": "sha512-DhW47DQEZKCdSq92v5F03rqdpjRXdDMqxfu4uAlZ9Uo1wJEGvY23e1SNmhji2sVHsZbBjSvoXoBLk0v00nSG8w==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.0.tgz", + "integrity": "sha512-SknGu96W0mzHtLHWm+62fk5+Omp9fMPFO7AWyGFmz2tr8EgRRXtTSrBUnWhAbgcalnhen48GsvtMdxf1KNputg==", "cpu": [ "arm64" ], @@ -2094,9 +857,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.12.tgz", - "integrity": "sha512-PR57pT3TssnCRvdsaKNsxZy9N8rFg9AKA1U7W+LxbZ/7Z7PHc5PjxF0GgZpE/aLmU6xOn5VyQTlzjoamVkt05g==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.0.tgz", + "integrity": "sha512-/k3TDvpBRMDNskHooNN1KqwUhcwkfBlIYxRTnJvsfT2C7My4pffR+4KXmt0IKynlTTbCdlU/4jgX4801FSuliw==", "cpu": [ "arm64" ], @@ -2110,9 +873,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.12.tgz", - "integrity": "sha512-HLZIWNHWuFIlH+LEmXr1lBiwGQeCshKOGcqbJyz7xpqTh7m2IPAxPWEhr/qmMTMsjluGxeIsLrcsgreTyXtgNA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.0.tgz", + "integrity": "sha512-GYsTMvNt5+WTVlwwQzOOWsPMw6P/F41u5PGHWmfev8Nd4QJ1h3rWPySKk4mV42IJwH9MgQCVSl3ygwNqwl6kFg==", "cpu": [ "x64" ], @@ -2126,9 +889,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.12.tgz", - "integrity": "sha512-M5fBAtoOcpz2YQAFtNemrPod5BqmzAJc8pYtT3dVTn1MJllhmLHlphU8BQytvoGr1PHgJL8ZJBlBGdt70LQ7Mw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.0.tgz", + "integrity": "sha512-jGVPdM/VwF7kK/uYRW5N6FwzKf/FnDjGIR3RPvQokjYJy7Auk+3Oj21C0Jev7sIT9RYnO/TrFEoEozKeD/z2Qw==", "cpu": [ "x64" ], @@ -2142,9 +905,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.12.tgz", - "integrity": "sha512-K8LjjgZ7VQFtM+eXqjfAJ0z+TKVDng3r59QYn7CL6cyxZI2brLU3lNknZcUFSouZD+gsghZI/Zb8tQjVk7aKDQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.0.tgz", + "integrity": "sha512-biHYm1AronEKlt47O/H8sSOBM2BKXMmWT+ApvlxUw50m1RGNnVnE0bgY7tylFuuSiWyXsQPJbmUV708JqORXVg==", "cpu": [ "arm64" ], @@ -2158,9 +921,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.12.tgz", - "integrity": "sha512-hflO5LCxozngoOmiQbDPyvt6ODc5Cu9AwTJP9uH/BSMPdEQ6PCnefuUOJLAKew2q9o+NmDORuJk+vgqQz9Uzpg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.0.tgz", + "integrity": "sha512-TL5L2tFQb19kJwv6+elToGBj74QXCn9j+hZfwQatvZEJRA5rDK16eH6oAE751dGUArhnWlW3Vj65hViPvTuycw==", "cpu": [ "ia32" ], @@ -2174,9 +937,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.4.12", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.12.tgz", - "integrity": "sha512-3A4qMtddBDbtprV5edTB/SgJn9L+X5TL7RGgS3eWtEgn/NG8gA80X/scjf1v2MMeOsrcxiYhnemI2gXCKuQN2g==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.0.tgz", + "integrity": "sha512-e2xVezU7XZ2Stzn4i7TOQe2Kn84oYdG0M3A7XI7oTdcpsKCcKwgiMoroiAhqCv+iN20KNqhnWwJiUiTj/qN5AA==", "cpu": [ "x64" ], @@ -2195,72 +958,11 @@ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true }, - "node_modules/@swc/jest": { - "version": "0.2.36", - "resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.36.tgz", - "integrity": "sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==", - "dev": true, - "dependencies": { - "@jest/create-cache-key-function": "^29.7.0", - "@swc/counter": "^0.1.3", - "jsonc-parser": "^3.2.0" - }, - "engines": { - "npm": ">= 7.0.0" - }, - "peerDependencies": { - "@swc/core": "*" - } - }, "node_modules/@swc/types": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", - "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", - "dev": true, - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", + "dev": true }, "node_modules/@types/estree": { "version": "1.0.5", @@ -2268,114 +970,48 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, - "node_modules/@types/node": { - "version": "20.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.4.tgz", - "integrity": "sha512-E+Fa9z3wSQpzgYQdYmme5X3OTuejnnTx88A6p6vkkJosR3KBz+HpE3kqNm98VE6cfLFcISx7zW7MsJkH6KwbTw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", "dev": true }, "node_modules/@types/react": { - "version": "18.2.74", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz", - "integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==", + "version": "18.2.55", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz", + "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==", "dev": true, "dependencies": { "@types/prop-types": "*", + "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.24", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.24.tgz", - "integrity": "sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg==", + "version": "18.2.19", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz", + "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==", "dev": true, "dependencies": { "@types/react": "*" } }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true + }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", + "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -2623,33 +1259,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2674,31 +1283,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2722,16 +1306,15 @@ } }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" }, "engines": { @@ -2750,26 +1333,6 @@ "node": ">=8" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array.prototype.flat": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", @@ -2806,18 +1369,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, "node_modules/array.prototype.tosorted": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", @@ -2853,51 +1404,20 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", "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" - } - ], "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "has-symbols": "^1.0.3" } }, "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", + "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, "engines": { "node": ">= 0.4" }, @@ -2905,140 +1425,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.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 }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3060,53 +1452,6 @@ "node": ">=8" } }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "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" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -3135,44 +1480,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", - "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" - } - ] - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3189,102 +1496,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3303,48 +1514,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3359,75 +1534,12 @@ "node": ">= 8" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3445,44 +1557,22 @@ } } }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.3.tgz", + "integrity": "sha512-h3GBouC+RPtNX2N0hHVLo2ZwPYurq8mLmXpOLTsw71gr7lHt5VaI4vVkDUNOfiWmm48JEXe3VM7PmLX45AMmmg==", "dev": true, "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -3508,30 +1598,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3544,12 +1610,6 @@ "node": ">=8" } }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true - }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3562,97 +1622,51 @@ "node": ">=6.0.0" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.4.727", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.727.tgz", - "integrity": "sha512-brpv4KTeC4g0Fx2FeIKytLd4UGn1zBQq5Lauy7zEWT9oqkaj5mgsxblEZIAOf1HHLlXxzr6adGViiBy5Z39/CA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", + "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", + "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -3683,51 +1697,40 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", - "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.16.tgz", + "integrity": "sha512-CREG2A9Vq7bpDRnldhFcMKuKArvkZtsH6Y0DHOHVg49qhf+LD8uEdUM3OkOAICv0EziGtDEnQtqY2/mfBILpFw==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.6", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", + "es-abstract": "^1.22.3", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.0.2", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", + "has-property-descriptors": "^1.0.1", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "internal-slot": "^1.0.7", "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" + "safe-array-concat": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -3760,9 +1763,9 @@ } }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, "bin": { @@ -3772,38 +1775,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "engines": { - "node": ">=6" + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" } }, "node_modules/escape-string-regexp": { @@ -3819,16 +1813,16 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -3916,29 +1910,27 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.34.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", - "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlast": "^1.2.4", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.17", + "es-iterator-helpers": "^1.0.12", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7", - "object.hasown": "^1.1.3", - "object.values": "^1.1.7", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", + "resolve": "^2.0.0-next.4", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.10" + "string.prototype.matchall": "^4.0.8" }, "engines": { "node": ">=4" @@ -3960,9 +1952,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz", - "integrity": "sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz", + "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==", "dev": true, "peerDependencies": { "eslint": ">=7" @@ -4078,19 +2070,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -4133,54 +2112,6 @@ "node": ">=0.10.0" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4242,15 +2173,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4306,9 +2228,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "node_modules/for-each": { @@ -4320,47 +2242,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4417,24 +2298,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -4454,27 +2317,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -4608,12 +2450,6 @@ "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==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -4651,9 +2487,9 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", "dev": true, "engines": { "node": ">= 0.4" @@ -4690,9 +2526,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dev": true, "dependencies": { "function-bind": "^1.1.2" @@ -4701,21 +2537,6 @@ "node": ">= 0.4" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -4725,11 +2546,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -4746,25 +2562,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4820,12 +2617,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -4853,18 +2644,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -4905,21 +2684,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -4956,24 +2720,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -5002,21 +2748,18 @@ } }, "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "dev": true, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", "dev": true, "engines": { "node": ">= 0.4" @@ -5075,44 +2818,26 @@ } }, "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" + "call-bind": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-string": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", @@ -5159,13 +2884,10 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "dev": true, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5183,16 +2905,13 @@ } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5210,72 +2929,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -5289,605 +2942,6 @@ "set-function-name": "^2.0.1" } }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "dev": true, - "bin": { - "jiti": "bin/jiti.js" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5905,30 +2959,12 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5941,24 +2977,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -5983,24 +3001,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6014,37 +3014,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "dependencies": { - "lie": "3.1.1" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6078,53 +3047,17 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" + "yallist": "^4.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/match-sorter": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", - "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", - "dependencies": { - "@babel/runtime": "^7.23.8", - "remove-accents": "0.5.0" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6147,15 +3080,6 @@ "node": ">=8.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -6171,32 +3095,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -6221,48 +3125,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6272,15 +3134,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -6318,29 +3171,28 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -6350,31 +3202,27 @@ } }, "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -6392,21 +3240,6 @@ "wrappy": "1" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -6454,15 +3287,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6475,24 +3299,6 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6526,31 +3332,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-scurry": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -6578,101 +3359,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -6691,150 +3381,12 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "source-map-js": "^1.0.2" }, "engines": { "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-import/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.16", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "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 - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6871,45 +3423,6 @@ "node": ">=6.0.0" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -6921,12 +3434,6 @@ "react-is": "^16.13.1" } }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6936,22 +3443,6 @@ "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6996,100 +3487,22 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "node_modules/react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", - "dependencies": { - "@remix-run/router": "1.15.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", - "dependencies": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-shallow-renderer": { - "version": "16.15.0", - "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", - "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-test-renderer": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz", - "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", - "dev": true, - "dependencies": { - "react-is": "^18.2.0", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", + "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.5", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", + "es-abstract": "^1.22.3", + "es-errors": "^1.0.0", + "get-intrinsic": "^1.2.3", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" }, @@ -7100,11 +3513,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -7123,20 +3531,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/remove-accents": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", - "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -7154,27 +3548,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -7184,15 +3557,6 @@ "node": ">=4" } }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7219,9 +3583,9 @@ } }, "node_modules/rollup": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", - "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.10.0.tgz", + "integrity": "sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -7234,21 +3598,19 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.0", - "@rollup/rollup-android-arm64": "4.14.0", - "@rollup/rollup-darwin-arm64": "4.14.0", - "@rollup/rollup-darwin-x64": "4.14.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", - "@rollup/rollup-linux-arm64-gnu": "4.14.0", - "@rollup/rollup-linux-arm64-musl": "4.14.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", - "@rollup/rollup-linux-riscv64-gnu": "4.14.0", - "@rollup/rollup-linux-s390x-gnu": "4.14.0", - "@rollup/rollup-linux-x64-gnu": "4.14.0", - "@rollup/rollup-linux-x64-musl": "4.14.0", - "@rollup/rollup-win32-arm64-msvc": "4.14.0", - "@rollup/rollup-win32-ia32-msvc": "4.14.0", - "@rollup/rollup-win32-x64-msvc": "4.14.0", + "@rollup/rollup-android-arm-eabi": "4.10.0", + "@rollup/rollup-android-arm64": "4.10.0", + "@rollup/rollup-darwin-arm64": "4.10.0", + "@rollup/rollup-darwin-x64": "4.10.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.10.0", + "@rollup/rollup-linux-arm64-gnu": "4.10.0", + "@rollup/rollup-linux-arm64-musl": "4.10.0", + "@rollup/rollup-linux-riscv64-gnu": "4.10.0", + "@rollup/rollup-linux-x64-gnu": "4.10.0", + "@rollup/rollup-linux-x64-musl": "4.10.0", + "@rollup/rollup-win32-arm64-msvc": "4.10.0", + "@rollup/rollup-win32-ia32-msvc": "4.10.0", + "@rollup/rollup-win32-x64-msvc": "4.10.0", "fsevents": "~2.3.2" } }, @@ -7276,13 +3638,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -7333,51 +3695,32 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", "dev": true, "dependencies": { - "define-data-property": "^1.1.4", + "define-data-property": "^1.1.2", "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "has-property-descriptors": "^1.0.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", "dev": true, "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", + "define-data-property": "^1.0.1", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "has-property-descriptors": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -7405,12 +3748,12 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.6", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" @@ -7422,18 +3765,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7443,144 +3774,44 @@ "node": ">=8" } }, - "node_modules/sort-by": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/sort-by/-/sort-by-0.0.2.tgz", - "integrity": "sha512-iOX5oHA4a0eqTMFiWrHYqv924UeRKFBLhym7iwSVG37Egg2wApgZKAjyzM9WZjMwKv6+8Zi+nIaJ7FYsO9EkoA==" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -7590,31 +3821,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7632,37 +3860,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -7675,50 +3872,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.3.12", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.10.2" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7759,138 +3912,12 @@ "url": "https://opencollective.com/unts" } }, - "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", - "dev": true, - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7904,9 +3931,9 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", "dev": true, "engines": { "node": ">=16" @@ -7915,12 +3942,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -7939,15 +3960,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -7961,12 +3973,12 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", + "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.6", "es-errors": "^1.3.0", "is-typed-array": "^1.1.13" }, @@ -7975,16 +3987,15 @@ } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.2", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -7994,17 +4005,16 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -8014,29 +4024,23 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", "dev": true, "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.2", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "is-typed-array": "^1.1.9" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/typescript": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", - "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8061,42 +4065,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "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" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8106,35 +4074,15 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/vite": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", - "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", + "integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==", "dev": true, "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" }, "bin": { "vite": "bin/vite.js" @@ -8181,15 +4129,6 @@ } } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8248,34 +4187,31 @@ } }, "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", "dev": true, "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" + "has-tostringtag": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -8284,114 +4220,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/yaml": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", - "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index e8d78c1..48a0d73 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,38 +8,25 @@ "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", - "format": "prettier --config .prettierrc '**/*.ts' '**/*.tsx' '**/*.js' '**/*.json' --write", - "test": "jest" + "format": "prettier --config .prettierrc '**/*.ts' '**/*.tsx' '**/*.js' '**/*.json' --write" }, "dependencies": { - "localforage": "^1.10.0", - "match-sorter": "^6.3.4", "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.22.2", - "sort-by": "^0.0.2" + "react-dom": "^18.2.0" }, "devDependencies": { - "@swc/core": "^1.4.2", - "@swc/jest": "^0.2.36", - "@types/jest": "^29.5.12", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.19", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "@vitejs/plugin-react-swc": "^3.5.0", - "autoprefixer": "^10.4.17", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.5", - "jest": "^29.7.0", - "postcss": "^8.4.35", "prettier": "3.2.5", - "react-test-renderer": "^18.2.0", - "tailwindcss": "^3.4.1", "typescript": "^5.2.2", "vite": "^5.1.0" } diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js deleted file mode 100644 index 2aa7205..0000000 --- a/frontend/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/frontend/src/API/API.ts b/frontend/src/API/API.ts deleted file mode 100644 index 67768c2..0000000 --- a/frontend/src/API/API.ts +++ /dev/null @@ -1,1087 +0,0 @@ -import { AddMemberInfo } from "../Components/AddMember"; -import { ProjectRoleChange } from "../Components/ChangeRole"; -import { projectTimes } from "../Components/GetProjectTimes"; -import { ProjectMember } from "../Components/GetUsersInProject"; -import { - UpdateWeeklyReport, - NewWeeklyReport, - NewUser, - User, - Project, - NewProject, - WeeklyReport, - StrNameChange, - Statistics, -} from "../Types/goTypes"; - -/** - * Response object returned by API methods. - */ -export interface APIResponse { - /** Indicates whether the API call was successful */ - success: boolean; - /** Optional message providing additional information or error description */ - message?: string; - /** Optional data returned by the API method */ - data?: T; -} - -/** - * Interface defining methods that an instance of the API must implement. - */ -interface API { - /** - * Register a new user - * @param {NewUser} user The user object to be registered - * @returns {Promise>} A promise containing the API response with the user data. - */ - registerUser(user: NewUser): Promise>; - - /** - * Removes a user. - * @param {string} username The username of the user to be removed. - * @param {string} token The authentication token. - * @returns {Promise>} A promise containing the API response with the removed user data. - */ - removeUser(username: string, token: string): Promise>; - - /** - * Check if user is project manager. - * @param {string} username The username of the user. - * @param {string} projectName The name of the project. - * @param {string} token The authentication token. - * @returns {Promise>} A promise containing the API response indicating if the user is a project manager. - */ - checkIfProjectManager( - projectName: string, - token: string, - ): Promise>; - - /** Logs in a user with the provided credentials. - * @param {NewUser} NewUser The user object containing username and password. - * @returns {Promise>} A promise resolving to an API response with a token. - */ - login(NewUser: NewUser): Promise>; - - /** - * Renew the token - * @param {string} token The current authentication token. - * @returns {Promise>} A promise resolving to an API response with a renewed token. - */ - renewToken(token: string): Promise>; - - /** Promote user to admin */ - - /** Creates a new project. - * @param {NewProject} project The project object containing name and description. - * @param {string} token The authentication token. - * @returns {Promise>} A promise resolving to an API response with the created project. - */ - createProject(project: NewProject, token: string): Promise>; - - /** Submits a weekly report - * @param {NewWeeklyReport} weeklyReport The weekly report object. - * @param {string} token The authentication token. - * @returns {Promise>} A promise resolving to an API response with the submitted report. - */ - submitWeeklyReport( - weeklyReport: NewWeeklyReport, - token: string, - ): Promise>; - - /** - * Updates a weekly report. - * @param {UpdateWeeklyReport} weeklyReport The updated weekly report object. - * @param {string} token The authentication token. - * @returns {Promise>} A promise containing the API response with the updated report. - */ - updateWeeklyReport( - weeklyReport: UpdateWeeklyReport, - token: string, - ): Promise>; - - /** Gets a weekly report for a specific user, project and week. - * Keep in mind that the user within the token needs to be PM - * of the project to get the report, unless the user is the target user. - * @param {string} projectName The name of the project. - * @param {string} week The week number. - * @param {string} token The authentication token. - * @param {string} targetUser The username of the target user. Defaults to token user. - * @returns {Promise>} A promise resolving to an API response with the retrieved report. - */ - getWeeklyReport( - projectName: string, - week: string, - token: string, - targetUser?: string, - ): Promise>; - - /** - * Returns all the weekly reports for a user in a particular project - * The username is derived from the token - * @param {string} projectName The name of the project - * @param {string} token The token of the user - * @returns {APIResponse} A list of weekly reports - */ - getAllWeeklyReportsForUser( - projectName: string, - token: string, - targetUser?: string, - ): Promise>; - - /** Gets all the projects of a user - * @param {string} username - The authentication token. - * @param {string} token - The authentication token. - * @returns {Promise>} A promise containing the API response with the user's projects. - */ - getUserProjects( - username: string, - token: string, - ): Promise>; - - /** Gets a project by its id. - * @param {number} id The id of the project to retrieve. - * @returns {Promise>} A promise resolving to an API response containing the project data. - */ - getProject(id: number): Promise>; - - /** Gets a projects reported time - * @param {string} projectName The name of the project. - * @param {string} token The usertoken. - * @returns {Promise>} A promise resolving to an API response containing the project times. - */ - getProjectTimes( - projectName: string, - token: string, - ): Promise>; - - /** Gets a list of all users. - * @param {string} token The authentication token of the requesting user. - * @returns {Promise>} A promise resolving to an API response containing the list of users. - */ - getAllUsers(token: string): Promise>; - /** Gets all users in a project from name*/ - getAllUsersProject( - projectName: string, - token: string, - ): Promise>; - - /** Gets all unsigned reports in a project. - * @param {string} projectName The name of the project. - * @param {string} token The authentication token. - * @returns {Promise>} A promise resolving to an API response containing the list of unsigned reports. - */ - getUnsignedReportsInProject( - projectName: string, - token: string, - ): Promise>; - - /** - * Changes the username of a user in the database. - * @param {StrNameChange} data The object containing the previous and new username. - * @param {string} token The authentication token. - * @returns {Promise>} A promise resolving to an API response. - */ - changeUserName( - data: StrNameChange, - token: string, - ): Promise>; - /** - * Changes the role of a user in the database. - * @param {RoleChange} roleInfo The object containing the previous and new username. - * @param {string} token The authentication token. - * @returns {Promise>} A promise resolving to an API response. - */ - changeUserRole( - roleInfo: ProjectRoleChange, - token: string, - ): Promise>; - - addUserToProject( - addMemberInfo: AddMemberInfo, - token: string, - ): Promise>; - - removeUserFromProject( - user: string, - project: string, - token: string, - ): Promise>; - - removeProject( - projectName: string, - token: string, - ): Promise>; - - /** - * Signs a report. Keep in mind that the user which the token belongs to must be - * the project manager of the project the report belongs to. - * - * @param {number} reportId The id of the report to sign - * @param {string} token The authentication token - */ - signReport(reportId: number, token: string): Promise>; - - /** - * Unsigns a report. Keep in mind that the user which the token belongs to must be - * the project manager of the project the report belongs to. - * - * @param {number} reportId The id of the report to sign - * @param {string} token The authentication token - */ - unsignReport(reportId: number, token: string): Promise>; - - /** - * Promotes a user to project manager within a project. - * - * @param {string} userName The username of the user to promote - * @param {string} projectName The name of the project to promote the user in - * @returns {Promise} A promise resolving to an API response. - */ - promoteToPm( - userName: string, - projectName: string, - token: string, - ): Promise>; - /** - * Get the username from the id - * @param {number} id The id of the user - * @param {string} token Your token - */ - getUsername(id: number, token: string): Promise>; - - /** - * Deletes a WeeklyReport from the database - * @param {number} reportId The id of the report to delete - * @param {string} token The authentication token - */ - deleteWeeklyReport( - reportId: number, - token: string, - ): Promise>; - - /** - * Retrieves the total time spent on a project for a particular user (the user is determined by the token) - * - * @param {string} projectName The name of the project - * @param {string} token The authentication token - */ - getStatistics( - projectName: string, - token: string, - userName?: string, - ): Promise>; - - /** - * Changes the name of a project - * @param {string} projectName The name of the project - * @param {string} newProjectName The new name of the project - * @param {string} token The authentication token - */ - changeProjectName( - projectName: string, - newProjectName: string, - token: string, - ): Promise>; - - /** - * Changes the password of a user - * @param {string} username The username of the user - * @param {string} newPassword The new password - * @param {string} token The authentication token - */ - changeUserPassword( - username: string, - newPassword: string, - token: string, - ): Promise>; -} - -/** An instance of the API */ -export const api: API = { - async registerUser(user: NewUser): Promise> { - try { - const response = await fetch("/api/register", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(user), - }); - - if (!response.ok) { - return { - success: false, - message: "Failed to register user: " + response.status, - }; - } else { - // const data = (await response.json()) as User; // The API does not currently return the user - return { success: true }; - } - } catch (e) { - return { - success: false, - message: "Unknown error while registering user", - }; - } - }, - - async removeUser( - username: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/userdelete/${username}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(username), - }); - if (!response.ok) { - return { success: false, message: "Could not remove user" }; - } else { - return { success: true }; - } - } catch (e) { - return { success: false, message: "Failed to remove user" }; - } - }, - - async checkIfProjectManager( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/checkIfProjectManager/${projectName}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { - success: false, - message: "Failed to check if project manager", - }; - } else { - const data = (await response.json()) as boolean; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to check if project manager" }; - } - }, - - async createProject( - project: NewProject, - token: string, - ): Promise> { - try { - const response = await fetch("/api/project", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(project), - }); - - if (!response.ok) { - return { success: false, message: "Failed to create project" }; - } else { - return { success: true }; - } - } catch (e) { - return { success: false, message: "Failed to create project!" }; - } - }, - - async addUserToProject( - addMemberInfo: AddMemberInfo, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/addUserToProject/${addMemberInfo.projectName}/?userName=${addMemberInfo.userName}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { success: false, message: "Failed to add member" }; - } else { - return { success: true, message: "Added member" }; - } - } catch (e) { - return { success: false, message: "Failed to add member" }; - } - }, - - async removeUserFromProject( - user: string, - project: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/removeUserFromProject/${project}?userName=${user}`, - { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - if (!response.ok) { - return { success: false, message: "Failed to remove member" }; - } - } catch (e) { - return { success: false, message: "Failed to remove member" }; - } - return { success: true, message: "Removed member" }; - }, - - async renewToken(token: string): Promise> { - try { - const response = await fetch("/api/loginrenew", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to renew token" }; - } else { - const data = (await response.json()) as string; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to renew token" }; - } - }, - - async changeUserRole( - roleInfo: ProjectRoleChange, - token: string, - ): Promise> { - try { - const response = await fetch("/api/ProjectRoleChange", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(roleInfo), - }); - - if (!response.ok) { - if (response.status === 403) { - return { success: false, message: "Cannot change your own role" }; - } - return { success: false, message: "Could not change role" }; - } else { - return { success: true }; - } - } catch (e) { - return { success: false, message: "Could not change role" }; - } - }, - - async getUserProjects( - username: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/getUserProjects/${username}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: "Failed to get user projects", - }); - } else { - const data = (await response.json()) as Project[]; - return Promise.resolve({ success: true, data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "API fucked", - }); - } - }, - - async getProjectTimes( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/getProjectTimes/${projectName}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: - "Fetch error: " + response.status + ", failed to get project times", - }); - } else { - const data = (await response.json()) as projectTimes; - return Promise.resolve({ success: true, data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "API error! Could not get times.", - }); - } - }, - - async submitWeeklyReport( - weeklyReport: NewWeeklyReport, - token: string, - ): Promise> { - try { - const response = await fetch("/api/submitWeeklyReport", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(weeklyReport), - }); - - if (!response.ok) { - return { - success: false, - message: "Failed to submit weekly report", - }; - } - - const data = await response.text(); - return { success: true, message: data }; - } catch (e) { - return { - success: false, - message: "Failed to submit weekly report", - }; - } - }, - - async updateWeeklyReport( - weeklyReport: UpdateWeeklyReport, - token: string, - ): Promise> { - try { - const response = await fetch("/api/updateWeeklyReport", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(weeklyReport), - }); - - if (!response.ok) { - return { - success: false, - message: "Failed to update weekly report", - }; - } - - const data = await response.text(); - return { success: true, message: data }; - } catch (e) { - return { - success: false, - message: "Failed to update weekly report", - }; - } - }, - - async getWeeklyReport( - projectName: string, - week: string, - token: string, - targetUser?: string, - ): Promise> { - try { - const response = await fetch( - `/api/getWeeklyReport?projectName=${projectName}&week=${week}&targetUser=${targetUser ?? ""}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { success: false, message: "Failed to get weekly report" }; - } else { - const data = (await response.json()) as WeeklyReport; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to get weekly report" }; - } - }, - - async getAllWeeklyReportsForUser( - projectName: string, - token: string, - targetUser?: string, - ): Promise> { - try { - const response = await fetch( - `/api/getAllWeeklyReports/${projectName}?targetUser=${targetUser ?? ""}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { - success: false, - message: - "Failed to get weekly reports for project: Response code " + - response.status, - }; - } else { - const data = (await response.json()) as WeeklyReport[]; - return { success: true, data }; - } - } catch (e) { - return { - success: false, - message: "Failed to get weekly reports for project, unknown error", - }; - } - }, - - async login(NewUser: NewUser): Promise> { - try { - const response = await fetch("/api/login", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(NewUser), - }); - - if (!response.ok) { - return { - success: false, - data: `${response.status}`, - message: "Failed to login", - }; - } else { - const data = (await response.json()) as { token: string }; // Update the type of 'data' - return { success: true, data: data.token }; - } - } catch (e) { - return Promise.resolve({ success: false, message: "Failed to login" }); - } - }, - - async getProject(id: number): Promise> { - try { - const response = await fetch(`/api/project/${id}`, { - method: "GET", - }); - - if (!response.ok) { - return { - success: false, - message: "Failed to get project: Response code " + response.status, - }; - } else { - const data = (await response.json()) as Project; - return { success: true, data }; - } - // The code below is garbage but satisfies the linter - // This needs fixing, do not copy this pattern - } catch (e: unknown) { - return { - success: false, - message: "Failed to get project: " + (e as Error).toString(), - }; - } - }, - - async getAllUsers(token: string): Promise> { - try { - const response = await fetch("/api/users/all", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: "Failed to get users", - }); - } else { - const data = (await response.json()) as string[]; - return Promise.resolve({ success: true, data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "API is not ok", - }); - } - }, - //Gets all users in a project - async getAllUsersProject( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/getUsersProject/${projectName}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: "Failed to get users", - }); - } else { - const data = (await response.json()) as ProjectMember[]; - return Promise.resolve({ success: true, data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "API is not ok", - }); - } - }, - - async getUnsignedReportsInProject( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/getUnsignedReports/${projectName}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { - success: false, - message: - "Failed to get unsigned reports for project: Response code " + - response.status, - }; - } else { - const data = (await response.json()) as WeeklyReport[]; - return { success: true, data }; - } - } catch (e) { - return { - success: false, - message: "Failed to get unsigned reports for project, unknown error", - }; - } - }, - - async changeUserName( - data: StrNameChange, - token: string, - ): Promise> { - try { - const response = await fetch("/api/changeUserName", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - body: JSON.stringify(data), - }); - - if (!response.ok) { - return { success: false, message: "Failed to change username" }; - } else { - return { success: true }; - } - } catch (e) { - return { success: false, message: "Failed to change username" }; - } - }, - - async removeProject( - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/removeProject/${projectName}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return Promise.resolve({ - success: false, - message: "Failed to remove project", - }); - } else { - const data = await response.text(); - return Promise.resolve({ success: true, message: data }); - } - } catch (e) { - return Promise.resolve({ - success: false, - message: "Failed to remove project", - }); - } - }, - - async signReport( - reportId: number, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/signReport/${reportId}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to sign report" }; - } else { - return { success: true, message: "Report signed" }; - } - } catch (e) { - return { success: false, message: "Failed to sign report" }; - } - }, - - async unsignReport( - reportId: number, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/unsignReport/${reportId}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to unsign report" }; - } else { - return { success: true, message: "Report unsigned" }; - } - } catch (e) { - return { success: false, message: "Failed to unsign report" }; - } - }, - - async promoteToPm( - userName: string, - projectName: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/promoteToPm/${projectName}?userName=${userName}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - if (!response.ok) { - return { - success: false, - message: "Failed to promote user to project manager", - }; - } - } catch (e) { - return { - success: false, - message: "Failed to promote user to project manager", - }; - } - return { success: true, message: "User promoted to project manager" }; - }, - - async getUsername(id: number, token: string): Promise> { - try { - const response = await fetch(`/api/username?userId=${id}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to get username" }; - } else { - const data = (await response.json()) as string; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to get username" }; - } - }, - - async deleteWeeklyReport( - reportId: number, - token: string, - ): Promise> { - try { - const response = await fetch(`/api/deleteReport/${reportId}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }); - - if (!response.ok) { - return { success: false, message: "Failed to delete report" }; - } else { - return { success: true, message: "Report deleted" }; - } - } catch (e) { - return { success: false, message: "Failed to delete report" }; - } - }, - async getStatistics( - projectName: string, - token: string, - userName?: string, - ): Promise> { - try { - const response = await fetch( - `/api/getStatistics/?projectName=${projectName}&userName=${userName ?? ""}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { success: false, message: "Failed to get statistics" }; - } else { - const data = (await response.json()) as Statistics; - return { success: true, data }; - } - } catch (e) { - return { success: false, message: "Failed to get statistics" }; - } - }, - - async changeProjectName( - projectName: string, - newProjectName: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/changeProjectName/${projectName}?newProjectName=${newProjectName}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { success: false, message: "Failed to change project name" }; - } else { - return { success: true, message: "Project name changed" }; - } - } catch (e) { - return { success: false, message: "Failed to change project name" }; - } - }, - - async changeUserPassword( - username: string, - newPassword: string, - token: string, - ): Promise> { - try { - const response = await fetch( - `/api/changeUserPassword/${username}?newPassword=${newPassword}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + token, - }, - }, - ); - - if (!response.ok) { - return { success: false, message: "Failed to change password" }; - } else { - return { success: true, message: "Password changed" }; - } - } catch (e) { - return { success: false, message: "Failed to change password" }; - } - }, -}; diff --git a/frontend/src/API/GenApi.ts b/frontend/src/API/GenApi.ts deleted file mode 100644 index 8ca851b..0000000 --- a/frontend/src/API/GenApi.ts +++ /dev/null @@ -1,358 +0,0 @@ -/* eslint-disable */ -/* tslint:disable */ -/* - * --------------------------------------------------------------- - * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## - * ## ## - * ## AUTHOR: acacode ## - * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## - * --------------------------------------------------------------- - */ - -export interface TypesNewUser { - /** @example "password123" */ - password?: string; - /** @example "username123" */ - username?: string; -} - -export interface TypesToken { - token?: string; -} - -export type QueryParamsType = Record; -export type ResponseFormat = keyof Omit; - -export interface FullRequestParams extends Omit { - /** set parameter to `true` for call `securityWorker` for this request */ - secure?: boolean; - /** request path */ - path: string; - /** content type of request body */ - type?: ContentType; - /** query params */ - query?: QueryParamsType; - /** format of response (i.e. response.json() -> format: "json") */ - format?: ResponseFormat; - /** request body */ - body?: unknown; - /** base url */ - baseUrl?: string; - /** request cancellation token */ - cancelToken?: CancelToken; -} - -export type RequestParams = Omit; - -export interface ApiConfig { - baseUrl?: string; - baseApiParams?: Omit; - securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; - customFetch?: typeof fetch; -} - -export interface HttpResponse extends Response { - data: D; - error: E; -} - -type CancelToken = Symbol | string | number; - -export enum ContentType { - Json = "application/json", - FormData = "multipart/form-data", - UrlEncoded = "application/x-www-form-urlencoded", - Text = "text/plain", -} - -export class HttpClient { - public baseUrl: string = "//localhost:8080/api"; - private securityData: SecurityDataType | null = null; - private securityWorker?: ApiConfig["securityWorker"]; - private abortControllers = new Map(); - private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); - - private baseApiParams: RequestParams = { - credentials: "same-origin", - headers: {}, - redirect: "follow", - referrerPolicy: "no-referrer", - }; - - constructor(apiConfig: ApiConfig = {}) { - Object.assign(this, apiConfig); - } - - public setSecurityData = (data: SecurityDataType | null) => { - this.securityData = data; - }; - - protected encodeQueryParam(key: string, value: any) { - const encodedKey = encodeURIComponent(key); - return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; - } - - protected addQueryParam(query: QueryParamsType, key: string) { - return this.encodeQueryParam(key, query[key]); - } - - protected addArrayQueryParam(query: QueryParamsType, key: string) { - const value = query[key]; - return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); - } - - protected toQueryString(rawQuery?: QueryParamsType): string { - const query = rawQuery || {}; - const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); - return keys - .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) - .join("&"); - } - - protected addQueryParams(rawQuery?: QueryParamsType): string { - const queryString = this.toQueryString(rawQuery); - return queryString ? `?${queryString}` : ""; - } - - private contentFormatters: Record any> = { - [ContentType.Json]: (input: any) => - input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, - [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), - [ContentType.FormData]: (input: any) => - Object.keys(input || {}).reduce((formData, key) => { - const property = input[key]; - formData.append( - key, - property instanceof Blob - ? property - : typeof property === "object" && property !== null - ? JSON.stringify(property) - : `${property}`, - ); - return formData; - }, new FormData()), - [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), - }; - - protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { - return { - ...this.baseApiParams, - ...params1, - ...(params2 || {}), - headers: { - ...(this.baseApiParams.headers || {}), - ...(params1.headers || {}), - ...((params2 && params2.headers) || {}), - }, - }; - } - - protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { - if (this.abortControllers.has(cancelToken)) { - const abortController = this.abortControllers.get(cancelToken); - if (abortController) { - return abortController.signal; - } - return void 0; - } - - const abortController = new AbortController(); - this.abortControllers.set(cancelToken, abortController); - return abortController.signal; - }; - - public abortRequest = (cancelToken: CancelToken) => { - const abortController = this.abortControllers.get(cancelToken); - - if (abortController) { - abortController.abort(); - this.abortControllers.delete(cancelToken); - } - }; - - public request = async ({ - body, - secure, - path, - type, - query, - format, - baseUrl, - cancelToken, - ...params - }: FullRequestParams): Promise> => { - const secureParams = - ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && - this.securityWorker && - (await this.securityWorker(this.securityData))) || - {}; - const requestParams = this.mergeRequestParams(params, secureParams); - const queryString = query && this.toQueryString(query); - const payloadFormatter = this.contentFormatters[type || ContentType.Json]; - const responseFormat = format || requestParams.format; - - return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { - ...requestParams, - headers: { - ...(requestParams.headers || {}), - ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), - }, - signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null, - body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), - }).then(async (response) => { - const r = response as HttpResponse; - r.data = null as unknown as T; - r.error = null as unknown as E; - - const data = !responseFormat - ? r - : await response[responseFormat]() - .then((data) => { - if (r.ok) { - r.data = data; - } else { - r.error = data; - } - return r; - }) - .catch((e) => { - r.error = e; - return r; - }); - - if (cancelToken) { - this.abortControllers.delete(cancelToken); - } - - if (!response.ok) throw data; - return data; - }); - }; -} - -/** - * @title TTime API - * @version 0.0.1 - * @license AGPL (https://www.gnu.org/licenses/agpl-3.0.html) - * @baseUrl //localhost:8080/api - * @externalDocs https://swagger.io/resources/open-api/ - * @contact - * - * This is the API for TTime, a time tracking application. - */ -export class GenApi extends HttpClient { - login = { - /** - * @description Logs in a user and returns a JWT token - * - * @tags Auth - * @name LoginCreate - * @summary Login - * @request POST:/login - */ - loginCreate: (body: TypesNewUser, params: RequestParams = {}) => - this.request({ - path: `/login`, - method: "POST", - body: body, - type: ContentType.Json, - format: "json", - ...params, - }), - }; - loginrenew = { - /** - * @description Renews the users token. - * - * @tags Auth - * @name LoginrenewCreate - * @summary LoginRenews - * @request POST:/loginrenew - * @secure - */ - loginrenewCreate: (params: RequestParams = {}) => - this.request({ - path: `/loginrenew`, - method: "POST", - secure: true, - format: "json", - ...params, - }), - }; - promoteToAdmin = { - /** - * @description Promote chosen user to site admin - * - * @tags User - * @name PromoteToAdminCreate - * @summary PromoteToAdmin - * @request POST:/promoteToAdmin - * @secure - */ - promoteToAdminCreate: (NewUser: TypesNewUser, params: RequestParams = {}) => - this.request({ - path: `/promoteToAdmin`, - method: "POST", - body: NewUser, - secure: true, - type: ContentType.Json, - ...params, - }), - }; - register = { - /** - * @description Register a new user - * - * @tags Auth - * @name RegisterCreate - * @summary Register - * @request POST:/register - */ - registerCreate: (NewUser: TypesNewUser, params: RequestParams = {}) => - this.request({ - path: `/register`, - method: "POST", - body: NewUser, - type: ContentType.Json, - ...params, - }), - }; - userdelete = { - /** - * @description UserDelete deletes a user from the database - * - * @tags User - * @name UserdeleteDelete - * @summary UserDelete - * @request DELETE:/userdelete/{username} - * @secure - */ - userdeleteDelete: (username: string, params: RequestParams = {}) => - this.request({ - path: `/userdelete/${username}`, - method: "DELETE", - secure: true, - type: ContentType.Json, - ...params, - }), - }; - users = { - /** - * @description lists all users - * - * @tags User - * @name GetUsers - * @summary ListsAllUsers - * @request GET:/users/all - * @secure - */ - getUsers: (params: RequestParams = {}) => - this.request({ - path: `/users/all`, - method: "GET", - secure: true, - format: "json", - ...params, - }), - }; -} diff --git a/frontend/src/API/README.md b/frontend/src/API/README.md deleted file mode 100644 index cba9b03..0000000 --- a/frontend/src/API/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# API - -This file contains the high-level API interface and implementation. diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..20e5f1f --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,31 @@ +import reactLogo from "./assets/react.svg"; +import viteLogo from "/vite.svg"; +import "./App.css"; +import { CountButton } from "./Components/CountButton"; + +function App(): JSX.Element { + return ( + <> + +

Vite + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+ + ); +} + +export default App; diff --git a/frontend/src/Components/AddMember.tsx b/frontend/src/Components/AddMember.tsx deleted file mode 100644 index d8036b7..0000000 --- a/frontend/src/Components/AddMember.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { api } from "../API/API"; - -export interface AddMemberInfo { - userName: string; - projectName: string; -} - -/** - * Tries to add a member to a project - * @param {AddMemberInfo} props.membertoAdd - Contains user's name and project's name - * @returns {Promise} - */ -async function AddMember(props: { memberToAdd: AddMemberInfo }): Promise { - if (props.memberToAdd.userName === "") { - alert("You must choose at least one user to add"); - return; - } - try { - const response = await api.addUserToProject( - props.memberToAdd, - localStorage.getItem("accessToken") ?? "", - ); - if (response.success) { - alert(`[${props.memberToAdd.userName}] added`); - } else { - alert(`[${props.memberToAdd.userName}] not added`); - console.error(response.message); - } - } catch (error) { - alert(`[${props.memberToAdd.userName}] not added`); - console.error("An error occurred during member add:", error); - } -} - -export default AddMember; diff --git a/frontend/src/Components/AddProject.tsx b/frontend/src/Components/AddProject.tsx deleted file mode 100644 index 8c9c566..0000000 --- a/frontend/src/Components/AddProject.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { useState } from "react"; -import { api } from "../API/API"; -import { NewProject } from "../Types/goTypes"; -import Logo from "../assets/Logo.svg"; -import Button from "./Button"; -import { useNavigate } from "react-router-dom"; -import ProjectNameInput from "./Inputs/ProjectNameInput"; -import DescriptionInput from "./Inputs/DescriptionInput"; -import { alphanumeric } from "../Data/regex"; -import { projNameHighLimit, projNameLowLimit } from "../Data/constants"; - -/** - * Provides UI for adding a project to the system. - * @returns {JSX.Element} - Returns the component UI for adding a project - */ -function AddProject(): JSX.Element { - const [name, setName] = useState(""); - const [description, setDescription] = useState(""); - const navigate = useNavigate(); - - /** - * Tries to add a project to the system - */ - const handleCreateProject = async (): Promise => { - if ( - !alphanumeric.test(name) || - name.length > projNameHighLimit || - name.length < projNameLowLimit - ) { - alert( - "Please provide valid project name: \n-Between 10-99 characters \n-No special characters (.-!?/*)", - ); - return; - } - if (description.length > projNameHighLimit) { - alert("Please provide valid description: \n-Max 100 characters"); - return; - } - const project: NewProject = { - name: name.replace(/ /g, ""), - description: description.trim(), - }; - try { - const response = await api.createProject( - project, - localStorage.getItem("accessToken") ?? "", - ); - if (response.success) { - alert(`${project.name} added!`); - setDescription(""); - setName(""); - navigate("/admin"); - } else { - alert("Project not added, name could be taken"); - console.error(response.message); - } - } catch (error) { - alert("Project not added"); - console.error(error); - } - }; - - return ( -
-
-
{ - e.preventDefault(); - void handleCreateProject(); - }} - > - TTIME Logo -

- Create a new project -

- { - e.preventDefault(); - setName(e.target.value); - }} - /> -
- { - e.preventDefault(); - setDescription(e.target.value); - }} - placeholder={"Description (Optional)"} - /> -
-
- -

-
-
- ); -} - -export default AddProject; diff --git a/frontend/src/Components/AddUserToProject.tsx b/frontend/src/Components/AddUserToProject.tsx deleted file mode 100644 index 43d9345..0000000 --- a/frontend/src/Components/AddUserToProject.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { useEffect, useState } from "react"; -import Button from "./Button"; -import AddMember, { AddMemberInfo } from "./AddMember"; -import GetUsersInProject, { ProjectMember } from "./GetUsersInProject"; -import GetAllUsers from "./GetAllUsers"; -import InputField from "./InputField"; - -/** - * Provides UI for adding a member to a project. - * @returns {JSX.Element} - Returns the component UI for adding a member - */ -function AddUserToProject(props: { projectName: string }): JSX.Element { - const [names, setNames] = useState([]); - const [users, setUsers] = useState([]); - const [usersProj, setUsersProj] = useState([]); - const [search, setSearch] = useState(""); - - // Gets all users and project members for filtering - GetAllUsers({ setUsersProp: setUsers }); - GetUsersInProject({ - setUsersProp: setUsersProj, - projectName: props.projectName, - }); - /* - * Filters the members from users so that users who are already - * members are not shown - */ - useEffect(() => { - setUsers((prevUsers) => { - const filteredUsers = prevUsers.filter( - (user) => - !usersProj.some((projectUser) => projectUser.Username === user), - ); - return filteredUsers; - }); - }, [usersProj]); - - // Attempts to add all of the selected users to the project - const handleAddClick = async (): Promise => { - if (names.length === 0) { - alert("You have to choose at least one user to add"); - return; - } - for (const name of names) { - const newMember: AddMemberInfo = { - userName: name, - projectName: props.projectName, - }; - await AddMember({ memberToAdd: newMember }); - } - setNames([]); - location.reload(); - }; - - // Updates the names that have been selected - const handleUserClick = (user: string): void => { - setNames((prevNames): string[] => { - if (!prevNames.includes(user)) { - return [...prevNames, user]; - } - return prevNames.filter((name) => name !== user); - }); - }; - - return ( -
-

- {props.projectName} -

-

- Choose users to add: -

- -
- { - setSearch(e.target.value); - }} - /> -
    -
    - {users - .filter((user) => { - return search.toLowerCase() === "" - ? user - : user.toLowerCase().includes(search.toLowerCase()); - }) - .map((user) => ( -
  • { - handleUserClick(user); - }} - > - {user} -
  • - ))} -
-
-

- Number of users to be added: {names.length} -

-
-
-
- ); -} - -export default AddUserToProject; diff --git a/frontend/src/Components/AllTimeReportsInProject.tsx b/frontend/src/Components/AllTimeReportsInProject.tsx deleted file mode 100644 index aef1df1..0000000 --- a/frontend/src/Components/AllTimeReportsInProject.tsx +++ /dev/null @@ -1,71 +0,0 @@ -//Info: This component is used to display all the time reports for a project. It will display the week number, -//total time spent, and if the report has been signed or not. The user can click on a report to edit it. -import { useEffect, useState } from "react"; -import { WeeklyReport } from "../Types/goTypes"; -import { Link, useParams } from "react-router-dom"; -import { api } from "../API/API"; - -/** - * Renders a component that displays all the time reports for a specific project. - * @returns {JSX.Element} representing the component. - */ -function AllTimeReportsInProject(): JSX.Element { - const { projectName } = useParams(); - const [weeklyReports, setWeeklyReports] = useState([]); - - // Call getProjects when the component mounts - useEffect(() => { - const getWeeklyReports = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllWeeklyReportsForUser( - projectName ?? "", - token, - ); - console.log(response); - if (response.success) { - setWeeklyReports(response.data ?? []); - } else { - console.error(response.message); - } - }; - - void getWeeklyReports(); - }, [projectName]); - - return ( - <> -
- {weeklyReports.map((newWeeklyReport, index) => ( - -
-

- {"Week: "} - {newWeeklyReport.week} -

-

- {"Total Time: "} - {newWeeklyReport.developmentTime + - newWeeklyReport.meetingTime + - newWeeklyReport.adminTime + - newWeeklyReport.ownWorkTime + - newWeeklyReport.studyTime + - newWeeklyReport.testingTime}{" "} - min -

-

- {"Signed: "} - {newWeeklyReport.signedBy ? "YES" : "NO"} -

-
- - ))} -
- - ); -} - -export default AllTimeReportsInProject; diff --git a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx b/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx deleted file mode 100644 index a96c55b..0000000 --- a/frontend/src/Components/AllTimeReportsInProjectOtherUser.tsx +++ /dev/null @@ -1,73 +0,0 @@ -//Info: This component is used to display all the time reports for a project. It will display the week number, -//total time spent, and if the report has been signed or not. The user can click on a report to edit it. -import { useEffect, useState } from "react"; -import { WeeklyReport } from "../Types/goTypes"; -import { Link, useParams } from "react-router-dom"; -import { api } from "../API/API"; - -/** - * Renders a component that displays all the time reports for a specific project. - * @returns {JSX.Element} representing the component. - */ -function AllTimeReportsInProject(): JSX.Element { - const { username } = useParams(); - const { projectName } = useParams(); - const [weeklyReports, setWeeklyReports] = useState([]); - - useEffect(() => { - const getWeeklyReports = async (): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllWeeklyReportsForUser( - projectName ?? "", - token, - username ?? "", - ); - console.log(response); - if (response.success) { - setWeeklyReports(response.data ?? []); - } else { - console.error(response.message); - } - }; - - void getWeeklyReports(); - }, [projectName, username]); - - return ( - <> -

{username}'s Time Reports

-
- {weeklyReports.map((newWeeklyReport, index) => ( - -
-

- {"Week: "} - {newWeeklyReport.week} -

-

- {"Total Time: "} - {newWeeklyReport.developmentTime + - newWeeklyReport.meetingTime + - newWeeklyReport.adminTime + - newWeeklyReport.ownWorkTime + - newWeeklyReport.studyTime + - newWeeklyReport.testingTime}{" "} - min -

-

- {"Signed: "} - {newWeeklyReport.signedBy ? "YES" : "NO"} -

-
- - ))} -
- - ); -} - -export default AllTimeReportsInProject; diff --git a/frontend/src/Components/AuthorizedRoute.tsx b/frontend/src/Components/AuthorizedRoute.tsx deleted file mode 100644 index d9c8c59..0000000 --- a/frontend/src/Components/AuthorizedRoute.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Navigate } from "react-router-dom"; -import React from "react"; - -interface AuthorizedRouteProps { - children: React.ReactNode; - isAuthorized: boolean; -} - -export function AuthorizedRoute({ - children, - isAuthorized, -}: AuthorizedRouteProps): JSX.Element { - if (!isAuthorized) { - return ; - } - - return children as React.ReactElement; -} diff --git a/frontend/src/Components/BackButton.tsx b/frontend/src/Components/BackButton.tsx deleted file mode 100644 index 4f58140..0000000 --- a/frontend/src/Components/BackButton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -//info: Back button component to navigate back to the previous page -import { useNavigate } from "react-router-dom"; - -/** - * Renders a back button component. - * - * @returns The JSX element representing the back button. - */ -function BackButton(): JSX.Element { - const navigate = useNavigate(); - const goBack = (): void => { - navigate(-1); - }; - return ( - - ); -} - -export default BackButton; diff --git a/frontend/src/Components/BackgroundAnimation.tsx b/frontend/src/Components/BackgroundAnimation.tsx deleted file mode 100644 index 87fca9e..0000000 --- a/frontend/src/Components/BackgroundAnimation.tsx +++ /dev/null @@ -1,29 +0,0 @@ -//info: Background animation component to animate the background of loginpage -import { useEffect } from "react"; - -/** - * Renders a background animation component. - * This component pre-loads images and starts a background transition animation. - */ -const BackgroundAnimation = (): JSX.Element => { - useEffect(() => { - const images = [ - "src/assets/1.jpg", - "src/assets/2.jpg", - "src/assets/3.jpg", - "src/assets/4.jpg", - ]; - - // Pre-load images - for (const i of images) { - console.log(i); - } - - // Start animation - document.body.style.animation = "backgroundTransition 30s infinite"; - }, []); - - return <>; -}; - -export default BackgroundAnimation; diff --git a/frontend/src/Components/BasicWindow.tsx b/frontend/src/Components/BasicWindow.tsx deleted file mode 100644 index d53d367..0000000 --- a/frontend/src/Components/BasicWindow.tsx +++ /dev/null @@ -1,30 +0,0 @@ -//info: Basic window component to display content and buttons of a page, inclduing header and footer -//content to insert is placed in the content prop, and buttons in the buttons prop -import Header from "./Header"; -import Footer from "./Footer"; - -/** - * Renders a basic window component with a header, content, and footer. - * - * @param {Object} props - The component props. - * @param {React.ReactNode} props.content - The content to be rendered in the window. - * @param {React.ReactNode} props.buttons - The buttons to be rendered in the footer. - * @returns {JSX.Element} The rendered basic window component. - */ -function BasicWindow({ - content, - buttons, -}: { - content: React.ReactNode; - buttons: React.ReactNode; -}): JSX.Element { - return ( -
-
-
{content}
-
{buttons}
-
- ); -} - -export default BasicWindow; diff --git a/frontend/src/Components/Button.tsx b/frontend/src/Components/Button.tsx deleted file mode 100644 index 13ae807..0000000 --- a/frontend/src/Components/Button.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Button component to display a button with text and onClick function. - * - * @param {Object} props - The component props. - * @param {string} props.text - The text to display on the button. - * @param {Function} props.onClick - The function to run when the button is clicked. - * @param {"submit" | "button" | "reset"} props.type - The type of button. - * @returns {JSX.Element} The rendered Button component. - */ -function Button({ - text, - onClick, - type, -}: { - text: string; - onClick: () => void; - type: "submit" | "button" | "reset"; -}): JSX.Element { - return ( - - ); -} - -export default Button; diff --git a/frontend/src/Components/ChangeProjectName.tsx b/frontend/src/Components/ChangeProjectName.tsx deleted file mode 100644 index 8348217..0000000 --- a/frontend/src/Components/ChangeProjectName.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { APIResponse, api } from "../API/API"; - -/** - * Changes the name of a project - * @param {string} props.projectName - Current project name - * @param {string} props.newProjectName - New project name - * @returns {void} - Nothing - */ -export default function ChangeProjectName(props: { - projectName: string; - newProjectName: string; -}): void { - if (props.projectName === "" || props.projectName === props.newProjectName) { - alert("You have to give a new name\n\nName not changed"); - return; - } - api - .changeProjectName( - props.projectName, - props.newProjectName, - localStorage.getItem("accessToken") ?? "", - ) - .then((response: APIResponse) => { - if (response.success) { - alert("Name changed successfully"); - location.reload(); - } else { - alert("Name not changed, name could be taken"); - console.error(response.message); - } - }) - .catch((error) => { - alert("Name not changed"); - console.error("An error occurred during change:", error); - }); -} diff --git a/frontend/src/Components/ChangeRole.tsx b/frontend/src/Components/ChangeRole.tsx deleted file mode 100644 index 54b1468..0000000 --- a/frontend/src/Components/ChangeRole.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { APIResponse, api } from "../API/API"; - -export interface ProjectRoleChange { - username: string; - role: "project_manager" | "member" | ""; - projectname: string; -} - -export default function ChangeRole(roleChangeInfo: ProjectRoleChange): void { - if ( - roleChangeInfo.username === "" || - roleChangeInfo.role === "" || - roleChangeInfo.projectname === "" - ) { - // FOR DEBUG - // console.log(roleChangeInfo.role + ": Role"); - // console.log(roleChangeInfo.projectname + ": P-Name"); - // console.log(roleChangeInfo.username + ": U-name"); - alert("You have to select a role"); - return; - } - api - .changeUserRole(roleChangeInfo, localStorage.getItem("accessToken") ?? "") - .then((response: APIResponse) => { - if (response.success) { - alert("Role changed successfully"); - location.reload(); - } else { - alert(response.message); - console.error(response.message); - } - }) - .catch((error) => { - alert(error); - console.error("An error occurred during change:", error); - }); -} diff --git a/frontend/src/Components/ChangeRoleView.tsx b/frontend/src/Components/ChangeRoleView.tsx deleted file mode 100644 index 9a69e34..0000000 --- a/frontend/src/Components/ChangeRoleView.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { useState } from "react"; -import Button from "./Button"; -import ChangeRole, { ProjectRoleChange } from "./ChangeRole"; - -export default function ChangeRoleView(props: { - projectName: string; - username: string; - currentRole: string; -}): JSX.Element { - const [selectedRole, setSelectedRole] = useState< - "project_manager" | "member" | "" - >(""); - - const handleRoleChange = ( - event: React.ChangeEvent, - ): void => { - if (event.target.value === "member") { - setSelectedRole(event.target.value); - } else if (event.target.value === "project_manager") { - setSelectedRole(event.target.value); - } - }; - - const handleSubmit = (event: React.FormEvent): void => { - console.log("Cur: " + props.currentRole + " " + "new: " + selectedRole); - event.preventDefault(); - if (selectedRole === props.currentRole) { - alert(`Already ${props.currentRole}, nothing changed`); - return; - } - const roleChangeInfo: ProjectRoleChange = { - username: props.username, - projectname: props.projectName, - role: selectedRole, - }; - ChangeRole(roleChangeInfo); - }; - - return ( -
-

Select role:

-
-
- -
- -
-
- ); -} diff --git a/frontend/src/Components/ChangeRoles.tsx b/frontend/src/Components/ChangeRoles.tsx deleted file mode 100644 index e11d623..0000000 --- a/frontend/src/Components/ChangeRoles.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useState } from "react"; -import { useParams } from "react-router-dom"; -import Button from "./Button"; - -export default function ChangeRoles(): JSX.Element { - const [selectedRole, setSelectedRole] = useState(""); - const { username } = useParams(); - - const handleRoleChange = ( - event: React.ChangeEvent, - ): void => { - setSelectedRole(event.target.value); - }; - - // const handleSubmit = async (event: React.FormEvent) => { - // event.preventDefault(); - - // const response = await api.changeRole(username, selectedRole, token); - // if (response.success) { - // console.log("Role changed successfully"); - // } else { - // console.error("Failed to change role:", response.message); - // } - // }; - - return ( - <> -

- Change roll for: {username} -

-
-
-
- -
-
- -
-
- -
-
-
; +} diff --git a/frontend/src/Components/DeleteProject.tsx b/frontend/src/Components/DeleteProject.tsx deleted file mode 100644 index 4add857..0000000 --- a/frontend/src/Components/DeleteProject.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { api, APIResponse } from "../API/API"; - -/** - * Use to delete a project from the system - * @param {string} props.projectToDelete - The projectname of project to delete - * @returns {void} Nothing - * @example - * const exampleProjectName = "project"; - * DeleteProject({ projectToDelete: exampleProjectName }); - */ - -function DeleteProject(props: { projectToDelete: string }): void { - api - .removeProject( - props.projectToDelete, - localStorage.getItem("accessToken") ?? "", - ) - .then((response: APIResponse) => { - if (response.success) { - alert("Project has been deleted!"); - location.reload(); - } else { - alert("Project has not been deleted"); - console.error(response.message); - } - }) - .catch((error) => { - alert("project has not been deleted"); - console.error("An error occurred during deletion:", error); - }); -} - -export default DeleteProject; diff --git a/frontend/src/Components/DeleteUser.tsx b/frontend/src/Components/DeleteUser.tsx deleted file mode 100644 index 7c5e8e8..0000000 --- a/frontend/src/Components/DeleteUser.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { User } from "../Types/goTypes"; -import { api, APIResponse } from "../API/API"; - -/** - * Use to remove a user from the system - * @param {string} props.usernameToDelete - The username of user to remove - * @returns {boolean} True if removed, false if not - * @example - * const exampleUsername = "user"; - * DeleteUser({ usernameToDelete: exampleUsername }); - */ - -function DeleteUser(props: { usernameToDelete: string }): boolean { - let removed = false; - api - .removeUser( - props.usernameToDelete, - localStorage.getItem("accessToken") ?? "", - ) - .then((response: APIResponse) => { - if (response.success) { - alert("User has been deleted!"); - location.reload(); - removed = true; - } else { - alert("User has not been deleted"); - console.error(response.message); - } - }) - .catch((error) => { - alert("User has not been deleted"); - console.error("An error occurred during deletion:", error); - }); - return removed; -} - -export default DeleteUser; diff --git a/frontend/src/Components/DisplayUnsignedReports.tsx b/frontend/src/Components/DisplayUnsignedReports.tsx deleted file mode 100644 index 3cd32f8..0000000 --- a/frontend/src/Components/DisplayUnsignedReports.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useState, useEffect } from "react"; -import { Link, useParams } from "react-router-dom"; -import { api } from "../API/API"; -import { WeeklyReport } from "../Types/goTypes"; - -function DisplayUserProject(): JSX.Element { - const { projectName } = useParams(); - const [unsignedReports, setUnsignedReports] = useState([]); - const [usernames, setUsernames] = useState([]); - const token = localStorage.getItem("accessToken") ?? ""; - - useEffect(() => { - const getUnsignedReports = async (): Promise => { - const response = await api.getUnsignedReportsInProject( - projectName ?? "", - token, - ); - console.log(response); - if (response.success) { - setUnsignedReports(response.data ?? []); - const usernamesPromises = (response.data ?? []).map((report) => - api.getUsername(report.userId, token), - ); - const usernamesResponses = await Promise.all(usernamesPromises); - const usernames = usernamesResponses.map( - (res) => (res.data as { username?: string }).username ?? "", - ); - setUsernames(usernames); - } else { - console.error(response.message); - } - }; - - void getUnsignedReports(); - }, [projectName, token]); - - return ( - <> -

- All Unsigned Reports In: {projectName}{" "} -

-
- {unsignedReports.map((unsignedReport: WeeklyReport, index: number) => ( -

-
-
- Username: -

{usernames[index]}

{" "} - Week: -

{unsignedReport.week}

- Total Time: -

- {unsignedReport.developmentTime + - unsignedReport.meetingTime + - unsignedReport.adminTime + - unsignedReport.ownWorkTime + - unsignedReport.studyTime + - unsignedReport.testingTime} -

- Signed: -

NO

-
-
-
- -

- View Report -

- -
-
-
-

- ))} -
- - ); -} - -export default DisplayUserProject; diff --git a/frontend/src/Components/DisplayUserProjects.tsx b/frontend/src/Components/DisplayUserProjects.tsx deleted file mode 100644 index 551e809..0000000 --- a/frontend/src/Components/DisplayUserProjects.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useState } from "react"; -import { Project } from "../Types/goTypes"; -import { useNavigate } from "react-router-dom"; -import GetProjects from "./GetProjects"; -import { api } from "../API/API"; - -/** - * Renders a component that displays the projects a user is a part of and links to the projects start-page. - * @returns The JSX element representing the component. - */ -function DisplayUserProject(): JSX.Element { - const [projects, setProjects] = useState([]); - const navigate = useNavigate(); - - GetProjects({ - setProjectsProp: setProjects, - username: localStorage.getItem("username") ?? "", - }); - - const handleProjectClick = async (projectName: string): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.checkIfProjectManager(projectName, token); - console.log(response.data); - if (response.success) { - if ( - (response.data as unknown as { isProjectManager: boolean }) - .isProjectManager - ) { - navigate(`/PMProjectPage/${projectName}`); - } else { - navigate(`/project/${projectName}`); - } - } else { - // handle error - console.error(response.message); - } - }; - - return ( - <> -

Your Projects

-
- {projects.map((project) => ( -
void handleProjectClick(project.name)} - key={project.id} - > -

- {project.name} -

-
- ))} -
- - ); -} - -export default DisplayUserProject; diff --git a/frontend/src/Components/EditWeeklyReport.tsx b/frontend/src/Components/EditWeeklyReport.tsx deleted file mode 100644 index 5037a76..0000000 --- a/frontend/src/Components/EditWeeklyReport.tsx +++ /dev/null @@ -1,343 +0,0 @@ -import { useState, useEffect } from "react"; -import { WeeklyReport, UpdateWeeklyReport } from "../Types/goTypes"; -import { api } from "../API/API"; -import { useNavigate, useParams } from "react-router-dom"; -import Button from "./Button"; - -/** - * Renders the component for editing a weekly report. - * @returns JSX.Element - */ -export default function GetWeeklyReport(): JSX.Element { - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); - - const token = localStorage.getItem("accessToken") ?? ""; - const { projectName, fetchedWeek, signedOrUnsigned } = useParams<{ - projectName: string; - fetchedWeek: string; - signedOrUnsigned: string; - }>(); - const username = localStorage.getItem("userName") ?? ""; - console.log(projectName, fetchedWeek, signedOrUnsigned); - - useEffect(() => { - const fetchWeeklyReport = async (): Promise => { - const response = await api.getWeeklyReport( - projectName ?? "", - fetchedWeek ?? "", - token, - ); - - if (response.success) { - const report: WeeklyReport = response.data ?? { - reportId: 0, - userId: 0, - projectId: 0, - week: 0, - developmentTime: 0, - meetingTime: 0, - adminTime: 0, - ownWorkTime: 0, - studyTime: 0, - testingTime: 0, - }; - setWeek(report.week); - setDevelopmentTime(report.developmentTime); - setMeetingTime(report.meetingTime); - setAdminTime(report.adminTime); - setOwnWorkTime(report.ownWorkTime); - setStudyTime(report.studyTime); - setTestingTime(report.testingTime); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - }; - - void fetchWeeklyReport(); - }, [projectName, fetchedWeek, signedOrUnsigned, token]); - - const handleUpdateWeeklyReport = async (): Promise => { - const updateWeeklyReport: UpdateWeeklyReport = { - userName: username, - projectName: projectName ?? "", - week, - developmentTime, - meetingTime, - adminTime, - ownWorkTime, - studyTime, - testingTime, - }; - - await api.updateWeeklyReport(updateWeeklyReport, token); - }; - - const navigate = useNavigate(); - - return ( - <> -

Edit Time Report

-
-
{ - if (week === 0) { - alert("Please enter a week number"); - e.preventDefault(); - return; - } - e.preventDefault(); - void handleUpdateWeeklyReport(); - alert("Changes submitted"); - navigate(-1); - }} - > -
-
-

Week: {week}

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Activity - - Total Time (min) -
Development - { - if (e.target.value === "") { - setDevelopmentTime(0); - return; - } else { - setDevelopmentTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - onClick={() => { - if (signedOrUnsigned === "signed") { - alert("You cannot edit a signed report."); - } - }} - readOnly={signedOrUnsigned === "signed"} - /> -
Meeting - { - if (e.target.value === "") { - setMeetingTime(0); - return; - } else { - setMeetingTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - onClick={() => { - if (signedOrUnsigned === "signed") { - alert("You cannot edit a signed report."); - } - }} - readOnly={signedOrUnsigned === "signed"} - /> -
Administration - { - if (e.target.value === "") { - setAdminTime(0); - return; - } else { - setAdminTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - onClick={() => { - if (signedOrUnsigned === "signed") { - alert("You cannot edit a signed report."); - } - }} - readOnly={signedOrUnsigned === "signed"} - /> -
Own Work - { - if (e.target.value === "") { - setOwnWorkTime(0); - return; - } else { - setOwnWorkTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - onClick={() => { - if (signedOrUnsigned === "signed") { - alert("You cannot edit a signed report."); - } - }} - readOnly={signedOrUnsigned === "signed"} - /> -
Studies - { - if (e.target.value === "") { - setStudyTime(0); - return; - } else { - setStudyTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - onClick={() => { - if (signedOrUnsigned === "signed") { - alert("You cannot edit a signed report."); - } - }} - readOnly={signedOrUnsigned === "signed"} - /> -
Testing - { - if (e.target.value === "") { - setTestingTime(0); - return; - } else { - setTestingTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - onClick={() => { - if (signedOrUnsigned === "signed") { - alert("You cannot edit a signed report."); - } - }} - readOnly={signedOrUnsigned === "signed"} - /> -
- {signedOrUnsigned !== "signed" && ( -
-
-
- - ); -} diff --git a/frontend/src/Components/Footer.tsx b/frontend/src/Components/Footer.tsx deleted file mode 100644 index 192926f..0000000 --- a/frontend/src/Components/Footer.tsx +++ /dev/null @@ -1,21 +0,0 @@ -//info: Footer component to display the footer of a page where the buttons are placed -import React from "react"; - -/** - * Footer component. - * - * @param {Object} props - The component props. - * @param {React.ReactNode} props.children - The children elements to render inside the footer (buttons). - * @returns {JSX.Element} The rendered footer component. - */ -function Footer({ children }: { children: React.ReactNode }): JSX.Element { - return ( -
-
- {children} -
-
- ); -} - -export default Footer; diff --git a/frontend/src/Components/GetAllUsers.tsx b/frontend/src/Components/GetAllUsers.tsx deleted file mode 100644 index 73ad244..0000000 --- a/frontend/src/Components/GetAllUsers.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Dispatch, useEffect } from "react"; -import { api } from "../API/API"; - -/** - * Gets all usernames in the system and puts them in an array - * @param props - A setStateAction for the array you want to put users in - * @returns {void} Nothing - * @example - * const [users, setUsers] = useState([]); - * GetAllUsers({ setUsersProp: setUsers }); - */ -function GetAllUsers(props: { - setUsersProp: Dispatch>; -}): void { - const setUsers: Dispatch> = props.setUsersProp; - useEffect(() => { - const fetchUsers = async (): Promise => { - try { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllUsers(token); - if (response.success) { - setUsers(response.data ?? []); - } else { - console.error("Failed to fetch users:", response.message); - } - } catch (error) { - console.error("Error fetching users:", error); - } - }; - - void fetchUsers(); - }, [setUsers]); -} - -export default GetAllUsers; diff --git a/frontend/src/Components/GetProjectTimes.tsx b/frontend/src/Components/GetProjectTimes.tsx deleted file mode 100644 index 38288ec..0000000 --- a/frontend/src/Components/GetProjectTimes.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Dispatch, SetStateAction, useEffect } from "react"; -import { api } from "../API/API"; - -/** - * Interface for reported time per category + total time reported - */ -export interface projectTimes { - admin: number; - development: number; - meeting: number; - own_work: number; - study: number; - testing: number; - totalTime?: number; -} - -/** - * Gets all reported times for this project - * @param {Dispatch} props.setTimesProp - A setStateAction for the map you want to put times in - * @param {string} props.projectName - Username - * @returns {void} Nothing - * @example - * const projectName = "Example"; - * const [times, setTimes] = useState(); - * GetProjectTimes({ setTimesProp: setTimes, projectName: projectName }); - */ -function GetProjectTimes(props: { - setTimesProp: Dispatch>; - projectName: string; -}): void { - const setTimes: Dispatch> = - props.setTimesProp; - useEffect(() => { - const fetchUsers = async (): Promise => { - try { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getProjectTimes(props.projectName, token); - if (response.success && response.data) { - // Calculates total time reported - response.data.totalTime = response.data.admin; - response.data.totalTime += response.data.development; - response.data.totalTime += response.data.meeting; - response.data.totalTime += response.data.own_work; - response.data.totalTime += response.data.study; - response.data.totalTime += response.data.testing; - setTimes(response.data); - } else { - console.error("Failed to fetch project times:", response.message); - } - } catch (error) { - console.error("Error fetching times:", error); - } - }; - - void fetchUsers(); - }, [props.projectName, setTimes]); -} - -export default GetProjectTimes; diff --git a/frontend/src/Components/GetProjects.tsx b/frontend/src/Components/GetProjects.tsx deleted file mode 100644 index bd6c303..0000000 --- a/frontend/src/Components/GetProjects.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Dispatch, useEffect } from "react"; -import { Project } from "../Types/goTypes"; -import { api } from "../API/API"; - -/** - * Gets all projects that user is a member of - * @param {Dispatch} props.setProjectsProp - A setStateAction for the array you want to put projects in - * @param {string} props.username - Username - * @returns {void} Nothing - * @example - * const username = "Example"; - * const [projects, setProjects] = useState([]); - * GetProjects({ setProjectsProp: setProjects, username: username }); - */ -function GetProjects(props: { - setProjectsProp: Dispatch>; - username: string; -}): void { - const setProjects: Dispatch> = - props.setProjectsProp; - useEffect(() => { - const fetchUsers = async (): Promise => { - try { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getUserProjects(props.username, token); - if (response.success) { - setProjects(response.data ?? []); - } else { - console.error("Failed to fetch projects:", response.message); - } - } catch (error) { - console.error("Error fetching projects:", error); - } - }; - - void fetchUsers(); - }, [props.username, setProjects]); -} - -export default GetProjects; diff --git a/frontend/src/Components/GetUsersInProject.tsx b/frontend/src/Components/GetUsersInProject.tsx deleted file mode 100644 index eb32e9b..0000000 --- a/frontend/src/Components/GetUsersInProject.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Dispatch, useEffect } from "react"; -import { api } from "../API/API"; - -export interface ProjectMember { - Username: string; - UserRole: string; -} - -/** - * Gets all members of a project - * @param string - The project's name - * @param Dispatch - A setStateAction for the array you want to put members in - * @returns {void} Nothing - * @example - * const [users, setUsers] = useState([]); - * GetUsersInProject({ projectName: props.projectname, setUsersProp: setUsers }); - */ -function GetUsersInProject(props: { - projectName: string; - setUsersProp: Dispatch>; -}): void { - const setUsers: Dispatch> = - props.setUsersProp; - useEffect(() => { - const fetchUsers = async (): Promise => { - try { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.getAllUsersProject(props.projectName, token); - if (response.success) { - setUsers(response.data ?? []); - } else { - console.error("Failed to fetch members:", response.message); - } - } catch (error) { - console.error("Error fetching members:", error); - } - }; - void fetchUsers(); - }, [props.projectName, setUsers]); -} - -export default GetUsersInProject; diff --git a/frontend/src/Components/Header.tsx b/frontend/src/Components/Header.tsx deleted file mode 100644 index 9be2f4b..0000000 --- a/frontend/src/Components/Header.tsx +++ /dev/null @@ -1,70 +0,0 @@ -//info: Header component to display the header of the page including the logo and user information where thr user can logout -import { useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; -import backgroundImage from "../assets/1.jpg"; - -/** - * Renders the header component. - * @returns JSX.Element representing the header component. - */ -function Header(): JSX.Element { - const [isOpen, setIsOpen] = useState(false); - const username = localStorage.getItem("username"); - const navigate = useNavigate(); - - const handleLogout = (): void => { - localStorage.clear(); - }; - - const handleNavigation = (): void => { - if (username === "admin") { - navigate("/admin"); - } else { - navigate("/yourProjects"); - } - }; - - return ( -
-
- TTIME Logo -
- -
{ - setIsOpen(true); - }} - onMouseLeave={() => { - setIsOpen(false); - }} - > - - - {isOpen && ( -
- - - -
- )} -
-
- ); -} - -export default Header; diff --git a/frontend/src/Components/InputField.tsx b/frontend/src/Components/InputField.tsx deleted file mode 100644 index 5a5cdaf..0000000 --- a/frontend/src/Components/InputField.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/** - * A customizable input field - * @param props - Settings for the field - * @returns {JSX.Element} The input field - * @example - * { - * setExample(e.target.value); - * }} - * /> - */ -function InputField(props: { - label?: string; - placeholder?: string; - type?: string; - value?: string; - onChange?: (e: React.ChangeEvent) => void; -}): JSX.Element { - return ( -
- - -
- ); -} - -export default InputField; diff --git a/frontend/src/Components/Inputs/DescriptionInput.tsx b/frontend/src/Components/Inputs/DescriptionInput.tsx deleted file mode 100644 index 43e046c..0000000 --- a/frontend/src/Components/Inputs/DescriptionInput.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { projDescHighLimit, projDescLowLimit } from "../../Data/constants"; - -export default function DescriptionInput(props: { - desc: string; - placeholder: string; - onChange: (e: React.ChangeEvent) => void; -}): JSX.Element { - return ( - <> - -
- {props.desc.length > projDescHighLimit && ( -

- Description must be under 100 characters -

- )} - {props.desc.length <= projDescHighLimit && - props.desc.length > projDescLowLimit && ( -

- Valid project description! -

- )} -
- - ); -} diff --git a/frontend/src/Components/Inputs/PasswordInput.tsx b/frontend/src/Components/Inputs/PasswordInput.tsx deleted file mode 100644 index 9f67e98..0000000 --- a/frontend/src/Components/Inputs/PasswordInput.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { passwordLength } from "../../Data/constants"; -import { lowercase } from "../../Data/regex"; - -export default function PasswordInput(props: { - password: string; - onChange: (e: React.ChangeEvent) => void; -}): JSX.Element { - const password = props.password; - return ( - <> - -
- {password.length === passwordLength && - lowercase.test(props.password) && ( -

- Valid password! -

- )} - {password.length !== passwordLength && ( -

- Password must be 6 characters -

- )} - {!lowercase.test(password) && password !== "" && ( -

- No number, uppercase or special
characters allowed -

- )} -
- - ); -} diff --git a/frontend/src/Components/Inputs/ProjectNameInput.tsx b/frontend/src/Components/Inputs/ProjectNameInput.tsx deleted file mode 100644 index de28c12..0000000 --- a/frontend/src/Components/Inputs/ProjectNameInput.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { projNameHighLimit, projNameLowLimit } from "../../Data/constants"; -import { alphanumeric } from "../../Data/regex"; - -export default function ProjectNameInput(props: { - name: string; - onChange: (e: React.ChangeEvent) => void; -}): JSX.Element { - const name = props.name; - return ( - <> - = projNameLowLimit && - name.length <= projNameHighLimit && - alphanumeric.test(name) - ? "border-2 border-green-500 dark:border-green-500 focus-visible:border-green-500 outline-none rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight" - : "border-2 border-red-600 dark:border-red-600 focus:border-red-600 outline-none rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight" - } - spellCheck="false" - id="New name" - type="text" - placeholder="Project name" - value={name} - onChange={props.onChange} - /> -
- {!alphanumeric.test(name) && name !== "" && ( -

- No special characters allowed -

- )} - {(name.length < projNameLowLimit || - name.length > projNameHighLimit) && ( -

- Project name must be 10-99 characters -

- )} - {alphanumeric.test(props.name) && - name.length >= projNameLowLimit && - name.length <= projNameHighLimit && ( -

- Valid project name! -

- )} -
- - ); -} diff --git a/frontend/src/Components/Inputs/UsernameInput.tsx b/frontend/src/Components/Inputs/UsernameInput.tsx deleted file mode 100644 index 8f653ba..0000000 --- a/frontend/src/Components/Inputs/UsernameInput.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { usernameLowLimit, usernameUpLimit } from "../../Data/constants"; -import { alphanumeric } from "../../Data/regex"; - -export default function UsernameInput(props: { - username: string; - onChange: (e: React.ChangeEvent) => void; -}): JSX.Element { - const username = props.username; - return ( - <> - = usernameLowLimit && - username.length <= usernameUpLimit && - alphanumeric.test(props.username) - ? "border-2 border-green-500 dark:border-green-500 focus-visible:border-green-500 outline-none rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight" - : "border-2 border-red-600 dark:border-red-600 focus:border-red-600 outline-none rounded-2xl w-full py-2 px-3 text-gray-700 leading-tight" - } - spellCheck="false" - id="New username" - type="text" - placeholder="Username" - value={username} - onChange={props.onChange} - /> -
- {alphanumeric.test(username) && - username.length >= usernameLowLimit && - username.length <= usernameUpLimit && ( -

- Valid username! -

- )} - {!alphanumeric.test(username) && username !== "" && ( -

- No special characters allowed -

- )} - {!( - username.length >= usernameLowLimit && - username.length <= usernameUpLimit - ) && ( -

- Username must be 5-10 characters -

- )} -
- - ); -} diff --git a/frontend/src/Components/LoginCheck.tsx b/frontend/src/Components/LoginCheck.tsx deleted file mode 100644 index 50ffb98..0000000 --- a/frontend/src/Components/LoginCheck.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { NewUser } from "../Types/goTypes"; -import { api, APIResponse } from "../API/API"; -import { Dispatch, SetStateAction } from "react"; - -/* - * Checks if user is in database with api.login and then sets proper authority level - * TODO: change so that it checks for user type (admin, user, pm) somehow instead - **/ -function LoginCheck(props: { - username: string; - password: string; - setAuthority: Dispatch>; -}): void { - if (props.username === "" || props.password === "") { - alert("Please enter username and password to login"); - return; - } - const user: NewUser = { - username: props.username, - password: props.password, - }; - - localStorage.clear(); - - api - .login(user) - .then((response: APIResponse) => { - if (response.success) { - if (response.data !== undefined) { - const token = response.data; - localStorage.setItem("accessToken", token); - localStorage.setItem("username", props.username); - //TODO: change so that it checks for user type (admin, user, pm) instead - if (token !== "" && props.username === "admin") { - props.setAuthority((prevAuth) => { - prevAuth = 1; - return prevAuth; - }); - } else if (token !== "") { - props.setAuthority((prevAuth) => { - prevAuth = 2; - return prevAuth; - }); - } - } else { - console.error("Token was undefined"); - } - } else { - if (response.data === "500") { - console.error(response.message); - alert("No connection/Error"); - } else { - console.error( - "Token could not be fetched/No such user" + response.message, - ); - alert("Incorrect login information"); - } - } - }) - .catch((error) => { - console.error("An error occurred during login:", error); - }); -} - -export default LoginCheck; diff --git a/frontend/src/Components/LoginField.tsx b/frontend/src/Components/LoginField.tsx deleted file mode 100644 index 8d0aa62..0000000 --- a/frontend/src/Components/LoginField.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { Dispatch, FormEventHandler, SetStateAction } from "react"; -import Button from "./Button"; -import InputField from "./InputField"; - -/** - * A login field complete with input fields - * and a button for submitting the information - * @param props - Settings - * @returns {JSX.Element} A login component - * @example - * - */ -function Login(props: { - handleSubmit: FormEventHandler; - setUsername: Dispatch>; - setPassword: Dispatch>; - username: string; - password: string; -}): JSX.Element { - return ( -
-
- { - props.setUsername(e.target.value); - }} - value={props.username} - placeholder={"Username"} - /> - { - props.setPassword(e.target.value); - }} - value={props.password} - placeholder={"Password"} - /> -
-
- ); -} diff --git a/frontend/src/Components/NewWeeklyReport.tsx b/frontend/src/Components/NewWeeklyReport.tsx deleted file mode 100644 index ffc8b21..0000000 --- a/frontend/src/Components/NewWeeklyReport.tsx +++ /dev/null @@ -1,312 +0,0 @@ -//info: New weekly report form component to create a new weekly report to -//sumbit development time, meeting time, admin time, own work time, study time and testing time -import { useState } from "react"; -import type { NewWeeklyReport } from "../Types/goTypes"; -import { api } from "../API/API"; -import { useNavigate, useParams } from "react-router-dom"; -import Button from "./Button"; - -/** - * Renders a form for creating a new weekly report. - * @returns The JSX element representing the new weekly report form. - */ -export default function NewWeeklyReport(): JSX.Element { - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); - - const { projectName } = useParams(); - const token = localStorage.getItem("accessToken") ?? ""; - - const handleNewWeeklyReport = async (): Promise => { - const newWeeklyReport: NewWeeklyReport = { - projectName: projectName ?? "", - week: week, - developmentTime: developmentTime, - meetingTime: meetingTime, - adminTime: adminTime, - ownWorkTime: ownWorkTime, - studyTime: studyTime, - testingTime: testingTime, - }; - - const response = await api.submitWeeklyReport(newWeeklyReport, token); - console.log(response); - if (response.success) { - return true; - } else { - return false; - } - }; - - const navigate = useNavigate(); - // Check if the browser is Chrome or Edge - const isChromeOrEdge = /Chrome|Edg/.test(navigator.userAgent); - - return ( - <> -
-
{ - e.preventDefault(); - void (async (): Promise => { - if (week === 0 || week > 53 || week < 1) { - alert("Please enter a valid week number"); - return; - } - - const success = await handleNewWeeklyReport(); - if (!success) { - alert( - "Error occurred! Your connection to the server might be lost or a time report for this week already exists, please check your connection or go to the edit page to edit your report or change week number.", - ); - return; - } - alert("Weekly report submitted successfully"); - navigate(-1); - })(); - }} - > -
- {isChromeOrEdge ? ( - { - const weekNumber = parseInt(e.target.value.split("-W")[1]); - setWeek(weekNumber); - }} - onKeyDown={(event) => { - const keyValue = event.key; - if (!/\d/.test(keyValue) && keyValue !== "Backspace") - event.preventDefault(); - }} - onPaste={(event) => { - event.preventDefault(); - }} - /> - ) : ( - { - const weekNumber = parseInt(e.target.value); - setWeek(weekNumber); - }} - onKeyDown={(event) => { - const keyValue = event.key; - if (!/\d/.test(keyValue) && keyValue !== "Backspace") - event.preventDefault(); - }} - onPaste={(event) => { - event.preventDefault(); - }} - /> - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Activity - - Total Time (min) -
Development - { - if (e.target.value === "") { - setDevelopmentTime(0); - return; - } else { - setDevelopmentTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - /> -
Meeting - { - if (e.target.value === "") { - setMeetingTime(0); - return; - } else { - setMeetingTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - /> -
Administration - { - if (e.target.value === "") { - setAdminTime(0); - return; - } else { - setAdminTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - /> -
Own Work - { - if (e.target.value === "") { - setOwnWorkTime(0); - return; - } else { - setOwnWorkTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - /> -
Studies - { - if (e.target.value === "") { - setStudyTime(0); - return; - } else { - setStudyTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - /> -
Testing - { - if (e.target.value === "") { - setTestingTime(0); - return; - } else { - setTestingTime(parseInt(e.target.value)); - } - }} - onKeyDown={(event) => { - const keyValue = event.key; - if ( - !/\d/.test(keyValue) && - keyValue !== "Backspace" && - keyValue !== "ArrowLeft" && - keyValue !== "ArrowRight" - ) - event.preventDefault(); - }} - /> -
-
- -
- - ); -} diff --git a/frontend/src/Components/OtherUsersTR.tsx b/frontend/src/Components/OtherUsersTR.tsx deleted file mode 100644 index 40e0b94..0000000 --- a/frontend/src/Components/OtherUsersTR.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import { useState, useEffect } from "react"; -import { WeeklyReport } from "../Types/goTypes"; -import { api } from "../API/API"; -import { useParams, useNavigate } from "react-router-dom"; -import Button from "./Button"; - -/** - * Renders the component for editing a weekly report. - * @returns JSX.Element - */ - -//This component does not yet work as intended. It is supposed to display the weekly report of a user in a project. -export default function OtherUsersTR(): JSX.Element { - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); - const [reportId, setReportId] = useState(0); - - const token = localStorage.getItem("accessToken") ?? ""; - const { projectName } = useParams(); - const { username } = useParams(); - const { fetchedWeek } = useParams(); - const { signedOrUnsigned } = useParams(); - console.log(projectName, username, fetchedWeek, signedOrUnsigned); - - useEffect(() => { - const fetchUsersWeeklyReport = async (): Promise => { - const response = await api.getWeeklyReport( - projectName ?? "", - fetchedWeek?.toString() ?? "0", - token, - username ?? "", - ); - - if (response.success) { - const report: WeeklyReport = response.data ?? { - reportId: 0, - userId: 0, - projectId: 0, - week: 0, - developmentTime: 0, - meetingTime: 0, - adminTime: 0, - ownWorkTime: 0, - studyTime: 0, - testingTime: 0, - }; - setReportId(report.reportId); - setWeek(report.week); - setDevelopmentTime(report.developmentTime); - setMeetingTime(report.meetingTime); - setAdminTime(report.adminTime); - setOwnWorkTime(report.ownWorkTime); - setStudyTime(report.studyTime); - setTestingTime(report.testingTime); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - }; - - void fetchUsersWeeklyReport(); - }); - - const handleUnsignWeeklyReport = async (): Promise => { - const response = await api.unsignReport(reportId, token); - console.log(response); - console.log(reportId); - if (response.success) { - return true; - } else { - return false; - } - }; - - const handleDeleteWeeklyReport = async (): Promise => { - const response = await api.deleteWeeklyReport(reportId, token); - console.log(response); - if (response.success) { - return true; - } - return false; - }; - const navigate = useNavigate(); - - return ( - <> -

{username}'s Report

-
-
-
-

Week: {week}

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Activity - Total Time (min) -
Development - -
Meeting - -
Administration - -
Own Work - -
Studies - -
Testing - -
-
- {signedOrUnsigned === "signed" && ( -
-
-
- - ); -} diff --git a/frontend/src/Components/PMProjectMenu.tsx b/frontend/src/Components/PMProjectMenu.tsx deleted file mode 100644 index f0cb492..0000000 --- a/frontend/src/Components/PMProjectMenu.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Link, useParams } from "react-router-dom"; -import { JSX } from "react/jsx-runtime"; - -function PMProjectMenu(): JSX.Element { - const { projectName } = useParams(); - return ( - <> -

{projectName}

-
- -

- Your Time Reports -

- - -

- New Time Report -

- - -

- Statistics -

- - -

- Unsigned Time Reports -

- -
- - ); -} -export default PMProjectMenu; diff --git a/frontend/src/Components/ProjectInfoModal.tsx b/frontend/src/Components/ProjectInfoModal.tsx deleted file mode 100644 index 4be3397..0000000 --- a/frontend/src/Components/ProjectInfoModal.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import Button from "./Button"; -import GetUsersInProject, { ProjectMember } from "./GetUsersInProject"; -import { Link } from "react-router-dom"; -import GetProjectTimes, { projectTimes } from "./GetProjectTimes"; -import DeleteProject from "./DeleteProject"; -import InputField from "./InputField"; -import ProjectNameInput from "./Inputs/ProjectNameInput"; -import { alphanumeric } from "../Data/regex"; -import { projNameHighLimit, projNameLowLimit } from "../Data/constants"; -import ChangeProjectName from "./ChangeProjectName"; - -function ProjectInfoModal(props: { - projectname: string; - onClose: () => void; - onClick: (username: string, userRole: string) => void; -}): JSX.Element { - const [showInput, setShowInput] = useState(false); - const [users, setUsers] = useState([]); - const [times, setTimes] = useState(); - const [search, setSearch] = useState(""); - const [newProjName, setNewProjName] = useState(""); - const totalTime = useRef(0); - GetUsersInProject({ projectName: props.projectname, setUsersProp: setUsers }); - - GetProjectTimes({ setTimesProp: setTimes, projectName: props.projectname }); - - const handleChangeNameView = (): void => { - if (showInput) { - setNewProjName(""); - setShowInput(false); - } else { - setShowInput(true); - } - }; - - const handleClickChangeName = (): void => { - if ( - newProjName.length > projNameHighLimit || - newProjName.length < projNameLowLimit || - !alphanumeric.test(newProjName) - ) { - alert( - "Please provide valid project name: \n-Between 10-99 characters \n-No special characters (.-!?/*)", - ); - return; - } - - if ( - confirm( - `Are you sure you want to change name of ${props.projectname} to ${newProjName}?`, - ) - ) { - ChangeProjectName({ - projectName: props.projectname, - newProjectName: newProjName, - }); - } else { - alert("Name was not changed!"); - } - }; - - useEffect(() => { - if (times?.totalTime !== undefined) { - totalTime.current = times.totalTime; - } - }, [times]); - - return ( -
-
-
-

{props.projectname}

-

- (Change project name) -

- {showInput && ( - <> -

Change name:

-
- -
-
-
- - )} -

Statistics:

-
-

Number of members: {users.length}

-

- Total time reported:{" "} - {Math.floor(totalTime.current / 60 / 24) + " d "} - {Math.floor((totalTime.current / 60) % 24) + " h "} - {(totalTime.current % 60) + " m "} -

-
-

Project members:

-
- { - setSearch(e.target.value); - }} - /> -
    - {users - .filter((user) => { - return search.toLowerCase() === "" - ? user.Username - : user.Username.toLowerCase().includes( - search.toLowerCase(), - ); - }) - .map((user) => ( -
  • { - props.onClick(user.Username, user.UserRole); - }} - > - - Name: {user.Username} -
    - Role: {user.UserRole} -
    -
  • - ))} -
-
-
-
-
-
-
- ); -} - -export default ProjectInfoModal; diff --git a/frontend/src/Components/ProjectListAdmin.tsx b/frontend/src/Components/ProjectListAdmin.tsx deleted file mode 100644 index 6461dae..0000000 --- a/frontend/src/Components/ProjectListAdmin.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useState } from "react"; -import { NewProject } from "../Types/goTypes"; -import ProjectInfoModal from "./ProjectInfoModal"; -import MemberInfoModal from "./MemberInfoModal"; -import InputField from "./InputField"; - -/** - * A list of projects for admin manage projects page, that sets an onClick - * function for eact project
  • element, which displays a modul with - * user info. - * @param props - An array of projects to display - * @returns {JSX.Element} The project list - * @example - * const projects: NewProject[] = [{ name: "Project", description: "New" }]; - * return - */ - -export function ProjectListAdmin(props: { - projects: NewProject[]; -}): JSX.Element { - const [projectModalVisible, setProjectModalVisible] = useState(false); - const [projectName, setProjectName] = useState(""); - const [userModalVisible, setUserModalVisible] = useState(false); - const [username, setUsername] = useState(""); - const [userRole, setUserRole] = useState(""); - const [search, setSearch] = useState(""); - - const handleClickUser = (username: string, userRole: string): void => { - setUsername(username); - setUserRole(userRole); - setUserModalVisible(true); - }; - - const handleClickProject = (projectname: string): void => { - setProjectName(projectname); - setProjectModalVisible(true); - }; - - const handleCloseProject = (): void => { - setProjectName(""); - setProjectModalVisible(false); - }; - - const handleCloseUser = (): void => { - setUsername(""); - setUserRole(""); - setUserModalVisible(false); - }; - - return ( - <> -

    Manage Projects

    - {projectModalVisible && ( - - )} - {userModalVisible && ( - - )} -
    - { - setSearch(e.target.value); - }} - /> -
      - {props.projects - .filter((project) => { - return search.toLowerCase() === "" - ? project.name - : project.name.toLowerCase().includes(search.toLowerCase()); - }) - .map((project) => ( -
    • { - handleClickProject(project.name); - }} - > - {project.name} -
    • - ))} -
    -
    - - ); -} diff --git a/frontend/src/Components/ProjectListUser.tsx b/frontend/src/Components/ProjectListUser.tsx deleted file mode 100644 index 96eeaff..0000000 --- a/frontend/src/Components/ProjectListUser.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Link } from "react-router-dom"; -import { Project } from "../Types/goTypes"; - -/** - * The props for the ProjectsProps component - */ -interface ProjectProps { - projects: Project[]; -} - -/** - * A list of projects for users, that links the user to the right project page - * thanks to the state property - * @param props - The projects to display - * @returns {JSX.Element} The project list - * @example - * const projects = [{ id: 1, name: "Random name" }]; - * return ; - */ - -export function ProjectListUser(props: ProjectProps): JSX.Element { - return ( -
    -
      - {props.projects.map((project) => ( - -
    • - {project.name} -
    • - - ))} -
    -
    - ); -} diff --git a/frontend/src/Components/ProjectMembers.tsx b/frontend/src/Components/ProjectMembers.tsx deleted file mode 100644 index e06ed75..0000000 --- a/frontend/src/Components/ProjectMembers.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useState } from "react"; -import { Link, useParams } from "react-router-dom"; -import GetUsersInProject, { ProjectMember } from "./GetUsersInProject"; -import { api } from "../API/API"; - -function ProjectMembers(): JSX.Element { - const { projectName } = useParams(); - const [projectMembers, setProjectMembers] = useState([]); - - GetUsersInProject({ - projectName: projectName ?? "", - setUsersProp: setProjectMembers, - }); - - const handleUserDeleteClick = async (username: string): Promise => { - const token = localStorage.getItem("accessToken") ?? ""; - const response = await api.removeUserFromProject( - username, - projectName ?? "", - token, - ); - console.log(response.data); - - // Remove the deleted user from the state - setProjectMembers((prevMembers) => - prevMembers.filter((member) => member.Username !== username), - ); - }; - - return ( - <> -

    - All Members In: {projectName}{" "} -

    -
    - {projectMembers.map((projectMember: ProjectMember, index: number) => { - if (projectMember.Username === "admin") { - return null; // Skip rendering for admin user - } - return ( -

    -
    -
    -

    {projectMember.Username}

    - Role: -

    {projectMember.UserRole}

    -
    -
    -
    - {projectMember.Username !== - localStorage.getItem("username") && ( -

    { - confirm( - "Are you sure you want to delete this user? This action cannot be undone.", - ) && - void handleUserDeleteClick(projectMember.Username); - }} - > - Delete User -

    - )} - -

    - View Reports -

    - -
    -
    -
    -

    - ); - })} -
    - - ); -} - -export default ProjectMembers; diff --git a/frontend/src/Components/Register.tsx b/frontend/src/Components/Register.tsx deleted file mode 100644 index 7310e4f..0000000 --- a/frontend/src/Components/Register.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { useState } from "react"; -import { NewUser } from "../Types/goTypes"; -import { api } from "../API/API"; -import Logo from "../assets/Logo.svg"; -import Button from "./Button"; -import UsernameInput from "./Inputs/UsernameInput"; -import PasswordInput from "./Inputs/PasswordInput"; -import { alphanumeric, lowercase } from "../Data/regex"; -import { - passwordLength, - usernameLowLimit, - usernameUpLimit, -} from "../Data/constants"; - -/** - * Renders a registration form for the admin to add new users in. - * @returns The JSX element representing the registration form. - */ -export default function Register(): JSX.Element { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [errMessage, setErrMessage] = useState(""); - - const handleRegister = async (): Promise => { - if ( - username.length > usernameUpLimit || - username.length < usernameLowLimit || - !alphanumeric.test(username) - ) { - alert( - "Please provide valid username: \n-Between 5-10 characters \n-No special characters (.-!?/*)", - ); - return; - } - if (password.length !== passwordLength || !lowercase.test(password)) { - alert( - "Please provide valid password: \n-Exactly 6 characters \n-No uppercase letters \n-No numbers \n-No special characters (.-!?/*)", - ); - return; - } - const newUser: NewUser = { - username: username, - password: password, - }; - const response = await api.registerUser(newUser); - if (response.success) { - alert(`${newUser.username} added!`); - setPassword(""); - setUsername(""); - } else { - alert("User not added, name could be taken"); - setErrMessage(response.message ?? "Unknown error"); - console.error(errMessage); - } - }; - - return ( -
    -
    -
    { - e.preventDefault(); - void handleRegister(); - }} - > - TTIME Logo -

    - Register New User -

    - - { - setUsername(e.target.value); - }} - /> -
    - { - setPassword(e.target.value); - }} - /> - -
    -
    - -

    -
    -
    - ); -} diff --git a/frontend/src/Components/RemoveUserFromProj.tsx b/frontend/src/Components/RemoveUserFromProj.tsx deleted file mode 100644 index eab79d9..0000000 --- a/frontend/src/Components/RemoveUserFromProj.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { api, APIResponse } from "../API/API"; - -/** - * Removes a user from a project - * @param {string} props.usernameToDelete - The username of user to remove - * @param {string} props.projectName - Project to remove user from - * @returns {void} - * @example - * const exampleUsername = "user"; - * const exampleProjectName "project"; - * RemoveUserFromProj({ userToRemove: exampleUsername, projectName: exampleProjectName }); - */ - -export default function RemoveUserFromProj(props: { - userToRemove: string; - projectName: string; -}): void { - if (props.userToRemove === localStorage.getItem("username")) { - alert("Cannot remove yourself"); - return; - } - api - .removeUserFromProject( - props.userToRemove, - props.projectName, - localStorage.getItem("accessToken") ?? "", - ) - .then((response: APIResponse) => { - if (response.success) { - alert(`${props.userToRemove} has been removed!`); - location.reload(); - } else { - alert(`${props.userToRemove} has not been removed due to an error`); - console.error(response.message); - } - }) - .catch((error) => { - alert(`${props.userToRemove} has not been removed due to an error`); - console.error("An error occurred during deletion:", error); - }); -} diff --git a/frontend/src/Components/TaskList.tsx b/frontend/src/Components/TaskList.tsx deleted file mode 100644 index 1e9cb76..0000000 --- a/frontend/src/Components/TaskList.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The shape of a task - */ -interface Task { - id: number; - name: string; -} - -/** - * The props for the TaskList component - */ -interface TaskProps { - tasks: Task[]; -} - -/** - * A simple list of tasks - * @param props - The tasks to display - * @returns {JSX.Element} The task list - * @example - * const tasks = [{ id: 1, name: "Do the thing" }]; - * return ; - */ -export function TaskList(props: TaskProps): JSX.Element { - return ( -
    -

    Task List

    -

    Tasks go here

    -
      - {props.tasks.map((task) => ( -
    • {task.name}
    • - ))} -
    -
    - ); -} diff --git a/frontend/src/Components/TimePerActivity.tsx b/frontend/src/Components/TimePerActivity.tsx deleted file mode 100644 index 0963e88..0000000 --- a/frontend/src/Components/TimePerActivity.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; -import { api } from "../API/API"; -import { projectTimes } from "./GetProjectTimes"; - -/** - * Renders the component for showing total time per role in a project. - * @returns JSX.Element - */ -export default function TimePerRole(): JSX.Element { - const [development, setDevelopment] = useState(0); - const [meeting, setMeeting] = useState(0); - const [admin, setAdmin] = useState(0); - const [own_work, setOwnWork] = useState(0); - const [study, setStudy] = useState(0); - const [testing, setTesting] = useState(0); - const total = development + meeting + admin + own_work + study + testing; - - const token = localStorage.getItem("accessToken") ?? ""; - const { projectName } = useParams(); - - const fetchTimePerActivity = async (): Promise => { - const response = await api.getProjectTimes(projectName ?? "", token); - { - if (response.success) { - const report: projectTimes = response.data ?? { - development: 0, - meeting: 0, - admin: 0, - own_work: 0, - study: 0, - testing: 0, - }; - setDevelopment(report.development); - setMeeting(report.meeting); - setAdmin(report.admin); - setOwnWork(report.own_work); - setStudy(report.study); - setTesting(report.testing); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - } - }; - - useEffect(() => { - void fetchTimePerActivity(); - }); - - return ( - <> -

    - Total Time Per Activity For All Members In: {projectName}{" "} -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Activity - Total Time (min) -
    Development - -
    Meeting - -
    Administration - -
    Own Work - -
    Studies - -
    Testing - -
    In Total: -

    {total}

    -
    -
    -
    - - ); -} diff --git a/frontend/src/Components/TimePerRole.tsx b/frontend/src/Components/TimePerRole.tsx deleted file mode 100644 index f62d83a..0000000 --- a/frontend/src/Components/TimePerRole.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; - -/** - * Renders the component for showing total time per role in a project. - * @returns JSX.Element - */ -export default function TimePerRole(): JSX.Element { - const [PManagerTime, setPManagerTime] = useState(0); - const [SManagerTime, setSManagerTime] = useState(0); - const [DeveloperTime, setDeveloperTime] = useState(0); - const [TesterTime, setTesterTime] = useState(0); - - // const token = localStorage.getItem("accessToken") ?? ""; - // const username = localStorage.getItem("username") ?? ""; - const { projectName } = useParams(); - - // const fetchTimePerRole = async (): Promise => { - // const response = await api.getTimePerRole( - // username, - // projectName ?? "", - // token, - // ); - // { - // if (response.success) { - // const report: TimePerRole = response.data ?? { - // PManagerTime: 0, - // SManagerTime: 0, - // DeveloperTime: 0, - // TesterTime: 0, - // }; - // } else { - // console.error("Failed to fetch weekly report:", response.message); - // } - // } - - interface TimePerRole { - PManager: number; - SManager: number; - Developer: number; - Tester: number; - } - - const fetchTimePerRole = async (): Promise => { - // Use mock data - const report: TimePerRole = { - PManager: 120, - SManager: 80, - Developer: 200, - Tester: 150, - }; - - // Set the state with the mock data - setPManagerTime(report.PManager); - setSManagerTime(report.SManager); - setDeveloperTime(report.Developer); - setTesterTime(report.Tester); - - await Promise.resolve(); - }; - - useEffect(() => { - void fetchTimePerRole(); - }); - - return ( - <> -

    - Total Time Per Role In: {projectName}{" "} -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    Role - Total Time (min) -
    Project Manager - { - event.preventDefault(); - }} - /> -
    System Manager - { - event.preventDefault(); - }} - /> -
    Administration - { - event.preventDefault(); - }} - /> -
    Own Work - { - event.preventDefault(); - }} - /> -
    -
    -
    - - ); -} diff --git a/frontend/src/Components/UserInfoModal.tsx b/frontend/src/Components/UserInfoModal.tsx deleted file mode 100644 index 460ef85..0000000 --- a/frontend/src/Components/UserInfoModal.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import Button from "./Button"; -import DeleteUser from "./DeleteUser"; -import UserProjectListAdmin from "./UserProjectListAdmin"; -import { useState } from "react"; -import ChangeUsername from "./ChangeUsername"; -import { StrNameChange } from "../Types/goTypes"; -import UsernameInput from "./Inputs/UsernameInput"; -import PasswordInput from "./Inputs/PasswordInput"; -import { alphanumeric, lowercase } from "../Data/regex"; -import { - passwordLength, - usernameLowLimit, - usernameUpLimit, -} from "../Data/constants"; -import ChangeUserPassword from "./ChangeUserPassword"; - -function UserInfoModal(props: { - isVisible: boolean; - username: string; - onClose: () => void; -}): JSX.Element { - const [showNameInput, setShowNameInput] = useState(false); - const [showPwordInput, setShowPwordInput] = useState(false); - const [newUsername, setNewUsername] = useState(""); - const [newPassword, setNewPassword] = useState(""); - if (!props.isVisible) { - return <>; - } - - /* - * Switches name input between visible/invisible - * and makes password input invisible - */ - const handleShowNameInput = (): void => { - if (showPwordInput) setShowPwordInput(false); - if (showNameInput) { - setShowNameInput(false); - setNewUsername(""); - } else { - setShowNameInput(true); - setNewPassword(""); - } - }; - - /* - * Switches password input between visible/invisible - * and makes username input invisible - */ - const handleShowPwordInput = (): void => { - if (showNameInput) setShowNameInput(false); - if (showPwordInput) { - setShowPwordInput(false); - setNewPassword(""); - } else { - setShowPwordInput(true); - setNewUsername(""); - } - }; - - // Handles name change and checks if new name meets requirements - const handleClickChangeName = (): void => { - if ( - !alphanumeric.test(newUsername) || - newUsername.length > usernameUpLimit || - newUsername.length < usernameLowLimit - ) { - alert( - "Please provide valid username: \n-Between 5-10 characters \n-No special characters (.-!?/*)", - ); - return; - } - if ( - confirm( - `Do you really want to change username of ${props.username} to ${newUsername}?`, - ) - ) { - const nameChange: StrNameChange = { - prevName: props.username, - newName: newUsername.replace(/ /g, ""), - }; - ChangeUsername({ nameChange: nameChange }); - } else { - alert("Name was not changed!"); - } - }; - - // Handles password change and checks if new password meets requirements - const handleClickChangePassword = (): void => { - if (newPassword.length !== passwordLength || !lowercase.test(newPassword)) { - alert( - "Please provide valid password: \n-Exactly 6 characters \n-No uppercase letters \n-No numbers \n-No special characters (.-!?/*)", - ); - return; - } - if ( - confirm(`Are you sure you want to change password of ${props.username}?`) - ) { - ChangeUserPassword({ - username: props.username, - newPassword: newPassword, - }); - } else { - alert("Password was not changed!"); - } - }; - - return ( -
    -
    -
    -

    {props.username}

    -

    Change:

    -

    - - Username - {" "} - - Password - -

    - {showNameInput && ( -
    - { - setNewUsername(e.target.value); - }} - /> -
    - )} - {showPwordInput && ( -
    - { - setNewPassword(e.target.value); - }} - /> -
    - )} -

    Member of these projects:

    - -
    -
    -
    -
    -
    - ); -} - -export default UserInfoModal; diff --git a/frontend/src/Components/UserListAdmin.tsx b/frontend/src/Components/UserListAdmin.tsx deleted file mode 100644 index 23e49db..0000000 --- a/frontend/src/Components/UserListAdmin.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useState } from "react"; -import UserInfoModal from "./UserInfoModal"; -import InputField from "./InputField"; - -/** - * A list of users for admin manage users page, that sets an onClick - * function for eact user
  • element, which displays a modul with - * user info. - * @param props - An array of users users to display - * @returns {JSX.Element} The user list - * @example - * const users = [{ id: 1, userName: "ExampleName" }]; - * return ; - */ - -export function UserListAdmin(props: { users: string[] }): JSX.Element { - const [modalVisible, setModalVisible] = useState(false); - const [username, setUsername] = useState(""); - const [search, setSearch] = useState(""); - - const handleClick = (username: string): void => { - setUsername(username); - setModalVisible(true); - }; - - const handleClose = (): void => { - setUsername(""); - setModalVisible(false); - }; - - return ( - <> -

    Manage Users

    - -
    - { - setSearch(e.target.value); - }} - /> -
      - {props.users - .filter((user) => { - return search.toLowerCase() === "" - ? user - : user.toLowerCase().includes(search.toLowerCase()); - }) - .map((user) => ( -
    • { - handleClick(user); - }} - > - {user} -
    • - ))} -
    -
    - - ); -} diff --git a/frontend/src/Components/UserProjectListAdmin.tsx b/frontend/src/Components/UserProjectListAdmin.tsx deleted file mode 100644 index 8f28ce9..0000000 --- a/frontend/src/Components/UserProjectListAdmin.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useState } from "react"; -import { Project } from "../Types/goTypes"; -import GetProjects from "./GetProjects"; - -function UserProjectListAdmin(props: { username: string }): JSX.Element { - const [projects, setProjects] = useState([]); - - GetProjects({ setProjectsProp: setProjects, username: props.username }); - - return ( -
    -
      - {projects.map((project) => ( -
    • - {project.name} -
    • - ))} -
    -
    - ); -} - -export default UserProjectListAdmin; diff --git a/frontend/src/Components/UserProjectMenu.tsx b/frontend/src/Components/UserProjectMenu.tsx deleted file mode 100644 index 4be4dee..0000000 --- a/frontend/src/Components/UserProjectMenu.tsx +++ /dev/null @@ -1,32 +0,0 @@ -//info: User project menu component to display the user project menu where the user can navigate to -//existing time reports in a project and create a new time report -import { useParams, Link } from "react-router-dom"; -import { JSX } from "react/jsx-runtime"; - -/** - * Renders the user project menu component. - * - * @returns JSX.Element representing the user project menu. - */ -function UserProjectMenu(): JSX.Element { - const { projectName } = useParams(); - - return ( - <> -

    {projectName}

    -
    - -

    - Your Time Reports -

    - - -

    - New Time Report -

    - -
    - - ); -} -export default UserProjectMenu; diff --git a/frontend/src/Components/UserStatistics.tsx b/frontend/src/Components/UserStatistics.tsx deleted file mode 100644 index c84f1a0..0000000 --- a/frontend/src/Components/UserStatistics.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { useState, useEffect } from "react"; -import { useParams } from "react-router-dom"; -import { api } from "../API/API"; -import { Statistics } from "../Types/goTypes"; - -/** - * Renders the component for showing total time per role in a project. - * @returns JSX.Element - */ -export default function UserStatistics(): JSX.Element { - const [development, setDevelopment] = useState(0); - const [meeting, setMeeting] = useState(0); - const [admin, setAdmin] = useState(0); - const [own_work, setOwnWork] = useState(0); - const [study, setStudy] = useState(0); - const [testing, setTesting] = useState(0); - const total = development + meeting + admin + own_work + study + testing; - - const token = localStorage.getItem("accessToken") ?? ""; - const { projectName } = useParams(); - const { username } = useParams(); - - const fetchTimePerActivity = async (): Promise => { - const response = await api.getStatistics( - projectName ?? "", - token, - username ?? "", - ); - { - if (response.success) { - const statistics: Statistics = response.data ?? { - totalDevelopmentTime: 0, - totalMeetingTime: 0, - totalAdminTime: 0, - totalOwnWorkTime: 0, - totalStudyTime: 0, - totalTestingTime: 0, - }; - setDevelopment(statistics.totalDevelopmentTime); - setMeeting(statistics.totalMeetingTime); - setAdmin(statistics.totalAdminTime); - setOwnWork(statistics.totalOwnWorkTime); - setStudy(statistics.totalStudyTime); - setTesting(statistics.totalTestingTime); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - } - }; - - useEffect(() => { - void fetchTimePerActivity(); - }); - - return ( - <> -

    - Total Time In: {projectName}{" "} -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Activity - Total Time (min) -
    Development - -
    Meeting - -
    Administration - -
    Own Work - -
    Studies - -
    Testing - -
    In Total: -

    {total}

    -
    -
    -
    - - ); -} diff --git a/frontend/src/Components/ViewOtherTimeReport.tsx b/frontend/src/Components/ViewOtherTimeReport.tsx deleted file mode 100644 index 3689854..0000000 --- a/frontend/src/Components/ViewOtherTimeReport.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import { useState, useEffect } from "react"; -import { WeeklyReport } from "../Types/goTypes"; -import { api } from "../API/API"; -import { useNavigate, useParams } from "react-router-dom"; -import Button from "./Button"; - -/** - * Renders the component for editing a weekly report. - * @returns JSX.Element - */ - -//This component does not yet work as intended. It is supposed to display the weekly report of a user in a project. -export default function GetOtherUsersReport(): JSX.Element { - const [week, setWeek] = useState(0); - const [developmentTime, setDevelopmentTime] = useState(0); - const [meetingTime, setMeetingTime] = useState(0); - const [adminTime, setAdminTime] = useState(0); - const [ownWorkTime, setOwnWorkTime] = useState(0); - const [studyTime, setStudyTime] = useState(0); - const [testingTime, setTestingTime] = useState(0); - const [reportId, setReportId] = useState(0); - - const token = localStorage.getItem("accessToken") ?? ""; - const { projectName } = useParams(); - const { username } = useParams(); - const { fetchedWeek } = useParams(); - - useEffect(() => { - const fetchUsersWeeklyReport = async (): Promise => { - const response = await api.getWeeklyReport( - projectName ?? "", - fetchedWeek?.toString() ?? "0", - token, - username ?? "", - ); - console.log(response); - if (response.success) { - const report: WeeklyReport = response.data ?? { - reportId: 0, - userId: 0, - projectId: 0, - week: 0, - developmentTime: 0, - meetingTime: 0, - adminTime: 0, - ownWorkTime: 0, - studyTime: 0, - testingTime: 0, - }; - setReportId(report.reportId); - setWeek(report.week); - setDevelopmentTime(report.developmentTime); - setMeetingTime(report.meetingTime); - setAdminTime(report.adminTime); - setOwnWorkTime(report.ownWorkTime); - setStudyTime(report.studyTime); - setTestingTime(report.testingTime); - } else { - console.error("Failed to fetch weekly report:", response.message); - } - }; - - void fetchUsersWeeklyReport(); - }); - - const handleSignWeeklyReport = async (): Promise => { - const response = await api.signReport(reportId, token); - if (response.success) { - return true; - } else { - return false; - } - }; - - const navigate = useNavigate(); - - return ( - <> -

    {username}'s Report

    -
    -
    { - e.preventDefault(); - void (async (): Promise => { - const success = await handleSignWeeklyReport(); - if (!success) { - alert("Failed to sign report!"); - return; - } - alert("Report successfully signed!"); - navigate(-1); - })(); - }} - > -
    -
    -

    Week: {week}

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Activity - - Total Time (min) -
    Development - -
    Meeting - -
    Administration - -
    Own Work - -
    Studies - -
    Testing - -
    -
    - -
    - - ); -} diff --git a/frontend/src/Components/__tests__/CountButton.test.ts b/frontend/src/Components/__tests__/CountButton.test.ts deleted file mode 100644 index ecd577b..0000000 --- a/frontend/src/Components/__tests__/CountButton.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { describe, expect, test } from "@jest/globals"; - -function sum(a: number, b: number): number { - return a + b; -} - -// This is obviously not testing the proper component -describe("sum module", () => { - test("adds 1 + 2 to equal 3", () => { - expect(sum(1, 1)).toBe(2); - }); -}); diff --git a/frontend/src/Containers/ContainerDemo.tsx b/frontend/src/Containers/ContainerDemo.tsx deleted file mode 100644 index ac1cfc6..0000000 --- a/frontend/src/Containers/ContainerDemo.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ReactNode } from "react"; - -/** The props for the container */ -interface ContainerProps { - children: ReactNode; -} - -/** - * Contains children - * @param props - The children to contain - * @returns {JSX.Element} The container - */ -export function Container(props: ContainerProps): JSX.Element { - return
    {props.children}
    ; -} - -/** - * Contains even more children - * @param props - * @returns {JSX.Element} - */ -export function Container2(props: ContainerProps): JSX.Element { - return {props.children}; -} diff --git a/frontend/src/Containers/GenApiDemo.tsx b/frontend/src/Containers/GenApiDemo.tsx deleted file mode 100644 index 27092d8..0000000 --- a/frontend/src/Containers/GenApiDemo.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect } from "react"; -import { GenApi } from "../API/GenApi"; - -// Instantiation of the API -const api = new GenApi(); - -export function GenApiDemo(): JSX.Element { - // Sync wrapper around the loginCreate method - const register = async (): Promise => { - const response = await api.login.loginCreate({ - username: "admin", - password: "admin", - }); - console.log(response.data); // This should be the inner type of the response - }; - - // Call the wrapper - useEffect(() => { - void register(); - }); - - return <>; -} diff --git a/frontend/src/Data/constants.ts b/frontend/src/Data/constants.ts deleted file mode 100644 index c803ad4..0000000 --- a/frontend/src/Data/constants.ts +++ /dev/null @@ -1,36 +0,0 @@ -//Different character limits certain strings - -/** - * Allowed character length for password - */ -export const passwordLength = 6; - -/** - * Lower limit for username length - */ -export const usernameLowLimit = 5; - -/** - * Upper limit for password length - */ -export const usernameUpLimit = 10; - -/** - * Lower limit for project name length - */ -export const projNameLowLimit = 10; - -/** - * Upper limit for project name length - */ -export const projNameHighLimit = 99; - -/** - * Upper limit for project description length - */ -export const projDescLowLimit = 0; - -/** - * Upper limit for project description length - */ -export const projDescHighLimit = 99; diff --git a/frontend/src/Data/regex.ts b/frontend/src/Data/regex.ts deleted file mode 100644 index ceb22cd..0000000 --- a/frontend/src/Data/regex.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Only alphanumerical characters - */ -export const alphanumeric = /^[a-zA-Z0-9]+$/; - -/** - * Only lowercase letters - */ -export const lowercase = /^[a-z]+$/; diff --git a/frontend/src/Pages/AdminPages/AdminAddProject.tsx b/frontend/src/Pages/AdminPages/AdminAddProject.tsx deleted file mode 100644 index aedbd3f..0000000 --- a/frontend/src/Pages/AdminPages/AdminAddProject.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import AddProject from "../../Components/AddProject"; -import BackButton from "../../Components/BackButton"; -import BasicWindow from "../../Components/BasicWindow"; - -function AdminAddProject(): JSX.Element { - const content = ; - - const buttons = ( - <> - - - ); - - return ; -} -export default AdminAddProject; diff --git a/frontend/src/Pages/AdminPages/AdminAddUser.tsx b/frontend/src/Pages/AdminPages/AdminAddUser.tsx deleted file mode 100644 index 4af2eb7..0000000 --- a/frontend/src/Pages/AdminPages/AdminAddUser.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import BackButton from "../../Components/BackButton"; -import BasicWindow from "../../Components/BasicWindow"; -import Register from "../../Components/Register"; - -function AdminAddUser(): JSX.Element { - const content = ( - <> - - - ); - - const buttons = ( - <> - - - ); - - return ; -} -export default AdminAddUser; diff --git a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx b/frontend/src/Pages/AdminPages/AdminManageProjects.tsx deleted file mode 100644 index 296dc59..0000000 --- a/frontend/src/Pages/AdminPages/AdminManageProjects.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Link } from "react-router-dom"; -import BasicWindow from "../../Components/BasicWindow"; -import Button from "../../Components/Button"; -import { ProjectListAdmin } from "../../Components/ProjectListAdmin"; -import { Project } from "../../Types/goTypes"; -import GetProjects from "../../Components/GetProjects"; -import { useState } from "react"; -import NavButton from "../../Components/NavButton"; - -function AdminManageProjects(): JSX.Element { - const [projects, setProjects] = useState([]); - GetProjects({ - setProjectsProp: setProjects, - username: localStorage.getItem("username") ?? "", - }); - const content = ; - - const buttons = ( - <> - -