From ddbf96850f699e83583af1e551f2469c69ba8026 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 6 Dec 2024 17:21:43 -0300 Subject: [PATCH 01/33] =?UTF-8?q?=F0=9F=8E=89started=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/.gitingore | 113 ++++++++ backend/.idea/backend.iml | 9 + backend/.idea/misc.xml | 6 + backend/.idea/modules.xml | 8 + backend/.idea/vcs.xml | 6 + backend/desafiovotacao/.gitattributes | 2 + backend/desafiovotacao/.gitignore | 33 +++ .../.mvn/wrapper/maven-wrapper.properties | 19 ++ backend/desafiovotacao/mvnw | 259 ++++++++++++++++++ backend/desafiovotacao/mvnw.cmd | 149 ++++++++++ backend/desafiovotacao/pom.xml | 54 ++++ .../DesafiovotacaoApplication.java | 24 ++ .../src/main/resources/application.properties | 1 + .../DesafiovotacaoApplicationTests.java | 13 + 14 files changed, 696 insertions(+) create mode 100644 backend/.gitingore create mode 100644 backend/.idea/backend.iml create mode 100644 backend/.idea/misc.xml create mode 100644 backend/.idea/modules.xml create mode 100644 backend/.idea/vcs.xml create mode 100644 backend/desafiovotacao/.gitattributes create mode 100644 backend/desafiovotacao/.gitignore create mode 100644 backend/desafiovotacao/.mvn/wrapper/maven-wrapper.properties create mode 100644 backend/desafiovotacao/mvnw create mode 100644 backend/desafiovotacao/mvnw.cmd create mode 100644 backend/desafiovotacao/pom.xml create mode 100644 backend/desafiovotacao/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java create mode 100644 backend/desafiovotacao/src/main/resources/application.properties create mode 100644 backend/desafiovotacao/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java diff --git a/backend/.gitingore b/backend/.gitingore new file mode 100644 index 0000000..75e0631 --- /dev/null +++ b/backend/.gitingore @@ -0,0 +1,113 @@ +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* \ No newline at end of file diff --git a/backend/.idea/backend.iml b/backend/.idea/backend.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/backend/.idea/backend.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/backend/.idea/misc.xml b/backend/.idea/misc.xml new file mode 100644 index 0000000..6f29fee --- /dev/null +++ b/backend/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/backend/.idea/modules.xml b/backend/.idea/modules.xml new file mode 100644 index 0000000..e066844 --- /dev/null +++ b/backend/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/backend/.idea/vcs.xml b/backend/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/backend/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/backend/desafiovotacao/.gitattributes b/backend/desafiovotacao/.gitattributes new file mode 100644 index 0000000..3b41682 --- /dev/null +++ b/backend/desafiovotacao/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/backend/desafiovotacao/.gitignore b/backend/desafiovotacao/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/backend/desafiovotacao/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/backend/desafiovotacao/.mvn/wrapper/maven-wrapper.properties b/backend/desafiovotacao/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..d58dfb7 --- /dev/null +++ b/backend/desafiovotacao/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/backend/desafiovotacao/mvnw b/backend/desafiovotacao/mvnw new file mode 100644 index 0000000..19529dd --- /dev/null +++ b/backend/desafiovotacao/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/backend/desafiovotacao/mvnw.cmd b/backend/desafiovotacao/mvnw.cmd new file mode 100644 index 0000000..249bdf3 --- /dev/null +++ b/backend/desafiovotacao/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/backend/desafiovotacao/pom.xml b/backend/desafiovotacao/pom.xml new file mode 100644 index 0000000..62ff61c --- /dev/null +++ b/backend/desafiovotacao/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.0 + + + com.votacao + desafiovotacao + 0.0.1-SNAPSHOT + desafiovotacao + desafiovotacao + + + + + + + + + + + + + + + 23 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/backend/desafiovotacao/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java b/backend/desafiovotacao/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java new file mode 100644 index 0000000..62a8747 --- /dev/null +++ b/backend/desafiovotacao/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java @@ -0,0 +1,24 @@ +package com.votacao.desafiovotacao; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@SpringBootApplication +@RestController +public class DesafiovotacaoApplication { + + public static void main(String[] args) { + SpringApplication.run(DesafiovotacaoApplication.class, args); + } + + @GetMapping("/") + public String sayHello(@RequestParam(value = "myName", defaultValue = "World") String name) { + return String.format("Hello %s!", name); + } + +} + + diff --git a/backend/desafiovotacao/src/main/resources/application.properties b/backend/desafiovotacao/src/main/resources/application.properties new file mode 100644 index 0000000..72cf091 --- /dev/null +++ b/backend/desafiovotacao/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=desafiovotacao diff --git a/backend/desafiovotacao/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java b/backend/desafiovotacao/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java new file mode 100644 index 0000000..733c6e8 --- /dev/null +++ b/backend/desafiovotacao/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java @@ -0,0 +1,13 @@ +package com.votacao.desafiovotacao; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DesafiovotacaoApplicationTests { + + @Test + void contextLoads() { + } + +} From de52f7b14509bd49ad91825170c97987184c1612 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sat, 7 Dec 2024 15:48:00 -0300 Subject: [PATCH 02/33] =?UTF-8?q?=E2=9C=A8feature/=20created=20entities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/{desafiovotacao => }/.gitattributes | 0 ...ackend.iml => desafio-votacao-backend.iml} | 4 ++- backend/.idea/misc.xml | 10 +++++- backend/.idea/modules.xml | 2 +- .../.mvn/wrapper/maven-wrapper.properties | 0 backend/desafiovotacao.iml | 8 +++++ backend/desafiovotacao/.gitignore | 33 ------------------- .../src/main/resources/application.properties | 1 - backend/{desafiovotacao => }/mvnw | 0 backend/{desafiovotacao => }/mvnw.cmd | 0 backend/{desafiovotacao => }/pom.xml | 24 ++++++++++++++ .../DesafiovotacaoApplication.java | 0 .../domain/entities/Agenda.java | 21 ++++++++++++ .../domain/entities/Associated.java | 23 +++++++++++++ .../domain/entities/Session.java | 31 +++++++++++++++++ .../desafiovotacao/domain/entities/Vote.java | 24 ++++++++++++++ .../src/main/resources/application.properties | 2 ++ .../DesafiovotacaoApplicationTests.java | 0 18 files changed, 146 insertions(+), 37 deletions(-) rename backend/{desafiovotacao => }/.gitattributes (100%) rename backend/.idea/{backend.iml => desafio-votacao-backend.iml} (72%) rename backend/{desafiovotacao => }/.mvn/wrapper/maven-wrapper.properties (100%) create mode 100644 backend/desafiovotacao.iml delete mode 100644 backend/desafiovotacao/.gitignore delete mode 100644 backend/desafiovotacao/src/main/resources/application.properties rename backend/{desafiovotacao => }/mvnw (100%) rename backend/{desafiovotacao => }/mvnw.cmd (100%) rename backend/{desafiovotacao => }/pom.xml (69%) rename backend/{desafiovotacao => }/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java (100%) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Agenda.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Session.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java create mode 100644 backend/src/main/resources/application.properties rename backend/{desafiovotacao => }/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java (100%) diff --git a/backend/desafiovotacao/.gitattributes b/backend/.gitattributes similarity index 100% rename from backend/desafiovotacao/.gitattributes rename to backend/.gitattributes diff --git a/backend/.idea/backend.iml b/backend/.idea/desafio-votacao-backend.iml similarity index 72% rename from backend/.idea/backend.iml rename to backend/.idea/desafio-votacao-backend.iml index d6ebd48..478db44 100644 --- a/backend/.idea/backend.iml +++ b/backend/.idea/desafio-votacao-backend.iml @@ -2,7 +2,9 @@ - + + + diff --git a/backend/.idea/misc.xml b/backend/.idea/misc.xml index 6f29fee..001e756 100644 --- a/backend/.idea/misc.xml +++ b/backend/.idea/misc.xml @@ -1,6 +1,14 @@ - + + + + + \ No newline at end of file diff --git a/backend/.idea/modules.xml b/backend/.idea/modules.xml index e066844..ad0f3b2 100644 --- a/backend/.idea/modules.xml +++ b/backend/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/backend/desafiovotacao/.mvn/wrapper/maven-wrapper.properties b/backend/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from backend/desafiovotacao/.mvn/wrapper/maven-wrapper.properties rename to backend/.mvn/wrapper/maven-wrapper.properties diff --git a/backend/desafiovotacao.iml b/backend/desafiovotacao.iml new file mode 100644 index 0000000..517d60b --- /dev/null +++ b/backend/desafiovotacao.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/backend/desafiovotacao/.gitignore b/backend/desafiovotacao/.gitignore deleted file mode 100644 index 549e00a..0000000 --- a/backend/desafiovotacao/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/backend/desafiovotacao/src/main/resources/application.properties b/backend/desafiovotacao/src/main/resources/application.properties deleted file mode 100644 index 72cf091..0000000 --- a/backend/desafiovotacao/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=desafiovotacao diff --git a/backend/desafiovotacao/mvnw b/backend/mvnw similarity index 100% rename from backend/desafiovotacao/mvnw rename to backend/mvnw diff --git a/backend/desafiovotacao/mvnw.cmd b/backend/mvnw.cmd similarity index 100% rename from backend/desafiovotacao/mvnw.cmd rename to backend/mvnw.cmd diff --git a/backend/desafiovotacao/pom.xml b/backend/pom.xml similarity index 69% rename from backend/desafiovotacao/pom.xml rename to backend/pom.xml index 62ff61c..4028909 100644 --- a/backend/desafiovotacao/pom.xml +++ b/backend/pom.xml @@ -40,6 +40,30 @@ spring-boot-starter-test test + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.projectlombok + lombok + 1.18.36 + provided + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.data + spring-data-mongodb + 4.4.0 + + diff --git a/backend/desafiovotacao/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java similarity index 100% rename from backend/desafiovotacao/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java rename to backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Agenda.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Agenda.java new file mode 100644 index 0000000..70ba1dc --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Agenda.java @@ -0,0 +1,21 @@ +package com.votacao.desafiovotacao.domain.entities; + +import lombok.Getter; +import lombok.Setter; +import lombok.Builder; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + + +@Setter +@Getter +@Builder +@Document +public class Agenda { + @Id + private String id; + private String title; + private String description; + private String status; +} + diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java new file mode 100644 index 0000000..51be449 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java @@ -0,0 +1,23 @@ +package com.votacao.desafiovotacao.domain.entities; + +import lombok.Getter; +import lombok.Setter; +import lombok.Builder; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Getter +@Setter +@Builder +@Document +public class Associated { + @Id + private String id; + private String cpf; + private String name; + private Associated ableToVote; + + public enum ableToVote { + ABLE_TO_VOTE, UNABLE_TO_VOTE + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Session.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Session.java new file mode 100644 index 0000000..4f716ec --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Session.java @@ -0,0 +1,31 @@ +package com.votacao.desafiovotacao.domain.entities; + +import lombok.Getter; +import lombok.Setter; +import lombok.Builder; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.time.LocalDateTime; + + +@Getter +@Setter +@Builder +@Document +public class Session { + @Id + private String id; + private String agendaId; + private LocalDateTime startTime; + private LocalDateTime closedTime; + private StatusSession status; + + public enum StatusSession { + ABERTA, ENCERRADA + } +} + + + + diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java new file mode 100644 index 0000000..d30fb7f --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java @@ -0,0 +1,24 @@ +package com.votacao.desafiovotacao.domain.entities; + +import lombok.Getter; +import lombok.Setter; +import lombok.Builder; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + + +@Getter +@Setter +@Builder +@Document +public class Vote { + @Id + private String id; + private Session session; + private String associatedId; + private voteType voto; + + public enum voteType { + SIM, NAO + } +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties new file mode 100644 index 0000000..298b38c --- /dev/null +++ b/backend/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.application.name=desafiovotacao +spring.data.mongodb.uri=mongodb://localhost:27017/voting diff --git a/backend/desafiovotacao/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java b/backend/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java similarity index 100% rename from backend/desafiovotacao/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java rename to backend/src/test/java/com/votacao/desafiovotacao/DesafiovotacaoApplicationTests.java From c1fe4ae16168f6a21bf40c0ff2efb67fe790c100 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sat, 7 Dec 2024 16:11:30 -0300 Subject: [PATCH 03/33] =?UTF-8?q?=E2=9C=A8feature/=20created=20repositorie?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../desafiovotacao/DesafiovotacaoApplication.java | 8 +++----- .../desafiovotacao/infra/AgendaRepository.java | 7 +++++++ .../infra/AssociatedRepository.java | 4 ++++ .../desafiovotacao/infra/SessionRepository.java | 11 +++++++++++ .../desafiovotacao/infra/VoteRepository.java | 15 +++++++++++++++ backend/src/main/resources/application.properties | 1 + 6 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java index 62a8747..893d23b 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java @@ -2,11 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -@SpringBootApplication +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @RestController public class DesafiovotacaoApplication { @@ -18,7 +19,4 @@ public static void main(String[] args) { public String sayHello(@RequestParam(value = "myName", defaultValue = "World") String name) { return String.format("Hello %s!", name); } - -} - - +} \ No newline at end of file diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java new file mode 100644 index 0000000..f2318d0 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.infra; + +import com.votacao.desafiovotacao.domain.entities.Agenda; +import org.springframework.data.mongodb.repository.MongoRepository; + +public interface AgendaRepository extends MongoRepository { +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java new file mode 100644 index 0000000..0842bc5 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java @@ -0,0 +1,4 @@ +package com.votacao.desafiovotacao.infra; + +public class AssociatedRepository { +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java new file mode 100644 index 0000000..3f6a0d3 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java @@ -0,0 +1,11 @@ +package com.votacao.desafiovotacao.infra; + +import com.votacao.desafiovotacao.domain.entities.Session; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; + +public interface SessionRepository extends MongoRepository { + // Consult open sessions + List findByStatus(Session.StatusSession status); +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java new file mode 100644 index 0000000..a96f2b3 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java @@ -0,0 +1,15 @@ +package com.votacao.desafiovotacao.infra; + +import com.votacao.desafiovotacao.domain.entities.Vote; +import org.springframework.data.mongodb.repository.MongoRepository; + +import java.util.List; +import java.util.Optional; + +public interface VoteRepository extends MongoRepository { + // Search all votes from a session + List findBySessionId(String sessaoId); + + // Search vote by session and associated + Optional findBySessionIdAndAssociatedId(String sessionId, String associatedId); +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 298b38c..b7f398a 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -1,2 +1,3 @@ spring.application.name=desafiovotacao +server.port=8080 spring.data.mongodb.uri=mongodb://localhost:27017/voting From 0e85599f05c4474b535f6f2f406a8dd21b97b694 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sat, 7 Dec 2024 23:00:25 -0300 Subject: [PATCH 04/33] =?UTF-8?q?=F0=9F=9A=A7=20stared=20services=20and=20?= =?UTF-8?q?created=20voteDTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactored to adjust vote entity and vote repository --- .../application/dtos/VoteDTO.java | 13 +++ .../desafiovotacao/domain/entities/Vote.java | 8 +- .../exceptions/AgendaNotFoundException.java | 8 ++ .../exceptions/SessionTimeException.java | 7 ++ .../domain/services/AgendaService.java | 27 ++++++ .../domain/services/SessionService.java | 67 ++++++++++++++ .../domain/services/VoteService.java | 89 +++++++++++++++++++ .../desafiovotacao/infra/VoteRepository.java | 6 +- 8 files changed, 215 insertions(+), 10 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/SessionTimeException.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java new file mode 100644 index 0000000..2fd6716 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java @@ -0,0 +1,13 @@ +package com.votacao.desafiovotacao.application.dtos; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class VoteDTO { + private String votesYes; + private String votesNo; +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java index d30fb7f..ee6f37a 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Vote.java @@ -14,11 +14,7 @@ public class Vote { @Id private String id; - private Session session; + private String vote; private String associatedId; - private voteType voto; - - public enum voteType { - SIM, NAO - } + private Session session; } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java new file mode 100644 index 0000000..2e8662a --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java @@ -0,0 +1,8 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class AgendaNotFoundException extends Exception { + + public AgendaNotFoundException() { + super("Agenda couldn't be found"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/SessionTimeException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/SessionTimeException.java new file mode 100644 index 0000000..34ec8d7 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/SessionTimeException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class SessionTimeException extends Exception { + public SessionTimeException() { + super("Session time isn't correct"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java new file mode 100644 index 0000000..d9d1895 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java @@ -0,0 +1,27 @@ +package com.votacao.desafiovotacao.domain.services; + +import com.votacao.desafiovotacao.domain.entities.Agenda; +import com.votacao.desafiovotacao.infra.AgendaRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class AgendaService { + + @Autowired + private AgendaRepository agendaRepository; + + public List findAll() { + return agendaRepository.findAll(); + } + + public Agenda findById(String id) { + return agendaRepository.findById(id).orElse(null); + } + + public Agenda save(Agenda agenda) { + return agendaRepository.save(agenda); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java new file mode 100644 index 0000000..9ce51b5 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java @@ -0,0 +1,67 @@ +package com.votacao.desafiovotacao.domain.services; + +import com.votacao.desafiovotacao.domain.entities.Agenda; +import com.votacao.desafiovotacao.domain.entities.Session; +import com.votacao.desafiovotacao.domain.exceptions.AgendaNotFoundException; +import com.votacao.desafiovotacao.domain.exceptions.SessionTimeException; +import com.votacao.desafiovotacao.infra.SessionRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; + +import java.time.LocalDateTime; +import java.util.Optional; + +@Service +public class SessionService { + + private static final Long TIME_DEFAULT = 1L; + + @Autowired + SessionRepository sessionRepository; + + public Optional save(Session session) { + return Optional.ofNullable(sessionRepository.save(session)); + } + + public Optional get(String sessionId) { + return sessionRepository.findById(sessionId); + } + + + public ResponseEntity startSession(Long minutesOpened, Agenda agenda) { + if (minutesOpened == null) { + minutesOpened = TIME_DEFAULT; + } + + Session session = Session.builder() + .agendaId(agenda.getId()) + .startTime(LocalDateTime.now()) + .closedTime(LocalDateTime.now().plusMinutes(minutesOpened)) + .status(Session.StatusSession.ABERTA) + .build(); + + Optional optionalSessionEntity = save(session); + + if (optionalSessionEntity.isPresent()) { + return new ResponseEntity<>(optionalSessionEntity.get(), HttpStatus.CREATED); + + } + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + public void validatePostSessionRequest(Optional optionalAgenda, Long minutesOpened) + throws AgendaNotFoundException, SessionTimeException { + + if (!optionalAgenda.isPresent()) { + throw new AgendaNotFoundException(); + } + + if (ObjectUtils.isEmpty(minutesOpened) || minutesOpened < TIME_DEFAULT) { + throw new SessionTimeException(); + + } + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java new file mode 100644 index 0000000..900632a --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java @@ -0,0 +1,89 @@ +package com.votacao.desafiovotacao.domain.services; + +import com.votacao.desafiovotacao.domain.entities.Session; +import com.votacao.desafiovotacao.domain.entities.Vote; +import com.votacao.desafiovotacao.infra.VoteRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import com.votacao.desafiovotacao.application.dtos.VoteDTO; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +public class VoteService { + + private static final String VOTE_YES = "SIM"; + + @Autowired + private VoteRepository voteRepository; + + public Optional save(Vote vote) { + return Optional.ofNullable(voteRepository.save(vote)); + } + + public List findAll() { + return voteRepository.findAll(); + } + + public ResponseEntity getVotes(String sessionId) { + List selectedVotes = selectVotesPerSession(sessionId); + Long totalVotesYes = 0L; + Long totalVotesNo = 0L; + + for (Vote vote : selectedVotes) { + if (vote.getVote().equals(VOTE_YES)) { + totalVotesYes++; + + } else { + totalVotesNo++; + + } + } + VoteDTO voteDTO = VoteDTO.builder() + .votesYes(String.valueOf(totalVotesYes)) + .votesNo(String.valueOf(totalVotesNo)) + .build(); + + return new ResponseEntity<>(voteDTO, HttpStatus.OK); + } + + public List selectVotesPerSession(String sessionId) { + List listVote = findAll(); + + return listVote + .stream() + .filter(e -> e.getSession().getId().equals(sessionId)) + .collect(Collectors.toList()); + } + + public ResponseEntity voteSession(String associatedVote, String associatedId, Session session) { + Vote vote = Vote.builder() + .vote(associatedVoteHandler(associatedVote)) + .associatedId(associatedId) + .session(session) + .build(); + + Optional optionalVoteEntity = save(vote); + + return optionalVoteEntity.map(value -> new ResponseEntity<>(value, HttpStatus.CREATED)) + .orElseGet(() -> new ResponseEntity<>(HttpStatus.BAD_REQUEST)); + } + + private String associatedVoteHandler(String associatedVote) { + return associatedVote.toUpperCase(); + } + + public Boolean validateAssociateAlreadyVote(Session session, String associatedId) { + List listVoteEntity = voteRepository.findAllBySession(session); + + return listVoteEntity + .stream() + .anyMatch(entity -> + entity.getAssociatedId().equals(associatedId) + ); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java index a96f2b3..3439398 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java @@ -1,5 +1,6 @@ package com.votacao.desafiovotacao.infra; +import com.votacao.desafiovotacao.domain.entities.Session; import com.votacao.desafiovotacao.domain.entities.Vote; import org.springframework.data.mongodb.repository.MongoRepository; @@ -8,8 +9,5 @@ public interface VoteRepository extends MongoRepository { // Search all votes from a session - List findBySessionId(String sessaoId); - - // Search vote by session and associated - Optional findBySessionIdAndAssociatedId(String sessionId, String associatedId); + List findAllBySession(Session session); } From 48e662c8bb7d9d0e0d603d402e1e8bceff9b2671 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Mon, 9 Dec 2024 15:28:30 -0300 Subject: [PATCH 05/33] =?UTF-8?q?=F0=9F=9A=A7=20refactored=20DTOs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../desafiovotacao/application/dtos/AgendaDTO.java | 5 +++++ .../desafiovotacao/application/dtos/AssociatedDTO.java | 5 +++++ .../desafiovotacao/application/dtos/SessionDTO.java | 5 +++++ .../desafiovotacao/application/dtos/VoteDTO.java | 10 +--------- 4 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AgendaDTO.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AssociatedDTO.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/dtos/SessionDTO.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AgendaDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AgendaDTO.java new file mode 100644 index 0000000..f1e280c --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AgendaDTO.java @@ -0,0 +1,5 @@ +package com.votacao.desafiovotacao.application.dtos; + + +public record AgendaDTO (String id, String title, String description, String status) { +} \ No newline at end of file diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AssociatedDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AssociatedDTO.java new file mode 100644 index 0000000..50ce5d2 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/AssociatedDTO.java @@ -0,0 +1,5 @@ +package com.votacao.desafiovotacao.application.dtos; + + +public record AssociatedDTO (String id, String name, String cpf) { +} \ No newline at end of file diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/SessionDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/SessionDTO.java new file mode 100644 index 0000000..86f6d7f --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/SessionDTO.java @@ -0,0 +1,5 @@ +package com.votacao.desafiovotacao.application.dtos; + + +public record SessionDTO (String id, String agendaId, String duration) { +} \ No newline at end of file diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java index 2fd6716..526aeb7 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java @@ -1,13 +1,5 @@ package com.votacao.desafiovotacao.application.dtos; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; -@Getter -@Setter -@Builder -public class VoteDTO { - private String votesYes; - private String votesNo; +public record VoteDTO (String votesYes, String votesNo) { } From f3b1ff4148665842b8db33e5d63b802f3bd3e1c1 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Mon, 9 Dec 2024 22:49:41 -0300 Subject: [PATCH 06/33] =?UTF-8?q?=F0=9F=9A=A7=20refactored=20voteDTO=20and?= =?UTF-8?q?=20created=20VoteResponseDTO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/votacao/desafiovotacao/application/dtos/VoteDTO.java | 2 +- .../desafiovotacao/application/dtos/VoteResponseDTO.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteResponseDTO.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java index 526aeb7..99d7646 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteDTO.java @@ -1,5 +1,5 @@ package com.votacao.desafiovotacao.application.dtos; -public record VoteDTO (String votesYes, String votesNo) { +public record VoteDTO (String agendaId, String sessionId, String associatedId, String voteId, String vote) { } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteResponseDTO.java b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteResponseDTO.java new file mode 100644 index 0000000..c23989c --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/dtos/VoteResponseDTO.java @@ -0,0 +1,4 @@ +package com.votacao.desafiovotacao.application.dtos; + +public record VoteResponseDTO(String voteYes, String voteNo, String totalVotes) { +} From 18265986755b0c82db53ed276dcc4a821079620c Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Mon, 9 Dec 2024 22:50:15 -0300 Subject: [PATCH 07/33] =?UTF-8?q?=F0=9F=9A=A7=20fixed=20the=20repositories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../votacao/desafiovotacao/infra/AssociatedRepository.java | 7 ++++++- .../votacao/desafiovotacao/infra/SessionRepository.java | 2 ++ .../com/votacao/desafiovotacao/infra/VoteRepository.java | 4 +++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java index 0842bc5..0786fb1 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java @@ -1,4 +1,9 @@ package com.votacao.desafiovotacao.infra; -public class AssociatedRepository { +import com.votacao.desafiovotacao.domain.entities.Associated; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface AssociatedRepository extends MongoRepository { } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java index 3f6a0d3..4ff344d 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/SessionRepository.java @@ -2,9 +2,11 @@ import com.votacao.desafiovotacao.domain.entities.Session; import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; import java.util.List; +@Repository public interface SessionRepository extends MongoRepository { // Consult open sessions List findByStatus(Session.StatusSession status); diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java index 3439398..b4d07bb 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java @@ -3,11 +3,13 @@ import com.votacao.desafiovotacao.domain.entities.Session; import com.votacao.desafiovotacao.domain.entities.Vote; import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; import java.util.List; import java.util.Optional; +@Repository public interface VoteRepository extends MongoRepository { // Search all votes from a session - List findAllBySession(Session session); + List findAllBySession(Optional session); } From 8a51f34a1fb20be67b228cd0627923645ef955e7 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Mon, 9 Dec 2024 22:57:00 -0300 Subject: [PATCH 08/33] =?UTF-8?q?=F0=9F=9A=A7=20fixed=20the=20agenda=20rep?= =?UTF-8?q?ository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/votacao/desafiovotacao/infra/AgendaRepository.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java index f2318d0..10d30f4 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/AgendaRepository.java @@ -2,6 +2,8 @@ import com.votacao.desafiovotacao.domain.entities.Agenda; import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; +@Repository public interface AgendaRepository extends MongoRepository { } From 4f54ce94ad59756992f863a5247a68fc6bf15c40 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Mon, 9 Dec 2024 22:58:56 -0300 Subject: [PATCH 09/33] =?UTF-8?q?=F0=9F=9A=A7=20cleaned=20initial=20test?= =?UTF-8?q?=20from=20main=20springApplication=20run?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../desafiovotacao/DesafiovotacaoApplication.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java index 893d23b..12c048a 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java @@ -2,21 +2,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) -@RestController +@SpringBootApplication public class DesafiovotacaoApplication { public static void main(String[] args) { SpringApplication.run(DesafiovotacaoApplication.class, args); } - - @GetMapping("/") - public String sayHello(@RequestParam(value = "myName", defaultValue = "World") String name) { - return String.format("Hello %s!", name); - } } \ No newline at end of file From bd1d2bfeeff260b3d5922fc9505b594f48e10c25 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Mon, 9 Dec 2024 23:01:36 -0300 Subject: [PATCH 10/33] =?UTF-8?q?=F0=9F=9A=A7=20created=20the=20endpoints?= =?UTF-8?q?=20and=20adjusted=20their=20services?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/pom.xml | 15 ++- .../controllers/AgendaController.java | 65 +++++++++++++ .../controllers/AssociatedController.java | 57 ++++++++++++ .../controllers/SessionController.java | 68 ++++++++++++++ .../controllers/VoteController.java | 77 +++++++++++++++ .../domain/services/AgendaService.java | 36 ++++++- .../domain/services/AssociatedService.java | 55 +++++++++++ .../domain/services/SessionService.java | 87 +++++++++++++---- .../domain/services/VoteService.java | 93 ++++++++++++------- 9 files changed, 488 insertions(+), 65 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java diff --git a/backend/pom.xml b/backend/pom.xml index 4028909..6a6d96c 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -41,10 +41,10 @@ test - - org.springframework.boot - spring-boot-starter-data-jpa - + + + + org.projectlombok @@ -53,9 +53,14 @@ provided + + + + + org.springframework.boot - spring-boot-starter-data-jpa + spring-boot-starter-data-mongodb diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java new file mode 100644 index 0000000..743d2bf --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java @@ -0,0 +1,65 @@ +package com.votacao.desafiovotacao.application.controllers; + +import com.votacao.desafiovotacao.application.dtos.AgendaDTO; +import com.votacao.desafiovotacao.domain.entities.Agenda; +import com.votacao.desafiovotacao.domain.exceptions.AgendaNotValidException; +import com.votacao.desafiovotacao.domain.services.AgendaService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMessage; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/agenda") +public class AgendaController { + + @Autowired + private AgendaService agendaService; + + @PostMapping + @CrossOrigin("*") + public ResponseEntity createAgenda(@RequestBody AgendaDTO agendaDTO) throws AgendaNotValidException { + try { + return new ResponseEntity<>(agendaService.createAgenda(agendaDTO), HttpStatus.CREATED); + + // TODO: fix this exception handling + } catch (AgendaNotValidException e) { + e.printStackTrace(); + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } + + @GetMapping + @CrossOrigin("*") + public ResponseEntity findAll() { + return new ResponseEntity<>(agendaService.findAll(), HttpStatus.OK); + } + + @GetMapping("/{id}") + @CrossOrigin("*") + public ResponseEntity get(@PathVariable(value = "id") String id) { + return new ResponseEntity<>(agendaService.get(id), HttpStatus.OK); + } + + @PutMapping("/{id}") + @CrossOrigin("*") + public ResponseEntity update(@PathVariable(value = "id") String id, @RequestBody AgendaDTO agendaDTO) { + return new ResponseEntity<>(agendaService.update(id, agendaDTO), HttpStatus.OK); + } + + @DeleteMapping("/{id}") + @CrossOrigin("*") + public ResponseEntity delete(@PathVariable(value = "id") String id) { + agendaService.delete(id); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping + @CrossOrigin("*") + public ResponseEntity deleteAll() { + agendaService.deleteAll(); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java new file mode 100644 index 0000000..d2c1458 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java @@ -0,0 +1,57 @@ +package com.votacao.desafiovotacao.application.controllers; + +import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; +import com.votacao.desafiovotacao.domain.entities.Associated; +import com.votacao.desafiovotacao.domain.services.AssociatedService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/associated") +public class AssociatedController { + + @Autowired + private AssociatedService associatedService; + + @PostMapping("/create") + @CrossOrigin("*") + public ResponseEntity createAssociated(@RequestBody AssociatedDTO associatedDTO) { + return new ResponseEntity<>(associatedService.createAssociated(associatedDTO), HttpStatus.CREATED); + } + + @GetMapping("/{id}") + @CrossOrigin("*") + public ResponseEntity get(@PathVariable(value = "id") String id) { + return new ResponseEntity<>(associatedService.get(id), HttpStatus.OK); + } + + @GetMapping + @CrossOrigin("*") + public ResponseEntity findAll() { + return new ResponseEntity<>(associatedService.findAll(), HttpStatus.OK); + } + + @PutMapping("/{id}") + @CrossOrigin("*") + public ResponseEntity update(@PathVariable(value = "id") String id, @RequestBody AssociatedDTO associatedDTO) { + return new ResponseEntity<>(associatedService.update(id, associatedDTO), HttpStatus.OK); + } + + + @DeleteMapping("/{id}") + @CrossOrigin("*") + public ResponseEntity delete(@PathVariable(value = "id") String id) { + associatedService.delete(id); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping + @CrossOrigin("*") + public ResponseEntity deleteAll() { + associatedService.deleteAll(); + return new ResponseEntity<>(HttpStatus.OK); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java new file mode 100644 index 0000000..f616cf9 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java @@ -0,0 +1,68 @@ +package com.votacao.desafiovotacao.application.controllers; + +import com.votacao.desafiovotacao.application.dtos.SessionDTO; +import com.votacao.desafiovotacao.application.dtos.VoteResponseDTO; +import com.votacao.desafiovotacao.domain.entities.Session; +import com.votacao.desafiovotacao.domain.exceptions.AgendaNotFoundException; +import com.votacao.desafiovotacao.domain.exceptions.SessionTimeException; +import com.votacao.desafiovotacao.domain.services.SessionService; +import com.votacao.desafiovotacao.domain.services.VoteService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/session") +public class SessionController { + + @Autowired + private SessionService sessionService; + @Autowired + private VoteService voteService; + + @PostMapping("/create") + @CrossOrigin("*") + public ResponseEntity createSession(@RequestBody SessionDTO sessionDTO) + throws AgendaNotFoundException, SessionTimeException { + return new ResponseEntity<>(sessionService.createSession(sessionDTO), HttpStatus.CREATED); + } + + @GetMapping("/id") + @CrossOrigin("*") + public ResponseEntity get(@RequestParam(value = "id") String id) { + return new ResponseEntity<>(sessionService.get(id), HttpStatus.OK); + } + + @GetMapping + @CrossOrigin("*") + public ResponseEntity findAll() { + return new ResponseEntity<>(sessionService.findAll(), HttpStatus.OK); + } + + @PutMapping("/id") + @CrossOrigin("*") + public ResponseEntity update(@RequestParam(value = "id") String id, @RequestBody SessionDTO sessionDTO) { + return new ResponseEntity<>(sessionService.update(id, sessionDTO), HttpStatus.OK); + } + + @DeleteMapping("/id") + @CrossOrigin("*") + public ResponseEntity delete(@RequestParam(value = "id") String id) { + sessionService.delete(id); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping + @CrossOrigin("*") + public ResponseEntity deleteAll() { + sessionService.deleteAll(); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/result") + public ResponseEntity getVotesResult(@RequestParam(value = "sessionId") String sessionId) { + return voteService.getVotesResult(sessionId); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java new file mode 100644 index 0000000..c7886bc --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java @@ -0,0 +1,77 @@ +package com.votacao.desafiovotacao.application.controllers; + +import com.votacao.desafiovotacao.application.dtos.VoteDTO; +import com.votacao.desafiovotacao.domain.exceptions.AlreadyVotedException; +import com.votacao.desafiovotacao.domain.exceptions.NoSessionToVoteException; +import com.votacao.desafiovotacao.domain.services.AgendaService; +import com.votacao.desafiovotacao.domain.services.SessionService; +import com.votacao.desafiovotacao.domain.services.VoteService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/agenda/session/associated") +public class VoteController { + + @Autowired + private AgendaService agendaService; + + @Autowired + private SessionService sessionService; + + @Autowired + private VoteService voteService; + + @PostMapping("/vote") + @CrossOrigin("*") + public ResponseEntity registerVote(@RequestBody VoteDTO voteDTO) throws AlreadyVotedException, NoSessionToVoteException { + try { + return new ResponseEntity<>(voteService.registerVote(voteDTO), HttpStatus.OK); + } catch (AlreadyVotedException e) { + return new ResponseEntity<>("Associated already voted", HttpStatus.BAD_REQUEST); + } catch (NoSessionToVoteException e) { + return new ResponseEntity<>("Can't vote in without a session", HttpStatus.BAD_REQUEST); + } + } + + @GetMapping("/result") + @CrossOrigin("*") + public ResponseEntity getResult(@RequestParam (value = "sessionId") String sessionId) { + return new ResponseEntity<>(voteService.getVotesResult(sessionId) , HttpStatus.OK); + } + + @GetMapping("/vote") + @CrossOrigin("*") + public ResponseEntity getVote(@RequestParam (value = "voteId") String voteId) { + return new ResponseEntity<>(voteService.get(voteId), HttpStatus.OK); + } + + @GetMapping + @CrossOrigin("*") + public ResponseEntity getVotes() { + return new ResponseEntity<>(voteService.findAll(), HttpStatus.OK); + } + + @DeleteMapping("/vote/{id}") + @CrossOrigin("*") + public ResponseEntity deleteVote(@PathVariable (value = "id") String id) { + voteService.delete(id); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping("/votes") + @CrossOrigin("*") + public ResponseEntity deleteVotes() { + voteService.deleteAll(); + return new ResponseEntity<>(HttpStatus.OK); + } + + @GetMapping("/votes/result/{session_id}") + @CrossOrigin("*") + public ResponseEntity getVotesResult(@PathVariable (value = "session_id") String session_id) { + return new ResponseEntity<>(voteService.getVotesResult(session_id), HttpStatus.OK); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java index d9d1895..1dc155d 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java @@ -1,6 +1,8 @@ package com.votacao.desafiovotacao.domain.services; +import com.votacao.desafiovotacao.application.dtos.AgendaDTO; import com.votacao.desafiovotacao.domain.entities.Agenda; +import com.votacao.desafiovotacao.domain.exceptions.AgendaNotValidException; import com.votacao.desafiovotacao.infra.AgendaRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -13,15 +15,41 @@ public class AgendaService { @Autowired private AgendaRepository agendaRepository; - public List findAll() { - return agendaRepository.findAll(); + public Agenda createAgenda(AgendaDTO agendaDTO) throws AgendaNotValidException { + + if (agendaDTO.description().isEmpty()) throw new AgendaNotValidException(); + + Agenda agenda = Agenda.builder() + .id(agendaDTO.id()) + .title(agendaDTO.title()) + .description(agendaDTO.description()) + .status(agendaDTO.status()) + .build(); + return agendaRepository.save(agenda); } - public Agenda findById(String id) { + public Agenda get(String id) { return agendaRepository.findById(id).orElse(null); } - public Agenda save(Agenda agenda) { + public List findAll() { + return agendaRepository.findAll(); + } + + public Agenda update(String id, AgendaDTO agendaDTO) { + Agenda agenda = agendaRepository.findById(id).orElse(null); + if (agenda == null) return null; + agenda.setTitle(agendaDTO.title()); + agenda.setDescription(agendaDTO.description()); + agenda.setStatus(agendaDTO.status()); return agendaRepository.save(agenda); } + + public void delete(String id) { + agendaRepository.deleteById(id); + } + + public void deleteAll() { + agendaRepository.deleteAll(); + } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java new file mode 100644 index 0000000..3ceb422 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java @@ -0,0 +1,55 @@ +package com.votacao.desafiovotacao.domain.services; + +import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; +import com.votacao.desafiovotacao.domain.entities.Associated; +import com.votacao.desafiovotacao.infra.AssociatedRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class AssociatedService { + + @Autowired + private AssociatedRepository associatedRepository; + + public Associated createAssociated(AssociatedDTO associatedDTO) { + + // TODO: add tarefa bonus 1 + Associated associated = Associated.builder() + .id(associatedDTO.id()) + .cpf(associatedDTO.cpf()) + .name(associatedDTO.name()) + .build(); + return associatedRepository.save(associated); + } + + public Associated get(String id) { + return associatedRepository.findById(id).orElse(null); + } + + public List findAll() { + return associatedRepository.findAll(); + } + + public Associated update(String id, AssociatedDTO associatedDTO) { + Associated associated = associatedRepository.findById(id).orElse(null); + if (associated == null) return null; + associated.setCpf(associatedDTO.cpf()); + associated.setName(associatedDTO.name()); + return associatedRepository.save(associated); + } + + public void delete(String id) { + associatedRepository.deleteById(id); + } + + public void deleteAll() { + associatedRepository.deleteAll(); + } + + +} + + diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java index 9ce51b5..8b392be 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java @@ -1,19 +1,21 @@ package com.votacao.desafiovotacao.domain.services; +import com.votacao.desafiovotacao.application.dtos.SessionDTO; import com.votacao.desafiovotacao.domain.entities.Agenda; import com.votacao.desafiovotacao.domain.entities.Session; import com.votacao.desafiovotacao.domain.exceptions.AgendaNotFoundException; import com.votacao.desafiovotacao.domain.exceptions.SessionTimeException; +import com.votacao.desafiovotacao.infra.AgendaRepository; import com.votacao.desafiovotacao.infra.SessionRepository; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; + @Service public class SessionService { @@ -21,47 +23,92 @@ public class SessionService { @Autowired SessionRepository sessionRepository; + @Autowired + private AgendaRepository agendaRepository; - public Optional save(Session session) { - return Optional.ofNullable(sessionRepository.save(session)); - } + public Session createSession(SessionDTO sessionDTO) + throws AgendaNotFoundException, SessionTimeException { - public Optional get(String sessionId) { - return sessionRepository.findById(sessionId); - } + long duration = Long.parseLong(sessionDTO.duration()); + String agendaId = sessionDTO.agendaId(); - public ResponseEntity startSession(Long minutesOpened, Agenda agenda) { - if (minutesOpened == null) { - minutesOpened = TIME_DEFAULT; - } + // TODO: fix this + // Validate if the agenda exists and if the time is valid + validatePostSessionRequest(sessionDTO, duration); + // Create the session Session session = Session.builder() - .agendaId(agenda.getId()) + .id(sessionDTO.id()) + .agendaId(agendaId) .startTime(LocalDateTime.now()) - .closedTime(LocalDateTime.now().plusMinutes(minutesOpened)) + .closedTime(LocalDateTime.now().plusMinutes(duration)) .status(Session.StatusSession.ABERTA) .build(); + return sessionRepository.save(session); + } + + public Optional get(String sessionId) { - Optional optionalSessionEntity = save(session); + Session session = sessionRepository.findById(sessionId).get(); + + if (isSessionClosed(session)) { + closeSession(session); + } + return Optional.of(session); + } - if (optionalSessionEntity.isPresent()) { - return new ResponseEntity<>(optionalSessionEntity.get(), HttpStatus.CREATED); + public List findAll() { + List sessions = sessionRepository.findAll(); + for (Session s : sessions) { + if (isSessionClosed(s)) { + closeSession(s); + } } - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + return sessions; } - public void validatePostSessionRequest(Optional optionalAgenda, Long minutesOpened) + public Session update(String id, SessionDTO sessionDTO) { + Session session = sessionRepository.findById(id).orElse(null); + if (session == null) return null; + session.setAgendaId(sessionDTO.agendaId()); + session.setStartTime(LocalDateTime.now()); + session.setClosedTime(LocalDateTime.now().plusMinutes(Long.parseLong(sessionDTO.duration()))); + session.setStatus(Session.StatusSession.ABERTA); + return sessionRepository.save(session); + } + + public void delete(String sessionId) { + sessionRepository.deleteById(sessionId); + } + + public void deleteAll() { + sessionRepository.deleteAll(); + } + + public boolean isSessionClosed(Session session) { + return LocalDateTime.now().isAfter(session.getClosedTime()); + } + + public void closeSession(Session session) { + session.setStatus(Session.StatusSession.ENCERRADA); + sessionRepository.save(session); + } + + public void validatePostSessionRequest(SessionDTO sessionDTO, Long minutesOpened) throws AgendaNotFoundException, SessionTimeException { + // Validate if the agenda exists + Optional optionalAgenda = agendaRepository.findById(sessionDTO.agendaId()); + if (!optionalAgenda.isPresent()) { throw new AgendaNotFoundException(); } + // Validate if the time is valid if (ObjectUtils.isEmpty(minutesOpened) || minutesOpened < TIME_DEFAULT) { throw new SessionTimeException(); - } } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java index 900632a..8d98cdd 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java @@ -1,7 +1,10 @@ package com.votacao.desafiovotacao.domain.services; +import com.votacao.desafiovotacao.application.dtos.VoteResponseDTO; import com.votacao.desafiovotacao.domain.entities.Session; import com.votacao.desafiovotacao.domain.entities.Vote; +import com.votacao.desafiovotacao.domain.exceptions.AlreadyVotedException; +import com.votacao.desafiovotacao.domain.exceptions.NoSessionToVoteException; import com.votacao.desafiovotacao.infra.VoteRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -11,7 +14,7 @@ import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; + @Service public class VoteService { @@ -21,69 +24,87 @@ public class VoteService { @Autowired private VoteRepository voteRepository; - public Optional save(Vote vote) { - return Optional.ofNullable(voteRepository.save(vote)); + @Autowired + private SessionService sessionService; + + public Optional registerVote(VoteDTO voteDTO) throws AlreadyVotedException, NoSessionToVoteException { + + if (validateAssociateAlreadyVote(sessionService.get(voteDTO.sessionId()), voteDTO.associatedId())) { + throw new AlreadyVotedException(); + } + + if (!validateSessionForVoting(voteDTO)) { + throw new NoSessionToVoteException(); + } + + Vote vote = Vote.builder() + .id(voteDTO.voteId()) + .vote(associatedVoteHandler(voteDTO.vote())) + .associatedId(voteDTO.associatedId()) + .session(sessionService.get(voteDTO.sessionId()).get()) + .build(); + return Optional.of(voteRepository.save(vote)); + } + + public Vote get(String id) { + return voteRepository.findById(id).orElse(null); } public List findAll() { return voteRepository.findAll(); } - public ResponseEntity getVotes(String sessionId) { - List selectedVotes = selectVotesPerSession(sessionId); + public void delete(String id) { + voteRepository.deleteById(id); + } + + public void deleteAll() { + voteRepository.deleteAll(); + } + + public ResponseEntity getVotesResult(String sessionId) { + + List votes = voteRepository.findAll(); + List selectedVotes = votes.stream() + .filter(e -> e.getSession().getId().equals(sessionId)) + .toList(); + Long totalVotesYes = 0L; Long totalVotesNo = 0L; + Long totalVotes = 0L; for (Vote vote : selectedVotes) { if (vote.getVote().equals(VOTE_YES)) { totalVotesYes++; - } else { totalVotesNo++; - } + totalVotes++; } - VoteDTO voteDTO = VoteDTO.builder() - .votesYes(String.valueOf(totalVotesYes)) - .votesNo(String.valueOf(totalVotesNo)) - .build(); + VoteResponseDTO voteResponseDTO = new VoteResponseDTO(String.valueOf(totalVotesYes), + String.valueOf(totalVotesNo), String.valueOf(totalVotes)); - return new ResponseEntity<>(voteDTO, HttpStatus.OK); + return new ResponseEntity<>(voteResponseDTO, HttpStatus.OK); } - public List selectVotesPerSession(String sessionId) { - List listVote = findAll(); + public Boolean validateSessionForVoting(VoteDTO voteDTO) { - return listVote - .stream() - .filter(e -> e.getSession().getId().equals(sessionId)) - .collect(Collectors.toList()); + Optional session = sessionService.get(voteDTO.sessionId()); + return session.isPresent(); } - public ResponseEntity voteSession(String associatedVote, String associatedId, Session session) { - Vote vote = Vote.builder() - .vote(associatedVoteHandler(associatedVote)) - .associatedId(associatedId) - .session(session) - .build(); - - Optional optionalVoteEntity = save(vote); - return optionalVoteEntity.map(value -> new ResponseEntity<>(value, HttpStatus.CREATED)) - .orElseGet(() -> new ResponseEntity<>(HttpStatus.BAD_REQUEST)); - } - - private String associatedVoteHandler(String associatedVote) { - return associatedVote.toUpperCase(); - } - - public Boolean validateAssociateAlreadyVote(Session session, String associatedId) { + public Boolean validateAssociateAlreadyVote(Optional session, String associateId) { List listVoteEntity = voteRepository.findAllBySession(session); return listVoteEntity .stream() .anyMatch(entity -> - entity.getAssociatedId().equals(associatedId) + entity.getAssociatedId().equals(associateId) ); } + + private String associatedVoteHandler(String associatedVote) { + return associatedVote.toUpperCase().trim(); + } } From 5367e014ec05a2fd7cf5d9466257f8825832a00f Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Tue, 10 Dec 2024 11:41:59 -0300 Subject: [PATCH 11/33] =?UTF-8?q?=F0=9F=9A=A7=20refactored=20the=20query?= =?UTF-8?q?=20form=20to=20RequestParam=20and=20removed=20the=20status=20fr?= =?UTF-8?q?om=20the=20entity=20Associated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/controllers/AgendaController.java | 12 ++++++------ .../controllers/AssociatedController.java | 12 ++++++------ .../application/controllers/VoteController.java | 8 ++++---- .../desafiovotacao/domain/entities/Associated.java | 5 ----- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java index 743d2bf..eae4478 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java @@ -37,21 +37,21 @@ public ResponseEntity findAll() { return new ResponseEntity<>(agendaService.findAll(), HttpStatus.OK); } - @GetMapping("/{id}") + @GetMapping("/id") @CrossOrigin("*") - public ResponseEntity get(@PathVariable(value = "id") String id) { + public ResponseEntity get(@RequestParam(value = "id") String id) { return new ResponseEntity<>(agendaService.get(id), HttpStatus.OK); } - @PutMapping("/{id}") + @PutMapping("/id") @CrossOrigin("*") - public ResponseEntity update(@PathVariable(value = "id") String id, @RequestBody AgendaDTO agendaDTO) { + public ResponseEntity update(@RequestParam(value = "id") String id, @RequestBody AgendaDTO agendaDTO) { return new ResponseEntity<>(agendaService.update(id, agendaDTO), HttpStatus.OK); } - @DeleteMapping("/{id}") + @DeleteMapping("/id") @CrossOrigin("*") - public ResponseEntity delete(@PathVariable(value = "id") String id) { + public ResponseEntity delete(@RequestParam(value = "id") String id) { agendaService.delete(id); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java index d2c1458..3d86959 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java @@ -22,9 +22,9 @@ public ResponseEntity createAssociated(@RequestBody AssociatedDTO as return new ResponseEntity<>(associatedService.createAssociated(associatedDTO), HttpStatus.CREATED); } - @GetMapping("/{id}") + @GetMapping("/id") @CrossOrigin("*") - public ResponseEntity get(@PathVariable(value = "id") String id) { + public ResponseEntity get(@RequestParam(value = "id") String id) { return new ResponseEntity<>(associatedService.get(id), HttpStatus.OK); } @@ -34,16 +34,16 @@ public ResponseEntity findAll() { return new ResponseEntity<>(associatedService.findAll(), HttpStatus.OK); } - @PutMapping("/{id}") + @PutMapping("/id") @CrossOrigin("*") - public ResponseEntity update(@PathVariable(value = "id") String id, @RequestBody AssociatedDTO associatedDTO) { + public ResponseEntity update(@RequestParam(value = "id") String id, @RequestBody AssociatedDTO associatedDTO) { return new ResponseEntity<>(associatedService.update(id, associatedDTO), HttpStatus.OK); } - @DeleteMapping("/{id}") + @DeleteMapping("/id") @CrossOrigin("*") - public ResponseEntity delete(@PathVariable(value = "id") String id) { + public ResponseEntity delete(@RequestParam(value = "id") String id) { associatedService.delete(id); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java index c7886bc..c819568 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java @@ -55,9 +55,9 @@ public ResponseEntity getVotes() { return new ResponseEntity<>(voteService.findAll(), HttpStatus.OK); } - @DeleteMapping("/vote/{id}") + @DeleteMapping("/vote") @CrossOrigin("*") - public ResponseEntity deleteVote(@PathVariable (value = "id") String id) { + public ResponseEntity deleteVote(@RequestParam (value = "id") String id) { voteService.delete(id); return new ResponseEntity<>(HttpStatus.OK); } @@ -69,9 +69,9 @@ public ResponseEntity deleteVotes() { return new ResponseEntity<>(HttpStatus.OK); } - @GetMapping("/votes/result/{session_id}") + @GetMapping("/votes/result") @CrossOrigin("*") - public ResponseEntity getVotesResult(@PathVariable (value = "session_id") String session_id) { + public ResponseEntity getVotesResult(@RequestParam (value = "session_id") String session_id) { return new ResponseEntity<>(voteService.getVotesResult(session_id), HttpStatus.OK); } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java index 51be449..da15df4 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/entities/Associated.java @@ -15,9 +15,4 @@ public class Associated { private String id; private String cpf; private String name; - private Associated ableToVote; - - public enum ableToVote { - ABLE_TO_VOTE, UNABLE_TO_VOTE - } } From 73d3eb9e44fc1271130524e3c47b4545b8395b15 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Tue, 10 Dec 2024 13:39:51 -0300 Subject: [PATCH 12/33] =?UTF-8?q?=E2=9C=A8feature/=20added=20api=20to=20va?= =?UTF-8?q?lidate=20a=20cpf=20and=20if=20the=20cpf=20is=20able=20to=20vote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Tarefa Bônus 1 - Integração com sistemas externos" --- .../controllers/CPFValidationController.java | 37 ++++++++++++ .../domain/services/CPFValidationService.java | 60 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java new file mode 100644 index 0000000..2f6bb01 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java @@ -0,0 +1,37 @@ +package com.votacao.desafiovotacao.application.controllers; + +import com.votacao.desafiovotacao.domain.services.CPFValidationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/CPFvalidator") +public class CPFValidationController { + + @Autowired + private CPFValidationService cpfValidationService; + + @GetMapping("") + public Object validateCpf(@RequestParam String cpf) { + + if(!cpfValidationService.isValid(cpf)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("CPF inválido"); + } + + String cpfStatus = cpfValidationService.isAbleToVote(cpf); + Map response = new HashMap<>(); + response.put("status", cpfStatus); + if (cpfStatus.equals("ABLE_TO_VOTE")) { + return ResponseEntity.ok(response); + } + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java new file mode 100644 index 0000000..c5a5dc9 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java @@ -0,0 +1,60 @@ +package com.votacao.desafiovotacao.domain.services; + +import org.springframework.stereotype.Service; + +import java.util.Random; + +@Service +public class CPFValidationService { + + private final Random random = new Random(); + + public boolean isValid(String cpf) { + + if (cpf == null || cpf.isEmpty()) { + return false; + } + + // Remove all non-digit characters + cpf = cpf.replaceAll("\\D", ""); + + if (cpf.length() != 11) { + return false; + } + + // verify if all digits are the same + if (cpf.matches("(\\d)\\1{10}")) { + return false; + } + + // Calculating the two verification digits + try { + int[] wheigth = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + int firstDigit = calculateCheckDigit(cpf, wheigth); + + wheigth = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int secondDigit = calculateCheckDigit(cpf, wheigth); + + // Validate the calculated verification digits with the ones informed in the CPF + return firstDigit == Character.getNumericValue(cpf.charAt(9)) && + secondDigit == Character.getNumericValue(cpf.charAt(10)); + } catch (Exception e) { + return false; + } + } + + private static int calculateCheckDigit(String cpf, int[] wheigth) { + int sum = 0; + for (int i = 0; i < wheigth.length; i++) { + sum += Character.getNumericValue(cpf.charAt(i)) * wheigth[i]; + } + int remainder = sum % 11; + return remainder == 10 ? 0 : remainder; + } + + public String isAbleToVote(String cpf) { + return random.nextBoolean() ? "ABLE_TO_VOTE" : "UNABLE_TO_VOTE"; + } +} + + From 459a375930fcb8c0c818194dd520791f7ab7fdf9 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Tue, 10 Dec 2024 18:45:13 -0300 Subject: [PATCH 13/33] =?UTF-8?q?=E2=9C=A8feature/=20added=20exceptions=20?= =?UTF-8?q?handlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/AgendaController.java | 8 ++------ .../controllers/AssociatedController.java | 13 ++++++++++--- .../controllers/SessionController.java | 13 ++++++++++--- .../application/controllers/VoteController.java | 5 +++-- .../exceptions/AgendaNotFoundException.java | 1 - .../exceptions/AgendaNotValidException.java | 7 +++++++ .../exceptions/AlreadyVotedException.java | 7 +++++++ .../domain/exceptions/CPFInvalidException.java | 7 +++++++ .../domain/exceptions/NameNeededException.java | 7 +++++++ .../exceptions/NoSessionToVoteException.java | 7 +++++++ .../domain/services/AssociatedService.java | 17 +++++++++++++---- .../domain/services/SessionService.java | 16 ++++++++++++---- .../domain/services/VoteService.java | 5 +++-- 13 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotValidException.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AlreadyVotedException.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFInvalidException.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NameNeededException.java create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoSessionToVoteException.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java index eae4478..01c66a2 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java @@ -1,11 +1,9 @@ package com.votacao.desafiovotacao.application.controllers; import com.votacao.desafiovotacao.application.dtos.AgendaDTO; -import com.votacao.desafiovotacao.domain.entities.Agenda; import com.votacao.desafiovotacao.domain.exceptions.AgendaNotValidException; import com.votacao.desafiovotacao.domain.services.AgendaService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpMessage; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -20,14 +18,12 @@ public class AgendaController { @PostMapping @CrossOrigin("*") - public ResponseEntity createAgenda(@RequestBody AgendaDTO agendaDTO) throws AgendaNotValidException { + public ResponseEntity createAgenda(@RequestBody AgendaDTO agendaDTO) throws AgendaNotValidException { try { return new ResponseEntity<>(agendaService.createAgenda(agendaDTO), HttpStatus.CREATED); - // TODO: fix this exception handling } catch (AgendaNotValidException e) { - e.printStackTrace(); - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + return new ResponseEntity<>("Agenda not valid", HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java index 3d86959..f23be45 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java @@ -1,7 +1,8 @@ package com.votacao.desafiovotacao.application.controllers; import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; -import com.votacao.desafiovotacao.domain.entities.Associated; +import com.votacao.desafiovotacao.domain.exceptions.CPFInvalidException; +import com.votacao.desafiovotacao.domain.exceptions.NameNeededException; import com.votacao.desafiovotacao.domain.services.AssociatedService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -18,8 +19,14 @@ public class AssociatedController { @PostMapping("/create") @CrossOrigin("*") - public ResponseEntity createAssociated(@RequestBody AssociatedDTO associatedDTO) { - return new ResponseEntity<>(associatedService.createAssociated(associatedDTO), HttpStatus.CREATED); + public ResponseEntity createAssociated(@RequestBody AssociatedDTO associatedDTO) { + try { + return new ResponseEntity<>(associatedService.createAssociated(associatedDTO), HttpStatus.CREATED); + } catch (NameNeededException e) { + return new ResponseEntity<>(e.getMessage() ,HttpStatus.BAD_REQUEST); + } catch (CPFInvalidException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } } @GetMapping("/id") diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java index f616cf9..8a96715 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java @@ -24,9 +24,16 @@ public class SessionController { @PostMapping("/create") @CrossOrigin("*") - public ResponseEntity createSession(@RequestBody SessionDTO sessionDTO) - throws AgendaNotFoundException, SessionTimeException { - return new ResponseEntity<>(sessionService.createSession(sessionDTO), HttpStatus.CREATED); + public ResponseEntity createSession(@RequestBody SessionDTO sessionDTO) { + try { + return new ResponseEntity<>(sessionService.createSession(sessionDTO), HttpStatus.CREATED); + } catch (AgendaNotFoundException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } catch (SessionTimeException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } catch (NumberFormatException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } } @GetMapping("/id") diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java index c819568..78b898f 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java @@ -3,6 +3,7 @@ import com.votacao.desafiovotacao.application.dtos.VoteDTO; import com.votacao.desafiovotacao.domain.exceptions.AlreadyVotedException; import com.votacao.desafiovotacao.domain.exceptions.NoSessionToVoteException; +import com.votacao.desafiovotacao.domain.exceptions.SessionTimeException; import com.votacao.desafiovotacao.domain.services.AgendaService; import com.votacao.desafiovotacao.domain.services.SessionService; import com.votacao.desafiovotacao.domain.services.VoteService; @@ -31,9 +32,9 @@ public ResponseEntity registerVote(@RequestBody VoteDTO voteDTO) throws Alrea try { return new ResponseEntity<>(voteService.registerVote(voteDTO), HttpStatus.OK); } catch (AlreadyVotedException e) { - return new ResponseEntity<>("Associated already voted", HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } catch (NoSessionToVoteException e) { - return new ResponseEntity<>("Can't vote in without a session", HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java index 2e8662a..5e19fe0 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotFoundException.java @@ -1,7 +1,6 @@ package com.votacao.desafiovotacao.domain.exceptions; public class AgendaNotFoundException extends Exception { - public AgendaNotFoundException() { super("Agenda couldn't be found"); } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotValidException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotValidException.java new file mode 100644 index 0000000..37be33c --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AgendaNotValidException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class AgendaNotValidException extends Exception { + public AgendaNotValidException() { + super("Agenda not valid"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AlreadyVotedException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AlreadyVotedException.java new file mode 100644 index 0000000..c5aa54e --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/AlreadyVotedException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class AlreadyVotedException extends Exception { + public AlreadyVotedException() { + super("Associated has already vote in this session"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFInvalidException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFInvalidException.java new file mode 100644 index 0000000..808351e --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFInvalidException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class CPFInvalidException extends Exception { + public CPFInvalidException() { + super("CPF valid needed"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NameNeededException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NameNeededException.java new file mode 100644 index 0000000..4f9f24e --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NameNeededException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class NameNeededException extends Exception { + public NameNeededException() { + super("Name needed"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoSessionToVoteException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoSessionToVoteException.java new file mode 100644 index 0000000..789ca52 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoSessionToVoteException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class NoSessionToVoteException extends Exception { + public NoSessionToVoteException() { + super("No session to vote or it is closed"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java index 3ceb422..5b75f8c 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java @@ -2,6 +2,8 @@ import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; import com.votacao.desafiovotacao.domain.entities.Associated; +import com.votacao.desafiovotacao.domain.exceptions.CPFInvalidException; +import com.votacao.desafiovotacao.domain.exceptions.NameNeededException; import com.votacao.desafiovotacao.infra.AssociatedRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -14,9 +16,18 @@ public class AssociatedService { @Autowired private AssociatedRepository associatedRepository; - public Associated createAssociated(AssociatedDTO associatedDTO) { + @Autowired + private CPFValidationService cpfValidationService; + + public Associated createAssociated(AssociatedDTO associatedDTO) throws NameNeededException, CPFInvalidException { + + if (associatedDTO.name().isEmpty()) { + throw new NameNeededException(); + } + if (!cpfValidationService.isValid(associatedDTO.cpf())) { + throw new CPFInvalidException(); + } - // TODO: add tarefa bonus 1 Associated associated = Associated.builder() .id(associatedDTO.id()) .cpf(associatedDTO.cpf()) @@ -48,8 +59,6 @@ public void delete(String id) { public void deleteAll() { associatedRepository.deleteAll(); } - - } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java index 8b392be..43593cc 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java @@ -29,11 +29,19 @@ public class SessionService { public Session createSession(SessionDTO sessionDTO) throws AgendaNotFoundException, SessionTimeException { - long duration = Long.parseLong(sessionDTO.duration()); + long duration; + if (!sessionDTO.duration().isEmpty()) { + try { + duration = Long.parseLong(sessionDTO.duration()); + } catch (NumberFormatException e) { + throw new NumberFormatException("The duration must be a integer"); + } + } else { + duration = TIME_DEFAULT; + } String agendaId = sessionDTO.agendaId(); - // TODO: fix this // Validate if the agenda exists and if the time is valid validatePostSessionRequest(sessionDTO, duration); @@ -102,12 +110,12 @@ public void validatePostSessionRequest(SessionDTO sessionDTO, Long minutesOpened // Validate if the agenda exists Optional optionalAgenda = agendaRepository.findById(sessionDTO.agendaId()); - if (!optionalAgenda.isPresent()) { + if (optionalAgenda.isEmpty()) { throw new AgendaNotFoundException(); } // Validate if the time is valid - if (ObjectUtils.isEmpty(minutesOpened) || minutesOpened < TIME_DEFAULT) { + if (minutesOpened < TIME_DEFAULT) { throw new SessionTimeException(); } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java index 8d98cdd..15b178f 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java @@ -33,7 +33,7 @@ public Optional registerVote(VoteDTO voteDTO) throws AlreadyVotedException throw new AlreadyVotedException(); } - if (!validateSessionForVoting(voteDTO)) { + if (validateSessionForVoting(voteDTO)) { throw new NoSessionToVoteException(); } @@ -90,7 +90,8 @@ public ResponseEntity getVotesResult(String sessionId) { public Boolean validateSessionForVoting(VoteDTO voteDTO) { Optional session = sessionService.get(voteDTO.sessionId()); - return session.isPresent(); + + return (!session.isPresent()) || session.get().getStatus().equals(Session.StatusSession.ENCERRADA); } From 9835f9488877d3bbe54bb7291b58eb662f20d5c7 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Thu, 12 Dec 2024 20:42:58 -0300 Subject: [PATCH 14/33] =?UTF-8?q?=F0=9F=8E=89=20frontend=20created=20the?= =?UTF-8?q?=20projetc=20prototipe=20and=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.gitignore | 24 + frontend/README.md | 50 + frontend/eslint.config.js | 28 + frontend/index.html | 13 + frontend/package-lock.json | 3938 +++++++++++++++++++++++ frontend/package.json | 33 + frontend/public/vite.svg | 1 + frontend/src/Agenda/index.tsx | 12 + frontend/src/App.css | 42 + frontend/src/App.tsx | 33 + frontend/src/Home/index.tsx | 77 + frontend/src/Session/index.tsx | 12 + frontend/src/Vote/vote.tsx | 12 + frontend/src/assets/react.svg | 1 + frontend/src/associated/index.tsx | 56 + frontend/src/main.tsx | 9 + frontend/src/page.tsx | 35 + frontend/src/ui/Util/main-container.tsx | 29 + frontend/src/ui/Util/returnButton.tsx | 22 + frontend/src/vite-env.d.ts | 1 + frontend/tsconfig.app.json | 26 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 24 + frontend/vite.config.ts | 7 + 24 files changed, 4492 insertions(+) create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/vite.svg create mode 100644 frontend/src/Agenda/index.tsx create mode 100644 frontend/src/App.css create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/Home/index.tsx create mode 100644 frontend/src/Session/index.tsx create mode 100644 frontend/src/Vote/vote.tsx create mode 100644 frontend/src/assets/react.svg create mode 100644 frontend/src/associated/index.tsx create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/page.tsx create mode 100644 frontend/src/ui/Util/main-container.tsx create mode 100644 frontend/src/ui/Util/returnButton.tsx create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..74872fd --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,50 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: + +- Configure the top-level `parserOptions` property like this: + +```js +export default tseslint.config({ + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` +- Optionally add `...tseslint.configs.stylisticTypeChecked` +- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: + +```js +// eslint.config.js +import react from 'eslint-plugin-react' + +export default tseslint.config({ + // Set the react version + settings: { react: { version: '18.3' } }, + plugins: { + // Add the react plugin + react, + }, + rules: { + // other rules... + // Enable its recommended rules + ...react.configs.recommended.rules, + ...react.configs['jsx-runtime'].rules, + }, +}) +``` diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..165f4d6 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3938 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/material": "^6.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.0.2" + }, + "devDependencies": { + "@eslint/js": "^9.15.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.15.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.12.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.15.0", + "vite": "^6.0.1" + } + }, + "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, + "license": "Apache-2.0", + "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.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", + "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.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/generator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.3", + "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==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.2.0.tgz", + "integrity": "sha512-Nn5PSkUqbDrvezpiiiYZiAbX4SFEiy3CbikUL6pWOXEUsq+L1j50OOyyUIHpaX2Hr+5V5UxTh+fPeC4nsGNhdw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.2.0.tgz", + "integrity": "sha512-7FXXUPIyYzP02a7GvqwJ7ocmdP+FkvLvmy/uxG1TDmTlsr8nEClQp75uxiVznJqAY/jJy4d+Rj/fNWNxwidrYQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.2.0", + "@mui/system": "^6.2.0", + "@mui/types": "^7.2.19", + "@mui/utils": "^6.2.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.11", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.2.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.2.0.tgz", + "integrity": "sha512-lYd2MrVddhentF1d/cMXKnwlDjr/shbO3A2eGq22PCYUoZaqtAGZMc0U86KnJ/Sh5YzNYePqTOaaowAN8Qea8A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.2.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.2.0.tgz", + "integrity": "sha512-rV4YCu6kcCjMnHFXU/tQcL6XfYVfFVR8n3ZVNGnk2rpXnt/ctOPJsF+eUQuhkHciueLVKpI06+umr1FxWWhVmQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.2.0.tgz", + "integrity": "sha512-DCeqev9Cd4f4pm3O1lqSGW/DIHHBG6ZpqMX9iIAvN4asYv+pPWv2/lKov9kWk5XThhxFnGSv93SRNE1kNRRg5Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.2.0", + "@mui/styled-engine": "^6.2.0", + "@mui/types": "^7.2.19", + "@mui/utils": "^6.2.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.19", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", + "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.2.0.tgz", + "integrity": "sha512-77CaFJi+OIi2SjbPwCis8z5DXvE0dfx9hBz5FguZHt1VYFlWEPCWTHcMsQCahSErnfik5ebLsYK8+D+nsjGVfw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.19", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz", + "integrity": "sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz", + "integrity": "sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz", + "integrity": "sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz", + "integrity": "sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz", + "integrity": "sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz", + "integrity": "sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz", + "integrity": "sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz", + "integrity": "sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz", + "integrity": "sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz", + "integrity": "sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz", + "integrity": "sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz", + "integrity": "sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz", + "integrity": "sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz", + "integrity": "sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz", + "integrity": "sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz", + "integrity": "sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz", + "integrity": "sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz", + "integrity": "sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz", + "integrity": "sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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, + "license": "MIT", + "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, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.16.tgz", + "integrity": "sha512-oh8AMIC4Y2ciKufU8hnKgs+ufgbA/dhPTACaZPM86AbwX9QwnFtSoPWEeRUj8fge+v6kFt78BXcDhAU1SrrAsw==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.0.tgz", + "integrity": "sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/type-utils": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.0.tgz", + "integrity": "sha512-hgUZ3kTEpVzKaK3uNibExUYm6SKKOmTU2BOxBSvOYwtJEPdVQ70kZJpPjstlnhCHcuc2WGfSbpKlb/69ttyN5Q==", + "dev": true, + "license": "MITClause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.0.tgz", + "integrity": "sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.0.tgz", + "integrity": "sha512-er224jRepVAVLnMF2Q7MZJCq5CsdH2oqjP4dT7K6ij09Kyd+R21r7UVJrF0buMVdZS5QRhDzpvzAxHxabQadow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.18.0", + "@typescript-eslint/utils": "8.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.0.tgz", + "integrity": "sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.0.tgz", + "integrity": "sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/visitor-keys": "8.18.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.0.tgz", + "integrity": "sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.18.0", + "@typescript-eslint/types": "8.18.0", + "@typescript-eslint/typescript-estree": "8.18.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.0.tgz", + "integrity": "sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.0", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", + "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.0", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001688", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", + "integrity": "sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "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, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "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, + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.73", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz", + "integrity": "sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==", + "dev": true, + "license": "ISC" + }, + "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==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", + "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.16.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.5", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.16.tgz", + "integrity": "sha512-slterMlxAhov/DZO8NScf6mEeMBBXodFUolijDvrtTxyezyLoTQaa73FyYus/VbTdftd8wBgBxPMRk3poleXNQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.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", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.13.0.tgz", + "integrity": "sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/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==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "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", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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, + "license": "MIT" + }, + "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==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "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==", + "license": "MIT", + "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", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "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==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.2.tgz", + "integrity": "sha512-m5AcPfTRUcjwmhBzOJGEl6Y7+Crqyju0+TgTQxoS4SO+BkWbhOrcfZNq6wSWdl2BBbJbsAoBUb8ZacOFT+/JlA==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.0.2.tgz", + "integrity": "sha512-VJOQ+CDWFDGaWdrG12Nl+d7yHtLaurNgAQZVgaIy7/Xd+DojgmYLosFfZdGz1wpxmjJIAkAMVTKWcvkx1oggAw==", + "license": "MIT", + "dependencies": { + "react-router": "7.0.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", + "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/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.28.1.tgz", + "integrity": "sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.28.1", + "@rollup/rollup-android-arm64": "4.28.1", + "@rollup/rollup-darwin-arm64": "4.28.1", + "@rollup/rollup-darwin-x64": "4.28.1", + "@rollup/rollup-freebsd-arm64": "4.28.1", + "@rollup/rollup-freebsd-x64": "4.28.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.28.1", + "@rollup/rollup-linux-arm-musleabihf": "4.28.1", + "@rollup/rollup-linux-arm64-gnu": "4.28.1", + "@rollup/rollup-linux-arm64-musl": "4.28.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.28.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.28.1", + "@rollup/rollup-linux-riscv64-gnu": "4.28.1", + "@rollup/rollup-linux-s390x-gnu": "4.28.1", + "@rollup/rollup-linux-x64-gnu": "4.28.1", + "@rollup/rollup-linux-x64-musl": "4.28.1", + "@rollup/rollup-win32-arm64-msvc": "4.28.1", + "@rollup/rollup-win32-ia32-msvc": "4.28.1", + "@rollup/rollup-win32-x64-msvc": "4.28.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "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, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.0.tgz", + "integrity": "sha512-Xq2rRjn6tzVpAyHr3+nmSg1/9k9aIHnJ2iZeOH7cfGOWqTkXTm3kwpQglEuLGdNrYvPF+2gtAs+/KF5rjVo+WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.18.0", + "@typescript-eslint/parser": "8.18.0", + "@typescript-eslint/utils": "8.18.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.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", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", + "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.0", + "postcss": "^8.4.49", + "rollup": "^4.23.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..17e5d4b --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,33 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/material": "^6.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.0.2" + }, + "devDependencies": { + "@eslint/js": "^9.15.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.15.0", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", + "globals": "^15.12.0", + "typescript": "~5.6.2", + "typescript-eslint": "^8.15.0", + "vite": "^6.0.1" + } +} diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/frontend/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/Agenda/index.tsx b/frontend/src/Agenda/index.tsx new file mode 100644 index 0000000..0d697a5 --- /dev/null +++ b/frontend/src/Agenda/index.tsx @@ -0,0 +1,12 @@ +"use cliente" + +import MainContainer from "../ui/Util/main-container.tsx"; + +export default function Index() { + + return ( + +

Criar Pauta

+
+ ); +} \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000..8a02e97 --- /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..240f067 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,33 @@ +"use client"; +import MainContainer from "./ui/Util/main-container.tsx"; +import { + BrowserRouter as Router, + Route, Routes, +} from "react-router-dom"; + +import Associated from "./associated"; +import Agenda from "./Agenda"; +import Index from "./Session"; +import Home from "./Home"; +import Vote from "./Vote/vote.tsx"; + + +export function App() { + + return ( + + + + }> + }> + }> + }> + }> + {/*}>*/} + + + + ) +} + +export default App diff --git a/frontend/src/Home/index.tsx b/frontend/src/Home/index.tsx new file mode 100644 index 0000000..9a4d214 --- /dev/null +++ b/frontend/src/Home/index.tsx @@ -0,0 +1,77 @@ +"use client"; +import React from "react"; +import {Box, Button, Grid2, Paper, Stack} from "@mui/material"; +import {Link, Navigate} from "react-router-dom"; +import MainContainer from "../ui/Util/main-container.tsx"; + + +function Home() { + + const [goToAssociated, setGoToAssociated] = React.useState(false); + const [goToAgenda, setGoToAgenda] = React.useState(false); + + if (goToAssociated) { + return ; + } + + if (goToAgenda) { + return ; + } + + + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default Home; diff --git a/frontend/src/Session/index.tsx b/frontend/src/Session/index.tsx new file mode 100644 index 0000000..d39b8f7 --- /dev/null +++ b/frontend/src/Session/index.tsx @@ -0,0 +1,12 @@ +"use cliente" + +import MainContainer from "../ui/Util/main-container.tsx"; + +export default function Index() { + + return ( + +

Criar sessão

+
+ ); +} \ No newline at end of file diff --git a/frontend/src/Vote/vote.tsx b/frontend/src/Vote/vote.tsx new file mode 100644 index 0000000..36d54ff --- /dev/null +++ b/frontend/src/Vote/vote.tsx @@ -0,0 +1,12 @@ +"use cliente" + +import MainContainer from "../ui/Util/main-container.tsx"; + +export default function Vote() { + + return ( + +

Votar

+
+ ); +} \ No newline at end of file diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/associated/index.tsx b/frontend/src/associated/index.tsx new file mode 100644 index 0000000..bac4e2b --- /dev/null +++ b/frontend/src/associated/index.tsx @@ -0,0 +1,56 @@ +"use client"; +import {Box, Button, Grid2, Stack, TextField} from "@mui/material"; +import MainContainer from "../ui/Util/main-container.tsx"; +import ReturnButton from "../ui/Util/returnButton.tsx"; + +export default function Associated() { + + + return ( + + + +

Criar Associado

+
+ + + + + + + + + + + + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..4aff025 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/frontend/src/page.tsx b/frontend/src/page.tsx new file mode 100644 index 0000000..ba9d5c7 --- /dev/null +++ b/frontend/src/page.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import './App.css' + +function App() { + const [count, setCount] = useState(0) + + 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/ui/Util/main-container.tsx b/frontend/src/ui/Util/main-container.tsx new file mode 100644 index 0000000..1f2b27f --- /dev/null +++ b/frontend/src/ui/Util/main-container.tsx @@ -0,0 +1,29 @@ +import { Box } from "@mui/material"; + +export default function MainContainer({children, }: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/frontend/src/ui/Util/returnButton.tsx b/frontend/src/ui/Util/returnButton.tsx new file mode 100644 index 0000000..b078005 --- /dev/null +++ b/frontend/src/ui/Util/returnButton.tsx @@ -0,0 +1,22 @@ +"use client"; + +import {Navigate} from "react-router-dom"; +import {Button} from "@mui/material"; +import React from "react"; + +function ReturnButton() { + const [goToHome, setGoToHome] = React.useState(false); + if (goToHome) { + return ; + } + return ( + + + ); +} + +export default ReturnButton; diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000..358ca9b --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000..8b0f57b --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) From 2acc750a7016ce8086d5b6faf61edae0197c7aa2 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Thu, 12 Dec 2024 22:46:08 -0300 Subject: [PATCH 15/33] =?UTF-8?q?=F0=9F=9A=A7=20created=20a=20simple=20flu?= =?UTF-8?q?x=20to=20exemplify=20the=20frontend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/Agenda/index.tsx | 54 +++++++++- frontend/src/App.tsx | 9 +- frontend/src/Home/index.tsx | 50 +++++++++- frontend/src/Session/index.tsx | 68 ++++++++++++- frontend/src/Vote/index.tsx | 127 ++++++++++++++++++++++++ frontend/src/Vote/result.tsx | 49 +++++++++ frontend/src/Vote/vote.tsx | 12 --- frontend/src/ui/Util/main-container.tsx | 1 + frontend/src/ui/Util/returnButton.tsx | 4 +- 9 files changed, 343 insertions(+), 31 deletions(-) create mode 100644 frontend/src/Vote/index.tsx create mode 100644 frontend/src/Vote/result.tsx delete mode 100644 frontend/src/Vote/vote.tsx diff --git a/frontend/src/Agenda/index.tsx b/frontend/src/Agenda/index.tsx index 0d697a5..44401f0 100644 --- a/frontend/src/Agenda/index.tsx +++ b/frontend/src/Agenda/index.tsx @@ -1,12 +1,58 @@ -"use cliente" - +"use client"; +import {Box, Button, Grid2, Stack, TextField} from "@mui/material"; import MainContainer from "../ui/Util/main-container.tsx"; +import ReturnButton from "../ui/Util/returnButton.tsx"; + +export default function Agenda() { -export default function Index() { return ( + -

Criar Pauta

+ +

Criar uma Pauta

+
+ + + + + + + + + + + + + + + + + + +
); } \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 240f067..1da195e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,9 +7,10 @@ import { import Associated from "./associated"; import Agenda from "./Agenda"; -import Index from "./Session"; +import Session from "./Session"; import Home from "./Home"; -import Vote from "./Vote/vote.tsx"; +import Vote from "./Vote"; +import VoteResult from "./Vote/result.tsx"; export function App() { @@ -21,9 +22,9 @@ export function App() { }> }> }> - }> + }> }> - {/*}>*/} + }> diff --git a/frontend/src/Home/index.tsx b/frontend/src/Home/index.tsx index 9a4d214..de4ce1a 100644 --- a/frontend/src/Home/index.tsx +++ b/frontend/src/Home/index.tsx @@ -1,7 +1,7 @@ "use client"; import React from "react"; import {Box, Button, Grid2, Paper, Stack} from "@mui/material"; -import {Link, Navigate} from "react-router-dom"; +import {Navigate} from "react-router-dom"; import MainContainer from "../ui/Util/main-container.tsx"; @@ -9,6 +9,9 @@ function Home() { const [goToAssociated, setGoToAssociated] = React.useState(false); const [goToAgenda, setGoToAgenda] = React.useState(false); + const [goToSession, setGoToSession] = React.useState(false); + const [goToVote, setGoToVote] = React.useState(false); + const [goToVoteResult, setGoToVoteResult] = React.useState(false); if (goToAssociated) { return ; @@ -18,6 +21,18 @@ function Home() { return ; } + if (goToSession) { + return + } + + if (goToVote) { + return + } + + if (goToVoteResult) { + return + } + return ( @@ -61,12 +76,37 @@ function Home() { - + + + + + + + + diff --git a/frontend/src/Session/index.tsx b/frontend/src/Session/index.tsx index d39b8f7..72f9426 100644 --- a/frontend/src/Session/index.tsx +++ b/frontend/src/Session/index.tsx @@ -1,12 +1,72 @@ -"use cliente" - +"use client"; +import {Box, Button, Grid2, Stack, TextField, Typography} from "@mui/material"; import MainContainer from "../ui/Util/main-container.tsx"; +import ReturnButton from "../ui/Util/returnButton.tsx"; + +export default function Agenda() { -export default function Index() { return ( + -

Criar sessão

+ +

Abrir uma Sessão de Votação

+
+ + + + + + Para abrir uma sessão de votação, é necessário informar o ID da sessão e o ID da pauta. + + + + + + + + + + + + + + + + + + + +
); } \ No newline at end of file diff --git a/frontend/src/Vote/index.tsx b/frontend/src/Vote/index.tsx new file mode 100644 index 0000000..bdce379 --- /dev/null +++ b/frontend/src/Vote/index.tsx @@ -0,0 +1,127 @@ +"use client"; +import { + Box, + Button, + FormControl, + FormControlLabel, + FormLabel, + Grid2, + Radio, RadioGroup, + Stack, + TextField, + Typography +} from "@mui/material"; +import MainContainer from "../ui/Util/main-container.tsx"; +import ReturnButton from "../ui/Util/returnButton.tsx"; +import React from "react"; +import {green, pink} from "@mui/material/colors"; + +export default function Agenda() { + const [value, setValue] = React.useState('female'); + + const handleChange = (event: React.ChangeEvent) => { + setValue((event.target as HTMLInputElement).value); + }; + + return ( + + + +

Votar

+
+ + + + + + Lembre-se que você só pode votar uma única vez, + precisando identificar o ID da pauta, da sessão e o seu + para que seja possível realizar seu voto. + + + + + + + + + + + + + + + + Espaço onde deve ir a descrição da pauta que se está votando. + + + + + + + Votar: + + } label="SIM" /> + } label="NÃO" /> + + + + + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/Vote/result.tsx b/frontend/src/Vote/result.tsx new file mode 100644 index 0000000..ae4ed55 --- /dev/null +++ b/frontend/src/Vote/result.tsx @@ -0,0 +1,49 @@ +"use client"; +import { + Box, + Grid2, + Stack, + Typography +} from "@mui/material"; +import MainContainer from "../ui/Util/main-container.tsx"; +import ReturnButton from "../ui/Util/returnButton.tsx"; + +export default function VoteResult() { + + + return ( + + + +

Resultado das Votações

+
+ + + + + + Aqui você pode conferir o resultado de todas as votações realizadas. + + + + + + + + + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/Vote/vote.tsx b/frontend/src/Vote/vote.tsx deleted file mode 100644 index 36d54ff..0000000 --- a/frontend/src/Vote/vote.tsx +++ /dev/null @@ -1,12 +0,0 @@ -"use cliente" - -import MainContainer from "../ui/Util/main-container.tsx"; - -export default function Vote() { - - return ( - -

Votar

-
- ); -} \ No newline at end of file diff --git a/frontend/src/ui/Util/main-container.tsx b/frontend/src/ui/Util/main-container.tsx index 1f2b27f..3b8bb5e 100644 --- a/frontend/src/ui/Util/main-container.tsx +++ b/frontend/src/ui/Util/main-container.tsx @@ -12,6 +12,7 @@ export default function MainContainer({children, }: { minHeight: "100vh" }, marginTop: 4, + marginBottom: 4, // marginRight: 2, // paddingY: 2, // paddingX: 4, diff --git a/frontend/src/ui/Util/returnButton.tsx b/frontend/src/ui/Util/returnButton.tsx index b078005..30aa62f 100644 --- a/frontend/src/ui/Util/returnButton.tsx +++ b/frontend/src/ui/Util/returnButton.tsx @@ -10,10 +10,10 @@ function ReturnButton() { return ; } return ( - ); From 94d69cbe48821edde021862460f38b3e1062a60a Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 13 Dec 2024 11:22:00 -0300 Subject: [PATCH 16/33] =?UTF-8?q?=E2=9C=A8feature/=20created=20unit=20test?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/VoteController.java | 1 - .../domain/services/VoteService.java | 4 + .../infra/AssociatedRepository.java | 1 + .../desafiovotacao/infra/VoteRepository.java | 2 + .../services/VoteServiceTest.java | 117 ++++++++++++++++++ 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 backend/src/test/java/com/votacao/desafiovotacao/services/VoteServiceTest.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java index 78b898f..c60dea9 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java @@ -3,7 +3,6 @@ import com.votacao.desafiovotacao.application.dtos.VoteDTO; import com.votacao.desafiovotacao.domain.exceptions.AlreadyVotedException; import com.votacao.desafiovotacao.domain.exceptions.NoSessionToVoteException; -import com.votacao.desafiovotacao.domain.exceptions.SessionTimeException; import com.votacao.desafiovotacao.domain.services.AgendaService; import com.votacao.desafiovotacao.domain.services.SessionService; import com.votacao.desafiovotacao.domain.services.VoteService; diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java index 15b178f..f3de6e6 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java @@ -108,4 +108,8 @@ public Boolean validateAssociateAlreadyVote(Optional session, String as private String associatedVoteHandler(String associatedVote) { return associatedVote.toUpperCase().trim(); } + + public List findAllVotesBySessionById(String sessionId) { + return voteRepository.findAllBySessionId(sessionId); + } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java index 0786fb1..5df1cdf 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/AssociatedRepository.java @@ -6,4 +6,5 @@ @Repository public interface AssociatedRepository extends MongoRepository { + Boolean findByCpf(String cpf); } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java index b4d07bb..815c934 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/infra/VoteRepository.java @@ -12,4 +12,6 @@ public interface VoteRepository extends MongoRepository { // Search all votes from a session List findAllBySession(Optional session); + + List findAllBySessionId(String sessionId); } diff --git a/backend/src/test/java/com/votacao/desafiovotacao/services/VoteServiceTest.java b/backend/src/test/java/com/votacao/desafiovotacao/services/VoteServiceTest.java new file mode 100644 index 0000000..42d4b2e --- /dev/null +++ b/backend/src/test/java/com/votacao/desafiovotacao/services/VoteServiceTest.java @@ -0,0 +1,117 @@ +package com.votacao.desafiovotacao.services; + +import com.votacao.desafiovotacao.domain.entities.Agenda; +import com.votacao.desafiovotacao.domain.entities.Associated; +import com.votacao.desafiovotacao.domain.entities.Session; +import com.votacao.desafiovotacao.domain.entities.Vote; +import com.votacao.desafiovotacao.domain.services.VoteService; +import com.votacao.desafiovotacao.infra.AssociatedRepository; +import com.votacao.desafiovotacao.infra.VoteRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class VoteServiceTest { + + @Mock + VoteRepository voteRepository; + + @InjectMocks + VoteService voteService; + + @Mock + AssociatedRepository associatedRepository; + + @Test + void shouldSelectVotesPerSession() { + List expectedListVote = Collections.singletonList(buildVote()); + + given(voteService.findAllVotesBySessionById("1")).willReturn(expectedListVote); + List resultListVote = voteService.findAllVotesBySessionById("1"); + + assertThat(expectedListVote).isEqualTo(resultListVote); + } + + @Test + void shouldReturnEmptyListOfVotesPerSession() { + List resultListVote = voteService.findAllVotesBySessionById("0"); + assertThat(resultListVote).isEmpty(); + } + + @Test + void associatedAlreadyVoted() { + Session session = buildSession(); + List expectedListVote = Collections.singletonList(buildVote()); + + String associateId = "321"; + lenient().when(voteRepository.findAllBySession(Optional.ofNullable(session))).thenReturn(expectedListVote); + + Boolean resultAlreadyVote = voteService.validateAssociateAlreadyVote(Optional.ofNullable(session), associateId); + assertThat(resultAlreadyVote).isTrue(); + } + + @Test + void associatedDidNotVote() { + Session session = buildSession(); + List expectedListVote = Collections.emptyList(); + + String associateId = "321"; + lenient().when(voteRepository.findAllBySession(Optional.ofNullable(session))).thenReturn(expectedListVote); + + Boolean resultAlreadyVote = voteService.validateAssociateAlreadyVote(Optional.ofNullable(session), associateId); + assertThat(resultAlreadyVote).isFalse(); + } + + @Test + public void AssociatedCpfAlreadyExists() { + when(associatedRepository.findByCpf(any())).thenReturn(true); + assertThat(associatedRepository.findByCpf(any())).isTrue(); + } + + private Agenda buildAgenda() { + return Agenda.builder() + .id("123") + .title("Unit Test") + .description("Unit test execution") + .build(); + } + + private Session buildSession() { + buildAgenda(); + return Session.builder() + .id("1") + .agendaId("123") + .status(Session.StatusSession.ABERTA) + .build(); + } + + private Associated buildAssociated() { + return Associated.builder() + .id("321") + .name("Tester") + .cpf("00401509095") + .build(); + } + + private Vote buildVote() { + buildAssociated(); + return Vote.builder() + .id("123") + .vote("SIM") + .associatedId("321") + .session(buildSession()) + .build(); + } +} From d465af42f7e30f0d3da9cb0046442c3755734030 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 13 Dec 2024 15:08:16 -0300 Subject: [PATCH 17/33] =?UTF-8?q?=E2=9C=A8feature/=20add=20docker=20compos?= =?UTF-8?q?e=20to=20make=20it=20easy=20to=20use=20mongoDB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/docker-compose.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 backend/docker-compose.yml diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml new file mode 100644 index 0000000..3f55c47 --- /dev/null +++ b/backend/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.2' + +services: + + mongo: + image: mongo + restart: always + ports: + - 27017:27017 + + mongo-express: + image: mongo-express + restart: always + ports: + - 8081:8081 \ No newline at end of file From ff67cbe4e8afa3c47049969a1e1e894afcc822d4 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 13 Dec 2024 15:10:49 -0300 Subject: [PATCH 18/33] =?UTF-8?q?=E2=9C=A8added=20the=20cpf=20already=20ex?= =?UTF-8?q?ists=20exception=20and=20cleaned=20the=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/pom.xml | 10 ---------- .../desafiovotacao/DesafiovotacaoApplication.java | 1 + .../application/controllers/AgendaController.java | 2 +- .../controllers/AssociatedController.java | 4 +++- .../controllers/SessionController.java | 1 - .../exceptions/CPFAlreadyExistsException.java | 7 +++++++ .../domain/services/AgendaService.java | 1 + .../domain/services/AssociatedService.java | 15 ++++++++++++++- .../domain/services/CPFValidationService.java | 5 +---- .../domain/services/SessionService.java | 5 ----- backend/src/main/resources/application.properties | 2 +- 11 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFAlreadyExistsException.java diff --git a/backend/pom.xml b/backend/pom.xml index 6a6d96c..5633ec3 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -41,11 +41,6 @@ test
- - - - - org.projectlombok lombok @@ -53,11 +48,6 @@ provided - - - - - org.springframework.boot spring-boot-starter-data-mongodb diff --git a/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java index 12c048a..f4b2ba4 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/DesafiovotacaoApplication.java @@ -3,6 +3,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + @SpringBootApplication public class DesafiovotacaoApplication { diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java index 01c66a2..d78766c 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java @@ -23,7 +23,7 @@ public ResponseEntity createAgenda(@RequestBody AgendaDTO agendaDTO) throws A return new ResponseEntity<>(agendaService.createAgenda(agendaDTO), HttpStatus.CREATED); } catch (AgendaNotValidException e) { - return new ResponseEntity<>("Agenda not valid", HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java index f23be45..ed5c3b5 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java @@ -1,6 +1,7 @@ package com.votacao.desafiovotacao.application.controllers; import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; +import com.votacao.desafiovotacao.domain.exceptions.CPFAlreadyExistsException; import com.votacao.desafiovotacao.domain.exceptions.CPFInvalidException; import com.votacao.desafiovotacao.domain.exceptions.NameNeededException; import com.votacao.desafiovotacao.domain.services.AssociatedService; @@ -26,6 +27,8 @@ public ResponseEntity createAssociated(@RequestBody AssociatedDTO associatedD return new ResponseEntity<>(e.getMessage() ,HttpStatus.BAD_REQUEST); } catch (CPFInvalidException e) { return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } catch (CPFAlreadyExistsException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } } @@ -47,7 +50,6 @@ public ResponseEntity update(@RequestParam(value = "id") String id, @RequestB return new ResponseEntity<>(associatedService.update(id, associatedDTO), HttpStatus.OK); } - @DeleteMapping("/id") @CrossOrigin("*") public ResponseEntity delete(@RequestParam(value = "id") String id) { diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java index 8a96715..2599710 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java @@ -2,7 +2,6 @@ import com.votacao.desafiovotacao.application.dtos.SessionDTO; import com.votacao.desafiovotacao.application.dtos.VoteResponseDTO; -import com.votacao.desafiovotacao.domain.entities.Session; import com.votacao.desafiovotacao.domain.exceptions.AgendaNotFoundException; import com.votacao.desafiovotacao.domain.exceptions.SessionTimeException; import com.votacao.desafiovotacao.domain.services.SessionService; diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFAlreadyExistsException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFAlreadyExistsException.java new file mode 100644 index 0000000..8f6ce15 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/CPFAlreadyExistsException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class CPFAlreadyExistsException extends Exception { + public CPFAlreadyExistsException() { + super("CPF already exists"); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java index 1dc155d..360363c 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java @@ -9,6 +9,7 @@ import java.util.List; + @Service public class AgendaService { diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java index 5b75f8c..b8aabeb 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java @@ -2,6 +2,7 @@ import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; import com.votacao.desafiovotacao.domain.entities.Associated; +import com.votacao.desafiovotacao.domain.exceptions.CPFAlreadyExistsException; import com.votacao.desafiovotacao.domain.exceptions.CPFInvalidException; import com.votacao.desafiovotacao.domain.exceptions.NameNeededException; import com.votacao.desafiovotacao.infra.AssociatedRepository; @@ -10,6 +11,7 @@ import java.util.List; + @Service public class AssociatedService { @@ -19,7 +21,8 @@ public class AssociatedService { @Autowired private CPFValidationService cpfValidationService; - public Associated createAssociated(AssociatedDTO associatedDTO) throws NameNeededException, CPFInvalidException { + public Associated createAssociated(AssociatedDTO associatedDTO) + throws NameNeededException, CPFInvalidException, CPFAlreadyExistsException { if (associatedDTO.name().isEmpty()) { throw new NameNeededException(); @@ -28,6 +31,16 @@ public Associated createAssociated(AssociatedDTO associatedDTO) throws NameNeede throw new CPFInvalidException(); } + List associatedList = associatedRepository.findAll(); + boolean cpfExists = associatedList.stream() + .anyMatch(entity -> + entity.getCpf().equals(associatedDTO.cpf()) + ); + + if (cpfExists) { + throw new CPFAlreadyExistsException(); + } + Associated associated = Associated.builder() .id(associatedDTO.id()) .cpf(associatedDTO.cpf()) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java index c5a5dc9..870b1f4 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java @@ -4,6 +4,7 @@ import java.util.Random; + @Service public class CPFValidationService { @@ -15,19 +16,16 @@ public boolean isValid(String cpf) { return false; } - // Remove all non-digit characters cpf = cpf.replaceAll("\\D", ""); if (cpf.length() != 11) { return false; } - // verify if all digits are the same if (cpf.matches("(\\d)\\1{10}")) { return false; } - // Calculating the two verification digits try { int[] wheigth = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int firstDigit = calculateCheckDigit(cpf, wheigth); @@ -35,7 +33,6 @@ public boolean isValid(String cpf) { wheigth = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int secondDigit = calculateCheckDigit(cpf, wheigth); - // Validate the calculated verification digits with the ones informed in the CPF return firstDigit == Character.getNumericValue(cpf.charAt(9)) && secondDigit == Character.getNumericValue(cpf.charAt(10)); } catch (Exception e) { diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java index 43593cc..31ace0c 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/SessionService.java @@ -9,7 +9,6 @@ import com.votacao.desafiovotacao.infra.SessionRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.util.ObjectUtils; import java.time.LocalDateTime; import java.util.List; @@ -42,10 +41,8 @@ public Session createSession(SessionDTO sessionDTO) String agendaId = sessionDTO.agendaId(); - // Validate if the agenda exists and if the time is valid validatePostSessionRequest(sessionDTO, duration); - // Create the session Session session = Session.builder() .id(sessionDTO.id()) .agendaId(agendaId) @@ -107,14 +104,12 @@ public void closeSession(Session session) { public void validatePostSessionRequest(SessionDTO sessionDTO, Long minutesOpened) throws AgendaNotFoundException, SessionTimeException { - // Validate if the agenda exists Optional optionalAgenda = agendaRepository.findById(sessionDTO.agendaId()); if (optionalAgenda.isEmpty()) { throw new AgendaNotFoundException(); } - // Validate if the time is valid if (minutesOpened < TIME_DEFAULT) { throw new SessionTimeException(); } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index b7f398a..95525eb 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -1,3 +1,3 @@ spring.application.name=desafiovotacao server.port=8080 -spring.data.mongodb.uri=mongodb://localhost:27017/voting +#spring.data.mongodb.uri=mongodb://localhost:27017/voting From 89ce5af22569648122037ca6cb86f14a2daef4d6 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 13 Dec 2024 15:20:17 -0300 Subject: [PATCH 19/33] =?UTF-8?q?=F0=9F=93=9D=20added=20documentation=20to?= =?UTF-8?q?=20the=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 backend/README.md diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..60f8dfe --- /dev/null +++ b/backend/README.md @@ -0,0 +1,73 @@ +# Sistema de Votação + +Este projeto foi desenvolvido para o desafio de desenvolvimento de uma API de votação para uma cooperativa. + +## Technologias Utilizadas + +- Java 23 +- Spring Boot +- Maven +- MongoDB +- Docker +- Docker Compose + +## Estrutura do Projeto + +Esse projeto foi criado utilizando a abordagem de design +de DDD (Domain Driven Development) e "Clean Architecture". + + +- `src/main/java/com/votacao/desafiovotacao`: Contém o código principal do aplicativo. + - `application`: Contém controladores e DTOs. + - `domain`:Contém entidades, serviços e exceções. + - `infra`: Contém as interfaces dos repositórios. + + +### Prerequisitos + +- Java 23 +- Maven +- MongoDB +- Docker +- Docker Compose + + +### Explicação breve do porquê das escolhas tomadas durante o desenvolvimento da solução + +A escolha de usar o design DDD e o clean architecture foi feita para facilitar a manutenção e a escalabilidade do projeto. +Tendo sempre em vista uma boa organização para o bom entendimento do fluxo de dados e ações, o que +permite que o código seja facilmente compreendido e alterado. + +Com isso em mente o projeto foi divido em 3 camadas, a de aplicação onde ficam os controladores e os DTOS +que se comunicam com a camada de domínio, onde ficam as entidades, serviços e exceções. +E os serviços se utilizam das entidade e dos repositórios que estão na camada de infraestrutura. +E as entidades são o núcleo do projeto, onde ficam as regras de negócio. + +O uso do MongoDB foi escolhido por ser um banco de dados NoSQL que é mais performático que um banco relacional, isso ocorre devido +ao MongoDB ser um banco de dados CP (do Teorema CAP) o que oferece consistência e tolerância à partição em detrimento da disponibilidade. + +Portanto o uso do MongoDB é ideal para o cenário de votação permite que que existam centenas de milhares de votos, conforme exigido +pela "Tarefa Bônus 2 - Performance". + +Foi criado o serviço para validar o CPF informado conforme solicitado na "Tarefa Bônus 1 - Validação do Associado". +Contudo o quesito de retornar "ABLE_TO_VOTE" ou "UNABLE_TO_VOTE" não é utilizado na criação de associado, há apenas +um endpoint para sua utilização, tendo em vista que não há razão para colocar algo aleatório no código. Ao contrário da +validação do CPF que foi adicionado ao código para garantir que não há CPFs inválidos no banco de dados. + + +A atualização de se uma sessão está aberta ou encerrada se dá quando ela é procurada. + + + +### Versionamento da API + +"Como você versionaria a API da sua aplicação? Que estratégia usar?" + +A versão da API pode ser controlada por meio da URL. Aconselhável quando a API sofre mudanças significativas. +Por exemplo, a versão 1 da API seria acessada através de `/api/v1/`. + +Outra estratégia seria usar o content-type para controlar a versão da API, +por exemplo: content-type: `application/vnd.api.v1+json`. Aconselhável quando a API sofre +mudanças incrementais ou mesmo mundanças de no conteúdo trocado com o cliente. + +Portanto, eu versionaria a API conforme a mundança que será feita no código. From c75e6187edbce56cebe0eb30b873fc33b55d7890 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 13 Dec 2024 18:02:48 -0300 Subject: [PATCH 20/33] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactored=20code=20?= =?UTF-8?q?creating=20a=20global=20exception=20handler=20and=20cleaning=20?= =?UTF-8?q?the=20controllers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/AgendaController.java | 5 -- .../controllers/AssociatedController.java | 13 ++--- .../controllers/CPFValidationController.java | 10 ++-- .../controllers/GlobalExceptionHandler.java | 53 +++++++++++++++++++ .../controllers/SessionController.java | 11 +--- .../controllers/VoteController.java | 17 +----- .../domain/services/AssociatedService.java | 15 ++++-- .../domain/services/CPFValidationService.java | 1 + .../domain/services/VoteService.java | 3 +- 9 files changed, 78 insertions(+), 50 deletions(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java index d78766c..095e1e4 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AgendaController.java @@ -19,12 +19,7 @@ public class AgendaController { @PostMapping @CrossOrigin("*") public ResponseEntity createAgenda(@RequestBody AgendaDTO agendaDTO) throws AgendaNotValidException { - try { return new ResponseEntity<>(agendaService.createAgenda(agendaDTO), HttpStatus.CREATED); - - } catch (AgendaNotValidException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } } @GetMapping diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java index ed5c3b5..7e3034d 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/AssociatedController.java @@ -20,16 +20,9 @@ public class AssociatedController { @PostMapping("/create") @CrossOrigin("*") - public ResponseEntity createAssociated(@RequestBody AssociatedDTO associatedDTO) { - try { - return new ResponseEntity<>(associatedService.createAssociated(associatedDTO), HttpStatus.CREATED); - } catch (NameNeededException e) { - return new ResponseEntity<>(e.getMessage() ,HttpStatus.BAD_REQUEST); - } catch (CPFInvalidException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } catch (CPFAlreadyExistsException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } + public ResponseEntity createAssociated(@RequestBody AssociatedDTO associatedDTO) + throws NameNeededException, CPFInvalidException, CPFAlreadyExistsException { + return new ResponseEntity<>(associatedService.createAssociated(associatedDTO), HttpStatus.CREATED); } @GetMapping("/id") diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java index 2f6bb01..19376e4 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/CPFValidationController.java @@ -4,10 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.Map; @@ -20,7 +17,8 @@ public class CPFValidationController { private CPFValidationService cpfValidationService; @GetMapping("") - public Object validateCpf(@RequestParam String cpf) { + @CrossOrigin("*") + public ResponseEntity validateCpf(@RequestParam String cpf) { if(!cpfValidationService.isValid(cpf)) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("CPF inválido"); @@ -33,5 +31,7 @@ public Object validateCpf(@RequestParam String cpf) { return ResponseEntity.ok(response); } return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } } + diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java new file mode 100644 index 0000000..7416948 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java @@ -0,0 +1,53 @@ +package com.votacao.desafiovotacao.application.controllers; + +import com.votacao.desafiovotacao.domain.exceptions.*; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(CPFAlreadyExistsException.class) + public ResponseEntity handleCPFAlreadyExistsException(final CPFAlreadyExistsException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(NameNeededException.class) + public ResponseEntity handleNameNeededException(final CPFAlreadyExistsException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(CPFInvalidException.class) + public ResponseEntity handleCPFInvalidException(final CPFAlreadyExistsException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(AgendaNotValidException.class) + public ResponseEntity handleAgendaNotValidException(final AgendaNotValidException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(AgendaNotFoundException.class) + public ResponseEntity handleAgendaNotFoundException(final AgendaNotFoundException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(SessionTimeException.class) + public ResponseEntity handleSessionTimeException(final SessionTimeException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(NumberFormatException.class) + public ResponseEntity handleNumberFormatException(final NumberFormatException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(AlreadyVotedException.class) + public ResponseEntity handleAlreadyVotedException(final AlreadyVotedException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + @ExceptionHandler(NoSessionToVoteException.class) + public ResponseEntity handleNoSessionToVoteException(final NoSessionToVoteException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } +} + + diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java index 2599710..a8c93f5 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/SessionController.java @@ -23,16 +23,9 @@ public class SessionController { @PostMapping("/create") @CrossOrigin("*") - public ResponseEntity createSession(@RequestBody SessionDTO sessionDTO) { - try { + public ResponseEntity createSession(@RequestBody SessionDTO sessionDTO) + throws AgendaNotFoundException, SessionTimeException { return new ResponseEntity<>(sessionService.createSession(sessionDTO), HttpStatus.CREATED); - } catch (AgendaNotFoundException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } catch (SessionTimeException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } catch (NumberFormatException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } } @GetMapping("/id") diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java index c60dea9..a23dc14 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/VoteController.java @@ -3,8 +3,6 @@ import com.votacao.desafiovotacao.application.dtos.VoteDTO; import com.votacao.desafiovotacao.domain.exceptions.AlreadyVotedException; import com.votacao.desafiovotacao.domain.exceptions.NoSessionToVoteException; -import com.votacao.desafiovotacao.domain.services.AgendaService; -import com.votacao.desafiovotacao.domain.services.SessionService; import com.votacao.desafiovotacao.domain.services.VoteService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -16,25 +14,14 @@ @RequestMapping("/agenda/session/associated") public class VoteController { - @Autowired - private AgendaService agendaService; - - @Autowired - private SessionService sessionService; - @Autowired private VoteService voteService; @PostMapping("/vote") @CrossOrigin("*") - public ResponseEntity registerVote(@RequestBody VoteDTO voteDTO) throws AlreadyVotedException, NoSessionToVoteException { - try { + public ResponseEntity registerVote(@RequestBody VoteDTO voteDTO) + throws AlreadyVotedException, NoSessionToVoteException { return new ResponseEntity<>(voteService.registerVote(voteDTO), HttpStatus.OK); - } catch (AlreadyVotedException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } catch (NoSessionToVoteException e) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); - } } @GetMapping("/result") diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java index b8aabeb..fae9309 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java @@ -1,11 +1,13 @@ package com.votacao.desafiovotacao.domain.services; import com.votacao.desafiovotacao.application.dtos.AssociatedDTO; +import com.votacao.desafiovotacao.domain.entities.Agenda; import com.votacao.desafiovotacao.domain.entities.Associated; import com.votacao.desafiovotacao.domain.exceptions.CPFAlreadyExistsException; import com.votacao.desafiovotacao.domain.exceptions.CPFInvalidException; import com.votacao.desafiovotacao.domain.exceptions.NameNeededException; import com.votacao.desafiovotacao.infra.AssociatedRepository; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -41,11 +43,14 @@ public Associated createAssociated(AssociatedDTO associatedDTO) throw new CPFAlreadyExistsException(); } - Associated associated = Associated.builder() - .id(associatedDTO.id()) - .cpf(associatedDTO.cpf()) - .name(associatedDTO.name()) - .build(); + Associated associated = Associated.builder().build(); + BeanUtils.copyProperties(associatedDTO, associated); + +// Associated associated = Associated.builder() +// .id(associatedDTO.id()) +// .cpf(associatedDTO.cpf()) +// .name(associatedDTO.name()) +// .build(); return associatedRepository.save(associated); } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java index 870b1f4..a88bdce 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/CPFValidationService.java @@ -1,5 +1,6 @@ package com.votacao.desafiovotacao.domain.services; +import com.votacao.desafiovotacao.domain.exceptions.CPFInvalidException; import org.springframework.stereotype.Service; import java.util.Random; diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java index f3de6e6..35c8048 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java @@ -27,7 +27,8 @@ public class VoteService { @Autowired private SessionService sessionService; - public Optional registerVote(VoteDTO voteDTO) throws AlreadyVotedException, NoSessionToVoteException { + public Optional registerVote(VoteDTO voteDTO) + throws AlreadyVotedException, NoSessionToVoteException { if (validateAssociateAlreadyVote(sessionService.get(voteDTO.sessionId()), voteDTO.associatedId())) { throw new AlreadyVotedException(); From 89817bcfe24f0326581a798221251a9c7ddf7e8f Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Fri, 13 Dec 2024 18:04:49 -0300 Subject: [PATCH 21/33] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactored=20agenda?= =?UTF-8?q?=20service=20using=20beanUtils=20cleaning=20the=20createAgenda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/services/AgendaService.java | 12 +++++------- .../domain/services/AssociatedService.java | 5 ----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java index 360363c..24783b0 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java @@ -4,6 +4,7 @@ import com.votacao.desafiovotacao.domain.entities.Agenda; import com.votacao.desafiovotacao.domain.exceptions.AgendaNotValidException; import com.votacao.desafiovotacao.infra.AgendaRepository; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -18,14 +19,11 @@ public class AgendaService { public Agenda createAgenda(AgendaDTO agendaDTO) throws AgendaNotValidException { - if (agendaDTO.description().isEmpty()) throw new AgendaNotValidException(); + if (agendaDTO.description().isEmpty() || agendaDTO.id().isEmpty()) throw new AgendaNotValidException(); + + Agenda agenda = Agenda.builder().build(); + BeanUtils.copyProperties(agendaDTO, agenda); - Agenda agenda = Agenda.builder() - .id(agendaDTO.id()) - .title(agendaDTO.title()) - .description(agendaDTO.description()) - .status(agendaDTO.status()) - .build(); return agendaRepository.save(agenda); } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java index fae9309..31425cd 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AssociatedService.java @@ -46,11 +46,6 @@ public Associated createAssociated(AssociatedDTO associatedDTO) Associated associated = Associated.builder().build(); BeanUtils.copyProperties(associatedDTO, associated); -// Associated associated = Associated.builder() -// .id(associatedDTO.id()) -// .cpf(associatedDTO.cpf()) -// .name(associatedDTO.name()) -// .build(); return associatedRepository.save(associated); } From 987e516bf92569641331008c9a3c523d5407bd85 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sat, 14 Dec 2024 17:38:34 -0300 Subject: [PATCH 22/33] =?UTF-8?q?=F0=9F=9A=A7=20started=20integration=20wi?= =?UTF-8?q?th=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 4 +- frontend/package-lock.json | 100 ++++++++++++++++++ frontend/package.json | 1 + frontend/public/vite.svg | 1 - frontend/src/Agenda/index.tsx | 58 ---------- frontend/src/App.css | 42 -------- frontend/src/App.tsx | 10 +- frontend/src/Vote/result.tsx | 49 --------- frontend/src/assets/react.svg | 1 - frontend/src/axios/config.ts | 10 ++ frontend/src/page.tsx | 35 ------ frontend/src/pages/Agenda/index.tsx | 79 ++++++++++++++ .../Associated}/index.tsx | 37 +++++-- frontend/src/{ => pages}/Session/index.tsx | 28 ++++- frontend/src/{ => pages}/Vote/index.tsx | 65 +++++++++--- frontend/src/pages/Vote/result.tsx | 95 +++++++++++++++++ 16 files changed, 396 insertions(+), 219 deletions(-) delete mode 100644 frontend/public/vite.svg delete mode 100644 frontend/src/Agenda/index.tsx delete mode 100644 frontend/src/App.css delete mode 100644 frontend/src/Vote/result.tsx delete mode 100644 frontend/src/assets/react.svg create mode 100644 frontend/src/axios/config.ts delete mode 100644 frontend/src/page.tsx create mode 100644 frontend/src/pages/Agenda/index.tsx rename frontend/src/{associated => pages/Associated}/index.tsx (53%) rename frontend/src/{ => pages}/Session/index.tsx (70%) rename frontend/src/{ => pages}/Vote/index.tsx (65%) create mode 100644 frontend/src/pages/Vote/result.tsx diff --git a/frontend/index.html b/frontend/index.html index e4b78ea..84f14e2 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - Vite + React + TS + Votação
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 165f4d6..9b11201 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/material": "^6.2.0", + "axios": "^1.7.9", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.0.2" @@ -2067,6 +2068,23 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -2222,6 +2240,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2315,6 +2345,15 @@ "dev": true, "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -2711,6 +2750,40 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3082,6 +3155,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3333,6 +3427,12 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 17e5d4b..9bb272e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/material": "^6.2.0", + "axios": "^1.7.9", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.0.2" diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/frontend/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/Agenda/index.tsx b/frontend/src/Agenda/index.tsx deleted file mode 100644 index 44401f0..0000000 --- a/frontend/src/Agenda/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client"; -import {Box, Button, Grid2, Stack, TextField} from "@mui/material"; -import MainContainer from "../ui/Util/main-container.tsx"; -import ReturnButton from "../ui/Util/returnButton.tsx"; - -export default function Agenda() { - - - return ( - - - -

Criar uma Pauta

-
- - - - - - - - - - - - - - - - - - - -
- ); -} \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index 8a02e97..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -/*#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 index 1da195e..1ccde5b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5,12 +5,12 @@ import { Route, Routes, } from "react-router-dom"; -import Associated from "./associated"; -import Agenda from "./Agenda"; -import Session from "./Session"; +import Associated from "./pages/Associated"; +import Agenda from "./pages/Agenda"; +import Session from "./pages/Session"; import Home from "./Home"; -import Vote from "./Vote"; -import VoteResult from "./Vote/result.tsx"; +import Vote from "./pages/Vote"; +import VoteResult from "./pages/Vote/result.tsx"; export function App() { diff --git a/frontend/src/Vote/result.tsx b/frontend/src/Vote/result.tsx deleted file mode 100644 index ae4ed55..0000000 --- a/frontend/src/Vote/result.tsx +++ /dev/null @@ -1,49 +0,0 @@ -"use client"; -import { - Box, - Grid2, - Stack, - Typography -} from "@mui/material"; -import MainContainer from "../ui/Util/main-container.tsx"; -import ReturnButton from "../ui/Util/returnButton.tsx"; - -export default function VoteResult() { - - - return ( - - - -

Resultado das Votações

-
- - - - - - Aqui você pode conferir o resultado de todas as votações realizadas. - - - - - - - - - - - - - - - - -
- ); -} \ No newline at end of file diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/frontend/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/axios/config.ts b/frontend/src/axios/config.ts new file mode 100644 index 0000000..df2706b --- /dev/null +++ b/frontend/src/axios/config.ts @@ -0,0 +1,10 @@ +import axios from 'axios'; + +const apiFetch = axios.create({ + baseURL: 'http://localhost:8080', + headers: { + 'Content-Type': 'application/json', + }, +}); + +export default apiFetch; \ No newline at end of file diff --git a/frontend/src/page.tsx b/frontend/src/page.tsx deleted file mode 100644 index ba9d5c7..0000000 --- a/frontend/src/page.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' -import './App.css' - -function App() { - const [count, setCount] = useState(0) - - 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/pages/Agenda/index.tsx b/frontend/src/pages/Agenda/index.tsx new file mode 100644 index 0000000..24a3d90 --- /dev/null +++ b/frontend/src/pages/Agenda/index.tsx @@ -0,0 +1,79 @@ +"use client"; +import {FormEvent, useState} from "react"; +import {Box, Button, Grid2, Stack, TextField} from "@mui/material"; +import MainContainer from "../../ui/Util/main-container.tsx"; +import ReturnButton from "../../ui/Util/returnButton.tsx"; +import apiFetch from "../../axios/config"; + + +export default function Agenda() { + + const [id, setId] = useState(""); + const [title, setTitle] = useState(""); + const [description, setDescription] = useState(""); + + const createAgenda = async (e: FormEvent) => { + e.preventDefault() + console.log('Criando pauta...') + console.log('ID: ', id) + console.log('Título: ', title) + console.log('Descrição: ', description) + + const response = {id, title, description}; + await apiFetch.post('/agenda', { + body: response, + }); + } + + return ( + + + +

Criar uma Pauta

+
+ + + + + setId(e.target.value)} + /> + + setTitle(e.target.value)} + /> + + setDescription(e.target.value)} + /> + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/associated/index.tsx b/frontend/src/pages/Associated/index.tsx similarity index 53% rename from frontend/src/associated/index.tsx rename to frontend/src/pages/Associated/index.tsx index bac4e2b..967b1fa 100644 --- a/frontend/src/associated/index.tsx +++ b/frontend/src/pages/Associated/index.tsx @@ -1,10 +1,28 @@ "use client"; import {Box, Button, Grid2, Stack, TextField} from "@mui/material"; -import MainContainer from "../ui/Util/main-container.tsx"; -import ReturnButton from "../ui/Util/returnButton.tsx"; +import MainContainer from "../../ui/Util/main-container.tsx"; +import ReturnButton from "../../ui/Util/returnButton.tsx"; +import apiFetch from "../../axios/config"; +import {FormEvent, useState} from "react"; export default function Associated() { + const [id, setId] = useState(""); + const [name, setName] = useState(""); + const [cpf, setCPF] = useState(""); + + const createAssociated = async (e: FormEvent) => { + e.preventDefault() + console.log('Criando associado...') + console.log('ID: ', id) + console.log('Nome: ', name) + console.log('Descrição: ', cpf) + + const response = {id, name, cpf}; + await apiFetch.post('/associated', { + body: response, + }); + } return ( @@ -18,6 +36,7 @@ export default function Associated() { sx={{'& .MuiTextField-root': {m: 1, width: '45ch'}}} noValidate autoComplete="off" + onSubmit={createAssociated} > @@ -26,21 +45,27 @@ export default function Associated() { id="filled-required" label="ID do Associado" variant="filled" - fullWidth /> + fullWidth + onChange={(e) => setId(e.target.value)} + /> + variant="filled" + onChange={(e) => setName(e.target.value)} + /> + variant="filled" + onChange={(e) => setCPF(e.target.value)} + /> - diff --git a/frontend/src/Session/index.tsx b/frontend/src/pages/Session/index.tsx similarity index 70% rename from frontend/src/Session/index.tsx rename to frontend/src/pages/Session/index.tsx index 72f9426..9b55f61 100644 --- a/frontend/src/Session/index.tsx +++ b/frontend/src/pages/Session/index.tsx @@ -1,10 +1,28 @@ "use client"; import {Box, Button, Grid2, Stack, TextField, Typography} from "@mui/material"; -import MainContainer from "../ui/Util/main-container.tsx"; -import ReturnButton from "../ui/Util/returnButton.tsx"; +import MainContainer from "../../ui/Util/main-container.tsx"; +import ReturnButton from "../../ui/Util/returnButton.tsx"; +import {FormEvent, useState} from "react"; +import apiFetch from "../../axios/config.ts"; export default function Agenda() { + const [id, setId] = useState(""); + const [agendaId, setAgendaId] = useState(""); + const [duration, setDuration] = useState(""); + + const createSession = async (e: FormEvent) => { + e.preventDefault() + console.log('Criando sessão...') + console.log('ID da Sessão: ', id) + console.log('ID da Pauta: ', agendaId) + console.log('Duração: ', duration) + + const response = {id, agendaId, duration}; + await apiFetch.post('/associated', { + body: response, + }); + } return ( @@ -18,6 +36,7 @@ export default function Agenda() { sx={{'& .MuiTextField-root': {m: 1, width: '45ch'}}} noValidate autoComplete="off" + onSubmit={createSession} > @@ -35,12 +54,14 @@ export default function Agenda() { id="filled-required" label="ID da Sessão" variant="filled" + onChange={(e) => setId(e.target.value)} /> setAgendaId(e.target.value)} /> setDuration(e.target.value)} /> - diff --git a/frontend/src/Vote/index.tsx b/frontend/src/pages/Vote/index.tsx similarity index 65% rename from frontend/src/Vote/index.tsx rename to frontend/src/pages/Vote/index.tsx index bdce379..08c1971 100644 --- a/frontend/src/Vote/index.tsx +++ b/frontend/src/pages/Vote/index.tsx @@ -11,17 +11,33 @@ import { TextField, Typography } from "@mui/material"; -import MainContainer from "../ui/Util/main-container.tsx"; -import ReturnButton from "../ui/Util/returnButton.tsx"; -import React from "react"; +import MainContainer from "../../ui/Util/main-container.tsx"; +import ReturnButton from "../../ui/Util/returnButton.tsx"; +import {FormEvent, useState} from "react"; import {green, pink} from "@mui/material/colors"; +import apiFetch from "../../axios/config.ts"; -export default function Agenda() { - const [value, setValue] = React.useState('female'); +export default function Vote() { + const [id, setId] = useState(""); + const [associatedId, setAssociatedId] = useState(""); + const [agendaId, setAgendaId] = useState(""); + const [sessionId, setSessionId] = useState(""); + const [vote, setVote] = useState(""); - const handleChange = (event: React.ChangeEvent) => { - setValue((event.target as HTMLInputElement).value); - }; + const registerVote = async (e: FormEvent) => { + e.preventDefault() + console.log('Votando ...') + console.log('ID do Voto: ', id) + console.log('ID do Associado: ', id) + console.log('ID da Pauta: ', agendaId) + console.log('ID da Sessão: ', id) + console.log('Voto: ', vote) + + const response = {id, associatedId, agendaId, sessionId, vote}; + await apiFetch.post('/associated', { + body: response, + }); + } return ( @@ -35,6 +51,7 @@ export default function Agenda() { sx={{'& .MuiTextField-root': {m: 1, width: '45ch'}}} noValidate autoComplete="off" + onSubmit={registerVote} > @@ -49,23 +66,33 @@ export default function Agenda() { + setId(e.target.value)} + /> setAssociatedId(e.target.value)} /> setAgendaId(e.target.value)} /> setSessionId(e.target.value)} /> + variant="filled" + /> @@ -93,25 +121,28 @@ export default function Agenda() { setVote(e.target.value)} > - } label="SIM" /> - } label="SIM" + onChange={() => setVote("SIM")} + /> + } label="NÃO" /> - + }}/>} label="NÃO" + onChange={() => setVote("NÃO")} + /> + - diff --git a/frontend/src/pages/Vote/result.tsx b/frontend/src/pages/Vote/result.tsx new file mode 100644 index 0000000..1dd9ad2 --- /dev/null +++ b/frontend/src/pages/Vote/result.tsx @@ -0,0 +1,95 @@ +"use client"; +import apiFetch from "../../axios/config"; +import { + Box, Button, + Grid2, + Stack, TextField, + Typography +} from "@mui/material"; +import MainContainer from "../../ui/Util/main-container.tsx"; +import ReturnButton from "../../ui/Util/returnButton.tsx"; +import {useState} from "react"; + +export default function VoteResult() { + const [sessionId, setSessionId] = useState(""); + const [voteResult, setVoteResult] = useState<{ voteYes: string; voteNo: string; totalVotes: string }[]>([]); + + const getVoteResult = async () => { + setVoteResult([]); // Reset voteResult state + try { + const response = await apiFetch.get(`/agenda/session/associated/result`, { + params: { sessionId } + }); + + const data = response.data.body; + setVoteResult(data); + + // { voteYes: "2", voteNo: "1", totalVotes: "3" } + console.log(voteResult); + console.log(voteResult.length); + + console.log(data); + } catch (e) { + console.error(e); + } + }; + + return ( + + +

Resultado das Votações

+
+ + + + + setSessionId(e.target.value)} + /> + + + + {voteResult.length === 0 ? ( + +


+ Nenhuma votação foi realizada. +


+
+ ) : ( + +


+ Votos Sim: {voteResult[1]?.voteYes}
+ Votos Não: {voteResult[0]?.voteNo}
+ Total de Votos: {voteResult[0]?.totalVotes} +
+ )} +
+
+
+ + + + + + +
+
+ ); +} \ No newline at end of file From 28e17aeaa1d40736b87dc314af08e43579560993 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sat, 14 Dec 2024 18:27:36 -0300 Subject: [PATCH 23/33] =?UTF-8?q?=E2=9C=A8=20added=20favicon=20and=20integ?= =?UTF-8?q?ration=20with=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/public/favicon.jpg | Bin 0 -> 29751 bytes frontend/src/pages/Vote/index.tsx | 2 +- frontend/src/pages/Vote/result.tsx | 54 +++++++++++++++++++---------- 3 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 frontend/public/favicon.jpg diff --git a/frontend/public/favicon.jpg b/frontend/public/favicon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d6d888e903394ad3ab343fd49948cf526496b06 GIT binary patch literal 29751 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 SPZh*!L([]); + // const [voteResult, setVoteResult] = useState([]); + const [voteResult, setVoteResult] = useState(null); + + interface VoteResult { + voteYes: number; + voteNo: number; + totalVotes: number; + } const getVoteResult = async () => { - setVoteResult([]); // Reset voteResult state + // setVoteResult([]); // Reset voteResult state try { const response = await apiFetch.get(`/agenda/session/associated/result`, { params: { sessionId } - }); - + }).then((response) => response); const data = response.data.body; setVoteResult(data); // { voteYes: "2", voteNo: "1", totalVotes: "3" } - console.log(voteResult); - console.log(voteResult.length); - console.log(data); + + return data; } catch (e) { console.error(e); } @@ -66,20 +71,33 @@ export default function VoteResult() { - {voteResult.length === 0 ? ( - + {voteResult?.totalVotes === "0" || voteResult === null ? + (


Nenhuma votação foi realizada.


-
- ) : ( - -


- Votos Sim: {voteResult[1]?.voteYes}
- Votos Não: {voteResult[0]?.voteNo}
- Total de Votos: {voteResult[0]?.totalVotes} -
- )} +
) + : + ( + + Resultado da votação: + + + Votos Sim: {voteResult.voteYes} + + + Votos Não: {voteResult.voteNo} + + + Total de Votos: {voteResult.totalVotes} + + + Resultado da votação: {voteResult?.voteYes > voteResult?.voteNo ? "Aprovado" : "Reprovado"} + +
+
+
) + }
From c41efef63dc00339d29ef97943aa31e91a244867 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sat, 14 Dec 2024 18:34:47 -0300 Subject: [PATCH 24/33] =?UTF-8?q?=20=E2=99=BB=EF=B8=8F=20refactored=20a=20?= =?UTF-8?q?comparison=20in=20result.tsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Vote/result.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Vote/result.tsx b/frontend/src/pages/Vote/result.tsx index 0417bec..c498af3 100644 --- a/frontend/src/pages/Vote/result.tsx +++ b/frontend/src/pages/Vote/result.tsx @@ -71,7 +71,7 @@ export default function VoteResult() { - {voteResult?.totalVotes === "0" || voteResult === null ? + {voteResult?.totalVotes == 0 || voteResult === null ? (


Nenhuma votação foi realizada. From ba613b72e640584796f7ec750fefe6b3305cefac Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 12:44:32 -0300 Subject: [PATCH 25/33] =?UTF-8?q?=F0=9F=90=9B=20fixed=20bug=20from=20agend?= =?UTF-8?q?a=20status=20being=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../votacao/desafiovotacao/domain/services/AgendaService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java index 24783b0..a7e2430 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/AgendaService.java @@ -24,6 +24,10 @@ public Agenda createAgenda(AgendaDTO agendaDTO) throws AgendaNotValidException { Agenda agenda = Agenda.builder().build(); BeanUtils.copyProperties(agendaDTO, agenda); + if(agendaDTO.status() == null) { + agenda.setStatus("aberta"); + } + return agendaRepository.save(agenda); } From e462f4c61a894eb8ce1e5f00f68cfa7a96e47147 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 16:09:58 -0300 Subject: [PATCH 26/33] =?UTF-8?q?=F0=9F=90=9B=20fixed=20some=20bugs=20in?= =?UTF-8?q?=20the=20pages=20integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adjusted favicon --- frontend/index.html | 2 +- frontend/src/Home/index.tsx | 16 +- frontend/src/pages/Agenda/index.tsx | 76 ++++---- frontend/src/pages/Associated/index.tsx | 94 +++++----- frontend/src/pages/Session/index.tsx | 95 +++++++--- frontend/src/pages/Vote/index.tsx | 229 +++++++++++++++++++----- frontend/src/pages/Vote/result.tsx | 20 +-- 7 files changed, 351 insertions(+), 181 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 84f14e2..2d25fa9 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,7 @@ - + Votação diff --git a/frontend/src/Home/index.tsx b/frontend/src/Home/index.tsx index de4ce1a..ab3564e 100644 --- a/frontend/src/Home/index.tsx +++ b/frontend/src/Home/index.tsx @@ -1,6 +1,6 @@ "use client"; import React from "react"; -import {Box, Button, Grid2, Paper, Stack} from "@mui/material"; +import {Alert, Box, Button, Grid2, Paper, Stack} from "@mui/material"; import {Navigate} from "react-router-dom"; import MainContainer from "../ui/Util/main-container.tsx"; @@ -16,25 +16,19 @@ function Home() { if (goToAssociated) { return ; } - if (goToAgenda) { return ; } - if (goToSession) { return } - if (goToVote) { return } - if (goToVoteResult) { return } - - return ( @@ -71,7 +65,6 @@ function Home() { setGoToAgenda(true); }} > - Criar uma PAUTA @@ -81,7 +74,6 @@ function Home() { setGoToSession(true); }} > - Abrir uma Sessão para votação @@ -91,7 +83,6 @@ function Home() { setGoToVote(true); }} > - Votar @@ -102,11 +93,12 @@ function Home() { setGoToVoteResult(true); }} > - Resultado das Votações - + + Os IDs exigidos nesse aplicação não possuem restrição de número ou letra. +
diff --git a/frontend/src/pages/Agenda/index.tsx b/frontend/src/pages/Agenda/index.tsx index 24a3d90..ff63433 100644 --- a/frontend/src/pages/Agenda/index.tsx +++ b/frontend/src/pages/Agenda/index.tsx @@ -20,18 +20,21 @@ export default function Agenda() { console.log('Descrição: ', description) const response = {id, title, description}; - await apiFetch.post('/agenda', { - body: response, - }); + try { + await apiFetch.post('/agenda', + response + ); + console.log('Pauta criada com sucesso!') + } catch (e) { + console.error(e) + } } return ( -

Criar uma Pauta

- - - setId(e.target.value)} - /> - - setTitle(e.target.value)} - /> - - setDescription(e.target.value)} - /> - - - - - + + setId(e.target.value)} + /> + setTitle(e.target.value)} + /> + setDescription(e.target.value)} + /> + + +
diff --git a/frontend/src/pages/Associated/index.tsx b/frontend/src/pages/Associated/index.tsx index 967b1fa..066cae5 100644 --- a/frontend/src/pages/Associated/index.tsx +++ b/frontend/src/pages/Associated/index.tsx @@ -17,65 +17,59 @@ export default function Associated() { console.log('ID: ', id) console.log('Nome: ', name) console.log('Descrição: ', cpf) - - const response = {id, name, cpf}; - await apiFetch.post('/associated', { - body: response, - }); + try { + await apiFetch.post('/associated/create', {id, name, cpf}) + console.log('Associado criado com sucesso!') + } catch (e) { + console.error(e) + } } - return ( - - - + return ( + +

Criar Associado

-
- - + + > - - setId(e.target.value)} - /> - - setName(e.target.value)} - /> - - setCPF(e.target.value)} - /> - - - - - - + + setId(e.target.value)} + /> + setName(e.target.value)} + /> + setCPF(e.target.value)} + /> + + + - - -
- ); +
+ ); } \ No newline at end of file diff --git a/frontend/src/pages/Session/index.tsx b/frontend/src/pages/Session/index.tsx index 9b55f61..eb98a51 100644 --- a/frontend/src/pages/Session/index.tsx +++ b/frontend/src/pages/Session/index.tsx @@ -1,12 +1,15 @@ "use client"; -import {Box, Button, Grid2, Stack, TextField, Typography} from "@mui/material"; +import {Alert, Box, Button, Grid2, Stack, TextField, Typography} from "@mui/material"; import MainContainer from "../../ui/Util/main-container.tsx"; import ReturnButton from "../../ui/Util/returnButton.tsx"; import {FormEvent, useState} from "react"; import apiFetch from "../../axios/config.ts"; -export default function Agenda() { +export default function Session() { + const [alertSession, setAlertSession] = useState(false); + const [alertAgenda, setAlertAgenda] = useState(false); + const [alertAgendaNotFound, setAlertAgendaNotFound] = useState(false); const [id, setId] = useState(""); const [agendaId, setAgendaId] = useState(""); const [duration, setDuration] = useState(""); @@ -18,14 +21,42 @@ export default function Agenda() { console.log('ID da Pauta: ', agendaId) console.log('Duração: ', duration) + setAlertSession(false); + setAlertAgenda(false); + setAlertAgendaNotFound(false); + + if (!id) { + console.log('session Id is required'); + setAlertSession(true); + } + + if (!agendaId) { + console.log('session Id is required'); + setAlertAgenda(true); + } + + const agendaResponse = await apiFetch.get(`/agenda/id`, { + params: {id: agendaId} + } + ); + if (!agendaResponse.data && agendaId) { + console.log('Agenda not found'); + setAlertAgendaNotFound(true); + } + const response = {id, agendaId, duration}; - await apiFetch.post('/associated', { - body: response, - }); + console.log(response); + try { + await apiFetch.post('/session/create', + response + ); + console.log('Sessão criada com sucesso!') + } catch (e) { + console.error(e) + } } return ( -

Abrir uma Sessão de Votação

@@ -39,56 +70,74 @@ export default function Agenda() { onSubmit={createSession} > - - - Para abrir uma sessão de votação, é necessário informar o ID da sessão e o ID da pauta. - + + + Para abrir uma sessão de votação, é necessário informar o ID da sessão e o ID da pauta. + - + - + setId(e.target.value)} /> + {alertSession ? + <> + Um ID de sessão válida é importante. +
+ : "" + } setAgendaId(e.target.value)} /> + {alertAgenda ? + <> + Um ID de pauta válida é importante. +
+ : "" + } + {alertAgendaNotFound ? + <> + Pauta não encontrada. +
+ : "" + } setDuration(e.target.value)} /> - + + O valor mínimo para a duração da sessão é de 1 minuto. + - - +
-
- -
); } \ No newline at end of file diff --git a/frontend/src/pages/Vote/index.tsx b/frontend/src/pages/Vote/index.tsx index 268ee44..af66e34 100644 --- a/frontend/src/pages/Vote/index.tsx +++ b/frontend/src/pages/Vote/index.tsx @@ -1,5 +1,6 @@ "use client"; import { + Alert, Box, Button, FormControl, @@ -18,34 +19,138 @@ import {green, pink} from "@mui/material/colors"; import apiFetch from "../../axios/config.ts"; export default function Vote() { - const [id, setId] = useState(""); + const [voteId, setId] = useState(""); const [associatedId, setAssociatedId] = useState(""); const [agendaId, setAgendaId] = useState(""); const [sessionId, setSessionId] = useState(""); const [vote, setVote] = useState(""); + const [alertSession, setAlertSession] = useState(false); + const [alertAgenda, setAlertAgenda] = useState(false); + const [alertAssociated, setAlertAssociated] = useState(false); + const [alertVote, setAlertVote] = useState(false); + const [alertVoteChoice, setAlertVoteChoice] = useState(false); + const [alertAgendaNotFound, setAlertAgendaNotFound] = useState(false); + const [showDescription, setShowDescription] = useState(false); + const [descriptionState, setDescriptionState] = useState(""); const registerVote = async (e: FormEvent) => { e.preventDefault() console.log('Votando ...') - console.log('ID do Voto: ', id) - console.log('ID do Associado: ', id) + console.log('ID do Voto: ', voteId) + console.log('ID do Associado: ', associatedId) console.log('ID da Pauta: ', agendaId) - console.log('ID da Sessão: ', id) + console.log('ID da Sessão: ', sessionId) console.log('Voto: ', vote) - const response = {id, associatedId, agendaId, sessionId, vote}; - await apiFetch.post('/agenda/session/associated/vote', { - body: response, - }); + setAlertVote(false); + setAlertVoteChoice(false); + setAlertSession(false); + setAlertAgenda(false); + + if (!voteId) { + console.log('vote Id is required'); + setAlertVote(true); + return; + } + if (!vote) { + console.log('voting is the reason of this page!!!'); + setAlertVoteChoice(true); + return; + } + + if (!sessionId) { + console.log('session Id is required'); + setAlertSession(true); + return; + } else { + const sessionResponse = await apiFetch.get(`/session/id`, { + params: {id: sessionId} + } + ); + console.log("session response: ", sessionResponse); + if (!sessionResponse.data) { + console.log('Session not found'); + setAlertSession(true); + return; + } + } + if (!agendaId) { + console.log('agenda Id is required'); + setAlertAgenda(true); + return; + } else { + const agendaResponse = await apiFetch.get(`/agenda/id`, { + params: {id: agendaId} + } + ); + console.log("agenda response: ", agendaResponse); + if (!agendaResponse.data) { + console.log('Agenda not found'); + setAlertAgenda(true); + return; + } + } + if (!associatedId) { + console.log('associated Id is required'); + setAlertAssociated(true); + return; + } else { + const AssociatedResponse = await apiFetch.get(`/associated/id`, { + params: {id: associatedId} + } + ); + console.log("associated response: ", AssociatedResponse); + if (!AssociatedResponse.data) { + console.log('Associated not found'); + setAlertAssociated(true); + return; + } + } + + const response = {voteId, agendaId, sessionId, associatedId, vote}; + console.log(response) + try { + await apiFetch.post('/agenda/session/associated/vote', + response + ); + console.log('Voto registrado com sucesso!') + } catch (e) { + console.error(e) + } } + const getDescriptions = async () => { - return ( + const agendaId = (document.getElementById("agendaId") as HTMLInputElement)?.value; + console.log("agendaId123: ", agendaId); + + if (!agendaId) { + setDescriptionState(""); + setShowDescription(false); + return; + } + + setDescriptionState(""); + setShowDescription(false); + const agendaResponse = await apiFetch.get(`/agenda/id`, { + params: {id: agendaId} + } + ); + if (!agendaResponse.data) { + console.log('Agenda not found'); + setAlertAgendaNotFound(true); + setShowDescription(false); + } else { + setShowDescription(true); + setDescriptionState(agendaResponse.data.description.toString()); + } + } + + return (

Votar

- - + Lembre-se que você só pode votar uma única vez, precisando identificar o ID da pauta, da sessão e o seu para que seja possível realizar seu voto. @@ -63,59 +168,86 @@ export default function Vote() { - setId(e.target.value)} /> + {alertVote ? + <> + ID do Voto faltando. +
+ : "" + } setAssociatedId(e.target.value)} /> + {alertAssociated ? + <> + ID do associado é um requisito. +
+ : "" + } setAgendaId(e.target.value)} /> + +
+ {showDescription ? + <>
+ : "" + } + {alertAgenda || alertAgendaNotFound ? + <> + Um ID de pauta válida é importante. +
+ : "" + } setSessionId(e.target.value)} /> - - - - - - Espaço onde deve ir a descrição da pauta que se está votando. - - - - - + {alertSession ? + <> + Um ID de sessão válida é necessário. +
+ : "" + } Votar: setVote(e.target.value)} > - } label="SIM" - onChange={() => setVote("SIM")} + onChange={() => setVote("SIM")} /> } label="NÃO" - onChange={() => setVote("NÃO")} + onChange={() => setVote("NÃO")} /> - + - + {alertVoteChoice ? + <> + Você precisa escolher se vota SIM ou NÃO para registrar o voto. +
+ : "" + } - - +
-
- -
); } \ No newline at end of file diff --git a/frontend/src/pages/Vote/result.tsx b/frontend/src/pages/Vote/result.tsx index c498af3..97e5c55 100644 --- a/frontend/src/pages/Vote/result.tsx +++ b/frontend/src/pages/Vote/result.tsx @@ -1,6 +1,7 @@ "use client"; import apiFetch from "../../axios/config"; import { + Alert, Box, Button, Grid2, Stack, TextField, @@ -12,7 +13,6 @@ import {useState} from "react"; export default function VoteResult() { const [sessionId, setSessionId] = useState(""); - // const [voteResult, setVoteResult] = useState([]); const [voteResult, setVoteResult] = useState(null); interface VoteResult { @@ -22,19 +22,18 @@ export default function VoteResult() { } const getVoteResult = async () => { - // setVoteResult([]); // Reset voteResult state try { const response = await apiFetch.get(`/agenda/session/associated/result`, { - params: { sessionId } + params: {sessionId} }).then((response) => response); const data = response.data.body; setVoteResult(data); - // { voteYes: "2", voteNo: "1", totalVotes: "3" } console.log(data); return data; } catch (e) { + console.error(e); } }; @@ -44,7 +43,6 @@ export default function VoteResult() {

Resultado das Votações

- Pesquisar Resultado - {voteResult?.totalVotes == 0 || voteResult === null ? ( + + Um ID de sessão válida é importante. +


Nenhuma votação foi realizada.


) - : + : ( Resultado da votação: @@ -92,7 +92,8 @@ export default function VoteResult() { Total de Votos: {voteResult.totalVotes} - Resultado da votação: {voteResult?.voteYes > voteResult?.voteNo ? "Aprovado" : "Reprovado"} + Resultado da + votação: {voteResult?.voteYes > voteResult?.voteNo ? "Aprovado" : "Reprovado"}

@@ -101,10 +102,9 @@ export default function VoteResult() {
- - +
From 8d2cf461f80aaf704c62e626423531baa301398a Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 16:12:25 -0300 Subject: [PATCH 27/33] =?UTF-8?q?=F0=9F=90=9B=20fixed=20bug=20in=20the=20v?= =?UTF-8?q?oting=20register=20process?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and changed docker-compose version --- backend/docker-compose.yml | 2 +- .../application/controllers/GlobalExceptionHandler.java | 4 ++++ .../desafiovotacao/domain/exceptions/NoVoteException.java | 7 +++++++ .../desafiovotacao/domain/services/VoteService.java | 7 +++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoVoteException.java diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index 3f55c47..4b0c424 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.2' +version: '3.8' services: diff --git a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java index 7416948..928a02e 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/application/controllers/GlobalExceptionHandler.java @@ -48,6 +48,10 @@ public ResponseEntity handleAlreadyVotedException(final AlreadyVotedException public ResponseEntity handleNoSessionToVoteException(final NoSessionToVoteException e) { return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } + @ExceptionHandler(NoVoteException.class) + public ResponseEntity handleNoVoteException(final NoVoteException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.I_AM_A_TEAPOT); + } } diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoVoteException.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoVoteException.java new file mode 100644 index 0000000..a4bb987 --- /dev/null +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/exceptions/NoVoteException.java @@ -0,0 +1,7 @@ +package com.votacao.desafiovotacao.domain.exceptions; + +public class NoVoteException extends RuntimeException { + public NoVoteException() { + super("Requirement for voting not attended."); + } +} diff --git a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java index 35c8048..87dd7e3 100644 --- a/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java +++ b/backend/src/main/java/com/votacao/desafiovotacao/domain/services/VoteService.java @@ -5,6 +5,7 @@ import com.votacao.desafiovotacao.domain.entities.Vote; import com.votacao.desafiovotacao.domain.exceptions.AlreadyVotedException; import com.votacao.desafiovotacao.domain.exceptions.NoSessionToVoteException; +import com.votacao.desafiovotacao.domain.exceptions.NoVoteException; import com.votacao.desafiovotacao.infra.VoteRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -38,6 +39,12 @@ public Optional registerVote(VoteDTO voteDTO) throw new NoSessionToVoteException(); } + if (voteDTO.vote().isEmpty() || voteDTO.associatedId().isEmpty() + || voteDTO.agendaId().isEmpty() || voteDTO.sessionId().isEmpty() + || voteDTO.voteId().isEmpty()) { + throw new NoVoteException(); + } + Vote vote = Vote.builder() .id(voteDTO.voteId()) .vote(associatedVoteHandler(voteDTO.vote())) From dcffa01251d71d3b19a2b070d378716eccc4bb08 Mon Sep 17 00:00:00 2001 From: Gabriel Wagner Piazenski <100728241+gabrielwp87@users.noreply.github.com> Date: Sun, 15 Dec 2024 19:25:37 -0300 Subject: [PATCH 28/33] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📝 updated readme from root --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index c37909b..4bccd88 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Votação +## Instruções de como rodar o projeto +As instruções de como rodar o frontend do projeto estão no diretório /frontend, assim como as instruções de como rodar o backend do projeto estão no diretório /backend, cada em seus +respectivos README's. + + ## Objetivo No cooperativismo, cada associado possui um voto e as decisões são tomadas em assembleias, por votação. Imagine que você deve criar uma solução we para gerenciar e participar dessas sessões de votação. From b117a24fa26a382b0a8d277b10b501912fb7b5e5 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 18:25:33 -0300 Subject: [PATCH 29/33] =?UTF-8?q?=F0=9F=93=9D=20updated=20readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/README.md | 115 +++++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 41 deletions(-) diff --git a/frontend/README.md b/frontend/README.md index 74872fd..85b5c42 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,50 +1,83 @@ -# React + TypeScript + Vite +# Documentação do Frontend -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +## Visão Geral do Projeto -Currently, two official plugins are available: +Este projeto é uma aplicação web construída usando React e TypeScript. +Com o intuito de criar um fronted simples para a API de votação em pautas. -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +## Tecnologias Utilizadas -## Expanding the ESLint configuration +- **TypeScript**: Um superconjunto tipado de JavaScript que compila para JavaScript puro. +- **React**: Uma biblioteca JavaScript para construção de interfaces de usuário. +- **Material-UI**: Um popular framework de UI para React. +- **React Router**: Uma biblioteca para roteamento em aplicações React. +- **npm**: Um gerenciador de pacotes para JavaScript. -If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: +## Estrutura do Projeto -- Configure the top-level `parserOptions` property like this: +- `src/`: Contém o código fonte do aplicativo. + - `axios/`: Configuração para o Axios manipular solicitações da API. + - `ui/`: UI componentes reutilizáveis. + - `pages/`: Páginas da aplicação. + - `App.tsx`: Compnente aplicação principal. + - `index.tsx`: Ponto de entrada da aplicação. -```js -export default tseslint.config({ - languageOptions: { - // other options... - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - }, -}) -``` +## Alguns CPFs para teste + +- 086.492.750-95 +- 984.928.230-46 +- 582.106.150-41 +- 263.220.420-84 +- 333.535.550-48 + +Caso seja necessário mais CPFs para teste, pode-se utilizar o site +https://www.4devs.com.br/gerador_de_cpf -- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked` -- Optionally add `...tseslint.configs.stylisticTypeChecked` -- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config: - -```js -// eslint.config.js -import react from 'eslint-plugin-react' - -export default tseslint.config({ - // Set the react version - settings: { react: { version: '18.3' } }, - plugins: { - // Add the react plugin - react, - }, - rules: { - // other rules... - // Enable its recommended rules - ...react.configs.recommended.rules, - ...react.configs['jsx-runtime'].rules, - }, -}) +## Como executar o frontend do projeto + +### Primeiro instalar as dependências +``` +npm install ``` +### Depois executar o projeto +``` +npm run dev +``` +### Abrir o navegador e acessar o endereço: +``` +http://localhost:5173/ +``` + +## Explicação breve do porquê das escolhas tomadas durante o desenvolvimento da solução + +Foi utilizado o react router dom para a navegação entre as páginas, +o material-ui para a estilização dos componentes e o axios para a comunicação com a API. + +O material-ui além de fácil utilização também é responsivo, o que facilita a adaptação da aplicação para diferentes +dispositivos. + +Foi usado o diretório /ui para armazenar componentes que seriam reutilizados em diferentes partes da aplicação. +Também poderia ser utilizado para armazenar o conteúdo dos index.tsx, mas se decidiu contra isso por questões de não ser +necessário nesse projeto. Além de não ter sido necessário criar outros diretórios além do /Util para organização do código. + +Não foi criado um diretório Pages para armazenar os diretórios de cada página por ser um projeto pequeno +e não ter sido considerado necessário. + +O ID dos diversos itens nesse projeto foram deixados para o usuário inserir para facilitar a utilização +e teste da aplicação. + +O único lugar no código que se decidiu por não ser em inglês, fora o que vai ser exibido para o usuário, +foi o valor do voto nos botões ("radio group") para escolha de voto. Isso foi feito para facilitar o transporte de dado +para o backend. + + +O atributo inputProps em Session/index.tsx está marcado como obsoleto. Apesar da pesquisa, +nenhuma solução alternativa foi encontrada. A documentação do Material-UI sugere que +ele ainda é utilizável, pois nenhuma substituição foi fornecida. + +" +You might also have noticed that some native HTML input properties are missing from the TextField component. +This is on purpose. The component takes care of the most used properties. Then, it's up to the user to use +the underlying component shown in the following demo. Still, you can use inputProps +(and InputProps, InputLabelProps properties) if you want to avoid some boilerplate. +" (https://mui.com/material-ui/react-text-field/#components) \ No newline at end of file From 541b9c7794422748c54abb7364e1fbb0ab5925fa Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 18:25:50 -0300 Subject: [PATCH 30/33] =?UTF-8?q?=F0=9F=93=9D=20updated=20readme=20with=20?= =?UTF-8?q?cpfs=20examples=20for=20tests=20and=20how=20to=20run=20the=20pr?= =?UTF-8?q?oject?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/backend/README.md b/backend/README.md index 60f8dfe..5993e1c 100644 --- a/backend/README.md +++ b/backend/README.md @@ -31,6 +31,31 @@ de DDD (Domain Driven Development) e "Clean Architecture". - Docker - Docker Compose +## Alguns CPFs para teste + +- 086.492.750-95 +- 984.928.230-46 +- 582.106.150-41 +- 263.220.420-84 +- 333.535.550-48 + + +## Como executar o projeto + +Para rodar o projeto é necessário ter o Java 23 e o Maven instalados. +Assim como estar com o docker e o docker-compose instalados e funcionando. + +### Primeiro instalar as dependências +``` + +``` + +### Depois de executar o projeto e com o docker funcionando +``` +docker compose up -d +``` + + ### Explicação breve do porquê das escolhas tomadas durante o desenvolvimento da solução @@ -57,6 +82,7 @@ validação do CPF que foi adicionado ao código para garantir que não há CPFs A atualização de se uma sessão está aberta ou encerrada se dá quando ela é procurada. +O Status da pauta não fecha, pois não há um limite para quantas sessões podem ser criadas nela. ### Versionamento da API @@ -71,3 +97,31 @@ por exemplo: content-type: `application/vnd.api.v1+json`. Aconselhável quando a mudanças incrementais ou mesmo mundanças de no conteúdo trocado com o cliente. Portanto, eu versionaria a API conforme a mundança que será feita no código. + + +## Como rodar o backend do projeto + + Para rodar o projeto é necessário ter o Java e o Maven e o Docker funcionando. + +### Subir o banco de dados +``` +docker compose up -d +``` + + +- Execute a aplicação Spring Boot. +``` +mvn clean +``` +``` +mvn spring-boot:run +``` + + + + + +### Para acessar a aplicação: +``` +http://localhost:8080/ +``` From 0ad70ac1603058cb298e357190e0f02b8d20ee9b Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 18:57:24 -0300 Subject: [PATCH 31/33] =?UTF-8?q?=F0=9F=90=9B=20changed=20the=20java=20ver?= =?UTF-8?q?sion,=20bc=20it=20wasn't=20compiling=20by=20command=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pom.xml b/backend/pom.xml index 5633ec3..c511045 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -27,7 +27,7 @@ - 23 + 21 From 0d1298f2d9bad76feda9c53e174e2debc9835f50 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 21:17:23 -0300 Subject: [PATCH 32/33] =?UTF-8?q?=F0=9F=93=9D=20updated=20readme=20bits=20?= =?UTF-8?q?of=20whys=20of=20my=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/README.md | 117 +++++++++++++++++++++++---------------------- frontend/README.md | 22 +++++---- 2 files changed, 71 insertions(+), 68 deletions(-) diff --git a/backend/README.md b/backend/README.md index 5993e1c..ecabe7b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -1,10 +1,11 @@ # Sistema de Votação -Este projeto foi desenvolvido para o desafio de desenvolvimento de uma API de votação para uma cooperativa. +Este projeto foi desenvolvido para o desafio de desenvolvimento de uma API de +votação para uma cooperativa. ## Technologias Utilizadas -- Java 23 +- Java 21 - Spring Boot - Maven - MongoDB @@ -25,7 +26,7 @@ de DDD (Domain Driven Development) e "Clean Architecture". ### Prerequisitos -- Java 23 +- Java 21 - Maven - MongoDB - Docker @@ -39,89 +40,89 @@ de DDD (Domain Driven Development) e "Clean Architecture". - 263.220.420-84 - 333.535.550-48 +Caso seja necessário mais CPFs para teste, pode-se utilizar o site +https://www.4devs.com.br/gerador_de_cpf -## Como executar o projeto +Pois adicionei um serviço para validar o CPF informado. -Para rodar o projeto é necessário ter o Java 23 e o Maven instalados. -Assim como estar com o docker e o docker-compose instalados e funcionando. +## Como rodar o backend do projeto -### Primeiro instalar as dependências -``` +Para rodar o projeto é necessário ter o Java e o Maven e o Docker funcionando. -``` - -### Depois de executar o projeto e com o docker funcionando +- Subir o banco de dados ``` docker compose up -d ``` +- Execute a aplicação Spring Boot. +``` +mvn clean +``` +``` +mvn spring-boot:run +``` - -### Explicação breve do porquê das escolhas tomadas durante o desenvolvimento da solução - -A escolha de usar o design DDD e o clean architecture foi feita para facilitar a manutenção e a escalabilidade do projeto. -Tendo sempre em vista uma boa organização para o bom entendimento do fluxo de dados e ações, o que -permite que o código seja facilmente compreendido e alterado. - -Com isso em mente o projeto foi divido em 3 camadas, a de aplicação onde ficam os controladores e os DTOS -que se comunicam com a camada de domínio, onde ficam as entidades, serviços e exceções. -E os serviços se utilizam das entidade e dos repositórios que estão na camada de infraestrutura. -E as entidades são o núcleo do projeto, onde ficam as regras de negócio. - -O uso do MongoDB foi escolhido por ser um banco de dados NoSQL que é mais performático que um banco relacional, isso ocorre devido -ao MongoDB ser um banco de dados CP (do Teorema CAP) o que oferece consistência e tolerância à partição em detrimento da disponibilidade. - -Portanto o uso do MongoDB é ideal para o cenário de votação permite que que existam centenas de milhares de votos, conforme exigido -pela "Tarefa Bônus 2 - Performance". - -Foi criado o serviço para validar o CPF informado conforme solicitado na "Tarefa Bônus 1 - Validação do Associado". -Contudo o quesito de retornar "ABLE_TO_VOTE" ou "UNABLE_TO_VOTE" não é utilizado na criação de associado, há apenas -um endpoint para sua utilização, tendo em vista que não há razão para colocar algo aleatório no código. Ao contrário da -validação do CPF que foi adicionado ao código para garantir que não há CPFs inválidos no banco de dados. - - -A atualização de se uma sessão está aberta ou encerrada se dá quando ela é procurada. - -O Status da pauta não fecha, pois não há um limite para quantas sessões podem ser criadas nela. +- Para acessar a aplicação: +``` +http://localhost:8080/ +``` ### Versionamento da API "Como você versionaria a API da sua aplicação? Que estratégia usar?" -A versão da API pode ser controlada por meio da URL. Aconselhável quando a API sofre mudanças significativas. +A versão da API pode ser controlada por meio da URL. Aconselhável quando a API sofre +mudanças significativas. Por exemplo, a versão 1 da API seria acessada através de `/api/v1/`. -Outra estratégia seria usar o content-type para controlar a versão da API, +Outra estratégia seria usar o content-type para controlar a versão da API, por exemplo: content-type: `application/vnd.api.v1+json`. Aconselhável quando a API sofre mudanças incrementais ou mesmo mundanças de no conteúdo trocado com o cliente. -Portanto, eu versionaria a API conforme a mundança que será feita no código. +Portanto, eu versionaria a API conforme a mundança que seria feita no código. -## Como rodar o backend do projeto +### Explicação breve do porquê das escolhas tomadas durante o desenvolvimento da solução - Para rodar o projeto é necessário ter o Java e o Maven e o Docker funcionando. +Escolhi de usar o design DDD e o clean architecture para facilitar a manutenção +e o entendimento do projeto. Tendo sempre em vista uma boa organização e +bom entendimento do fluxo de dados e ações, o que permitindo o código ser +facilmente compreendido e alterado. -### Subir o banco de dados -``` -docker compose up -d -``` +Com isso em mente o projeto foi divido em 3 camadas: +- a camada de Aplicação: onde ficam os controladores, os DTOS que são transmitidos para +a camada de domínio e o handler global de exceções. +- a camada do Domínio: onde se encontram as entidades, serviços e exceções. +- a camada da Infraestutura (infra): no qual estão as interfaces dos repositórios que estão na camada de +- infraestrutura. E as entidades são o núcleo do projeto, onde ficam as regras de negócio. +Escolhi usar o MongoDB por ser um banco de dados NoSQL que traz a proposta de ser +mais performático que um banco relacional, isso ocorre devido ao MongoDB ser um +banco de dados CP (do Teorema CAP) o que oferece consistência e tolerância à +partição em detrimento da disponibilidade. -- Execute a aplicação Spring Boot. -``` -mvn clean -``` -``` -mvn spring-boot:run -``` +O uso do MongoDB, portanto, é ideal para o cenário de votação permite que existam +centenas de milhares de votos, conforme exigido pela "Tarefa Bônus 2 - Performance". +Foi criado o serviço para validar o CPF informado conforme solicitado na +"Tarefa Bônus 1 - Validação do Associado". Entretanto optei por não integrar essa +funcionalidade de retornar aleatoriamente "ABLE_TO_VOTE" ou "UNABLE_TO_VOTE" +no projeto, sendo feito apenas um endpoint para sua utilização. Já a validação +do CPF foi adicionado ao código para garantir que não há CPFs +inválidos no banco de dados. +Só se altera o status de uma sessão para encerrada quando se procura por uma sessão e +se verifica que o tempo de duração da sessão foi alcançado. +O Status da pauta não fecha, pois não há um limite para quantas sessões +podem ser criadas nela. +Criei o CRUD completo dos endpoints para caso sejam úteis ao testar a aplicação. -### Para acessar a aplicação: -``` -http://localhost:8080/ -``` +Escolhi deixar que o ID dos diversos itens nesse projeto sejam inseridos diretamente pelo +o usuário para facilitar a utilização e teste da aplicação. + +Apenas o voto não está em inglês e esse readme, no backend, para facilitar o transporte de +de dados do voto entre o frontend e o backend e por que o readme do desafio esta em +português. diff --git a/frontend/README.md b/frontend/README.md index 85b5c42..17380ca 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -33,6 +33,8 @@ Com o intuito de criar um fronted simples para a API de votação em pautas. Caso seja necessário mais CPFs para teste, pode-se utilizar o site https://www.4devs.com.br/gerador_de_cpf +Pois adicionei um serviço para validar o CPF informado. + ## Como executar o frontend do projeto ### Primeiro instalar as dependências @@ -57,19 +59,19 @@ O material-ui além de fácil utilização também é responsivo, o que facilita dispositivos. Foi usado o diretório /ui para armazenar componentes que seriam reutilizados em diferentes partes da aplicação. -Também poderia ser utilizado para armazenar o conteúdo dos index.tsx, mas se decidiu contra isso por questões de não ser +Também poderia ser utilizado para armazenar o conteúdo dos index.tsx, mas decidi contra isso por questões de não ser necessário nesse projeto. Além de não ter sido necessário criar outros diretórios além do /Util para organização do código. -Não foi criado um diretório Pages para armazenar os diretórios de cada página por ser um projeto pequeno -e não ter sido considerado necessário. - -O ID dos diversos itens nesse projeto foram deixados para o usuário inserir para facilitar a utilização -e teste da aplicação. +Criei um diretório Pages para armazenar os diretórios de cada página por +para facilitar a organização e compreensão do projeto. -O único lugar no código que se decidiu por não ser em inglês, fora o que vai ser exibido para o usuário, -foi o valor do voto nos botões ("radio group") para escolha de voto. Isso foi feito para facilitar o transporte de dado -para o backend. +Escolhi deixar que o ID dos diversos itens nesse projeto sejam inseridos diretamente pelo +o usuário para facilitar a utilização e teste da aplicação. +Os únicos lugares no código que se decidiu por não ser em inglês, foi +nos readme's, o que vai ser exibido para o usuário e o valor do voto +nos botões ("radio group") para escolha de voto. Isso foi feito para +facilitar o transporte de dado para o backend. O atributo inputProps em Session/index.tsx está marcado como obsoleto. Apesar da pesquisa, nenhuma solução alternativa foi encontrada. A documentação do Material-UI sugere que @@ -80,4 +82,4 @@ You might also have noticed that some native HTML input properties are missing f This is on purpose. The component takes care of the most used properties. Then, it's up to the user to use the underlying component shown in the following demo. Still, you can use inputProps (and InputProps, InputLabelProps properties) if you want to avoid some boilerplate. -" (https://mui.com/material-ui/react-text-field/#components) \ No newline at end of file +" (https://mui.com/material-ui/react-text-field/#components) From 4b7fd97f6a4f3d4bc117018a91f9813fd14066f7 Mon Sep 17 00:00:00 2001 From: Gabriel Piazenski Date: Sun, 15 Dec 2024 21:20:40 -0300 Subject: [PATCH 33/33] =?UTF-8?q?=F0=9F=93=9D=20updated=20readme=20with=20?= =?UTF-8?q?the=20whys=20of=20my=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/README.md b/backend/README.md index ecabe7b..35a626f 100644 --- a/backend/README.md +++ b/backend/README.md @@ -125,4 +125,4 @@ o usuário para facilitar a utilização e teste da aplicação. Apenas o voto não está em inglês e esse readme, no backend, para facilitar o transporte de de dados do voto entre o frontend e o backend e por que o readme do desafio esta em -português. +português. \ No newline at end of file