diff --git a/api/server.js b/api/server.js index e85ef232a7d69299e0ce3c9ceffd20b3d44a868a..4cd6c13181ab6fe279410c64b1c2b20886572187 100644 --- a/api/server.js +++ b/api/server.js @@ -13,54 +13,54 @@ server.use(middlewares); server.get('/structures/count', (req, res) => { let structureCountTab = []; // Compétences de base - structureCountTab.push({ id: 260, count: 3 }); - structureCountTab.push({ id: 260, count: 3 }); - structureCountTab.push({ id: 259, count: 3 }); - structureCountTab.push({ id: 261, count: 3 }); - structureCountTab.push({ id: 249, count: 3 }); - structureCountTab.push({ id: 222, count: 2 }); - structureCountTab.push({ id: 212, count: 3 }); - structureCountTab.push({ id: 186, count: 2 }); - structureCountTab.push({ id: 183, count: 2 }); + structureCountTab.push({ id: '260', count: 3 }); + structureCountTab.push({ id: '260', count: 3 }); + structureCountTab.push({ id: '259', count: 3 }); + structureCountTab.push({ id: '261', count: 3 }); + structureCountTab.push({ id: '249', count: 3 }); + structureCountTab.push({ id: '222', count: 2 }); + structureCountTab.push({ id: '212', count: 3 }); + structureCountTab.push({ id: '186', count: 2 }); + structureCountTab.push({ id: '183', count: 2 }); // Accès aux droits - structureCountTab.push({ id: 176, count: 2 }); - structureCountTab.push({ id: 175, count: 1 }); - structureCountTab.push({ id: 174, count: 1 }); - structureCountTab.push({ id: 173, count: 1 }); - structureCountTab.push({ id: 172, count: 1 }); - structureCountTab.push({ id: 171, count: 1 }); - structureCountTab.push({ id: 167, count: 1 }); - structureCountTab.push({ id: 165, count: 1 }); + structureCountTab.push({ id: '176', count: 2 }); + structureCountTab.push({ id: '175', count: 1 }); + structureCountTab.push({ id: '174', count: 1 }); + structureCountTab.push({ id: '173', count: 1 }); + structureCountTab.push({ id: '172', count: 1 }); + structureCountTab.push({ id: '171', count: 1 }); + structureCountTab.push({ id: '167', count: 1 }); + structureCountTab.push({ id: '165', count: 1 }); // Insertion sociale et professionnelle - structureCountTab.push({ id: 254, count: 2 }); - structureCountTab.push({ id: 240, count: 2 }); - structureCountTab.push({ id: 194, count: 3 }); - structureCountTab.push({ id: 193, count: 3 }); - structureCountTab.push({ id: 192, count: 3 }); - structureCountTab.push({ id: 191, count: 3 }); - structureCountTab.push({ id: 262, count: 3 }); - structureCountTab.push({ id: 263, count: 2 }); - structureCountTab.push({ id: 3, count: 2 }); + structureCountTab.push({ id: '254', count: 2 }); + structureCountTab.push({ id: '240', count: 2 }); + structureCountTab.push({ id: '194', count: 3 }); + structureCountTab.push({ id: '193', count: 3 }); + structureCountTab.push({ id: '192', count: 3 }); + structureCountTab.push({ id: '191', count: 3 }); + structureCountTab.push({ id: '262', count: 3 }); + structureCountTab.push({ id: '263', count: 2 }); + structureCountTab.push({ id: '3', count: 2 }); // Aide à la parentalité - structureCountTab.push({ id: 257, count: 2 }); - structureCountTab.push({ id: 238, count: 2 }); - structureCountTab.push({ id: 178, count: 1 }); - structureCountTab.push({ id: 166, count: 1 }); + structureCountTab.push({ id: '257', count: 2 }); + structureCountTab.push({ id: '238', count: 2 }); + structureCountTab.push({ id: '178', count: 1 }); + structureCountTab.push({ id: '166', count: 1 }); // Culture et sécurité numérique - structureCountTab.push({ id: 264, count: 2 }); - structureCountTab.push({ id: 255, count: 2 }); - structureCountTab.push({ id: 265, count: 2 }); - structureCountTab.push({ id: 232, count: 2 }); - structureCountTab.push({ id: 225, count: 2 }); - structureCountTab.push({ id: 221, count: 2 }); - structureCountTab.push({ id: 218, count: 1 }); - structureCountTab.push({ id: 209, count: 1 }); - structureCountTab.push({ id: 208, count: 1 }); - structureCountTab.push({ id: 206, count: 2 }); - structureCountTab.push({ id: 195, count: 1 }); - structureCountTab.push({ id: 164, count: 1 }); - structureCountTab.push({ id: 163, count: 1 }); - structureCountTab.push({ id: 162, count: 2 }); + structureCountTab.push({ id: '264', count: 2 }); + structureCountTab.push({ id: '255', count: 2 }); + structureCountTab.push({ id: '265', count: 2 }); + structureCountTab.push({ id: '232', count: 2 }); + structureCountTab.push({ id: '225', count: 2 }); + structureCountTab.push({ id: '221', count: 2 }); + structureCountTab.push({ id: '218', count: 1 }); + structureCountTab.push({ id: '209', count: 1 }); + structureCountTab.push({ id: '208', count: 1 }); + structureCountTab.push({ id: '206', count: 2 }); + structureCountTab.push({ id: '195', count: 1 }); + structureCountTab.push({ id: '164', count: 1 }); + structureCountTab.push({ id: '163', count: 1 }); + structureCountTab.push({ id: '162', count: 2 }); return res.status(200).jsonp(structureCountTab); }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a2af3088eb43c377246b6b92da09a971c95b70d0..a737e0f0fe92ea4a96d326015b026745f7d37c5c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -16,9 +16,10 @@ import { StructureListComponent } from './structure-list/structure-list.componen import { CardComponent } from './structure-list/components/card/card.component'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { SearchComponent } from './structure-list/components/search/search.component'; -import { ModalFilterComponent } from './structure-list/components/modal-filter/modal-filter.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'; @NgModule({ declarations: [ @@ -32,6 +33,7 @@ import { StructureOpeningStatusComponent } from './structure-list/components/str ModalFilterComponent, StructureDetailsComponent, StructureOpeningStatusComponent, + ModalOutsideDirective, ], imports: [ BrowserModule, diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 8d8ab36e948009cb1091d12e4aded7b874ef0591..0f2ccf855c892e813002809742931f2a50f794f7 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -6,6 +6,7 @@ (displayMapMarkerId)="setMapMarkerId($event)" (selectedMarkerId)="setSelectedMarkerId($event)" class="left-pane" + fxLayout="column" ></app-structure-list> <app-map [structures]="structures" diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index e5291b76321043b3645c1cfe9eb6ed6898c2dc1b..54325dc33a27b93745e845f85a3cc19e7b176a0c 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -55,12 +55,15 @@ export class HomeComponent implements OnInit { return new Promise((resolve, reject) => { this.getCoord(structure.voie).subscribe((coord: GeoJson) => { structure.address = coord.properties.name + ' - ' + coord.properties.postcode + ' ' + coord.properties.city; - structure.distance = this.geoJsonService.getDistance( - coord.geometry.getLon(), - coord.geometry.getLat(), - this.currentLocation.geometry.getLon(), - this.currentLocation.geometry.getLat(), - 'M' + structure.distance = parseInt( + this.geoJsonService.getDistance( + coord.geometry.getLon(), + coord.geometry.getLat(), + this.currentLocation.geometry.getLon(), + this.currentLocation.geometry.getLat(), + 'M' + ), + 10 ); resolve(structure); }); diff --git a/src/app/models/address.model.ts b/src/app/models/address.model.ts index b91f488bb703e051124ac05efda68173fb4c87b7..312a24fbd28bec1a938da27efd007fc4946fbf56 100644 --- a/src/app/models/address.model.ts +++ b/src/app/models/address.model.ts @@ -11,6 +11,6 @@ export class Address { } public queryString(): string { - return this.text + ' ' + this.citycode; + return this.text + ' ' + this.zipcode; } } diff --git a/src/app/models/structure.model.ts b/src/app/models/structure.model.ts index 6cc2c585f5c142d79a99e779438b47f97ce96614..9be7c612c9a066580f81ba969a0900cd0cd28a9b 100644 --- a/src/app/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -35,7 +35,7 @@ export class Structure { public hours: Week; public isOpen: boolean; public openedOn: OpeningDay; - public distance?: string; + public distance?: number; public address?: string; constructor(obj?: any) { @@ -86,9 +86,9 @@ export class Structure { return 3; } else { // If it's in km - if (parseInt(this.distance, 10) > 10000) { + if (this.distance > 10000) { return 3; - } else if (parseInt(this.distance, 10) < 5000) { + } else if (this.distance < 5000) { // If it's between 0 and 500 m return 1; } else { diff --git a/src/app/services/structure-list.service.spec.ts b/src/app/services/structure-list.service.spec.ts index 230f2e331d15a4316a931d442b658b48a4d2a5f6..5a1b05cc44b9160457a6d3b5b0744cac775d9f14 100644 --- a/src/app/services/structure-list.service.spec.ts +++ b/src/app/services/structure-list.service.spec.ts @@ -2,6 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; import { Day } from '../models/day.model'; import { Structure } from '../models/structure.model'; +import { Time } from '../models/time.model'; import { Week } from '../models/week.model'; import { StructureService } from './structure-list.service'; const { DateTime } = require('luxon'); @@ -28,12 +29,11 @@ describe('StructureService', () => { s.hours.friday = new Day(false); s.hours.saturday = new Day(false); s.hours.sunday = new Day(false); - s.hours.thursday.open = true; - s.hours.thursday.time = [ - { openning: 805, closing: 1200 }, - { openning: 1400, closing: 1600 }, - ]; + s.hours.thursday.time = new Array( + new Time({ openning: 805, closing: 1200 }), + new Time({ openning: 1400, closing: 1600 }) + ); // Init date sur un jeudi à 9h05 const dt = new DateTime.local(2020, 10, 8, 9, 5); @@ -54,8 +54,7 @@ describe('StructureService', () => { s.hours.sunday = new Day(); s.hours.thursday.open = true; - s.hours.thursday.time = [{ openning: 1400, closing: 1600 }]; - + s.hours.thursday.time = new Array(new Time({ openning: 1400, closing: 1600 })); // Init date sur un jeudi à 9h05 const dt = new DateTime.local(2020, 10, 8, 9, 5); const result = structureService.updateOpeningStructure(s, dt); diff --git a/src/app/structure-list/components/card/card.component.ts b/src/app/structure-list/components/card/card.component.ts index 606792875af39008d3a77bc61e70629a8033b796..4050fe75977be0d50656f01a435fc478ffb41f4a 100644 --- a/src/app/structure-list/components/card/card.component.ts +++ b/src/app/structure-list/components/card/card.component.ts @@ -22,8 +22,8 @@ export class CardComponent implements OnInit { * Display distance in m or km according to value */ public formatDistance(): string { - if (this.structure.distance.length > 3) { - return (parseInt(this.structure.distance, 10) / 1000).toFixed(1).toString() + ' km'; + if (this.structure.distance > 1000) { + return (this.structure.distance / 1000).toFixed(1).toString() + ' km'; } else { return this.structure.distance + ' m'; } 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 ec2d69fb075c58894c9f0c3848264bd7f82f00fa..3acb10b380aec1a87587e4709f7c677f93d9e7af 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 @@ -1,4 +1,4 @@ -<div *ngIf="modalType" fxLayout="column" fxLayoutAlign="space-between" [ngClass]="['modal', 'modal' + modalType]"> +<div fxLayout="column" fxLayoutAlign="space-between" [ngClass]="['modal', 'modal' + getModalType()]"> <div class="body-wrap"> <div class="contentModal" fxLayout="row wrap" fxLayoutAlign="flex-start" *ngIf="categories.length > 0"> <div class="blockFiltre" *ngFor="let c of categories"> @@ -11,9 +11,17 @@ <label> <input type="checkbox" - [checked]="getIndex(module.id, c.name) > -1" + [checked]=" + c.name !== 'Équipements et services proposés' + ? getIndex(module.id, c.name) > -1 + : getIndex('True', module.id) > -1 + " [value]="module.id" - (change)="onCheckboxChange($event, c.name)" + (change)=" + c.name !== 'Équipements et services proposés' + ? onCheckboxChange($event, c.name, false) + : onCheckboxChange($event, module.id, true) + " /> <span class="customCheck"></span> <div class="label">{{ module.text }}</div> @@ -27,7 +35,7 @@ </div> <div class="footer" fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="3vw"> <a (click)="clearFilters()">Effacer</a> - <button type="button" (click)="emitModules()">Appliquer</button> + <button type="button" (click)="emitModules(checkedModules)">Appliquer</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 5c3a3f64d3f904ab18c02ef3279a76b173ff90d3..9f12d9b37f12a84ced020f975fe9f895d27bddd9 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 @@ -22,12 +22,9 @@ max-height: 648px; max-width: 754px; width: 94%; - border-left: 6.5px solid transparent; - border-bottom: 6.5px solid transparent; border-radius: 11px; - z-index: 401 !important; + z-index: 1001 !important; position: absolute; - border: 1px solid $grey-6; border-radius: 6px; margin-top: 3.5px; @include background-hash; @@ -46,11 +43,14 @@ max-width: 1100px; border-bottom: 1px solid $grey; margin-bottom: 10px; - max-height: 500px; + max-height: 438px; .blockFiltre { width: 100%; - padding: 32px 40px 10px 40px; + padding: 32px 40px 0px 40px; min-width: 450px; + &:last-child { + padding-bottom: 32px; + } } .blockLigne { padding-left: 0; @@ -61,6 +61,7 @@ column-count: 2; column-gap: 46px; column-rule: dashed 1px $grey; + margin: 0px; @media #{$large-phone} { -moz-column-count: 1; -webkit-column-count: 1; @@ -75,6 +76,7 @@ display: flex; align-items: center; margin-top: 0; + margin-bottom: 9px; } .nbResult { @include cn-regular-14; diff --git a/src/app/structure-list/components/modal-filter/modal-filter.component.spec.ts b/src/app/structure-list/components/modal-filter/modal-filter.component.spec.ts index 9c1ad3d140e147fb54201d06335a3fcd2f06e378..cac3dc7a3e005f6151981d063c666afd838b3e60 100644 --- a/src/app/structure-list/components/modal-filter/modal-filter.component.spec.ts +++ b/src/app/structure-list/components/modal-filter/modal-filter.component.spec.ts @@ -29,58 +29,57 @@ describe('ModalFilterComponent', () => { it('should emit modules', () => { const modules: Module[] = [ - { id: 176, text: 'training', count: 3 }, - { id: 173, text: 'training', count: 2 }, - { id: 172, text: 'training', count: 2 }, + { id: '176', text: 'training', count: 3 }, + { id: '173', text: 'training', count: 2 }, + { id: '172', text: 'training', count: 2 }, ]; - component.checkedModules = modules; spyOn(component.searchEvent, 'emit'); - component.emitModules(); + component.emitModules(modules); expect(component.searchEvent.emit).toHaveBeenCalled(); expect(component.searchEvent.emit).toHaveBeenCalledWith(modules); }); it('should return an index or -1', () => { const modules: Module[] = [ - { id: 176, text: 'training', count: 0 }, - { id: 173, text: 'training', count: 0 }, - { id: 172, text: 'training', count: 0 }, + { id: '176', text: 'training', count: 0 }, + { id: '173', text: 'training', count: 0 }, + { id: '172', text: 'training', count: 0 }, ]; component.checkedModules = modules; - const foundItem = component.getIndex(173, 'training'); - const notFoundItem = component.getIndex(189, 'training'); + const foundItem = component.getIndex('173', 'training'); + const notFoundItem = component.getIndex('189', 'training'); expect(foundItem).toEqual(1); expect(notFoundItem).toEqual(-1); }); it('should add a module to checkedModule array', () => { const modules: Module[] = [ - { id: 176, text: 'training', count: 0 }, - { id: 173, text: 'training', count: 0 }, - { id: 172, text: 'training', count: 0 }, + { id: '176', text: 'training', count: 0 }, + { id: '173', text: 'training', count: 0 }, + { id: '172', text: 'training', count: 0 }, ]; component.checkedModules = modules; - const evt = { target: { checked: true, value: 175 } }; + const evt = { target: { checked: true, value: '175' } }; component.onCheckboxChange(evt, 'training'); expect(component.checkedModules.length).toEqual(4); }); it('should remove a module to checkedModule array', () => { const modules: Module[] = [ - { id: 176, text: 'training', count: 0 }, - { id: 173, text: 'training', count: 0 }, - { id: 172, text: 'training', count: 0 }, + { id: '176', text: 'training', count: 0 }, + { id: '173', text: 'training', count: 0 }, + { id: '172', text: 'training', count: 0 }, ]; component.checkedModules = modules; - const evt = { target: { checked: false, value: 173 } }; + const evt = { target: { checked: false, value: '173' } }; component.onCheckboxChange(evt, 'training'); expect(component.checkedModules.length).toEqual(2); }); it('should remove all modules checked from same modal, here morefilters', () => { const modules: Module[] = [ - { id: 176, text: 'morefilters', count: 0 }, - { id: 173, text: 'morefilters', count: 0 }, - { id: 172, text: 'morefilters', count: 0 }, - { id: 179, text: 'training', count: 0 }, - { id: 190, text: 'training', count: 0 }, - { id: 167, text: 'training', count: 0 }, + { id: '176', text: 'morefilters', count: 0 }, + { id: '173', text: 'morefilters', count: 0 }, + { id: '172', text: 'morefilters', count: 0 }, + { id: '179', text: 'training', count: 0 }, + { id: '190', text: 'training', count: 0 }, + { id: '167', text: 'training', count: 0 }, ]; component.checkedModules = modules; const category: Category = { name: 'morefilters', modules: [modules[0], modules[1], modules[2]] }; diff --git a/src/app/structure-list/components/modal-filter/modal-filter.component.ts b/src/app/structure-list/components/modal-filter/modal-filter.component.ts index 2f46075fa9809c9adf2d9d261735956069d3819c..5bab3d8d2ebbdf3681dab33b0845da7ce585a6b8 100644 --- a/src/app/structure-list/components/modal-filter/modal-filter.component.ts +++ b/src/app/structure-list/components/modal-filter/modal-filter.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; +import { TypeModal } from '../../enum/typeModal.enum'; import { Category } from '../../models/category.model'; -import { Filter } from '../../models/filter.model'; import { Module } from '../../models/module.model'; @Component({ @@ -15,10 +15,11 @@ export class ModalFilterComponent implements OnInit { searchTerm: '', }); } - @Input() public modalType: string; + @Input() public modalType: TypeModal; @Input() public categories: Category[]; @Input() public modules: Module[] = []; @Output() searchEvent = new EventEmitter(); + @Output() closeEvent = new EventEmitter(); // Checkbox variable checkedModules: Module[]; // Form search input @@ -29,13 +30,13 @@ export class ModalFilterComponent implements OnInit { } // Return index of a specific module in array modules - public getIndex(id: number, categ: string): number { + public getIndex(id: string, categ: string): number { return this.checkedModules.findIndex((m: Module) => m.id === id && m.text === categ); } // Management of the checkbox event (Check / Uncheck) - public onCheckboxChange(event, categ: string): void { - const checkValue: number = parseInt(event.target.value, 10); + public onCheckboxChange(event, categ: string, isSpecial: boolean): void { + const checkValue: string = isSpecial ? 'True' : event.target.value; if (event.target.checked) { this.checkedModules.push(new Module(checkValue, categ)); } else { @@ -45,20 +46,39 @@ export class ModalFilterComponent implements OnInit { } } } - // Clear only filters in the current modal public clearFilters(): void { this.categories.forEach((categ: Category) => { categ.modules.forEach((module: Module) => { - if (this.getIndex(module.id, categ.name) > -1) { - this.checkedModules.splice(this.getIndex(module.id, categ.name), 1); + const index = this.getIndex(module.id, categ.name); + const indexSpecial = this.getIndex('True', module.id); + if (index > -1) { + this.checkedModules.splice(index, 1); + } else if (indexSpecial > -1) { + this.checkedModules.splice(indexSpecial, 1); } }); }); + this.emitModules(this.checkedModules); } // Sends an array containing all modules - public emitModules(): void { - this.searchEvent.emit(this.checkedModules); + public emitModules(m: Module[]): void { + this.searchEvent.emit(m); + } + + public getModalType(): string { + switch (this.modalType) { + case TypeModal.training: + return 'training'; + case TypeModal.moreFilters: + return 'moreFilters'; + default: + return ''; + } + } + + public closeModal(): void { + this.closeEvent.emit(); } } diff --git a/src/app/structure-list/components/search/search.component.html b/src/app/structure-list/components/search/search.component.html index 9de95de2431a81f9a7bc1da82ed11a7a77416087..ee31cd2f18f1f7d850b49481c8b4a482385486fb 100644 --- a/src/app/structure-list/components/search/search.component.html +++ b/src/app/structure-list/components/search/search.component.html @@ -12,7 +12,7 @@ <div class="inputSection" fxLayout="row" fxLayoutAlign="space-between center"> <input type="text" formControlName="searchTerm" placeholder="Rechercher une adresse, une association..." /> <div class="icon close" (click)="clearInput()"> - <div class="ico-close grey"></div> + <div class="ico-close-search"></div> </div> <span class="separator"></span> <div class="icon pin"> @@ -24,46 +24,57 @@ </div> </form> </div> - - <div class="btnSection" fxLayout="row" fxLayoutAlign="space-between center"> - <button - type="button" - [ngClass]="{ selected: modalTypeOpened === TypeModal[0] }" - (click)="openModal(TypeModal[0])" - fxLayout="row" - fxLayoutAlign="space-between center" - > - <span class="btnText">Accompagnement</span> - <div class="arrow"></div> - </button> - <button - type="button" - [ngClass]="{ selected: modalTypeOpened === TypeModal[1] }" - (click)="openModal(TypeModal[1])" - fxLayout="row" - fxLayoutAlign="space-between center" - > - <span class="btnText">Formations</span> - <div class="arrow"></div> - </button> - <button - type="button" - [ngClass]="{ selected: modalTypeOpened === TypeModal[2] }" - (click)="openModal(TypeModal[2])" - fxLayout="row" - fxLayoutAlign="space-between center" - > - <span class="btnText">Plus de filtres</span> - <div class="arrow"></div> - </button> - </div> - <div *ngIf="modalTypeOpened"> - <app-modal-filter - [modalType]="modalTypeOpened" - [categories]="categories" - [modules]="checkedModulesFilter" - (searchEvent)="fetchResults($event)" - ></app-modal-filter> + <div (clickOutside)="closeModal()"> + <div class="btnSection" fxLayout="row" fxLayoutAlign="space-between center"> + <button + type="button" + [ngClass]="{ + selected: modalTypeOpened === TypeModal.accompaniment, + containCheckedFilters: numberAccompanimentChecked + }" + (click)="openModal(TypeModal.accompaniment)" + fxLayout="row" + fxLayoutAlign="space-between center" + > + <span class="btnText">Accompagnement</span> + <div class="arrow"></div> + </button> + <button + type="button" + [ngClass]="{ + selected: modalTypeOpened === TypeModal.training, + containCheckedFilters: numberTrainingChecked + }" + (click)="openModal(TypeModal.training)" + fxLayout="row" + fxLayoutAlign="space-between center" + > + <span class="btnText">Formations</span> + <div class="arrow"></div> + </button> + <button + type="button" + [ngClass]="{ + selected: modalTypeOpened === TypeModal.moreFilters, + containCheckedFilters: numberMoreFiltersChecked + }" + (click)="openModal(TypeModal.moreFilters)" + fxLayout="row" + fxLayoutAlign="space-between center" + > + <span class="btnText">Plus de filtres</span> + <div class="arrow"></div> + </button> + </div> + <div *ngIf="modalTypeOpened"> + <app-modal-filter + [modalType]="modalTypeOpened" + [categories]="categories" + [modules]="checkedModulesFilter" + (searchEvent)="fetchResults($event)" + (closeEvent)="closeModal()" + ></app-modal-filter> + </div> </div> </div> <div class="footerSearchSection" fxLayout="row" fxLayoutAlign="space-between center"> @@ -76,5 +87,7 @@ </label> </div> </div> - <a href="">Ajouter une structure</a> + <a href="https://services.formulaireextranet.grandlyon.com/saisie-fiche-structure/" target="_blank" + >Ajouter une structure</a + > </div> diff --git a/src/app/structure-list/components/search/search.component.scss b/src/app/structure-list/components/search/search.component.scss index 6ee105f2f7cf869ff9eab0ae7731331913eb85b8..9d93e9bf73b512a514fe9d21ed1306e8232c5fba 100644 --- a/src/app/structure-list/components/search/search.component.scss +++ b/src/app/structure-list/components/search/search.component.scss @@ -33,18 +33,18 @@ .separator { height: 100%; width: 2px; - background-color: $grey-6; + background-color: $grey-4; margin: 0 5px 0 5px; } .inputSection { padding: 6px 3px 6px 6px; min-width: 463px; - border: 1px solid $grey-6; + border: 1px solid $grey-4; background-color: $white; height: 40px; } .searchButton { - border: 1px solid $grey-6; + border: 1px solid $grey-4; border-radius: 6px; @include background-hash; padding: 0 0 4px 5px; @@ -63,9 +63,12 @@ @include cn-regular-14; } } + .containCheckedFilters { + border-color: $secondary-color; + } .selected { - border: 1px solid $primary-color; - color: $primary-color; + border-color: $primary-color; + color: inherit; .arrow { background-color: transparent; border-bottom: 1px solid $primary-color; diff --git a/src/app/structure-list/components/search/search.component.spec.ts b/src/app/structure-list/components/search/search.component.spec.ts index a5507ed760dbed84c2fd7f249061d9e59eac3156..a4cdfb3996a5c88e92c2e04568d4202fbfabe0a3 100644 --- a/src/app/structure-list/components/search/search.component.spec.ts +++ b/src/app/structure-list/components/search/search.component.spec.ts @@ -1,11 +1,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; -import { Category } from '../../models/category.model'; import { Filter } from '../../models/filter.model'; -import { Module } from '../../models/module.model'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { SearchComponent } from './search.component'; -import { TypeModal } from '../../enum/typeModal.enum'; describe('SearchComponent', () => { let component: SearchComponent; @@ -35,14 +32,4 @@ describe('SearchComponent', () => { expect(component.searchEvent.emit).toHaveBeenCalled(); expect(component.searchEvent.emit).toHaveBeenCalledWith(filter); }); - - it('should update categories', () => { - let categories: Category[] = [new Category({ name: 'Accompagnement des démarches' })]; - categories[0].modules = []; - for (let i = 0; i < 7; i++) { - categories[0].modules.push(new Module(5 + i, 'CAF' + i)); - } - component.openModal(TypeModal[0]); - expect(component.categories).toEqual(categories); - }); }); diff --git a/src/app/structure-list/components/search/search.component.ts b/src/app/structure-list/components/search/search.component.ts index 10f0393f2308c909fae300627cc278d50a2c9634..5e8e4768999db47243ec169a8a8fb4a610290e2b 100644 --- a/src/app/structure-list/components/search/search.component.ts +++ b/src/app/structure-list/components/search/search.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output, Type } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { forkJoin } from 'rxjs'; import { TypeModal } from '../../enum/typeModal.enum'; @@ -26,10 +26,14 @@ export class SearchComponent implements OnInit { public searchForm: FormGroup; // Modal variable public categories: Category[]; - public modalTypeOpened: string; + public modalTypeOpened: TypeModal; // Checkbox variable public checkedModulesFilter: Module[]; + public numberTrainingChecked = 0; + public numberAccompanimentChecked = 0; + public numberMoreFiltersChecked = 0; + ngOnInit(): void { // Will store the different categories this.categories = []; @@ -64,32 +68,70 @@ export class SearchComponent implements OnInit { } // Delete when getting back-end - private mockApiNumber(nb: number): string { + private mockApiNumber(nb: string): string { return ('00' + nb).slice(-3); } public fetchResults(checkedModules: Module[]): void { const inputTerm = this.searchForm.get('searchTerm').value; - + // Check if some modules is checked in filters + if (this.checkedModulesFilter !== checkedModules) { + // First btn + switch (this.modalTypeOpened) { + case TypeModal.accompaniment: + this.numberAccompanimentChecked = this.countCheckFiltersOnModules( + checkedModules, + this.numberTrainingChecked + this.numberMoreFiltersChecked + ); + break; + case TypeModal.training: + this.numberTrainingChecked = this.countCheckFiltersOnModules( + checkedModules, + this.numberAccompanimentChecked + this.numberMoreFiltersChecked + ); + break; + case TypeModal.moreFilters: + this.numberMoreFiltersChecked = this.countCheckFiltersOnModules( + checkedModules, + this.numberAccompanimentChecked + this.numberTrainingChecked + ); + break; + default: + throw new Error('Modal type not handle'); + } + } // Store checked modules this.checkedModulesFilter = checkedModules; // Close modal after receive filters from her. - this.openModal(this.modalTypeOpened); + this.closeModal(); inputTerm ? this.applyFilter(inputTerm) : this.applyFilter(null); } + // Check if some modules is checked on first filter and store number of modules checked + public countCheckFiltersOnModules(checkedModules: Module[], value: number): number { + if (checkedModules.length && value !== checkedModules.length) { + return checkedModules.length - value; + } else { + return 0; + } + } // Open the modal and display the list according to the right filter button - public openModal(option: string): void { + public openModal(modalType: TypeModal): void { this.categories = []; - if (this.modalTypeOpened !== option) { - this.modalTypeOpened = option; - this.fakeData(option); - } else { - this.modalTypeOpened = null; + // if modal already opened, reset type + if (this.modalTypeOpened === modalType) { + this.modalTypeOpened = undefined; + } else if (this.modalTypeOpened !== modalType) { + this.modalTypeOpened = modalType; + this.fakeData(modalType); } } + public closeModal(): void { + this.modalTypeOpened = undefined; + } + private fromStringToIdExcel(categ: string): string { const splitStr = categ.toLowerCase().split(' '); for (let i = 1; i < splitStr.length; i++) { @@ -103,21 +145,10 @@ export class SearchComponent implements OnInit { .replace('?', ''); } - // Fake service api - private mockService(module: Category[], titre: string, categ: any, nbCateg: number): void { - const category = new Category({ name: titre, modules: [] }); - for (let i = 0; i < nbCateg; i++) { - category.modules.push(new Module(categ.id + i, categ.name + i)); - } - module.push(category); - } - // Get the correct list of checkbox/modules depending on the type of modal. - private fakeData(option: string): void { - if (option === TypeModal[0]) { - this.mockService(this.categories, 'Accompagnement des démarches', { name: 'CAF', id: 5 }, 7); - } else if (option === TypeModal[1]) { - forkJoin([this.searchService.getCategoriesFormations(), this.searchService.getFakeCounterModule()]).subscribe( + private fakeData(option: TypeModal): void { + if (option === TypeModal.accompaniment) { + forkJoin([this.searchService.getCategoriesAccompaniment(), this.searchService.getFakeCounterModule()]).subscribe( (res) => { const categories: Category[] = res[0]; const structureCounter: StructureCounter[] = res[1]; @@ -127,18 +158,28 @@ export class SearchComponent implements OnInit { }); } ); - } else if (option === TypeModal[2]) { - this.mockService( - this.categories, - 'Équipements', - { name: 'Accès à des revues ou livres infoirmatiques numériques', id: 1 }, - 8 + } else if (option === TypeModal.training) { + forkJoin([this.searchService.getCategoriesTraining(), this.searchService.getFakeCounterModule()]).subscribe( + (res) => { + const categories: Category[] = res[0]; + const structureCounter: StructureCounter[] = res[1]; + categories.forEach((category) => { + category = this.searchService.setCountModules(category, structureCounter); + this.categories.push(category); + }); + } + ); + } else if (option === TypeModal.moreFilters) { + forkJoin([this.searchService.getCategoriesMoreFilters(), this.searchService.getFakeCounterModule()]).subscribe( + (res) => { + const categories: Category[] = res[0]; + const structureCounter: StructureCounter[] = res[1]; + categories.forEach((category) => { + category = this.searchService.setCountModules(category, structureCounter); + this.categories.push(category); + }); + } ); - this.mockService(this.categories, "Modalité d'accueil", { name: 'Matériel mis à dispostion', id: 2 }, 6); - this.mockService(this.categories, "Type d'acteurs", { name: 'Lieux de médiation (Pimms, assos...)', id: 3 }, 5); - this.mockService(this.categories, 'Publics', { name: 'Langues étrangères autres qu’anglais', id: 4 }, 12); - this.mockService(this.categories, 'Labelisation', { name: 'Prescripteur du Pass Numérique', id: 5 }, 6); - this.mockService(this.categories, 'Type de structure', { name: 'Espace de co-working', id: 6 }, 6); } } } diff --git a/src/app/structure-list/enum/typeModal.enum.ts b/src/app/structure-list/enum/typeModal.enum.ts index ab391e2f6ccbeeb72f81aea06ff806c764a27d2e..53ef9b1ec16106bd8195d4060686fb9ea12e1819 100644 --- a/src/app/structure-list/enum/typeModal.enum.ts +++ b/src/app/structure-list/enum/typeModal.enum.ts @@ -1,5 +1,5 @@ export enum TypeModal { - accompaniment, + accompaniment = 1, training, moreFilters, } diff --git a/src/app/structure-list/models/module.model.ts b/src/app/structure-list/models/module.model.ts index 340c04092a730edcaa4a4a1f38d6778ee94064c1..5bbbc940c8e26fe0fc3b2de819c02b1714eacc63 100644 --- a/src/app/structure-list/models/module.model.ts +++ b/src/app/structure-list/models/module.model.ts @@ -1,9 +1,9 @@ export class Module { - id: number; + id: string; text: string; count: number; - constructor(id: number, text: string) { + constructor(id: string, text: string) { this.id = id; this.text = text; } diff --git a/src/app/structure-list/models/structureCounter.model.ts b/src/app/structure-list/models/structureCounter.model.ts index d208192f08299466b39fc4659fba3574804d04c4..4121bb18c1fedcb4ec08ba8a86720921b0ea1a8b 100644 --- a/src/app/structure-list/models/structureCounter.model.ts +++ b/src/app/structure-list/models/structureCounter.model.ts @@ -1,5 +1,5 @@ export class StructureCounter { - id: number; + id: string; count: number; constructor(obj?: any) { diff --git a/src/app/structure-list/services/modalOutside.directive.ts b/src/app/structure-list/services/modalOutside.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..b8ebc284c1ccfb67aa0b0527df5e39322e834ec5 --- /dev/null +++ b/src/app/structure-list/services/modalOutside.directive.ts @@ -0,0 +1,18 @@ +/* tslint:disable:member-ordering */ +import { Directive, ElementRef, HostListener, Output, EventEmitter } from '@angular/core'; + +@Directive({ + selector: '[clickOutside]', +}) +export class ModalOutsideDirective { + constructor(private _elementRef: ElementRef) {} + + @Output('clickOutside') clickOutside: EventEmitter<any> = new EventEmitter(); + + @HostListener('document:mousedown', ['$event.target']) onMouseEnter(targetElement) { + const clickedInside = this._elementRef.nativeElement.contains(targetElement); + if (!clickedInside) { + this.clickOutside.emit(null); + } + } +} diff --git a/src/app/structure-list/services/search.service.spec.ts b/src/app/structure-list/services/search.service.spec.ts index 4da5c595424448853477d2530cbc4c68d0fc84ed..666955f99731e786af4843fb684ab70f5396b7ef 100644 --- a/src/app/structure-list/services/search.service.spec.ts +++ b/src/app/structure-list/services/search.service.spec.ts @@ -21,13 +21,13 @@ describe('SearchService', () => { it('should return category with number of modules inside', () => { const structureCount: StructureCounter[] = [ - { id: 176, count: 2 }, - { id: 172, count: 1 }, - { id: 173, count: 1 }, + { id: '176', count: 2 }, + { id: '172', count: 1 }, + { id: '173', count: 1 }, ]; - const m1: Module = { id: 176, text: 'strm1', count: 0 }; - const m2: Module = { id: 173, text: 'strm2', count: 0 }; - const m3: Module = { id: 172, text: 'strm3', count: 0 }; + const m1: Module = { id: '176', text: 'strm1', count: 0 }; + const m2: Module = { id: '173', text: 'strm2', count: 0 }; + const m3: Module = { id: '172', text: 'strm3', count: 0 }; const category: Category = { name: 'strCateg', modules: [m1, m2, m3] }; const result = service.setCountModules(category, structureCount); expect(result.modules[0].count).toBe(2); diff --git a/src/app/structure-list/services/search.service.ts b/src/app/structure-list/services/search.service.ts index 1b6a33b59d159fd9f30a32e1b3051971819e67a2..b7d8250162748f16712db8ac2f9ba1302058662b 100644 --- a/src/app/structure-list/services/search.service.ts +++ b/src/app/structure-list/services/search.service.ts @@ -12,11 +12,21 @@ import { StructureCounter } from '../models/structureCounter.model'; export class SearchService { constructor(private http: HttpClient) {} - public getCategoriesFormations(): Observable<Category[]> { + public getCategoriesTraining(): Observable<Category[]> { return this.http .get('/api/CategoriesFormations') .pipe(map((data: any[]) => data.map((item) => new Category(item)))); } + public getCategoriesAccompaniment(): Observable<Category[]> { + return this.http + .get('/api/CategoriesAccompagnement') + .pipe(map((data: any[]) => data.map((item) => new Category(item)))); + } + public getCategoriesMoreFilters(): Observable<Category[]> { + return this.http + .get('/api/CategoriesPlusDeFiltres') + .pipe(map((data: any[]) => data.map((item) => new Category(item)))); + } public getFakeCounterModule(): Observable<StructureCounter[]> { return this.http @@ -26,6 +36,8 @@ export class SearchService { public setCountModules(category: Category, structureCountTab: StructureCounter[]): Category { category.modules.forEach((m: Module) => { for (let i = 0; i < structureCountTab.length; i++) { + // Force type + m.id = m.id.toString(); if (structureCountTab[i].id === m.id) { m.count = structureCountTab[i].count; } diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss index 4d203db525ff5ec4351c8e405f3ddf4b72db43a8..a6e87f83d6e1df1ab098303d083e80eb17430ad7 100644 --- a/src/assets/scss/_buttons.scss +++ b/src/assets/scss/_buttons.scss @@ -5,7 +5,7 @@ background: $white; height: 40px; width: 190px; - border: 1px solid $grey-6; + border: 1px solid $grey-4; padding: 3px 16px 3px 16px; outline: none; border-radius: 4px; diff --git a/src/assets/scss/_icons.scss b/src/assets/scss/_icons.scss index f1334bbc885333ce0fb9a8226e644e3d5b4e6149..5e359a721a72ca7fe3b85793cf069bde372d2ccd 100644 --- a/src/assets/scss/_icons.scss +++ b/src/assets/scss/_icons.scss @@ -172,7 +172,7 @@ } } -.ico-close { +.ico-close-search { width: 15px; height: 13px; text-align: center; @@ -183,13 +183,13 @@ transform: rotate(-45deg); } } -.ico-close:before, -.ico-close:after { +.ico-close-search:before, +.ico-close-search:after { position: absolute; content: ''; height: 14px; width: 2px; - background-color: $grey-6; + background-color: $grey-4; } .ic-arrow-left { diff --git a/src/styles.scss b/src/styles.scss index e9f97287370120b4ff70c628cd068c872d1cb05f..f5c771f59f6a5ff02f96ec5393d07568bcea529c 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -41,8 +41,6 @@ a { .content-container { margin: 0; padding: 30px 0 30px 0; - overflow-y: auto; - overflow-x: hidden; width: 100%; height: 100%; &.medium-pt {