diff --git a/package-lock.json b/package-lock.json index 07922d7a054af9bb7fedb2e0c0c68b24426e9dba..6009fbc9069eefc25a7b92cdf251b68f037079c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2306,9 +2306,9 @@ "dev": true }, "bulma": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.4.tgz", - "integrity": "sha512-krG2rP6eAX1WE0sf6O0SC/FUVSOBX4m1PBC2+GKLpb2pX0qanaDqcv9U2nu75egFrsHkI0zdWYuk/oGwoszVWg==" + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz", + "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==" }, "bytes": { "version": "3.0.0", diff --git a/package.json b/package.json index a1428dabd9c40c857672e959205208d28c8fac4c..e3778f8b312b4c480be731bba399529e69440f63 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@angular/platform-browser": "^7.2.4", "@angular/platform-browser-dynamic": "^7.2.4", "@angular/router": "^7.2.4", - "bulma": "^0.7.4", + "bulma": "^0.7.5", "core-js": "^2.6.4", "node-rsa": "^1.0.5", "rxjs": "^6.4.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 3043831d86a8dae809137666c003e22eeb3ad9cc..af521fa392d7c3eb073c562f3346df61fe408de6 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -5,10 +5,71 @@ </div> <h4>Plateforme Data Admin</h4> - <div class="logo"> - <img src="assets/img/logo.svg" alt="Le Grand Lyon, la métropole"> + <div class="navbar-header-item navbar-right-side"> + <div class="connect-me"> + <a routerLink="/login" *ngIf="!loggedInUserFullname; else userSignedInTemplate"> + <button class="button connect-button"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23"> + <g id="btn_x5F_connect"> + <path + d="M11.5 11.7c-3.1 0-5.6-2.6-5.6-5.6C5.9 3 8.5.5 11.5.5c3.1 0 5.6 2.6 5.6 5.6 0 3-2.6 5.6-5.6 5.6zM17.2 14.6c1.3.6 2.3 1.5 3 2.6 1.2 1.9 1.2 3.8 1.2 3.9 0 .8-.6 1.4-1.2 1.4H2.6c-.6 0-1.2-.6-1.2-1.4 0-.1 0-2 1.2-3.9.7-1.1 1.8-2 3-2.6 1.6-.8 3.5-1.2 5.8-1.2s4.2.4 5.8 1.2z" + class="stroke_white" /> + </g> + </svg> + <span>Me connecter</span> + </button> + </a> + </div> + + <ng-template #userSignedInTemplate> + <div class="put-menu-to-right" [ngClass]="{'is-active': userDropdownIsOpened}"> + + <button class="button connect-button" (click)="toggleUserDropdown()" + (clickOutside)="closeUserDropdown()"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23" class="is-connected"> + <g id="btn_x5F_connect"> + <path + d="M11.5 11.7c-3.1 0-5.6-2.6-5.6-5.6C5.9 3 8.5.5 11.5.5c3.1 0 5.6 2.6 5.6 5.6 0 3-2.6 5.6-5.6 5.6zM17.2 14.6c1.3.6 2.3 1.5 3 2.6 1.2 1.9 1.2 3.8 1.2 3.9 0 .8-.6 1.4-1.2 1.4H2.6c-.6 0-1.2-.6-1.2-1.4 0-.1 0-2 1.2-3.9.7-1.1 1.8-2 3-2.6 1.6-.8 3.5-1.2 5.8-1.2s4.2.4 5.8 1.2z" + class="stroke_white" /> + </g> + </svg> + <span class="text-overflow">{{ loggedInUserFullname }}</span> + </button> + + <div class="dropdown-content user-information"> + <a href preventDefault (click)="loggout()" class="dropdown-item with-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23"> + <g id="picto_x5F_deconnect"> + <g class="fill_black"> + <g class="fill_black"> + <path + d="M11 18H8c-1.7 0-3-1.3-3-3V9c0-1.7 1.3-3 3-3h3c.6 0 1 .4 1 1s-.4 1-1 1H8c-.6 0-1 .4-1 1v6c0 .6.4 1 1 1h3c.6 0 1 .4 1 1s-.4 1-1 1z" + class="fill_black" /> + </g> + <g class="fill_black"> + <path + d="M11 18H8c-1.7 0-3-1.3-3-3V9c0-1.7 1.3-3 3-3h3c.6 0 1 .4 1 1s-.4 1-1 1H8c-.6 0-1 .4-1 1v6c0 .6.4 1 1 1h3c.6 0 1 .4 1 1s-.4 1-1 1z" + class="fill_black" /> + </g> + </g> + <path + d="M19.4 11.3L14.1 6v4H10c-.6 0-1 .4-1 1v2c0 .6.4 1 1 1h4.1v4l5.3-5.3c.4-.4.4-1 0-1.4z" + class="fill_black" /> + </g> + </svg> + <span i18n="@@header.signOut">Sign Out</span> + </a> + </div> + </div> + <div class="modal" [ngClass]="{'is-active': userDropdownIsOpened}"></div> + </ng-template> + <div class="logo"> + <img src="assets/img/logo.svg" alt="Le Grand Lyon, la métropole"> + </div> + </div> + </header> <nav class="main-nav" [ngClass]="{'is-active': sidebarOpened}"> <app-menu [expanded]="sidebarOpened"></app-menu> diff --git a/src/app/app.component.scss b/src/app/app.component.scss index e88db96820f572b128d6900e0b8290a631430028..629e4b28590c8729f353903b485d8f39c4832ff6 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,20 +1,22 @@ +@import '../scss/variables.scss'; +@import '../../node_modules/bulma/sass/utilities/_all.sass'; -.grid{ +.grid { display: grid; grid-template-columns: 50px auto; grid-template-rows: 60px minmax(calc(100vh - 60px), auto); - overflow: hidden; + // overflow: hidden; } -.main-header{ +.main-header { grid-row: 1; - grid-column: 1 / -1; + grid-column: 1 / span 2; display: flex; flex-flow: row; align-items: center; - background-color: white;; + background-color: white; border-bottom: 1px solid lightgray; h4 { @@ -22,9 +24,9 @@ } .logo { - margin-left: auto; padding-right: 20px; } + .hamburger { width: 50px; height: 30px; @@ -36,29 +38,32 @@ } .hamburger.expanded { - background-image: url("../assets/img/hamburger_bitten.png"); + background-image: url("../assets/img/hamburger_bitten.png"); } } -.main-nav{ +.main-nav { grid-row: 2; - grid-column: 1 / span 1; + grid-column: 1; width: 50px; - z-index: 200; + height: 100vh; + z-index: 200; transition: all .2s linear; background-color: #333745; + position: sticky; + top: 0px; &.is-active { width: 200px; } } -.main-content{ +.main-content { grid-row: 2; - grid-column: 2 / -1; + grid-column: 2; margin-left: 150px; - background-color: #f2f2f2; + background-color: $grey-background-color; transition: all 200ms cubic-bezier(0.7, 0, 0.3, 1); &.wide { @@ -66,3 +71,155 @@ } } +// Connexion button +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; +} + +svg { + height: 21px; + stroke: $brand-color; + stroke-width: 1px; + flex-shrink: 0; + + .stroke_white { + fill: transparent; + } +} + +.connect-button { + border-color: $grey-dark-color; + width: 100%; + background: transparent; + padding: 0.5em 1.2em; + height: 2.5rem; + font-weight: 600; + line-height: 1; + + span { + color: $grey-dark-color; + } + + &:hover { + color: $grey-super-light-color; + } + + svg { + margin-right: 0.6rem; + } + + @media screen and (max-width: $tablet) { + height: 2.5rem; + width: 2.5rem; + padding: 7px; + justify-content: center; + + svg { + margin-right: 0; + } + + span { + display: none; + } + } +} + +svg.is-connected { + .stroke_white { + fill: $grey-dark-color; + } +} + +.navbar-right-side { + display: flex; + align-items: center; + z-index: 5; + margin-left: auto; +} + +.dropdown-item.with-icon { + display: flex; + align-items: center; + + span { + margin-left: 0.5rem; + } + + svg { + fill: $brand-color; + } + + &:hover { + background-color: transparent; + + span { + font-weight: 600; + } + } +} + +.put-menu-to-right { + position: relative; + z-index: 15; + + .user-information { + width: 223px; + border-radius: 5px; + display: none; + position: absolute; + background-color: white; + top: 3.6em; + left: -151px; + padding: 0; + + @media screen and (min-width: $tablet) { + left: -28px; + } + } + + .user-information::before { + content: ""; + position: absolute; + width: 12px; + height: 12px; + top: -6px; + left: 50%; + background: inherit; + border: inherit; + border-right: 0; + border-bottom: 0; + transform: rotate(45deg); + + // When tablet, change the position to not be cut + @media screen and (max-width: $desktop) { + & { + left: 73%; + } + } + } + + &.is-active { + .user-information { + display: block; + } + } +} + +.connect-me { + margin-right: 1rem; +} + +.connect-me a:hover { + text-decoration: none; +} + +.modal.is-active { + position: fixed; + z-index: 10; + bottom: 0; + left: 0; + right: 0; + top: $header-bar-height; + background-color: rgba(10, 10, 10, 0.86); +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d00c62e939d0fa8b9677eab2569861298897d7ec..994fd9e1ac95c651f574ab12b569a2e5710c373c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,8 @@ import { Component, OnInit } from '@angular/core'; +import { UserService } from './user/services'; +import { Router, NavigationEnd } from '@angular/router'; +import { filter } from 'rxjs/operators'; +import { NavigationHistoryService } from './services'; @Component({ selector: 'app-root', @@ -9,11 +13,41 @@ export class AppComponent implements OnInit { title: string; sidebarOpened: boolean; + userDropdownIsOpened = false; constructor( - ) {} + private _userService: UserService, + private _router: Router, + private _navigationHistoryService: NavigationHistoryService, + ) { } ngOnInit(): void { - this.sidebarOpened = false; + this.sidebarOpened = true; + + // Activate navigation history + this._router.events.pipe( + filter(e => e instanceof NavigationEnd), + ).subscribe((e) => { + this._navigationHistoryService.add(e['urlAfterRedirects']); + }); + } + + get loggedInUserFullname() { + return this._userService.user ? `${this._userService.user.firstName} ${this._userService.user.lastName}` : null; + } + + loggout() { + console.log('blabla') + this._userService.resetAuth(); + this.closeUserDropdown(); + this._router.navigate(['/login']); + } + + toggleUserDropdown() { + this.userDropdownIsOpened = !this.userDropdownIsOpened; + } + + closeUserDropdown() { + this.userDropdownIsOpened = false; } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 2b577c9af1411a17305f79881a6ec74139773d22..4a6a4d926204b5947fcf8eb44bdc0500e501ccbc 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -10,6 +10,7 @@ import { AppServices, AppConfigService } from './services'; import { AppComponents } from './components'; import { UserModule } from './user/user.module'; import { UserService } from './user/services'; +import { AppDirectives } from './directives'; // Function used by APP_INITIALIZER before the app start: init user info / statut (expect a promise) export function initUserService(authService: UserService) { @@ -34,6 +35,7 @@ export function initAppConfig(appConfigService: AppConfigService) { declarations: [ AppComponent, ...AppComponents, + ...AppDirectives, ], imports: [ AppRoutingModule, diff --git a/src/app/app.routing.module.ts b/src/app/app.routing.module.ts index bd1e3dd11913c815ebccfe6c0719eba08ce5990e..dff4741e385756beb9e7213beb6d99fb379cf584 100644 --- a/src/app/app.routing.module.ts +++ b/src/app/app.routing.module.ts @@ -1,4 +1,3 @@ -import { WelcomeComponent } from './components/welcome/welcome.component'; import { RouterModule, Routes } from '@angular/router'; import { OrganizationsComponent } from './components/organizations/list/organizations.component'; import { NgModule } from '@angular/core'; @@ -18,42 +17,39 @@ import { LogsHomeComponent } from './components/logs-dashboard/logs-home/logs-ho const appRoutes: Routes = [ { path: '', - component: WelcomeComponent, - canActivate: [AuthenticatedGuard], - data: { - title: 'Bienvenue', - }, + pathMatch: 'full', + redirectTo: 'producteur-de-donnees', }, { - path: 'organizations', + path: 'producteur-de-donnees', component: OrganizationsComponent, canActivate: [AuthenticatedGuard], data: { - title: 'Organisations', + title: 'Producteur de donnees', }, }, { - path: 'organizations/new', + path: 'producteur-de-donnees/new', component: OrganizationFormComponent, canActivate: [AuthenticatedGuard], data: { - title: 'Nouvelle organisation', + title: 'Nouveau producteur de données', }, }, { - path: 'organizations/:id/edit', + path: 'producteur-de-donnees/:id/edit', component: OrganizationFormComponent, canActivate: [AuthenticatedGuard], data: { - title: 'Modifier l\'organisation', + title: 'Modifier le producteur de données', }, }, { - path: 'organizations/:id', + path: 'producteur-de-donnees/:id', component: OrganizationDetailComponent, canActivate: [AuthenticatedGuard], data: { - title: 'Organisation', + title: 'Detail du producteur de données', }, }, { @@ -85,7 +81,7 @@ const appRoutes: Routes = [ component: ResourceDetailComponent, canActivate: [AuthenticatedGuard], data: { - title: 'Ressource', + title: 'Detail de la ressource', }, }, { @@ -117,7 +113,7 @@ const appRoutes: Routes = [ component: FormatDetailComponent, canActivate: [AuthenticatedGuard], data: { - title: 'Format', + title: 'Detail du format', }, }, { diff --git a/src/app/components/formats/detail/format-detail.component.html b/src/app/components/formats/detail/format-detail.component.html index 064569e10d7d0053978eb236b6998d0667cefdc2..4e96e4bc012d215778937107ef3595f4fb4932da 100644 --- a/src/app/components/formats/detail/format-detail.component.html +++ b/src/app/components/formats/detail/format-detail.component.html @@ -1,30 +1,27 @@ -<ng-container *ngIf="format"> +<section class="section page-container" *ngIf="format"> + <app-page-header [pageInfo]="{title: title}"></app-page-header> - <app-back-button [route]="'/formats'" [title]="'Retourner à la liste des formats'"></app-back-button> - - <section class="section"> - <div class="columns is-centered"> - <div class="column is-8"> - <div class="card"> - <header class="card-header"> - <p class="card-header-title has-text-centered"> - {{format.name}} + <div class="columns is-centered"> + <div class="column is-8"> + <div class="card"> + <header class="card-header"> + <p class="card-header-title has-text-centered"> + {{format.name}} + </p> + </header> + <div class="card-content"> + <div class="content"> + <p> + <span class="has-text-weight-bold">Id: </span> + <span>{{format.id}}</span> + </p> + <p> + <span class="has-text-weight-bold">Type MapServer: </span> + <span>{{format.mapServerType}}</span> </p> - </header> - <div class="card-content"> - <div class="content"> - <p> - <span class="has-text-weight-bold">Id: </span> - <span>{{format.id}}</span> - </p> - <p> - <span class="has-text-weight-bold">Type MapServer: </span> - <span>{{format.mapServerType}}</span> - </p> - </div> </div> </div> </div> </div> - </section> -</ng-container> \ No newline at end of file + </div> +</section> \ No newline at end of file diff --git a/src/app/components/formats/detail/format-detail.component.ts b/src/app/components/formats/detail/format-detail.component.ts index 52b8bf8ce53e15d473d61a1639262df97609b87b..2cff26276db89db09a1c436cb6534c7c18706715 100644 --- a/src/app/components/formats/detail/format-detail.component.ts +++ b/src/app/components/formats/detail/format-detail.component.ts @@ -13,6 +13,7 @@ import { Format } from 'src/app/models/format.model'; export class FormatDetailComponent implements OnInit { format: Format; + title: string; constructor( private _route: ActivatedRoute, @@ -21,6 +22,7 @@ export class FormatDetailComponent implements OnInit { } ngOnInit(): void { + this.title = this._route.snapshot.data.title; this._route.paramMap.pipe( switchMap((params: ParamMap) => this._formatService.findById(params.get('id')))) .subscribe((format: Format) => this.format = format); diff --git a/src/app/components/formats/edit/format-form.component.html b/src/app/components/formats/edit/format-form.component.html index dc9588a857641d4e0dcaff8b6343ca0fc2d66df4..b4042a8203e825f720cbfaad75bd7c9c16edfa30 100644 --- a/src/app/components/formats/edit/format-form.component.html +++ b/src/app/components/formats/edit/format-form.component.html @@ -1,8 +1,5 @@ -<ng-container *ngIf="format"> - - <app-back-button [route]="'/formats'" [title]="'Retourner à la liste des formats'"></app-back-button> - - <h1>{{ title }}</h1> +<section class="section page-container" *ngIf="format"> + <app-page-header [pageInfo]="{title: title}"></app-page-header> <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless"> <div class="column is-7"> @@ -38,4 +35,4 @@ </div> </div> </form> -</ng-container> \ No newline at end of file +</section> \ No newline at end of file diff --git a/src/app/components/formats/list/formats.component.html b/src/app/components/formats/list/formats.component.html index 9bb68d7be561e6f54b47659456ac6e85f63b8a16..21681fd61701b3a8b2360be75b1c6a71631eaed4 100644 --- a/src/app/components/formats/list/formats.component.html +++ b/src/app/components/formats/list/formats.component.html @@ -1,59 +1,68 @@ -<ng-container *ngIf="formats"> - <div> - <div class="section"> - <div class="columns is-centered is-marginless"> - <div class="column has-text-centered"> - <h2>{{ totalElement }} formats trouvés</h2> - </div> - </div> - <div class="add-item has-text-right"> - <a class="button button-gl" [routerLink]="['new']"> - Ajouter - </a> +<section class="section page-container" *ngIf="formats"> + <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header> + <div class="add-item-link has-text-right"> + <a class="button button-gl" [routerLink]="['new']"> + Ajouter + </a> + </div> + <div class="table entity-list-table"> + <div class="header columns is-marginless"> + <div class="column is-2"> + <span (click)="sortBy('name')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span> + </span> </div> - <div class="table"> - <div class="header columns is-marginless"> - <div class="column is-2 has-text-centered"> - <span (click)="sortBy('name')" class="is-sortable"> - <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span> - <span *ngIf="sortOptions.value === 'name'" class="has-text-danger"> - <i class="fas sort-order-icon" - [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> - </span> + <div class="column is-2"> + <span (click)="sortBy('mapServerType')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'mapServerType' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'mapServerType' && sortOptions.order === 'asc'}"></i> </span> - </div> - <div class="column is-2 has-text-centered"> - <span class="column-title">Type MapServer</span> - </div> - <div class="column is-offset-7 is-1 has-text-centered"> - <span class="column-title">Actions</span> - </div> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === mapServerType}">Type + MapServer</span> + </span> + </div> + <div class="column is-offset-7 is-1 has-text-centered"> + <span class="column-title">Actions</span> + </div> + </div> + <div class="data-list"> + <div class="data columns is-multiline is-vcentered is-marginless" + *ngFor="let format of formats; let i=index; let odd=odd; let even=even;" [ngClass]="{ odd: odd, even: even }"> + <div class="column is-2"> + <span>{{ format.name}}</span> </div> - <div class="data-list"> - <div class="data columns is-multiline is-vcentered is-marginless" - *ngFor="let format of formats; let i=index; let odd=odd; let even=even;" - [ngClass]="{ odd: odd, even: even }"> - <div class="column is-2 has-text-centered"> - {{ format.name}} - </div> - <div class="column is-2 has-text-centered"> - {{ format.mapServerType}} - </div> - <div class="column is-offset-7 is-1 has-text-centered actions"> - <app-crud-buttons [id]="format.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> - </div> - </div> + <div class="column is-2"> + <span>{{ format.mapServerType}}</span> </div> - <div class="columns is-marginless"> - <div class="column"> - <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" - [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" - [showFirstLastButtons]="true" (page)="changePagination($event)" - (pageSizeChanged)="changePageSize($event)"> - </app-paginator> - </div> + <div class="column is-offset-7 is-1 has-text-centered actions"> + <app-crud-buttons [id]="format.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> </div> </div> </div> + <div class="columns is-marginless paginator"> + <div class="column"> + <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" + [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" + [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)"> + </app-paginator> + </div> + </div> </div> -</ng-container> +</section> \ No newline at end of file diff --git a/src/app/components/formats/list/formats.component.scss b/src/app/components/formats/list/formats.component.scss index 087d2f4242ed39d8f3505d06012bf823f015572e..8b137891791fe96927ad78e64b0aad7bded08bdc 100644 --- a/src/app/components/formats/list/formats.component.scss +++ b/src/app/components/formats/list/formats.component.scss @@ -1,53 +1 @@ -section { - padding: 20px; -} -.section { - padding-top: 0; -} -.table { - background-color: white; - border: 1px solid lightgray; -} - -.center { - text-align: center; -} - -img { - max-width: 100px; -} - -.header { - border-bottom: 1px solid lightgray; - background-color: #fafafa; - span { - color: #999; - - } -} - -.is-sortable .column-title { - cursor: pointer; -} - -.table .columns { - border-bottom: 1px solid lightgray -} - -i { - - &.title-icon { - margin-right: 0.75rem; - } - - &.sort-order-icon { - padding-left: 0.75rem; - cursor: pointer; - } -} - - -.add-item { - margin-bottom: 20px; -} diff --git a/src/app/components/formats/list/formats.component.ts b/src/app/components/formats/list/formats.component.ts index 0efba29653deeaa91189cc967d4cfe9a8edea5bb..950708a57bc2a87bbf015edbf48ff40fb99248a6 100644 --- a/src/app/components/formats/list/formats.component.ts +++ b/src/app/components/formats/list/formats.component.ts @@ -3,6 +3,7 @@ import { FormatService } from 'src/app/services/format.service'; import { Subscription } from 'rxjs'; import { PaginatorOptions } from 'src/app/models/paginator-options.model'; import { Format, FormatRO } from 'src/app/models/format.model'; +import { IPageHeaderInfo } from '../../../models/page.model'; @Component({ selector: 'app-formats', @@ -11,6 +12,9 @@ import { Format, FormatRO } from 'src/app/models/format.model'; }) export class FormatsComponent implements OnInit { + pageHeaderInfo: IPageHeaderInfo = { + title: '', + }; formats: Format[]; searchChangeSub: Subscription; @@ -58,6 +62,8 @@ export class FormatsComponent implements OnInit { this.formats = items.formats; this.totalElement = items.totalCount; + this.pageHeaderInfo.title = `${this.totalElement} formats trouvés`; + this.paginator.limit = this.formatsService.limit; this.paginator.pageIndex = this.formatsService.pageNumber; this.paginator.length = items.totalCount; diff --git a/src/app/components/image-upload/image-upload.component.html b/src/app/components/image-upload/image-upload.component.html index 72e3400a5b9ffda47784a569bdcaca09f18d99ff..acf76a9096674091801f6144bfb7a73773f005d3 100644 --- a/src/app/components/image-upload/image-upload.component.html +++ b/src/app/components/image-upload/image-upload.component.html @@ -1,5 +1,6 @@ <div class="field"> - <label class="label" [for]="fieldParams.inputName">{{ fieldParams.label }}</label> + <label class="label" [ngClass]="{'required': fieldParams.isRequired}" + [for]="fieldParams.inputName">{{ fieldParams.label }}</label> <div class="image-preview-container" *ngIf="fieldParams.existingImageUrl && !removeImageBtnClicked"> <button class="button" (click)="removeImage()"><i class="far fa-times-circle"></i></button> <img [src]="fieldParams.existingImageUrl" alt="" class="image-preview"> diff --git a/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts index e4edb3bc4ea696613b45ac5930233d9a2d6670ad..6fbfd987845cef001860c9c5fe1900073bd45f1c 100644 --- a/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts +++ b/src/app/components/logs-dashboard/logs-sessions/logs-sessions.component.ts @@ -29,14 +29,14 @@ export class LogsSessionsComponent implements OnInit { getProcessTimeAggr (sessionId) { this.dataLogsService.getProcessTimeAggr(sessionId).subscribe((result) => { console.log(result[0]); - this.allSessionsIdInfo.push(result[0]); + this.allSessionsIdInfo = result[0]; }, ); } getAllUniqueFields() { this.dataLogsService.getAllUniqueFields().subscribe((result) => { this.childSessionsList = result[0]['session_id_list']; - for (let i = 0; i < this.childNbSessions; i++) { + for (let i = 0; i < this.childNbSessions; i += 1) { this.getProcessTimeAggr(this.childSessionsList[i]); } console.log('allSessionsIdInfo: ', this.allSessionsIdInfo); diff --git a/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts index 5ec5335d23f25e281dc6c9eefce900ecdea08d4a..a149b61f02acfeb8b7e55bf7511f81c04aa38821 100644 --- a/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts +++ b/src/app/components/logs-dashboard/logs-slugs/logs-slugs.component.ts @@ -34,14 +34,14 @@ export class LogsSlugsComponent implements OnInit { getProcessTimeAggr (sessionId) { this.dataLogsService.getProcessTimeAggr(sessionId).subscribe((result) => { console.log(result[0]); - this.allSessionsIdInfo.push(result[0]); + this.allSessionsIdInfo = result[0]; }, ); } getAllUniqueFields() { this.dataLogsService.getAllUniqueFields().subscribe((result) => { this.childSlugsList = result[0]['slug_list']; - for (let i = 0; i < this.childNbSlugs; i++) { + for (let i = 0; i < this.childNbSlugs; i += 1) { this.getProcessTimeAggr(this.childSlugsList[i]); } console.log('allSessionsIdInfo: ', this.allSessionsIdInfo); diff --git a/src/app/components/menu/menu.component.html b/src/app/components/menu/menu.component.html index d20095e82abc8836a8486b4df703e7db18a44bd8..664b9b151671039377b2566ed9198ab1514406de 100644 --- a/src/app/components/menu/menu.component.html +++ b/src/app/components/menu/menu.component.html @@ -1,32 +1,30 @@ - <aside class="menu" [ngClass]="{'expanded': expanded}"> <ul class="menu-list"> - <li><a [routerLink]="['/']" routerLinkActive="active-link" [routerLinkActiveOptions]="{ exact: true }"> - <span class="icon"> - <i class="fas fa-home"></i> + <li><a [routerLink]="['/', 'producteur-de-donnees']" routerLinkActive="active-link"> + <span class="icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23"> + <g id="picto_x5F_acteur"> + <path + d="M16.2 12.8c1.1-.8 1.8-2 1.8-3.3 0-2.2-1.8-4-4-4s-4 1.8-4 4c0 1.4.7 2.6 1.8 3.3-3 1-5.2 3.8-5.2 7.1 0 .3.2.5.5.5s.5-.2.5-.5c0-3.6 2.9-6.5 6.5-6.5s6.5 2.9 6.5 6.5c0 .3.2.5.5.5s.5-.2.5-.5c-.1-3.4-2.3-6.2-5.4-7.1zm-5.3-3.3c0-1.6 1.4-3 3-3s3 1.3 3 3-1.4 3-3 3-3-1.4-3-3zM7.9 9.5c0-.4 0-.8.1-1.2-1.2-.4-2.1-1.5-2.1-2.8 0-1.6 1.4-3 3-3 1.1 0 2.1.6 2.6 1.4.3-.1.6-.2 1-.3-.7-1.3-2-2.1-3.6-2.1-2.2 0-4 1.8-4 4 0 1.4.7 2.6 1.8 3.3-3 1-5.2 3.8-5.2 7.1 0 .3.2.5.5.5s.5-.2.5-.5c0-3.2 2.4-5.8 5.4-6.4z" + class="main" /> + </g> + </svg> </span> - <span class="label-menu">Dashboard</span> - </a> - </li> - <li><a [routerLink]="['/', 'organizations']" routerLinkActive="active-link"> - <span class="icon"> - <i class="fas fa-city"></i> - </span> - <span class="label-menu">Organisations</span> + <span class="label-menu">Producteurs de données</span> </a> </li> <li><a [routerLink]="['/', 'resources']" routerLinkActive="active-link"> - <span class="icon"> + <span class="icon"> <i class="fas fa-tint"></i> </span> - <span class="label-menu">Ressources</span> + <span class="label-menu">Ressources</span> </a> </li> <li><a [routerLink]="['/', 'formats']" routerLinkActive="active-link"> - <span class="icon"> + <span class="icon"> <i class="far fa-file"></i> </span> - <span class="label-menu">Formats</span> + <span class="label-menu">Formats</span> </a> </li> <li><a [routerLink]="['/', 'datalogs']" routerLinkActive="active-link"> @@ -37,4 +35,4 @@ </a> </li> </ul> -</aside> +</aside> \ No newline at end of file diff --git a/src/app/components/menu/menu.component.scss b/src/app/components/menu/menu.component.scss index a3172b6b6b674406c5a483a2355a059682eae417..abe3f4fa0a50204dbd6df6b16495961d7f709354 100644 --- a/src/app/components/menu/menu.component.scss +++ b/src/app/components/menu/menu.component.scss @@ -1,15 +1,20 @@ +@import '../../../scss/variables.scss'; + .menu { padding: 0px; - padding-top: 20px; span { color: white; } + .icon svg { + stroke: white; + width: 1.2rem; + } + a { - white-space: nowrap; - overflow-x: hidden; - overflow-y: hidden; + display: flex; + align-items: center; } .label-menu { @@ -21,13 +26,6 @@ } .menu.expanded { - - a { - white-space: nowrap; - overflow-x: hidden; - overflow-y: hidden; - } - .label-menu { padding-left: 10px; opacity: 1; @@ -40,10 +38,28 @@ li a { border-left: 3px solid #333745; - padding-left: calc(0.75em - 3px) ; - + padding: 1rem calc(1rem - 3px); + &.active-link { - border-color:#d5232a; + background-color: $background-link-hover-color; + border-color: #d5232a; + + span { + color: $menu-selected-color; + } + + .icon svg { + stroke: $menu-selected-color; + } + } + + &:hover { + color: white; + background-color: $background-link-hover-color; + } + + &:hover:not(.active-link) { + border-left: 3px solid $background-link-hover-color; } -} +} diff --git a/src/app/components/menu/menu.component.ts b/src/app/components/menu/menu.component.ts index 66e6a52ad33786ea8f4d382374ee20d4e036ea5d..013bf9db3fe7e743020aa79ae62774269f659b9d 100644 --- a/src/app/components/menu/menu.component.ts +++ b/src/app/components/menu/menu.component.ts @@ -11,8 +11,7 @@ export class MenuComponent { @Input() expanded: boolean; - constructor( - ) { + constructor() { } } diff --git a/src/app/components/organizations/detail/organization-detail.component.html b/src/app/components/organizations/detail/organization-detail.component.html index 3d17686ee4cdaecaee0b39828686063c00b39507..fcde85111da378bd3530e34756395e9fa74f2396 100644 --- a/src/app/components/organizations/detail/organization-detail.component.html +++ b/src/app/components/organizations/detail/organization-detail.component.html @@ -1,52 +1,43 @@ -<ng-container *ngIf="organization"> - - <app-back-button [route]="'/organizations'" [title]="'Retourner à la liste des organisations'"></app-back-button> - - <section class="section"> - <div class="columns is-centered"> - <div class="column is-8"> - <div class="card"> - <header class="card-header"> - <p class="card-header-title has-text-centered"> - {{organization.name}} - </p> - </header> - <div class="card-image"> - <figure class="image"> - <img [src]="organization.logo" alt="Logo de l'organization"> - </figure> - </div> - <div class="card-content"> - - <div class="content"> - <div> - <p>{{organization.description}}</p> - </div> - <br> - <div> - <p><span class="has-text-weight-bold">Id:</span> {{ organization.id}}</p> - </div> - <br> - <div> - <p class="has-text-weight-bold">Nom ElasticSearch de l'organisation:</p> - <p>{{ organization.elasticSearchName}}</p> - </div> - <br> - <div> - <p class="has-text-weight-bold">Liens</p> - <p *ngFor="let link of organization.links"> - <a href="{{ link.url }}" target="_blank">{{link.url}}</a> - </p> - </div> +<section class="section page-container" *ngIf="organization"> + <app-page-header [pageInfo]="{title: title}"></app-page-header> + <div class="columns is-centered"> + <div class="column is-8"> + <div class="card"> + <header class="card-header"> + <p class="card-header-title has-text-centered"> + {{organization.name}} + </p> + </header> + <div class="card-image"> + <figure class="image"> + <img [src]="organization.logo" alt="Logo du producteur de données"> + </figure> + </div> + <div class="card-content"> + <div class="content"> + <div> + <p>{{organization.description}}</p> + </div> + <br> + <div> + <p><span class="has-text-weight-bold">Id:</span> {{ organization.id}}</p> + </div> + <br> + <div> + <p class="has-text-weight-bold">Nom ElasticSearch de l'organisation:</p> + <p>{{ organization.elasticSearchName}}</p> + </div> + <br> + <div> + <p class="has-text-weight-bold">Liens</p> + <p *ngFor="let link of organization.links"> + <a href="{{ link.url }}" target="_blank">{{link.url}}</a> + </p> </div> </div> </div> </div> - </div> - - - </section> - -</ng-container> \ No newline at end of file + </div> +</section> \ No newline at end of file diff --git a/src/app/components/organizations/detail/organization-detail.component.ts b/src/app/components/organizations/detail/organization-detail.component.ts index 459164c4e696a5965f18fc7adece80e6c8c6165a..b0f291aee1562aeb3824a1c3468ab5fbbe4920ab 100644 --- a/src/app/components/organizations/detail/organization-detail.component.ts +++ b/src/app/components/organizations/detail/organization-detail.component.ts @@ -14,16 +14,18 @@ import { OrganizationService } from 'src/app/services/organization.service'; export class OrganizationDetailComponent implements OnInit { organization: Organization; + title: string; constructor( - private route: ActivatedRoute, - private organizationService: OrganizationService, + private _route: ActivatedRoute, + private _organizationService: OrganizationService, ) { } ngOnInit(): void { - this.route.paramMap.pipe( - switchMap((params: ParamMap) => this.organizationService.findById(params.get('id')))) + this.title = this._route.snapshot.data.title; + this._route.paramMap.pipe( + switchMap((params: ParamMap) => this._organizationService.findById(params.get('id')))) .subscribe((organization: Organization) => this.organization = organization); } } diff --git a/src/app/components/organizations/edit/organization-form.component.html b/src/app/components/organizations/edit/organization-form.component.html index d00a3415c96010844093813d85249076880e9878..dc8ea2d64fa1c4dad0e86a157b1f452201085088 100644 --- a/src/app/components/organizations/edit/organization-form.component.html +++ b/src/app/components/organizations/edit/organization-form.component.html @@ -1,8 +1,5 @@ -<ng-container *ngIf="organization"> - - <app-back-button [route]="'/organizations'" [title]="'Retourner à la liste des organisations'"></app-back-button> - - <h1>{{ title }}</h1> +<section class="section page-container" *ngIf="organization"> + <app-page-header [pageInfo]="{title: title}"></app-page-header> <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless"> <div class="column is-7"> @@ -15,7 +12,7 @@ </div> <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger"> <p *ngIf="name.errors['required']" class="help is-danger"> - Le nom de l'organisation est obligatoire. + Le nom du producteur de données est obligatoire. </p> </div> </div> @@ -33,13 +30,13 @@ </div> <div *ngIf="description.invalid && (description.dirty || description.touched)" class="alert alert-danger"> <p *ngIf="description.errors['required']" class="help is-danger"> - La description de l'organisation est obligatoire. + La description du producteur de données est obligatoire. </p> </div> </div> <div class="field"> - <label class="label" for="elasticSearchName">Nom ElasticSearch de l'organisation</label> + <label class="label" for="elasticSearchName">Nom ElasticSearch du producteur de données</label> <div class="control"> <input class="input" type="text" [value]="organization.elasticSearchName" formControlName="elasticSearchName" id="elasticSearchName"> @@ -87,4 +84,4 @@ </div> </div> </form> -</ng-container> \ No newline at end of file +</section> \ No newline at end of file diff --git a/src/app/components/organizations/edit/organization-form.component.ts b/src/app/components/organizations/edit/organization-form.component.ts index 6952b5acf2765a670c81d8a822bc13353d8917a8..b8a22a5f7fee592f7a91fbfb7863edc7f4d49832 100644 --- a/src/app/components/organizations/edit/organization-form.component.ts +++ b/src/app/components/organizations/edit/organization-form.component.ts @@ -21,6 +21,7 @@ export class OrganizationFormComponent implements OnInit { inputName: 'logo', label: 'Logo', existingImageUrl: null, + isRequired: true, }; logo: File; title: string; @@ -125,14 +126,14 @@ export class OrganizationFormComponent implements OnInit { action.subscribe( (organizationCreated) => { this._notificationService.notify({ - message: 'L\'organisation a été créée avec succès.', + message: 'Le producteur de données a été créée avec succès.', type: 'success', }); - this._router.navigate(['/organizations', organizationCreated.id]); + this._router.navigate(['/producteur-de-donnees', organizationCreated.id]); }, () => { this._notificationService.notify({ - message: 'Une erreur est survenue lors de la création de l\'organisation.', + message: 'Une erreur est survenue lors de la création du producteur de données.', type: 'error', }); }, diff --git a/src/app/components/organizations/list/organizations.component.html b/src/app/components/organizations/list/organizations.component.html index cd06cbfebf61d7545861c6e923a256096f5ff6e3..f30fe6a126772c926a0eefc3db29202345127685 100644 --- a/src/app/components/organizations/list/organizations.component.html +++ b/src/app/components/organizations/list/organizations.component.html @@ -1,73 +1,83 @@ -<ng-container *ngIf="organizations"> - <div> - <div class="section"> - <div class="columns is-centered is-marginless"> - <div class="column has-text-centered"> - <h2>{{ totalElement }} organisations trouvées</h2> - </div> +<div class="section page-container" *ngIf="organizations"> + <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header> + <div class="add-item-link has-text-right"> + <a class="button button-gl" [routerLink]="['new']"> + Ajouter + </a> + </div> + <div class="table entity-list-table"> + <div class="header columns is-marginless"> + <div class="column is-2"> + <span (click)="sortBy('name')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span> + </span> + </div> + <div class="column is-2 has-text-centered"> + <span class="column-title">Logo</span> + </div> + <div class="column is-4"> + <span (click)="sortBy('description')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title">Description</span> + </span> </div> - <div class="add-item has-text-right"> - <a class="button button-gl" [routerLink]="['new']"> - Ajouter - </a> + <div class="column is-3"> + <span class="column-title">Liens</span> </div> - <div class="table"> - <div class="header columns is-marginless"> - <div class="column is-2 has-text-centered"> - <span (click)="sortBy('name')" class="is-sortable"> - <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Name</span> - <span *ngIf="sortOptions.value === 'name'" class="has-text-danger"> - <i class="fas sort-order-icon" [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> - </span> - </span> - </div> - <div class="column is-2 has-text-centered"> - <span class="column-title">Logo</span> - </div> - <div class="column is-4 has-text-centered"> - <span class="column-title">Description</span> - </div> - <div class="column is-3 has-text-centered"> - <span class="column-title">Liens</span> - </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Actions</span> - </div> - </div> - <div class="data-list"> - <div class="data columns is-multiline is-vcentered is-marginless" - *ngFor="let organization of organizations; let i=index; let odd=odd; let even=even;" - [ngClass]="{ odd: odd, even: even }"> - <div class="column is-2 has-text-centered"> - {{ organization.name}} - </div> - <div class="column is-2 has-text-centered"> - <img src="{{organization.logo}}" alt=""> - </div> - <div class="column is-4 has-text-justified"> - {{ organization.description | slice:0:200}}... - </div> - <div class="column is-3 has-text-centered"> - <p *ngFor="let link of organization.links"> - <a href="{{ link.url }}" target="_blank">{{link.url}}</a> - </p> - </div> - <div class="column is-1 has-text-centered actions"> - <app-crud-buttons [id]="organization.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> - </div> - - </div> - </div> - <div class="columns is-marginless"> - <div class="column"> - <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" - [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" - [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)"> - </app-paginator> - </div> - </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Actions</span> </div> + </div> + <div class="data-list"> + <div class="data columns is-multiline is-vcentered is-marginless" + *ngFor="let organization of organizations; let i=index; let odd=odd; let even=even;" + [ngClass]="{ odd: odd, even: even }"> + <div class="column is-2"> + <span>{{ organization.name}}</span> + </div> + <div class="column is-2 has-text-centered"> + <img src="{{organization.logo}}" alt=""> + </div> + <div class="column is-4"> + <span>{{ organization.description | slice:0:200}}...</span> + </div> + <div class="column is-3"> + <p *ngFor="let link of organization.links"> + <a href="{{ link.url }}" target="_blank">{{link.url}}</a> + </p> + </div> + <div class="column is-1 has-text-centered actions"> + <app-crud-buttons [id]="organization.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> + </div> + </div> + </div> + <div class="columns is-marginless paginator"> + <div class="column"> + <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" + [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" + [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)"> + </app-paginator> + </div> </div> </div> -</ng-container> +</div> \ No newline at end of file diff --git a/src/app/components/organizations/list/organizations.component.scss b/src/app/components/organizations/list/organizations.component.scss index 087d2f4242ed39d8f3505d06012bf823f015572e..d1ab57677f61171791f67b163f6b933ef1dd4a41 100644 --- a/src/app/components/organizations/list/organizations.component.scss +++ b/src/app/components/organizations/list/organizations.component.scss @@ -1,53 +1,3 @@ -section { - padding: 20px; -} -.section { - padding-top: 0; -} - -.table { - background-color: white; - border: 1px solid lightgray; -} - -.center { - text-align: center; -} - img { max-width: 100px; } - -.header { - border-bottom: 1px solid lightgray; - background-color: #fafafa; - span { - color: #999; - - } -} - -.is-sortable .column-title { - cursor: pointer; -} - -.table .columns { - border-bottom: 1px solid lightgray -} - -i { - - &.title-icon { - margin-right: 0.75rem; - } - - &.sort-order-icon { - padding-left: 0.75rem; - cursor: pointer; - } -} - - -.add-item { - margin-bottom: 20px; -} diff --git a/src/app/components/organizations/list/organizations.component.ts b/src/app/components/organizations/list/organizations.component.ts index 60020afb5dd4c14df58fccfe84cb081347bc9620..c2c4551db3b71a1ab7a5903a0b57b62760762ad7 100644 --- a/src/app/components/organizations/list/organizations.component.ts +++ b/src/app/components/organizations/list/organizations.component.ts @@ -3,6 +3,7 @@ import { Organization, OrganizationRO } from 'src/app/models/organization.model' import { OrganizationService } from 'src/app/services/organization.service'; import { Subscription } from 'rxjs'; import { PaginatorOptions } from 'src/app/models/paginator-options.model'; +import { IPageHeaderInfo } from '../../../models/page.model'; @Component({ selector: 'app-organizations', @@ -11,6 +12,9 @@ import { PaginatorOptions } from 'src/app/models/paginator-options.model'; }) export class OrganizationsComponent implements OnInit { + pageHeaderInfo: IPageHeaderInfo = { + title: '', + }; organizations: Organization[]; searchChangeSub: Subscription; @@ -58,6 +62,8 @@ export class OrganizationsComponent implements OnInit { this.organizations = items.organizations; this.totalElement = items.totalCount; + this.pageHeaderInfo.title = `${this.totalElement} producteurs de données trouvés`; + this.paginator.limit = this.organizationsService.limit; this.paginator.pageIndex = this.organizationsService.pageNumber; this.paginator.length = items.totalCount; @@ -88,7 +94,7 @@ export class OrganizationsComponent implements OnInit { } displayDeletePopup(organizationId) { - const pop = confirm('Etes vous sûr de vouloir supprimer cette organisation ?'); + const pop = confirm('Etes vous sûr de vouloir supprimer ce producteur de données ?'); if (pop) { this.organizationsService.delete(organizationId).subscribe(() => { this.organizationsService.pageNumber = 1; diff --git a/src/app/components/page-header/page-header.component.html b/src/app/components/page-header/page-header.component.html new file mode 100644 index 0000000000000000000000000000000000000000..fa2b49d223e9f3932a5e7273e68c800008000196 --- /dev/null +++ b/src/app/components/page-header/page-header.component.html @@ -0,0 +1,21 @@ +<div class="page-header"> + <div class="back-button" (click)="goToPreviousPage()" + [ngClass]="{'has-beta-style': pageInfo.hasBetaStyle, 'hidden': hideBackButton}"> + <img class="icon-left-arrow" src="./assets/img/left_arrow.svg" alt="Picto de la flèche retour"> + <span>Retour</span> + </div> + + <div class="page-title" [ngClass]="{'has-surtitle': pageInfo.surtitle}"> + <div class="surtitle-item" *ngIf="pageInfo.surtitle"> + <span>{{ pageInfo.surtitle }}</span> + </div> + <div class="title-item"> + {{ pageInfo.title }} <span class="metadataSubtitle" *ngIf="pageInfo.metadataSubtitle"> - + {{ pageInfo.metadataSubtitle }} </span> + </div> + + <div class="subtitle-item" *ngIf="pageInfo.subtitle"> + <span>{{ pageInfo.subtitle }}</span> + </div> + </div> +</div> \ No newline at end of file diff --git a/src/app/components/page-header/page-header.component.scss b/src/app/components/page-header/page-header.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..f2646b90d894da46aae7a30c9d66fe68e26b256d --- /dev/null +++ b/src/app/components/page-header/page-header.component.scss @@ -0,0 +1,88 @@ +@import '../../../scss/variables.scss'; +@import '../../../../node_modules/bulma/sass/utilities/_all.sass'; + +.page-header { + display: flex; + padding-top: 1.875rem; + padding-bottom: 1.875rem; +} + +.back-button { + color: $grey-dark-color; + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + padding: 1.125rem 1rem 1.125rem 0; + width: 6rem; + white-space: nowrap; + align-items: center; + align-self: flex-start; + flex-shrink: 0; + justify-content: center; + // Hide back button for mobile + display: none; + + &.hidden { + display: none; + } + + @media screen and (min-width: $tablet) { + display: flex; + } + + .icon-left-arrow { + margin-right: 0.5rem; + } + + &:hover { + text-decoration: underline; + } +} + +.page-title { + align-self: center; + padding-left: 1rem; + + border-left: 4px solid $tomato-color; + + &.has-beta-style { + border-left-color: $yellow-dd-color; + } +} + +.page-title.has-surtitle { + display: flex; + flex-direction: column; + justify-content: space-between; + align-self: unset; +} + +.surtitle-item span { + margin-bottom: 0.5rem; + font-size: 0.75rem; + text-transform: uppercase; + color: $grey-dark-color; + line-height: 1; + vertical-align: text-top; +} + +.subtitle-item span { + margin-top: 0.5rem; + font-size: 1rem; + color: $grey-dark-color; + line-height: 1; + vertical-align: text-bottom; +} + +.metadataSubtitle { + color: $grey-dark-color; + font-weight: 300; + font-size: inherit; +} + +.title-item { + font-size: 1.75rem; + font-weight: bold; + line-height: 1.15; + color: $brand-color; +} diff --git a/src/app/components/page-header/page-header.component.spec.ts b/src/app/components/page-header/page-header.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..21189ddf954f43c25ddee7be4bfb6b9724f9e3bc --- /dev/null +++ b/src/app/components/page-header/page-header.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PageHeaderComponent } from './page-header.component'; + +describe('PageHeaderComponent', () => { + let component: PageHeaderComponent; + let fixture: ComponentFixture<PageHeaderComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PageHeaderComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PageHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/page-header/page-header.component.ts b/src/app/components/page-header/page-header.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d824ad8570f807af5ec8d8e5de919426a609ce4 --- /dev/null +++ b/src/app/components/page-header/page-header.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Router } from '@angular/router'; +import { NavigationHistoryService } from '../../services'; +import { IPageHeaderInfo } from '../../models/page.model'; + +@Component({ + selector: 'app-page-header', + templateUrl: './page-header.component.html', + styleUrls: ['./page-header.component.scss'], +}) +export class PageHeaderComponent implements OnInit { + + constructor( + private _navigationHistoryService: NavigationHistoryService, + private _router: Router, + ) { } + + @Input() pageInfo: IPageHeaderInfo; + @Input() customGoToPreviousPage: any; + @Input() hideBackButton: boolean; + + ngOnInit() { + } + + goToPreviousPage() { + if (this.customGoToPreviousPage) { + this.customGoToPreviousPage(); + } else { + const index = 1; // Start to retrieve the previous element + let url = this._navigationHistoryService.getFromLast(index); + + // If url is null then redirect to home page + if (url == null) { + url = '/'; + } + this._router.navigateByUrl(url); + } + } + +} diff --git a/src/app/components/paginator/paginator.component.html b/src/app/components/paginator/paginator.component.html index 157567c077ff9adbcdd5030fc9ebf3d2898ffbec..dd5cef2baa36e77bcc7b6e4d0b78cd735587fa33 100644 --- a/src/app/components/paginator/paginator.component.html +++ b/src/app/components/paginator/paginator.component.html @@ -1,84 +1,64 @@ <div class="columns"> - <div class="column is-narrow has-text-centered"> - <!-- Dropdown list of pageSize number --> - <div class="dropdown" [ngClass]="{'is-active': pageSizeDropdownToggle}" (click)="pageSizeDropdownToggle=!pageSizeDropdownToggle"> - <div class="dropdown-trigger"> - <button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu"> - <span>{{ pageSize }}</span> - <span class="icon is-small"> - <i class="fas fa-angle-down" aria-hidden="true"></i> - </span> - </button> - </div> - <div class="dropdown-menu" id="dropdown-menu" role="menu"> - <div class="dropdown-content"> - <span role="button" - tabindex="0" - class="dropdown-item" - [ngClass]="{'is-active': nb === pageSize}" - *ngFor="let nb of pageSizeOptions" - (click)="changePageSize(nb)" - (keyup.enter)="changePageSize(nb)"> - {{ nb }} - </span> - </div> - </div> - </div> - </div> <div class="column"> - <div class="pagination is-centered is-small" role="navigation" aria-label="pagination" *ngIf="length > 0"> - <!-- First button --> - <button class="pagination-previous button" *ngIf="pageIndex === 1 || loading; else activeFirstPageLink" disabled> - <i class="fas fa-angle-double-left"></i> - </button> - <ng-template #activeFirstPageLink> - <button class="pagination-previous button" (click)="onPage(1)"> - <i class="fas fa-angle-double-left"></i> - </button> - </ng-template> - + <div class="pagination-dgl is-centered is-small" role="navigation" aria-label="pagination" *ngIf="length > 0"> <!-- Previous button --> - <button class="pagination-previous button" *ngIf="pageIndex === 1 || loading; else activePreviousLink" disabled> - <i class="fas fa-angle-left"></i> + <button class="button previous" *ngIf="pageIndex === 1 || loading; else activePreviousLink" disabled> + <svg xmlns="http://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9"> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + </svg> </button> <ng-template #activePreviousLink> - <button class="pagination-previous button" (click)="onPrev()"> - <i class="fas fa-angle-left"></i> + <button class="button previous" (click)="onPrev()"> + <svg xmlns="http://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9"> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + </svg> </button> </ng-template> <!-- List of pages number --> - <ul class="pagination-list"> + <ul> <ng-container *ngFor="let pageNum of getPages(); let i = index"> - <li *ngIf="displayEllipsis(i)"> - <span class="pagination-ellipsis">…</span> - </li> <li> - <button *ngIf="!loading; else pageNumberDisabled" (click)="onPage(pageNum)" class="pagination-link button" [ngClass]="{'is-current': pageNum === pageIndex}">{{ pageNum }}</button> + <button *ngIf="!loading; else pageNumberDisabled" (click)="onPage(pageNum)" class="button" + [ngClass]="{'is-current': pageNum === pageIndex}">{{ pageNum }}</button> <ng-template #pageNumberDisabled> - <button class="pagination-link button" [ngClass]="{'is-current': pageNum === pageIndex}" disabled>{{ pageNum }}</button> + <button class="button" [ngClass]="{'is-current': pageNum === pageIndex}" disabled>{{ pageNum }}</button> </ng-template> </li> </ng-container> </ul> <!-- Next button --> - <button class="pagination-next button" *ngIf="lastPage() || loading; else activeNextLink" disabled> - <i class="fas fa-angle-right"></i> + <button class="button" *ngIf="lastPage() || loading; else activeNextLink" disabled> + <svg xmlns="http://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9"> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + </svg> </button> <ng-template #activeNextLink> - <button class="pagination-next button" (click)="onNext()"> - <i class="fas fa-angle-right"></i> - </button> - </ng-template> - - <!-- Last button --> - <button class="pagination-next button" *ngIf="lastPage() || loading; else activeLastPageLink" disabled> - <i class="fas fa-angle-double-right"></i> - </button> - <ng-template #activeLastPageLink> - <button class="pagination-next button" (click)="onPage(totalPages())"> - <i class="fas fa-angle-double-right"></i> + <button class="button next" (click)="onNext()"> + <svg xmlns="http://www.w3.org/2000/svg" id="chevron" viewBox="0 0 15 9"> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + <path + d="M7.5 7.5c-.1 0-.3-.1-.4-.1l-6-6C1 1.1 1 .8 1.1.6s.5-.2.7 0l5.6 5.6L13 .6c.2-.2.5-.2.7 0s.2.5 0 .7l-6 6c.1.1-.1.2-.2.2z" + class="brandcolor" /> + </svg> </button> </ng-template> </div> diff --git a/src/app/components/paginator/paginator.component.scss b/src/app/components/paginator/paginator.component.scss index 37b33eca9ad0a4062c594698265b4ef71b05d2a3..a1becd90948a55e867c9a99d9d62493aaa4a9cc5 100644 --- a/src/app/components/paginator/paginator.component.scss +++ b/src/app/components/paginator/paginator.component.scss @@ -1,10 +1,49 @@ +@import "./../../../scss/variables.scss"; + .dropdown-menu { min-width: unset; } + .dropdown-content { padding-top: unset; padding-bottom: unset; } + .dropdown-item { padding: 0.3rem 1rem; -} \ No newline at end of file +} + +ul, +li { + display: inline-block; +} + +.pagination-dgl { + text-align: center; + + .button { + border: none; + color: $grey-dark-color; + padding-left: 0.5em; + padding-right: 0.5em; + margin-left: 0.25rem; + + &:hover { + text-decoration: underline; + } + + &.is-current { + color: $tomato-color; + } + } +} + +svg { + width: 13px; + fill: $grey-dark-color; + transform: rotate(-90deg); +} + +.previous svg { + transform: rotate(90deg); +} diff --git a/src/app/components/paginator/paginator.component.spec.ts b/src/app/components/paginator/paginator.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b842808244f54cb38f5b1a699512cf14220547f1 --- /dev/null +++ b/src/app/components/paginator/paginator.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PaginatorComponent } from './paginator.component'; + +describe('PaginatorComponent', () => { + let component: PaginatorComponent; + let fixture: ComponentFixture<PaginatorComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PaginatorComponent], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PaginatorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/paginator/paginator.component.ts b/src/app/components/paginator/paginator.component.ts index c0ba5eeca447e2fab2d95edf0f8b5885bdbacacf..0debb3cfb0985ef9dcbd7c8d8542880645b099d7 100644 --- a/src/app/components/paginator/paginator.component.ts +++ b/src/app/components/paginator/paginator.component.ts @@ -63,10 +63,11 @@ export class PaginatorComponent implements OnInit { getPages(): number[] { const totalP = Math.ceil(this.length / this.pageSize); // Total number of page const p = this.pageIndex || 1; // Current page index - const pagesToShow = this.pagesToShow || 5; // Total number of pages to be displayed in the paginator + const midPage = Math.round(totalP / 2); + const pagesToShow = 3; // Total number of pages to be displayed in the paginator const pages: number[] = []; // Page number to be displayed pages.push(p); // Push the current page index - let times = pagesToShow - 1; // Number of page to be added to the displayed page + let times = pagesToShow; // Number of page to be added to the displayed page if (p !== 1) { // If current page is different from the first page remove one (first page automatically // added to the array at the end of the function) @@ -110,6 +111,10 @@ export class PaginatorComponent implements OnInit { if (pages.indexOf(totalP) === -1) { pages.push(totalP); } + // Adding half page if not already in the array + if (pages.indexOf(midPage) === -1) { + pages.push(midPage); + } // Reordering the array pages.sort((a, b) => a - b); return pages; diff --git a/src/app/components/resources/detail/resource-detail.component.html b/src/app/components/resources/detail/resource-detail.component.html index 60a5b0114ad29ff66798be9b4543a00585edf42a..142398b016a9d9ce69cb516509d3ad70298f52df 100644 --- a/src/app/components/resources/detail/resource-detail.component.html +++ b/src/app/components/resources/detail/resource-detail.component.html @@ -1,113 +1,105 @@ -<ng-container *ngIf="resource"> - - <app-back-button [route]="'/resources'" [title]="'Retourner à la liste des ressources'"></app-back-button> - - <section class="section"> - <div class="columns is-centered"> - <div class="column is-8"> - <div class="card"> - <header class="card-header"> - <p class="card-header-title has-text-centered"> - {{resource.name}} +<section class="section page-container" *ngIf="resource"> + <app-page-header [pageInfo]="{title: title}"></app-page-header> + + <div class="columns is-centered"> + <div class="column is-8"> + <div class="card"> + <header class="card-header"> + <p class="card-header-title has-text-centered"> + {{resource.name}} + </p> + </header> + <div class="card-content"> + <div class="content"> + <p> + <span class="has-text-weight-bold">Id: </span> + <span>{{resource.id}}</span> + </p> + <p> + <span class="has-text-weight-bold">Acronyme: </span> + <span *ngIf="resource.acronym; else emptyAcronymWarning">{{resource.acronym}}</span> + <ng-template #emptyAcronymWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> </p> - </header> - <div class="card-content"> - <div class="content"> - <p> - <span class="has-text-weight-bold">Id: </span> - <span>{{resource.id}}</span> - </p> - <p> - <span class="has-text-weight-bold">Acronyme: </span> - <span *ngIf="resource.acronym; else emptyAcronymWarning">{{resource.acronym}}</span> - <ng-template #emptyAcronymWarning> - <span class="empty-property">Non renseigné</span> - </ng-template> - </p> - - <p> - <span class="has-text-weight-bold">Type: </span> - <span *ngIf="resource.type; else emptyTypeWarning">{{resource.type}}</span> - <ng-template #emptyTypeWarning> - <span class="empty-property">Non renseigné</span> - </ng-template> - </p> - - <div *ngIf="resource.description"> - <span class="has-text-weight-bold">Description: </span> - <span *ngIf="resource.description; else emptyDescriptionWarning">{{resource.description}}</span> - <ng-template #emptyDescriptionWarning> - <span class="empty-property">Non renseigné</span> - </ng-template> - </div> - - <p> - <span class="has-text-weight-bold">Requêtable: </span> - <span class="icon has-text-success" - [ngClass]="{'has-text-success': resource.isQueryable, 'has-text-danger': !resource.isQueryable}"> - <i class="far fa-check-circle" - [ngClass]="{'fa-check-circle': resource.isQueryable, 'fa-times-circle': !resource.isQueryable}"></i> - </span> - </p> - <p> - <span class="has-text-weight-bold">Téléchargeable: </span> - <span class="icon has-text-success" - [ngClass]="{'has-text-success': resource.isDownloadable, 'has-text-danger': !resource.isDownloadable}"> - <i class="far fa-check-circle" - [ngClass]="{'fa-check-circle': resource.isDownloadable, 'fa-times-circle': !resource.isDownloadable}"></i> - </span> - </p> + <p> + <span class="has-text-weight-bold">Type: </span> + <span *ngIf="resource.type; else emptyTypeWarning">{{resource.type}}</span> + <ng-template #emptyTypeWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </p> - <p> - <span class="has-text-weight-bold">Standardisé: </span> - <span class="icon has-text-success" - [ngClass]="{'has-text-success': resource.isStandard, 'has-text-danger': !resource.isStandard}"> - <i class="far fa-check-circle" - [ngClass]="{'fa-check-circle': resource.isStandard, 'fa-times-circle': !resource.isStandard}"></i> - </span> - </p> + <div *ngIf="resource.description"> + <span class="has-text-weight-bold">Description: </span> + <span *ngIf="resource.description; else emptyDescriptionWarning">{{resource.description}}</span> + <ng-template #emptyDescriptionWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </div> - <div *ngIf="resource.parametersUrl"> - <span class="has-text-weight-bold">Parametres URL: </span> - <span *ngIf="resource.parametersUrl; else emptyParameterUrlWarning">{{resource.parametersUrl}}</span> - <ng-template #emptyParameterUrlWarning> - <span class="empty-property">Non renseigné</span> - </ng-template> - </div> + <p> + <span class="has-text-weight-bold">Requêtable: </span> + <span class="icon has-text-success" + [ngClass]="{'has-text-success': resource.isQueryable, 'has-text-danger': !resource.isQueryable}"> + <i class="far fa-check-circle" + [ngClass]="{'fa-check-circle': resource.isQueryable, 'fa-times-circle': !resource.isQueryable}"></i> + </span> + </p> - <div *ngIf="resource.messageWarning"> - <span class="has-text-weight-bold">Message d'alerte: </span> - <span *ngIf="resource.messageWarning; else emptyMessageWarning">{{resource.messageWarning}}</span> - <ng-template #emptyMessageWarning> - <span class="empty-property">Non renseigné</span> - </ng-template> - </div> - <br> + <p> + <span class="has-text-weight-bold">Téléchargeable: </span> + <span class="icon has-text-success" + [ngClass]="{'has-text-success': resource.isDownloadable, 'has-text-danger': !resource.isDownloadable}"> + <i class="far fa-check-circle" + [ngClass]="{'fa-check-circle': resource.isDownloadable, 'fa-times-circle': !resource.isDownloadable}"></i> + </span> + </p> - <div> - <span class="has-text-weight-bold">Formats de sortie: </span> - <ul - *ngIf="resource.resourceFormats && resource.resourceFormats.length > 0; else noResourceFormatTemplate"> - <li *ngFor="let resourceFormat of resource.resourceFormats"> - {{ resourceFormat.format.name }}<span *ngIf="resourceFormat.isCuttable">, découpable</span><span - *ngIf="resourceFormat.isProjectable">, projectable</span> - </li> - </ul> - <ng-template #noResourceFormatTemplate> - <span class="empty-property">Non renseigné</span> - </ng-template> - </div> + <p> + <span class="has-text-weight-bold">Standardisé: </span> + <span class="icon has-text-success" + [ngClass]="{'has-text-success': resource.isStandard, 'has-text-danger': !resource.isStandard}"> + <i class="far fa-check-circle" + [ngClass]="{'fa-check-circle': resource.isStandard, 'fa-times-circle': !resource.isStandard}"></i> + </span> + </p> + <div *ngIf="resource.parametersUrl"> + <span class="has-text-weight-bold">Parametres URL: </span> + <span *ngIf="resource.parametersUrl; else emptyParameterUrlWarning">{{resource.parametersUrl}}</span> + <ng-template #emptyParameterUrlWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> + </div> + <div *ngIf="resource.messageWarning"> + <span class="has-text-weight-bold">Message d'alerte: </span> + <span *ngIf="resource.messageWarning; else emptyMessageWarning">{{resource.messageWarning}}</span> + <ng-template #emptyMessageWarning> + <span class="empty-property">Non renseigné</span> + </ng-template> </div> + <br> + + <div> + <span class="has-text-weight-bold">Formats de sortie: </span> + <ul + *ngIf="resource.resourceFormats && resource.resourceFormats.length > 0; else noResourceFormatTemplate"> + <li *ngFor="let resourceFormat of resource.resourceFormats"> + {{ resourceFormat.format.name }}<span *ngIf="resourceFormat.isCuttable">, découpable</span><span + *ngIf="resourceFormat.isProjectable">, projectable</span> + </li> + </ul> + <ng-template #noResourceFormatTemplate> + <span class="empty-property">Non renseigné</span> + </ng-template> + </div> + </div> </div> </div> - </div> - - - </section> - -</ng-container> \ No newline at end of file + </div> +</section> \ No newline at end of file diff --git a/src/app/components/resources/detail/resource-detail.component.ts b/src/app/components/resources/detail/resource-detail.component.ts index 161b88bc0c5108afa75b8504b380fe1381b6504b..d2ab5ad766194db926d0ed3a31d93925dde380b0 100644 --- a/src/app/components/resources/detail/resource-detail.component.ts +++ b/src/app/components/resources/detail/resource-detail.component.ts @@ -14,16 +14,18 @@ import { ResourceService } from 'src/app/services/resource.service'; export class ResourceDetailComponent implements OnInit { resource: Resource; + title: string; constructor( - private route: ActivatedRoute, - private resourceService: ResourceService, + private _route: ActivatedRoute, + private _resourceService: ResourceService, ) { } ngOnInit(): void { - this.route.paramMap.pipe( - switchMap((params: ParamMap) => this.resourceService.findById(params.get('id')))) + this.title = this._route.snapshot.data.title; + this._route.paramMap.pipe( + switchMap((params: ParamMap) => this._resourceService.findById(params.get('id')))) .subscribe((resource: Resource) => this.resource = resource); } } diff --git a/src/app/components/resources/edit/resource-form.component.html b/src/app/components/resources/edit/resource-form.component.html index 9e24f8e03eb9db223d6f6fd09aaab963021582af..c41e9d4629578b708384787c031f58495eeb5a53 100644 --- a/src/app/components/resources/edit/resource-form.component.html +++ b/src/app/components/resources/edit/resource-form.component.html @@ -1,8 +1,5 @@ -<ng-container *ngIf="resource"> - - <app-back-button [route]="'/resources'" [title]="'Retourner à la liste des ressources'"></app-back-button> - - <h1>{{ title }}</h1> +<section class="section page-container" *ngIf="resource"> + <app-page-header [pageInfo]="{title: title}"></app-page-header> <form [formGroup]="form" (ngSubmit)="onSubmit()" class="columns is-centered is-marginless"> <div class="column is-7"> @@ -90,14 +87,14 @@ </div> <div class="field"> - <label class="label required" for="parametersUrl">Paramètres URL</label> + <label class="label" for="parametersUrl">Paramètres URL</label> <div class="control"> <input class="input" type="text" formControlName="parametersUrl" id="parametersUrl"> </div> </div> <div class="field"> - <label class="label required" for="messageWarning">Message d'alerte</label> + <label class="label" for="messageWarning">Message d'alerte</label> <div class="control"> <input class="input" type="text" formControlName="messageWarning" id="messageWarning"> </div> @@ -178,4 +175,4 @@ </div> </form> -</ng-container> \ No newline at end of file +</section> \ No newline at end of file diff --git a/src/app/components/resources/edit/resource-form.component.ts b/src/app/components/resources/edit/resource-form.component.ts index b40283eb2dbed3b4d2148c60ebd4bb3e6e048052..c379dc1024d744291b88030815c2df643de0114f 100644 --- a/src/app/components/resources/edit/resource-form.component.ts +++ b/src/app/components/resources/edit/resource-form.component.ts @@ -52,8 +52,8 @@ export class ResourceFormComponent implements OnInit { this.resource.resourceFormats.forEach((resourceFormat) => { resourceFormats.push(this._fb.group({ formatId: [resourceFormat.format.id, Validators.required], - isProjectable: resourceFormat.isProjectable, - isCuttable: resourceFormat.isCuttable, + isProjectable: [resourceFormat.isProjectable, Validators.required], + isCuttable: [resourceFormat.isCuttable, Validators.required], id: resourceFormat.id, })); }); diff --git a/src/app/components/resources/list/resources.component.html b/src/app/components/resources/list/resources.component.html index 67f84ebc6389a162554ef6300e57f97b2ccc1b1c..f544a69abeaaa7d31ac75a5bf05f4ade9253d221 100644 --- a/src/app/components/resources/list/resources.component.html +++ b/src/app/components/resources/list/resources.component.html @@ -1,105 +1,186 @@ <ng-container *ngIf="resources"> - <div> - <div class="section"> - <div class="columns is-centered is-marginless"> - <div class="column has-text-centered"> - <h2>{{ totalElement }} ressources trouvées</h2> + <div class="section page-container"> + <app-page-header [pageInfo]="pageHeaderInfo" [hideBackButton]="true"></app-page-header> + <div class="add-item-link has-text-right"> + <a class="button button-gl" [routerLink]="['new']"> + Ajouter + </a> + </div> + <div class="table entity-list-table"> + <div class="header columns is-marginless"> + <div class="column is-2"> + <span (click)="sortBy('name')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'name' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span> + </span> </div> - </div> - <div class="add-item has-text-right"> - <a class="button button-gl" [routerLink]="['new']"> - Ajouter - </a> - </div> - <div class="table"> - <div class="header columns is-marginless"> - <div class="column is-2 has-text-centered"> - <span (click)="sortBy('name')" class="is-sortable"> - <span class="column-title" [ngClass]="{'active': sortOptions.value === name}">Nom</span> - <span *ngIf="sortOptions.value === 'name'" class="has-text-danger"> - <i class="fas sort-order-icon" - [ngClass]="{'fa-arrow-up': sortOptions.order === 'asc', 'fa-arrow-down': sortOptions.order === 'desc'}"></i> + <div class="column is-1"> + <span (click)="sortBy('acronym')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'acronym' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'acronym' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === acronym}">Acronyme</span> + </span> + </div> + <div class="column is-1"> + <span (click)="sortBy('type')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'type' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'type' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === type}">Type</span> + </span> + </div> + <div class="column is-4"> + <span (click)="sortBy('description')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'description' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === description}">Description</span> + </span> + </div> + <div class="column is-1 has-text-centered"> + <span (click)="sortBy('isQueryable')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'isQueryable' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'isQueryable' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === isQueryable}">Requêtable</span> + </span> + </div> + <div class="column is-1 has-text-centered"> + <span (click)="sortBy('isDownloadable')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'isDownloadable' && sortOptions.order === 'desc'}"></i> + </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'isDownloadable' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" + [ngClass]="{'active': sortOptions.value === isDownloadable}">Téléchargeable</span> + </span> + </div> + <div class="column is-1 has-text-centered"> + <span (click)="sortBy('isStandard')" class="is-sortable"> + <span class="sort-icons"> + <span class="icon"> + <i class="fas fa-sort-up" + [ngClass]="{'icon-red': sortOptions.value === 'isStandard' && sortOptions.order === 'desc'}"></i> </span> + <span class="icon"> + <i class="fas fa-sort-down" + [ngClass]="{'icon-red': sortOptions.value === 'isStandard' && sortOptions.order === 'asc'}"></i> + </span> + </span> + <span class="column-title" [ngClass]="{'active': sortOptions.value === isStandard}">Standard</span> + </span> + </div> + <div class="column is-1 has-text-centered"> + <span class="column-title">Actions</span> + </div> + </div> + <div class="data-list"> + <div class="data columns is-multiline is-vcentered is-marginless" + *ngFor="let resource of resources; let i=index; let odd=odd; let even=even;" + [ngClass]="{ odd: odd, even: even }"> + <div class="column is-2"> + <span> + {{ resource.name}} </span> + </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Acronyme</span> + <div class="column is-1"> + <span> + {{ resource.acronym}} + </span> + </div> - <div class="column is-1 has-text-centered"> - <span class="column-title">Type</span> + <div class="column is-1"> + <span> + {{ resource.type}} + </span> + </div> - <div class="column is-4 has-text-centered"> - <span class="column-title">Description</span> + <div class="column is-4"> + <span> + {{ resource.description | slice:0:300}} + </span> </div> <div class="column is-1 has-text-centered"> - <span class="column-title">Requêtable</span> + <span class="icon has-text-success" *ngIf="resource.isQueryable"> + <i class="far fa-check-circle"></i> + </span> + <span class="icon has-text-danger" *ngIf="!resource.isQueryable"> + <i class="far fa-times-circle"></i> + </span> </div> <div class="column is-1 has-text-centered"> - <span class="column-title">Téléchargeable</span> + <span class="icon has-text-success" *ngIf="resource.isDownloadable"> + <i class="far fa-check-circle"></i> + </span> + <span class="icon has-text-danger" *ngIf="!resource.isDownloadable"> + <i class="far fa-times-circle"></i> + </span> </div> <div class="column is-1 has-text-centered"> - <span class="column-title">Standard</span> + <span class="icon has-text-success" *ngIf="resource.isStandard"> + <i class="far fa-check-circle"></i> + </span> + <span class="icon has-text-danger" *ngIf="!resource.isStandard"> + <i class="far fa-times-circle"></i> + </span> </div> <div class="column is-1 has-text-centered"> - <span class="column-title">Actions</span> - </div> - </div> - <div class="data-list"> - <div class="data columns is-multiline is-vcentered is-marginless" - *ngFor="let resource of resources; let i=index; let odd=odd; let even=even;" - [ngClass]="{ odd: odd, even: even }"> - <div class="column is-2 has-text-centered"> - {{ resource.name}} - </div> - <div class="column is-1 has-text-centered"> - {{ resource.acronym}} - </div> - <div class="column is-1 has-text-centered"> - {{ resource.type}} - </div> - <div class="column is-4 has-text-centered"> - {{ resource.description | slice:0:300}} - </div> - <div class="column is-1 has-text-centered"> - <span class="icon has-text-success" *ngIf="resource.isQueryable"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.isQueryable"> - <i class="far fa-times-circle"></i> - </span> - </div> - <div class="column is-1 has-text-centered"> - <span class="icon has-text-success" *ngIf="resource.isDownloadable"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.isDownloadable"> - <i class="far fa-times-circle"></i> - </span> - </div> - <div class="column is-1 has-text-centered"> - <span class="icon has-text-success" *ngIf="resource.isStandard"> - <i class="far fa-check-circle"></i> - </span> - <span class="icon has-text-danger" *ngIf="!resource.isStandard"> - <i class="far fa-times-circle"></i> - </span> - </div> - <div class="column is-1 has-text-centered"> - <app-crud-buttons [id]="resource.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> - </div> + <app-crud-buttons [id]="resource.id" (delete)="displayDeletePopup($event)"></app-crud-buttons> </div> </div> - <div class="columns is-marginless"> - <div class="column"> - <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" - [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" - [showFirstLastButtons]="true" (page)="changePagination($event)" - (pageSizeChanged)="changePageSize($event)"> - </app-paginator> - </div> + </div> + <div class="columns is-marginless paginator"> + <div class="column"> + <app-paginator *ngIf="paginator.length > 0" [length]="paginator.length" [pageSize]="paginator.limit" + [pageSizeOptions]="paginator.pageSizeOptions" [pageIndex]="paginator.pageIndex" [pagesToShow]="5" + [showFirstLastButtons]="true" (page)="changePagination($event)" (pageSizeChanged)="changePageSize($event)"> + </app-paginator> </div> </div> - </div> - </div> -</ng-container> \ No newline at end of file + </div> \ No newline at end of file diff --git a/src/app/components/resources/list/resources.component.scss b/src/app/components/resources/list/resources.component.scss index c746b911909d67f2432690682808294ac825224e..8b137891791fe96927ad78e64b0aad7bded08bdc 100644 --- a/src/app/components/resources/list/resources.component.scss +++ b/src/app/components/resources/list/resources.component.scss @@ -1,56 +1 @@ -section { - padding: 20px; -} -.section { - padding-top: 0; -} -.table { - background-color: white; - border: 1px solid lightgray; -} - -.center { - text-align: center; -} - -img { - max-width: 100px; -} - -.header { - border-bottom: 1px solid lightgray; - background-color: #fafafa; - span { - color: #999; - - } -} - -.is-sortable .column-title { - cursor: pointer; -} - -.table .columns { - border-bottom: 1px solid lightgray -} - -.icon { - height: unset; -} - -i { - - &.title-icon { - margin-right: 0.75rem; - } - - &.sort-order-icon { - padding-left: 0.75rem; - cursor: pointer; - } -} - -.add-item { - margin-bottom: 20px; -} diff --git a/src/app/components/resources/list/resources.component.ts b/src/app/components/resources/list/resources.component.ts index a56ed6ddeeaf05512edcd33cddbfa12247c45bb7..b19fe3e458a99f7a4a7a0818d03fe2929d81b218 100644 --- a/src/app/components/resources/list/resources.component.ts +++ b/src/app/components/resources/list/resources.component.ts @@ -1,8 +1,9 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { Resource, ResourceRO } from 'src/app/models/resource.model'; import { ResourceService } from 'src/app/services/resource.service'; import { Subscription } from 'rxjs'; import { PaginatorOptions } from 'src/app/models/paginator-options.model'; +import { IPageHeaderInfo } from '../../../models/page.model'; @Component({ selector: 'app-resources', @@ -11,6 +12,9 @@ import { PaginatorOptions } from 'src/app/models/paginator-options.model'; }) export class ResourcesComponent implements OnInit { + pageHeaderInfo: IPageHeaderInfo = { + title: '', + }; resources: Resource[]; searchChangeSub: Subscription; @@ -58,6 +62,8 @@ export class ResourcesComponent implements OnInit { this.resources = items.resources; this.totalElement = items.totalCount; + this.pageHeaderInfo.title = `${this.totalElement} ressources trouvées`; + this.paginator.limit = this.resourcesService.limit; this.paginator.pageIndex = this.resourcesService.pageNumber; this.paginator.length = items.totalCount; diff --git a/src/app/components/welcome/welcome.component.html b/src/app/components/welcome/welcome.component.html deleted file mode 100644 index 099c771bdebfe62ef89f8357e6a8805c8e2c0cbf..0000000000000000000000000000000000000000 --- a/src/app/components/welcome/welcome.component.html +++ /dev/null @@ -1,7 +0,0 @@ -<section> - <div class="user-info-container"> - <p class="welcome-back-message">Content de vous revoir {{ loggedInUserFullname }}</p> - - <button class="button button-gl" (click)="loggout()">Deconnexion</button> - </div> -</section> \ No newline at end of file diff --git a/src/app/components/welcome/welcome.component.scss b/src/app/components/welcome/welcome.component.scss deleted file mode 100644 index 13ea27f83c932252bc787143512353bdbdc0272b..0000000000000000000000000000000000000000 --- a/src/app/components/welcome/welcome.component.scss +++ /dev/null @@ -1,9 +0,0 @@ -.user-info-container { - text-align: center; - margin-top: 2rem; -} - -.welcome-back-message { - font-size: 1.5rem; - margin-bottom: 1rem; -} diff --git a/src/app/components/welcome/welcome.component.ts b/src/app/components/welcome/welcome.component.ts deleted file mode 100644 index 4d118c0dc02557dee83bf5f83b1159c360b0b731..0000000000000000000000000000000000000000 --- a/src/app/components/welcome/welcome.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { UserService } from '../../user/services'; -import { Router } from '@angular/router'; -import { NotificationService } from '../../services'; - -@Component({ - selector: 'app-welcome', - templateUrl: './welcome.component.html', - styleUrls: ['./welcome.component.scss'], -}) - -export class WelcomeComponent implements OnInit { - - constructor( - private _userService: UserService, - private _router: Router, - ) { } - - ngOnInit(): void { } - - get loggedInUserFullname() { - return `${this._userService.user.firstName} ${this._userService.user.lastName}`; - } - - loggout() { - this._userService.resetAuth(); - this._router.navigate(['/login']); - } - -} diff --git a/src/app/directives/click-outside.directive.ts b/src/app/directives/click-outside.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..d89778c8bacbaeb2b1e0d6bee03c4193ac0c7cae --- /dev/null +++ b/src/app/directives/click-outside.directive.ts @@ -0,0 +1,20 @@ +import { Directive, ElementRef, Output, EventEmitter, HostListener } from '@angular/core'; + +@Directive({ + selector: '[clickOutside]', +}) +export class ClickOutsideDirective { + constructor(private _elementRef: ElementRef) { + } + + @Output() + public clickOutside = new EventEmitter(); + + @HostListener('document:click', ['$event.target']) + public onClick(targetElement) { + const clickedInside = this._elementRef.nativeElement.contains(targetElement); + if (!clickedInside) { + this.clickOutside.emit(null); + } + } +} diff --git a/src/app/directives/index.ts b/src/app/directives/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd7141291ad94d129b9303135f6a1f17c11f3a61 --- /dev/null +++ b/src/app/directives/index.ts @@ -0,0 +1,10 @@ +import { ClickOutsideDirective } from './click-outside.directive'; +import { PreventDefaultDirective } from './prevent-default.directive'; + +export { ClickOutsideDirective, PreventDefaultDirective }; + +// tslint:disable-next-line:variable-name +export const AppDirectives = [ + ClickOutsideDirective, + PreventDefaultDirective, +]; diff --git a/src/app/directives/prevent-default.directive.ts b/src/app/directives/prevent-default.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..e0cb8870042e4c76cbbc06ad74ec73a2d09beb04 --- /dev/null +++ b/src/app/directives/prevent-default.directive.ts @@ -0,0 +1,17 @@ +import { Directive } from '@angular/core'; + +@Directive({ + selector: '[preventDefault]', + host: { + '(click)': 'onClick($event)', + }, +}) +export class PreventDefaultDirective { + + constructor() { } + + onClick(e) { + e.preventDefault(); + e.stopPropagation(); + } +} diff --git a/src/app/models/image-upload.model.ts b/src/app/models/image-upload.model.ts index 0c4a768dc92888a6ef1f8ee5f8f0ceba92f32b4e..26160e8663126e184ff817ee60af6e6c416aa053 100644 --- a/src/app/models/image-upload.model.ts +++ b/src/app/models/image-upload.model.ts @@ -2,4 +2,5 @@ export interface IImageUploadFieldParams { label: string; inputName: string; existingImageUrl?: string; + isRequired?: boolean; } diff --git a/src/app/models/page.model.ts b/src/app/models/page.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..94a75073ebfe5a78ff3286f7dd6c0341153ad309 --- /dev/null +++ b/src/app/models/page.model.ts @@ -0,0 +1,7 @@ +export interface IPageHeaderInfo { + title: string; + metadataSubtitle?: string; + surtitle?: string; + subtitle?: string; + hasBetaStyle?: boolean; +} diff --git a/src/app/services/index.ts b/src/app/services/index.ts index f667d334c2d6b4ca957c674e76e4e2804be6a79b..a742af1b6f115fa2240963593cdde4448b93a440 100644 --- a/src/app/services/index.ts +++ b/src/app/services/index.ts @@ -4,6 +4,7 @@ import { ResourceService } from './resource.service'; import { FormatService } from './format.service'; import { NotificationService } from './notification.service'; import { DataLogsService } from './data-logs.service'; +import { NavigationHistoryService } from './navigation-history.service'; export { AppConfigService, @@ -12,6 +13,7 @@ export { FormatService, NotificationService, DataLogsService, + NavigationHistoryService, }; // tslint:disable-next-line:variable-name @@ -22,4 +24,5 @@ export const AppServices = [ FormatService, NotificationService, DataLogsService, + NavigationHistoryService, ]; diff --git a/src/app/services/navigation-history.service.ts b/src/app/services/navigation-history.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..608c4a52fc5ea94113eb2967ddc079e1b4524f5d --- /dev/null +++ b/src/app/services/navigation-history.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class NavigationHistoryService { + history: string[] = []; + maxHistoryLength = 20; // Avoid to store too many url limite back history to 20 + + constructor( + ) { } + + add(url: string) { + // If max size reach remove the oldest url + if (this.history.length >= this.maxHistoryLength) { + this.history.shift(); + } + this.history.push(url); + } + + getFromLast(index: number): string { + const position = this.history.length - 1 - index; + let res = null; + if (position >= 0) { + res = this.history[position]; + } + return res; + } +} diff --git a/src/app/services/resource.service.ts b/src/app/services/resource.service.ts index a46b3d35be2e366d01c26696c4b87fa7ab1db43f..52b4b03363f4e926e9f97a8e4ea486cf82c515d0 100644 --- a/src/app/services/resource.service.ts +++ b/src/app/services/resource.service.ts @@ -36,6 +36,7 @@ export class ResourceService { map((response) => { const totalCount = response.headers.get('Content-Range'); const resources = []; + console.log(response); response.body.forEach((resource) => { resources.push(new Resource(resource)); }); diff --git a/src/assets/favicons/android-chrome-144x144.png b/src/assets/favicons/android-chrome-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..affe519cbb437504db917693d1ccf48afb005262 Binary files /dev/null and b/src/assets/favicons/android-chrome-144x144.png differ diff --git a/src/assets/favicons/android-chrome-192x192.png b/src/assets/favicons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..716424e879cbd3abce07b77762a132a2b6e9f38a Binary files /dev/null and b/src/assets/favicons/android-chrome-192x192.png differ diff --git a/src/assets/favicons/android-chrome-256x256.png b/src/assets/favicons/android-chrome-256x256.png new file mode 100644 index 0000000000000000000000000000000000000000..97f02308e8b6b07782db4355b67612d75b8460ca Binary files /dev/null and b/src/assets/favicons/android-chrome-256x256.png differ diff --git a/src/assets/favicons/android-chrome-36x36.png b/src/assets/favicons/android-chrome-36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..331ff23e72311eeba45b46bed767aac4757e7972 Binary files /dev/null and b/src/assets/favicons/android-chrome-36x36.png differ diff --git a/src/assets/favicons/android-chrome-48x48.png b/src/assets/favicons/android-chrome-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..b1b9c5c9b17519a136f6c008aaf4fb19010dc5a6 Binary files /dev/null and b/src/assets/favicons/android-chrome-48x48.png differ diff --git a/src/assets/favicons/android-chrome-72x72.png b/src/assets/favicons/android-chrome-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..43f3938b6760fde9aa96dc25aff0f16bce95ac19 Binary files /dev/null and b/src/assets/favicons/android-chrome-72x72.png differ diff --git a/src/assets/favicons/android-chrome-96x96.png b/src/assets/favicons/android-chrome-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..8ec029c93d5cf3ed8ffb680dbdfef874bb6a456c Binary files /dev/null and b/src/assets/favicons/android-chrome-96x96.png differ diff --git a/src/assets/favicons/apple-touch-icon-114x114.png b/src/assets/favicons/apple-touch-icon-114x114.png new file mode 100644 index 0000000000000000000000000000000000000000..be600ca1872283dfa0d2e5ff67c6ce8a66e2e1c4 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-114x114.png differ diff --git a/src/assets/favicons/apple-touch-icon-120x120.png b/src/assets/favicons/apple-touch-icon-120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..6c1fb5825213a8abeaf7ff223adc3a5e511c91ff Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-120x120.png differ diff --git a/src/assets/favicons/apple-touch-icon-144x144.png b/src/assets/favicons/apple-touch-icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..f1e3bc4b6f2fc936b6ae62c72b20dcca42e622c7 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-144x144.png differ diff --git a/src/assets/favicons/apple-touch-icon-152x152.png b/src/assets/favicons/apple-touch-icon-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae86d5b92d8f9d903ca0e3813bf3f6240fdf1c1 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-152x152.png differ diff --git a/src/assets/favicons/apple-touch-icon-180x180.png b/src/assets/favicons/apple-touch-icon-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..e350cf725d908a3705d6734ff512d7ec3ec38fff Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-180x180.png differ diff --git a/src/assets/favicons/apple-touch-icon-57x57.png b/src/assets/favicons/apple-touch-icon-57x57.png new file mode 100644 index 0000000000000000000000000000000000000000..557771e4e8f7c6aaf5cfd57016aeb7d2a85dc6a9 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-57x57.png differ diff --git a/src/assets/favicons/apple-touch-icon-60x60.png b/src/assets/favicons/apple-touch-icon-60x60.png new file mode 100644 index 0000000000000000000000000000000000000000..d11f017085dc60df62ed62a4329dcb57b67b3179 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-60x60.png differ diff --git a/src/assets/favicons/apple-touch-icon-72x72.png b/src/assets/favicons/apple-touch-icon-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..42314ed36e638b911d70b209d97b5edfddf80bd7 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-72x72.png differ diff --git a/src/assets/favicons/apple-touch-icon-76x76.png b/src/assets/favicons/apple-touch-icon-76x76.png new file mode 100644 index 0000000000000000000000000000000000000000..393dd9e2b1ae6b04d91a05e4f896c25b09ced358 Binary files /dev/null and b/src/assets/favicons/apple-touch-icon-76x76.png differ diff --git a/src/assets/favicons/apple-touch-icon.png b/src/assets/favicons/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e350cf725d908a3705d6734ff512d7ec3ec38fff Binary files /dev/null and b/src/assets/favicons/apple-touch-icon.png differ diff --git a/src/assets/favicons/browserconfig.xml b/src/assets/favicons/browserconfig.xml new file mode 100644 index 0000000000000000000000000000000000000000..8ba3c6f419f9650c602cc4030af4c354bc19af75 --- /dev/null +++ b/src/assets/favicons/browserconfig.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<browserconfig> + <msapplication> + <tile> + <square70x70logo src="./mstile-70x70.png"/> + <square150x150logo src="./mstile-150x150.png"/> + <square310x310logo src="./mstile-310x310.png"/> + <wide310x150logo src="./mstile-310x150.png"/> + <TileColor>#242b3f</TileColor> + </tile> + </msapplication> +</browserconfig> diff --git a/src/assets/favicons/favicon-16x16.png b/src/assets/favicons/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..6ab698d089909f2ce6816642730e8277478eb40a Binary files /dev/null and b/src/assets/favicons/favicon-16x16.png differ diff --git a/src/assets/favicons/favicon-32x32.png b/src/assets/favicons/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..378a0e79b7ba510e434bf9397f80a1c41ab0b81b Binary files /dev/null and b/src/assets/favicons/favicon-32x32.png differ diff --git a/src/assets/favicons/favicon.ico b/src/assets/favicons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..38ab13c41ea6560010737b1ffd22ff9d58e875ca Binary files /dev/null and b/src/assets/favicons/favicon.ico differ diff --git a/src/assets/favicons/mstile-144x144.png b/src/assets/favicons/mstile-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..f6a7ddacdb968159dd544aace6959f82a0bd1a7b Binary files /dev/null and b/src/assets/favicons/mstile-144x144.png differ diff --git a/src/assets/favicons/mstile-150x150.png b/src/assets/favicons/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..9c5c175246ad5a13611a99497b5408564fc5c735 Binary files /dev/null and b/src/assets/favicons/mstile-150x150.png differ diff --git a/src/assets/favicons/mstile-310x150.png b/src/assets/favicons/mstile-310x150.png new file mode 100644 index 0000000000000000000000000000000000000000..091b2ff605a12e6345d80f840d2609359c21a0e0 Binary files /dev/null and b/src/assets/favicons/mstile-310x150.png differ diff --git a/src/assets/favicons/mstile-310x310.png b/src/assets/favicons/mstile-310x310.png new file mode 100644 index 0000000000000000000000000000000000000000..a62920ccd57c60651a3a8e65013b0e3bdeab5515 Binary files /dev/null and b/src/assets/favicons/mstile-310x310.png differ diff --git a/src/assets/favicons/mstile-70x70.png b/src/assets/favicons/mstile-70x70.png new file mode 100644 index 0000000000000000000000000000000000000000..24094017a5874fb612c757c1a489941382ce071b Binary files /dev/null and b/src/assets/favicons/mstile-70x70.png differ diff --git a/src/assets/favicons/safari-pinned-tab.svg b/src/assets/favicons/safari-pinned-tab.svg new file mode 100644 index 0000000000000000000000000000000000000000..163e4ea0b5e7da8aa7ea440b5bf58cac2c461bd6 --- /dev/null +++ b/src/assets/favicons/safari-pinned-tab.svg @@ -0,0 +1,21 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> +<svg version="1.0" xmlns="http://www.w3.org/2000/svg" + width="260.000000pt" height="260.000000pt" viewBox="0 0 260.000000 260.000000" + preserveAspectRatio="xMidYMid meet"> +<metadata> +Created by potrace 1.11, written by Peter Selinger 2001-2013 +</metadata> +<g transform="translate(0.000000,260.000000) scale(0.100000,-0.100000)" +fill="#000000" stroke="none"> +<path d="M0 1300 l0 -1300 1300 0 1300 0 0 1300 0 1300 -1300 0 -1300 0 0 +-1300z m1540 41 l0 -849 -50 -21 c-110 -47 -215 -65 -385 -65 -147 0 -167 2 +-241 27 -118 39 -209 107 -273 202 -165 248 -140 664 54 873 126 136 338 192 +520 137 l45 -13 0 279 0 279 165 0 165 0 0 -849z m564 -565 c71 -54 100 -139 +76 -219 -27 -92 -93 -140 -191 -140 -69 0 -121 24 -163 77 -29 35 -31 44 -31 +115 0 67 4 83 27 118 39 59 96 85 179 81 54 -3 73 -9 103 -32z"/> +<path d="M970 1419 c-96 -38 -150 -170 -151 -365 -2 -295 91 -417 298 -394 39 +4 76 11 82 14 8 5 11 114 11 366 l0 359 -52 16 c-58 17 -150 19 -188 4z"/> +</g> +</svg> diff --git a/src/assets/favicons/site.webmanifest b/src/assets/favicons/site.webmanifest new file mode 100644 index 0000000000000000000000000000000000000000..8f74d86caf4f1a50063ce9d567b0e25988b0854d --- /dev/null +++ b/src/assets/favicons/site.webmanifest @@ -0,0 +1,43 @@ +{ + "name": "data.grandlyon.com", + "short_name": "data.grandlyon.com", + "icons": [{ + "src": "./android-chrome-36x36.png", + "sizes": "36x36", + "type": "image/png" + }, + { + "src": "./android-chrome-48x48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "./android-chrome-72x72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "./android-chrome-96x96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "./android-chrome-144x144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "./android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "./android-chrome-256x256.png", + "sizes": "256x256", + "type": "image/png" + } + ], + "theme_color": "#242b3f", + "background_color": "#242b3f", + "display": "standalone" +} \ No newline at end of file diff --git a/src/assets/img/data_loader.gif b/src/assets/img/data_loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..6027181d82b601f519b7469be884b1b6c2351a5c Binary files /dev/null and b/src/assets/img/data_loader.gif differ diff --git a/src/assets/img/favicon.ico b/src/assets/img/favicon.ico deleted file mode 100644 index 5619a8f1cf3db720725e00d80636e23de406e8de..0000000000000000000000000000000000000000 Binary files a/src/assets/img/favicon.ico and /dev/null differ diff --git a/src/assets/img/left_arrow.svg b/src/assets/img/left_arrow.svg new file mode 100644 index 0000000000000000000000000000000000000000..ecd6f771dd0432615fe78f566f1e804fd2b5e344 --- /dev/null +++ b/src/assets/img/left_arrow.svg @@ -0,0 +1,3 @@ +<svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M5 1L1 5L5 9" stroke="#818080" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/src/favicon.ico b/src/favicon.ico index 8081c7ceaf2be08bf59010158c586170d9d2d517..e2c6f06d1073863153b63a50f6191e7c5e03daf2 100644 Binary files a/src/favicon.ico and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html index b0e833c746feb7b256d84120886d6ff18b45cefc..4d975d93c83aa45567fe6a0210cee1fd890c83d5 100644 --- a/src/index.html +++ b/src/index.html @@ -6,18 +6,56 @@ <title>Plateforme data back office</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="icon" type="image/x-icon" href="assets/img/favicon.ico"> - <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" - crossorigin="anonymous"> + + <link rel="apple-touch-icon" sizes="180x180" href="./assets/favicons/apple-touch-icon.png"> + <link rel="icon" type="image/png" sizes="32x32" href="./assets/favicons/favicon-32x32.png"> + <link rel="icon" type="image/png" sizes="192x192" href="./assets/favicons/android-chrome-192x192.png"> + <link rel="icon" type="image/png" sizes="16x16" href="./assets/favicons/favicon-16x16.png"> + <link rel="manifest" href="./assets/favicons/site.webmanifest"> + <link rel="mask-icon" href="./assets/favicons/safari-pinned-tab.svg" color="#5bbad5"> + <link rel="shortcut icon" href="./assets/favicons/favicon.ico"> + <meta name="apple-mobile-web-app-title" content="data.grandlyon.com"> + <meta name="application-name" content="data.grandlyon.com"> + <meta name="msapplication-TileColor" content="#242b3f"> + <meta name="msapplication-TileImage" content="./assets/favicons/mstile-144x144.png"> + <meta name="msapplication-config" content="./assets/favicons/browserconfig.xml"> + <meta name="theme-color" content="#242b3f"> + + <link href="https://fonts.googleapis.com/css?family=Titillium+Web:300,400,500,600,700,800" rel="stylesheet"> + <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" + integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous"> + + <style> + body { + margin: 0; + } + + .AppLoader { + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background-color: #242b3f; + } + + .AppLoader img { + width: 60%; + } + + @media screen and (min-width: 705px) { + .AppLoader img { + width: 25%; + } + } + </style> </head> <body class="mat-app-background"> <app-root> <section class="AppLoader"> - <img src="assets/img/logo.svg" alt="Grand Lyon, la métropole"> - </mat-progress-spinner> + <img src="assets/img/data_loader.gif" alt="data grand lyon logo"> </section> </app-root> </body> -</html> +</html> \ No newline at end of file diff --git a/src/scss/init_bulma.scss b/src/scss/init_bulma.scss new file mode 100644 index 0000000000000000000000000000000000000000..233d54e3a2b260c24b8dad3fe549d039a43a23d5 --- /dev/null +++ b/src/scss/init_bulma.scss @@ -0,0 +1,44 @@ +// 1. Import the initial variables +@import "../../node_modules/bulma/sass/utilities/initial-variables"; +@import "../../node_modules/bulma/sass/utilities/functions"; +@import "./variables"; + +// Checkbox Bulma extension +$checkbox-border: 1px solid #818080; + +// 3. Set the derived variables +// Use the new pink as the primary color +$primary: $brand-color; +$danger: $tomato-color; + +// Tabs +$tabs-border-bottom-width: 0.0625rem; +$tabs-border-bottom-color: lightgrey; +$tabs-link-hover-border-bottom-color: $tomato-color; +$tabs-link-active-border-bottom-color: $tomato-color; +$tabs-link-hover-color: $tomato-color; +$tabs-link-active-color: $tomato-color; +$tabs-toggle-link-border-width: 5px; + +// Navbar +$navbar-background-color: transparent; + +// Use the new serif family +$family-primary: $family-sans-serif; +$pagination-current-background-color: transparent; +$pagination-current-border-color: $tomato-color; +$pagination-current-color: $tomato-color; + +$modal-background-background-color: rgba($black, 0.4); + +// Forms +$input-color: $brand-color; + +// 6. Import the rest of Bulma +@import "../../node_modules/bulma/sass/utilities/_all"; +@import "../../node_modules/bulma/sass/base/_all"; +@import "../../node_modules/bulma/sass/elements/_all"; +@import "../../node_modules/bulma/sass/components/_all"; +@import "../../node_modules/bulma/sass/grid/_all"; +@import "../../node_modules/bulma/sass/form/_all"; +@import "../../node_modules/bulma/sass/layout/_all"; diff --git a/src/scss/variables.scss b/src/scss/variables.scss new file mode 100644 index 0000000000000000000000000000000000000000..c7e633f574aa6110575f10b93a57a0778f14cc9b --- /dev/null +++ b/src/scss/variables.scss @@ -0,0 +1,43 @@ +// COLORS +$tomato-color: #da322f; // Tomato +$warning-color: #b9541d; +$success-color: #3c8225; +$brand-color: #242b3f; +$grey-dark-color: #464a57; +$grey-background-color: #f2f2f2; +$grey-super-light-color: #b4b4b4; +$menu-selected-color: #c8c8d0; +$link-color: #0057a8; +$yellow-dd-color: #ffd402; + +// Font size settings based on 16px (default browser font-size) +$size-1: 3rem; +$size-2: 2.5rem; // 40px +$size-3: 2rem; +$size-4: 1.25rem; //20px +$size-5: 1rem; //16px +$size-6: 0.875rem; //14px +$size-7: 0.8125rem; //13px +$size-8: 0.75rem; //12px + +// Add a serif family +$family-sans-serif: 'Titillium Web', +sans-serif; +$title-font: 'Titillium Web', +sans-serif; + +$menu-item-color: white; +$menu-item-hover-color: white; +$menu-item-hover-background-color: #222222; +$menu-item-active-background-color: #222222; + +$pagination-current-background-color: $tomato-color; +$pagination-current-border-color: $tomato-color; +$pagination-focus-border-color: $tomato-color; + +$button-focus-border-color: $tomato-color; + +// Header height +$header-bar-height: 60px; + +$background-link-hover-color: #191c27; diff --git a/src/styles.scss b/src/styles.scss index bf73b5d1a06258854bb9c4790a85956889795f62..09822331d571d2ba51805064dfcb6a158b662a04 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,29 +1,8 @@ -$menu-item-color: white; -$menu-item-hover-color: white; -$menu-item-hover-background-color: #222222; -$menu-item-active-background-color: #222222; -// $menu-list-border-left: 1px solid $tomato-color; - -$tomato-color: #f72f2f; -$dark-blue: #333745; -$brand-color: #333744; -$grey-dark-color: #515151; -$grey-light-color: #818080; -$grey-background-color: #F2F2F2; -$grey-super-light-color: #B4B4B4; -$link-color: #1D92FF; - -$pagination-current-background-color: $tomato-color; -$pagination-current-border-color: $tomato-color; -$pagination-focus-border-color: $tomato-color; - -$button-focus-border-color: $tomato-color; - -@import "../node_modules/bulma/bulma.sass"; +@import './scss/init_bulma.scss'; html, body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: $family-sans-serif; margin: 0; padding: 0; height: 100vh; @@ -42,7 +21,8 @@ select, input, ul, tbody { - color: $dark-blue; + color: $brand-color; + font-family: $family-sans-serif; } h1, @@ -122,7 +102,7 @@ textarea.ng-invalid:not(form).ng-touched { &.is-outlined { border: 1px solid $grey-super-light-color; - color: $grey-light-color; + color: $grey-dark-color; background: transparent; &:hover, @@ -132,7 +112,7 @@ textarea.ng-invalid:not(form).ng-touched { } &:disabled { - color: $grey-light-color; + color: $grey-dark-color; background-color: transparent; opacity: 0.38; border-color: $grey-super-light-color; @@ -156,3 +136,71 @@ textarea.ng-invalid:not(form).ng-touched { } } } + +.page-container { + min-height: calc(100% - 1rem); + max-width: 100%; + background-color: white; + padding-top: 0; + + @media screen and (min-width: $tablet) { + margin: 1rem 1rem 0 1rem; + } +} + +// Style for tables +.add-item-link { + margin-bottom: 1.25rem; +} + +.table.entity-list-table { + background-color: white; + border: 1px solid $grey-super-light-color; + + span { + font-size: 0.875rem; + } + + .header { + border-bottom: 1px solid $grey-super-light-color; + background-color: white; + + span { + color: $grey-dark-color; + } + } + + .columns.even { + background-color: $grey-background-color; + } + + .sort-icons { + display: flex; + flex-direction: column; + align-self: center; + margin-right: 0.3rem; + + .icon { + height: 0.2rem; + width: unset; + justify-content: center; + } + + .icon-red { + color: $tomato-color; + } + } + + .is-sortable { + display: flex; + } + + .is-sortable .icon, + .is-sortable .column-title { + cursor: pointer; + } + + .paginator { + border-top: 1px solid $grey-super-light-color; + } +}