diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 22a9a2020b9e7fd538ad576db38155202cfdd283..f49e4bb521a868341ea506516742e4255eea7f4a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,7 +18,6 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { SearchComponent } from './structure-list/components/search/search.component'; import { StructureDetailsComponent } from './structure-list/components/structure-details/structure-details.component'; import { StructureOpeningStatusComponent } from './structure-list/components/structure-opening-status/structure-opening-status.component'; -import { ModalOutsideDirective } from './structure-list/services/modalOutside.directive'; import { ModalFilterComponent } from './structure-list/components/modal-filter/modal-filter.component'; import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; import { AboutComponent } from './about/about.component'; @@ -35,7 +34,6 @@ import { AboutComponent } from './about/about.component'; ModalFilterComponent, StructureDetailsComponent, StructureOpeningStatusComponent, - ModalOutsideDirective, LegalNoticeComponent, AboutComponent, ], diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 612c33ebdb6f7b20a11a4ac2fa077358270dee63..35317699a2720d9279686694123f124e4f0328cd 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -15,10 +15,18 @@ padding: 0 20px; a { font-style: italic; - color: $primary-color; + color: $grey-2; &.active { font-weight: bold; color: $grey-1; + &:before { + color: $primary-color; + content: '// '; + } + &:after { + color: $primary-color; + content: ' //'; + } } } .right-header { diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 0f2ccf855c892e813002809742931f2a50f794f7..db8ce67263eedc9b6fb2638225526f893a7299c3 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,10 +1,12 @@ <div fxLayout="row" class="content-container"> <app-structure-list (searchEvent)="getStructures($event)" + (loadMoreStructures)="loadMoreStructures()" [structureList]="structures" [location]="currentLocation" (displayMapMarkerId)="setMapMarkerId($event)" (selectedMarkerId)="setSelectedMarkerId($event)" + [selectedStructure]="currentStructure" class="left-pane" fxLayout="column" ></app-structure-list> @@ -12,6 +14,7 @@ [structures]="structures" [toogleToolTipId]="displayMarkerId" [selectedMarkerId]="selectedMarkerId" + (selectedStructure)="showDetailStructure($event)" class="right-pane" ></app-map> </div> diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss index e501173f8a4396e1be6c09814793874c8da26f6d..bfa9fd02cfa8fcfc20683307ba925805ecc98d91 100644 --- a/src/app/home/home.component.scss +++ b/src/app/home/home.component.scss @@ -4,8 +4,7 @@ } .right-pane { width: 80%; - height: 80%; - padding: 0 40px 40px 40px; + padding: 0 40px; } .content-container { height: 100%; diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 2a1d85c1d7b75b2b3f477fbbecd636f0a64344fb..c96c6f15b2e1c9b5837c08df00599c15094aa6ae 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -21,6 +21,10 @@ export class HomeComponent implements OnInit { public selectedMarkerId: number; public geolocation = false; public currentLocation: GeoJson; + public pageStructures = 0; + public structuresChunked: Structure[][] = []; + private chunck = 10; + public currentStructure: Structure; constructor(private structureService: StructureService, private geoJsonService: GeojsonService) {} ngOnInit(): void { @@ -31,23 +35,35 @@ export class HomeComponent implements OnInit { } public getStructures(filters: Filter[]): void { + if (filters) { + this.pageStructures = 0; + this.structuresChunked = []; + } this.structureService.getStructures(filters).subscribe((structures) => { - console.log(filters); filters ? (structures = this.applyFilters(structures, filters)) : structures; - - Promise.all( - structures.map((structure) => { - if (this.geolocation) { - return this.getStructurePosition(structure).then((val) => { - return this.structureService.updateOpeningStructure(val, DateTime.local()); - }); - } else { - return this.structureService.updateOpeningStructure(structure, DateTime.local()); + if (structures) { + Promise.all( + structures.map((structure) => { + if (this.geolocation) { + return this.getStructurePosition(structure).then((val) => { + return this.structureService.updateOpeningStructure(val, DateTime.local()); + }); + } else { + return this.structureService.updateOpeningStructure(structure, DateTime.local()); + } + }) + ).then((structureList) => { + structureList = _.sortBy(structureList, ['distance']); + if (this.pageStructures == 0) { + for (let i = 0; i < structureList.length; i += this.chunck) { + this.structuresChunked.push(structureList.slice(i, i + this.chunck)); + } } - }) - ).then((structureList) => { - this.structures = _.sortBy(structureList, ['distance']); - }); + this.structures = this.structuresChunked[0]; + }); + } else { + this.structures = null; + } }); } @@ -128,4 +144,16 @@ export class HomeComponent implements OnInit { public setSelectedMarkerId(id: number): void { this.selectedMarkerId = id; } + + public loadMoreStructures(): void { + if (this.pageStructures < this.structuresChunked.length - 1) { + this.pageStructures++; + const newStructures = _.map(this.structuresChunked[this.pageStructures]); + this.structures = [...this.structures, ...newStructures]; + } + } + + public showDetailStructure(structure: Structure): void { + this.currentStructure = new Structure(structure); + } } diff --git a/src/app/map/components/map.component.scss b/src/app/map/components/map.component.scss index e4ff668495cf978ff3ea268898d9f78f67971ee6..c6eba45fc010f38e7b433a5d3b380e449ebc0c1b 100644 --- a/src/app/map/components/map.component.scss +++ b/src/app/map/components/map.component.scss @@ -3,6 +3,7 @@ @import '../../../assets/scss/icons'; @import '../../../assets/scss/typography'; @import '../../../assets/scss/shapes'; +@import '../../../assets/scss/buttons'; .map-wrapper { border-radius: 6px; @@ -11,11 +12,15 @@ } #map { - height: calc(100vh - #{$header-height} - #{$footer-height} - 120px); + height: calc(100vh - #{$header-height} - #{$footer-height} - 87px); border: 10px solid $white; border-radius: 6px; } +::ng-deep .leaflet-popup-close-button { + display: none; +} + ::ng-deep .fa-map-marker { color: $black; position: absolute; @@ -55,9 +60,11 @@ stroke-width: unset !important; } } + margin-top: 0 !important; + margin-left: -21px !important; } -::ng-deep .leaflet-tooltip { +::ng-deep .leaflet-popup { padding: 8px 10px 8px 10px; border-radius: 6px; h1 { @@ -70,9 +77,18 @@ @include cn-regular-16; margin: 0 0 13px 0; } - div { - align-items: center; - display: flex; + .pop-up { + text-align: center; + padding-top: 20px; + + button { + @include btn-search-filter; + @include cn-bold-14; + width: 149px; + } + } + span { + display: inline-block; } } diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts index 732c1c0b56aefb1e5cf9d0118e3c02583140ba92..9e92dd389566c7488649cf53cb5ac5788b261812 100644 --- a/src/app/map/components/map.component.ts +++ b/src/app/map/components/map.component.ts @@ -1,13 +1,22 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; -import { latLng, MapOptions, tileLayer, Map, CRS, TileLayer, LatLngBounds, latLngBounds, Control } from 'leaflet'; +import { + Component, + EventEmitter, + HostListener, + Input, + OnChanges, + Output, + SimpleChanges, + ViewChild, +} from '@angular/core'; +import { latLng, MapOptions, tileLayer, Map, CRS, TileLayer, LatLngBounds, latLngBounds, Marker } from 'leaflet'; import { Observable } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { Structure } from '../../models/structure.model'; import { GeoJson } from '../models/geojson.model'; import { GeojsonService } from '../../services/geojson.service'; import { MapService } from '../services/map.service'; -import { LeafletControlLayersChanges } from '@asymmetrik/ngx-leaflet'; import { NgxLeafletLocateComponent } from '@runette/ngx-leaflet-locate'; +import * as _ from 'lodash'; @Component({ selector: 'app-map', @@ -19,6 +28,9 @@ export class MapComponent implements OnChanges { @Input() public toogleToolTipId: number; @Input() public selectedMarkerId: number; @ViewChild(NgxLeafletLocateComponent, { static: false }) locateComponent: NgxLeafletLocateComponent; + @Output() selectedStructure: EventEmitter<Structure> = new EventEmitter<Structure>(); + private currentStructure: Structure; + public map: Map; public mapOptions: MapOptions; // Init locate options @@ -30,13 +42,21 @@ export class MapComponent implements OnChanges { circlePadding: [5, 5], }; + // Add listener on the popup button to show details of structure + @HostListener('document:click', ['$event']) + public clickout(event): void { + if (event.target.classList.contains('btnShowDetails')) { + this.selectedStructure.emit(this.currentStructure); + } + } + constructor(private mapService: MapService, private geoJsonService: GeojsonService) { this.initializeMapOptions(); } ngOnChanges(changes: SimpleChanges): void { if (changes.structures) { - this.getStructurePosition(); + this.handleStructurePosition(changes.structures.previousValue); } // Handle map marker tooltip if (changes.toogleToolTipId && changes.toogleToolTipId.currentValue !== changes.toogleToolTipId.previousValue) { @@ -59,12 +79,31 @@ export class MapComponent implements OnChanges { /** * Get structures positions and add marker corresponding to those positons on the map */ - private getStructurePosition(): void { - this.structures.forEach((element: Structure) => { + private handleStructurePosition(previousStructuresValue: Structure[]): void { + // If there is more structure than before, append them + if ( + previousStructuresValue && + previousStructuresValue.length > 0 && + previousStructuresValue.length < this.structures.length + ) { + const newStructures = _.differenceWith(this.structures, previousStructuresValue, _.isEqual); + this.getStructuresPositions(newStructures); + } else if (this.structures) { + this.map = this.mapService.cleanMap(this.map); + this.getStructuresPositions(this.structures); + } + } + + private getStructuresPositions(structureListe: Structure[]): void { + structureListe.forEach((element: Structure) => { this.getCoord(element.voie).subscribe((coord: GeoJson) => { this.mapService .createMarker(coord.geometry.getLon(), coord.geometry.getLat(), element.id, this.buildToolTip(element)) - .addTo(this.map); + .addTo(this.map) + // store structure before user click on button + .on('popupopen', () => { + this.currentStructure = element; + }); }); }); } @@ -82,7 +121,6 @@ export class MapComponent implements OnChanges { cssAvailabilityClass = 'unknown'; } } - return ( '<h1>' + structure.nomDeVotreStructure + @@ -94,7 +132,7 @@ export class MapComponent implements OnChanges { cssAvailabilityClass + '"></span><span>' + structure.openDisplay() + - '</span></div>' + '</span></div><div class="pop-up"><button type="button" class="btnShowDetails">Voir</button></div>' ); } diff --git a/src/app/map/services/map.service.ts b/src/app/map/services/map.service.ts index 1668a68464c07bf58a5a7d022a0f92b380b4f51b..9be8deb95bb4b021824d1412652e121f7d1c7b99 100644 --- a/src/app/map/services/map.service.ts +++ b/src/app/map/services/map.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import { divIcon } from 'leaflet'; -import { icon, Marker, Map } from 'leaflet'; +import { divIcon, Map } from 'leaflet'; +import { Marker } from 'leaflet'; @Injectable({ providedIn: 'root', @@ -17,12 +17,12 @@ export class MapService { iconAnchor: [13, 41], }); const marker = new Marker([lat, lon], { icon: markerIcon }); + marker.on('mouseover', (evt) => { + evt.target.openPopup(); + }); if (tooltip) { - marker.bindTooltip(tooltip, { - opacity: 1, - direction: 'top', - }); + marker.bindPopup(tooltip); } MapService.markersList[id] = marker; return marker; @@ -44,7 +44,7 @@ export class MapService { */ public toogleToolTip(id: number): void { if (id) { - this.getMarker(id).toggleTooltip(); + this.getMarker(id).togglePopup(); } } @@ -97,4 +97,14 @@ export class MapService { public getMarker(id: number): Marker { return MapService.markersList[id] ? MapService.markersList[id] : null; } + + public cleanMap(map: Map): Map { + MapService.markersList = {}; + if (map) { + map.eachLayer((layer) => { + if (layer instanceof Marker) map.removeLayer(layer); + }); + } + return map; + } } diff --git a/src/app/shared/components/button/button.component.html b/src/app/shared/components/button/button.component.html new file mode 100644 index 0000000000000000000000000000000000000000..bd98c853b5a423f2df286050a247b8ffdb289023 --- /dev/null +++ b/src/app/shared/components/button/button.component.html @@ -0,0 +1,19 @@ +<ng-container *ngIf="style === 'buttonWithHash'"> + <button class="btnSearch" type="{{ type }}"> + <div *ngIf="!iconBtn" class="searchButton">{{ text }}</div> + <div + *ngIf="iconBtn" + fxLayout="row center" + class="searchButton withIcon" + fxLayoutAlign="space-between center" + fxLayoutGap="13px" + > + <app-svg-icon style="height: 100%" [type]="'ico'" [iconClass]="'icon-32'" [icon]="iconBtn"></app-svg-icon> + {{ text }} + </div> + </button> +</ng-container> + +<ng-container *ngIf="style === 'button'"> + <button class="btn-search-filter" type="{{ type }}">{{ text }}</button> +</ng-container> diff --git a/src/app/shared/components/button/button.component.scss b/src/app/shared/components/button/button.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..6506693b2e69be5ada4de9237f274d9d5755e2e9 --- /dev/null +++ b/src/app/shared/components/button/button.component.scss @@ -0,0 +1,74 @@ +@import '../../../../assets/scss/typography'; +@import '../../../../assets/scss/color'; +@import '../../../../assets/scss/shapes'; + +@mixin btn-bold { + @include cn-bold-16; + line-height: 18px; +} +@mixin btn-bold-underline { + @include btn-bold; + text-decoration: underline; +} +@mixin btn-regular { + @include cn-regular-16; + line-height: 19px; +} + +button { + outline: none; + border-radius: 4px; + cursor: pointer; + border: 1px solid; +} +.btnSearch { + @include background-hash; + border-color: $grey-4; + padding: 0 0 4px 5px; + &:hover { + border-color: $grey-6; + .searchButton { + color: $grey-3 !important; + } + } + &:focus { + border-color: $blue-hover; + } + &:active { + background: none; + border-color: $grey-6; + } + .searchButton { + background: $white; + height: 31px; + color: $primary-color; + padding: 3px 16px 3px 16px; + display: table-cell; + vertical-align: middle; + @include btn-bold; + &.withIcon { + color: $black; + height: 36px; + } + } +} +.btn-search-filter { + background: $secondary-color; + height: 40px; + color: $white; + padding: 4px 37px 4px 37px; + border-color: transparent; + @include btn-bold; + line-break: 18px; + &:hover { + background-color: $blue-hover; + } + &:focus { + background-color: $white; + border-color: $secondary-color; + color: $secondary-color; + } + &:active { + background-color: $blue-active; + } +} diff --git a/src/app/shared/components/button/button.component.spec.ts b/src/app/shared/components/button/button.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b2d1343741e4a367e4dce58f1549fc88dd4b29d --- /dev/null +++ b/src/app/shared/components/button/button.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ButtonComponent } from './button.component'; + +describe('button', () => { + let component: ButtonComponent; + let fixture: ComponentFixture<ButtonComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ButtonComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/button/button.component.ts b/src/app/shared/components/button/button.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1e007e33dbfddd5ab19fe885c53cafe0516ce29 --- /dev/null +++ b/src/app/shared/components/button/button.component.ts @@ -0,0 +1,16 @@ +import { Component, Input, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-button', + templateUrl: './button.component.html', + styleUrls: ['./button.component.scss'], +}) +export class ButtonComponent implements OnInit { + @Input() public style: string; + @Input() public text: string; + @Input() public type: string; + @Input() public iconBtn: string; + constructor() {} + + ngOnInit(): void {} +} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index ae8dcc9b9cbbd9db5d243cf0428f4f6498db533d..c16e176b8a231207116e9055974735f25a43259e 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -1,8 +1,10 @@ +import { ButtonComponent } from './button/button.component'; import { LogoCardComponent } from './logo-card/logo-card.component'; +import { ModalComponent } from './modal/modal.component'; import { SvgIconComponent } from './svg-icon/svg-icon.component'; // tslint:disable-next-line: max-line-length -export { LogoCardComponent, SvgIconComponent }; +export { LogoCardComponent, SvgIconComponent, ModalComponent, ButtonComponent }; // tslint:disable-next-line:variable-name -export const SharedComponents = [LogoCardComponent, SvgIconComponent]; +export const SharedComponents = [LogoCardComponent, SvgIconComponent, ModalComponent, ButtonComponent]; diff --git a/src/app/shared/components/modal/modal.component.html b/src/app/shared/components/modal/modal.component.html new file mode 100644 index 0000000000000000000000000000000000000000..44094a5b8ae11d23fda1f3e476ac33543a2b7ed0 --- /dev/null +++ b/src/app/shared/components/modal/modal.component.html @@ -0,0 +1,15 @@ +<div class="cModal" [ngClass]="openned ? 'oModal' : ''"> + <div (clickOutside)="closeModal()"> + <p>{{ content }}</p> + <footer class="footer"> + <a + (click)="closeModal()" + href="https://services.formulaireextranet.grandlyon.com/saisie-fiche-structure/" + tabindex="0" + target="_blank" + rel="noreferrer noopener" + >Continuer vers le formulaire</a + > + </footer> + </div> +</div> diff --git a/src/app/shared/components/modal/modal.component.scss b/src/app/shared/components/modal/modal.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..1ef0ec830fadf90089bf47a5dc4f89e69a1d6cd2 --- /dev/null +++ b/src/app/shared/components/modal/modal.component.scss @@ -0,0 +1,39 @@ +@import '../../../../assets/scss/typography'; +@import '../../../../assets/scss/color'; +@import '../../../../assets/scss/buttons'; +@import '../../../../assets/scss/z-index'; +@import '../../../../assets/scss/hyperlink'; +.cModal { + position: fixed; + z-index: $modal-add-structure-z-index; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: $modal-background; + display: none; +} +.cModal > div { + max-width: 450px; + position: relative; + top: 40%; + left: 50%; + transform: translate(-50%, -50%); + border-radius: 2px; + background: $white; + text-align: center; + border-radius: 4px; + @include cn-bold-16; + p { + padding: 30px 48px 0 40px; + } +} +.oModal { + display: block; +} +.footer { + a { + @include hyperlink; + } + padding: 28px; +} diff --git a/src/app/shared/components/modal/modal.component.spec.ts b/src/app/shared/components/modal/modal.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..8f64b3e86881e22f2d2378db6bf6cfa2954e4c13 --- /dev/null +++ b/src/app/shared/components/modal/modal.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalComponent } from './modal.component'; + +describe('ModalComponent', () => { + let component: ModalComponent; + let fixture: ComponentFixture<ModalComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ModalComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/modal/modal.component.ts b/src/app/shared/components/modal/modal.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4a3566e38eea410d444a0b1a3518ec19c620b7a --- /dev/null +++ b/src/app/shared/components/modal/modal.component.ts @@ -0,0 +1,19 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +@Component({ + selector: 'app-modal', + templateUrl: './modal.component.html', + styleUrls: ['./modal.component.scss'], +}) +export class ModalComponent implements OnInit { + constructor() {} + + @Input() public openned: boolean; + @Input() public content: string; + @Output() closed = new EventEmitter(); + ngOnInit(): void {} + + public closeModal(): void { + this.closed.emit(); + } +} diff --git a/src/app/shared/directives/index.ts b/src/app/shared/directives/index.ts index 9cee80e98a91ceba14c2c0c1356f669d0d347793..6f873009890cff2f656316fadce624731b672a4e 100644 --- a/src/app/shared/directives/index.ts +++ b/src/app/shared/directives/index.ts @@ -1,4 +1,6 @@ -export {}; +import { ModalOutsideDirective } from './modalOutside.directive'; + +export { ModalOutsideDirective }; // tslint:disable-next-line:variable-name -export const SharedDirectives = []; +export const SharedDirectives = [ModalOutsideDirective]; diff --git a/src/app/structure-list/services/modalOutside.directive.ts b/src/app/shared/directives/modalOutside.directive.ts similarity index 100% rename from src/app/structure-list/services/modalOutside.directive.ts rename to src/app/shared/directives/modalOutside.directive.ts diff --git a/src/app/structure-list/components/modal-filter/modal-filter.component.html b/src/app/structure-list/components/modal-filter/modal-filter.component.html index e09cabe48ca53f996ee85f1b5af6d9b336544530..84740d349587af4e71c2601a88f8d56ca2721aa0 100644 --- a/src/app/structure-list/components/modal-filter/modal-filter.component.html +++ b/src/app/structure-list/components/modal-filter/modal-filter.component.html @@ -34,8 +34,8 @@ </div> </div> <div class="footer" fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="3vw"> - <a tabindex="0" (click)="clearFilters()">Effacer</a> - <button type="button" (click)="emitModules(checkedModules)">Appliquer</button> + <a (click)="clearFilters()" tabindex="0">Effacer</a> + <app-button [style]="'button'" [link]="" [text]="'Appliquer'" (click)="emitModules(checkedModules)"></app-button> </div> </div> </div> diff --git a/src/app/structure-list/components/modal-filter/modal-filter.component.scss b/src/app/structure-list/components/modal-filter/modal-filter.component.scss index 7e449bd96566a24534e970d9e1e5ebcc8353372f..24ce62663174a3f186a1878b22b41f63e6c42bcb 100644 --- a/src/app/structure-list/components/modal-filter/modal-filter.component.scss +++ b/src/app/structure-list/components/modal-filter/modal-filter.component.scss @@ -3,7 +3,7 @@ @import '../../../../assets/scss/typography'; @import '../../../../assets/scss/breakpoint'; @import '../../../../assets/scss/shapes'; -@import '../../../../assets/scss/buttons'; +@import '../../../../assets/scss/hyperlink'; .modaltraining { @media #{$desktop} { @@ -96,38 +96,9 @@ } .footer { margin: 0px 20px 16px 0; - a { - @include btn-search-addStructure; - border: 1px solid transparent; - padding: 8px 8px 6px 8px; - - &:hover { - color: $blue-hover; - } - &:focus { - color: $secondary-color; - border-color: $secondary-color; - border-radius: 4px; - } - &:active { - border: none; - color: $blue-active; - } - } height: 32px; - button { - @include btn-search-filter; - &:hover { - background-color: $blue-hover; - } - &:focus { - background-color: $white; - border-color: $secondary-color; - color: $secondary-color; - } - &:active { - background-color: $blue-active; - } - } } } +a { + @include hyperlink; +} diff --git a/src/app/structure-list/components/search/search.component.html b/src/app/structure-list/components/search/search.component.html index 1a8539fc30d9dfeed94ca3aa1aedc2a22e8624ae..35639e453e6c32c81cf6f350bb9464041165cfc8 100644 --- a/src/app/structure-list/components/search/search.component.html +++ b/src/app/structure-list/components/search/search.component.html @@ -12,56 +12,55 @@ > <div class="inputSection" fxLayout="row" fxLayoutAlign="space-between center"> <input type="text" formControlName="searchTerm" placeholder="Rechercher une adresse, une association..." /> - <button type="button" (click)="clearInput()" class="icon close"><div class="ico-close-search"></div></button> - + <button (click)="clearInput()" class="icon close" type="button"><div class="ico-close-search"></div></button> <span class="separator"></span> - <button class="icon pin" type="button" (click)="locateMe()"><div class="ico-pin-search"></div></button> + <button (click)="locateMe()" class="icon pin" type="button"><div class="ico-pin-search"></div></button> </div> - - <button class="btnSearch" type="submit"> - <div class="searchButton">Rechercher</div> - </button> + <app-button [style]="'buttonWithHash'" [text]="'Rechercher'" [type]="'submit'"></app-button> </form> </div> <div (clickOutside)="closeModal()"> <div class="btnSection" fxLayout="row" fxLayoutAlign="space-between center"> <button + class="btn-filter" type="button" + fxLayout="row" [ngClass]="{ selected: modalTypeOpened === TypeModal.accompaniment, containCheckedFilters: numberAccompanimentChecked }" - (click)="openModal(TypeModal.accompaniment)" - fxLayout="row" fxLayoutAlign="space-between center" + (click)="openModal(TypeModal.accompaniment)" > - <span class="btnText">Accompagnement</span> + <span>Accompagnement</span> <div class="arrow"></div> </button> <button + class="btn-filter" type="button" + fxLayout="row" [ngClass]="{ selected: modalTypeOpened === TypeModal.training, containCheckedFilters: numberTrainingChecked }" - (click)="openModal(TypeModal.training)" - fxLayout="row" fxLayoutAlign="space-between center" + (click)="openModal(TypeModal.training)" > - <span class="btnText">Formations</span> + <span>Formations</span> <div class="arrow"></div> </button> <button + class="btn-filter" type="button" + fxLayout="row" [ngClass]="{ selected: modalTypeOpened === TypeModal.moreFilters, containCheckedFilters: numberMoreFiltersChecked }" - (click)="openModal(TypeModal.moreFilters)" - fxLayout="row" fxLayoutAlign="space-between center" + (click)="openModal(TypeModal.moreFilters)" > - <span class="btnText">Plus de filtres</span> + <span>Plus de filtres</span> <div class="arrow"></div> </button> </div> @@ -91,7 +90,10 @@ </label> </div> </div> - <a href="https://services.formulaireextranet.grandlyon.com/saisie-fiche-structure/" target="_blank" - >Ajouter une structure</a - > + <a (click)="openConfirmationModal()" tabindex="0">Ajouter une structure</a> </div> +<app-modal + [openned]="isConfirmationModalOpen" + [content]="confirmationModalContent" + (closed)="closeConfirmationModal()" +></app-modal> diff --git a/src/app/structure-list/components/search/search.component.scss b/src/app/structure-list/components/search/search.component.scss index 4f2d6c8e59363dbea201ca76138de20447c4708d..1c2a86a42f51e4932ec27750a7764d3caba77ee1 100644 --- a/src/app/structure-list/components/search/search.component.scss +++ b/src/app/structure-list/components/search/search.component.scss @@ -1,10 +1,7 @@ -@import '../../../../assets/scss/icons'; @import '../../../../assets/scss/color'; @import '../../../../assets/scss/typography'; -@import '../../../../assets/scss/breakpoint'; -@import '../../../../assets/scss/shapes'; -@import '../../../../assets/scss/buttons'; @import '../../../../assets/scss/inputs'; +@import '../../../../assets/scss/hyperlink'; .header { .title { @@ -23,41 +20,6 @@ @include input-search; } .searchSection { - .icon { - background-color: transparent; - border: 1px solid transparent; - outline: none; - cursor: pointer; - &.pin { - padding: 4px 6px 8px 6px; - &:hover { - .ico-pin-search { - background-color: $blue-hover; - } - } - &:focus { - border-color: $secondary-color; - .ico-pin-search { - background-color: $secondary-color; - } - } - &:active { - border-color: transparent; - .ico-pin-search { - background-color: $blue-active; - } - } - } - &.close { - padding: 4px 7px 10px 5px; - &:focus { - border-color: $secondary-color; - } - &:active { - border-color: transparent; - } - } - } .separator { height: 100%; width: 2px; @@ -71,42 +33,32 @@ background-color: $white; height: 40px; } - .btnSearch { - @include background-hash; - border: 1px solid $grey-4; - border-radius: 4px; - padding: 0 0 4px 5px; - outline: none; - &:hover { - border-color: $grey-6; - .searchButton { - color: $grey-3; - } - } - &:focus { - border-color: $blue-hover; - } - &:active { - background: none; - border-color: $grey-6; - } - .searchButton { - border-radius: 4px; - @include btn-search; - } - } } .btnSection { padding: 16px 0 0px 0; button { - @include btn-filter; + background: $white; + height: 40px; + width: 190px; + border: 1px solid $grey-4; + padding: 3px 16px 3px 16px; + outline: none; + border-radius: 4px; + cursor: pointer; + @include btn-normal; + .arrow { + background-color: transparent; + border-bottom: 1px solid $grey-2; + border-right: 1px solid $grey-2; + transform: translateY(-25%) rotate(45deg); + margin: 0 5px 0 10px; + height: 7px; + width: 7px; + } &:focus { - border-color: $secondary-color; + border-color: $blue-hover; } } - .containCheckedFilters { - border-color: $secondary-color; - } .selected { border-color: $primary-color !important; color: inherit; @@ -120,14 +72,8 @@ width: 7px; } } - .arrow { - background-color: transparent; - border-bottom: 1px solid $grey-2; - border-right: 1px solid $grey-2; - transform: translateY(-25%) rotate(45deg); - margin: 0 5px 0 10px; - height: 7px; - width: 7px; + .containCheckedFilters { + border-color: $secondary-color; } } } @@ -135,22 +81,42 @@ .footerSearchSection { margin: 8px 0px 8px 0px; height: 40px; - a { - @include btn-search-addStructure; - border: 1px solid transparent; - padding: 8px 8px 6px 8px; +} +.icon { + background-color: transparent; + border: 1px solid transparent; + outline: none; + cursor: pointer; + &.pin { + padding: 4px 6px 8px 6px; &:hover { - color: $blue-hover; + .ico-pin-search { + background-color: $blue-hover; + } } &:focus { - color: $secondary-color; border-color: $secondary-color; - border-radius: 4px; + .ico-pin-search { + background-color: $secondary-color; + } } &:active { - border: none; - color: $blue-active; + border-color: transparent; + .ico-pin-search { + background-color: $blue-active; + } } } + &.close { + &:focus { + border-color: $secondary-color; + } + &:active { + border-color: transparent; + } + } +} +a { + @include hyperlink; } diff --git a/src/app/structure-list/components/search/search.component.ts b/src/app/structure-list/components/search/search.component.ts index 414dc2df46c39edba8574b53c1b82303f6890fc7..39a0f9eaafc65cfa68f429eecf5a6c1f9b2bcc46 100644 --- a/src/app/structure-list/components/search/search.component.ts +++ b/src/app/structure-list/components/search/search.component.ts @@ -36,6 +36,11 @@ export class SearchComponent implements OnInit { public numberAccompanimentChecked = 0; public numberMoreFiltersChecked = 0; + // Modal confirmation variable + public isConfirmationModalOpen = false; + public confirmationModalContent = + 'Afin d’ajouter votre structure,vous allez être redirigé vers le formulaire Grand Lyon à remplir.'; + ngOnInit(): void { // Will store the different categories this.categories = []; @@ -216,4 +221,10 @@ export class SearchComponent implements OnInit { ); } } + public openConfirmationModal(): void { + this.isConfirmationModalOpen = true; + } + public closeConfirmationModal(): void { + this.isConfirmationModalOpen = false; + } } diff --git a/src/app/structure-list/components/structure-details/structure-details.component.html b/src/app/structure-list/components/structure-details/structure-details.component.html index 5edf8c8a507a9a924630e3bbfa01e93413c14806..a01d871b8bb7e46e37832fd7880c09336578cf0d 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.html +++ b/src/app/structure-list/components/structure-details/structure-details.component.html @@ -3,13 +3,24 @@ <div fxLayout="row" class="structrue-details-block" fxLayoutAlign="baseline baseline" fxLayoutGap="20px"> <em class="ic-arrow-left clickable" (click)="close()"></em> <div fxLayout="column" fxLayoutGap="10px" fxFlex="100%"> - <div fxLayout="column" fxLayoutAlign="center"> - <h2 class="bold">{{ structure.nomDeVotreStructure }}</h2> - <h3>{{ structure.typeDeStructure }}</h3> + <div fxLayout="row" fxLayoutAlign="space-between"> + <div fxLayout="column center"> + <h2 class="bold">{{ structure.nomDeVotreStructure }}</h2> + <h3>{{ structure.typeDeStructure }}</h3> + </div> + <div fxLayout="column" fxLayoutAlign="end"> + <app-button [type]="'button'" [style]="'buttonWithHash'" [text]="'Imprimer'" [iconBtn]="'print'"></app-button> + </div> </div> - <app-structure-opening-status [structure]="structure"></app-structure-opening-status> + <div fxLayout="row"> <div fxLayout="column" fxFlex="60%"> + <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="13px"> + <app-structure-opening-status + [structure]="structure" + [isCalledByDetails]="true" + ></app-structure-opening-status> + </div> <div *ngIf="structure.voie" fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="13px"> <app-svg-icon [type]="'ico'" [icon]="'adress'"></app-svg-icon> <p>{{ structure.n }} {{ structure.address }}</p> @@ -28,6 +39,10 @@ <app-svg-icon [type]="'ico'" [icon]="'email'"></app-svg-icon> <a [href]="'mailto:' + structure.courriel">{{ structure.courriel }}</a> </div> + <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="13px"> + <app-svg-icon [type]="'ico'" [icon]="'calendar'"></app-svg-icon> + <p>Mise-à -jour le {{ structure.derniereModification | date: 'shortDate' }}</p> + </div> </div> </div> </div> diff --git a/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.html b/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.html index a03a3a762955c03638985b1f0d9008dc714e76d7..384d443c5e6ac1173978280c867a0ebadae1c53d 100644 --- a/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.html +++ b/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.html @@ -1,7 +1,7 @@ -<div class="statusStructure" fxLayout="row" fxLayoutAlign="start center"> +<div class="statusStructure" [class]="isCalledByDetails ? 'details' : ''" fxLayout="row" fxLayoutAlign="start center"> <div> <span *ngIf="structure.isOpen; else closed" class="ico-dot-available"></span> - <span>{{ structure.openDisplay() }}</span> + <span class="text">{{ structure.openDisplay() }}</span> </div> <ng-template #closed> <span *ngIf="structure.openedOn.day; else unkown" class="ico-dot-unavailable"></span> diff --git a/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.scss b/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.scss index c4855a912d105ec405b0afa7e954a4112b47c5cb..f25bb4708b77947af128c6c97e18c8627a77dc1d 100644 --- a/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.scss +++ b/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.scss @@ -8,3 +8,15 @@ display: inline-block; } } +.details { + span { + margin-right: 12px; + padding: 1px; + margin-left: 5px; + @include cn-regular-16; + } + .text { + margin-top: 9px; + margin-bottom: 9px; + } +} diff --git a/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.ts b/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.ts index c2fe6417698033a5a05999f2a3fa05af1240225f..2c1e0afa7e0ac0b36e27a7dec2088cc956812c6a 100644 --- a/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.ts +++ b/src/app/structure-list/components/structure-opening-status/structure-opening-status.component.ts @@ -8,6 +8,7 @@ import { Structure } from '../../../models/structure.model'; }) export class StructureOpeningStatusComponent implements OnInit { @Input() public structure: Structure; + @Input() public isCalledByDetails: boolean; constructor() {} diff --git a/src/app/structure-list/structure-list.component.html b/src/app/structure-list/structure-list.component.html index cf88f4479d2217165b4b6337f1e2e63f8b75c05b..1d80802a9cfddc7a15cde77973eaef686bc4c09e 100644 --- a/src/app/structure-list/structure-list.component.html +++ b/src/app/structure-list/structure-list.component.html @@ -1,9 +1,11 @@ <div class="topBlock"> <app-structure-list-search (searchEvent)="fetchResults($event)"></app-structure-list-search> </div> -<div class="nbStructuresLabel">{{ structureList.length }} structure{{ structureList.length > 1 ? 's' : '' }}</div> +<div class="nbStructuresLabel"> + {{ structureList ? structureList.length : '0' }} structure{{ structureList && structureList.length > 1 ? 's' : '' }} +</div> -<div class="listCard" (mouseout)="mouseOut()"> +<div (scroll)="onScrollDown($event)" class="listCard" (mouseout)="mouseOut()"> <app-card *ngFor="let structure of structureList" [structure]="structure" diff --git a/src/app/structure-list/structure-list.component.scss b/src/app/structure-list/structure-list.component.scss index b619da552a6d813ad91cec12773e16be18da7683..66ef37ec675564b3414adfa2bdb33169d0365842 100644 --- a/src/app/structure-list/structure-list.component.scss +++ b/src/app/structure-list/structure-list.component.scss @@ -13,7 +13,6 @@ margin: 0 16px; } .listCard { - overflow-y: scroll; overflow-y: overlay; padding: 0 25px; } diff --git a/src/app/structure-list/structure-list.component.ts b/src/app/structure-list/structure-list.component.ts index 1a849b45becdd23db725f0ba451244cae03749ea..33f3417b37517218ddcdf1a67698c6821043adf9 100644 --- a/src/app/structure-list/structure-list.component.ts +++ b/src/app/structure-list/structure-list.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core'; import { Filter } from './models/filter.model'; import { Structure } from '../models/structure.model'; import { GeoJson } from '../map/models/geojson.model'; @@ -7,19 +7,24 @@ import { GeoJson } from '../map/models/geojson.model'; templateUrl: './structure-list.component.html', styleUrls: ['./structure-list.component.scss'], }) -export class StructureListComponent implements OnInit { +export class StructureListComponent implements OnChanges { @Input() public structureList: Structure[]; @Output() searchEvent = new EventEmitter(); @Input() public location: GeoJson; + @Input() public selectedStructure: Structure = new Structure(); @Output() public displayMapMarkerId: EventEmitter<Array<number>> = new EventEmitter<Array<number>>(); @Output() public hoverOut: EventEmitter<Array<number>> = new EventEmitter<Array<number>>(); @Output() public selectedMarkerId: EventEmitter<number> = new EventEmitter<number>(); + @Output() loadMoreStructures = new EventEmitter(); public showStructureDetails = false; public structure: Structure; constructor() {} - ngOnInit(): void {} - + ngOnChanges(changes: SimpleChanges): void { + if (changes.selectedStructure && this.selectedStructure) { + this.showDetails(this.selectedStructure); + } + } public fetchResults(filters: Filter[]): void { this.searchEvent.emit(filters); } @@ -41,4 +46,13 @@ export class StructureListComponent implements OnInit { public mouseOut(): void { this.displayMapMarkerId.emit([undefined]); } + + public onScrollDown(event): void { + if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight - 100) { + console.log('loading...'); + } + if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight - 50) { + this.loadMoreStructures.emit(); + } + } } diff --git a/src/assets/scss/_color.scss b/src/assets/scss/_color.scss index 4d99113345b1272768d49d373e5938d3a6155b6a..2e5b3fbe2838fc61c7800875b4d226b44632f925 100644 --- a/src/assets/scss/_color.scss +++ b/src/assets/scss/_color.scss @@ -15,7 +15,6 @@ $red: #f98181; $blue: #348899; $blue-hover: #117083; $blue-active: #8cb6be; -$grey-1: #594d59; $red-metro: #d50000; /* APP COLORS */ $primary-color: $red-metro; @@ -23,3 +22,4 @@ $secondary-color: $blue; $default-link-color: $grey-2; $button-secondary: $blue; $app-background: $grey-6; +$modal-background: rgba($grey-1, 0.75); diff --git a/src/assets/scss/_hyperlink.scss b/src/assets/scss/_hyperlink.scss new file mode 100644 index 0000000000000000000000000000000000000000..5eed9c1f2ce663bbcf14ac5ed6b685ebb54aa8ac --- /dev/null +++ b/src/assets/scss/_hyperlink.scss @@ -0,0 +1,20 @@ +@import './typography.scss'; +@mixin hyperlink { + border: 1px solid transparent; + padding: 8px 8px 6px 8px; + color: $secondary-color; + outline: none; + @include btn-bold-sousligne; + &:hover { + color: $blue-hover; + } + &:focus { + color: $secondary-color; + border-color: $secondary-color; + border-radius: 4px; + } + &:active { + border: none; + color: $blue-active; + } +} diff --git a/src/assets/scss/_icons.scss b/src/assets/scss/_icons.scss index 052eb5e858a35eeb392dee9a7e5086aa5d6267e7..adcc85c0df44df682ad939230ca41e63235ce1e5 100644 --- a/src/assets/scss/_icons.scss +++ b/src/assets/scss/_icons.scss @@ -171,9 +171,10 @@ } .ico-close-search { - width: 15px; - height: 7px; - text-align: center; + width: 16px; + height: 16px; + display: inline-block; + vertical-align: middle; &:before { transform: rotate(45deg); } @@ -185,7 +186,7 @@ .ico-close-search:after { position: absolute; content: ''; - height: 14px; + height: 16px; width: 2px; background-color: $grey-4; } diff --git a/src/assets/scss/_z-index.scss b/src/assets/scss/_z-index.scss index fee23f71f9f88cc52f42772e9d0c3a5bb8402a8a..5180807ab293ecf5bb1d08d0aebdba09b4d42a53 100644 --- a/src/assets/scss/_z-index.scss +++ b/src/assets/scss/_z-index.scss @@ -1 +1,2 @@ $structure-details-z-index: 1001; +$modal-add-structure-z-index: 1001; diff --git a/src/styles.scss b/src/styles.scss index d79b100f2e1dcc052123ad4db4107136f46898b3..a32ecf96448f9befab94b8d6a1efecdca64ef3e4 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -40,7 +40,7 @@ a { // Containers .content-container { margin: 0; - padding: 30px 0 30px 0; + padding-top: 30px; width: 100%; height: 100%; &.medium-pt {