Co-authored-by: Sieciech <www.sieciech@gmail.com> Reviewed-on: http://git.fufle.net/Community/CureNet/pulls/28 Co-Authored-By: Sieciech <sieciech@noreply.fufle.net> Co-Committed-By: Sieciech <sieciech@noreply.fufle.net>
This commit is contained in:
		
							parent
							
								
									cbca7cb582
								
							
						
					
					
						commit
						f52e611c67
					
				| 
						 | 
				
			
			@ -19,9 +19,7 @@ services:
 | 
			
		|||
      - ./logs:/var/log
 | 
			
		||||
      - vendor:/web/backend/vendor
 | 
			
		||||
      - var:/web/backend/var
 | 
			
		||||
      - nodemodules:/web/frontend/node_modules
 | 
			
		||||
volumes:
 | 
			
		||||
  nodemodules:
 | 
			
		||||
  vendor:
 | 
			
		||||
  var:
 | 
			
		||||
  nodemodules:
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
echo "> Configure cache"
 | 
			
		||||
    mkdir -p /tmp/app/{vendor,node_modules}
 | 
			
		||||
    ln -s /tmp/app/node_modules /web/frontend/node_modules
 | 
			
		||||
    mkdir -p /tmp/app/vendor
 | 
			
		||||
    ln -s /tmp/app/vendor /web/backend/vendor
 | 
			
		||||
 | 
			
		||||
echo "> Configure user"
 | 
			
		||||
| 
						 | 
				
			
			@ -46,10 +45,14 @@ 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
 | 
			
		||||
    su developer -c 'time npm ci'
 | 
			
		||||
    time su developer -c 'npm ci'
 | 
			
		||||
 | 
			
		||||
echo "> Starting angular"
 | 
			
		||||
    su developer -c 'ng serve --disable-host-check --host 0.0.0.0 --poll'
 | 
			
		||||
 | 
			
		||||
echo "> Waiting"
 | 
			
		||||
    sleep Infinity
 | 
			
		||||
| 
						 | 
				
			
			@ -30,3 +30,9 @@ 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 ###
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,3 +18,7 @@
 | 
			
		|||
/phpunit.xml
 | 
			
		||||
.phpunit.result.cache
 | 
			
		||||
###< phpunit/phpunit ###
 | 
			
		||||
 | 
			
		||||
###> lexik/jwt-authentication-bundle ###
 | 
			
		||||
/config/jwt/*.pem
 | 
			
		||||
###< lexik/jwt-authentication-bundle ###
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,10 @@
 | 
			
		|||
        "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.*",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -12,4 +12,6 @@ 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],
 | 
			
		||||
];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
lexik_jwt_authentication:
 | 
			
		||||
    secret_key: '%env(resolve:JWT_SECRET_KEY)%'
 | 
			
		||||
    public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
 | 
			
		||||
    pass_phrase: '%env(JWT_PASSPHRASE)%'
 | 
			
		||||
| 
						 | 
				
			
			@ -13,17 +13,29 @@ security:
 | 
			
		|||
        app_user_provider:
 | 
			
		||||
            entity:
 | 
			
		||||
                class: App\Entity\User
 | 
			
		||||
                property: id
 | 
			
		||||
                property: email
 | 
			
		||||
        # 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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,3 +47,6 @@ 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 }
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +1,3 @@
 | 
			
		|||
docker.exe compose -p curenet exec app bash -c 'cd /web/backend && bash'
 | 
			
		||||
cd ../../docker
 | 
			
		||||
docker.exe compose -p curenet exec app bash -c 'cd /web/backend && bash'
 | 
			
		||||
cd src/backend
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
<?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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
<?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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,17 +2,21 @@
 | 
			
		|||
 | 
			
		||||
namespace App\Entity;
 | 
			
		||||
 | 
			
		||||
use App\Repository\UserRepository;
 | 
			
		||||
use Doctrine\ORM\Mapping as ORM;
 | 
			
		||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
 | 
			
		||||
use App\Repository\UserRepository;
 | 
			
		||||
use App\Entity\Abstraction\BaseEntity;
 | 
			
		||||
use Symfony\Component\Security\Core\User\UserInterface;
 | 
			
		||||
use Lexik\Bundle\JWTAuthenticationBundle\Security\User\JWTUserInterface;
 | 
			
		||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @ORM\Entity(repositoryClass=UserRepository::class)
 | 
			
		||||
 * @ORM\Table(name="`user`")
 | 
			
		||||
 */
 | 
			
		||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
 | 
			
		||||
class User extends BaseEntity implements UserInterface, PasswordAuthenticatedUserInterface, JWTUserInterface
 | 
			
		||||
{
 | 
			
		||||
    protected array $hidden = ['password', 'salt', 'userIdentifier', 'username'];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @ORM\Id
 | 
			
		||||
     * @ORM\GeneratedValue
 | 
			
		||||
| 
						 | 
				
			
			@ -29,38 +33,44 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
 | 
			
		|||
     * @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;
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +90,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
 | 
			
		|||
     */
 | 
			
		||||
    public function getUserIdentifier(): string
 | 
			
		||||
    {
 | 
			
		||||
        return (string) $this->id;
 | 
			
		||||
        return (string) $this->email;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +98,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
 | 
			
		|||
     */
 | 
			
		||||
    public function getUsername(): string
 | 
			
		||||
    {
 | 
			
		||||
        return (string) $this->id;
 | 
			
		||||
        return (string) $this->email;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -143,77 +153,83 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
 | 
			
		|||
    {
 | 
			
		||||
        // 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 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';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,20 @@ 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
 | 
			
		||||
    //  */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
<?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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -91,12 +91,33 @@
 | 
			
		|||
    "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"
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -222,6 +243,9 @@
 | 
			
		|||
            "config/packages/sensio_framework_extra.yaml"
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    "symfony-bundles/json-request-bundle": {
 | 
			
		||||
        "version": "4.0.5"
 | 
			
		||||
    },
 | 
			
		||||
    "symfony/asset": {
 | 
			
		||||
        "version": "v5.3.2"
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -442,6 +466,9 @@
 | 
			
		|||
    "symfony/polyfill-mbstring": {
 | 
			
		||||
        "version": "v1.23.0"
 | 
			
		||||
    },
 | 
			
		||||
    "symfony/polyfill-php56": {
 | 
			
		||||
        "version": "v1.20.0"
 | 
			
		||||
    },
 | 
			
		||||
    "symfony/polyfill-php73": {
 | 
			
		||||
        "version": "v1.23.0"
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1715,6 +1715,14 @@
 | 
			
		|||
        "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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@
 | 
			
		|||
    "@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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +16,10 @@ interface AppRoute extends Route {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
export const appRoutes: AppRoute[] = [
 | 
			
		||||
    {
 | 
			
		||||
        path: 'auth',
 | 
			
		||||
        loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule),
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: '',
 | 
			
		||||
        component: NotFoundComponent,
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +30,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                matchExact: true,
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'profile',
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +42,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                icon: 'fa fa-user',
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'community',
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +54,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                icon: 'fa fa-users',
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'questions',
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +67,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                level: 1,
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'messages',
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +79,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                icon: 'fa fa-comments',
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'tests',
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +91,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                icon: 'fa fa-heartbeat',
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'research',
 | 
			
		||||
| 
						 | 
				
			
			@ -86,6 +103,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                icon: 'fa fa-flask',
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        path: 'admin',
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +122,8 @@ export const appRoutes: AppRoute[] = [
 | 
			
		|||
                level: 1,
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        canActivate: [ AuthGuard, ],
 | 
			
		||||
        canActivateChild: [ AuthGuard, ],
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,23 @@
 | 
			
		|||
 | 
			
		||||
<ng-template #toolbar>
 | 
			
		||||
    <mat-toolbar [color]="isDark ? null : 'primary'">
 | 
			
		||||
        <button mat-icon-button (click)="toggleSidebar()">
 | 
			
		||||
          <i class="fa fa-bars"></i>
 | 
			
		||||
    <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)">
 | 
			
		||||
    <mat-drawer [mode]="sidebarMode" [opened]="isSidebarOpen" (openedChange)="onSidebarOpenedChange($event)" *ngIf="!isSidebarHidden">
 | 
			
		||||
        <div class="menu">
 | 
			
		||||
            <div>
 | 
			
		||||
                <ng-container *ngFor="let route of appRoutes">
 | 
			
		||||
| 
						 | 
				
			
			@ -29,10 +34,9 @@
 | 
			
		|||
                        </div>
 | 
			
		||||
                    </ng-container>
 | 
			
		||||
                </ng-container>
 | 
			
		||||
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>
 | 
			
		||||
                <div class="d-flex user-box">
 | 
			
		||||
                <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">
 | 
			
		||||
| 
						 | 
				
			
			@ -48,21 +52,23 @@
 | 
			
		|||
                                    <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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,12 +5,16 @@ import { AppService, WindowSize } from './modules/shared/services/app/app.servic
 | 
			
		|||
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',
 | 
			
		||||
| 
						 | 
				
			
			@ -28,76 +32,102 @@ export class AppComponent {
 | 
			
		|||
  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',
 | 
			
		||||
  };
 | 
			
		||||
  user: UserModel;
 | 
			
		||||
  isSidebarHidden = false;
 | 
			
		||||
  dynamicToolbarComponents = [];
 | 
			
		||||
 | 
			
		||||
  get isSidebarOpen(): boolean {
 | 
			
		||||
    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 appService: AppService,
 | 
			
		||||
    private themeService: ThemeService,
 | 
			
		||||
    private browserStorageService: BrowserStorageService,
 | 
			
		||||
    private sanitizer: DomSanitizer,
 | 
			
		||||
    private authService: AuthService,
 | 
			
		||||
    private router: Router,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.themes = themeService.getThemes();
 | 
			
		||||
    this.langs = appService.getLangs();
 | 
			
		||||
    this.lang = appService.getLang();
 | 
			
		||||
    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 => {
 | 
			
		||||
      themeService.setTheme(theme);
 | 
			
		||||
        this.themeService.setTheme(theme);
 | 
			
		||||
    });
 | 
			
		||||
    appService.changeLang(this.lang);
 | 
			
		||||
    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 => {
 | 
			
		||||
      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);
 | 
			
		||||
        this.appService.changeLang(lang);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const sidebarUserPrefference = this.browserStorageService.getItem('sidebar.open');
 | 
			
		||||
    if (sidebarUserPrefference !== null) {
 | 
			
		||||
      this.sidebarOpen = sidebarUserPrefference;
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    this.theme = theme;
 | 
			
		||||
    this.themeControl.setValue(theme);
 | 
			
		||||
    this.isDark = theme.indexOf('dark') !== -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onResize(size: WindowSize): void {
 | 
			
		||||
    if (size === undefined) {
 | 
			
		||||
      return;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    this.isMobile = size.isMobile;
 | 
			
		||||
    this.defaultSidebarOpen = !size.isMobile;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,4 +154,9 @@ export class AppComponent {
 | 
			
		|||
  url(url: string): SafeStyle {
 | 
			
		||||
    return this.sanitizer.bypassSecurityTrustStyle(`url('${url}')`);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  logout(): void {
 | 
			
		||||
      this.authService.logout();
 | 
			
		||||
      this.router.navigateByUrl('/');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
import { BrowserModule } from '@angular/platform-browser';
 | 
			
		||||
import { NgModule } from '@angular/core';
 | 
			
		||||
import { HttpClientModule, HttpClient } from '@angular/common/http';
 | 
			
		||||
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';
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +11,9 @@ import { BrowserStorageService } from './modules/shared/services/browser-storage
 | 
			
		|||
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');
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +44,9 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
 | 
			
		|||
    AppService,
 | 
			
		||||
    ThemeService,
 | 
			
		||||
    BrowserStorageService,
 | 
			
		||||
    AuthTokenService,
 | 
			
		||||
    AuthService,
 | 
			
		||||
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
 | 
			
		||||
  ],
 | 
			
		||||
  bootstrap: [AppComponent]
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
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[] = [
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +10,7 @@ const routes: Route[] = [
 | 
			
		|||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: 'users',
 | 
			
		||||
    component: NotFoundComponent,
 | 
			
		||||
    component: UsersComponent,
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,14 +3,21 @@ 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: [],
 | 
			
		||||
  declarations: [
 | 
			
		||||
    UsersComponent
 | 
			
		||||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
    AdminRoutingModule,
 | 
			
		||||
    SharedModule,
 | 
			
		||||
  ]
 | 
			
		||||
  ],
 | 
			
		||||
  providers: [
 | 
			
		||||
      AdminUserService,
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class AdminModule { }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
<p>users works!</p>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
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(() => {
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
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');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
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 { }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
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 { }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
<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>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
.primary-background{
 | 
			
		||||
    background-color: var(--toolbar-background);
 | 
			
		||||
    height: 200px;
 | 
			
		||||
}
 | 
			
		||||
.content{
 | 
			
		||||
    margin: 0px auto;
 | 
			
		||||
    margin-top: -150px;
 | 
			
		||||
    height: 200px;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,118 @@
 | 
			
		|||
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);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
<button mat-icon-button (click)="toggleTheme()">
 | 
			
		||||
    <i class="fa" [class.fa-sun-o]="isDark" [class.fa-moon-o]="!isDark"></i>
 | 
			
		||||
</button>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
export enum AuthTabEnum {
 | 
			
		||||
    Login = 'login',
 | 
			
		||||
    Register = 'register',
 | 
			
		||||
    RestorePassword = 'restore-password',
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
export interface LoginModel {
 | 
			
		||||
    email: string;
 | 
			
		||||
    password: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
export interface RegisterModel {
 | 
			
		||||
    name: string;
 | 
			
		||||
    surname: string;
 | 
			
		||||
    email: string;
 | 
			
		||||
    password: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
export interface RestoreModel {
 | 
			
		||||
    email: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
export interface TokenResponse {
 | 
			
		||||
    token: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
export interface UserModel {
 | 
			
		||||
    id: number;
 | 
			
		||||
    name: string;
 | 
			
		||||
    surname: string;
 | 
			
		||||
    avatar: string | null;
 | 
			
		||||
    country: string | null;
 | 
			
		||||
    state: string | null;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,68 @@
 | 
			
		|||
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']);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,23 +5,29 @@ 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,
 | 
			
		||||
    MatToolbarModule,
 | 
			
		||||
    MatButtonModule,
 | 
			
		||||
    MatSidenavModule,
 | 
			
		||||
    MatSelectModule,
 | 
			
		||||
    MatMenuModule,
 | 
			
		||||
    MatCardModule,
 | 
			
		||||
    MatTabsModule,
 | 
			
		||||
    MatInputModule,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
  declarations: [],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
    ...itemsToExport,
 | 
			
		||||
  ],
 | 
			
		||||
  exports: [
 | 
			
		||||
    ...itemsToExport,
 | 
			
		||||
  ]
 | 
			
		||||
    declarations: [],
 | 
			
		||||
    imports: [
 | 
			
		||||
        CommonModule,
 | 
			
		||||
        ...itemsToExport,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        ...itemsToExport,
 | 
			
		||||
    ]
 | 
			
		||||
})
 | 
			
		||||
export class MaterialModule { }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,10 @@
 | 
			
		|||
<div class="m-3 p-3">
 | 
			
		||||
    {{ 'ERROR.PAGE_NOT_FOUND' | translate }}
 | 
			
		||||
</div>
 | 
			
		||||
<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>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
.primary-background{
 | 
			
		||||
    background-color: var(--toolbar-background);
 | 
			
		||||
    height: 100px;
 | 
			
		||||
}
 | 
			
		||||
.content{
 | 
			
		||||
    margin: 0px auto;
 | 
			
		||||
    margin-top: -100px;
 | 
			
		||||
    height: 200px;
 | 
			
		||||
    display: flex;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
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);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
export class User {
 | 
			
		||||
    id: number;
 | 
			
		||||
    email: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
    surname: string;
 | 
			
		||||
    avatar: string;
 | 
			
		||||
    country: string;
 | 
			
		||||
    state: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,70 +4,107 @@ import { fromEvent, Subject } from 'rxjs';
 | 
			
		|||
import { BrowserStorageService } from '../browser-storage/browser-storage.service';
 | 
			
		||||
 | 
			
		||||
export class WindowSize {
 | 
			
		||||
  isMobile: boolean;
 | 
			
		||||
    isMobile: boolean;
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public width: number,
 | 
			
		||||
    public height: number,
 | 
			
		||||
  ) {
 | 
			
		||||
    this.isMobile = this.width <= 700;
 | 
			
		||||
  }
 | 
			
		||||
    constructor(
 | 
			
		||||
        public width: number,
 | 
			
		||||
        public height: number,
 | 
			
		||||
    ) {
 | 
			
		||||
        this.isMobile = this.width <= 700;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  static generate(): WindowSize {
 | 
			
		||||
    return new WindowSize(window.innerWidth, window.innerHeight);
 | 
			
		||||
  }
 | 
			
		||||
    static generate(): WindowSize {
 | 
			
		||||
        return new WindowSize(window.innerWidth, window.innerHeight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  update(): WindowSize {
 | 
			
		||||
    this.width = window.innerWidth;
 | 
			
		||||
    this.height = window.innerHeight;
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
    update(): WindowSize {
 | 
			
		||||
        this.width = window.innerWidth;
 | 
			
		||||
        this.height = window.innerHeight;
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class AppService {
 | 
			
		||||
  private lang = 'en';
 | 
			
		||||
  private defaultLang = 'en';
 | 
			
		||||
  langChange = new Subject<string>();
 | 
			
		||||
  size: WindowSize;
 | 
			
		||||
  sizeChange = new Subject<WindowSize>();
 | 
			
		||||
    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(
 | 
			
		||||
    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 language = browserStorageService.getItem('language');
 | 
			
		||||
    if (!language) {
 | 
			
		||||
        language = this.defaultLang;
 | 
			
		||||
    constructor(
 | 
			
		||||
        private browserStorageService: BrowserStorageService,
 | 
			
		||||
        private translate: TranslateService,
 | 
			
		||||
    ) {
 | 
			
		||||
        this.configureResizeEvents();
 | 
			
		||||
        this.configureLanguageEvents();
 | 
			
		||||
        this.configureSidebarEvents();
 | 
			
		||||
    }
 | 
			
		||||
    this.translate.setDefaultLang(this.defaultLang);
 | 
			
		||||
    this.changeLang(language);
 | 
			
		||||
    fromEvent(window, 'resize').subscribe(e => {
 | 
			
		||||
      this.sizeChange.next(WindowSize.generate());
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  changeLang(lang: string): void {
 | 
			
		||||
    if (lang !== this.lang) {
 | 
			
		||||
      this.translate.use(lang);
 | 
			
		||||
      this.langChange.next(lang);
 | 
			
		||||
    configureSidebarEvents(): void {
 | 
			
		||||
        this.sidebarHiddenChange.subscribe(hidden => {
 | 
			
		||||
            this.sidebarHidden = hidden;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getLang(): string {
 | 
			
		||||
    return this.lang;
 | 
			
		||||
  }
 | 
			
		||||
    configureResizeEvents(): void {
 | 
			
		||||
        this.sizeChange.subscribe(size => this.size = size);
 | 
			
		||||
        this.sizeChange.next(WindowSize.generate());
 | 
			
		||||
        fromEvent(window, 'resize').subscribe(e => {
 | 
			
		||||
            this.sizeChange.next(WindowSize.generate());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  getLangs(): string[] {
 | 
			
		||||
    return [
 | 
			
		||||
      'pl',
 | 
			
		||||
      'en',
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
    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',
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ 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: [
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +12,7 @@ import { HttpClient } from '@angular/common/http';
 | 
			
		|||
  ],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CommonModule,
 | 
			
		||||
    MaterialModule,
 | 
			
		||||
    TranslateModule.forRoot({
 | 
			
		||||
        defaultLanguage: 'en',
 | 
			
		||||
        loader: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,38 @@
 | 
			
		|||
@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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
// 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-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).
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
@use "sass:map";
 | 
			
		||||
// Custom Theming for Angular Material
 | 
			
		||||
// For more information: https://material.angular.io/guide/theming
 | 
			
		||||
@use '~@angular/material' as mat;
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +12,7 @@
 | 
			
		|||
// 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-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).
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +28,10 @@ $theme: mat.define-light-theme((
 | 
			
		|||
  )
 | 
			
		||||
));
 | 
			
		||||
 | 
			
		||||
$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.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 104 KiB  | 
| 
						 | 
				
			
			@ -42,4 +42,9 @@ body .mat-drawer.mat-drawer-side {
 | 
			
		|||
}
 | 
			
		||||
.w-220{
 | 
			
		||||
  width: 220px;
 | 
			
		||||
}
 | 
			
		||||
[required]:after{
 | 
			
		||||
    content: "*";
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    color: var(--warn-color);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue