Merge remote-tracking branch 'origin/dev' into 4-pipeline
This commit is contained in:
commit
1d9f5bbc50
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "PHP 8.0",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"pathMappings": {
|
||||
"/web/backend": "${workspaceFolder}\\src\\backend"
|
||||
},
|
||||
"skipFiles": [
|
||||
"${workspaceFolder}/src/backend/vendor/",
|
||||
"/web/backend/vendor/"
|
||||
],
|
||||
"port": 7780,
|
||||
"hostname": "127.0.0.1",
|
||||
"xdebugSettings": {
|
||||
"max_children": 100,
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "PHP 8.0 with vendor",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"pathMappings": {
|
||||
"/web/backend": "${workspaceFolder}\\src\\backend"
|
||||
},
|
||||
"port": 7780,
|
||||
"hostname": "127.0.0.1",
|
||||
"xdebugSettings": {
|
||||
"max_children": 100,
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "PHP 8.0 CLI",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"pathMappings": {
|
||||
"/web/backend": "${workspaceFolder}\\src\\backend"
|
||||
},
|
||||
"skipFiles": [
|
||||
"${workspaceFolder}/src/backend/vendor/",
|
||||
"/web/backend/vendor/"
|
||||
],
|
||||
"port": 7781,
|
||||
"hostname": "127.0.0.1",
|
||||
"xdebugSettings": {
|
||||
"max_children": 100,
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "PHP 8.0 CLI with vendor",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"pathMappings": {
|
||||
"/web/backend": "${workspaceFolder}\\src\\backend"
|
||||
},
|
||||
"port": 7781,
|
||||
"hostname": "127.0.0.1",
|
||||
"xdebugSettings": {
|
||||
"max_children": 100,
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
}
|
|
@ -12,6 +12,8 @@ We also encourage doctors and specialists to join the community, they will have
|
|||
### Disease analysis
|
||||
We want users to have access to the log of their disease where they can enter how it is progressing. On this basis, together with specialists, we will be able to find some common features for new diseases and predict as many negative symptoms as possible in order to be able to prevent them as much as possible.
|
||||
|
||||
[Technical documentation](./src/#technical-documentation)
|
||||
|
||||
# Polski
|
||||
## Czym jest CureNet?
|
||||
### Opis
|
||||
|
@ -24,4 +26,7 @@ Każdy może założyć profil oraz dodać jedną lub więcej chorób. Dzięki t
|
|||
### Społeczność specjalistów
|
||||
Zachęcamy także lekarzy i specjalistów, aby dołączyli do społeczności, będą oni mieli wgląd w listę swoich pacjentów ale także do listy osób, które mają podobne schorzenia jak ich podopieczni, dzięki czemu będą mieli dostęp do szerszego grona osób z daną chorobą. Będą mogli także przedyskutować swoje obserwacje z innymi lekarzami, którzy także mają doświadczenie z daną chorobą. Jest to naprawdę ważne przy bardzo unikalnych i nie dokońca poznanych jeszcze schorzeniach.
|
||||
### Analiza chorób
|
||||
Chcemy, aby użytkownicy mieli dostęp do dzinnika swojej choroby, gdzie będą mogli wprowadzać jak ona przebiega. Na tej podstawie wraz ze specjalistami będziemy w stanie znaleźć pewne wsólne cechy dla nowych chorób i przewidzieć jak najwięcej negatywnych symptomów, aby móc im jak najlepiej zapobiec.
|
||||
Chcemy, aby użytkownicy mieli dostęp do dziennika swojej choroby, gdzie będą mogli wprowadzać jak ona przebiega. Na tej podstawie wraz ze specjalistami będziemy w stanie znaleźć pewne wsólne cechy dla nowych chorób i przewidzieć jak najwięcej negatywnych symptomów, aby móc im jak najlepiej zapobiec.
|
||||
|
||||
[Dokumentacja techniczna](./src/#technical-documentation)
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
logs/*
|
||||
!logs/.gitkeep
|
||||
cache/*/*
|
||||
!cache/*/.gitkeep
|
|
@ -0,0 +1,50 @@
|
|||
FROM debian:10
|
||||
|
||||
RUN apt update \
|
||||
&& echo "Binary::apt::APT::Keep-Downloaded-Packages \"true\";" | tee /etc/apt/apt.conf.d/01keep-debs \
|
||||
&& apt install -y wget lsb-release ca-certificates apt-transport-https software-properties-common gnupg2 gcc g++ make \
|
||||
&& echo "configure repos" \
|
||||
&& echo "deb https://packages.sury.org/php/ buster main" | tee /etc/apt/sources.list.d/php.list \
|
||||
&& echo "deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main" | tee /etc/apt/sources.list.d/postgresql.list \
|
||||
&& echo "deb https://ftp.postgresql.org/pub/pgadmin/pgadmin4/apt/buster pgadmin4 main" | tee /etc/apt/sources.list.d/pgdg.list \
|
||||
&& echo "download keys" \
|
||||
&& (wget -qO - https://packages.sury.org/php/apt.gpg | apt-key add - ) \
|
||||
&& (wget -qO - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - ) \
|
||||
&& (wget -qO - https://www.pgadmin.org/static/packages_pgadmin_org.pub | apt-key add - ) \
|
||||
&& echo "install nodejs" \
|
||||
&& wget -qO - https://deb.nodesource.com/setup_16.x | bash - \
|
||||
&& echo "update repos" \
|
||||
&& apt update \
|
||||
&& echo "install software" \
|
||||
&& apt install -y aptitude unzip \
|
||||
&& apt install -y postgresql pgadmin4 \
|
||||
&& apt install -y php8.0 php8.0-ctype php8.0-iconv php8.0-simplexml php8.0-tokenizer php8.0-fpm php8.0-pdo php8.0-pdo-pgsql php8.0-pgsql php8.0-mbstring php8.0-zip php8.0-xdebug \
|
||||
&& apt install -y nginx \
|
||||
&& apt install -y nodejs \
|
||||
&& echo "configure tools" \
|
||||
&& echo "# angular" \
|
||||
&& npm install -g @angular/cli \
|
||||
&& echo "# composer" \
|
||||
&& php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||
&& php composer-setup.php --install-dir=/usr/local/bin/ --filename=composer \
|
||||
&& php -r "unlink('composer-setup.php');" \
|
||||
&& echo "# nginx" \
|
||||
&& ln -s /web/config/nginx/app.conf /etc/nginx/conf.d/app.conf \
|
||||
&& ln -s /web/config/nginx/php80.conf /etc/nginx/php80.conf \
|
||||
&& echo "# postgresql" \
|
||||
&& echo "postgres:WeryStronkPaz2" | chpasswd \
|
||||
&& echo "# pgadmin4" \
|
||||
&& bash -c 'PGADMIN_SETUP_EMAIL="admin@pgadmin.localhost" PGADMIN_SETUP_PASSWORD="WeryStronkPaz2" /usr/pgadmin4/bin/setup-web.sh --yes' \
|
||||
&& echo "" > /etc/nginx/sites-enabled/default \
|
||||
&& echo "!!! DONE !!!"
|
||||
|
||||
RUN apt install -y nano openssh-server telnet
|
||||
VOLUME /web/backend/vendor
|
||||
VOLUME /web/backend/var
|
||||
VOLUME /web/frontend/node_modules
|
||||
|
||||
EXPOSE 7700 7780 7781 49153
|
||||
|
||||
STOPSIGNAL SIGQUIT
|
||||
|
||||
CMD ["bash", "/web/config/startup.sh"]
|
|
@ -0,0 +1,27 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- 7700:7700
|
||||
- 49153:49153
|
||||
expose:
|
||||
- 7780:7780
|
||||
- 7781:7781
|
||||
volumes:
|
||||
- .:/web/config
|
||||
- ../src/backend:/web/backend:cached
|
||||
- type: bind
|
||||
target: /web/frontend
|
||||
source: ../src/frontend
|
||||
volume:
|
||||
nocopy: true
|
||||
- ./logs:/var/log
|
||||
- vendor:/web/backend/vendor
|
||||
- var:/web/backend/var
|
||||
- nodemodules:/web/frontend/node_modules
|
||||
volumes:
|
||||
nodemodules:
|
||||
vendor:
|
||||
var:
|
||||
nodemodules:
|
|
@ -0,0 +1,27 @@
|
|||
server {
|
||||
server_name localhost;
|
||||
listen 7700;
|
||||
root /web/backend/public;
|
||||
index index.htm index.html index.php;
|
||||
location / {
|
||||
proxy_bind 127.0.0.1;
|
||||
proxy_pass http://localhost:4200;
|
||||
}
|
||||
location /api/ {
|
||||
try_files $uri $uri/ /index.php/$uri?$query_string;
|
||||
}
|
||||
location /coverage/ {
|
||||
alias /web/coverage/html-coverage/;
|
||||
index index.html;
|
||||
}
|
||||
location /pgadmin4/ {
|
||||
include proxy_params;
|
||||
proxy_bind 127.0.0.1;
|
||||
proxy_pass http://localhost:80;
|
||||
proxy_set_header X-Script-Name /pgadmin4;
|
||||
}
|
||||
proxy_intercept_errors on;
|
||||
fastcgi_intercept_errors on;
|
||||
fastcgi_hide_header X-Powered-By;
|
||||
include "php80.conf";
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
index index.php index.html index.htm;
|
||||
include snippets/fastcgi-php.conf;
|
||||
location ~ [^/]\.php(/|$) {
|
||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||
if (!-f $document_root$fastcgi_script_name) {
|
||||
return 404;
|
||||
}
|
||||
fastcgi_param HTTP_PROXY "";
|
||||
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
fastcgi_buffering off;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
zend_extension=xdebug.so
|
||||
|
||||
[xdebug]
|
||||
xdebug.mode=coverage
|
||||
xdebug.start_with_request=yes
|
||||
xdebug.client_host=host.docker.internal
|
||||
xdebug.client_port=7781
|
||||
|
||||
xdebug.show_exception_trace = 1
|
||||
xdebug.show_error_trace = 1
|
||||
|
||||
xdebug.log_level = 0
|
|
@ -0,0 +1,12 @@
|
|||
zend_extension=xdebug.so
|
||||
|
||||
[xdebug]
|
||||
xdebug.mode=debug
|
||||
xdebug.start_with_request=yes
|
||||
xdebug.client_host=host.docker.internal
|
||||
xdebug.client_port=7780
|
||||
|
||||
xdebug.show_exception_trace = 1
|
||||
xdebug.show_error_trace = 1
|
||||
|
||||
xdebug.log_level = 0
|
|
@ -0,0 +1,4 @@
|
|||
ALTER USER postgres WITH PASSWORD 'WeryStronkPaz2';
|
||||
CREATE USER curenet WITH PASSWORD 'WeryStronkPaz2';
|
||||
CREATE DATABASE curenet;
|
||||
GRANT ALL PRIVILEGES ON DATABASE curenet to curenet;
|
|
@ -0,0 +1 @@
|
|||
docker.exe compose -p curenet up
|
|
@ -0,0 +1 @@
|
|||
docker.exe compose -p curenet exec app bash -c 'cd /web && bash'
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "> Configure cache"
|
||||
mkdir -p /tmp/app/{vendor/node_modules}
|
||||
ln -s /tmp/app/node_modules /web/frontend/node_modules
|
||||
ln -s /tmp/app/vendor /web/backend/vendor
|
||||
|
||||
echo "> Configure user"
|
||||
useradd developer
|
||||
chown developer:developer /web/frontend -R
|
||||
chown developer:developer /web/backend -R
|
||||
|
||||
echo "> Configure logs"
|
||||
mkdir -p /var/log/{apache2,nginx,pgadmin,postgresql}
|
||||
chown postgres:adm /var/log/postgresql -R
|
||||
chown root:postgres /var/log/postgresql
|
||||
|
||||
echo "> Configure volumes"
|
||||
[ -e /web/frontend/node_modules ] || ln -s /web/node /web/frontend/node_modules
|
||||
[ -e /web/backend/var ] || ln -s /web/var /web/backend/var
|
||||
[ -e /web/backend/vendor ] || ln -s /web/vendor /web/backend/vendor
|
||||
|
||||
echo "> Starting php8.0-fpm server"
|
||||
[ -e /etc/php/8.0/fpm/conf.d/20-xdebug.ini ] && rm /etc/php/8.0/fpm/conf.d/20-xdebug.ini || echo OK
|
||||
[ -e /etc/php/8.0/cli/conf.d/20-xdebug.ini ] && rm /etc/php/8.0/cli/conf.d/20-xdebug.ini || echo OK
|
||||
cp /web/config/php8.0/xdebug-fpm.ini /etc/php/8.0/fpm/conf.d/20-xdebug.ini
|
||||
cp /web/config/php8.0/xdebug-cli.ini /etc/php/8.0/cli/conf.d/20-xdebug.ini
|
||||
service php8.0-fpm start
|
||||
|
||||
echo "> Starting nginx server"
|
||||
service nginx start
|
||||
|
||||
echo "> Starting apache2 server for pgAdmin4"
|
||||
service apache2 start
|
||||
|
||||
echo "> Starting postgresql database"
|
||||
service postgresql start
|
||||
|
||||
echo "> Starting ssh server"
|
||||
service ssh start
|
||||
|
||||
echo "> Configure database"
|
||||
su postgres -c psql postgres < /web/config/postgres/init-data.sql
|
||||
|
||||
echo "> Configure symfony"
|
||||
cd /web/backend
|
||||
composer install
|
||||
php bin/console doctrine:schema:update --force
|
||||
|
||||
echo "> Configure angular"
|
||||
cd /web/frontend
|
||||
su developer -c 'time npm ci'
|
||||
|
||||
echo "> Starting angular"
|
||||
su developer -c 'ng serve --disable-host-check --host 0.0.0.0 --poll'
|
|
@ -0,0 +1,74 @@
|
|||
# Technical documentation
|
||||
## Frontend
|
||||
1. [HTML 5](https://developer.mozilla.org/en-US/docs/Glossary/HTML5)
|
||||
1. [CSS 3](https://developer.mozilla.org/pl/docs/Web/CSS)
|
||||
1. [JavaScript](https://developer.mozilla.org/pl/docs/orphaned/Web/JavaScript)
|
||||
1. [TypeScript](https://www.typescriptlang.org/docs/ "Documentation")
|
||||
1. [Angular 12](https://angular.io/docs "Documentation") (Frontend framework)
|
||||
## Backend
|
||||
1. [PHP 8.0](https://www.php.net/docs.php "Documentation")
|
||||
1. [Symfony 5](https://symfony.com/doc/current/index.html "Documentation") (PHP Framework)
|
||||
1. [Postgresql 13](https://www.postgresql.org/docs/13/index.html "Documentation") (Database)
|
||||
1. [Docker](https://docs.docker.com/) (Local development environment)
|
||||
|
||||
## Tools
|
||||
### Neccessary
|
||||
1. [Docker](https://docs.docker.com/) - to run local environment
|
||||
### Optional
|
||||
1. [Firefox Developer Edition](https://www.mozilla.org/pl/firefox/developer/)
|
||||
2. [VS Code](https://code.visualstudio.com/) - [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment)
|
||||
- Plugins for PHP
|
||||
- [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug)
|
||||
- [PHP Intelephense](https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client)
|
||||
- Plugins for Angular
|
||||
- [Angular Language Service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template)
|
||||
- Plugins for database
|
||||
- [SQLTools](https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools)
|
||||
- [SQLTools PostgreSQL/Redshift Driver](https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools-driver-pg)
|
||||
- Plugins for git:
|
||||
- [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)
|
||||
|
||||
## How to run app?
|
||||
1. Download project from repository using standalone git or built in any IDE
|
||||
2. Install Docker Desktop
|
||||
3. run command line and go to project directory
|
||||
4. Go to docker directory
|
||||
5. Execute `docker compose -p curenet up`
|
||||
6. Wait until docker building and lauching the app.
|
||||
At first attempt it can take a few minutes
|
||||
|
||||
## Application links
|
||||
1. Main app: `http://localhost:7700`
|
||||
2. PgAdmin4 `http://localhost:7700/pgadmin4`
|
||||
3. PHP Xdebug for fpm: `localhost:7780`
|
||||
3. PHP Xdebug for cli: `localhost:7781`
|
||||
|
||||
## Credentials
|
||||
### PgAdmin4
|
||||
- Login: `admin@pgadmin.localhost`
|
||||
- Password: `WeryStronkPaz2`
|
||||
- Configuration:
|
||||
1. Click on Server in left panel
|
||||
2. Go to Create > Server
|
||||
3. Enter name (you can name it as you want)
|
||||
4. Go to Connection tab
|
||||
5. Enter host name: `localhost`
|
||||
6. Enter username: `postgres`
|
||||
7. Enter password: `WeryStronkPaz2`
|
||||
8. Save it
|
||||
9. Go to Servers > [your name] > Databases > curenet > Schemas > public > Tables
|
||||
10. You can see all tables
|
||||
### Database
|
||||
- Server: `localhost`
|
||||
- Port: `5432`
|
||||
- Username
|
||||
- For application: `curenet`
|
||||
- For admin: `postgres`
|
||||
- Database
|
||||
- For application: `curenet`
|
||||
- For admin: `postgres`
|
||||
- Password: `WeryStronkPaz2`
|
||||
|
||||
|
||||
### Known issues
|
||||
1. If the docker build the app but can't run it, check the docker/startup.sh file, it should be saved with LF as end of line sequence. If it's CRLF try to change it to LF, save the file and try to run app again
|
|
@ -0,0 +1,3 @@
|
|||
APP_ENV=dev
|
||||
APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
|
||||
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"
|
|
@ -0,0 +1,3 @@
|
|||
APP_ENV=dev
|
||||
APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
|
||||
DATABASE_URL="postgresql://curenet:WeryStronkPaz2@127.0.0.1:5432/curenet?serverVersion=13&charset=utf8"
|
|
@ -0,0 +1,3 @@
|
|||
APP_ENV=dev
|
||||
APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
|
||||
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"
|
|
@ -1,100 +1,100 @@
|
|||
{
|
||||
"type": "project",
|
||||
"license": "proprietary",
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"composer/package-versions-deprecated": "1.11.99.2",
|
||||
"doctrine/annotations": "^1.0",
|
||||
"doctrine/doctrine-bundle": "^2.4",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.1",
|
||||
"doctrine/orm": "^2.9",
|
||||
"phpdocumentor/reflection-docblock": "^5.2",
|
||||
"sensio/framework-extra-bundle": "^6.1",
|
||||
"symfony/asset": "5.3.*",
|
||||
"symfony/console": "5.3.*",
|
||||
"symfony/dotenv": "5.3.*",
|
||||
"symfony/expression-language": "5.3.*",
|
||||
"symfony/flex": "^1.3.1",
|
||||
"symfony/form": "5.3.*",
|
||||
"symfony/framework-bundle": "5.3.*",
|
||||
"symfony/http-client": "5.3.*",
|
||||
"symfony/intl": "5.3.*",
|
||||
"symfony/mailer": "5.3.*",
|
||||
"symfony/mime": "5.3.*",
|
||||
"symfony/monolog-bundle": "^3.1",
|
||||
"symfony/notifier": "5.3.*",
|
||||
"symfony/process": "5.3.*",
|
||||
"symfony/property-access": "5.3.*",
|
||||
"symfony/property-info": "5.3.*",
|
||||
"symfony/proxy-manager-bridge": "5.3.*",
|
||||
"symfony/runtime": "5.3.*",
|
||||
"symfony/security-bundle": "5.3.*",
|
||||
"symfony/serializer": "5.3.*",
|
||||
"symfony/string": "5.3.*",
|
||||
"symfony/translation": "5.3.*",
|
||||
"symfony/twig-bundle": "^5.3",
|
||||
"symfony/validator": "5.3.*",
|
||||
"symfony/web-link": "5.3.*",
|
||||
"symfony/yaml": "5.3.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"symfony/browser-kit": "^5.3",
|
||||
"symfony/css-selector": "^5.3",
|
||||
"symfony/debug-bundle": "^5.3",
|
||||
"symfony/maker-bundle": "^1.0",
|
||||
"symfony/phpunit-bridge": "^5.3",
|
||||
"symfony/stopwatch": "^5.3",
|
||||
"symfony/var-dumper": "^5.3",
|
||||
"symfony/web-profiler-bundle": "^5.3"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": {
|
||||
"*": "dist"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"App\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-ctype": "*",
|
||||
"symfony/polyfill-iconv": "*",
|
||||
"symfony/polyfill-php72": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd",
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||
},
|
||||
"post-install-cmd": [
|
||||
"@auto-scripts"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@auto-scripts"
|
||||
]
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/symfony": "*"
|
||||
},
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "5.3.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"type": "project",
|
||||
"license": "proprietary",
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"composer/package-versions-deprecated": "1.11.99.2",
|
||||
"doctrine/annotations": "^1.0",
|
||||
"doctrine/doctrine-bundle": "^2.4",
|
||||
"doctrine/doctrine-migrations-bundle": "^3.1",
|
||||
"doctrine/orm": "^2.9",
|
||||
"phpdocumentor/reflection-docblock": "^5.2",
|
||||
"sensio/framework-extra-bundle": "^6.1",
|
||||
"symfony/asset": "5.3.*",
|
||||
"symfony/console": "5.3.*",
|
||||
"symfony/dotenv": "5.3.*",
|
||||
"symfony/expression-language": "5.3.*",
|
||||
"symfony/flex": "^1.3.1",
|
||||
"symfony/form": "5.3.*",
|
||||
"symfony/framework-bundle": "5.3.*",
|
||||
"symfony/http-client": "5.3.*",
|
||||
"symfony/intl": "5.3.*",
|
||||
"symfony/mailer": "5.3.*",
|
||||
"symfony/mime": "5.3.*",
|
||||
"symfony/monolog-bundle": "^3.1",
|
||||
"symfony/notifier": "5.3.*",
|
||||
"symfony/process": "5.3.*",
|
||||
"symfony/property-access": "5.3.*",
|
||||
"symfony/property-info": "5.3.*",
|
||||
"symfony/proxy-manager-bridge": "5.3.*",
|
||||
"symfony/runtime": "5.3.*",
|
||||
"symfony/security-bundle": "5.3.*",
|
||||
"symfony/serializer": "5.3.*",
|
||||
"symfony/string": "5.3.*",
|
||||
"symfony/translation": "5.3.*",
|
||||
"symfony/twig-bundle": "^5.3",
|
||||
"symfony/validator": "5.3.*",
|
||||
"symfony/web-link": "5.3.*",
|
||||
"symfony/yaml": "5.3.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"symfony/browser-kit": "^5.3",
|
||||
"symfony/css-selector": "^5.3",
|
||||
"symfony/debug-bundle": "^5.3",
|
||||
"symfony/maker-bundle": "^1.33",
|
||||
"symfony/phpunit-bridge": "^5.3",
|
||||
"symfony/stopwatch": "^5.3",
|
||||
"symfony/var-dumper": "^5.3",
|
||||
"symfony/web-profiler-bundle": "^5.3"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": {
|
||||
"*": "dist"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"App\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-ctype": "*",
|
||||
"symfony/polyfill-iconv": "*",
|
||||
"symfony/polyfill-php72": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"auto-scripts": {
|
||||
"cache:clear": "symfony-cmd",
|
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd"
|
||||
},
|
||||
"post-install-cmd": [
|
||||
"@auto-scripts"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@auto-scripts"
|
||||
]
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/symfony": "*"
|
||||
},
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "5.3.*"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,25 +1,37 @@
|
|||
security:
|
||||
# https://symfony.com/doc/current/security/experimental_authenticators.html
|
||||
enable_authenticator_manager: true
|
||||
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
|
||||
providers:
|
||||
users_in_memory: { memory: null }
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: users_in_memory
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#firewalls-authentication
|
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
||||
# switch_user: true
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
security:
|
||||
# https://symfony.com/doc/current/security/experimental_authenticators.html
|
||||
enable_authenticator_manager: true
|
||||
password_hashers:
|
||||
App\Entity\User:
|
||||
algorithm: auto
|
||||
|
||||
|
||||
|
||||
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
|
||||
providers:
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
app_user_provider:
|
||||
entity:
|
||||
class: App\Entity\User
|
||||
property: id
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
# used to reload user from session & other features (e.g. switch_user)
|
||||
firewalls:
|
||||
dev:
|
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||
security: false
|
||||
main:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
|
||||
# activate different ways to authenticate
|
||||
# https://symfony.com/doc/current/security.html#firewalls-authentication
|
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
||||
# switch_user: true
|
||||
|
||||
# Easy way to control access for large sections of your site
|
||||
# Note: Only the *first* access control that matches will be used
|
||||
access_control:
|
||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
||||
# - { path: ^/profile, roles: ROLE_USER }
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20210723170329 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SEQUENCE "user_id_seq" INCREMENT BY 1 MINVALUE 1 START 1');
|
||||
$this->addSql('CREATE TABLE "user" (id INT NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP SEQUENCE "user_id_seq" CASCADE');
|
||||
$this->addSql('DROP TABLE "user"');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20210723171757 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE "user" ADD email VARCHAR(255) NOT NULL');
|
||||
$this->addSql('ALTER TABLE "user" ADD name VARCHAR(255) NOT NULL');
|
||||
$this->addSql('ALTER TABLE "user" ADD surname VARCHAR(255) NOT NULL');
|
||||
$this->addSql('ALTER TABLE "user" ADD avatar VARCHAR(255) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE "user" ADD country VARCHAR(3) DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE "user" ADD state VARCHAR(10) DEFAULT NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE "user" DROP email');
|
||||
$this->addSql('ALTER TABLE "user" DROP name');
|
||||
$this->addSql('ALTER TABLE "user" DROP surname');
|
||||
$this->addSql('ALTER TABLE "user" DROP avatar');
|
||||
$this->addSql('ALTER TABLE "user" DROP country');
|
||||
$this->addSql('ALTER TABLE "user" DROP state');
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
docker.exe compose -p curenet exec app bash -c 'cd /web/backend && bash'
|
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* @ORM\Entity(repositoryClass=UserRepository::class)
|
||||
* @ORM\Table(name="`user`")
|
||||
*/
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="json")
|
||||
*/
|
||||
private $roles = [];
|
||||
|
||||
/**
|
||||
* @var string The hashed password
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $password;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
private $email;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255)
|
||||
*/
|
||||
private $surname;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
*/
|
||||
private $avatar;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=3, nullable=true)
|
||||
*/
|
||||
private $country;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=10, nullable=true)
|
||||
*/
|
||||
private $state;
|
||||
|
||||
public function getId(): ?string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): self
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A visual identifier that represents this user.
|
||||
*
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getUserIdentifier(): string
|
||||
{
|
||||
return (string) $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 5.3, use getUserIdentifier instead
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return (string) $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
// guarantee every user at least has ROLE_USER
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
return array_unique($roles);
|
||||
}
|
||||
|
||||
public function setRoles(array $roles): self
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PasswordAuthenticatedUserInterface
|
||||
*/
|
||||
public function getPassword(): string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setPassword(string $password): self
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returning a salt is only needed, if you are not using a modern
|
||||
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
|
||||
*
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function getSalt(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see UserInterface
|
||||
*/
|
||||
public function eraseCredentials()
|
||||
{
|
||||
// If you store any temporary, sensitive data on the user, clear it here
|
||||
// $this->plainPassword = null;
|
||||
}
|
||||
|
||||
public function getEmail(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(string $email): self
|
||||
{
|
||||
$this->email = $email;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): self
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSurname(): ?string
|
||||
{
|
||||
return $this->surname;
|
||||
}
|
||||
|
||||
public function setSurname(string $surname): self
|
||||
{
|
||||
$this->surname = $surname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAvatar(): ?string
|
||||
{
|
||||
return $this->avatar;
|
||||
}
|
||||
|
||||
public function setAvatar(?string $avatar): self
|
||||
{
|
||||
$this->avatar = $avatar;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCountry(): ?string
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
public function setCountry(?string $country): self
|
||||
{
|
||||
$this->country = $country;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getState(): ?string
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function setState(?string $state): self
|
||||
{
|
||||
$this->state = $state;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||
|
||||
/**
|
||||
* @method User|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method User|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method User[] findAll()
|
||||
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to upgrade (rehash) the user's password automatically over time.
|
||||
*/
|
||||
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
|
||||
{
|
||||
if (!$user instanceof User) {
|
||||
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', \get_class($user)));
|
||||
}
|
||||
|
||||
$user->setPassword($newHashedPassword);
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return User[] Returns an array of User objects
|
||||
// */
|
||||
/*
|
||||
public function findByExampleField($value)
|
||||
{
|
||||
return $this->createQueryBuilder('u')
|
||||
->andWhere('u.exampleField = :val')
|
||||
->setParameter('val', $value)
|
||||
->orderBy('u.id', 'ASC')
|
||||
->setMaxResults(10)
|
||||
->getQuery()
|
||||
->getResult()
|
||||
;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public function findOneBySomeField($value): ?User
|
||||
{
|
||||
return $this->createQueryBuilder('u')
|
||||
->andWhere('u.exampleField = :val')
|
||||
->setParameter('val', $value)
|
||||
->getQuery()
|
||||
->getOneOrNullResult()
|
||||
;
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
|
||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
|
@ -0,0 +1,16 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,46 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
|
@ -0,0 +1,27 @@
|
|||
# Frontend
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.8.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
|
@ -0,0 +1,164 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"frontend": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss",
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:class": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:guard": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:interceptor": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:module": {
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"skipTests": true
|
||||
},
|
||||
"@schematics/angular:service": {
|
||||
"skipTests": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
{
|
||||
"input": "src/app/styles/light.scss",
|
||||
"bundleName": "light",
|
||||
"inject": false
|
||||
},
|
||||
{
|
||||
"input": "src/app/styles/dark.scss",
|
||||
"bundleName": "dark",
|
||||
"inject": false
|
||||
}
|
||||
],
|
||||
"scripts": [],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"buildOptimizer": false,
|
||||
"sourceMap": true,
|
||||
"optimization": false,
|
||||
"namedChunks": true
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": ""
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "frontend:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "frontend:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "frontend:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "frontend:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "frontend:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
"defaultProject": "frontend"
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({
|
||||
spec: {
|
||||
displayStacktrace: StacktraceOption.PRETTY
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('frontend app is running!');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo(): Promise<unknown> {
|
||||
return browser.get(browser.baseUrl) as Promise<unknown>;
|
||||
}
|
||||
|
||||
getTitleText(): Promise<string> {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/frontend'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~12.1.3",
|
||||
"@angular/cdk": "^12.1.3",
|
||||
"@angular/common": "~12.1.3",
|
||||
"@angular/compiler": "~12.1.3",
|
||||
"@angular/core": "~12.1.3",
|
||||
"@angular/forms": "~12.1.3",
|
||||
"@angular/material": "^12.1.3",
|
||||
"@angular/platform-browser": "~12.1.3",
|
||||
"@angular/platform-browser-dynamic": "~12.1.3",
|
||||
"@angular/router": "~12.1.3",
|
||||
"@ngx-translate/core": "^13.0.0",
|
||||
"@ngx-translate/http-loader": "^6.0.0",
|
||||
"bootstrap": "^5.0.2",
|
||||
"font-awesome": "^4.7.0",
|
||||
"rxjs": "~6.5.5",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~12.1.3",
|
||||
"@angular/cli": "~12.1.3",
|
||||
"@angular/compiler-cli": "~12.1.3",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.3.4",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.3.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
cd ../../docker
|
||||
docker.exe compose -p curenet exec app bash -c 'cd /web/frontend && bash'
|
||||
cd ../src/frontend
|
|
@ -0,0 +1,102 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { Route, RouterModule } from '@angular/router';
|
||||
import { NotFoundComponent } from './modules/shared/components/not-found/not-found.component';
|
||||
|
||||
interface AppMenuEntry {
|
||||
label: string;
|
||||
icon: string;
|
||||
path?: string;
|
||||
matchExact?: boolean;
|
||||
level?: number;
|
||||
}
|
||||
|
||||
interface AppRoute extends Route {
|
||||
menuEntries?: AppMenuEntry[];
|
||||
}
|
||||
|
||||
export const appRoutes: AppRoute[] = [
|
||||
{
|
||||
path: '',
|
||||
component: NotFoundComponent,
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.HOME',
|
||||
icon: 'fa fa-home',
|
||||
matchExact: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'profile',
|
||||
component: NotFoundComponent,
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.PROFILE',
|
||||
icon: 'fa fa-user',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'community',
|
||||
component: NotFoundComponent,
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.COMMUNITY',
|
||||
icon: 'fa fa-users',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'messages',
|
||||
component: NotFoundComponent,
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.MESSAGES',
|
||||
icon: 'fa fa-comments',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'tests',
|
||||
component: NotFoundComponent,
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.TESTS',
|
||||
icon: 'fa fa-heartbeat',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'research',
|
||||
component: NotFoundComponent,
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.RESEARCH',
|
||||
icon: 'fa fa-flask',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'admin',
|
||||
loadChildren: () => import('./modules/admin/admin.module').then(m => m.AdminModule),
|
||||
menuEntries: [
|
||||
{
|
||||
label: 'MENU.ADMIN',
|
||||
icon: 'fa fa-id-card',
|
||||
matchExact: true,
|
||||
},
|
||||
{
|
||||
path: 'admin/users',
|
||||
label: 'MENU.USERS',
|
||||
icon: 'fa fa-users',
|
||||
level: 1,
|
||||
}
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(appRoutes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
<ng-template #toolbar>
|
||||
<mat-toolbar [color]="isDark ? null : 'primary'">
|
||||
<button mat-icon-button (click)="toggleSidebar()">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<span>{{ 'APP.NAME' | translate }}</span>
|
||||
<span class="flex-grow-1"></span>
|
||||
</mat-toolbar>
|
||||
</ng-template>
|
||||
<ng-container *ngIf="!isMobile">
|
||||
<ng-container *ngTemplateOutlet="toolbar"></ng-container>
|
||||
</ng-container>
|
||||
<mat-drawer-container class="flex-grow-1">
|
||||
<mat-drawer [mode]="sidebarMode" [opened]="isSidebarOpen" (openedChange)="onSidebarOpenedChange($event)">
|
||||
<div class="menu">
|
||||
<div>
|
||||
<ng-container *ngFor="let route of appRoutes">
|
||||
<ng-container *ngIf="route.menuEntries">
|
||||
<div
|
||||
*ngFor="let menu of route.menuEntries"
|
||||
class="menu-item"
|
||||
[attr.style]="menu.level ? 'margin-left: '+(menu.level * 20)+'px' : ''"
|
||||
[routerLink]="menu.path ? menu.path : route.path"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{exact: menu.matchExact}">
|
||||
<i [class]="menu.icon" [title]="menu.label | translate"></i>
|
||||
<span>{{ menu.label | translate }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex user-box">
|
||||
<div class="avatar" [style.background-image]="url(user.avatar)"></div>
|
||||
<div class="d-flex align-items-center flex-grow-1 p-2">{{ user.name }} {{ user.surname }}</div>
|
||||
<div class="user-menu-container">
|
||||
<div class="user-menu">
|
||||
<button mat-icon-button [matMenuTriggerFor]="usermenu">
|
||||
<i class="fa fa-cog"></i>
|
||||
</button>
|
||||
</div>
|
||||
<mat-menu #usermenu="matMenu" yPosition="above" xPosition="before" class="w-220">
|
||||
<mat-form-field class="w-100 px-3">
|
||||
<mat-label>{{ 'APP.LANGUAGE' | translate }}</mat-label>
|
||||
<mat-select [formControl]="langControl">
|
||||
<mat-option *ngFor="let lang of langs" [value]="lang">{{ 'LANGUAGE.'+lang.toUpperCase() | translate }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="w-100 px-3">
|
||||
<mat-label>{{ 'APP.THEME' | translate }}</mat-label>
|
||||
<mat-select [formControl]="themeControl">
|
||||
<mat-option *ngFor="let theme of themes" [value]="theme">{{ 'THEME.'+theme.toUpperCase() | translate }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</mat-drawer>
|
||||
|
||||
<ng-container *ngIf="isMobile">
|
||||
<ng-container *ngTemplateOutlet="toolbar"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<div class="example-sidenav-content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</mat-drawer-container>
|
|
@ -0,0 +1,91 @@
|
|||
$sidebar-width: 250px;
|
||||
:host{
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.menu{
|
||||
width: $sidebar-width;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
> :first-child{
|
||||
flex-grow: 1;
|
||||
}
|
||||
> * {
|
||||
width: $sidebar-width;
|
||||
}
|
||||
}
|
||||
.avatar{
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 10px;
|
||||
background-color: rgba(0,0,0, 0.25);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
.menu-item{
|
||||
display: flex;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
transition-duration: 400ms !important;
|
||||
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||
transition-property: all !important;
|
||||
&:after{
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0px;
|
||||
width: 3px;
|
||||
height: 0%;
|
||||
background: var(--primary-color);
|
||||
transition-duration: 400ms !important;
|
||||
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||
transition-property: all !important;
|
||||
}
|
||||
&.active{
|
||||
&:after{
|
||||
top: 0%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
i {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10px;
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
span{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
.user-box{
|
||||
padding: 6px;
|
||||
}
|
||||
.user-menu-container{
|
||||
width: 46px;
|
||||
.user-menu{
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
right: 6px;
|
||||
height: 48px;
|
||||
width: 46px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { appRoutes } from './app-routing.module';
|
||||
import { AppService, WindowSize } from './modules/shared/services/app/app.service';
|
||||
import { BrowserStorageService } from './modules/shared/services/browser-storage/browser-storage.service';
|
||||
import { ThemeService } from './modules/shared/services/theme/theme.service';
|
||||
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
|
||||
|
||||
enum SidebarOpenEnum {
|
||||
Default,
|
||||
Open,
|
||||
Closed,
|
||||
}
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
isMobile = true;
|
||||
title = 'CureNet';
|
||||
sidebarOpen = SidebarOpenEnum.Default;
|
||||
sidebarMode = 'over';
|
||||
defaultSidebarOpen = false;
|
||||
lang: string;
|
||||
theme: string;
|
||||
langs: string[] = [];
|
||||
themes: string[] = [];
|
||||
themeControl = new FormControl();
|
||||
langControl = new FormControl();
|
||||
isDark = true;
|
||||
appRoutes = appRoutes;
|
||||
user = {
|
||||
name: 'Name',
|
||||
surname: 'Surname',
|
||||
avatar: 'https://www.gravatar.com/avatar/81b206a89f89d5b1123b87606075c6a8?s=514&d=robohash',
|
||||
};
|
||||
|
||||
get isSidebarOpen() {
|
||||
switch (this.sidebarOpen) {
|
||||
case SidebarOpenEnum.Open:
|
||||
return true;
|
||||
break;
|
||||
case SidebarOpenEnum.Closed:
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return this.defaultSidebarOpen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
appService: AppService,
|
||||
themeService: ThemeService,
|
||||
private browserStorageService: BrowserStorageService,
|
||||
private sanitizer: DomSanitizer,
|
||||
) {
|
||||
this.themes = themeService.getThemes();
|
||||
this.langs = appService.getLangs();
|
||||
this.lang = appService.getLang();
|
||||
|
||||
this.themeControl.valueChanges.subscribe(theme => {
|
||||
themeService.setTheme(theme);
|
||||
});
|
||||
appService.changeLang(this.lang);
|
||||
this.langControl.valueChanges.subscribe(lang => {
|
||||
appService.changeLang(lang);
|
||||
});
|
||||
this.onThemeChanged(themeService.getTheme());
|
||||
themeService.themeChange.subscribe(theme => {
|
||||
this.onThemeChanged(theme);
|
||||
});
|
||||
this.onResize(appService.size);
|
||||
appService.sizeChange.subscribe(size => {
|
||||
this.onResize(size);
|
||||
});
|
||||
this.onLangChanged(appService.getLang());
|
||||
appService.langChange.subscribe(lang => {
|
||||
this.onLangChanged(lang);
|
||||
});
|
||||
|
||||
const sidebarUserPrefference = this.browserStorageService.getItem('sidebar.open');
|
||||
if (sidebarUserPrefference !== null) {
|
||||
this.sidebarOpen = sidebarUserPrefference;
|
||||
}
|
||||
}
|
||||
|
||||
onThemeChanged(theme: string) {
|
||||
if (theme === undefined) {
|
||||
return;
|
||||
}
|
||||
this.theme = theme;
|
||||
this.themeControl.setValue(theme);
|
||||
this.isDark = theme.indexOf('dark') !== -1;
|
||||
}
|
||||
|
||||
onResize(size: WindowSize) {
|
||||
if (size === undefined) {
|
||||
return;
|
||||
}
|
||||
this.isMobile = size.isMobile;
|
||||
this.defaultSidebarOpen = !size.isMobile;
|
||||
this.sidebarMode = this.isMobile ? 'over' : 'side';
|
||||
}
|
||||
|
||||
onLangChanged(lang: string) {
|
||||
if (lang === undefined) {
|
||||
return;
|
||||
}
|
||||
this.lang = lang;
|
||||
this.langControl.setValue(lang);
|
||||
}
|
||||
|
||||
toggleSidebar() {
|
||||
this.sidebarOpen = this.isSidebarOpen ? SidebarOpenEnum.Closed : SidebarOpenEnum.Open;
|
||||
this.browserStorageService.setItem('sidebar.open', this.sidebarOpen);
|
||||
}
|
||||
|
||||
onSidebarOpenedChange(opened: boolean) {
|
||||
this.sidebarOpen = opened ? SidebarOpenEnum.Open : SidebarOpenEnum.Closed;
|
||||
}
|
||||
|
||||
url(url: string): SafeStyle {
|
||||
return this.sanitizer.bypassSecurityTrustStyle(`url('${url}')`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MaterialModule } from './modules/material/material.module';
|
||||
import { AppService } from './modules/shared/services/app/app.service';
|
||||
import { ThemeService } from './modules/shared/services/theme/theme.service';
|
||||
import { BrowserStorageService } from './modules/shared/services/browser-storage/browser-storage.service';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
|
||||
export function HttpLoaderFactory(http: HttpClient) {
|
||||
return new TranslateHttpLoader(http, '/assets/lang/', '.json');
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
MaterialModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
HttpClientModule,
|
||||
TranslateModule.forRoot({
|
||||
defaultLanguage: 'en',
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
AppService,
|
||||
ThemeService,
|
||||
BrowserStorageService,
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
|
@ -0,0 +1,20 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Route } from '@angular/router';
|
||||
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
|
||||
|
||||
const routes: Route[] = [
|
||||
{
|
||||
path: '',
|
||||
component: NotFoundComponent,
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
component: NotFoundComponent,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AdminRoutingModule { }
|
|
@ -0,0 +1,16 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AdminRoutingModule } from './admin-routing.module';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
AdminRoutingModule,
|
||||
SharedModule,
|
||||
]
|
||||
})
|
||||
export class AdminModule { }
|
|
@ -0,0 +1,27 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
|
||||
const itemsToExport = [
|
||||
MatToolbarModule,
|
||||
MatButtonModule,
|
||||
MatSidenavModule,
|
||||
MatSelectModule,
|
||||
MatMenuModule,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
...itemsToExport,
|
||||
],
|
||||
exports: [
|
||||
...itemsToExport,
|
||||
]
|
||||
})
|
||||
export class MaterialModule { }
|
|
@ -0,0 +1,3 @@
|
|||
<div class="m-3 p-3">
|
||||
{{ 'ERROR.PAGE_NOT_FOUND' | translate }}
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-not-found',
|
||||
templateUrl: './not-found.component.html',
|
||||
styleUrls: ['./not-found.component.scss']
|
||||
})
|
||||
export class NotFoundComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { fromEvent, Subject } from 'rxjs';
|
||||
import { BrowserStorageService } from '../browser-storage/browser-storage.service';
|
||||
|
||||
export class WindowSize {
|
||||
isMobile: boolean;
|
||||
|
||||
constructor(
|
||||
public width: number,
|
||||
public height: number,
|
||||
) {
|
||||
this.isMobile = this.width <= 700;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.width = window.innerWidth;
|
||||
this.height = window.innerHeight;
|
||||
}
|
||||
|
||||
static generate() {
|
||||
return new WindowSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
private lang = 'en';
|
||||
private defaultLang = 'en';
|
||||
langChange = new Subject<string>();
|
||||
size: WindowSize;
|
||||
sizeChange = new Subject<WindowSize>();
|
||||
|
||||
constructor(
|
||||
browserStorageService: BrowserStorageService,
|
||||
private translate: TranslateService,
|
||||
) {
|
||||
this.langChange.subscribe(lang => {
|
||||
this.lang = lang;
|
||||
browserStorageService.setItem('language', lang);
|
||||
});
|
||||
this.sizeChange.subscribe(size => this.size = size);
|
||||
this.sizeChange.next(WindowSize.generate());
|
||||
let lang = browserStorageService.getItem('language');
|
||||
if (!lang) {
|
||||
lang = this.defaultLang;
|
||||
}
|
||||
this.translate.setDefaultLang(this.defaultLang);
|
||||
this.changeLang(lang);
|
||||
fromEvent(window, 'resize').subscribe(e => {
|
||||
this.sizeChange.next(WindowSize.generate());
|
||||
})
|
||||
}
|
||||
|
||||
changeLang(lang: string) {
|
||||
if (lang !== this.lang) {
|
||||
this.translate.use(lang);
|
||||
this.langChange.next(lang);
|
||||
}
|
||||
}
|
||||
|
||||
getLang() {
|
||||
return this.lang;
|
||||
}
|
||||
|
||||
getLangs() {
|
||||
return [
|
||||
'pl',
|
||||
'en',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class BrowserStorageService {
|
||||
|
||||
private storage: Storage;
|
||||
private namespace: string;
|
||||
constructor() {
|
||||
this.storage = localStorage;
|
||||
this.namespace = 'default';
|
||||
}
|
||||
|
||||
setNamespace(namespace: string) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
getNamespace(namespace: string | null = null): string {
|
||||
return namespace ?? this.namespace;
|
||||
}
|
||||
|
||||
setItem(key: string, value: any, namespace: string | null = null) {
|
||||
namespace = this.getNamespace(namespace);
|
||||
const path = `${namespace}.${key}`;
|
||||
this.storage.setItem(path, JSON.stringify(value));
|
||||
}
|
||||
|
||||
getItem(key: string, namespace: string | null = null): any {
|
||||
namespace = this.getNamespace(namespace);
|
||||
const path = `${namespace}.${key}`;
|
||||
return JSON.parse(this.storage.getItem(path) ?? 'null');
|
||||
}
|
||||
|
||||
getItemOrDefault(key: string, defaultValue: any, namespace: string | null = null): any {
|
||||
return this.getItem(key, namespace) ?? defaultValue;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BrowserStorageService } from '../browser-storage/browser-storage.service';
|
||||
|
||||
@Injectable()
|
||||
export class ThemeService {
|
||||
private theme: string;
|
||||
themeChange = new Subject<string>();
|
||||
|
||||
constructor(
|
||||
browserStorageService: BrowserStorageService
|
||||
) {
|
||||
this.themeChange.subscribe(theme => {
|
||||
this.theme = theme;
|
||||
browserStorageService.setItem('current-theme', theme);
|
||||
});
|
||||
let userTheme = browserStorageService.getItem('current-theme');
|
||||
if (!userTheme) {
|
||||
userTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
this.setTheme(userTheme);
|
||||
}
|
||||
|
||||
getTheme() {
|
||||
return this.theme;
|
||||
}
|
||||
|
||||
setTheme(theme: string) {
|
||||
if (theme !== this.theme) {
|
||||
this.themeChange.next(theme);
|
||||
document.getElementById('current-theme').setAttribute('href', `/${theme}.css`);
|
||||
}
|
||||
}
|
||||
|
||||
getThemes(): string[] {
|
||||
return [
|
||||
'light',
|
||||
'dark',
|
||||
];
|
||||
};
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NotFoundComponent } from './components/not-found/not-found.component';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { HttpLoaderFactory } from 'src/app/app.module';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
NotFoundComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
TranslateModule.forRoot({
|
||||
defaultLanguage: 'en',
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
],
|
||||
})
|
||||
export class SharedModule { }
|
|
@ -0,0 +1,6 @@
|
|||
@use 'sass:map';
|
||||
|
||||
$primary-color: map.get($theme, color, primary, 500);
|
||||
body {
|
||||
--primary-color: #{$primary-color};
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
$theme: (
|
||||
color: (
|
||||
primary: (
|
||||
50: #e8eaf6,
|
||||
100: #c5cae9,
|
||||
200: #9fa8da,
|
||||
300: #7986cb,
|
||||
400: #5c6bc0,
|
||||
500: #3f51b5,
|
||||
600: #3949ab,
|
||||
700: #303f9f,
|
||||
800: #283593,
|
||||
900: #1a237e,
|
||||
A100: #8c9eff,
|
||||
A200: #536dfe,
|
||||
A400: #3d5afe,
|
||||
A700: #304ffe,
|
||||
contrast: (
|
||||
50: rgba(0, 0, 0, 0.87),
|
||||
100: rgba(0, 0, 0, 0.87),
|
||||
200: rgba(0, 0, 0, 0.87),
|
||||
300: white,
|
||||
400: white,
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: rgba(0, 0, 0, 0.87),
|
||||
A200: white,
|
||||
A400: white,
|
||||
A700: white,
|
||||
),
|
||||
default: #3f51b5,
|
||||
lighter: #c5cae9,
|
||||
darker: #303f9f,
|
||||
text: #3f51b5,
|
||||
default-contrast: white,
|
||||
lighter-contrast: rgba(0, 0, 0, 0.87),
|
||||
darker-contrast: white,
|
||||
"50-contrast": rgba(0, 0, 0, 0.87),
|
||||
"100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"200-contrast": rgba(0, 0, 0, 0.87),
|
||||
"300-contrast": white,
|
||||
"400-contrast": white,
|
||||
"500-contrast": white,
|
||||
"600-contrast": white,
|
||||
"700-contrast": white,
|
||||
"800-contrast": white,
|
||||
"900-contrast": white,
|
||||
"A100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"A200-contrast": white,
|
||||
"A400-contrast": white,
|
||||
"A700-contrast": white,
|
||||
"contrast-contrast": null,
|
||||
),
|
||||
accent: (
|
||||
50: #fce4ec,
|
||||
100: #f8bbd0,
|
||||
200: #f48fb1,
|
||||
300: #f06292,
|
||||
400: #ec407a,
|
||||
500: #e91e63,
|
||||
600: #d81b60,
|
||||
700: #c2185b,
|
||||
800: #ad1457,
|
||||
900: #880e4f,
|
||||
A100: #ff80ab,
|
||||
A200: #ff4081,
|
||||
A400: #f50057,
|
||||
A700: #c51162,
|
||||
contrast: (
|
||||
50: rgba(0, 0, 0, 0.87),
|
||||
100: rgba(0, 0, 0, 0.87),
|
||||
200: rgba(0, 0, 0, 0.87),
|
||||
300: rgba(0, 0, 0, 0.87),
|
||||
400: rgba(0, 0, 0, 0.87),
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: rgba(0, 0, 0, 0.87),
|
||||
A200: white,
|
||||
A400: white,
|
||||
A700: white,
|
||||
),
|
||||
default: #ff4081,
|
||||
lighter: #ff80ab,
|
||||
darker: #f50057,
|
||||
text: #ff4081,
|
||||
default-contrast: white,
|
||||
lighter-contrast: rgba(0, 0, 0, 0.87),
|
||||
darker-contrast: white,
|
||||
"50-contrast": rgba(0, 0, 0, 0.87),
|
||||
"100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"200-contrast": rgba(0, 0, 0, 0.87),
|
||||
"300-contrast": rgba(0, 0, 0, 0.87),
|
||||
"400-contrast": rgba(0, 0, 0, 0.87),
|
||||
"500-contrast": white,
|
||||
"600-contrast": white,
|
||||
"700-contrast": white,
|
||||
"800-contrast": white,
|
||||
"900-contrast": white,
|
||||
"A100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"A200-contrast": white,
|
||||
"A400-contrast": white,
|
||||
"A700-contrast": white,
|
||||
"contrast-contrast": null,
|
||||
),
|
||||
warn: (
|
||||
50: #ffebee,
|
||||
100: #ffcdd2,
|
||||
200: #ef9a9a,
|
||||
300: #e57373,
|
||||
400: #ef5350,
|
||||
500: #f44336,
|
||||
600: #e53935,
|
||||
700: #d32f2f,
|
||||
800: #c62828,
|
||||
900: #b71c1c,
|
||||
A100: #ff8a80,
|
||||
A200: #ff5252,
|
||||
A400: #ff1744,
|
||||
A700: #d50000,
|
||||
contrast: (
|
||||
50: rgba(0, 0, 0, 0.87),
|
||||
100: rgba(0, 0, 0, 0.87),
|
||||
200: rgba(0, 0, 0, 0.87),
|
||||
300: rgba(0, 0, 0, 0.87),
|
||||
400: rgba(0, 0, 0, 0.87),
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: rgba(0, 0, 0, 0.87),
|
||||
A200: white,
|
||||
A400: white,
|
||||
A700: white,
|
||||
),
|
||||
default: #f44336,
|
||||
lighter: #ffcdd2,
|
||||
darker: #d32f2f,
|
||||
text: #f44336,
|
||||
default-contrast: white,
|
||||
lighter-contrast: rgba(0, 0, 0, 0.87),
|
||||
darker-contrast: white,
|
||||
"50-contrast": rgba(0, 0, 0, 0.87),
|
||||
"100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"200-contrast": rgba(0, 0, 0, 0.87),
|
||||
"300-contrast": rgba(0, 0, 0, 0.87),
|
||||
"400-contrast": rgba(0, 0, 0, 0.87),
|
||||
"500-contrast": white,
|
||||
"600-contrast": white,
|
||||
"700-contrast": white,
|
||||
"800-contrast": white,
|
||||
"900-contrast": white,
|
||||
"A100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"A200-contrast": white,
|
||||
"A400-contrast": white,
|
||||
"A700-contrast": white,
|
||||
"contrast-contrast": null,
|
||||
),
|
||||
is-dark: true,
|
||||
foreground: (
|
||||
base: white,
|
||||
divider: rgba(255, 255, 255, 0.12),
|
||||
dividers: rgba(255, 255, 255, 0.12),
|
||||
disabled: rgba(255, 255, 255, 0.5),
|
||||
disabled-button: rgba(255, 255, 255, 0.3),
|
||||
disabled-text: rgba(255, 255, 255, 0.5),
|
||||
elevation: black,
|
||||
hint-text: rgba(255, 255, 255, 0.5),
|
||||
secondary-text: rgba(255, 255, 255, 0.7),
|
||||
icon: white,
|
||||
icons: white,
|
||||
text: white,
|
||||
slider-min: white,
|
||||
slider-off: rgba(255, 255, 255, 0.3),
|
||||
slider-off-active: rgba(255, 255, 255, 0.3),
|
||||
),
|
||||
background: (
|
||||
status-bar: black,
|
||||
app-bar: #212121,
|
||||
background: #303030,
|
||||
hover: rgba(255, 255, 255, 0.04),
|
||||
card: #424242,
|
||||
dialog: #424242,
|
||||
disabled-button: rgba(255, 255, 255, 0.12),
|
||||
raised-button: #424242,
|
||||
focused-button: rgba(255, 255, 255, 0.12),
|
||||
selected-button: #212121,
|
||||
selected-disabled-button: #424242,
|
||||
disabled-button-toggle: black,
|
||||
unselected-chip: #616161,
|
||||
disabled-list-option: black,
|
||||
tooltip: #616161,
|
||||
),
|
||||
),
|
||||
primary: (
|
||||
50: #e8eaf6,
|
||||
100: #c5cae9,
|
||||
200: #9fa8da,
|
||||
300: #7986cb,
|
||||
400: #5c6bc0,
|
||||
500: #3f51b5,
|
||||
600: #3949ab,
|
||||
700: #303f9f,
|
||||
800: #283593,
|
||||
900: #1a237e,
|
||||
A100: #8c9eff,
|
||||
A200: #536dfe,
|
||||
A400: #3d5afe,
|
||||
A700: #304ffe,
|
||||
contrast: (
|
||||
50: rgba(0, 0, 0, 0.87),
|
||||
100: rgba(0, 0, 0, 0.87),
|
||||
200: rgba(0, 0, 0, 0.87),
|
||||
300: white,
|
||||
400: white,
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: rgba(0, 0, 0, 0.87),
|
||||
A200: white,
|
||||
A400: white,
|
||||
A700: white,
|
||||
),
|
||||
default: #3f51b5,
|
||||
lighter: #c5cae9,
|
||||
darker: #303f9f,
|
||||
text: #3f51b5,
|
||||
default-contrast: white,
|
||||
lighter-contrast: rgba(0, 0, 0, 0.87),
|
||||
darker-contrast: white,
|
||||
"50-contrast": rgba(0, 0, 0, 0.87),
|
||||
"100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"200-contrast": rgba(0, 0, 0, 0.87),
|
||||
"300-contrast": white,
|
||||
"400-contrast": white,
|
||||
"500-contrast": white,
|
||||
"600-contrast": white,
|
||||
"700-contrast": white,
|
||||
"800-contrast": white,
|
||||
"900-contrast": white,
|
||||
"A100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"A200-contrast": white,
|
||||
"A400-contrast": white,
|
||||
"A700-contrast": white,
|
||||
"contrast-contrast": null,
|
||||
),
|
||||
accent: (
|
||||
50: #fce4ec,
|
||||
100: #f8bbd0,
|
||||
200: #f48fb1,
|
||||
300: #f06292,
|
||||
400: #ec407a,
|
||||
500: #e91e63,
|
||||
600: #d81b60,
|
||||
700: #c2185b,
|
||||
800: #ad1457,
|
||||
900: #880e4f,
|
||||
A100: #ff80ab,
|
||||
A200: #ff4081,
|
||||
A400: #f50057,
|
||||
A700: #c51162,
|
||||
contrast: (
|
||||
50: rgba(0, 0, 0, 0.87),
|
||||
100: rgba(0, 0, 0, 0.87),
|
||||
200: rgba(0, 0, 0, 0.87),
|
||||
300: rgba(0, 0, 0, 0.87),
|
||||
400: rgba(0, 0, 0, 0.87),
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: rgba(0, 0, 0, 0.87),
|
||||
A200: white,
|
||||
A400: white,
|
||||
A700: white,
|
||||
),
|
||||
default: #ff4081,
|
||||
lighter: #ff80ab,
|
||||
darker: #f50057,
|
||||
text: #ff4081,
|
||||
default-contrast: white,
|
||||
lighter-contrast: rgba(0, 0, 0, 0.87),
|
||||
darker-contrast: white,
|
||||
"50-contrast": rgba(0, 0, 0, 0.87),
|
||||
"100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"200-contrast": rgba(0, 0, 0, 0.87),
|
||||
"300-contrast": rgba(0, 0, 0, 0.87),
|
||||
"400-contrast": rgba(0, 0, 0, 0.87),
|
||||
"500-contrast": white,
|
||||
"600-contrast": white,
|
||||
"700-contrast": white,
|
||||
"800-contrast": white,
|
||||
"900-contrast": white,
|
||||
"A100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"A200-contrast": white,
|
||||
"A400-contrast": white,
|
||||
"A700-contrast": white,
|
||||
"contrast-contrast": null,
|
||||
),
|
||||
warn: (
|
||||
50: #ffebee,
|
||||
100: #ffcdd2,
|
||||
200: #ef9a9a,
|
||||
300: #e57373,
|
||||
400: #ef5350,
|
||||
500: #f44336,
|
||||
600: #e53935,
|
||||
700: #d32f2f,
|
||||
800: #c62828,
|
||||
900: #b71c1c,
|
||||
A100: #ff8a80,
|
||||
A200: #ff5252,
|
||||
A400: #ff1744,
|
||||
A700: #d50000,
|
||||
contrast: (
|
||||
50: rgba(0, 0, 0, 0.87),
|
||||
100: rgba(0, 0, 0, 0.87),
|
||||
200: rgba(0, 0, 0, 0.87),
|
||||
300: rgba(0, 0, 0, 0.87),
|
||||
400: rgba(0, 0, 0, 0.87),
|
||||
500: white,
|
||||
600: white,
|
||||
700: white,
|
||||
800: white,
|
||||
900: white,
|
||||
A100: rgba(0, 0, 0, 0.87),
|
||||
A200: white,
|
||||
A400: white,
|
||||
A700: white,
|
||||
),
|
||||
default: #f44336,
|
||||
lighter: #ffcdd2,
|
||||
darker: #d32f2f,
|
||||
text: #f44336,
|
||||
default-contrast: white,
|
||||
lighter-contrast: rgba(0, 0, 0, 0.87),
|
||||
darker-contrast: white,
|
||||
"50-contrast": rgba(0, 0, 0, 0.87),
|
||||
"100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"200-contrast": rgba(0, 0, 0, 0.87),
|
||||
"300-contrast": rgba(0, 0, 0, 0.87),
|
||||
"400-contrast": rgba(0, 0, 0, 0.87),
|
||||
"500-contrast": white,
|
||||
"600-contrast": white,
|
||||
"700-contrast": white,
|
||||
"800-contrast": white,
|
||||
"900-contrast": white,
|
||||
"A100-contrast": rgba(0, 0, 0, 0.87),
|
||||
"A200-contrast": white,
|
||||
"A400-contrast": white,
|
||||
"A700-contrast": white,
|
||||
"contrast-contrast": null,
|
||||
),
|
||||
is-dark: true,
|
||||
foreground: (
|
||||
base: white,
|
||||
divider: rgba(255, 255, 255, 0.12),
|
||||
dividers: rgba(255, 255, 255, 0.12),
|
||||
disabled: rgba(255, 255, 255, 0.5),
|
||||
disabled-button: rgba(255, 255, 255, 0.3),
|
||||
disabled-text: rgba(255, 255, 255, 0.5),
|
||||
elevation: black,
|
||||
hint-text: rgba(255, 255, 255, 0.5),
|
||||
secondary-text: rgba(255, 255, 255, 0.7),
|
||||
icon: white,
|
||||
icons: white,
|
||||
text: white,
|
||||
slider-min: white,
|
||||
slider-off: rgba(255, 255, 255, 0.3),
|
||||
slider-off-active: rgba(255, 255, 255, 0.3),
|
||||
),
|
||||
background: (
|
||||
status-bar: black,
|
||||
app-bar: #212121,
|
||||
background: #303030,
|
||||
hover: rgba(255, 255, 255, 0.04),
|
||||
card: #424242,
|
||||
dialog: #424242,
|
||||
disabled-button: rgba(255, 255, 255, 0.12),
|
||||
raised-button: #424242,
|
||||
focused-button: rgba(255, 255, 255, 0.12),
|
||||
selected-button: #212121,
|
||||
selected-disabled-button: #424242,
|
||||
disabled-button-toggle: black,
|
||||
unselected-chip: #616161,
|
||||
disabled-list-option: black,
|
||||
tooltip: #616161,
|
||||
),
|
||||
);
|
|
@ -0,0 +1,35 @@
|
|||
// Custom Theming for Angular Material
|
||||
// For more information: https://material.angular.io/guide/theming
|
||||
@use '~@angular/material' as mat;
|
||||
// Plus imports for other components in your app.
|
||||
|
||||
// Include the common styles for Angular Material. We include this here so that you only
|
||||
// have to load a single css file for Angular Material in your app.
|
||||
// Be sure that you only ever include this mixin once!
|
||||
@include mat.core();
|
||||
|
||||
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||
// hue. Available color palettes: https://material.io/design/color/
|
||||
$theme-primary: mat.define-palette(mat.$blue-palette);
|
||||
$theme-accent: mat.define-palette(mat.$green-palette, A200, A100, A400);
|
||||
|
||||
// The warn palette is optional (defaults to red).
|
||||
$theme-warn: mat.define-palette(mat.$red-palette);
|
||||
|
||||
// Create the theme object. A theme consists of configurations for individual
|
||||
// theming systems such as "color" or "typography".
|
||||
$theme: mat.define-dark-theme((
|
||||
color: (
|
||||
primary: $theme-primary,
|
||||
accent: $theme-accent,
|
||||
warn: $theme-warn,
|
||||
)
|
||||
));
|
||||
|
||||
// Include theme styles for core and each component used in your app.
|
||||
// Alternatively, you can import and @include the theme mixins for each component
|
||||
// that you are using.
|
||||
@include mat.all-component-themes($theme);
|
||||
|
||||
@import "./base";
|
|
@ -0,0 +1,35 @@
|
|||
// Custom Theming for Angular Material
|
||||
// For more information: https://material.angular.io/guide/theming
|
||||
@use '~@angular/material' as mat;
|
||||
// Plus imports for other components in your app.
|
||||
|
||||
// Include the common styles for Angular Material. We include this here so that you only
|
||||
// have to load a single css file for Angular Material in your app.
|
||||
// Be sure that you only ever include this mixin once!
|
||||
@include mat.core();
|
||||
|
||||
// Define the palettes for your theme using the Material Design palettes available in palette.scss
|
||||
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
|
||||
// hue. Available color palettes: https://material.io/design/color/
|
||||
$theme-primary: mat.define-palette(mat.$indigo-palette);
|
||||
$theme-accent: mat.define-palette(mat.$light-blue-palette, A200, A100, A400);
|
||||
|
||||
// The warn palette is optional (defaults to red).
|
||||
$theme-warn: mat.define-palette(mat.$red-palette);
|
||||
|
||||
// Create the theme object. A theme consists of configurations for individual
|
||||
// theming systems such as "color" or "typography".
|
||||
$theme: mat.define-light-theme((
|
||||
color: (
|
||||
primary: $theme-primary,
|
||||
accent: $theme-accent,
|
||||
warn: $theme-warn,
|
||||
)
|
||||
));
|
||||
|
||||
// Include theme styles for core and each component used in your app.
|
||||
// Alternatively, you can import and @include the theme mixins for each component
|
||||
// that you are using.
|
||||
@include mat.all-component-themes($theme);
|
||||
|
||||
@import "./base";
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"APP": {
|
||||
"NAME": "CureNet",
|
||||
"THEME": "Theme",
|
||||
"LANGUAGE": "Language"
|
||||
},
|
||||
"ERROR": {
|
||||
"PAGE_NOT_FOUND": "Page not found or not implemented yet!"
|
||||
},
|
||||
"THEME": {
|
||||
"LIGHT": "Light",
|
||||
"DARK": "Dark"
|
||||
},
|
||||
"LANGUAGE": {
|
||||
"PL": "Polish",
|
||||
"EN": "English"
|
||||
},
|
||||
"MENU": {
|
||||
"HOME": "Dashboard",
|
||||
"PROFILE": "Profile",
|
||||
"COMMUNITY": "Community",
|
||||
"MESSAGES": "Messages",
|
||||
"TESTS": "Laboratory tests",
|
||||
"RESEARCH": "Research",
|
||||
"ADMIN": "Admin",
|
||||
"USERS": "Users"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"APP": {
|
||||
"NAME": "CureNet",
|
||||
"THEME": "Motyw",
|
||||
"LANGUAGE": "Język"
|
||||
},
|
||||
"ERROR": {
|
||||
"PAGE_NOT_FOUND": "Nie znaleziono strony lub nie została ona jeszcze zaimplementowana!"
|
||||
},
|
||||
"THEME": {
|
||||
"LIGHT": "Jasny",
|
||||
"DARK": "Ciemny"
|
||||
},
|
||||
"LANGUAGE": {
|
||||
"PL": "Polski",
|
||||
"EN": "Angielski"
|
||||
},
|
||||
"MENU": {
|
||||
"HOME": "Kokpit",
|
||||
"PROFILE": "Profil",
|
||||
"COMMUNITY": "Społeczność",
|
||||
"MESSAGES": "Wiadomości",
|
||||
"TESTS": "Wyniki badań",
|
||||
"RESEARCH": "Badania naukowe",
|
||||
"ADMIN": "Panel administratora",
|
||||
"USERS": "Użytkownicy"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export const environment = {
|
||||
production: true
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
Binary file not shown.
After Width: | Height: | Size: 948 B |
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CureNet</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link rel="stylesheet" href="/light.css" id="current-theme">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
|
||||
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
|
||||
*/
|
||||
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js'; // Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
|
@ -0,0 +1,45 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
|
||||
html, body { height: 100%; }
|
||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||
|
||||
@import "~bootstrap/dist/css/bootstrap.css";
|
||||
@import "~font-awesome/css/font-awesome.css";
|
||||
|
||||
body .mat-drawer.mat-drawer-side {
|
||||
visibility: visible !important;
|
||||
transform: none !important;
|
||||
width: 250px;
|
||||
&,
|
||||
& .mat-drawer-inner-container{
|
||||
transition-duration: 400ms !important;
|
||||
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !important;
|
||||
transition-property: transform, margin-left, margin-right, width !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
& ~ .mat-drawer-content{
|
||||
margin-left: 250px !important;
|
||||
}
|
||||
&:not(.mat-drawer-opened) {
|
||||
width: 60px;
|
||||
.menu-item{
|
||||
margin-left: 0px !important;
|
||||
&:after{
|
||||
right: 190px;
|
||||
}
|
||||
& > i{
|
||||
width: 40px;
|
||||
font-size: 120%;
|
||||
}
|
||||
}
|
||||
.hide-on-side-opened{
|
||||
display: none;
|
||||
}
|
||||
& ~ .mat-drawer-content {
|
||||
margin-left: 60px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.w-220{
|
||||
width: 220px;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(path: string, deep?: boolean, filter?: RegExp): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
|
@ -0,0 +1,15 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"align": {
|
||||
"options": [
|
||||
"parameters",
|
||||
"statements"
|
||||
]
|
||||
},
|
||||
"array-type": false,
|
||||
"arrow-return-shorthand": true,
|
||||
"curly": true,
|
||||
"deprecation": {
|
||||
"severity": "warning"
|
||||
},
|
||||
"component-class-suffix": true,
|
||||
"contextual-lifecycle": true,
|
||||
"directive-class-suffix": true,
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"app",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"app",
|
||||
"kebab-case"
|
||||
],
|
||||
"eofline": true,
|
||||
"import-blacklist": [
|
||||
true,
|
||||
"rxjs/Rx"
|
||||
],
|
||||
"import-spacing": true,
|
||||
"indent": {
|
||||
"options": [
|
||||
"spaces"
|
||||
]
|
||||
},
|
||||
"max-classes-per-file": false,
|
||||
"max-line-length": [
|
||||
true,
|
||||
140
|
||||
],
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": [
|
||||
"static-field",
|
||||
"instance-field",
|
||||
"static-method",
|
||||
"instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-empty": false,
|
||||
"no-inferrable-types": [
|
||||
true,
|
||||
"ignore-params"
|
||||
],
|
||||
"no-non-null-assertion": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-var-requires": false,
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"as-needed"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"single"
|
||||
],
|
||||
"semicolon": {
|
||||
"options": [
|
||||
"always"
|
||||
]
|
||||
},
|
||||
"space-before-function-paren": {
|
||||
"options": {
|
||||
"anonymous": "never",
|
||||
"asyncArrow": "always",
|
||||
"constructor": "never",
|
||||
"method": "never",
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
"typedef": [
|
||||
true,
|
||||
"call-signature"
|
||||
],
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
"variable-name": {
|
||||
"options": [
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-pascal-case"
|
||||
]
|
||||
},
|
||||
"whitespace": {
|
||||
"options": [
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type",
|
||||
"check-typecast"
|
||||
]
|
||||
},
|
||||
"no-conflicting-lifecycle": true,
|
||||
"no-host-metadata-property": true,
|
||||
"no-input-rename": true,
|
||||
"no-inputs-metadata-property": true,
|
||||
"no-output-native": true,
|
||||
"no-output-on-prefix": true,
|
||||
"no-output-rename": true,
|
||||
"no-outputs-metadata-property": true,
|
||||
"template-banana-in-box": true,
|
||||
"template-no-negated-async": true,
|
||||
"use-lifecycle-interface": true,
|
||||
"use-pipe-transform-interface": true
|
||||
},
|
||||
"rulesDirectory": [
|
||||
"codelyzer"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue