diff --git a/.gitignore b/.gitignore index 1febf6ca..5ddfb87d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .idea/ -__pycache__ +*/__pycache__ diff --git a/back/.gitignore b/back/.gitignore new file mode 100644 index 00000000..6769e21d --- /dev/null +++ b/back/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/back/app/core/__pycache__/__init__.cpython-310.pyc b/back/app/core/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 84b43d66..00000000 Binary files a/back/app/core/__pycache__/__init__.cpython-310.pyc and /dev/null differ diff --git a/back/app/core/__pycache__/routes.cpython-310.pyc b/back/app/core/__pycache__/routes.cpython-310.pyc deleted file mode 100644 index cdbf309b..00000000 Binary files a/back/app/core/__pycache__/routes.cpython-310.pyc and /dev/null differ diff --git a/back/app/core/__pycache__/schemas.cpython-310.pyc b/back/app/core/__pycache__/schemas.cpython-310.pyc deleted file mode 100644 index 0920fde4..00000000 Binary files a/back/app/core/__pycache__/schemas.cpython-310.pyc and /dev/null differ diff --git a/back/app/core/routes.py b/back/app/core/routes.py index 041f51cf..95b4ea27 100644 --- a/back/app/core/routes.py +++ b/back/app/core/routes.py @@ -1,7 +1,11 @@ from beanie import PydanticObjectId +from beanie.odm.enums import SortDirection from fastapi import APIRouter, HTTPException -from typing import TypeVar, List, Generic +from fastapi_paginate import Page, Params, add_pagination +from fastapi_paginate.ext.motor import paginate + +from typing import TypeVar, List, Generic, Any, Dict T = TypeVar('T') @@ -10,7 +14,21 @@ V = TypeVar('V') W = TypeVar('W') +def parse_sort(sort_by): + fields = [] + for field in sort_by.split(','): + dir, col = field.split('(') + fields.append((col[:-1], 1 if dir == 'asc' else -1)) + + return fields + + +def parse_query(query) -> Dict[Any, Any]: + return {} + + def get_crud_router(model, model_create, model_read, model_update): + router = APIRouter() @router.post("/", response_description="{} added to the database".format(model.__name__)) @@ -22,12 +40,17 @@ def get_crud_router(model, model_create, model_read, model_update): @router.get("/{id}", response_description="{} record retrieved".format(model.__name__)) async def read_id(id: PydanticObjectId) -> model_read: item = await model.get(id) - return item + return model_read(**item.dict()) - @router.get("/", response_description="{} records retrieved".format(model.__name__)) - async def read_list() -> List[model_read]: - item = await model.find_all().to_list() - return item + @router.get("/", response_model=Page[model_read], response_description="{} records retrieved".format(model.__name__)) + async def read_list(size: int = 50, page: int = 1, sort_by: str = None, query: str = None) -> Page[model_read]: + sort = parse_sort(sort_by) + query = parse_query(query) + # limit=limit, skip=offset, + + collection = model.get_motor_collection() + items = paginate(collection, query, Params(**{'size': size, 'page': page}), sort=sort) + return await items @router.put("/{id}", response_description="{} record updated".format(model.__name__)) async def update(id: PydanticObjectId, req: model_update) -> model_read: @@ -44,7 +67,7 @@ def get_crud_router(model, model_create, model_read, model_update): ) await item.update(update_query) - return item + return model_read(**item.dict()) @router.delete("/{id}", response_description="{} record deleted from the database".format(model.__name__)) async def delete(id: PydanticObjectId) -> dict: @@ -61,4 +84,7 @@ def get_crud_router(model, model_create, model_read, model_update): "message": "{} deleted successfully".format(model.__name__) } + add_pagination(router) return router + + diff --git a/back/app/entity/__pycache__/models.cpython-310.pyc b/back/app/entity/__pycache__/models.cpython-310.pyc deleted file mode 100644 index be364450..00000000 Binary files a/back/app/entity/__pycache__/models.cpython-310.pyc and /dev/null differ diff --git a/back/app/entity/__pycache__/schemas.cpython-310.pyc b/back/app/entity/__pycache__/schemas.cpython-310.pyc deleted file mode 100644 index d3eb9b64..00000000 Binary files a/back/app/entity/__pycache__/schemas.cpython-310.pyc and /dev/null differ diff --git a/back/app/entity/models.py b/back/app/entity/models.py index 4bf22748..c5f3c0c7 100644 --- a/back/app/entity/models.py +++ b/back/app/entity/models.py @@ -18,3 +18,6 @@ class Entity(Document): address: str created_at: datetime = Field(default=datetime.utcnow(), nullable=False) updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False) + # + # class Settings: + # name = "entities" diff --git a/back/app/entity/schemas.py b/back/app/entity/schemas.py index fb04cc64..b9f1d072 100644 --- a/back/app/entity/schemas.py +++ b/back/app/entity/schemas.py @@ -4,13 +4,14 @@ from datetime import datetime from pydantic import BaseModel from .models import Entity, EntityType +from ..core.schemas import Writer class EntityRead(Entity): pass -class EntityCreate(BaseModel): +class EntityCreate(Writer): type: EntityType name: str address: str diff --git a/back/app/main.py b/back/app/main.py index e8d783af..2eb25bd4 100644 --- a/back/app/main.py +++ b/back/app/main.py @@ -1,5 +1,4 @@ from fastapi import FastAPI -from fastapi import Depends, Request from .contract import contract_router from .db import init_db diff --git a/back/app/user/__pycache__/manager.cpython-310.pyc b/back/app/user/__pycache__/manager.cpython-310.pyc deleted file mode 100644 index ef4e4d64..00000000 Binary files a/back/app/user/__pycache__/manager.cpython-310.pyc and /dev/null differ diff --git a/back/app/user/__pycache__/routes.cpython-310.pyc b/back/app/user/__pycache__/routes.cpython-310.pyc deleted file mode 100644 index bcf2f290..00000000 Binary files a/back/app/user/__pycache__/routes.cpython-310.pyc and /dev/null differ diff --git a/back/app/user/manager.py b/back/app/user/manager.py index 6d9f527d..6dfcad57 100644 --- a/back/app/user/manager.py +++ b/back/app/user/manager.py @@ -1,10 +1,14 @@ import uuid from typing import Any, Dict, Generic, Optional +from bson import ObjectId from fastapi import Depends -from fastapi_users import BaseUserManager, UUIDIDMixin, models, exceptions, schemas +from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models, exceptions, schemas +from fastapi_users.authentication import BearerTransport, AuthenticationBackend +from fastapi_users.authentication.strategy.db import AccessTokenDatabase, DatabaseStrategy + +from .models import User, get_user_db, AccessToken, get_access_token_db -from .models import User, get_user_db SECRET = "SECRET" @@ -66,6 +70,44 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): return created_user + def parse_id(self, value: Any) -> uuid.UUID: + if isinstance(value, ObjectId): + return value + if isinstance(value, uuid.UUID): + return value + try: + return uuid.UUID(value) + except ValueError as e: + raise exceptions.InvalidID() from e + async def get_user_manager(user_db=Depends(get_user_db)): - yield UserManager(user_db) \ No newline at end of file + yield UserManager(user_db) + + +def get_database_strategy( + access_token_db: AccessTokenDatabase[AccessToken] = Depends(get_access_token_db), +) -> DatabaseStrategy: + return DatabaseStrategy(access_token_db, lifetime_seconds=3600) + + +bearer_transport = BearerTransport(tokenUrl="auth/jwt/login") + + +auth_backend = AuthenticationBackend( + name="db", + transport=bearer_transport, + get_strategy=get_database_strategy, +) + + +fastapi_users = FastAPIUsers[User, uuid.UUID]( + get_user_manager, + [auth_backend], +) + +get_current_user = fastapi_users.current_user(active=True) + + +def get_auth_router(): + return fastapi_users.get_auth_router(auth_backend) diff --git a/back/app/user/routes.py b/back/app/user/routes.py index f3445525..cea59e5b 100644 --- a/back/app/user/routes.py +++ b/back/app/user/routes.py @@ -1,45 +1,13 @@ -import uuid -from typing import Optional - -from fastapi import Depends, Request -from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models, exceptions -from fastapi_users.authentication import BearerTransport, AuthenticationBackend -from fastapi_users.authentication.strategy.db import AccessTokenDatabase, DatabaseStrategy +from fastapi import Depends from beanie import PydanticObjectId from fastapi import APIRouter, HTTPException from typing import List -from .models import User, AccessToken, get_user_db, get_access_token_db +from .models import User from .schemas import UserRead, UserUpdate, UserCreate -from .manager import get_user_manager - - -def get_database_strategy( - access_token_db: AccessTokenDatabase[AccessToken] = Depends(get_access_token_db), -) -> DatabaseStrategy: - return DatabaseStrategy(access_token_db, lifetime_seconds=3600) - - -bearer_transport = BearerTransport(tokenUrl="auth/jwt/login") - - -auth_backend = AuthenticationBackend( - name="db", - transport=bearer_transport, - get_strategy=get_database_strategy, -) - - -fastapi_users = FastAPIUsers[User, uuid.UUID]( - get_user_manager, - [auth_backend], -) - - -def get_auth_router(): - return fastapi_users.get_auth_router(auth_backend) +from .manager import get_user_manager, get_current_user, get_auth_router router = APIRouter() @@ -51,10 +19,16 @@ async def create(user: UserCreate, user_manager=Depends(get_user_manager)) -> di return {"message": "User added successfully"} +@router.get("/me", response_description="User record retrieved") +async def read_me(user=Depends(get_current_user)) -> UserRead: + user = await User.get(user.id) + return UserRead(**user.dict()) + + @router.get("/{id}", response_description="User record retrieved") async def read_id(id: PydanticObjectId) -> UserRead: user = await User.get(id) - return user + return UserRead(**user.dict()) @router.get("/", response_model=List[UserRead], response_description="User records retrieved") diff --git a/back/requirements.txt b/back/requirements.txt index f016f1e5..f3c74a10 100644 --- a/back/requirements.txt +++ b/back/requirements.txt @@ -2,4 +2,5 @@ fastapi==0.88.0 fastapi_users==10.2.1 fastapi_users_db_beanie==1.1.2 motor==3.1.1 +fastapi-paginate==0.1.0 uvicorn \ No newline at end of file diff --git a/front/app/src/app/app.module.ts b/front/app/src/app/app.module.ts index c0bedb1e..7b1d7111 100644 --- a/front/app/src/app/app.module.ts +++ b/front/app/src/app/app.module.ts @@ -23,7 +23,7 @@ export function TranslateHttpLoaderFactory(http: HttpClient) { } import { LoginService } from '@core/authentication/login.service'; -import { FakeLoginService } from './fake-login.service'; +import { ChtloginService } from '@core/../custom/chtlogin.service'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; @NgModule({ @@ -49,7 +49,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; ], providers: [ { provide: BASE_URL, useValue: environment.baseUrl }, - { provide: LoginService, useClass: FakeLoginService }, // <= Remove it in the real APP + { provide: LoginService, useClass: ChtloginService }, // <= Remove it in the real APP httpInterceptorProviders, appInitializerProviders, ], diff --git a/front/app/src/app/custom/chtlogin.service.ts b/front/app/src/app/custom/chtlogin.service.ts new file mode 100644 index 00000000..c785bc3f --- /dev/null +++ b/front/app/src/app/custom/chtlogin.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { HttpHeaders, HttpParams } from '@angular/common/http'; +import { Token, User } from '../core/authentication/interface'; +import { Menu } from '@core'; +import { map } from 'rxjs/operators'; +import { LoginService } from '../core/authentication/login.service' + +@Injectable({ + providedIn: 'root', +}) +export class ChtloginService extends LoginService { + + login(username: string, password: string, rememberMe = false) { + const body = new HttpParams() + .set('username', username) + .set('password', password); + + return this.http.post('/api/v1/auth/login', body.toString(), { + headers: new HttpHeaders() + .set('Content-Type', 'application/x-www-form-urlencoded') + }); + } + + refresh(params: Record) { + return this.http.post('/api/v1/auth/refresh', params); + } + + logout() { + return this.http.post('/api/v1/auth/logout', {}); + } + + me() { + return this.http.get('/api/v1/users/me'); + } + + menu() { + return this.http + .get<{ menu: Menu[] }>('assets/data/menu.json?_t=' + Date.now()) + .pipe(map(res => res.menu)); + } +} diff --git a/front/app/src/app/routes/clients/card/card.component.css b/front/app/src/app/routes/clients/card/card.component.css new file mode 100644 index 00000000..e69de29b diff --git a/front/app/src/app/routes/clients/card/card.component.html b/front/app/src/app/routes/clients/card/card.component.html new file mode 100644 index 00000000..e2e06e18 --- /dev/null +++ b/front/app/src/app/routes/clients/card/card.component.html @@ -0,0 +1,28 @@ + + + +
+ + Name + + + + + Address + + + + + Type + + Particulier + Entreprise + Institution + + + + +
diff --git a/front/app/src/app/routes/clients/card/card.component.spec.ts b/front/app/src/app/routes/clients/card/card.component.spec.ts new file mode 100644 index 00000000..39952966 --- /dev/null +++ b/front/app/src/app/routes/clients/card/card.component.spec.ts @@ -0,0 +1,25 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClientsCardComponent } from './card.component'; + +describe('ClientsCardComponent', () => { + let component: ClientsCardComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ ClientsCardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientsCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/app/src/app/routes/clients/card/card.component.ts b/front/app/src/app/routes/clients/card/card.component.ts new file mode 100644 index 00000000..337cdd8d --- /dev/null +++ b/front/app/src/app/routes/clients/card/card.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { ClientsService } from '../clients.service' + +export interface Client { + _id: string; + name: string; + address: string; + type: string; +} + +@Component({ + selector: 'app-clients-card', + templateUrl: './card.component.html', + styleUrls: ['./card.component.css'] +}) +export class ClientsCardComponent implements OnInit { + @Input() id: string = ""; + + item: Client = {_id: '', name: '', address: '', type: ''}; + + cardForm = this.formBuilder.group({ + name: '', + address: '', + type: '' + }); + + constructor(private formBuilder: FormBuilder, private clientsService: ClientsService) { + if (this.id == "") { + const url_parts = window.location.href.split('/') + this.id = url_parts[url_parts.length - 1]; + } + this.item = {_id: '', name: '', address: '', type: ''} + } + + ngOnInit() { + this.getData(); + } + + private getData() { + this.clientsService.get(this.id).subscribe((data: any) => { + this.item = data; + }); + } + + onSubmit(): void { + } +} diff --git a/front/app/src/app/routes/clients/clients-routing.module.ts b/front/app/src/app/routes/clients/clients-routing.module.ts new file mode 100644 index 00000000..b97724e9 --- /dev/null +++ b/front/app/src/app/routes/clients/clients-routing.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { ClientsListComponent } from './list/list.component'; +import { ClientsCardComponent } from './card/card.component'; + +const routes: Routes = [{ path: 'list', component: ClientsListComponent }, +{ path: 'card/:id', component: ClientsCardComponent } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ClientsRoutingModule { } diff --git a/front/app/src/app/routes/clients/clients.module.ts b/front/app/src/app/routes/clients/clients.module.ts new file mode 100644 index 00000000..55048782 --- /dev/null +++ b/front/app/src/app/routes/clients/clients.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '@shared/shared.module'; +import { ClientsRoutingModule } from './clients-routing.module'; +import { ClientsListComponent } from './list/list.component'; +import { ClientsCardComponent } from './card/card.component'; + +const COMPONENTS: any[] = [ClientsListComponent, ClientsCardComponent]; +const COMPONENTS_DYNAMIC: any[] = []; + +@NgModule({ + imports: [ + SharedModule, + ClientsRoutingModule + ], + declarations: [ + ...COMPONENTS, + ...COMPONENTS_DYNAMIC + ] +}) +export class ClientsModule { } diff --git a/front/app/src/app/routes/clients/clients.service.ts b/front/app/src/app/routes/clients/clients.service.ts new file mode 100644 index 00000000..8ffd3449 --- /dev/null +++ b/front/app/src/app/routes/clients/clients.service.ts @@ -0,0 +1,23 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import {Client} from "./list/list.component"; + +@Injectable({ + providedIn: 'root' +}) +export class ClientsService { + + constructor(private http: HttpClient) { } + + public getList(page: number, size: number, sortColumn: string, sortDirection: string) { + return this.http.get<{ menu: Client[] }>( + `/api/v1/entity/?size=${size}&page=${page + 1}&sort_by=${sortDirection}(${sortColumn})` + ); + } + + public get(id: string) { + return this.http.get<{ menu: Client[] }>( + `/api/v1/entity/${id}` + ); + } +} diff --git a/front/app/src/app/routes/clients/list/list.component.css b/front/app/src/app/routes/clients/list/list.component.css new file mode 100644 index 00000000..e69de29b diff --git a/front/app/src/app/routes/clients/list/list.component.html b/front/app/src/app/routes/clients/list/list.component.html new file mode 100644 index 00000000..c46e9c99 --- /dev/null +++ b/front/app/src/app/routes/clients/list/list.component.html @@ -0,0 +1,43 @@ + + + + + Filter + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID {{item._id}} Nom {{item.name}} Type {{item.type}} Adresse {{item.address}}
+ + + diff --git a/front/app/src/app/routes/clients/list/list.component.spec.ts b/front/app/src/app/routes/clients/list/list.component.spec.ts new file mode 100644 index 00000000..8d86da69 --- /dev/null +++ b/front/app/src/app/routes/clients/list/list.component.spec.ts @@ -0,0 +1,25 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClientsListComponent } from './list.component'; + +describe('ClientsListComponent', () => { + let component: ClientsListComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ ClientsListComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClientsListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/front/app/src/app/routes/clients/list/list.component.ts b/front/app/src/app/routes/clients/list/list.component.ts new file mode 100644 index 00000000..ba52f446 --- /dev/null +++ b/front/app/src/app/routes/clients/list/list.component.ts @@ -0,0 +1,84 @@ +import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { MatSort, Sort} from '@angular/material/sort'; +import { Injectable } from '@angular/core'; +import { Menu } from "@core"; +import { BehaviorSubject, forkJoin, fromEvent, Observable } from "rxjs"; +import { map, take } from "rxjs/operators"; +import { ClientsService } from '../clients.service' +import {Location} from '@angular/common'; +import { Router } from '@angular/router'; + +export interface Client { + _id: string; + name: string; + address: string; + type: string; +} + +@Component({ + selector: 'app-clients-list', + templateUrl: './list.component.html', + styleUrls: ['./list.component.css'] +}) +export class ClientsListComponent implements OnInit { + + displayedColumns: string[] = ['type', 'name', 'address', '_id',]; + dataSource: Client[] = []; + + length: number = 0; + pageIndex: number = 0; + pageSize: number = 15; + + sortColumn: string = "name"; + sortDirection: string = "asc"; + + //@ViewChild(MatSort) sort: MatSort; + + constructor(private location: Location, private clientsService: ClientsService, private router: Router) { } + + ngAfterViewInit() { + } + + ngOnInit() { + this.getData(); + } + + handlePageEvent(e: PageEvent) { + this.length = e.length; + this.pageSize = e.pageSize; + this.pageIndex = e.pageIndex; + this.getData(); + } + + handleSortChange(s: Sort) { + this.sortColumn = s.active; + this.sortDirection = s.direction; + this.getData(); + } + + handleClickRow(row: any) { + window.location.href=window.location.href.replace('list', `card/${row._id}`) + this.router.navigateByUrl("card"); + } + + private getData() { + this.clientsService.getList(this.pageIndex, this.pageSize, this.sortColumn, this.sortDirection).subscribe((data: any) => { + this.dataSource = data.items; + this.length = data.total; + this.pageSize = data.size; + this.pageIndex = data.page - 1; + }); + } + + + + applyFilter(event: Event) { + // const filterValue = (event.target as HTMLInputElement).value; + // this.dataSource.filter = filterValue.trim().toLowerCase(); + // + // if (this.dataSource.paginator) { + // this.dataSource.paginator.firstPage(); + // } + } +} diff --git a/front/app/src/app/routes/routes-routing.module.ts b/front/app/src/app/routes/routes-routing.module.ts index 896ba3aa..f02d18ec 100644 --- a/front/app/src/app/routes/routes-routing.module.ts +++ b/front/app/src/app/routes/routes-routing.module.ts @@ -24,6 +24,7 @@ const routes: Routes = [ { path: '403', component: Error403Component }, { path: '404', component: Error404Component }, { path: '500', component: Error500Component }, + { path: 'clients', loadChildren: () => import('./clients/clients.module').then(m => m.ClientsModule) }, ], }, { diff --git a/front/app/src/assets/data/menu.json b/front/app/src/assets/data/menu.json index 7e1bcf3c..a2bedbe3 100644 --- a/front/app/src/assets/data/menu.json +++ b/front/app/src/assets/data/menu.json @@ -1,5 +1,11 @@ { "menu": [ + { + "route": "clients/list", + "name": "clients", + "type": "link", + "icon": "group" + }, { "route": "dashboard", "name": "dashboard", diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 05c331e8..1cf30cc1 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -34,5 +34,13 @@ http { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } + + location /ng-cli-ws { + proxy_pass http://docker-front ; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + } } } \ No newline at end of file