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"
|
||||
]
|
||||
}
|