initial survey renderer
30
build.gradle
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext {
|
||||||
|
gradleNodePluginVersion = '3.5.1'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "com.github.node-gradle:gradle-node-plugin:${gradleNodePluginVersion}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
if (it.name == 'survey') {
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'com.github.node-gradle.node'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'me.bvn13.tl-esa-tools'
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
gradle.properties
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.console = plain
|
||||||
|
org.gradle.logging.level = info
|
||||||
|
org.gradle.jvmargs = -Xmx4G
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Wed Dec 21 12:15:26 MSK 2022
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
185
gradlew
vendored
Executable file
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MSYS* | MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
20
q/template.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"title": "",
|
||||||
|
"intro": "",
|
||||||
|
"settings": {
|
||||||
|
"randomizeQuestions": false,
|
||||||
|
"randomizeOptions": false
|
||||||
|
},
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"group" : "",
|
||||||
|
"question": "",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
settings.gradle
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
rootProject.name = 'tl-esa-tools'
|
||||||
|
|
||||||
|
include 'survey'
|
8
site/.htaccess
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
Options -MultiViews
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ index.html [QSA,L]
|
||||||
|
</IfModule>
|
33
site/app/index.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* src/index.css */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family:
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
"Roboto",
|
||||||
|
"Oxygen",
|
||||||
|
"Ubuntu",
|
||||||
|
"Cantarell",
|
||||||
|
"Fira Sans",
|
||||||
|
"Droid Sans",
|
||||||
|
"Helvetica Neue",
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background-color: rgb(250 250 250);
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
font-family:
|
||||||
|
source-code-pro,
|
||||||
|
Menlo,
|
||||||
|
Monaco,
|
||||||
|
Consolas,
|
||||||
|
"Courier New",
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* src/App.css */
|
||||||
|
|
||||||
|
/* src/components/survey/Survey.css */
|
||||||
|
/*# sourceMappingURL=index.css.map */
|
7
site/app/index.css.map
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"sources": ["../../survey/src/index.css"],
|
||||||
|
"sourcesContent": ["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n background-color: rgb(250 250 250);\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n"],
|
||||||
|
"mappings": ";AAAA;AACI;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA;AACA;AACA;AAAA;AAGJ;AACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
||||||
|
"names": []
|
||||||
|
}
|
41373
site/app/index.js
Normal file
7
site/app/index.js.map
Normal file
BIN
site/favicon.ico
Normal file
After Width: | Height: | Size: 146 KiB |
25
site/index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description"
|
||||||
|
content="TL :: ESA"
|
||||||
|
/>
|
||||||
|
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<link rel="stylesheet" href="/app/index.css" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||||
|
/>
|
||||||
|
<title>TL :: ESA</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script src="/app/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
site/logo-big.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
site/logo192.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
site/logo512.png
Normal file
After Width: | Height: | Size: 50 KiB |
25
site/manifest.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
295
site/q/agile-compass.json
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
{
|
||||||
|
"title": "АGILE-КОМПАС",
|
||||||
|
"intro": "В этом тесте 15 вопросов, которые помогут определить оптимальную комбинацию целей на спринт по сферам и уровням.\n\nВаша задача — вспомнить, как часто за последний месяц вы испытывали то, что указано в каждом вопросе.\n\nЭтот тест рекомендуется проходить каждый спринт перед постановкой целей.",
|
||||||
|
"settings": {
|
||||||
|
"randomizeQuestions": false,
|
||||||
|
"randomizeOptions": false
|
||||||
|
},
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"group" : "1",
|
||||||
|
"title": "Я чувствую себя уставшим(ей) даже после длительного сна и отдыха",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "2",
|
||||||
|
"title": "Я могу разозлиться без видимой причины, меня раздражают позитивные и успешные люди",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мне сложно концентрироваться на задаче больше 5 минут",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я чувствую собственную беспомощность, кажется, что от меня ничего не зависит",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Все вокруг кажется пустым и неинтересным",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я чувствую, что мне не к кому обратиться за помощью и поддержкой в сложной ситуации",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я думаю, что близкие ценят меня за то, что я делаю и как себя веду, а не просто за то, что я есть",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "В кругу близких и друзей я вынужден(а) играть чужую роль, чтобы меня принимали таким(ой), какой(ая) я есть",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я чувствую, что никому не нужен (нужна) и никто меня не любит",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мне кажется, что никто из близких меня не понимает",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я боюсь потерять работу, потому что это приведет к финансовым трудностям",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мне не хватает денег на питание, оплату жилья и на то, чтобы покрыть привычные расходы",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я финансово завишу от других (родителей, супруга(и), босса, партнеров по бизнесу)",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я боюсь, что перемены в стране, отрасли, в которой я работаю, или экономике лишат меня средств к существованиюЭто обязательный вопрос",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мой доход скачет (то густо, то пусто) и находится вне моего контроля",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
20
site/q/template.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"title": "",
|
||||||
|
"intro": "",
|
||||||
|
"settings": {
|
||||||
|
"randomizeQuestions": false,
|
||||||
|
"randomizeOptions": false
|
||||||
|
},
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"group" : "",
|
||||||
|
"question": "",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
site/robots.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
41
survey/.eslintrc.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended'],
|
||||||
|
plugins: ['@typescript-eslint', 'react-hooks', 'prettier'],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2018,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true,
|
||||||
|
jest: true
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"no-control-regex": 0,
|
||||||
|
'no-undef': 0,
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'react/prop-types': 0,
|
||||||
|
'@typescript-eslint/camelcase': 0,
|
||||||
|
'@typescript-eslint/no-unused-vars': 1,
|
||||||
|
'@typescript-eslint/no-use-before-define': 0,
|
||||||
|
'@typescript-eslint/ban-ts-comment': 0,
|
||||||
|
'@typescript-eslint/ban-ts-ignore': 0,
|
||||||
|
'@typescript-eslint/explicit-member-accessibility': 0,
|
||||||
|
'@typescript-eslint/member-delimiter-style': 0,
|
||||||
|
'@typescript-eslint/no-empty-function': 0,
|
||||||
|
'@typescript-eslint/no-explicit-any': 0,
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn'
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
survey/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 160,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": false,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": true
|
||||||
|
}
|
140
survey/README.md
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<h1 align="center">New React App</h1>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
This is a blank README file that you can customize at your needs.\
|
||||||
|
Describe your project, how it works and how to contribute to it.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# 🚀 Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## ⚡️ start
|
||||||
|
|
||||||
|
```
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## 🧪 test
|
||||||
|
|
||||||
|
```
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn test
|
||||||
|
```
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## 🦾 build
|
||||||
|
|
||||||
|
```
|
||||||
|
npm build
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## 🧶 lint
|
||||||
|
|
||||||
|
```
|
||||||
|
npm lint
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Creates a `.eslintcache` file in which ESLint cache is stored. Running this command can dramatically improve ESLint's running time by ensuring that only changed files are linted.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
## 🎯 format
|
||||||
|
|
||||||
|
```
|
||||||
|
npm format
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn format
|
||||||
|
```
|
||||||
|
|
||||||
|
Checks if your files are formatted. This command will output a human-friendly message and a list of unformatted files, if any.
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
# 🧬 Project structure
|
||||||
|
|
||||||
|
This is the structure of the files in the project:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
│
|
||||||
|
├── public # public files (favicon, .htaccess, manifest, ...)
|
||||||
|
├── src # source files
|
||||||
|
│ ├── components
|
||||||
|
│ ├── pages
|
||||||
|
│ ├── resources # images, constants and other static resources
|
||||||
|
│ ├── store # Redux store
|
||||||
|
│ │ ├── actions # store's actions
|
||||||
|
│ │ └── reducers # store's reducers
|
||||||
|
│ ├── styles
|
||||||
|
│ ├── tests # all test files
|
||||||
|
│ ├── types # data interfaces
|
||||||
|
│ ├── utility # utilities functions and custom components
|
||||||
|
│ ├── App.tsx
|
||||||
|
│ ├── index.tsx
|
||||||
|
│ ├── react-app-env.d.ts
|
||||||
|
│ ├── RootComponent.tsx # React component with all the routes
|
||||||
|
│ ├── serviceWorker.ts
|
||||||
|
│ └── setupTests.ts
|
||||||
|
├── .eslintrc.js
|
||||||
|
├── .gitignore
|
||||||
|
├── .prettierrc
|
||||||
|
├── package.json
|
||||||
|
├── README.md
|
||||||
|
└── tsconfig.json
|
||||||
|
```
|
||||||
|
|
||||||
|
# 📖 Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
<p align="center">Bootstrapped with Create React App.</p>
|
93
survey/build.gradle
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// https://dzone.com/articles/integrating-java-and-npm-builds-using-gradle
|
||||||
|
// https://github.com/node-gradle/gradle-node-plugin/blob/master/examples/vertx-react/build.gradle
|
||||||
|
plugins {
|
||||||
|
id "base"
|
||||||
|
// You have to specify the plugin version, for instance
|
||||||
|
// id "com.github.node-gradle.node" version "3.0.0"
|
||||||
|
// This works as is here because we use the plugin source
|
||||||
|
id "com.github.node-gradle.node"
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/node-gradle/gradle-node-plugin/blob/master/examples/simple-node/npm/build.gradle
|
||||||
|
node {
|
||||||
|
/* gradle-node-plugin configuration
|
||||||
|
https://github.com/srs/gradle-node-plugin/blob/master/docs/node.md
|
||||||
|
|
||||||
|
Task name pattern:
|
||||||
|
./gradlew npm_<command> Executes an NPM command.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Version of node to use.
|
||||||
|
version = '18.12.1'
|
||||||
|
|
||||||
|
// Version of npm to use.
|
||||||
|
npmVersion = '8.1.4'
|
||||||
|
|
||||||
|
// Version of Yarn
|
||||||
|
yarnVersion = '1.22.19'
|
||||||
|
|
||||||
|
// If true, it will download node using above parameters.
|
||||||
|
// If false, it will try to use globally installed node.
|
||||||
|
download = true
|
||||||
|
}
|
||||||
|
|
||||||
|
def yarn = tasks.named("yarn")
|
||||||
|
|
||||||
|
def copyTask = tasks.register("copyPublic", Copy) {
|
||||||
|
dependsOn(yarn)
|
||||||
|
mustRunAfter(yarn)
|
||||||
|
from "public"
|
||||||
|
into "${buildDir}/../../site"
|
||||||
|
println("copied")
|
||||||
|
}
|
||||||
|
|
||||||
|
def rewriteContent = tasks.register("rewriteFileContent") {
|
||||||
|
dependsOn(copyTask)
|
||||||
|
mustRunAfter(copyTask)
|
||||||
|
inputs.dir("public")
|
||||||
|
doLast {
|
||||||
|
def publicPath = ""
|
||||||
|
def path = "${buildDir}/../../site"
|
||||||
|
|
||||||
|
ant.replaceregexp(match: '%PUBLIC_URL%', replace: publicPath, flags: 'g', byline: true) {
|
||||||
|
fileset(dir: path, includes: 'index.html,manifest.json')
|
||||||
|
}
|
||||||
|
println("rewritten")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.metachris.com/2021/04/starting-a-typescript-project-in-2021/
|
||||||
|
def buildTask = tasks.register("buildWebapp", NpxTask) {
|
||||||
|
command = "esbuild"
|
||||||
|
args = ["src/index.tsx", "--bundle", "--sourcemap", "--outfile=${buildDir}/../../site/app/index.js"]
|
||||||
|
dependsOn(rewriteContent)
|
||||||
|
mustRunAfter(rewriteContent)
|
||||||
|
inputs.dir(fileTree("src").exclude("**/*.test.js").exclude("**/*.spec.js").exclude("**/__tests__/**/*.js"))
|
||||||
|
inputs.dir("node_modules")
|
||||||
|
inputs.dir("public")
|
||||||
|
outputs.dir("${buildDir}/../../site")
|
||||||
|
environment = ["BUILD_PATH": "${buildDir}/../../site"]
|
||||||
|
println("built")
|
||||||
|
}
|
||||||
|
|
||||||
|
def testTask = tasks.register("testWebapp", NpxTask) {
|
||||||
|
command = "react-scripts"
|
||||||
|
args = ["test"]
|
||||||
|
dependsOn(yarn)
|
||||||
|
inputs.dir("node_modules")
|
||||||
|
inputs.dir("src")
|
||||||
|
inputs.dir("public")
|
||||||
|
outputs.upToDateWhen { true }
|
||||||
|
environment = ['CI': 'true']
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
java {
|
||||||
|
main {
|
||||||
|
resources {
|
||||||
|
// This makes the processResources task automatically depend on the buildWebapp one
|
||||||
|
srcDir(buildTask)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
survey/package.json
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"name": "my-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@reduxjs/toolkit": "^1.9.5",
|
||||||
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
|
"@testing-library/react": "^14.0.0",
|
||||||
|
"@testing-library/user-event": "^14.4.3",
|
||||||
|
"axios": "^0.27.2",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"moment": "^2.29.4",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"react-redux": "^8.0.5",
|
||||||
|
"react-router-dom": "^6.11.0",
|
||||||
|
"react-scripts": "^5.0.1",
|
||||||
|
"redux-persist": "^6.0.0",
|
||||||
|
"redux-thunk": "^2.4.2",
|
||||||
|
"sass": "^1.62.1",
|
||||||
|
"sass-loader": "^13.2.2",
|
||||||
|
"@mui/material": "^5.12.0",
|
||||||
|
"@mui/styles": "^5.12.0",
|
||||||
|
"notistack": "^3.0.1",
|
||||||
|
"@emotion/styled": "^11.10.5",
|
||||||
|
"@emotion/core": "^11.0.0",
|
||||||
|
"@emotion/react": "^11.10.5"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject",
|
||||||
|
"lint": "eslint --cache .",
|
||||||
|
"format": "prettier --check ."
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": "react-app"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"defaults",
|
||||||
|
"not IE 11"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^29.5.1",
|
||||||
|
"@types/node": "^18.16.3",
|
||||||
|
"@types/react": "^18.2.0",
|
||||||
|
"@types/react-dom": "^18.2.1",
|
||||||
|
"@types/react-redux": "^7.1.25",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
|
},
|
||||||
|
"homepage": ".",
|
||||||
|
"postcss": {
|
||||||
|
"plugins": {
|
||||||
|
"autoprefixer": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
survey/public/.htaccess
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
Options -MultiViews
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
RewriteRule ^ index.html [QSA,L]
|
||||||
|
</IfModule>
|
BIN
survey/public/favicon.ico
Normal file
After Width: | Height: | Size: 146 KiB |
25
survey/public/index.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description"
|
||||||
|
content="TL :: ESA"
|
||||||
|
/>
|
||||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<link rel="stylesheet" href="%PUBLIC_URL%/app/index.css" />
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||||
|
/>
|
||||||
|
<title>TL :: ESA</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script src="%PUBLIC_URL%/app/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
survey/public/logo-big.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
survey/public/logo192.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
survey/public/logo512.png
Normal file
After Width: | Height: | Size: 50 KiB |
25
survey/public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "logo512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
295
survey/public/q/agile-compass.json
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
{
|
||||||
|
"title": "АGILE-КОМПАС",
|
||||||
|
"intro": "В этом тесте 15 вопросов, которые помогут определить оптимальную комбинацию целей на спринт по сферам и уровням.\n\nВаша задача — вспомнить, как часто за последний месяц вы испытывали то, что указано в каждом вопросе.\n\nЭтот тест рекомендуется проходить каждый спринт перед постановкой целей.",
|
||||||
|
"settings": {
|
||||||
|
"randomizeQuestions": false,
|
||||||
|
"randomizeOptions": false
|
||||||
|
},
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"group" : "1",
|
||||||
|
"title": "Я чувствую себя уставшим(ей) даже после длительного сна и отдыха",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "2",
|
||||||
|
"title": "Я могу разозлиться без видимой причины, меня раздражают позитивные и успешные люди",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мне сложно концентрироваться на задаче больше 5 минут",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я чувствую собственную беспомощность, кажется, что от меня ничего не зависит",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Все вокруг кажется пустым и неинтересным",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я чувствую, что мне не к кому обратиться за помощью и поддержкой в сложной ситуации",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я думаю, что близкие ценят меня за то, что я делаю и как себя веду, а не просто за то, что я есть",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "В кругу близких и друзей я вынужден(а) играть чужую роль, чтобы меня принимали таким(ой), какой(ая) я есть",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я чувствую, что никому не нужен (нужна) и никто меня не любит",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мне кажется, что никто из близких меня не понимает",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я боюсь потерять работу, потому что это приведет к финансовым трудностям",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мне не хватает денег на питание, оплату жилья и на то, чтобы покрыть привычные расходы",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я финансово завишу от других (родителей, супруга(и), босса, партнеров по бизнесу)",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Я боюсь, что перемены в стране, отрасли, в которой я работаю, или экономике лишат меня средств к существованиюЭто обязательный вопрос",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group" : "3",
|
||||||
|
"title": "Мой доход скачет (то густо, то пусто) и находится вне моего контроля",
|
||||||
|
"question": "Как часто за последний месяц вы испытывали это?",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "Ни разу",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Время от времени",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"option": "Постоянно",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
20
survey/public/q/template.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"title": "",
|
||||||
|
"intro": "",
|
||||||
|
"settings": {
|
||||||
|
"randomizeQuestions": false,
|
||||||
|
"randomizeOptions": false
|
||||||
|
},
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"group" : "",
|
||||||
|
"question": "",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"option": "",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
survey/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
0
survey/src/App.css
Normal file
18
survey/src/App.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import './App.css'
|
||||||
|
import React from 'react'
|
||||||
|
import {Provider} from 'react-redux'
|
||||||
|
import {PersistGate} from 'redux-persist/integration/react'
|
||||||
|
import RootComponent from './RootComponent'
|
||||||
|
import {persistor, store} from './store/reducers/store'
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Provider store={store}>
|
||||||
|
<PersistGate loading={null} persistor={persistor}>
|
||||||
|
<RootComponent/>
|
||||||
|
</PersistGate>
|
||||||
|
</Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
19
survey/src/RootComponent.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'
|
||||||
|
import HomePage from './pages/HomePage'
|
||||||
|
import NotFoundPage from './pages/NotFoundPage'
|
||||||
|
import { ROUTES } from './resources/routes-constants'
|
||||||
|
// import './styles/main.sass'
|
||||||
|
|
||||||
|
const RootComponent: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<Routes>
|
||||||
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
|
<Route path={ROUTES.HOMEPAGE_ROUTE} element={<HomePage />} />
|
||||||
|
</Routes>
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RootComponent
|
37
survey/src/api/Api.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
export interface SettingsDto {
|
||||||
|
randomizeQuestions: boolean,
|
||||||
|
randomizeOptions: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OptionDto {
|
||||||
|
option: string,
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuestionDto {
|
||||||
|
group: string,
|
||||||
|
title: string,
|
||||||
|
question: string,
|
||||||
|
options: OptionDto[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SurveyDto {
|
||||||
|
title: string,
|
||||||
|
intro: string,
|
||||||
|
settings: SettingsDto,
|
||||||
|
questions: QuestionDto[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const Api = {
|
||||||
|
loadSurvey: (uri: string,
|
||||||
|
onSuccess: (survey: SurveyDto) => void,
|
||||||
|
onError: (failure: string) => void) => {
|
||||||
|
fetch(uri)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(out => onSuccess(out as SurveyDto))
|
||||||
|
.catch(err => onError(err))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Api;
|
24
survey/src/components/DateDisplay.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import moment from 'moment'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
const DateDisplay: React.FC = () => {
|
||||||
|
const [date, setDate] = useState('')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On component render sets the date state to current date and time
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setDate(moment().toDate().toString())
|
||||||
|
}, 1000)
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'relative', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
|
<span style={{ color: 'orange' }}>{date}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DateDisplay
|
0
survey/src/components/survey/Survey.css
Normal file
99
survey/src/components/survey/Survey.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import './Survey.css'
|
||||||
|
import * as React from "react";
|
||||||
|
import {SurveyDto} from "./../../api/Api";
|
||||||
|
import {Box, Button, Paper, Step, StepContent, StepLabel, Stepper, Typography} from "@mui/material";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
survey: SurveyDto | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const Survey: React.FC<Props> = ({survey}) => {
|
||||||
|
const [activeStep, setActiveStep] = React.useState(-1);
|
||||||
|
|
||||||
|
const handleNext = (group: string | undefined, value: number | undefined) => {
|
||||||
|
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
setActiveStep((prevActiveStep) => prevActiveStep - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setActiveStep(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const prepare = (text: string | undefined) => {
|
||||||
|
if (text !== undefined) {
|
||||||
|
return text
|
||||||
|
.replaceAll("\n", "<br/>")
|
||||||
|
.replaceAll("<script", "script")
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 style={{fontSize: '4em'}}>{survey?.title}</h1>
|
||||||
|
<div dangerouslySetInnerHTML={{__html: prepare(survey?.intro)}}></div>
|
||||||
|
<br/>
|
||||||
|
<Box sx={{maxWidth: 400}}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => handleNext(undefined, undefined)}
|
||||||
|
sx={{mt: 1, mr: 1}}
|
||||||
|
disabled={activeStep !== -1}
|
||||||
|
>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<br/>
|
||||||
|
<Box sx={{maxWidth: 400}}>
|
||||||
|
<Stepper activeStep={activeStep} orientation={"vertical"}>
|
||||||
|
{survey?.questions.map((question, index) =>
|
||||||
|
<Step key={question.question}>
|
||||||
|
<StepLabel optional={
|
||||||
|
index === survey?.questions.length - 1 ? (
|
||||||
|
<Typography variant="caption">Last step</Typography>
|
||||||
|
) : null
|
||||||
|
}>
|
||||||
|
{'Question ' + (index + 1)}
|
||||||
|
</StepLabel>
|
||||||
|
<StepContent>
|
||||||
|
<h4>{question.title}</h4>
|
||||||
|
<Typography>{question.question}</Typography>
|
||||||
|
<Box sx={{mb: 2}}>
|
||||||
|
<div>
|
||||||
|
{question.options.map((option, index) =>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => handleNext(question.group, option.value)}
|
||||||
|
sx={{mt: 1, mr: 1}}
|
||||||
|
>
|
||||||
|
{option.option}
|
||||||
|
</Button>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</StepContent>
|
||||||
|
</Step>
|
||||||
|
)}
|
||||||
|
</Stepper>
|
||||||
|
{activeStep === survey?.questions.length && (
|
||||||
|
<Paper square elevation={0} sx={{p: 3}}>
|
||||||
|
<Typography>All steps completed - you're finished</Typography>
|
||||||
|
<Button onClick={handleReset} sx={{mt: 1, mr: 1}}>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Survey;
|
16
survey/src/components/utils/If.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
interface MyProps {
|
||||||
|
condition: boolean,
|
||||||
|
children: any
|
||||||
|
}
|
||||||
|
|
||||||
|
const If = (props: MyProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.condition === true ? props.children: (<></>)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default If;
|
14
survey/src/index.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
background-color: rgb(250 250 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
13
survey/src/index.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import './index.css'
|
||||||
|
import React from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import App from './App'
|
||||||
|
import * as serviceWorker from './serviceWorker'
|
||||||
|
|
||||||
|
const root = createRoot(document.getElementById('root')!) // createRoot(container!) if you use TypeScript
|
||||||
|
root.render(<App />)
|
||||||
|
|
||||||
|
// If you want your app to work offline and load faster, you can change
|
||||||
|
// unregister() to register() below. Note this comes with some pitfalls.
|
||||||
|
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||||
|
serviceWorker.unregister()
|
43
survey/src/pages/HomePage.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import React, {useEffect} from 'react'
|
||||||
|
import DateDisplay from '../components/DateDisplay'
|
||||||
|
import {SnackbarOrigin, useSnackbar} from 'notistack';
|
||||||
|
import Api, {SurveyDto} from "./../api/Api";
|
||||||
|
import Survey from "./../components/survey/Survey";
|
||||||
|
import {Container, Grid} from "@mui/material";
|
||||||
|
|
||||||
|
const HomePage: React.FC = () => {
|
||||||
|
const [survey, setSurvey] = React.useState<SurveyDto>();
|
||||||
|
const queryParameters = new URLSearchParams(window.location.search);
|
||||||
|
const surveyUri: string = queryParameters.get("s") as string
|
||||||
|
const {enqueueSnackbar} = useSnackbar();
|
||||||
|
const snackbarAnchor = {
|
||||||
|
horizontal: 'center',
|
||||||
|
vertical: 'top'
|
||||||
|
} as SnackbarOrigin;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
Api.loadSurvey(surveyUri,
|
||||||
|
survey => {
|
||||||
|
setSurvey(survey);
|
||||||
|
console.log("Survey: " + survey)
|
||||||
|
},
|
||||||
|
(failure) => {
|
||||||
|
enqueueSnackbar(failure, {
|
||||||
|
variant: 'error',
|
||||||
|
anchorOrigin: snackbarAnchor
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, [surveyUri]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<Grid container={true} spacing={2}>
|
||||||
|
<Grid item xs={6} md={8}>
|
||||||
|
<Survey survey={survey}/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HomePage
|
25
survey/src/pages/NotFoundPage.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { ROUTES } from '../resources/routes-constants'
|
||||||
|
|
||||||
|
const NotFoundPage: React.FC = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this function to redirect the user to the homepage.
|
||||||
|
*/
|
||||||
|
const redirectToHomePage = () => {
|
||||||
|
navigate(ROUTES.HOMEPAGE_ROUTE)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: 'relative', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column' }}>
|
||||||
|
<h1 style={{ fontSize: '4em' }}>Oops 404!</h1>
|
||||||
|
<span style={{ cursor: 'pointer' }} onClick={() => redirectToHomePage()}>
|
||||||
|
Homepage
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFoundPage
|
1
survey/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="react-scripts" />
|
5
survey/src/resources/api-constants.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const baseUrl = 'http://exampleurl'
|
||||||
|
|
||||||
|
export const getData = (userId: number): string => {
|
||||||
|
return baseUrl + '/data/' + userId
|
||||||
|
}
|
3
survey/src/resources/routes-constants.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const ROUTES = {
|
||||||
|
HOMEPAGE_ROUTE: '/'
|
||||||
|
}
|
103
survey/src/serviceWorker.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// This lets the app load faster on subsequent visits in production, and gives
|
||||||
|
// it offline capabilities. However, it also means that developers (and users)
|
||||||
|
// will only see deployed updates on subsequent visits to a page, after all the
|
||||||
|
// existing tabs open on the page have been closed, since previously cached
|
||||||
|
// resources are updated in the background.
|
||||||
|
|
||||||
|
// To learn more about the benefits of this model and instructions on how to
|
||||||
|
// opt-in, read https://bit.ly/CRA-PWA
|
||||||
|
|
||||||
|
const isLocalhost = Boolean(
|
||||||
|
window.location.hostname === 'localhost' ||
|
||||||
|
window.location.hostname === '[::1]' ||
|
||||||
|
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config = {
|
||||||
|
onSuccess?: (registration: ServiceWorkerRegistration) => void
|
||||||
|
onUpdate?: (registration: ServiceWorkerRegistration) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const register = (config?: Config): void => {
|
||||||
|
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||||
|
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)
|
||||||
|
if (publicUrl.origin !== window.location.origin) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
|
||||||
|
if (isLocalhost) {
|
||||||
|
checkValidServiceWorker(swUrl, config)
|
||||||
|
navigator.serviceWorker.ready.then(() => {
|
||||||
|
console.log('This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
registerValidSW(swUrl, config)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerValidSW(swUrl: string, config?: Config) {
|
||||||
|
navigator.serviceWorker
|
||||||
|
.register(swUrl)
|
||||||
|
.then((registration) => {
|
||||||
|
registration.onupdatefound = () => {
|
||||||
|
const installingWorker = registration.installing
|
||||||
|
if (installingWorker == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
installingWorker.onstatechange = () => {
|
||||||
|
if (installingWorker.state === 'installed') {
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
console.log('New content is available and will be used when all ' + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.')
|
||||||
|
if (config && config.onUpdate) {
|
||||||
|
config.onUpdate(registration)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Content is cached for offline use.')
|
||||||
|
if (config && config.onSuccess) {
|
||||||
|
config.onSuccess(registration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error during service worker registration:', error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||||
|
fetch(swUrl, {
|
||||||
|
headers: { 'Service-Worker': 'script' }
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
const contentType = response.headers.get('content-type')
|
||||||
|
if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {
|
||||||
|
navigator.serviceWorker.ready.then((registration) => {
|
||||||
|
registration.unregister().then(() => {
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
registerValidSW(swUrl, config)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.log('No internet connection found. App is running in offline mode.')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unregister = (): void => {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.ready
|
||||||
|
.then((registration) => {
|
||||||
|
registration.unregister()
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
1
survey/src/setupTests.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import '@testing-library/jest-dom'
|
3
survey/src/store/actions/data.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { createAction } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
export const setContents = createAction<string[]>('data/setContents')
|
18
survey/src/store/reducers/data.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { createReducer } from '@reduxjs/toolkit'
|
||||||
|
import { setContents } from '../actions/data'
|
||||||
|
|
||||||
|
interface DataReducer {
|
||||||
|
contents: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: DataReducer = {
|
||||||
|
contents: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataReducer = createReducer<DataReducer>(initialState, (builder) => {
|
||||||
|
builder.addCase(setContents, (state, action) => {
|
||||||
|
state.contents = action.payload
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default dataReducer
|
36
survey/src/store/reducers/store.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { combineReducers, configureStore } from '@reduxjs/toolkit'
|
||||||
|
import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from 'redux-persist'
|
||||||
|
import storage from 'redux-persist/lib/storage' // defaults to localStorage
|
||||||
|
import data from './data'
|
||||||
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
data
|
||||||
|
})
|
||||||
|
|
||||||
|
const persistedReducer = persistReducer(
|
||||||
|
{
|
||||||
|
key: 'root',
|
||||||
|
storage,
|
||||||
|
whitelist: ['data']
|
||||||
|
},
|
||||||
|
rootReducer
|
||||||
|
)
|
||||||
|
|
||||||
|
export const store = configureStore({
|
||||||
|
reducer: persistedReducer,
|
||||||
|
middleware: (getDefaultMiddleware) =>
|
||||||
|
getDefaultMiddleware({
|
||||||
|
serializableCheck: {
|
||||||
|
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof store.getState>
|
||||||
|
export type AppDispatch = typeof store.dispatch
|
||||||
|
|
||||||
|
export const useAppDispatch = () => useDispatch<AppDispatch>()
|
||||||
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
||||||
|
|
||||||
|
export const persistor = persistStore(store)
|
8
survey/src/tests/components/DateDisplay.test.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import DateDisplay from '../../components/DateDisplay'
|
||||||
|
|
||||||
|
test('renders current date', () => {
|
||||||
|
render(<DateDisplay />)
|
||||||
|
const timeFormat = screen.getByText(/GMT/i)
|
||||||
|
expect(timeFormat).toBeInTheDocument()
|
||||||
|
})
|
8
survey/src/tests/pages/HomePage.test.tsx.disabled
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import HomePage from '../../pages/HomePage'
|
||||||
|
|
||||||
|
test('renders hello world message', () => {
|
||||||
|
render(<HomePage />)
|
||||||
|
const greetings = screen.getByText(/Hello world/i)
|
||||||
|
expect(greetings).toBeInTheDocument()
|
||||||
|
})
|
13
survey/src/tests/pages/NotFoundPage.test.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import { BrowserRouter as Router } from 'react-router-dom'
|
||||||
|
import NotFoundPage from '../../pages/NotFoundPage'
|
||||||
|
|
||||||
|
test('renders error message', () => {
|
||||||
|
render(
|
||||||
|
<Router>
|
||||||
|
<NotFoundPage />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
const errorMessage = screen.getByText(/Oops 404!/i)
|
||||||
|
expect(errorMessage).toBeInTheDocument()
|
||||||
|
})
|
10
survey/src/types/reducers.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export interface ReducerData {
|
||||||
|
contents: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ReduxActionData<T> = {
|
||||||
|
type: any
|
||||||
|
payload?: T
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ReduxAction<T> = (data: T) => ReduxActionData<T>
|
65
survey/src/utility/customAxios.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const CustomAxios = axios.create()
|
||||||
|
|
||||||
|
const toCamelCase: any = (object: any) => {
|
||||||
|
let transformedObject = object
|
||||||
|
if (typeof object === 'object' && object !== null) {
|
||||||
|
if (object instanceof Array) {
|
||||||
|
transformedObject = object.map(toCamelCase)
|
||||||
|
} else {
|
||||||
|
transformedObject = {}
|
||||||
|
for (const key in object) {
|
||||||
|
if (object[key] !== undefined) {
|
||||||
|
const newKey = key.replace(/(_\w)|(-\w)/g, (k) => k[1].toUpperCase())
|
||||||
|
transformedObject[newKey] = toCamelCase(object[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transformedObject
|
||||||
|
}
|
||||||
|
|
||||||
|
export const toSnackCase: any = (object: any) => {
|
||||||
|
let transformedObject = object
|
||||||
|
if (typeof object === 'object' && object !== null) {
|
||||||
|
if (object instanceof Array) {
|
||||||
|
transformedObject = object.map(toSnackCase)
|
||||||
|
} else {
|
||||||
|
transformedObject = {}
|
||||||
|
for (const key in object) {
|
||||||
|
if (object[key] !== undefined) {
|
||||||
|
const newKey = key
|
||||||
|
.replace(/\.?([A-Z]+)/g, function (_, y) {
|
||||||
|
return '_' + y.toLowerCase()
|
||||||
|
})
|
||||||
|
.replace(/^_/, '')
|
||||||
|
transformedObject[newKey] = toSnackCase(object[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transformedObject
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomAxios.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
response.data = toCamelCase(response.data)
|
||||||
|
return response
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CustomAxios.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
config.data = toSnackCase(config.data)
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default CustomAxios
|
8
survey/src/utility/functions.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* This function can be used anywhere in the app to greet the user
|
||||||
|
* @param userName The user's first name
|
||||||
|
* @returns A kind greeting message
|
||||||
|
*/
|
||||||
|
export const sayHello = (userName: string): string => {
|
||||||
|
return 'Welcome ' + userName + '!'
|
||||||
|
}
|
26
survey/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|