Compare commits

..

1 Commits

Author SHA1 Message Date
Sieciech 0417fea571 welcome message 2021-07-23 20:36:39 +02:00
77 changed files with 10121 additions and 13177 deletions

30
.vscode/launch.json vendored
View File

@ -30,36 +30,6 @@
"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,
}
}
]

198
Jenkinsfile vendored
View File

@ -1,198 +0,0 @@
pipeline {
agent any
environment {
DatabaseUrl = sh(returnStdout: true, script: 'bash /var/lib/jenkins/variables/CureNet/var.sh ${BRANCH_NAME} DatabaseUrl').trim()
ProjectPath = sh(returnStdout: true, script: 'bash /var/lib/jenkins/variables/CureNet/var.sh ${BRANCH_NAME} ProjectPath').trim()
DatabaseUrlTesting = sh(returnStdout: true, script: 'bash /var/lib/jenkins/variables/CureNet/var.sh master DatabaseUrl').trim()
}
stages {
stage('Pull') {
steps {
git(url: 'http://git.fufle.net/Community/CureNet.git', branch: '${BRANCH_NAME}', credentialsId: '799188a2-c281-4e4c-b78f-c82132ea792b', poll: true)
}
}
stage('Approve') {
steps {
echo "http://git.fufle.net/Community/CureNet/commit/${env.GIT_COMMIT}"
script {
if (env.BRANCH_NAME == "prod") {
echo "http://git.fufle.net/Community/CureNet/compare/prod...staging"
input(message: 'Deploy to production?', id: 'A', ok: 'Yes', submitter: 'deploy', submitterParameter: 'a')
}
if (env.BRANCH_NAME == "staging") {
echo "http://git.fufle.net/Community/CureNet/compare/staging...beta"
input(message: 'Deploy to staging instance?', id: 'A', ok: 'Yes', submitter: 'deploy', submitterParameter: 'a')
}
if (env.BRANCH_NAME == "beta") {
echo "http://git.fufle.net/Community/CureNet/compare/beta...dev"
input(message: 'Deploy to beta instance?', id: 'A', ok: 'Yes', submitter: 'deploy', submitterParameter: 'a')
}
}
}
}
stage('Install') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'sed -i "s/-1001/${BUILD_ID}/g" src/environments/*.ts'
sh 'Timestamp=`php8.0 -r "echo time();"` && sed -i "s/-1002/${Timestamp}/g" src/environments/*.ts'
sh 'npm ci'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'php8.0 /usr/local/bin/composer install'
}
}
}
}
}
stage('Configure for tests') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'echo skip'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'APP_SECRET=`php8.0 -r "echo md5(\\"branch=${BRANCH_NAME};\\");"` && sed -i "s/APP_SECRET=.*/APP_SECRET=${APP_SECRET}/g" .env'
sh '''sed -i 's/DATABASE_URL=.*/DATABASE_URL=${DatabaseUrlTesting}/g' .env.test'''
sh 'php8.0 /usr/local/bin/composer dump-env test'
sh 'php8.0 bin/console doctrine:schema:update --force';
}
}
}
}
}
stage('Test') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'ng lint'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'php8.0 vendor/bin/phpunit'
}
}
}
}
}
stage('Configure') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'echo skip'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh '''APP_SECRET=`php8.0 -r "echo md5(\\"branch=${BRANCH_NAME};\\");"` && sed -i 's/APP_SECRET=.*/APP_SECRET=${APP_SECRET}/g' .env'''
sh '''sed -i 's/DATABASE_URL=.*/DATABASE_URL=${DatabaseUrl}/g' .env.dev'''
sh 'php8.0 /usr/local/bin/composer dump-env dev'
}
}
}
}
}
stage('Build') {
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'ng b -c production'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'echo skip'
}
}
}
}
}
stage('Deploy') {
when {
anyOf {
branch 'master'
branch 'staging'
branch 'beta'
branch 'dev'
}
}
parallel {
stage('Frontend') {
steps {
dir(path: 'src/frontend') {
sh 'ssh web@fufle.net touch ${ProjectPath}/frontend/REMOVEME'
sh 'ssh web@fufle.net rm -rf ${ProjectPath}/frontend/*'
sh 'scp -r dist/* web@fufle.net:${ProjectPath}/frontend/'
}
}
}
stage('Backend') {
steps {
dir(path: 'src/backend') {
sh 'ssh web@fufle.net touch ${ProjectPath}/backend/REMOVEME'
sh 'ssh web@fufle.net rm -rf ${ProjectPath}/backend/*'
sh 'scp -r ./* web@fufle.net:${ProjectPath}/backend/'
}
}
}
}
}
}
}

View File

@ -12,7 +12,7 @@ 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)
[Technical documentation](./src/#c#technical-documentation)
# Polski
## Czym jest CureNet?
@ -28,5 +28,5 @@ Zachęcamy także lekarzy i specjalistów, aby dołączyli do społeczności, b
### Analiza chorób
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)
[Dokumentacja techniczna](./src/#c#technical-documentation)

View File

@ -43,7 +43,7 @@ VOLUME /web/backend/vendor
VOLUME /web/backend/var
VOLUME /web/frontend/node_modules
EXPOSE 7700 7780 7781 49153
EXPOSE 7700 7780 7781
STOPSIGNAL SIGQUIT

View File

@ -4,22 +4,19 @@ services:
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
- ../src/frontend:/web/frontend:cached
- ./logs:/var/log
- vendor:/web/backend/vendor
- var:/web/backend/var
- nodemodules:/web/frontend/node_modules
volumes:
nodemodules:
vendor:
var:
nodemodules:

View File

@ -1 +0,0 @@
docker.exe compose -p curenet exec app bash -c 'cd /web && bash'

View File

@ -1,14 +1,5 @@
#!/bin/bash
echo "> Configure cache"
mkdir -p /tmp/app/vendor
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
@ -45,14 +36,10 @@ echo "> Configure symfony"
cd /web/backend
composer install
php bin/console doctrine:schema:update --force
php php bin/console lexik:jwt:generate-keypair --skip-if-exists
echo "> Configure angular"
cd /web/frontend
time su developer -c 'npm ci'
npm ci
echo "> Starting angular"
su developer -c 'ng serve --disable-host-check --host 0.0.0.0 --poll'
echo "> Waiting"
sleep Infinity
ng serve

View File

@ -30,9 +30,3 @@ APP_SECRET=0fc8d6b67b9f1100b3eb3e3c80d36fda
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"
###< doctrine/doctrine-bundle ###
###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=8270442e040a48fd42967bf1690d5dba
###< lexik/jwt-authentication-bundle ###

View File

@ -4,4 +4,3 @@ APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"

View File

@ -18,7 +18,3 @@
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###
###> lexik/jwt-authentication-bundle ###
/config/jwt/*.pem
###< lexik/jwt-authentication-bundle ###

View File

@ -12,10 +12,8 @@
"doctrine/doctrine-bundle": "^2.4",
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/orm": "^2.9",
"lexik/jwt-authentication-bundle": "^2.12",
"phpdocumentor/reflection-docblock": "^5.2",
"sensio/framework-extra-bundle": "^6.1",
"symfony-bundles/json-request-bundle": "^4.0",
"symfony/asset": "5.3.*",
"symfony/console": "5.3.*",
"symfony/dotenv": "5.3.*",

20335
src/backend/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,4 @@ return [
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
SymfonyBundles\JsonRequestBundle\JsonRequestBundle::class => ['all' => true],
];

View File

@ -1,4 +0,0 @@
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'

View File

@ -13,29 +13,17 @@ security:
app_user_provider:
entity:
class: App\Entity\User
property: email
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:
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
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
@ -47,6 +35,3 @@ security:
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
# - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }

View File

@ -1,3 +0,0 @@
cd ../../docker
docker.exe compose -p curenet exec app bash -c 'cd /web/backend && bash'
cd src/backend

View File

@ -1,66 +0,0 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\Traits\JsonResponseTrait;
use App\Repository\UserRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface;
class AuthController extends AbstractController
{
use JsonResponseTrait;
public function __construct(private UserRepository $userRepository) {
}
#[Route('/api/register', methods: ["POST"], name: 'register')]
public function register(Request $request, UserPasswordHasherInterface $encoder)
{
$em = $this->getDoctrine()->getManager();
$name = $request->get('name');
$surname = $request->get('surname');
$password = $request->get('password');
$email = $request->get('email');
if (empty($name) || empty($password) || empty($email)){
return $this->notAcceptable("Invalid Username or Password or Email");
}
$existing = $this->userRepository->findByEmail($email);
if ($existing) {
return $this->notAcceptable("User email exists");
}
$user = new User();
$user->setPassword($encoder->hashPassword($user, $password));
$user->setEmail($email);
$user->setName($name);
$user->setSurname($surname);
$em->persist($user);
$em->flush();
return $this->created($user);
}
#[Route('/api/login', methods: ['POST'], name: 'login')]
public function login(JWTTokenManagerInterface $JWTManager)
{
$user = new User();
return $this->ok(['token' => $JWTManager->create($user)]);
}
#[Route('/api/user-check', methods: ['POST'])]
public function checkUser() {
$user = $this->getUser();
if (!$user) {
return $this->unauthorized([]);
}
$user->generateAvatar();
return $this->ok($user);
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Entity\Abstraction;
use ReflectionClass;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
class BaseEntity {
protected array $hidden = [];
protected array $map = [];
private array $systemParams = ['hidden', 'map', 'systemParams'];
public function toArray() {
$output = [];
$normalizers = [new ObjectNormalizer()];
$serializer = new Serializer($normalizers);
$array = $serializer->normalize($this, null);
$hidden = array_merge($this->hidden, $this->systemParams);
foreach ($array as $key => $value) {
if (in_array($key, $hidden)) {
continue;
}
if (isset($this->map[$key])) {
$key = $this->map[$key];
}
$output[$key] = $value;
}
return $output;
}
}

View File

@ -2,21 +2,17 @@
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\UserRepository;
use App\Entity\Abstraction\BaseEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface;
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 extends BaseEntity implements UserInterface, PasswordAuthenticatedUserInterface, JWTUserInterface
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
protected array $hidden = ['password', 'salt', 'userIdentifier', 'username'];
/**
* @ORM\Id
* @ORM\GeneratedValue
@ -33,44 +29,38 @@ class User extends BaseEntity implements UserInterface, PasswordAuthenticatedUse
* @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 $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 static function createFromPayload($username, array $payload)
{
$user = new User();
return $user;
}
public function getId(): ?string
{
return $this->id;
@ -90,7 +80,7 @@ class User extends BaseEntity implements UserInterface, PasswordAuthenticatedUse
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
return (string) $this->id;
}
/**
@ -98,7 +88,7 @@ class User extends BaseEntity implements UserInterface, PasswordAuthenticatedUse
*/
public function getUsername(): string
{
return (string) $this->email;
return (string) $this->id;
}
/**
@ -153,83 +143,77 @@ class User extends BaseEntity implements UserInterface, PasswordAuthenticatedUse
{
// 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;
}
public function generateAvatar() {
if (!$this->avatar) {
$this->avatar = 'https://www.gravatar.com/avatar/' . md5($this->email) . '?s=514&d=robohash';
}
}
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;
}
}

View File

@ -36,20 +36,6 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
$this->_em->flush();
}
/**
* @return User[] Returns an array of User objects
*/
public function findByEmail($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.email = :val')
->setParameter('val', $value)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult()
;
}
// /**
// * @return User[] Returns an array of User objects
// */

View File

@ -1,35 +0,0 @@
<?php
namespace App\Traits;
use App\Entity\Abstraction\BaseEntity;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
trait JsonResponseTrait {
protected function ok($data, $code = Response::HTTP_OK, $headers = []) {
return $this->response($data, $code, $headers);
}
protected function created($data, $code = Response::HTTP_CREATED, $headers = []) {
return $this->response($data, $code, $headers);
}
protected function notAcceptable($data, $code = Response::HTTP_NOT_ACCEPTABLE, $headers = []) {
return $this->response([ 'error' => 'Not acceptable', 'data' => $data], $code, $headers);
}
protected function unauthorized($data, $code = Response::HTTP_UNAUTHORIZED, $headers = []) {
return $this->response([ 'error' => 'Unauthorized', 'data' => $data], $code, $headers);
}
protected function response($data, $code = Response::HTTP_OK, $headers = []) {
if ($data instanceof BaseEntity) {
$data = $data->toArray();
}
return new JsonResponse($data, $code, $headers);
}
}

View File

@ -91,33 +91,12 @@
"laminas/laminas-code": {
"version": "4.4.2"
},
"lcobucci/clock": {
"version": "2.0.0"
},
"lcobucci/jwt": {
"version": "4.1.4"
},
"lexik/jwt-authentication-bundle": {
"version": "2.5",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.5",
"ref": "5b2157bcd5778166a5696e42f552ad36529a07a6"
},
"files": [
"config/packages/lexik_jwt_authentication.yaml"
]
},
"monolog/monolog": {
"version": "2.3.1"
},
"myclabs/deep-copy": {
"version": "1.10.2"
},
"namshi/jose": {
"version": "7.2.3"
},
"nikic/php-parser": {
"version": "v4.12.0"
},
@ -243,9 +222,6 @@
"config/packages/sensio_framework_extra.yaml"
]
},
"symfony-bundles/json-request-bundle": {
"version": "4.0.5"
},
"symfony/asset": {
"version": "v5.3.2"
},
@ -466,9 +442,6 @@
"symfony/polyfill-mbstring": {
"version": "v1.23.0"
},
"symfony/polyfill-php56": {
"version": "v1.20.0"
},
"symfony/polyfill-php73": {
"version": "v1.23.0"
},

View File

@ -48,17 +48,7 @@
"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
}
"src/styles.scss"
],
"scripts": [],
"vendorChunk": true,

View File

@ -291,23 +291,6 @@
"tslib": "^2.2.0"
}
},
"@angular/cdk": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.1.3.tgz",
"integrity": "sha512-uCWHk/PjddNJsdrmexasphWGbf4kYtYyhUCSd4HEBrIDjYz166MTVSr3FHgn/s8/tlVou7uTnaEZM+ILWoe2iQ==",
"requires": {
"parse5": "^5.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"optional": true
}
}
},
"@angular/cli": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.1.3.tgz",
@ -508,14 +491,6 @@
"tslib": "^2.2.0"
}
},
"@angular/material": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-12.1.3.tgz",
"integrity": "sha512-ekkg3MDEN133NhcE0du7a69lFAIFyvK6Va+YRTxYQE0BCNVMTWXtGbOXZLovDMjgo1xdXwrEJrMRBeYo+FIttA==",
"requires": {
"tslib": "^2.2.0"
}
},
"@angular/platform-browser": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.1.3.tgz",
@ -1715,14 +1690,6 @@
"schema-utils": "^2.7.0"
}
},
"@ng-stack/forms": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@ng-stack/forms/-/forms-2.4.0.tgz",
"integrity": "sha512-Czo4ZbzEcTSAQ8yS4d0PzYMeB8GidOz1ABZhUjwnZiLaNOY2gnIdpQEIqE+GjIVOyusGEjQ/psLneBj8gE2EjQ==",
"requires": {
"tslib": "^2.0.0"
}
},
"@ngtools/webpack": {
"version": "12.1.3",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.1.3.tgz",
@ -1732,22 +1699,6 @@
"enhanced-resolve": "5.8.2"
}
},
"@ngx-translate/core": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-13.0.0.tgz",
"integrity": "sha512-+tzEp8wlqEnw0Gc7jtVRAJ6RteUjXw6JJR4O65KlnxOmJrCGPI0xjV/lKRnQeU0w4i96PQs/jtpL921Wrb7PWg==",
"requires": {
"tslib": "^2.0.0"
}
},
"@ngx-translate/http-loader": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-6.0.0.tgz",
"integrity": "sha512-LCekn6qCbeXWlhESCxU1rAbZz33WzDG0lI7Ig0pYC1o5YxJWrkU9y3Y4tNi+jakQ7R6YhTR2D3ox6APxDtA0wA==",
"requires": {
"tslib": "^2.0.0"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -2774,16 +2725,6 @@
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@ -2878,11 +2819,6 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true
},
"bootstrap": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.2.tgz",
"integrity": "sha512-1Ge963tyEQWJJ+8qtXFU6wgmAVj9gweEjibUdbmcCEYsn38tVwRk8107rk2vzt6cfQcRr3SlZ8aQBqaD8aqf+Q=="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -5042,13 +4978,6 @@
"escape-string-regexp": "^1.0.5"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -5146,11 +5075,6 @@
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
"dev": true
},
"font-awesome": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
"integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@ -7506,13 +7430,6 @@
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
"dev": true
},
"nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"dev": true,
"optional": true
},
"nanoid": {
"version": "3.1.23",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz",
@ -12682,11 +12599,7 @@
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"dev": true,
"optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
"optional": true
},
"glob-parent": {
"version": "3.1.0",

View File

@ -12,20 +12,13 @@
"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",
"@ng-stack/forms": "^2.4.0",
"@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"
@ -34,9 +27,9 @@
"@angular-devkit/build-angular": "~12.1.3",
"@angular/cli": "~12.1.3",
"@angular/compiler-cli": "~12.1.3",
"@types/node": "^12.11.1",
"@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",

View File

@ -1,3 +0,0 @@
cd ../../docker
docker.exe compose -p curenet exec app bash -c 'cd /web/frontend && bash'
cd ../src/frontend

View File

@ -1,134 +1,10 @@
import { NgModule } from '@angular/core';
import { Route, RouterModule } from '@angular/router';
import { AuthGuard } from './modules/auth/services/auth/auth.guard';
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: 'auth',
loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule),
},
{
path: '',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.HOME',
icon: 'fa fa-home',
matchExact: true,
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'profile',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.PROFILE',
icon: 'fa fa-user',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'community',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.COMMUNITY',
icon: 'fa fa-users',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'questions',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.QUESTIONS',
icon: 'fa fa-question',
level: 1,
}
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'messages',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.MESSAGES',
icon: 'fa fa-comments',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'tests',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.TESTS',
icon: 'fa fa-heartbeat',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
path: 'research',
component: NotFoundComponent,
menuEntries: [
{
label: 'MENU.RESEARCH',
icon: 'fa fa-flask',
},
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
{
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,
}
],
canActivate: [ AuthGuard, ],
canActivateChild: [ AuthGuard, ],
},
];
@NgModule({
imports: [RouterModule.forRoot(appRoutes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -1,79 +1,2 @@
<ng-template #toolbar>
<mat-toolbar class="toolbar-background">
<button mat-icon-button (click)="toggleSidebar()" *ngIf="!isSidebarHidden">
<i class="fa fa-bars"></i>
</button>
<span>{{ 'APP.NAME' | translate }}</span>
<span class="flex-grow-1"></span>
<div>
<div *ngFor="let component of dynamicToolbarComponents">
<ng-container [ngComponentOutlet]="component"></ng-container>
</div>
</div>
</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)" *ngIf="!isSidebarHidden">
<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" *ngIf="user">
<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>
<button mat-menu-item (click)="logout()">
Wyloguj
</button>
</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>
<router-outlet></router-outlet>
It's works, you can start coding :)

View File

@ -1,91 +0,0 @@
$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;
}
}

View File

@ -1,162 +1,10 @@
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';
import { AuthService } from './modules/auth/services/auth/auth.service';
import { Router } from '@angular/router';
import { UserModel } from './modules/auth/models/user.model';
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();
appRoutes = appRoutes;
user: UserModel;
isSidebarHidden = false;
dynamicToolbarComponents = [];
get isSidebarOpen(): boolean {
switch (this.sidebarOpen) {
case SidebarOpenEnum.Open:
return true;
case SidebarOpenEnum.Closed:
return false;
default:
return this.defaultSidebarOpen;
}
}
constructor(
private appService: AppService,
private themeService: ThemeService,
private browserStorageService: BrowserStorageService,
private sanitizer: DomSanitizer,
private authService: AuthService,
private router: Router,
) {
this.configureSidebarEvents();
this.configureThemeEvents();
this.configureLanguageEvents();
this.configureResizeEvents();
this.configureUserEvents();
}
configureUserEvents(): void {
this.user = this.authService.getUser();
this.authService.userChange.subscribe(user => {
this.user = user;
});
}
configureSidebarEvents(): void {
this.isSidebarHidden = this.appService.getSidebarHidden();
this.appService.sidebarHiddenChange.subscribe(hidden => {
this.isSidebarHidden = hidden;
});
const sidebarUserPrefference = this.browserStorageService.getItem('sidebar.open');
if (sidebarUserPrefference !== null) {
this.sidebarOpen = sidebarUserPrefference;
}
this.appService.dynamicToolbarComponentsChange.subscribe(components => {
this.dynamicToolbarComponents = components;
});
}
configureThemeEvents(): void {
this.themes = this.themeService.getThemes();
this.themeControl.valueChanges.subscribe(theme => {
this.themeService.setTheme(theme);
});
this.onThemeChanged(this.themeService.getTheme());
this.themeService.themeChange.subscribe(theme => {
this.onThemeChanged(theme);
});
}
configureLanguageEvents(): void {
this.langs = this.appService.getLangs();
this.lang = this.appService.getLang();
this.appService.changeLang(this.lang);
this.langControl.valueChanges.subscribe(lang => {
this.appService.changeLang(lang);
});
this.onLangChanged(this.appService.getLang());
this.appService.langChange.subscribe(lang => {
this.onLangChanged(lang);
});
}
configureResizeEvents(): void {
this.onResize(this.appService.size);
this.appService.sizeChange.subscribe(size => {
this.onResize(size);
});
}
onThemeChanged(theme: string): void {
if (theme === undefined) {
return;
}
this.theme = theme;
this.themeControl.setValue(theme);
}
onResize(size: WindowSize): void {
if (size === undefined) {
return;
}
this.isMobile = size.isMobile;
this.defaultSidebarOpen = !size.isMobile;
this.sidebarMode = this.isMobile ? 'over' : 'side';
}
onLangChanged(lang: string): void {
if (lang === undefined) {
return;
}
this.lang = lang;
this.langControl.setValue(lang);
}
toggleSidebar(): void {
this.sidebarOpen = this.isSidebarOpen ? SidebarOpenEnum.Closed : SidebarOpenEnum.Open;
this.browserStorageService.setItem('sidebar.open', this.sidebarOpen);
}
onSidebarOpenedChange(opened: boolean): void {
this.sidebarOpen = opened ? SidebarOpenEnum.Open : SidebarOpenEnum.Closed;
}
url(url: string): SafeStyle {
return this.sanitizer.bypassSecurityTrustStyle(`url('${url}')`);
}
logout(): void {
this.authService.logout();
this.router.navigateByUrl('/');
}
}
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'CureNet';
}

View File

@ -1,53 +1,18 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } 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';
import { TokenInterceptor } from './modules/shared/interceptors/token/token.interceptor';
import { AuthTokenService } from './modules/shared/services/auth/auth-token.service';
import { AuthService } from './modules/auth/services/auth/auth.service';
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
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,
AuthTokenService,
AuthService,
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Route } from '@angular/router';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
import { UsersComponent } from './components/users/users.component';
const routes: Route[] = [
{
path: '',
component: NotFoundComponent,
},
{
path: 'users',
component: UsersComponent,
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule { }

View File

@ -1,23 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
import { SharedModule } from '../shared/shared.module';
import { UsersComponent } from './components/users/users.component';
import { AdminUserService } from './services/admin-user/admin-user.service';
@NgModule({
declarations: [
UsersComponent
],
imports: [
CommonModule,
AdminRoutingModule,
SharedModule,
],
providers: [
AdminUserService,
],
})
export class AdminModule { }

View File

@ -1 +0,0 @@
<p>users works!</p>

View File

@ -1,18 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { AdminUserService } from '../../services/admin-user/admin-user.service';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
})
export class UsersComponent implements OnInit {
constructor(private adminUserService: AdminUserService) { }
ngOnInit(): void {
this.adminUserService.getUsers().subscribe(() => {
});
}
}

View File

@ -1,14 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { UserModel } from 'src/app/modules/auth/models/user.model';
@Injectable()
export class AdminUserService {
constructor(private http: HttpClient) { }
getUsers(): Observable<UserModel[]> {
return this.http.get<UserModel[]>('/api/admin/users');
}
}

View File

@ -1,31 +0,0 @@
import { NgModule } from '@angular/core';
import { RouterModule, Route } from '@angular/router';
import { AuthComponent } from './components/auth/auth.component';
import { AuthTabEnum } from './enums/auth-tab.enum';
const routes: Route[] = [
{
path: AuthTabEnum.Login,
component: AuthComponent,
},
{
path: AuthTabEnum.Register,
component: AuthComponent,
},
{
path: AuthTabEnum.RestorePassword,
component: AuthComponent,
},
{
path: '**',
redirectTo: 'login',
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthRoutingModule { }

View File

@ -1,29 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthRoutingModule } from './auth-routing.module';
import { AuthComponent } from './components/auth/auth.component';
import { ThemeSwitcherComponent } from './components/theme-switcher/theme-switcher.component';
import { MaterialModule } from '../material/material.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AuthService } from './services/auth/auth.service';
import { NgStackFormsModule } from '@ng-stack/forms';
@NgModule({
declarations: [
AuthComponent,
ThemeSwitcherComponent,
],
imports: [
CommonModule,
AuthRoutingModule,
MaterialModule,
FormsModule,
ReactiveFormsModule,
NgStackFormsModule,
],
providers: [
AuthService,
],
})
export class AuthModule { }

View File

@ -1,66 +0,0 @@
<div class="primary-background toolbar-background"></div>
<div class="col-11 col-lg-7 col-xl-5 p-3 content">
<mat-card class="px-0">
<mat-tab-group mat-align-tabs="center" [selectedIndex]="selectedIndex" (selectedIndexChange)="selectedIndexChange($event)">
<mat-tab label="Logowanie">
<div [formGroup]="loginForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Email</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Hasło</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Hasło</mat-label>
<input formControlName="password" type="password" matInput>
</mat-form-field>
<div class="text-end">
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.RestorePassword))">Odzyskaj hasło</button>
<button mat-flat-button color="primary" (click)="login()">Zaloguj</button>
</div>
</div>
</mat-tab>
<mat-tab label="Rejestracja">
<div [formGroup]="registerForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Imię</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Imię</mat-label>
<input formControlName="name" matInput>
</mat-form-field>
<div class="col-12 col-md-4">Nazwisko</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Nazwisko</mat-label>
<input formControlName="surname" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Email</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="col-12 col-md-4" required>Hasło</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Hasło</mat-label>
<input formControlName="password" type="password" matInput>
</mat-form-field>
<div class="text-end">
<button mat-flat-button color="primary" (click)="register()">Zarejestruj</button>
</div>
</div>
</mat-tab>
<mat-tab label="Odzyskiwanie hasła" *ngIf="selectedIndex === 2">
<div [formGroup]="restoreForm" class="row align-items-center p-4 m-0">
<div class="col-12 col-md-4" required>Email</div>
<mat-form-field class="col-12 col-md-8">
<mat-label>Adres email</mat-label>
<input formControlName="email" matInput>
</mat-form-field>
<div class="text-end">
<button mat-button (click)="selectedIndexChange(getSelectedIndex(AuthTabEnum.Login))">Anuluj</button>
<button mat-flat-button color="primary">Odzyskaj hasło</button>
</div>
</div>
</mat-tab>
</mat-tab-group>
</mat-card>
</div>

View File

@ -1,9 +0,0 @@
.primary-background{
background-color: var(--toolbar-background);
height: 200px;
}
.content{
margin: 0px auto;
margin-top: -150px;
height: 200px;
}

View File

@ -1,118 +0,0 @@
import { Location } from '@angular/common';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router, UrlSegment } from '@angular/router';
import { FormBuilder, FormGroup } from '@ng-stack/forms';
import { AppService } from 'src/app/modules/shared/services/app/app.service';
import { AuthTabEnum } from '../../enums/auth-tab.enum';
import { LoginModel } from '../../models/login.model';
import { RegisterModel } from '../../models/register.model';
import { RestoreModel } from '../../models/restore.model';
import { AuthService } from '../../services/auth/auth.service';
import { ThemeSwitcherComponent } from '../theme-switcher/theme-switcher.component';
@Component({
selector: 'app-auth',
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss']
})
export class AuthComponent implements OnInit, OnDestroy {
registerForm: FormGroup<RegisterModel>;
loginForm: FormGroup<LoginModel>;
restoreForm: FormGroup<RestoreModel>;
tab: AuthTabEnum;
selectedIndex: number;
defaultTab = AuthTabEnum.Login;
tabIndexes = [
AuthTabEnum.Login,
AuthTabEnum.Register,
AuthTabEnum.RestorePassword,
];
showRestore = false;
AuthTabEnum = AuthTabEnum;
constructor(
private appService: AppService,
private authService: AuthService,
private activatedRoute: ActivatedRoute,
private router: Router,
private location: Location,
formBuilder: FormBuilder,
) {
this.registerForm = formBuilder.group<RegisterModel>({
name: 'Name',
surname: 'Surname',
email: 'email@test.com',
password: 'password',
});
this.loginForm = formBuilder.group<LoginModel>({
email: 'email@test.com',
password: 'password',
});
this.restoreForm = formBuilder.group<RestoreModel>({
email: 'email@test.com',
});
this.handleUrl(activatedRoute.snapshot.url);
activatedRoute.url.subscribe(url => {
this.handleUrl(url);
});
}
handleUrl(url: UrlSegment[]): void {
const path = url[0].path;
this.tab = path as AuthTabEnum;
this.selectedIndex = this.getSelectedIndex();
}
getSelectedIndex(tab: AuthTabEnum = null): number {
if (tab === null) {
tab = this.tab;
}
let index = this.tabIndexes.findIndex(i => i === tab);
if (index === -1) {
index = this.tabIndexes.findIndex(i => i === this.defaultTab);
}
return index;
}
getIndexTab(): AuthTabEnum {
return this.tabIndexes[this.selectedIndex];
}
selectedIndexChange(index: number): void {
this.selectedIndex = index;
const tab = this.getIndexTab();
const url = this.router.createUrlTree(['..', tab], {
relativeTo: this.activatedRoute,
}).toString();
this.location.go(url);
}
ngOnInit(): void {
this.appService.setSidebarHidden(true);
this.appService.addDynamicToolbarComponent(ThemeSwitcherComponent);
}
ngOnDestroy(): void {
this.appService.setSidebarHidden(false);
this.appService.removeDynamicToolbarComponent(ThemeSwitcherComponent);
}
register(): void {
this.authService.createAccount(this.registerForm.getRawValue()).subscribe(data => {
this.loginForm.patchValue({
email: this.registerForm.controls.email.value,
password: this.registerForm.controls.password.value,
});
this.selectedIndexChange(this.getSelectedIndex(AuthTabEnum.Login));
});
}
login(): void {
this.authService.login(this.loginForm.getRawValue()).subscribe(data => {
const afterUrl = '/';
this.router.navigateByUrl(afterUrl);
});
}
}

View File

@ -1,3 +0,0 @@
<button mat-icon-button (click)="toggleTheme()">
<i class="fa" [class.fa-sun-o]="isDark" [class.fa-moon-o]="!isDark"></i>
</button>

View File

@ -1,32 +0,0 @@
import { Component, OnInit } from '@angular/core';
import { ThemeService } from 'src/app/modules/shared/services/theme/theme.service';
@Component({
selector: 'app-theme-switcher',
templateUrl: './theme-switcher.component.html',
styleUrls: ['./theme-switcher.component.scss']
})
export class ThemeSwitcherComponent implements OnInit {
isDark = false;
darkTheme = 'dark';
lightTheme = 'light';
theme = 'light';
constructor(
private themeService: ThemeService,
) {
this.theme = themeService.getTheme();
themeService.themeChange.subscribe(theme => {
this.theme = theme;
});
}
ngOnInit(): void {
}
toggleTheme(): void {
this.themeService.setTheme(this.theme === this.darkTheme ? this.lightTheme : this.darkTheme);
this.isDark = this.theme === this.darkTheme;
}
}

View File

@ -1,5 +0,0 @@
export enum AuthTabEnum {
Login = 'login',
Register = 'register',
RestorePassword = 'restore-password',
}

View File

@ -1,4 +0,0 @@
export interface LoginModel {
email: string;
password: string;
}

View File

@ -1,6 +0,0 @@
export interface RegisterModel {
name: string;
surname: string;
email: string;
password: string;
}

View File

@ -1,3 +0,0 @@
export interface RestoreModel {
email: string;
}

View File

@ -1,3 +0,0 @@
export interface TokenResponse {
token: string;
}

View File

@ -1,8 +0,0 @@
export interface UserModel {
id: number;
name: string;
surname: string;
avatar: string | null;
country: string | null;
state: string | null;
}

View File

@ -1,34 +0,0 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthTokenService } from 'src/app/modules/shared/services/auth/auth-token.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanActivateChild {
constructor(private authTokenService: AuthTokenService, private router: Router) {
}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
const can = this.authTokenService.userValidate();
if (!can){
this.router.navigate(['/auth']);
}
return can;
}
canActivateChild(
childRoute: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
const can = this.authTokenService.userValidate();
if (!can){
this.router.navigate(['/auth']);
}
return can;
}
}

View File

@ -1,68 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { LoginModel } from '../../models/login.model';
import { RegisterModel } from '../../models/register.model';
import { TokenResponse } from '../../models/token.response';
import { UserModel } from '../../models/user.model';
import { AuthTokenService } from '../../../shared/services/auth/auth-token.service';
import { Router } from '@angular/router';
@Injectable()
export class AuthService {
private static updateAgent: any;
private user: UserModel;
public userChange = new Subject<UserModel>();
constructor(
private http: HttpClient,
private authTokenService: AuthTokenService,
private router: Router,
) {
this.checkUser();
this.userChange.subscribe(user => {
this.user = user;
});
}
getUser(): UserModel {
return this.user;
}
checkUser(): void {
this.http.post<UserModel>('/api/user-check', {}).subscribe(user => {
this.userChange.next(user);
if (AuthService.updateAgent) {
clearTimeout(AuthService.updateAgent);
}
AuthService.updateAgent = setTimeout(() => {
this.checkUser();
}, 2000) as any;
}, () => {
this.logout();
});
}
createAccount(data: RegisterModel): Observable<UserModel> {
return this.http.post<UserModel>('/api/register', data);
}
login(data: LoginModel): Observable<TokenResponse> {
const out = new Subject<TokenResponse>();
this.http.post<TokenResponse>('/api/login', {
username: data.email,
password: data.password,
}).subscribe(response => {
this.authTokenService.setToken(response.token);
this.checkUser();
out.next(response);
});
return out;
}
logout(): void {
this.authTokenService.removeToken();
this.router.navigate(['/auth']);
}
}

View File

@ -1,33 +0,0 @@
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';
import {MatCardModule} from '@angular/material/card';
import {MatTabsModule} from '@angular/material/tabs';
import {MatInputModule} from '@angular/material/input';
const itemsToExport = [
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatSelectModule,
MatMenuModule,
MatCardModule,
MatTabsModule,
MatInputModule,
];
@NgModule({
declarations: [],
imports: [
CommonModule,
...itemsToExport,
],
exports: [
...itemsToExport,
]
})
export class MaterialModule { }

View File

@ -1,10 +0,0 @@
<div class="primary-background toolbar-background"></div>
<div class="content align-items-center justify-content-center">
<div class="col-11 col-lg-7 col-xl-5">
<mat-card>
<div class="m-3 p-3">
{{ 'ERROR.PAGE_NOT_FOUND' | translate }}
</div>
</mat-card>
</div>
</div>

View File

@ -1,10 +0,0 @@
.primary-background{
background-color: var(--toolbar-background);
height: 100px;
}
.content{
margin: 0px auto;
margin-top: -100px;
height: 200px;
display: flex;
}

View File

@ -1,15 +0,0 @@
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 {
}
}

View File

@ -1,20 +0,0 @@
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { AuthTokenService } from 'src/app/modules/shared/services/auth/auth-token.service';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private authTokenService: AuthTokenService) {
}
intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authTokenService.hasToken()) {
const Authorization = `Bearer ${this.authTokenService.getToken()}`;
return next.handle(httpRequest.clone({ setHeaders: { Authorization } }));
}
return next.handle(httpRequest);
}
}

View File

@ -1,9 +0,0 @@
export class User {
id: number;
email: string;
name: string;
surname: string;
avatar: string;
country: string;
state: string;
}

View File

@ -1,110 +0,0 @@
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;
}
static generate(): WindowSize {
return new WindowSize(window.innerWidth, window.innerHeight);
}
update(): WindowSize {
this.width = window.innerWidth;
this.height = window.innerHeight;
return this;
}
}
@Injectable()
export class AppService {
private sidebarHidden = false;
private lang = 'en';
private defaultLang = 'en';
private dynamicToolbarComponents = [];
langChange = new Subject<string>();
size: WindowSize;
sizeChange = new Subject<WindowSize>();
sidebarHiddenChange = new Subject<boolean>();
dynamicToolbarComponentsChange = new Subject<any[]>();
constructor(
private browserStorageService: BrowserStorageService,
private translate: TranslateService,
) {
this.configureResizeEvents();
this.configureLanguageEvents();
this.configureSidebarEvents();
}
configureSidebarEvents(): void {
this.sidebarHiddenChange.subscribe(hidden => {
this.sidebarHidden = hidden;
});
}
configureResizeEvents(): void {
this.sizeChange.subscribe(size => this.size = size);
this.sizeChange.next(WindowSize.generate());
fromEvent(window, 'resize').subscribe(e => {
this.sizeChange.next(WindowSize.generate());
});
}
configureLanguageEvents(): void {
this.langChange.subscribe(lang => {
this.lang = lang;
this.browserStorageService.setItem('language', lang);
});
let language = this.browserStorageService.getItem('language');
if (!language) {
language = this.defaultLang;
}
this.translate.setDefaultLang(this.defaultLang);
this.changeLang(language);
}
addDynamicToolbarComponent(component): void {
this.dynamicToolbarComponents.push(component);
this.dynamicToolbarComponentsChange.next(this.dynamicToolbarComponents);
}
removeDynamicToolbarComponent(component): void {
this.dynamicToolbarComponents = this.dynamicToolbarComponents.filter(c => c !== component);
this.dynamicToolbarComponentsChange.next(this.dynamicToolbarComponents);
}
setSidebarHidden(hidden: boolean): void {
this.sidebarHiddenChange.next(hidden);
}
getSidebarHidden(): boolean {
return this.sidebarHidden;
}
changeLang(lang: string): void {
if (lang !== this.lang) {
this.translate.use(lang);
this.langChange.next(lang);
}
}
getLang(): string {
return this.lang;
}
getLangs(): string[] {
return [
'pl',
'en',
];
}
}

View File

@ -1,34 +0,0 @@
import { Injectable } from '@angular/core';
import { BrowserStorageService } from 'src/app/modules/shared/services/browser-storage/browser-storage.service';
@Injectable()
export class AuthTokenService {
private TokenKey = 'jwt.token';
constructor( private browserStorageService: BrowserStorageService) {
}
setToken(token: string): void {
this.browserStorageService.setItem(this.TokenKey, token);
}
hasToken(): boolean {
const token = this.browserStorageService.getItemOrDefault(this.TokenKey, null);
return token !== null;
}
getToken(): string {
return this.browserStorageService.getItem(this.TokenKey);
}
removeToken(): void {
this.browserStorageService.setItem(this.TokenKey, null);
}
userValidate(): boolean {
if (!this.hasToken()) {
return false;
}
return true;
}
}

View File

@ -1,38 +0,0 @@
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): void {
this.namespace = namespace;
}
getNamespace(namespace: string | null = null): string {
return namespace ?? this.namespace;
}
setItem(key: string, value: any, namespace: string | null = null): void {
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;
}
}

View File

@ -1,41 +0,0 @@
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(): string {
return this.theme;
}
setTheme(theme: string): void {
if (theme !== this.theme) {
this.themeChange.next(theme);
document.getElementById('current-theme').setAttribute('href', `/${theme}.css`);
}
}
getThemes(): string[] {
return [
'light',
'dark',
];
}
}

View File

@ -1,26 +0,0 @@
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';
import { MaterialModule } from '../material/material.module';
@NgModule({
declarations: [
NotFoundComponent
],
imports: [
CommonModule,
MaterialModule,
TranslateModule.forRoot({
defaultLanguage: 'en',
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
}
}),
],
})
export class SharedModule { }

View File

@ -1,38 +0,0 @@
@use 'sass:map';
$primary-color: map.get($theme, color, primary, 500);
$toolbar-background: map.get($theme, color, background, app-bar);
$toolbar-color: map.get($theme, color, primary, contrast, 500) !default;
$main-background: map.get($theme, color, background, background) !default;
$warn-color: map.get($theme, warn, 500) !default;
body {
--primary-color: #{$primary-color};
--toolbar-background: #{$toolbar-background};
--toolbar-color: #{$toolbar-color};
--main-background: #{$main-background};
--warn-color: #{$warn-color};
.mat-toolbar{
background-color: var(--toolbar-background);
color: var(--toolbar-color);
}
.toolbar-background{
position: relative;
& > *{
position: relative;
z-index: 2;
}
&:before{
position: absolute;
z-index: 0;
top: 0px;
left: 0px;
width:100%;
height: 100%;
content: "";
opacity: 0.125;
background-image: url('/assets/background/items.png');
background-position: top;
background-attachment: fixed;
}
}
}

View File

@ -1,398 +0,0 @@
$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,
),
);

View File

@ -1,35 +0,0 @@
// 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.$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";

View File

@ -1,40 +0,0 @@
@use "sass:map";
// 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.$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,
)
));
$toolbar-background: map.get($theme, color, primary, 500);
$toolbar-text: map.get($theme, color, primary, contrast, 500);
$theme: map.set($theme, color, background, app-bar, $toolbar-background);
$theme: map.set($theme, color, foreground, app-bar, $toolbar-text);
// 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";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1,30 +0,0 @@
{
"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",
"GROUP": "My group",
"MESSAGES": "Messages",
"QUESTIONS": "Q&A",
"TESTS": "Laboratory tests",
"RESEARCH": "Research",
"ADMIN": "Admin",
"USERS": "Users"
}
}

View File

@ -1,30 +0,0 @@
{
"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ść",
"GROUP": "Moja grupa",
"MESSAGES": "Wiadomości",
"QUESTIONS": "Pytania i odpowiedzi",
"TESTS": "Wyniki badań",
"RESEARCH": "Badania naukowe",
"ADMIN": "Panel administratora",
"USERS": "Użytkownicy"
}
}

View File

@ -1,5 +1,3 @@
export const environment = {
production: true,
buildId: -1001,
buildTime: -1002,
production: true
};

View File

@ -3,9 +3,7 @@
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
buildId: -1001,
buildTime: -1002,
production: false
};
/*

View File

@ -1,17 +1,13 @@
<!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>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Frontend</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@ -1,50 +1 @@
/* 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;
}
[required]:after{
content: "*";
font-weight: bold;
color: var(--warn-color);
}

View File

@ -1,5 +0,0 @@
name=value
name2="value 2"
#name3=value3
name4 = value4
name5 = "value 5"

View File

@ -1,80 +0,0 @@
<?php
namespace Tools;
class PutEnv {
private string $file;
private string $key;
private string $value;
private array $content;
public function __construct() {
$argv = $_SERVER['argv'];
$argc = sizeof($argv);
switch($argc) {
case 4:
$this->file = $argv[1];
$this->key = $argv[2];
$this->value = $argv[3];
break;
case 3:
$this->file = $argv[1];
$this->key = $argv[2];
$this->value = fgets(STDIN);
break;
default:
echo "usage:
php8.0 putenv.php filename key value
echo value | php8.0 putenv filename key";
throw new Exception("invalid params");
}
if (!file_exists($this->file)) {
throw new Exception('File not found');
}
$changed = 0;
$lineWasEmpty = false;
$key = $this->key;
$value = $this->value;
if (strpos($value, ' ') !== false) {
$value = '"' . addslashes($value) . '"';
}
$this->content = file($this->file);
foreach ($this->content as $l => $line) {
if (strlen(trim($line)) < 1) {
if ($lineWasEmpty) {
$this->content[$l] = null;
} else {
$lineWasEmpty = true;
}
continue;
}
$lineWasEmpty = false;
if (!str_starts_with($line, $this->key)) {
continue;
}
$next = trim(substr($line, strlen($this->key)));
if (strlen($next) === 0 || $next[0] !== '=') {
continue;
}
$this->content[$l] = $key . '=' . $value;
$changed++;
}
if ($changed === 0) {
$this->content[] = $key . '=' . $value;
} else if ($changed > 1) {
echo "Key $key changed $changed times\n";
}
$content = $this->content;
$this->content = [];
foreach ($content as $line) {
if ($line !== null) {
$this->content[] = $line;
}
}
$content = implode('', $this->content);
file_put_contents($this->file, $content);
}
}
new PutEnv();