├── .gitignore ├── Dockerfile ├── README.md ├── build.gradle.kts ├── docker-compose.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts └── src ├── main ├── kotlin │ └── com │ │ └── mutecomputers │ │ ├── Application.kt │ │ ├── controller │ │ ├── DataController.kt │ │ └── MainController.kt │ │ ├── model │ │ ├── CPU.kt │ │ ├── ComputerCase.kt │ │ ├── Cooler.kt │ │ ├── GPU.kt │ │ ├── HDD.kt │ │ ├── Motherboard.kt │ │ ├── Power.kt │ │ ├── RAM.kt │ │ └── SSD.kt │ │ └── repository │ │ ├── CPURepository.kt │ │ ├── CaseRepository.kt │ │ ├── CoolerRepository.kt │ │ ├── GPURepository.kt │ │ ├── HDDRepository.kt │ │ ├── MotherboardRepository.kt │ │ ├── PowerRepository.kt │ │ ├── RAMRepository.kt │ │ └── SSDRepository.kt └── resources │ ├── application.properties │ ├── static │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-grid.rtl.css │ │ ├── bootstrap-grid.rtl.css.map │ │ ├── bootstrap-grid.rtl.min.css │ │ ├── bootstrap-grid.rtl.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap-reboot.rtl.css │ │ ├── bootstrap-reboot.rtl.css.map │ │ ├── bootstrap-reboot.rtl.min.css │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ ├── bootstrap-utilities.css │ │ ├── bootstrap-utilities.css.map │ │ ├── bootstrap-utilities.min.css │ │ ├── bootstrap-utilities.min.css.map │ │ ├── bootstrap-utilities.rtl.css │ │ ├── bootstrap-utilities.rtl.css.map │ │ ├── bootstrap-utilities.rtl.min.css │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.css.map │ │ ├── bootstrap.rtl.css │ │ ├── bootstrap.rtl.css.map │ │ ├── bootstrap.rtl.min.css │ │ ├── bootstrap.rtl.min.css.map │ │ ├── left-navbar.css │ │ ├── main-content.css │ │ ├── radio-button.css │ │ ├── right-navbar.css │ │ └── scroll.css │ ├── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.esm.js │ │ ├── bootstrap.esm.js.map │ │ ├── bootstrap.esm.min.js │ │ ├── bootstrap.esm.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ ├── bootstrap.min.js.map │ │ ├── check-popup.js │ │ ├── export-pdf.js │ │ ├── info-popup.js │ │ ├── scrolling.js │ │ └── session.js │ └── svg │ │ ├── cancel.svg │ │ ├── case-2.svg │ │ ├── case.svg │ │ ├── cooler.svg │ │ ├── cpu.svg │ │ ├── gpu.svg │ │ ├── hdd.svg │ │ ├── info.svg │ │ ├── logo.svg │ │ ├── motherboard.svg │ │ ├── power.svg │ │ ├── ram.svg │ │ ├── ssd.svg │ │ └── tick.svg │ └── templates │ └── main.html └── test └── kotlin └── com └── mutecomputers └── ApplicationTests.kt /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazoncorretto:17-alpine AS builder 2 | COPY . /project/ 3 | WORKDIR /project/ 4 | RUN ./gradlew bootJar --configure-on-demand && \ 5 | # extracting jar-file 6 | java -Djarmode=layertools -jar ./build/libs/mute-computers-1.0.0.jar extract && \ 7 | # analyzing all dependencies 8 | jdeps --multi-release 17 --class-path 'dependencies/BOOT-INF/lib/*' --ignore-missing-deps --recursive \ 9 | --print-module-deps ./build/libs/mute-computers-1.0.0.jar > jre-deps.info && \ 10 | # required by jlink for --strip-debug to work 11 | apk add --no-cache binutils && \ 12 | # creating custom JRE and adding `jdk.crypto.ec` module manually 13 | jlink --strip-debug --no-man-pages --compress=2 --no-header-files --add-modules jdk.crypto.ec,$(cat jre-deps.info) --output /custom-jre 14 | 15 | # main app image 16 | FROM alpine 17 | ENV JAVA_HOME=/custom-jre 18 | ENV PATH="${JAVA_HOME}/bin:${PATH}" 19 | 20 | # copy JRE from the base image 21 | COPY --from=builder /custom-jre $JAVA_HOME 22 | 23 | # copy jar-file from the base image 24 | COPY --from=builder /project/build/libs/mute-computers-1.0.0.jar . 25 | 26 | EXPOSE 8080 27 | CMD ["java", "-jar", "mute-computers-1.0.0.jar"] 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MuteComputers 2 | 3 | Веб-приложение для проверки совместимости и сравнения характеристик комплектующих компьютера. 4 | 5 | Работующее веб-приложение можно посмотреть по текущей [ссылке](https://8080-79c43b1233cf42f7a2da9ca5ab19d2d7.onpatr.cloud/). 6 | 7 | ## Screenshots 8 | 9 |
10 | 11 | | | 12 | | :---: | 13 | | Главная страница | 14 | 15 |
16 | 17 | | | 18 | | :---: | 19 | | Боковая навигационная панель | 20 | 21 |
22 | 23 | | | 24 | | :---: | 25 | | Проверка совместимости комплетующих | 26 | 27 |
28 | 29 | | | 30 | | :---: | 31 | | Характеристики комплектующей | 32 | 33 |
34 | 35 | | | 36 | | :---: | 37 | | Адаптивный интерфейс под мобильные устройства | 38 | 39 |
40 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 2 | 3 | plugins { 4 | id("org.springframework.boot") version "3.1.0" 5 | id("io.spring.dependency-management") version "1.1.0" 6 | kotlin("jvm") version "1.8.21" 7 | kotlin("plugin.spring") version "1.8.21" 8 | kotlin("plugin.jpa") version "1.8.21" 9 | } 10 | 11 | group = "com.mutecomputers" 12 | version = "1.0.0" 13 | java.sourceCompatibility = JavaVersion.VERSION_17 14 | 15 | repositories { 16 | mavenCentral() 17 | } 18 | 19 | dependencies { 20 | implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0") 21 | implementation("org.springframework.boot:spring-boot-starter-data-jpa") 22 | implementation("org.springframework.boot:spring-boot-starter-validation") 23 | implementation("org.springframework.boot:spring-boot-starter-thymeleaf") 24 | implementation("org.springframework.boot:spring-boot-starter-web") 25 | implementation("com.fasterxml.jackson.module:jackson-module-kotlin") 26 | implementation("org.jetbrains.kotlin:kotlin-reflect") 27 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 28 | developmentOnly("org.springframework.boot:spring-boot-devtools") 29 | implementation("org.yaml:snakeyaml:2.0") 30 | runtimeOnly("org.postgresql:postgresql") 31 | testImplementation("org.springframework.boot:spring-boot-starter-test") 32 | } 33 | 34 | tasks.withType { 35 | kotlinOptions { 36 | freeCompilerArgs = listOf("-Xjsr305=strict") 37 | jvmTarget = "17" 38 | } 39 | } 40 | 41 | tasks.withType { 42 | useJUnitPlatform() 43 | } 44 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | #secrets: 3 | # db-name: 4 | # file: db-name.txt 5 | # db-user: 6 | # file: db-user.txt 7 | # db-password: 8 | # file: db-password.txt 9 | # db-url: 10 | # file: db-url.txt 11 | 12 | secrets: 13 | db-name: 14 | external: true 15 | db-user: 16 | external: true 17 | db-password: 18 | external: true 19 | db-url: 20 | external: true 21 | 22 | services: 23 | postgres: 24 | image: 'postgres:15-alpine' 25 | container_name: 'postgres' 26 | ports: 27 | - '5432:5432' 28 | secrets: 29 | - 'db-name' 30 | - 'db-user' 31 | - 'db-password' 32 | environment: 33 | # - 'POSTGRES_DB=my-database' 34 | # - 'POSTGRES_USER=khamroev' 35 | # - 'POSTGRES_PASSWORD=1234' 36 | - 'POSTGRES_DB_FILE=/run/secrets/db-name' 37 | - 'POSTGRES_USER_FILE=/run/secrets/db-user' 38 | - 'POSTGRES_PASSWORD_FILE=/run/secrets/db-password' 39 | 40 | app: 41 | build: ./ 42 | # container_name: 'spring-app' 43 | ports: 44 | - '8080:8080' 45 | secrets: 46 | - 'db-url' 47 | - 'db-user' 48 | - 'db-password' 49 | environment: 50 | - 'DB_URL_FILE=/run/secrets/db-url' 51 | - 'DB_USER_FILE=/run/secrets/db-user' 52 | - 'DB_PASSWORD_FILE=/run/secrets/db-password' 53 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/khamroevjs/mute-computers/8b72ef512595a32df3da16641dcc1b982958d804/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "mute-computers" 2 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/Application.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers 2 | 3 | import org.springframework.boot.autoconfigure.SpringBootApplication 4 | import org.springframework.boot.runApplication 5 | 6 | @SpringBootApplication 7 | class Application 8 | 9 | fun main(args: Array) { 10 | runApplication(*args) 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/controller/DataController.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.controller 2 | 3 | import com.mutecomputers.model.* 4 | import com.mutecomputers.repository.* 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.web.bind.annotation.PostMapping 7 | import org.springframework.web.bind.annotation.RequestBody 8 | import org.springframework.web.bind.annotation.RequestMapping 9 | import org.springframework.web.bind.annotation.RestController 10 | 11 | @RestController 12 | @RequestMapping("/data") 13 | class DataController @Autowired constructor( 14 | private val cpuRepository: CPURepository, 15 | private val gpuRepository: GPURepository, 16 | private val motherboardRepository: MotherboardRepository, 17 | private val ramRepository: RAMRepository, 18 | private val powerRepository: PowerRepository, 19 | private val coolerRepository: CoolerRepository, 20 | private val ssdRepository: SSDRepository, 21 | private val hddRepository: HDDRepository, 22 | private val caseRepository: CaseRepository 23 | ) { 24 | @PostMapping("/add-cpu") 25 | fun addCpu(@RequestBody cpuList: List) { 26 | cpuRepository.saveAll(cpuList) 27 | } 28 | 29 | @PostMapping("/add-gpu") 30 | fun addGpu(@RequestBody gpuList: List) { 31 | gpuRepository.saveAll(gpuList) 32 | } 33 | 34 | @PostMapping("/add-motherboards") 35 | fun addMotherboards(@RequestBody motherboardList: List) { 36 | motherboardRepository.saveAll(motherboardList) 37 | } 38 | 39 | @PostMapping("/add-ram") 40 | fun addRam(@RequestBody ramList: List) { 41 | ramRepository.saveAll(ramList) 42 | } 43 | 44 | @PostMapping("/add-power") 45 | fun addPower(@RequestBody powerList: List) { 46 | powerRepository.saveAll(powerList) 47 | } 48 | 49 | @PostMapping("/add-cooler") 50 | fun addCooler(@RequestBody coolerList: List) { 51 | coolerRepository.saveAll(coolerList) 52 | } 53 | 54 | @PostMapping("/add-ssd") 55 | fun addSsd(@RequestBody ssdList: List) { 56 | ssdRepository.saveAll(ssdList) 57 | } 58 | 59 | @PostMapping("/add-hdd") 60 | fun addHdd(@RequestBody hddList: List) { 61 | hddRepository.saveAll(hddList) 62 | } 63 | 64 | @PostMapping("/add-case") 65 | fun addCase(@RequestBody caseList: List) { 66 | caseRepository.saveAll(caseList) 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/controller/MainController.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.controller 2 | 3 | import com.mutecomputers.model.Motherboard 4 | import com.mutecomputers.repository.* 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.stereotype.Controller 7 | import org.springframework.ui.Model 8 | import org.springframework.web.bind.annotation.GetMapping 9 | import org.springframework.web.bind.annotation.RequestMapping 10 | 11 | @Controller 12 | @RequestMapping("/") 13 | class MainController @Autowired constructor( 14 | private val cpuRepository: CPURepository, 15 | private val gpuRepository: GPURepository, 16 | private val motherboardRepository: MotherboardRepository, 17 | private val ramRepository: RAMRepository, 18 | private val powerRepository: PowerRepository, 19 | private val coolerRepository: CoolerRepository, 20 | private val ssdRepository: SSDRepository, 21 | private val hddRepository: HDDRepository, 22 | private val caseRepository: CaseRepository 23 | ) { 24 | 25 | @GetMapping 26 | fun info(model: Model): String { 27 | 28 | addCpu(model) 29 | addGpu(model) 30 | addMotherboard(model) 31 | addRam(model) 32 | addPower(model) 33 | addCooler(model) 34 | addSsd(model) 35 | addHdd(model) 36 | addCase(model) 37 | 38 | return "main" 39 | } 40 | 41 | fun addMotherboard(model: Model) { 42 | val z590 = ArrayList() 43 | val h570 = ArrayList() 44 | val b560 = ArrayList() 45 | val z490 = ArrayList() 46 | val h470 = ArrayList() 47 | val b460 = ArrayList() 48 | val x570 = ArrayList() 49 | val b550 = ArrayList() 50 | val x470 = ArrayList() 51 | val b450 = ArrayList() 52 | val motherboards = motherboardRepository.findAll() 53 | for (item in motherboards) { 54 | when (item.chipset) { 55 | "Z590" -> z590.add(item) 56 | "H570" -> h570.add(item) 57 | "B560" -> b560.add(item) 58 | "Z490" -> z490.add(item) 59 | "H470" -> h470.add(item) 60 | "B460" -> b460.add(item) 61 | "X570" -> x570.add(item) 62 | "B550" -> b550.add(item) 63 | "X470" -> x470.add(item) 64 | "B450" -> b450.add(item) 65 | } 66 | } 67 | model.addAttribute("allMotherboards", motherboards) 68 | model.addAttribute("z590", z590) 69 | model.addAttribute("h570", h570) 70 | model.addAttribute("b560", b560) 71 | model.addAttribute("z490", z490) 72 | model.addAttribute("h470", h470) 73 | model.addAttribute("b460", b460) 74 | model.addAttribute("x570", x570) 75 | model.addAttribute("b550", b550) 76 | model.addAttribute("x470", x470) 77 | model.addAttribute("b450", b450) 78 | } 79 | 80 | fun addCpu(model: Model) { 81 | model.addAttribute("allCpu", cpuRepository.findAll()) 82 | model.addAttribute("intelCpu", cpuRepository.findAllByManufacturer("Intel")) 83 | model.addAttribute("amdCpu", cpuRepository.findAllByManufacturer("AMD")) 84 | } 85 | 86 | fun addGpu(model: Model) { 87 | model.addAttribute("allGpu", gpuRepository.findAll()) 88 | model.addAttribute("nvidiaGpu", gpuRepository.findAllByManufacturer("Nvidia")) 89 | model.addAttribute("amdGpu", gpuRepository.findAllByManufacturer("AMD")) 90 | } 91 | 92 | fun addRam(model: Model) { 93 | model.addAttribute("allRam", ramRepository.findAll()) 94 | model.addAttribute("hyperxRam", ramRepository.findAllByManufacturer("HyperX")) 95 | model.addAttribute("corsairRam", ramRepository.findAllByManufacturer("Corsair")) 96 | } 97 | 98 | fun addPower(model: Model) { 99 | model.addAttribute("allPower", powerRepository.findAll()) 100 | model.addAttribute("bequietPower", powerRepository.findAllByManufacturer("be quiet!")) 101 | model.addAttribute("masterPower", powerRepository.findAllByManufacturer("Cooler Master")) 102 | } 103 | 104 | fun addCooler(model: Model) { 105 | model.addAttribute("allCooler", coolerRepository.findAll()) 106 | model.addAttribute("airCooler", coolerRepository.findAllByType("Air")) 107 | model.addAttribute("liquidCooler", coolerRepository.findAllByType("Liquid")) 108 | } 109 | 110 | fun addSsd(model: Model) { 111 | model.addAttribute("allSsd", ssdRepository.findAll()) 112 | model.addAttribute("samsungSsd", ssdRepository.findAllByManufacturer("Samsung")) 113 | model.addAttribute("wdSsd", ssdRepository.findAllByManufacturer("Western Digital")) 114 | } 115 | 116 | fun addHdd(model: Model) { 117 | model.addAttribute("allHdd", hddRepository.findAll()) 118 | model.addAttribute("seagateHdd", hddRepository.findAllByManufacturer("Seagate")) 119 | model.addAttribute("wdHdd", hddRepository.findAllByManufacturer("Western Digital")) 120 | } 121 | 122 | fun addCase(model: Model) { 123 | model.addAttribute("allCase", caseRepository.findAll()) 124 | model.addAttribute("bequietCase", caseRepository.findAllByManufacturer("be quiet!")) 125 | model.addAttribute("masterCase", caseRepository.findAllByManufacturer("Cooler Master")) 126 | } 127 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/CPU.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class CPU( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val releaseYear: Int, 16 | val fabricationProcess: Int, // nm 17 | val TDP: Int, // W 18 | val socket: String, 19 | val microarchitecture: String, 20 | val codeName: String, 21 | val cores: Int, 22 | val threads: Int, 23 | val baseFrequency: Double, // GHz 24 | val turboFrequency: Double, // GHz 25 | val L1CacheInstruction: String, 26 | val L1CacheDate: String, 27 | val L2Cache: String, 28 | val L3Cache: String, 29 | val unlockedMultiplier: Boolean, 30 | val memoryType: String, 31 | val memoryBandwidth: Double, 32 | val memoryChannels: Int, // GB/s 33 | val maxMemorySize: Int, // GB 34 | val ECCMemory: Boolean, 35 | val integratedGraphics: String, 36 | val PCIExpressRevision: Int, 37 | val PCIExpressLanes: Int, 38 | ) 39 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/ComputerCase.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class ComputerCase( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val size: String, 16 | val dimensions: String, 17 | val weight: Double, 18 | val maxCoolerHeight: Int, 19 | val maxGpuLength: Int, 20 | ) -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/Cooler.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class Cooler( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val type: String, // Air or Liquid 16 | val dimensions: String, 17 | val TDP: Int, 18 | val fanCount: Int, 19 | ) 20 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/GPU.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | import jakarta.persistence.Entity 3 | import jakarta.persistence.GeneratedValue 4 | import jakarta.persistence.GenerationType 5 | import jakarta.persistence.Id 6 | 7 | @Entity 8 | data class GPU( 9 | @Id 10 | @GeneratedValue(strategy = GenerationType.IDENTITY) 11 | val id: Int, 12 | val manufacturer: String, 13 | val name: String, 14 | val releaseYear: Int, 15 | val microarchitecture: String, 16 | val fabricationProcess: Int, 17 | val TDP: Int, 18 | val interfacePort: String, 19 | val multiGPUSupport: String, 20 | val baseClock: Int, 21 | val boostClock: Int, 22 | val shadingUnits: Int, 23 | val L1Cache: String, 24 | val L2Cache: String, 25 | val L3Cache: String, 26 | val memoryType: String, 27 | val memorySize: Int, 28 | val memoryClock: Int, 29 | val memoryClockEffective: Int, 30 | val memoryBus: Int, 31 | val memoryBandwidth: Double 32 | ) 33 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/HDD.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class HDD( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val capacity: Int, 16 | val readSpeed: Int, // MB/s 17 | val writeSpeed: Int,// MB/s 18 | val cache: Int, 19 | val formFactor: String 20 | ) 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/Motherboard.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class Motherboard( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val chipset: String, 16 | val formFactor: String, 17 | val memorySlots: Int, 18 | val socket: String, 19 | val wifiSupport: Boolean, 20 | val bluetoothSupport: Boolean, 21 | val PCIExpressRevision: Int 22 | ) 23 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/Power.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class Power( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val certificate80Plus: String, 16 | val wattage: Int, 17 | ) 18 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/RAM.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class RAM( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val speed: Int, //MHz 16 | val capacity: Int, 17 | val type: String 18 | ) 19 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/model/SSD.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.model 2 | 3 | import jakarta.persistence.Entity 4 | import jakarta.persistence.GeneratedValue 5 | import jakarta.persistence.GenerationType 6 | import jakarta.persistence.Id 7 | 8 | @Entity 9 | data class SSD( 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.IDENTITY) 12 | val id: Int, 13 | val manufacturer: String, 14 | val name: String, 15 | val capacity: Int, 16 | val readSpeed: Int, // MB/s 17 | val writeSpeed: Int,// MB/s 18 | val totalBytesWritten: Int, // TB 19 | val formFactor: String, 20 | ) 21 | -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/CPURepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.CPU 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface CPURepository : JpaRepository { 9 | 10 | fun findAllByManufacturer(manufacturer: String) : List 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/CaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.ComputerCase 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface CaseRepository : JpaRepository { 9 | fun findAllByManufacturer(manufacturer: String): List 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/CoolerRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.Cooler 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface CoolerRepository : JpaRepository { 9 | fun findAllByType(type: String): List 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/GPURepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.CPU 4 | import com.mutecomputers.model.GPU 5 | import org.springframework.data.jpa.repository.JpaRepository 6 | import org.springframework.stereotype.Repository 7 | 8 | @Repository 9 | interface GPURepository : JpaRepository { 10 | 11 | fun findAllByManufacturer(manufacturer: String) : List 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/HDDRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.HDD 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface HDDRepository : JpaRepository { 9 | fun findAllByManufacturer(manufacturer: String) : List 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/MotherboardRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.Motherboard 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface MotherboardRepository : JpaRepository { 9 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/PowerRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.Power 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface PowerRepository : JpaRepository { 9 | fun findAllByManufacturer(manufacturer: String): List 10 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/RAMRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.RAM 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface RAMRepository : JpaRepository { 9 | 10 | fun findAllByManufacturer(manufacturer: String) : List 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/com/mutecomputers/repository/SSDRepository.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers.repository 2 | 3 | import com.mutecomputers.model.SSD 4 | import org.springframework.data.jpa.repository.JpaRepository 5 | import org.springframework.stereotype.Repository 6 | 7 | @Repository 8 | interface SSDRepository: JpaRepository { 9 | fun findAllByManufacturer(manufacturer: String) : List 10 | } -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Connection String 2 | spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/mute-computers} 3 | spring.datasource.username=${SPRING_DATASOURCE_USERNAME:postgres} 4 | spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:1234} 5 | 6 | # Creates the DB if it doesn't exist 7 | spring.jpa.hibernate.ddl-auto=update 8 | # For viewing exceptions in console 9 | server.error.include-message=always 10 | # Other settings 11 | spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect 12 | spring.jpa.properties.hibernate.format_sql=true 13 | spring.jpa.show-sql=true 14 | spring.jpa.open-in-view=false 15 | -------------------------------------------------------------------------------- /src/main/resources/static/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | @media (prefers-reduced-motion: no-preference) { 15 | :root { 16 | scroll-behavior: smooth; 17 | } 18 | } 19 | 20 | body { 21 | margin: 0; 22 | font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 23 | font-size: 1rem; 24 | font-weight: 400; 25 | line-height: 1.5; 26 | color: #212529; 27 | background-color: #fff; 28 | -webkit-text-size-adjust: 100%; 29 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 30 | } 31 | 32 | hr { 33 | margin: 1rem 0; 34 | color: inherit; 35 | background-color: currentColor; 36 | border: 0; 37 | opacity: 0.25; 38 | } 39 | 40 | hr:not([size]) { 41 | height: 1px; 42 | } 43 | 44 | h6, h5, h4, h3, h2, h1 { 45 | margin-top: 0; 46 | margin-bottom: 0.5rem; 47 | font-weight: 500; 48 | line-height: 1.2; 49 | } 50 | 51 | h1 { 52 | font-size: calc(1.375rem + 1.5vw); 53 | } 54 | @media (min-width: 1200px) { 55 | h1 { 56 | font-size: 2.5rem; 57 | } 58 | } 59 | 60 | h2 { 61 | font-size: calc(1.325rem + 0.9vw); 62 | } 63 | @media (min-width: 1200px) { 64 | h2 { 65 | font-size: 2rem; 66 | } 67 | } 68 | 69 | h3 { 70 | font-size: calc(1.3rem + 0.6vw); 71 | } 72 | @media (min-width: 1200px) { 73 | h3 { 74 | font-size: 1.75rem; 75 | } 76 | } 77 | 78 | h4 { 79 | font-size: calc(1.275rem + 0.3vw); 80 | } 81 | @media (min-width: 1200px) { 82 | h4 { 83 | font-size: 1.5rem; 84 | } 85 | } 86 | 87 | h5 { 88 | font-size: 1.25rem; 89 | } 90 | 91 | h6 { 92 | font-size: 1rem; 93 | } 94 | 95 | p { 96 | margin-top: 0; 97 | margin-bottom: 1rem; 98 | } 99 | 100 | abbr[title], 101 | abbr[data-bs-original-title] { 102 | -webkit-text-decoration: underline dotted; 103 | text-decoration: underline dotted; 104 | cursor: help; 105 | -webkit-text-decoration-skip-ink: none; 106 | text-decoration-skip-ink: none; 107 | } 108 | 109 | address { 110 | margin-bottom: 1rem; 111 | font-style: normal; 112 | line-height: inherit; 113 | } 114 | 115 | ol, 116 | ul { 117 | padding-left: 2rem; 118 | } 119 | 120 | ol, 121 | ul, 122 | dl { 123 | margin-top: 0; 124 | margin-bottom: 1rem; 125 | } 126 | 127 | ol ol, 128 | ul ul, 129 | ol ul, 130 | ul ol { 131 | margin-bottom: 0; 132 | } 133 | 134 | dt { 135 | font-weight: 700; 136 | } 137 | 138 | dd { 139 | margin-bottom: 0.5rem; 140 | margin-left: 0; 141 | } 142 | 143 | blockquote { 144 | margin: 0 0 1rem; 145 | } 146 | 147 | b, 148 | strong { 149 | font-weight: bolder; 150 | } 151 | 152 | small { 153 | font-size: 0.875em; 154 | } 155 | 156 | mark { 157 | padding: 0.2em; 158 | background-color: #fcf8e3; 159 | } 160 | 161 | sub, 162 | sup { 163 | position: relative; 164 | font-size: 0.75em; 165 | line-height: 0; 166 | vertical-align: baseline; 167 | } 168 | 169 | sub { 170 | bottom: -0.25em; 171 | } 172 | 173 | sup { 174 | top: -0.5em; 175 | } 176 | 177 | a { 178 | color: #0d6efd; 179 | text-decoration: underline; 180 | } 181 | a:hover { 182 | color: #0a58ca; 183 | } 184 | 185 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 186 | color: inherit; 187 | text-decoration: none; 188 | } 189 | 190 | pre, 191 | code, 192 | kbd, 193 | samp { 194 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 195 | font-size: 1em; 196 | direction: ltr /* rtl:ignore */; 197 | unicode-bidi: bidi-override; 198 | } 199 | 200 | pre { 201 | display: block; 202 | margin-top: 0; 203 | margin-bottom: 1rem; 204 | overflow: auto; 205 | font-size: 0.875em; 206 | } 207 | pre code { 208 | font-size: inherit; 209 | color: inherit; 210 | word-break: normal; 211 | } 212 | 213 | code { 214 | font-size: 0.875em; 215 | color: #d63384; 216 | word-wrap: break-word; 217 | } 218 | a > code { 219 | color: inherit; 220 | } 221 | 222 | kbd { 223 | padding: 0.2rem 0.4rem; 224 | font-size: 0.875em; 225 | color: #fff; 226 | background-color: #212529; 227 | border-radius: 0.2rem; 228 | } 229 | kbd kbd { 230 | padding: 0; 231 | font-size: 1em; 232 | font-weight: 700; 233 | } 234 | 235 | figure { 236 | margin: 0 0 1rem; 237 | } 238 | 239 | img, 240 | svg { 241 | vertical-align: middle; 242 | } 243 | 244 | table { 245 | caption-side: bottom; 246 | border-collapse: collapse; 247 | } 248 | 249 | caption { 250 | padding-top: 0.5rem; 251 | padding-bottom: 0.5rem; 252 | color: #6c757d; 253 | text-align: left; 254 | } 255 | 256 | th { 257 | text-align: inherit; 258 | text-align: -webkit-match-parent; 259 | } 260 | 261 | thead, 262 | tbody, 263 | tfoot, 264 | tr, 265 | td, 266 | th { 267 | border-color: inherit; 268 | border-style: solid; 269 | border-width: 0; 270 | } 271 | 272 | label { 273 | display: inline-block; 274 | } 275 | 276 | button { 277 | border-radius: 0; 278 | } 279 | 280 | button:focus:not(:focus-visible) { 281 | outline: 0; 282 | } 283 | 284 | input, 285 | button, 286 | select, 287 | optgroup, 288 | textarea { 289 | margin: 0; 290 | font-family: inherit; 291 | font-size: inherit; 292 | line-height: inherit; 293 | } 294 | 295 | button, 296 | select { 297 | text-transform: none; 298 | } 299 | 300 | [role=button] { 301 | cursor: pointer; 302 | } 303 | 304 | select { 305 | word-wrap: normal; 306 | } 307 | select:disabled { 308 | opacity: 1; 309 | } 310 | 311 | [list]::-webkit-calendar-picker-indicator { 312 | display: none; 313 | } 314 | 315 | button, 316 | [type=button], 317 | [type=reset], 318 | [type=submit] { 319 | -webkit-appearance: button; 320 | } 321 | button:not(:disabled), 322 | [type=button]:not(:disabled), 323 | [type=reset]:not(:disabled), 324 | [type=submit]:not(:disabled) { 325 | cursor: pointer; 326 | } 327 | 328 | ::-moz-focus-inner { 329 | padding: 0; 330 | border-style: none; 331 | } 332 | 333 | textarea { 334 | resize: vertical; 335 | } 336 | 337 | fieldset { 338 | min-width: 0; 339 | padding: 0; 340 | margin: 0; 341 | border: 0; 342 | } 343 | 344 | legend { 345 | float: left; 346 | width: 100%; 347 | padding: 0; 348 | margin-bottom: 0.5rem; 349 | font-size: calc(1.275rem + 0.3vw); 350 | line-height: inherit; 351 | } 352 | @media (min-width: 1200px) { 353 | legend { 354 | font-size: 1.5rem; 355 | } 356 | } 357 | legend + * { 358 | clear: left; 359 | } 360 | 361 | ::-webkit-datetime-edit-fields-wrapper, 362 | ::-webkit-datetime-edit-text, 363 | ::-webkit-datetime-edit-minute, 364 | ::-webkit-datetime-edit-hour-field, 365 | ::-webkit-datetime-edit-day-field, 366 | ::-webkit-datetime-edit-month-field, 367 | ::-webkit-datetime-edit-year-field { 368 | padding: 0; 369 | } 370 | 371 | ::-webkit-inner-spin-button { 372 | height: auto; 373 | } 374 | 375 | [type=search] { 376 | outline-offset: -2px; 377 | -webkit-appearance: textfield; 378 | } 379 | 380 | /* rtl:raw: 381 | [type="tel"], 382 | [type="url"], 383 | [type="email"], 384 | [type="number"] { 385 | direction: ltr; 386 | } 387 | */ 388 | ::-webkit-search-decoration { 389 | -webkit-appearance: none; 390 | } 391 | 392 | ::-webkit-color-swatch-wrapper { 393 | padding: 0; 394 | } 395 | 396 | ::file-selector-button { 397 | font: inherit; 398 | } 399 | 400 | ::-webkit-file-upload-button { 401 | font: inherit; 402 | -webkit-appearance: button; 403 | } 404 | 405 | output { 406 | display: inline-block; 407 | } 408 | 409 | iframe { 410 | border: 0; 411 | } 412 | 413 | summary { 414 | display: list-item; 415 | cursor: pointer; 416 | } 417 | 418 | progress { 419 | vertical-align: baseline; 420 | } 421 | 422 | [hidden] { 423 | display: none !important; 424 | } 425 | 426 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /src/main/resources/static/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /src/main/resources/static/css/bootstrap-reboot.rtl.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | @media (prefers-reduced-motion: no-preference) { 15 | :root { 16 | scroll-behavior: smooth; 17 | } 18 | } 19 | 20 | body { 21 | margin: 0; 22 | font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 23 | font-size: 1rem; 24 | font-weight: 400; 25 | line-height: 1.5; 26 | color: #212529; 27 | background-color: #fff; 28 | -webkit-text-size-adjust: 100%; 29 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 30 | } 31 | 32 | hr { 33 | margin: 1rem 0; 34 | color: inherit; 35 | background-color: currentColor; 36 | border: 0; 37 | opacity: 0.25; 38 | } 39 | 40 | hr:not([size]) { 41 | height: 1px; 42 | } 43 | 44 | h6, h5, h4, h3, h2, h1 { 45 | margin-top: 0; 46 | margin-bottom: 0.5rem; 47 | font-weight: 500; 48 | line-height: 1.2; 49 | } 50 | 51 | h1 { 52 | font-size: calc(1.375rem + 1.5vw); 53 | } 54 | @media (min-width: 1200px) { 55 | h1 { 56 | font-size: 2.5rem; 57 | } 58 | } 59 | 60 | h2 { 61 | font-size: calc(1.325rem + 0.9vw); 62 | } 63 | @media (min-width: 1200px) { 64 | h2 { 65 | font-size: 2rem; 66 | } 67 | } 68 | 69 | h3 { 70 | font-size: calc(1.3rem + 0.6vw); 71 | } 72 | @media (min-width: 1200px) { 73 | h3 { 74 | font-size: 1.75rem; 75 | } 76 | } 77 | 78 | h4 { 79 | font-size: calc(1.275rem + 0.3vw); 80 | } 81 | @media (min-width: 1200px) { 82 | h4 { 83 | font-size: 1.5rem; 84 | } 85 | } 86 | 87 | h5 { 88 | font-size: 1.25rem; 89 | } 90 | 91 | h6 { 92 | font-size: 1rem; 93 | } 94 | 95 | p { 96 | margin-top: 0; 97 | margin-bottom: 1rem; 98 | } 99 | 100 | abbr[title], 101 | abbr[data-bs-original-title] { 102 | -webkit-text-decoration: underline dotted; 103 | text-decoration: underline dotted; 104 | cursor: help; 105 | -webkit-text-decoration-skip-ink: none; 106 | text-decoration-skip-ink: none; 107 | } 108 | 109 | address { 110 | margin-bottom: 1rem; 111 | font-style: normal; 112 | line-height: inherit; 113 | } 114 | 115 | ol, 116 | ul { 117 | padding-right: 2rem; 118 | } 119 | 120 | ol, 121 | ul, 122 | dl { 123 | margin-top: 0; 124 | margin-bottom: 1rem; 125 | } 126 | 127 | ol ol, 128 | ul ul, 129 | ol ul, 130 | ul ol { 131 | margin-bottom: 0; 132 | } 133 | 134 | dt { 135 | font-weight: 700; 136 | } 137 | 138 | dd { 139 | margin-bottom: 0.5rem; 140 | margin-right: 0; 141 | } 142 | 143 | blockquote { 144 | margin: 0 0 1rem; 145 | } 146 | 147 | b, 148 | strong { 149 | font-weight: bolder; 150 | } 151 | 152 | small { 153 | font-size: 0.875em; 154 | } 155 | 156 | mark { 157 | padding: 0.2em; 158 | background-color: #fcf8e3; 159 | } 160 | 161 | sub, 162 | sup { 163 | position: relative; 164 | font-size: 0.75em; 165 | line-height: 0; 166 | vertical-align: baseline; 167 | } 168 | 169 | sub { 170 | bottom: -0.25em; 171 | } 172 | 173 | sup { 174 | top: -0.5em; 175 | } 176 | 177 | a { 178 | color: #0d6efd; 179 | text-decoration: underline; 180 | } 181 | a:hover { 182 | color: #0a58ca; 183 | } 184 | 185 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 186 | color: inherit; 187 | text-decoration: none; 188 | } 189 | 190 | pre, 191 | code, 192 | kbd, 193 | samp { 194 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 195 | font-size: 1em; 196 | direction: ltr ; 197 | unicode-bidi: bidi-override; 198 | } 199 | 200 | pre { 201 | display: block; 202 | margin-top: 0; 203 | margin-bottom: 1rem; 204 | overflow: auto; 205 | font-size: 0.875em; 206 | } 207 | pre code { 208 | font-size: inherit; 209 | color: inherit; 210 | word-break: normal; 211 | } 212 | 213 | code { 214 | font-size: 0.875em; 215 | color: #d63384; 216 | word-wrap: break-word; 217 | } 218 | a > code { 219 | color: inherit; 220 | } 221 | 222 | kbd { 223 | padding: 0.2rem 0.4rem; 224 | font-size: 0.875em; 225 | color: #fff; 226 | background-color: #212529; 227 | border-radius: 0.2rem; 228 | } 229 | kbd kbd { 230 | padding: 0; 231 | font-size: 1em; 232 | font-weight: 700; 233 | } 234 | 235 | figure { 236 | margin: 0 0 1rem; 237 | } 238 | 239 | img, 240 | svg { 241 | vertical-align: middle; 242 | } 243 | 244 | table { 245 | caption-side: bottom; 246 | border-collapse: collapse; 247 | } 248 | 249 | caption { 250 | padding-top: 0.5rem; 251 | padding-bottom: 0.5rem; 252 | color: #6c757d; 253 | text-align: right; 254 | } 255 | 256 | th { 257 | text-align: inherit; 258 | text-align: -webkit-match-parent; 259 | } 260 | 261 | thead, 262 | tbody, 263 | tfoot, 264 | tr, 265 | td, 266 | th { 267 | border-color: inherit; 268 | border-style: solid; 269 | border-width: 0; 270 | } 271 | 272 | label { 273 | display: inline-block; 274 | } 275 | 276 | button { 277 | border-radius: 0; 278 | } 279 | 280 | button:focus:not(:focus-visible) { 281 | outline: 0; 282 | } 283 | 284 | input, 285 | button, 286 | select, 287 | optgroup, 288 | textarea { 289 | margin: 0; 290 | font-family: inherit; 291 | font-size: inherit; 292 | line-height: inherit; 293 | } 294 | 295 | button, 296 | select { 297 | text-transform: none; 298 | } 299 | 300 | [role=button] { 301 | cursor: pointer; 302 | } 303 | 304 | select { 305 | word-wrap: normal; 306 | } 307 | select:disabled { 308 | opacity: 1; 309 | } 310 | 311 | [list]::-webkit-calendar-picker-indicator { 312 | display: none; 313 | } 314 | 315 | button, 316 | [type=button], 317 | [type=reset], 318 | [type=submit] { 319 | -webkit-appearance: button; 320 | } 321 | button:not(:disabled), 322 | [type=button]:not(:disabled), 323 | [type=reset]:not(:disabled), 324 | [type=submit]:not(:disabled) { 325 | cursor: pointer; 326 | } 327 | 328 | ::-moz-focus-inner { 329 | padding: 0; 330 | border-style: none; 331 | } 332 | 333 | textarea { 334 | resize: vertical; 335 | } 336 | 337 | fieldset { 338 | min-width: 0; 339 | padding: 0; 340 | margin: 0; 341 | border: 0; 342 | } 343 | 344 | legend { 345 | float: right; 346 | width: 100%; 347 | padding: 0; 348 | margin-bottom: 0.5rem; 349 | font-size: calc(1.275rem + 0.3vw); 350 | line-height: inherit; 351 | } 352 | @media (min-width: 1200px) { 353 | legend { 354 | font-size: 1.5rem; 355 | } 356 | } 357 | legend + * { 358 | clear: right; 359 | } 360 | 361 | ::-webkit-datetime-edit-fields-wrapper, 362 | ::-webkit-datetime-edit-text, 363 | ::-webkit-datetime-edit-minute, 364 | ::-webkit-datetime-edit-hour-field, 365 | ::-webkit-datetime-edit-day-field, 366 | ::-webkit-datetime-edit-month-field, 367 | ::-webkit-datetime-edit-year-field { 368 | padding: 0; 369 | } 370 | 371 | ::-webkit-inner-spin-button { 372 | height: auto; 373 | } 374 | 375 | [type=search] { 376 | outline-offset: -2px; 377 | -webkit-appearance: textfield; 378 | } 379 | 380 | [type="tel"], 381 | [type="url"], 382 | [type="email"], 383 | [type="number"] { 384 | direction: ltr; 385 | } 386 | ::-webkit-search-decoration { 387 | -webkit-appearance: none; 388 | } 389 | 390 | ::-webkit-color-swatch-wrapper { 391 | padding: 0; 392 | } 393 | 394 | ::file-selector-button { 395 | font: inherit; 396 | } 397 | 398 | ::-webkit-file-upload-button { 399 | font: inherit; 400 | -webkit-appearance: button; 401 | } 402 | 403 | output { 404 | display: inline-block; 405 | } 406 | 407 | iframe { 408 | border: 0; 409 | } 410 | 411 | summary { 412 | display: list-item; 413 | cursor: pointer; 414 | } 415 | 416 | progress { 417 | vertical-align: baseline; 418 | } 419 | 420 | [hidden] { 421 | display: none !important; 422 | } 423 | /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ -------------------------------------------------------------------------------- /src/main/resources/static/css/bootstrap-reboot.rtl.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */ -------------------------------------------------------------------------------- /src/main/resources/static/css/left-navbar.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --text-primary: #b6b6b6; 3 | --text-secondary: #ececec; 4 | --bg-primary: #23232e; 5 | --bg-secondary: #141418; 6 | --transition-speed: 300ms; 7 | } 8 | 9 | body { 10 | color: black; 11 | background: white; 12 | margin: 0; 13 | padding: 0; 14 | } 15 | 16 | .my-navbar { 17 | position: fixed; 18 | background-color: var(--bg-primary); 19 | transition: 200ms ease; 20 | z-index: 1; 21 | } 22 | 23 | 24 | .my-navbar-nav { 25 | height: 100%; 26 | list-style: none; 27 | padding: 0; 28 | margin: 0; 29 | display: flex; 30 | flex-direction: column; 31 | align-items: center; 32 | justify-content: space-between; 33 | overflow-x: hidden; 34 | overflow-y: auto; 35 | } 36 | 37 | .my-navbar-nav::-webkit-scrollbar { 38 | width: 0.4rem; 39 | } 40 | 41 | .my-nav-item { 42 | width: 100%; 43 | } 44 | 45 | .my-nav-link { 46 | display: flex; 47 | align-items: center; 48 | height: 5rem; 49 | color: var(--text-primary); 50 | text-decoration: none !important; 51 | filter: grayscale(100%) opacity(0.7); 52 | transition: var(--transition-speed); 53 | } 54 | 55 | .active { 56 | filter: grayscale(0) opacity(1); 57 | background: var(--bg-secondary); 58 | color: var(--text-secondary); 59 | } 60 | 61 | .my-nav-link:hover { 62 | filter: grayscale(0) opacity(1); 63 | background: var(--bg-secondary); 64 | color: var(--text-secondary); 65 | } 66 | 67 | .link-text { 68 | display: none; 69 | margin-left: 0.5rem; 70 | } 71 | 72 | .my-nav-link img { 73 | min-width: 3rem; 74 | max-width: 3rem; 75 | margin: 0 1rem; 76 | } 77 | 78 | /*can be removed*/ 79 | .logo { 80 | padding-right: 1.8rem; 81 | font-weight: bold; 82 | text-transform: uppercase; 83 | /*margin-bottom: 1rem;*/ 84 | text-align: center; 85 | color: var(--text-secondary); 86 | background: var(--bg-secondary); 87 | font-size: 1.5rem; 88 | letter-spacing: 0.1ch; 89 | width: 100%; 90 | } 91 | 92 | .transition { 93 | transition: var(--transition-speed); 94 | } 95 | 96 | .filter-white { 97 | filter: invert(100%) sepia(9%) saturate(0%) hue-rotate(134deg) brightness(107%) contrast(107%); 98 | } 99 | 100 | /* Small screen */ 101 | @media only screen and (max-width: 768px) { 102 | .my-navbar { 103 | bottom: 0; 104 | width: 100vw; 105 | height: 6rem; 106 | } 107 | 108 | .my-navbar-nav { 109 | flex-direction: row; 110 | overflow-x: auto; 111 | overflow-y: hidden; 112 | } 113 | 114 | .my-navbar-nav::-webkit-scrollbar { 115 | height: 0.4em; 116 | } 117 | 118 | .my-nav-link { 119 | height: 6rem; 120 | justify-content: space-evenly; 121 | flex-direction: column; 122 | } 123 | 124 | .link-text { 125 | display: block; 126 | margin: 0; 127 | } 128 | 129 | main { 130 | margin: 0; 131 | } 132 | } 133 | 134 | /* Large screen */ 135 | @media only screen and (min-width: 768px) { 136 | main { 137 | margin-left: 5rem; 138 | } 139 | 140 | .my-navbar { 141 | top: 0; 142 | width: 5rem; 143 | height: 100vh; 144 | } 145 | 146 | .my-navbar:hover { 147 | width: 13rem; 148 | } 149 | 150 | .my-navbar:hover .link-text { 151 | display: block; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/resources/static/css/main-content.css: -------------------------------------------------------------------------------- 1 | .my-0-75 { 2 | margin-top: 0.75rem; 3 | margin-bottom: 0.75rem; 4 | } 5 | 6 | .first-inner { 7 | display: flex; 8 | flex-direction: row; 9 | align-items: center; 10 | } 11 | 12 | .second-inner { 13 | display: flex; 14 | flex-direction: column; 15 | } 16 | 17 | .info-button { 18 | cursor: pointer; 19 | width: 1.4rem; 20 | height: 1.4rem; 21 | margin-left: 0.3rem; 22 | } 23 | 24 | .filter-green { 25 | filter: invert(39%) sepia(90%) saturate(1197%) hue-rotate(82deg) brightness(103%) contrast(101%); 26 | } 27 | 28 | .filter-red { 29 | filter: invert(14%) sepia(65%) saturate(5924%) hue-rotate(356deg) brightness(98%) contrast(96%); 30 | } 31 | 32 | .filter-grey { 33 | filter: invert(74%) sepia(0%) saturate(61%) hue-rotate(145deg) brightness(94%) contrast(83%); 34 | } 35 | 36 | select { 37 | background: #2f3640; 38 | color: #fff; 39 | margin-right: 0.6rem; 40 | } 41 | 42 | /* Small screen */ 43 | @media only screen and (max-width: 768px) { 44 | .page-section { 45 | margin: 1.5rem; 46 | /*margin: 1rem 1rem 5rem 5rem;*/ 47 | } 48 | } 49 | 50 | /* Large screen */ 51 | @media only screen and (min-width: 768px) { 52 | .page-section { 53 | margin: 2rem 5rem 5rem 5rem; 54 | } 55 | } -------------------------------------------------------------------------------- /src/main/resources/static/css/radio-button.css: -------------------------------------------------------------------------------- 1 | .radio { 2 | display: inline-flex; 3 | align-items: center; 4 | cursor: pointer; 5 | margin-right: 10px; 6 | } 7 | 8 | .radio__input { 9 | display: none; 10 | } 11 | 12 | .radio__radio { 13 | width: 1.4em; 14 | height: 1.4em; 15 | border: 2px solid #8A8A8A; 16 | border-radius: 50%; 17 | margin-right: 10px; 18 | box-sizing: border-box; 19 | padding: 2px; 20 | } 21 | 22 | .radio__radio::after { 23 | content: ""; 24 | width: 100%; 25 | height: 100%; 26 | display: block; 27 | background: #393939; 28 | border-radius: 50%; 29 | 30 | opacity: 0; 31 | transition: opacity 300ms; 32 | } 33 | 34 | .radio__input:checked + .radio__radio::after { 35 | opacity: 1; 36 | } -------------------------------------------------------------------------------- /src/main/resources/static/css/right-navbar.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --text-primary: #b6b6b6; 3 | --text-secondary: #ececec; 4 | --bg-primary: #23232e; 5 | --bg-secondary: #141418; 6 | } 7 | 8 | 9 | 10 | .right-navbar { 11 | z-index: 1; 12 | /*width: 20rem;*/ 13 | /*height: 76vh;*/ 14 | /*top: 12vh;*/ 15 | /*bottom: 12vh;*/ 16 | /*right: 0;*/ 17 | position: fixed; 18 | background-color: var(--bg-primary); 19 | display: flex; 20 | /*flex-direction: column;*/ 21 | justify-content: space-evenly; 22 | align-items: center; 23 | } 24 | 25 | .rn-div-1 { 26 | display: flex; 27 | } 28 | 29 | .filter-black { 30 | /*filter: invert(0) sepia(0) saturate(0) hue-rotate(0) brightness(0) contrast(1);*/ 31 | } 32 | 33 | /* Small screen */ 34 | @media only screen and (max-width: 768px) { 35 | /*main {*/ 36 | /* margin-right: 0rem;*/ 37 | /*}*/ 38 | .right-navbar { 39 | bottom: 6rem; 40 | width: 100vw; 41 | height: 4rem; 42 | } 43 | 44 | .right-navbar img { 45 | display: none; 46 | } 47 | 48 | .rn-div-1 { 49 | flex-direction: row-reverse; 50 | width: 100%; 51 | } 52 | 53 | .rn-btn-1 { 54 | margin-left: 0.5rem; 55 | margin-right: 0.25rem; 56 | } 57 | 58 | .rn-btn-2 { 59 | margin-left: 0.25rem; 60 | margin-right: 0.25rem; 61 | } 62 | 63 | .rn-btn-3 { 64 | margin-left: 0.25rem; 65 | margin-right: 0.5rem; 66 | } 67 | } 68 | 69 | /* Large screen */ 70 | @media only screen and (min-width: 768px) { 71 | main { 72 | margin-right: 20rem; 73 | } 74 | 75 | .right-navbar { 76 | top: 12vh; 77 | bottom: 12vh; 78 | right: 0; 79 | width: 20rem; 80 | height: 76vh; 81 | flex-direction: column; 82 | } 83 | 84 | .rn-div-1 { 85 | flex-direction: column; 86 | width: 75%; 87 | } 88 | 89 | .rn-div-2 { 90 | margin-top: 0.5rem; 91 | } 92 | 93 | .rn-btn-1 { 94 | margin-right: 0.25rem !important 95 | } 96 | 97 | .rn-btn-2 { 98 | margin-left: 0.25rem !important; 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /src/main/resources/static/css/scroll.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | width: 0.5rem; 3 | } 4 | 5 | ::-webkit-scrollbar-track { 6 | background: #1e1e24; 7 | } 8 | 9 | ::-webkit-scrollbar-thumb { 10 | background: #6649b8; 11 | border-radius: 0.25rem; 12 | } -------------------------------------------------------------------------------- /src/main/resources/static/js/check-popup.js: -------------------------------------------------------------------------------- 1 | function checkComponents() { 2 | const cpuValue = document.querySelector("input[name=cpu]:checked") // manufacturer, name, socket, integratedGraphics 3 | const gpuValue = document.querySelector("input[name=gpu]:checked") 4 | const motherboardValue = document.querySelector("input[name=motherboard]:checked") // manufacturer, name, memorySlots, socket, chipset 5 | const ramValue = document.querySelector("input[name=ram]:checked") 6 | const powerValue = document.querySelector("input[name=power]:checked") 7 | const coolerValue = document.querySelector("input[name=cooler]:checked") 8 | const ssdValue = document.querySelector("input[name=ssd]:checked") 9 | const hddValue = document.querySelector("input[name=hdd]:checked") 10 | const caseValue = document.querySelector("input[name=case]:checked") 11 | 12 | const modal = document.getElementById('check-modal') 13 | 14 | let allSelected = true 15 | const trArray = modal.querySelectorAll('tr') 16 | 17 | allSelected &= addComponent(trArray[0], cpuValue) 18 | allSelected &= addGpu(trArray[1], gpuValue) 19 | allSelected &= addComponent(trArray[2], motherboardValue) 20 | allSelected &= addRam(trArray[3], ramValue) 21 | allSelected &= addComponent(trArray[4], powerValue) 22 | allSelected &= addComponent(trArray[5], coolerValue) 23 | allSelected &= addStorage(trArray[6], trArray[7], ssdValue, hddValue) 24 | allSelected &= addComponent(trArray[8], caseValue) 25 | 26 | // Determining the overall problem 27 | const firstImg = modal.querySelector('img') 28 | const h1 = firstImg.nextElementSibling.firstElementChild 29 | if (!allSelected) { 30 | firstImg.src = '/svg/cancel.svg' 31 | firstImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 32 | firstImg.classList.add('filter-red') 33 | h1.innerHTML = 'Please, choose all required components' 34 | h1.nextElementSibling.style.display = 'none' 35 | return; 36 | } 37 | 38 | const cpuValueArray = cpuValue.value.split('|') 39 | const motherboardValueArray = motherboardValue.value.split('|') 40 | if (cpuValueArray[2] !== motherboardValueArray[3]) { 41 | const cpuImg = trArray[0].querySelectorAll('td')[2].firstElementChild 42 | const motherboardImg = trArray[2].querySelectorAll('td')[2].firstElementChild 43 | cpuImg.setAttribute('src', '/svg/cancel.svg') 44 | cpuImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 45 | cpuImg.classList.add('filter-red') 46 | motherboardImg.setAttribute('src', '/svg/cancel.svg') 47 | motherboardImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 48 | motherboardImg.classList.add('filter-red') 49 | firstImg.src = '/svg/cancel.svg' 50 | firstImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 51 | firstImg.classList.add('filter-red') 52 | h1.innerHTML = 'CPU and Motherboard are incompatible' 53 | h1.nextElementSibling.innerHTML = 'Please, choose another CPU or Motherboard' 54 | h1.nextElementSibling.style.display = '' 55 | return; 56 | } 57 | 58 | if (!checkChipset(cpuValueArray[0], cpuValueArray[1], motherboardValueArray[4])) { 59 | const cpuImg = trArray[0].querySelectorAll('td')[2].firstElementChild 60 | const motherboardImg = trArray[2].querySelectorAll('td')[2].firstElementChild 61 | cpuImg.setAttribute('src', '/svg/cancel.svg') 62 | cpuImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 63 | cpuImg.classList.add('filter-red') 64 | motherboardImg.setAttribute('src', '/svg/cancel.svg') 65 | motherboardImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 66 | motherboardImg.classList.add('filter-red') 67 | firstImg.src = '/svg/cancel.svg' 68 | firstImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 69 | firstImg.classList.add('filter-red') 70 | h1.innerHTML = 'CPU is incompatible with motherboard\'s chipset' 71 | h1.nextElementSibling.innerHTML = 'Please, choose another CPU or Motherboard' 72 | h1.nextElementSibling.style.display = '' 73 | return; 74 | } 75 | 76 | if (gpuValue === null && cpuValueArray[3] === 'N/A') { 77 | const cpuImg = trArray[0].querySelectorAll('td')[2].firstElementChild 78 | const gpuImg = trArray[1].querySelectorAll('td')[2].firstElementChild 79 | cpuImg.setAttribute('src', '/svg/cancel.svg') 80 | cpuImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 81 | cpuImg.classList.add('filter-red') 82 | gpuImg.setAttribute('src', '/svg/cancel.svg') 83 | gpuImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 84 | gpuImg.classList.add('filter-red') 85 | firstImg.src = '/svg/cancel.svg' 86 | firstImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 87 | firstImg.classList.add('filter-red') 88 | h1.innerHTML = 'CPU doesn\'t have integrated graphics' 89 | h1.nextElementSibling.innerHTML = 'Please, choose GPU or CPU with integrated graphics' 90 | h1.nextElementSibling.style.display = '' 91 | return; 92 | } 93 | 94 | const selectedMemories = parseInt(ramValue.nextElementSibling.nextElementSibling.value) 95 | const memorySlots = parseInt(motherboardValueArray[2]) 96 | 97 | if (selectedMemories > memorySlots) { 98 | const motheboardImg = trArray[2].querySelectorAll('td')[2].firstElementChild 99 | const ramImg = trArray[3].querySelectorAll('td')[2].firstElementChild 100 | motheboardImg.setAttribute('src', '/svg/cancel.svg') 101 | motheboardImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 102 | motheboardImg.classList.add('filter-red') 103 | ramImg.setAttribute('src', '/svg/cancel.svg') 104 | ramImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 105 | ramImg.classList.add('filter-red') 106 | firstImg.src = '/svg/cancel.svg' 107 | firstImg.classList.remove('filter-green', 'filter-red', 'filter-grey') 108 | firstImg.classList.add('filter-red') 109 | h1.innerHTML = 'Motherboard supports only ' + memorySlots + ' memory slots' 110 | h1.nextElementSibling.innerHTML = 'Please, choose another motherboard or decrease RAM modules' 111 | h1.nextElementSibling.style.display = '' 112 | return; 113 | } 114 | 115 | firstImg.src = '/svg/tick.svg' 116 | firstImg.classList.remove('filter-green', 'filter-red') 117 | firstImg.classList.add('filter-green') 118 | h1.innerHTML = 'All components are compatible' 119 | h1.nextElementSibling.style.display = 'none' 120 | } 121 | 122 | function checkChipset(cpuManufacturer, cpuName, motherboardChipset) { 123 | if (motherboardChipset[1] === '5') 124 | return true 125 | 126 | if (cpuManufacturer === 'Intel') 127 | return cpuName.substring(8, 10) === '10' 128 | 129 | if (cpuName.substring(8, 11) === 'PRO') 130 | return cpuName[12] === '4' 131 | 132 | return cpuName[8] === '4' 133 | } 134 | 135 | function addGpu(tr, gpuValue) { 136 | const tdArray = tr.querySelectorAll('td') 137 | const img = tdArray[2].firstElementChild 138 | if (gpuValue === null) { 139 | tdArray[1].innerHTML = '' 140 | img.setAttribute('src', '/svg/cancel.svg') 141 | img.classList.remove('filter-green', 'filter-red', 'filter-grey') 142 | img.classList.add('filter-grey') 143 | return true; 144 | } 145 | 146 | const valueArray = gpuValue.value.split('|') 147 | tdArray[1].innerHTML = valueArray[0] + ' ' + valueArray[1] 148 | img.setAttribute('src', '/svg/tick.svg') 149 | img.classList.remove('filter-red', 'filter-red', 'filter-grey') 150 | img.classList.add('filter-green') 151 | return true; 152 | } 153 | 154 | function addRam(tr, ramValue) { 155 | const tdArray = tr.querySelectorAll('td') 156 | const img = tdArray[2].firstElementChild 157 | if (ramValue === null) { 158 | tdArray[1].innerHTML = '' 159 | img.setAttribute('src', '/svg/cancel.svg') 160 | img.classList.remove('filter-green') 161 | img.classList.add('filter-red') 162 | return false; 163 | } 164 | 165 | const quantity = ramValue.nextElementSibling.nextElementSibling.value 166 | const valueArray = ramValue.value.split('|') 167 | tdArray[1].innerHTML = quantity + 'x ' + valueArray[0] + ' ' + valueArray[1] + ' ' + valueArray[2] + 'GB ' + valueArray[3] + 'MHz' 168 | img.setAttribute('src', '/svg/tick.svg') 169 | img.classList.remove('filter-red') 170 | img.classList.add('filter-green') 171 | return true; 172 | } 173 | 174 | function addStorage(tr1, tr2, ssdValue, hddValue) { 175 | const tdArray1 = tr1.querySelectorAll('td') 176 | const tdArray2 = tr2.querySelectorAll('td') 177 | const img1 = tdArray1[2].firstElementChild 178 | const img2 = tdArray2[2].firstElementChild 179 | if (ssdValue === null && hddValue === null) { 180 | tdArray1[1].innerHTML = '' 181 | tdArray2[1].innerHTML = '' 182 | img1.setAttribute('src', '/svg/cancel.svg') 183 | img2.setAttribute('src', '/svg/cancel.svg') 184 | img1.classList.remove('filter-green', 'filter-red', 'filter-grey') 185 | img2.classList.remove('filter-green', 'filter-red', 'filter-grey') 186 | img1.classList.add('filter-red') 187 | img2.classList.add('filter-red') 188 | return false; 189 | } 190 | 191 | if (ssdValue !== null && hddValue === null) { 192 | const ssdQuantity = ssdValue.nextElementSibling.nextElementSibling.value 193 | const ssdValueArray = ssdValue.value.split('|') 194 | tdArray1[1].innerHTML = ssdQuantity + 'x ' + ssdValueArray[0] + ' ' + ssdValueArray[1] 195 | tdArray2[1].innerHTML = '' 196 | img1.setAttribute('src', '/svg/tick.svg') 197 | img2.setAttribute('src', '/svg/cancel.svg') 198 | img1.classList.remove('filter-green', 'filter-red', 'filter-grey') 199 | img2.classList.remove('filter-green', 'filter-red', 'filter-grey') 200 | img1.classList.add('filter-green') 201 | img2.classList.add('filter-grey') 202 | return true; 203 | } 204 | if (ssdValue === null && hddValue !== null) { 205 | const hddQuantity = hddValue.nextElementSibling.nextElementSibling.value 206 | const hddValueArray = hddValue.value.split('|') 207 | tdArray1[1].innerHTML = '' 208 | tdArray2[1].innerHTML = hddQuantity + 'x ' + hddValueArray[0] + ' ' + hddValueArray[1] 209 | img1.setAttribute('src', '/svg/cancel.svg') 210 | img2.setAttribute('src', '/svg/tick.svg') 211 | img1.classList.remove('filter-green', 'filter-red', 'filter-grey') 212 | img2.classList.remove('filter-green', 'filter-red', 'filter-grey') 213 | img1.classList.add('filter-grey') 214 | img2.classList.add('filter-green') 215 | return true; 216 | } 217 | 218 | const ssdQuantity = ssdValue.nextElementSibling.nextElementSibling.value 219 | const hddQuantity = hddValue.nextElementSibling.nextElementSibling.value 220 | const hddValueArray = hddValue.value.split('|') 221 | const ssdValueArray = ssdValue.value.split('|') 222 | tdArray1[1].innerHTML = ssdQuantity + 'x ' + ssdValueArray[0] + ' ' + ssdValueArray[1] 223 | tdArray2[1].innerHTML = hddQuantity + 'x ' + hddValueArray[0] + ' ' + hddValueArray[1] 224 | img1.setAttribute('src', '/svg/tick.svg') 225 | img2.setAttribute('src', '/svg/tick.svg') 226 | img1.classList.remove('filter-green', 'filter-red', 'filter-grey') 227 | img2.classList.remove('filter-green', 'filter-red', 'filter-grey') 228 | img1.classList.add('filter-green') 229 | img2.classList.add('filter-green') 230 | return true; 231 | } 232 | 233 | function addComponent(tr, element) { 234 | const tdArray = tr.querySelectorAll('td') 235 | const img = tdArray[2].firstElementChild 236 | if (element === null) { 237 | tdArray[1].innerHTML = '' 238 | img.setAttribute('src', '/svg/cancel.svg') 239 | img.classList.remove('filter-green', 'filter-red', 'filter-grey') 240 | img.classList.add('filter-red') 241 | return false; 242 | } 243 | 244 | const valueArray = element.value.split('|') 245 | tdArray[1].innerHTML = valueArray[0] + ' ' + valueArray[1] 246 | img.setAttribute('src', '/svg/tick.svg') 247 | img.classList.remove('filter-green', 'filter-red', 'filter-grey') 248 | img.classList.add('filter-green') 249 | return true; 250 | } 251 | 252 | /** All cases: 253 | 1. [x] Please, choose all required components 254 | 2. [x] CPU and Motherboard are incompatible. Please, choose another CPU or Motherboard 255 | 3. [x] CPU is incompatible with motherboard's chipset. Please, choose another CPU or Motherboard 256 | 3. [x] CPU doesn't have integrated graphics. Please, choose GPU or CPU with integrated graphics. 257 | 4. Motherboard don't support 4 memory slots. Please, choose another motherboard or decrease RAMs modules 258 | 259 | 1. (x) CPU and Motherboard are incompatible. Please, choose another CPU or Motherboard 260 | 2. (x) CPU doesn't have integrated graphics. Please, choose GPU or CPU with integrated graphics. 261 | 3. (x) Please, choose all required components 262 | *list of all components* 263 | 4. (x) Some components are incompatible 264 | 5. () All components are compatible 265 | */ -------------------------------------------------------------------------------- /src/main/resources/static/js/export-pdf.js: -------------------------------------------------------------------------------- 1 | function exportPdf() { 2 | const cpuValue = document.querySelector("input[name=cpu]:checked") // manufacturer, name, socket, integratedGraphics 3 | const gpuValue = document.querySelector("input[name=gpu]:checked") 4 | const motherboardValue = document.querySelector("input[name=motherboard]:checked") // manufacturer, name, memorySlots, socket, chipset 5 | const ramValue = document.querySelector("input[name=ram]:checked") 6 | const powerValue = document.querySelector("input[name=power]:checked") 7 | const coolerValue = document.querySelector("input[name=cooler]:checked") 8 | const ssdValue = document.querySelector("input[name=ssd]:checked") 9 | const hddValue = document.querySelector("input[name=hdd]:checked") 10 | const caseValue = document.querySelector("input[name=case]:checked") 11 | 12 | const template = document.getElementById('pdf-template') 13 | const tdArray = template.querySelectorAll('td') 14 | tdArray[1].innerText = getComponentString(cpuValue) 15 | tdArray[3].innerText = getComponentString(gpuValue) 16 | tdArray[5].innerText = getComponentString(motherboardValue) 17 | 18 | tdArray[7].innerHTML = getRamString(ramValue) 19 | tdArray[9].innerText = getComponentString(powerValue) 20 | tdArray[11].innerText = getComponentString(coolerValue) 21 | 22 | tdArray[13].innerText = getStorageString(ssdValue) 23 | tdArray[15].innerText = getStorageString(hddValue) 24 | tdArray[17].innerText = getComponentString(caseValue) 25 | 26 | // Setting the date 27 | const today = new Date() 28 | const date = String(today.getDate()).padStart(2, '0') + '.' + String(today.getMonth() + 1).padStart(2, '0') + '.' + today.getFullYear() 29 | const time = today.getHours() + ':' + today.getMinutes() 30 | document.getElementById('date').lastElementChild.innerHTML = date + ' ' + time.padStart(2, '0') 31 | 32 | // Exporting PDF 33 | const opt = { 34 | margin: 0.5, 35 | filename: 'configuration.pdf', 36 | image: {type: 'jpeg', quality: '1'}, 37 | html2canvas: {scale: 2}, 38 | jsPDF: {unit: 'in', format: 'A4', orientation: 'portrait'} 39 | }; 40 | html2pdf().set(opt).from(template).save() 41 | } 42 | 43 | function getComponentString(element) { 44 | if (element === null) 45 | return '' 46 | 47 | const elementArray = element.value.split('|') 48 | return elementArray[0] + ' ' + elementArray[1] 49 | } 50 | 51 | function getRamString(ramValue) { 52 | if (ramValue === null) 53 | return '' 54 | 55 | const ramQuantity = ramValue.nextElementSibling.nextElementSibling.value 56 | const ramArray = ramValue.value.split('|') 57 | return ramQuantity + 'x ' + ramArray[0] + ' ' + ramArray[1] + ' ' + ramArray[2] + 'GB ' + ramArray[3] + 'MHz' 58 | } 59 | 60 | function getStorageString(storageValue) { 61 | if (storageValue === null) 62 | return '' 63 | 64 | const storageQuantity = storageValue.nextElementSibling.nextElementSibling.value 65 | const storageArray = storageValue.value.split('|') 66 | return storageQuantity + 'x ' + storageArray[0] + ' ' + storageArray[1] 67 | } -------------------------------------------------------------------------------- /src/main/resources/static/js/info-popup.js: -------------------------------------------------------------------------------- 1 | function popupCpu(manufacturer, name, releaseYear, fabricationProcess, TDP, socket, microarchitecture, codeName, cores, 2 | threads, baseFrequency, turboFrequency, L1CacheInstruction, L1CacheDate, L2Cache, L3Cache, 3 | unlockedMultiplier, memoryType, memoryBandwidth, memoryChannels, maxMemorySize, ECCMemory, 4 | integratedGraphics, PCIExpressRevision, PCIExpressLanes) { 5 | 6 | const element = document.getElementById('modal_body') 7 | element.innerHTML = '' 8 | 9 | const button = createButton() 10 | element.append(button) 11 | 12 | const img = createImg('cpu') 13 | element.append(img) 14 | 15 | const title = createTitle(manufacturer, name) 16 | element.append(title) 17 | 18 | const div = createDiv() 19 | element.append(div) 20 | 21 | // Main content 22 | let divRow = createDivRow() 23 | let span1 = createSpan('Release Year') 24 | divRow.append(span1) 25 | let span2 = createSpan(releaseYear) 26 | divRow.append(span2) 27 | div.append(divRow) 28 | 29 | divRow = createDivRow() 30 | span1 = createSpan('Fab. Process') 31 | divRow.append(span1) 32 | span2 = createSpan(fabricationProcess + ' nm') 33 | divRow.append(span2) 34 | div.append(divRow) 35 | 36 | divRow = createDivRow() 37 | span1 = createSpan('TDP') 38 | divRow.append(span1) 39 | span2 = createSpan(TDP + ' W') 40 | divRow.append(span2) 41 | div.append(divRow) 42 | 43 | divRow = createDivRow() 44 | span1 = createSpan('Socket') 45 | divRow.append(span1) 46 | span2 = createSpan(socket) 47 | divRow.append(span2) 48 | div.append(divRow) 49 | 50 | divRow = createDivRow() 51 | span1 = createSpan('Microarchitecture') 52 | divRow.append(span1) 53 | span2 = createSpan(microarchitecture) 54 | divRow.append(span2) 55 | div.append(divRow) 56 | 57 | divRow = createDivRow() 58 | span1 = createSpan('Codename') 59 | divRow.append(span1) 60 | span2 = createSpan(codeName) 61 | divRow.append(span2) 62 | div.append(divRow) 63 | 64 | divRow = createDivRow() 65 | span1 = createSpan('Cores') 66 | divRow.append(span1) 67 | span2 = createSpan(cores) 68 | divRow.append(span2) 69 | div.append(divRow) 70 | 71 | divRow = createDivRow() 72 | span1 = createSpan('Threads') 73 | divRow.append(span1) 74 | span2 = createSpan(threads) 75 | divRow.append(span2) 76 | div.append(divRow) 77 | 78 | divRow = createDivRow() 79 | span1 = createSpan('Base Frequency') 80 | divRow.append(span1) 81 | span2 = createSpan(baseFrequency + ' GHz') 82 | divRow.append(span2) 83 | div.append(divRow) 84 | 85 | divRow = createDivRow() 86 | span1 = createSpan('Turbo Frequency') 87 | divRow.append(span1) 88 | span2 = createSpan(turboFrequency + ' GHz') 89 | divRow.append(span2) 90 | div.append(divRow) 91 | 92 | divRow = createDivRow() 93 | span1 = createSpan('L1 Cache (Instruction)') 94 | divRow.append(span1) 95 | span2 = createSpan(L1CacheInstruction) 96 | divRow.append(span2) 97 | div.append(divRow) 98 | 99 | divRow = createDivRow() 100 | span1 = createSpan('L1 Cache (Data)') 101 | divRow.append(span1) 102 | span2 = createSpan(L1CacheDate) 103 | divRow.append(span2) 104 | div.append(divRow) 105 | 106 | divRow = createDivRow() 107 | span1 = createSpan('L2 Cache') 108 | divRow.append(span1) 109 | span2 = createSpan(L2Cache) 110 | divRow.append(span2) 111 | div.append(divRow) 112 | 113 | divRow = createDivRow() 114 | span1 = createSpan('L3 Cache') 115 | divRow.append(span1) 116 | span2 = createSpan(L3Cache) 117 | divRow.append(span2) 118 | div.append(divRow) 119 | 120 | divRow = createDivRow() 121 | span1 = createSpan('Unlocked Multiplier') 122 | divRow.append(span1) 123 | span2 = createSpan(unlockedMultiplier ? 'Yes' : 'No') 124 | divRow.append(span2) 125 | div.append(divRow) 126 | 127 | divRow = createDivRow() 128 | span1 = createSpan('Memory Type') 129 | divRow.append(span1) 130 | span2 = createSpan(memoryType) 131 | divRow.append(span2) 132 | div.append(divRow) 133 | 134 | divRow = createDivRow() 135 | span1 = createSpan('Memory Bandwidth') 136 | divRow.append(span1) 137 | span2 = createSpan(memoryBandwidth + " GB/s") 138 | divRow.append(span2) 139 | div.append(divRow) 140 | 141 | divRow = createDivRow() 142 | span1 = createSpan('Memory Channels') 143 | divRow.append(span1) 144 | span2 = createSpan(memoryChannels) 145 | divRow.append(span2) 146 | div.append(divRow) 147 | 148 | divRow = createDivRow() 149 | span1 = createSpan('Max. Memory Size') 150 | divRow.append(span1) 151 | span2 = createSpan(maxMemorySize + " GB") 152 | divRow.append(span2) 153 | div.append(divRow) 154 | 155 | divRow = createDivRow() 156 | span1 = createSpan('ECC Memory') 157 | divRow.append(span1) 158 | span2 = createSpan(ECCMemory ? 'Yes' : 'No') 159 | divRow.append(span2) 160 | div.append(divRow) 161 | 162 | divRow = createDivRow() 163 | span1 = createSpan('Integrated Graphics') 164 | divRow.append(span1) 165 | span2 = createSpan(integratedGraphics) 166 | divRow.append(span2) 167 | div.append(divRow) 168 | 169 | divRow = createDivRow() 170 | span1 = createSpan('PCI-Express') 171 | divRow.append(span1) 172 | span2 = createSpan(PCIExpressRevision) 173 | divRow.append(span2) 174 | div.append(divRow) 175 | 176 | divRow = createDivRow() 177 | span1 = createSpan('PCI-Express Lanes') 178 | divRow.append(span1) 179 | span2 = createSpan(PCIExpressLanes) 180 | divRow.append(span2) 181 | div.append(divRow) 182 | } 183 | 184 | function popupGpu( 185 | manufacturer, 186 | name, 187 | releaseYear, 188 | microarchitecture, 189 | fabricationProcess, 190 | TDP, 191 | interfacePort, 192 | multiGPUSupport, 193 | baseClock, 194 | boostClock, 195 | shadingUnits, 196 | L1Cache, 197 | L2Cache, 198 | L3Cache, 199 | memoryType, 200 | memorySize, 201 | memoryClock, 202 | memoryClockEffective, 203 | memoryBus, 204 | memoryBandwidth) { 205 | 206 | const element = document.getElementById('modal_body') 207 | element.innerHTML = '' 208 | 209 | const button = createButton() 210 | element.append(button) 211 | 212 | const img = createImg('gpu') 213 | element.append(img) 214 | 215 | const title = createTitle(manufacturer, name) 216 | element.append(title) 217 | 218 | const div = createDiv() 219 | element.append(div) 220 | 221 | // Main content 222 | let divRow = createDivRow() 223 | let span1 = createSpan('Release Year') 224 | divRow.append(span1) 225 | let span2 = createSpan(releaseYear) 226 | divRow.append(span2) 227 | div.append(divRow) 228 | 229 | divRow = createDivRow() 230 | span1 = createSpan('Fab. Process') 231 | divRow.append(span1) 232 | span2 = createSpan(fabricationProcess + ' nm') 233 | divRow.append(span2) 234 | div.append(divRow) 235 | 236 | divRow = createDivRow() 237 | span1 = createSpan('TDP') 238 | divRow.append(span1) 239 | span2 = createSpan(TDP + ' W') 240 | divRow.append(span2) 241 | div.append(divRow) 242 | 243 | divRow = createDivRow() 244 | span1 = createSpan('Interface') 245 | divRow.append(span1) 246 | span2 = createSpan(interfacePort) 247 | divRow.append(span2) 248 | div.append(divRow) 249 | 250 | divRow = createDivRow() 251 | span1 = createSpan('Microarchitecture') 252 | divRow.append(span1) 253 | span2 = createSpan(microarchitecture) 254 | divRow.append(span2) 255 | div.append(divRow) 256 | 257 | divRow = createDivRow() 258 | span1 = createSpan('Multi-GPU Support') 259 | divRow.append(span1) 260 | span2 = createSpan(multiGPUSupport) 261 | divRow.append(span2) 262 | div.append(divRow) 263 | 264 | divRow = createDivRow() 265 | span1 = createSpan('Base Clock') 266 | divRow.append(span1) 267 | span2 = createSpan(baseClock + ' MHz') 268 | divRow.append(span2) 269 | div.append(divRow) 270 | 271 | divRow = createDivRow() 272 | span1 = createSpan('Boost Clock') 273 | divRow.append(span1) 274 | span2 = createSpan(boostClock + ' MHz') 275 | divRow.append(span2) 276 | div.append(divRow) 277 | 278 | divRow = createDivRow() 279 | span1 = createSpan('Shading Units') 280 | divRow.append(span1) 281 | span2 = createSpan(shadingUnits) 282 | divRow.append(span2) 283 | div.append(divRow) 284 | 285 | divRow = createDivRow() 286 | span1 = createSpan('L1 Cache') 287 | divRow.append(span1) 288 | span2 = createSpan(L1Cache) 289 | divRow.append(span2) 290 | div.append(divRow) 291 | 292 | divRow = createDivRow() 293 | span1 = createSpan('L2 Cache') 294 | divRow.append(span1) 295 | span2 = createSpan(L2Cache) 296 | divRow.append(span2) 297 | div.append(divRow) 298 | 299 | divRow = createDivRow() 300 | span1 = createSpan('L3 Cache') 301 | divRow.append(span1) 302 | span2 = createSpan(L3Cache) 303 | divRow.append(span2) 304 | div.append(divRow) 305 | 306 | divRow = createDivRow() 307 | span1 = createSpan('Memory Type') 308 | divRow.append(span1) 309 | span2 = createSpan(memoryType) 310 | divRow.append(span2) 311 | div.append(divRow) 312 | 313 | divRow = createDivRow() 314 | span1 = createSpan('Memory Type') 315 | divRow.append(span1) 316 | span2 = createSpan(memoryType) 317 | divRow.append(span2) 318 | div.append(divRow) 319 | 320 | divRow = createDivRow() 321 | span1 = createSpan('Memory Size') 322 | divRow.append(span1) 323 | span2 = createSpan(memorySize + ' GB') 324 | divRow.append(span2) 325 | div.append(divRow) 326 | 327 | divRow = createDivRow() 328 | span1 = createSpan('Memory Clock') 329 | divRow.append(span1) 330 | span2 = createSpan(memoryClock + ' MHz') 331 | divRow.append(span2) 332 | div.append(divRow) 333 | 334 | divRow = createDivRow() 335 | span1 = createSpan('Memory Clock (Effective)') 336 | divRow.append(span1) 337 | span2 = createSpan(memoryClockEffective + ' MHz') 338 | divRow.append(span2) 339 | div.append(divRow) 340 | 341 | divRow = createDivRow() 342 | span1 = createSpan('Memory Bus') 343 | divRow.append(span1) 344 | span2 = createSpan(memoryBus + ' bits') 345 | divRow.append(span2) 346 | div.append(divRow) 347 | 348 | divRow = createDivRow() 349 | span1 = createSpan('Memory Bandwidth') 350 | divRow.append(span1) 351 | span2 = createSpan(memoryBandwidth + ' GB/s') 352 | divRow.append(span2) 353 | div.append(divRow) 354 | } 355 | 356 | function popupMotherboard( 357 | manufacturer, 358 | name, 359 | chipset, 360 | formFactor, 361 | memorySlots, 362 | socket, 363 | wifiSupport, 364 | bluetoothSupport, 365 | PCIExpressRevision 366 | ) { 367 | const element = document.getElementById('modal_body') 368 | element.innerHTML = '' 369 | 370 | const button = createButton() 371 | element.append(button) 372 | 373 | const img = createImg('motherboard') 374 | element.append(img) 375 | 376 | const title = createTitle(manufacturer, name) 377 | element.append(title) 378 | 379 | const div = createDiv() 380 | element.append(div) 381 | 382 | // Main content 383 | let divRow = createDivRow() 384 | let span1 = createSpan('Chipset') 385 | divRow.append(span1) 386 | let span2 = createSpan(chipset) 387 | divRow.append(span2) 388 | div.append(divRow) 389 | 390 | divRow = createDivRow() 391 | span1 = createSpan('Form Factor') 392 | divRow.append(span1) 393 | span2 = createSpan(formFactor) 394 | divRow.append(span2) 395 | div.append(divRow) 396 | 397 | divRow = createDivRow() 398 | span1 = createSpan('Memory Slots') 399 | divRow.append(span1) 400 | span2 = createSpan(memorySlots) 401 | divRow.append(span2) 402 | div.append(divRow) 403 | 404 | divRow = createDivRow() 405 | span1 = createSpan('Socket') 406 | divRow.append(span1) 407 | span2 = createSpan(socket) 408 | divRow.append(span2) 409 | div.append(divRow) 410 | 411 | divRow = createDivRow() 412 | span1 = createSpan('Wi-Fi Support') 413 | divRow.append(span1) 414 | span2 = createSpan(wifiSupport ? ' Yes' : 'No') 415 | divRow.append(span2) 416 | div.append(divRow) 417 | 418 | divRow = createDivRow() 419 | span1 = createSpan('Bluetooth Support') 420 | divRow.append(span1) 421 | span2 = createSpan(bluetoothSupport ? ' Yes' : 'No') 422 | divRow.append(span2) 423 | div.append(divRow) 424 | 425 | divRow = createDivRow() 426 | span1 = createSpan('PCI-Express Revision') 427 | divRow.append(span1) 428 | span2 = createSpan(PCIExpressRevision) 429 | divRow.append(span2) 430 | div.append(divRow) 431 | } 432 | 433 | function popupRam(manufacturer, name, speed, capacity, type) { 434 | 435 | const element = document.getElementById('modal_body') 436 | element.innerHTML = '' 437 | 438 | const button = createButton() 439 | element.append(button) 440 | 441 | const img = createImg('ram') 442 | element.append(img) 443 | 444 | const title = createTitle(manufacturer, name) 445 | element.append(title) 446 | 447 | const div = createDiv() 448 | element.append(div) 449 | 450 | // Main content 451 | let divRow = createDivRow() 452 | let span1 = createSpan('Speed') 453 | divRow.append(span1) 454 | let span2 = createSpan(speed + ' MHz') 455 | divRow.append(span2) 456 | div.append(divRow) 457 | 458 | divRow = createDivRow() 459 | span1 = createSpan('Capacity') 460 | divRow.append(span1) 461 | span2 = createSpan(capacity + ' GB') 462 | divRow.append(span2) 463 | div.append(divRow) 464 | 465 | divRow = createDivRow() 466 | span1 = createSpan('Type') 467 | divRow.append(span1) 468 | span2 = createSpan(type) 469 | divRow.append(span2) 470 | div.append(divRow) 471 | } 472 | 473 | function popupPower(manufacturer, name, certificate80Plus, wattage) { 474 | const element = document.getElementById('modal_body') 475 | element.innerHTML = '' 476 | 477 | const button = createButton() 478 | element.append(button) 479 | 480 | const img = createImg('power') 481 | element.append(img) 482 | 483 | const title = createTitle(manufacturer, name) 484 | element.append(title) 485 | 486 | const div = createDiv() 487 | element.append(div) 488 | 489 | // Main content 490 | let divRow = createDivRow() 491 | let span1 = createSpan('Certificate 80Plus') 492 | divRow.append(span1) 493 | let span2 = createSpan(certificate80Plus) 494 | divRow.append(span2) 495 | div.append(divRow) 496 | 497 | divRow = createDivRow() 498 | span1 = createSpan('Wattage') 499 | divRow.append(span1) 500 | span2 = createSpan(wattage + ' W') 501 | divRow.append(span2) 502 | div.append(divRow) 503 | } 504 | 505 | function popupCooler(manufacturer, name, type, dimensions, TDP, fanCount) { 506 | const element = document.getElementById('modal_body') 507 | element.innerHTML = '' 508 | 509 | const button = createButton() 510 | element.append(button) 511 | 512 | const img = createImg('cooler') 513 | element.append(img) 514 | 515 | const title = createTitle(manufacturer, name) 516 | element.append(title) 517 | 518 | const div = createDiv() 519 | element.append(div) 520 | 521 | // Main content 522 | let divRow = createDivRow() 523 | let span1 = createSpan('Type') 524 | divRow.append(span1) 525 | let span2 = createSpan(type) 526 | divRow.append(span2) 527 | div.append(divRow) 528 | 529 | divRow = createDivRow() 530 | span1 = createSpan('Dimensions (L x W x H)') 531 | divRow.append(span1) 532 | span2 = createSpan(dimensions) 533 | divRow.append(span2) 534 | div.append(divRow) 535 | 536 | divRow = createDivRow() 537 | span1 = createSpan('TDP') 538 | divRow.append(span1) 539 | span2 = createSpan(TDP + ' W') 540 | divRow.append(span2) 541 | div.append(divRow) 542 | 543 | divRow = createDivRow() 544 | span1 = createSpan('Fans') 545 | divRow.append(span1) 546 | span2 = createSpan(fanCount) 547 | divRow.append(span2) 548 | div.append(divRow) 549 | } 550 | 551 | function popupSsd(manufacturer, name, capacity, readSpeed, writeSpeed, totalBytesWritten, formFactor) { 552 | const element = document.getElementById('modal_body') 553 | element.innerHTML = '' 554 | 555 | const button = createButton() 556 | element.append(button) 557 | 558 | const img = createImg('ssd') 559 | element.append(img) 560 | 561 | const title = createTitle(manufacturer, name) 562 | element.append(title) 563 | 564 | const div = createDiv() 565 | element.append(div) 566 | 567 | // Main content 568 | let divRow = createDivRow() 569 | let span1 = createSpan('Capacity') 570 | divRow.append(span1) 571 | let span2 = createSpan(capacity + ' GB') 572 | divRow.append(span2) 573 | div.append(divRow) 574 | 575 | divRow = createDivRow() 576 | span1 = createSpan('Read Speed') 577 | divRow.append(span1) 578 | span2 = createSpan(readSpeed + ' MB/s') 579 | divRow.append(span2) 580 | div.append(divRow) 581 | 582 | divRow = createDivRow() 583 | span1 = createSpan('Write Speed') 584 | divRow.append(span1) 585 | span2 = createSpan(writeSpeed + ' MB/s') 586 | divRow.append(span2) 587 | div.append(divRow) 588 | 589 | divRow = createDivRow() 590 | span1 = createSpan('Total Bytes Written (TBW)') 591 | divRow.append(span1) 592 | span2 = createSpan(totalBytesWritten + ' TB') 593 | divRow.append(span2) 594 | div.append(divRow) 595 | 596 | divRow = createDivRow() 597 | span1 = createSpan('Form Factor') 598 | divRow.append(span1) 599 | span2 = createSpan(formFactor) 600 | divRow.append(span2) 601 | div.append(divRow) 602 | } 603 | 604 | function popupHdd(manufacturer, name, capacity, readSpeed, writeSpeed, cache, formFactor) { 605 | const element = document.getElementById('modal_body') 606 | element.innerHTML = '' 607 | 608 | const button = createButton() 609 | element.append(button) 610 | 611 | const img = createImg('hdd') 612 | element.append(img) 613 | 614 | const title = createTitle(manufacturer, name) 615 | element.append(title) 616 | 617 | const div = createDiv() 618 | element.append(div) 619 | 620 | // Main content 621 | let divRow = createDivRow() 622 | let span1 = createSpan('Capacity') 623 | divRow.append(span1) 624 | let span2 = createSpan(capacity + ' TB') 625 | divRow.append(span2) 626 | div.append(divRow) 627 | 628 | divRow = createDivRow() 629 | span1 = createSpan('Read Speed') 630 | divRow.append(span1) 631 | span2 = createSpan(readSpeed + ' MB/s') 632 | divRow.append(span2) 633 | div.append(divRow) 634 | 635 | divRow = createDivRow() 636 | span1 = createSpan('Write Speed') 637 | divRow.append(span1) 638 | span2 = createSpan(writeSpeed + ' MB/s') 639 | divRow.append(span2) 640 | div.append(divRow) 641 | 642 | divRow = createDivRow() 643 | span1 = createSpan('Cache') 644 | divRow.append(span1) 645 | span2 = createSpan(cache + ' MB') 646 | divRow.append(span2) 647 | div.append(divRow) 648 | 649 | divRow = createDivRow() 650 | span1 = createSpan('Form Factor') 651 | divRow.append(span1) 652 | span2 = createSpan(formFactor) 653 | divRow.append(span2) 654 | div.append(divRow) 655 | } 656 | 657 | function popupCase(manufacturer, name, size, dimensions, weight, maxCoolerHeight, maxGpuLength) { 658 | const element = document.getElementById('modal_body') 659 | element.innerHTML = '' 660 | 661 | const button = createButton() 662 | element.append(button) 663 | 664 | const img = createImg('case') 665 | element.append(img) 666 | 667 | const title = createTitle(manufacturer, name) 668 | element.append(title) 669 | 670 | const div = createDiv() 671 | element.append(div) 672 | 673 | // Main content 674 | let divRow = createDivRow() 675 | let span1 = createSpan('Size') 676 | divRow.append(span1) 677 | let span2 = createSpan(size) 678 | divRow.append(span2) 679 | div.append(divRow) 680 | 681 | divRow = createDivRow() 682 | span1 = createSpan('Dimensions') 683 | divRow.append(span1) 684 | span2 = createSpan(dimensions) 685 | divRow.append(span2) 686 | div.append(divRow) 687 | 688 | divRow = createDivRow() 689 | span1 = createSpan('Weight') 690 | divRow.append(span1) 691 | span2 = createSpan(weight + ' KG') 692 | divRow.append(span2) 693 | div.append(divRow) 694 | 695 | divRow = createDivRow() 696 | span1 = createSpan('Max Cooler Height') 697 | divRow.append(span1) 698 | span2 = createSpan(maxCoolerHeight + ' mm') 699 | divRow.append(span2) 700 | div.append(divRow) 701 | 702 | divRow = createDivRow() 703 | span1 = createSpan('Max GPU Length') 704 | divRow.append(span1) 705 | span2 = createSpan(maxGpuLength + ' mm') 706 | divRow.append(span2) 707 | div.append(divRow) 708 | } 709 | 710 | //region Styles 711 | function createDivRow() { 712 | const divRow = document.createElement('div') 713 | divRow.classList.add('row', 'border-bottom', 'py-2') 714 | return divRow 715 | } 716 | 717 | function createSpan(innerText) { 718 | const span = document.createElement('span') 719 | span.classList.add('col', 'fs-5') 720 | span.innerText = innerText 721 | return span 722 | } 723 | 724 | function createButton() { 725 | const button = document.createElement('button') 726 | button.setAttribute('type', 'button') 727 | button.setAttribute('data-bs-dismiss', 'modal') 728 | button.setAttribute('aria-label', 'Close') 729 | button.classList.add('btn-close', 'align-self-end', 'mt-2', 'me-2') 730 | return button 731 | } 732 | 733 | function createImg(imageName) { 734 | const img = document.createElement('img') 735 | img.classList.add('align-self-center') 736 | img.width = 250 737 | img.src = '/svg/' + imageName + '.svg' 738 | img.alt = '#'; 739 | return img 740 | } 741 | 742 | function createTitle(manufacturer, name) { 743 | const title = document.createElement('span') 744 | title.classList.add('fs-2', 'ms-3', 'mt-3', 'mb-1') 745 | title.innerText = manufacturer + ' ' + name 746 | return title 747 | } 748 | 749 | function createDiv() { 750 | const div = document.createElement('div') 751 | div.classList.add('mx-3', 'mb-3') 752 | return div 753 | } 754 | 755 | //endregion -------------------------------------------------------------------------------- /src/main/resources/static/js/scrolling.js: -------------------------------------------------------------------------------- 1 | // Code for changing active link on clicking 2 | let buttons = $("#nav-main .my-navbar-nav .my-nav-link"); 3 | for (let i = 0; i < buttons.length; i++) { 4 | buttons[i].addEventListener("click", function () { 5 | let current = document.getElementsByClassName("active"); 6 | 7 | if (current.length > 0) { 8 | current[0].className = current[0].className.replace(" active", ""); 9 | } 10 | 11 | this.className += " active"; 12 | }); 13 | } 14 | 15 | // Code for changing active link on Scrolling 16 | $(window).scroll(function () { 17 | let distance = $(window).scrollTop(); 18 | $('.page-section').each(function (i) { 19 | 20 | if ($(this).position().top 21 | <= distance + 250) { 22 | 23 | $('.my-navbar-nav a.active') 24 | .removeClass('active'); 25 | 26 | $('.my-navbar-nav a').eq(i) 27 | .addClass('active'); 28 | } 29 | }); 30 | }).scroll(); 31 | 32 | // For smooth scrolling on clicking 33 | $(document).ready(function () { 34 | // Add smooth scrolling to all links 35 | $("a").on('click', function (event) { 36 | 37 | // Make sure this.hash has a value before overriding default behavior 38 | if (this.hash !== "") { 39 | // Prevent default anchor click behavior 40 | event.preventDefault(); 41 | 42 | // Store hash 43 | let hash = this.hash; 44 | 45 | // Using jQuery's animate() method to add smooth page scroll 46 | // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area 47 | $('html, body').animate({ 48 | scrollTop: $(hash).offset().top 49 | }, 400, function () { 50 | 51 | // Add hash (#) to URL when done scrolling (default click behavior) 52 | window.location.hash = hash; 53 | }); 54 | } // End if 55 | }); 56 | }); -------------------------------------------------------------------------------- /src/main/resources/static/js/session.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { 2 | getAndCheck('cpu') 3 | getAndCheck('gpu') 4 | getAndCheck('motherboard') 5 | getAndCheckStorage('ram') 6 | getAndCheck('power') 7 | getAndCheck('cooler') 8 | getAndCheckStorage('ssd') 9 | getAndCheckStorage('hdd') 10 | getAndCheck('case') 11 | } 12 | 13 | window.onbeforeunload = function () { 14 | setOrRemoveItem('cpu') 15 | setOrRemoveItem('gpu') 16 | setOrRemoveItem('motherboard') 17 | setOrRemoveStorage('ram') 18 | setOrRemoveItem('power') 19 | setOrRemoveItem('cooler') 20 | setOrRemoveStorage('ssd') 21 | setOrRemoveStorage('hdd') 22 | setOrRemoveItem('case') 23 | } 24 | 25 | function setOrRemoveStorage(name) { 26 | const item = document.querySelector('input[name=' + name + ']:checked') 27 | if (item === null) { 28 | localStorage.removeItem(name) 29 | localStorage.removeItem(name + '-quantity') 30 | return 31 | } 32 | const itemQuantity = item.nextElementSibling.nextElementSibling.value 33 | localStorage.setItem(name, item.value) 34 | localStorage.setItem(name + '-quantity', itemQuantity) 35 | } 36 | 37 | function getAndCheckStorage(name) { 38 | const itemValue = localStorage.getItem(name) 39 | 40 | if (itemValue !== null) { 41 | const radio = document.querySelector('input[value="' + itemValue + '"]') 42 | radio.checked = true 43 | const select = radio.nextElementSibling.nextElementSibling 44 | select.hidden = false 45 | select.value = localStorage.getItem(name + '-quantity') 46 | } 47 | } 48 | 49 | function setOrRemoveItem(name) { 50 | const item = document.querySelector('input[name=' + name + ']:checked') 51 | if (item === null) { 52 | localStorage.removeItem(name) 53 | return 54 | } 55 | 56 | localStorage.setItem(name, item.value) 57 | } 58 | 59 | function getAndCheck(key) { 60 | const itemValue = localStorage.getItem(key); 61 | if (itemValue !== null) 62 | document.querySelector('input[value="' + itemValue + '"]').checked = true 63 | } 64 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/case-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/case.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 47 | 48 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/cooler.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | 57 | 58 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/cpu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 435 | 436 | 437 | 438 | 439 | 443 | 444 | 445 | 446 | 447 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/gpu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 98 | 99 | 100 | 101 | 102 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/hdd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/info.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/motherboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 42 | 43 | 44 | 45 | 46 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 125 | 126 | 127 | 128 | 129 | 133 | 134 | 135 | 136 | 137 | 142 | 143 | 144 | 145 | 146 | 150 | 151 | 152 | 153 | 154 | 159 | 160 | 161 | 162 | 163 | 168 | 169 | 170 | 171 | 172 | 177 | 178 | 179 | 180 | 181 | 186 | 187 | 188 | 189 | 190 | 192 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 201 | 202 | 204 | 205 | 206 | 207 | 208 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/power.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 66 | 67 | 68 | 69 | 70 | 74 | 75 | 76 | 77 | 78 | 84 | 85 | 86 | 87 | 88 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/ram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 21 | 22 | 23 | 24 | 25 | 41 | 42 | 43 | 44 | 45 | 61 | 62 | 63 | 64 | 65 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/ssd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 56 | 57 | 58 | 59 | 60 | 63 | 64 | 65 | 66 | 67 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/main/resources/static/svg/tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/test/kotlin/com/mutecomputers/ApplicationTests.kt: -------------------------------------------------------------------------------- 1 | package com.mutecomputers 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.springframework.boot.test.context.SpringBootTest 5 | 6 | @SpringBootTest 7 | class ApplicationTests { 8 | 9 | @Test 10 | fun contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------