+
+
+
+
+
+
+
+
+
+
+ It's works, you can start coding :)
+
+
\ No newline at end of file
diff --git a/src/frontend/src/app/app.component.scss b/src/frontend/src/app/app.component.scss
index e69de29..2b2b8e2 100644
--- a/src/frontend/src/app/app.component.scss
+++ b/src/frontend/src/app/app.component.scss
@@ -0,0 +1,19 @@
+:host{
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+.menu{
+ width: 250px;
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ > :first-child{
+ flex-grow: 1;
+ }
+}
\ No newline at end of file
diff --git a/src/frontend/src/app/app.component.ts b/src/frontend/src/app/app.component.ts
index c6a5451..2e120bb 100644
--- a/src/frontend/src/app/app.component.ts
+++ b/src/frontend/src/app/app.component.ts
@@ -1,10 +1,104 @@
-import { Component } from '@angular/core';
-
-@Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.scss']
-})
-export class AppComponent {
- title = 'CureNet';
-}
+import { Component } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { AppService, WindowSize } from './modules/shared/services/app/app.service';
+import { ThemeService } from './modules/shared/services/theme/theme.service';
+
+enum SidebarOpenEnum {
+ Default,
+ Open,
+ Closed,
+}
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss']
+})
+export class AppComponent {
+ isMobile = true;
+ title = 'CureNet';
+ sidebarOpen = SidebarOpenEnum.Default;
+ sidebarMode = 'over';
+ defaultSidebarOpen = false;
+ lang: string;
+ theme: string;
+ langs: string[] = [];
+ themes: string[] = [];
+ themeControl = new FormControl();
+ langControl = new FormControl();
+ isDark = true;
+
+ get isSidebarOpen() {
+ switch (this.sidebarOpen) {
+ case SidebarOpenEnum.Open:
+ return true;
+ break;
+ case SidebarOpenEnum.Closed:
+ return false;
+ break;
+ default:
+ return this.defaultSidebarOpen;
+ break;
+ }
+ }
+ constructor(
+ appService: AppService,
+ themeService: ThemeService,
+ ) {
+ this.themes = themeService.getThemes();
+ this.langs = appService.getLangs();
+ this.lang = appService.getLang();
+
+ this.themeControl.valueChanges.subscribe(theme => {
+ themeService.setTheme(theme);
+ });
+ 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);
+ });
+ }
+
+ onThemeChanged(theme: string) {
+ if (theme === undefined) {
+ return;
+ }
+ this.theme = theme;
+ this.themeControl.setValue(theme);
+ this.isDark = theme.indexOf('dark') !== -1;
+ }
+
+ onResize(size: WindowSize) {
+ if (size === undefined) {
+ return;
+ }
+ this.isMobile = size.isMobile;
+ this.defaultSidebarOpen = !this.isMobile;
+ this.sidebarMode = this.isMobile ? 'over' : 'side';
+ }
+
+ onLangChanged(lang: string) {
+ if (lang === undefined) {
+ return;
+ }
+ this.lang = lang;
+ this.langControl.setValue(lang);
+ }
+
+ toggleSidebar() {
+ this.sidebarOpen = this.isSidebarOpen ? SidebarOpenEnum.Closed : SidebarOpenEnum.Open;
+ }
+
+ onSidebarOpenedChange(opened: boolean) {
+ this.sidebarOpen = opened ? SidebarOpenEnum.Open : SidebarOpenEnum.Closed;
+ }
+}
\ No newline at end of file
diff --git a/src/frontend/src/app/app.module.ts b/src/frontend/src/app/app.module.ts
index 2c3ba29..6b83379 100644
--- a/src/frontend/src/app/app.module.ts
+++ b/src/frontend/src/app/app.module.ts
@@ -1,18 +1,47 @@
-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 { }
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { HttpClientModule, HttpClient } from '@angular/common/http';
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MaterialModule } from './modules/material/material.module';
+import { AppService } from './modules/shared/services/app/app.service';
+import { ThemeService } from './modules/shared/services/theme/theme.service';
+import { BrowserStorageService } from './modules/shared/services/browser-storage/browser-storage.service';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+
+export function HttpLoaderFactory(http: HttpClient) {
+ return new TranslateHttpLoader(http, '/assets/lang/', '.json');
+}
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ BrowserAnimationsModule,
+ MaterialModule,
+ FormsModule,
+ ReactiveFormsModule,
+ HttpClientModule,
+ TranslateModule.forRoot({
+ defaultLanguage: 'en',
+ loader: {
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [HttpClient]
+ }
+ }),
+ ],
+ providers: [
+ AppService,
+ ThemeService,
+ BrowserStorageService,
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/src/frontend/src/app/modules/material/material.module.ts b/src/frontend/src/app/modules/material/material.module.ts
new file mode 100644
index 0000000..5e504e5
--- /dev/null
+++ b/src/frontend/src/app/modules/material/material.module.ts
@@ -0,0 +1,25 @@
+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';
+
+const itemsToExport = [
+ MatToolbarModule,
+ MatButtonModule,
+ MatSidenavModule,
+ MatSelectModule,
+];
+
+@NgModule({
+ declarations: [],
+ imports: [
+ CommonModule,
+ ...itemsToExport,
+ ],
+ exports: [
+ ...itemsToExport,
+ ]
+})
+export class MaterialModule { }
diff --git a/src/frontend/src/app/modules/shared/services/app/app.service.ts b/src/frontend/src/app/modules/shared/services/app/app.service.ts
new file mode 100644
index 0000000..69d4fff
--- /dev/null
+++ b/src/frontend/src/app/modules/shared/services/app/app.service.ts
@@ -0,0 +1,73 @@
+import { Injectable } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { fromEvent, Subject } from 'rxjs';
+import { BrowserStorageService } from '../browser-storage/browser-storage.service';
+
+export class WindowSize {
+ isMobile: boolean;
+
+ constructor(
+ public width: number,
+ public height: number,
+ ) {
+ this.isMobile = this.width <= 700;
+ }
+
+ update() {
+ this.width = window.innerWidth;
+ this.height = window.innerHeight;
+ }
+
+ static generate() {
+ return new WindowSize(window.innerWidth, window.innerHeight);
+ }
+}
+
+@Injectable()
+export class AppService {
+ private lang = 'en';
+ private defaultLang = 'en';
+ langChange = new Subject