diff --git a/.gitignore b/.gitignore index b1b6bcc815c1b0c4b26e93c5c5dfd67f44fa1505..efa4fe6db9d2f04b66243d0b0c78113be6b3625f 100644 --- a/.gitignore +++ b/.gitignore @@ -45,5 +45,7 @@ testem.log .DS_Store Thumbs.db +#config +.env # apiMock api/db.json diff --git a/angular.json b/angular.json index 1761772a341bf8d9c20075b8996716d215b4c9dc..a41d8047cf334587bfcd0b6b032a683a91471779 100644 --- a/angular.json +++ b/angular.json @@ -30,8 +30,20 @@ "tsConfig": "tsconfig.app.json", "localize": true, "aot": true, - "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.scss"], + "assets": [ + "src/favicon.ico", + "src/assets", + { + "glob": "**/*", + "input": "./node_modules/leaflet/dist/images", + "output": "assets/" + } + ], + "styles": [ + "src/styles.scss", + "./node_modules/leaflet/dist/leaflet.css", + "./node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css" + ], "scripts": [] }, "configurations": { diff --git a/package-lock.json b/package-lock.json index da8384e00f03050052857af50dc2296371b85d68..c141501b35a17e2df1651dba90c3cfeb080bd9ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -773,6 +773,11 @@ "tslib": "^2.0.0" } }, + "@asymmetrik/ngx-leaflet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@asymmetrik/ngx-leaflet/-/ngx-leaflet-8.1.0.tgz", + "integrity": "sha512-lq7LduBP/vXcaSEmKnx7mzCR8WsoYqh9pB6BNnq53yeCwsqRbG3GdKye1/i8VvoRzjDsmQBPQsIFZ9uclXrtgg==" + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -1897,6 +1902,14 @@ "webpack-sources": "1.4.3" } }, + "@ngx-translate/core": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-13.0.0.tgz", + "integrity": "sha512-+tzEp8wlqEnw0Gc7jtVRAJ6RteUjXw6JJR4O65KlnxOmJrCGPI0xjV/lKRnQeU0w4i96PQs/jtpL921Wrb7PWg==", + "requires": { + "tslib": "^2.0.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -1940,6 +1953,28 @@ } } }, + "@runette/ngx-leaflet-locate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@runette/ngx-leaflet-locate/-/ngx-leaflet-locate-1.0.3.tgz", + "integrity": "sha512-U792/VO5PvQKok7H4CHJ+u3L7Vy3Pca3Iki32CXVqloVzUOO0VcNMcF07NoLcXkUHEXX7hJ5I7OGivTyMFcYpQ==", + "requires": { + "@types/leaflet.locatecontrol": "^0.60.7", + "leaflet.locatecontrol": "^0.68.0", + "tslib": "^1.9.0" + }, + "dependencies": { + "leaflet.locatecontrol": { + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/leaflet.locatecontrol/-/leaflet.locatecontrol-0.68.0.tgz", + "integrity": "sha512-jXJCpBvkyH6shjPEOK/DWu/tKX/WdkNeO96jyPrnGelYp9u6wSDj4V1V4aX9+CMTIrEyVB4/4XuU+T7VTRpb6w==" + }, + "tslib": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" + } + } + }, "@schematics/angular": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-10.1.3.tgz", @@ -1986,6 +2021,11 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" + }, "@types/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", @@ -2017,6 +2057,22 @@ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/leaflet": { + "version": "1.5.17", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.5.17.tgz", + "integrity": "sha512-2XYq9k6kNjhNI7PaTz8Rdxcc8Vzwu97OaS9CtcrTxnTSxFUGwjlGjTDvhTLJU+JRSfZ4lBwGcl0SjZHALdVr6g==", + "requires": { + "@types/geojson": "*" + } + }, + "@types/leaflet.locatecontrol": { + "version": "0.60.7", + "resolved": "https://registry.npmjs.org/@types/leaflet.locatecontrol/-/leaflet.locatecontrol-0.60.7.tgz", + "integrity": "sha512-sac/MeK4gB+3XTJ3JzCe3HqLwKNHblIpZrxUJ6FapWK8uISZ0wcy8motVO7+v/yO47tQgsnYaobwFZ//beWHBQ==", + "requires": { + "@types/leaflet": "*" + } + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -2030,9 +2086,9 @@ "dev": true }, "@types/node": { - "version": "12.12.62", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.62.tgz", - "integrity": "sha512-qAfo81CsD7yQIM9mVyh6B/U47li5g7cfpVQEDMfQeF8pSZVwzbhwU3crc0qG4DmpsebpJPR49AKOExQyJ05Cpg==", + "version": "12.12.67", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.67.tgz", + "integrity": "sha512-R48tgL2izApf+9rYNH+3RBMbRpPeW3N8f0I9HMhggeq4UXwBDqumJ14SDs4ctTMhG11pIOduZ4z3QWGOiMc9Vg==", "dev": true }, "@types/normalize-package-data": { @@ -6838,6 +6894,12 @@ "minimalistic-assert": "^1.0.1" } }, + "hat": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/hat/-/hat-0.0.3.tgz", + "integrity": "sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=", + "dev": true + }, "hex-color-regex": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", @@ -7998,6 +8060,12 @@ "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=" }, + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8525,6 +8593,20 @@ } } }, + "karma-browserify": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/karma-browserify/-/karma-browserify-7.0.0.tgz", + "integrity": "sha512-SLgh1dmF2eZEj3glrmTD2CMJRGZwEiKA6k2hBr2+2JDC4JMU1dlsvBKpV66Lvi/tbj3H9qA+Vl/FdIcfPRrJpA==", + "dev": true, + "requires": { + "convert-source-map": "^1.1.3", + "hat": "^0.0.3", + "js-string-escape": "^1.0.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.0", + "os-shim": "^0.1.3" + } + }, "karma-chrome-launcher": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", @@ -8605,6 +8687,16 @@ "package-json": "^6.3.0" } }, + "leaflet": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz", + "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==" + }, + "leaflet.locatecontrol": { + "version": "0.72.0", + "resolved": "https://registry.npmjs.org/leaflet.locatecontrol/-/leaflet.locatecontrol-0.72.0.tgz", + "integrity": "sha512-enAf10UG9Z1bV0siTP/+vG/ZVncDqSA3V8c6iZ3s6KWL5Ngkk4A4mk9Ssefj46ey98I9HSYWqoS+k2Y7EaKjwQ==" + }, "less": { "version": "3.12.2", "resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz", @@ -10338,6 +10430,12 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "os-shim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", + "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "dev": true + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", diff --git a/package.json b/package.json index fe8dccab6d06468a17a239d7892aa77da817a0ba..4b09a880a8b22842e0d35675d0a97c84bb377251 100644 --- a/package.json +++ b/package.json @@ -3,14 +3,14 @@ "version": "0.0.3", "scripts": { "ng": "ng", - "start": "ng serve --configuration=fr", + "start": "ng serve --configuration=fr --proxy-config proxy.conf.json", "build:prod": "ng build --configuration=production,fr --output-path=dist", "build:dev": "ng build --configuration=fr --output-path=dist", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", "release": "standard-version", - "translate": "ng xi18n --output-path src/locale --out-file messages.en.xlf", + "translate": "ng xi18n --ivy --output-path src/locale --out-file messages.en.xlf", "api": "json-server api/db.json --routes api/routes.json --no-cors=true" }, "private": true, @@ -25,7 +25,12 @@ "@angular/platform-browser": "~10.1.3", "@angular/platform-browser-dynamic": "~10.1.3", "@angular/router": "~10.1.3", + "@asymmetrik/ngx-leaflet": "^8.1.0", + "@ngx-translate/core": "^13.0.0", + "@runette/ngx-leaflet-locate": "^1.0.3", "json-server": "^0.16.2", + "leaflet": "^1.7.1", + "leaflet.locatecontrol": "^0.72.0", "luxon": "^1.25.0", "rxjs": "~6.6.0", "tslib": "^2.0.0", @@ -38,11 +43,13 @@ "@angular/localize": "^10.1.3", "@types/jasmine": "~3.5.0", "@types/jasminewd2": "~2.0.3", - "@types/node": "^12.12.62", + "@types/leaflet": "^1.5.17", + "@types/node": "^12.12.67", "codelyzer": "^6.0.0", "jasmine-core": "~3.6.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", + "karma-browserify": "^7.0.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", diff --git a/proxy.conf.json b/proxy.conf.json index c394a84fbd59c2856fb643e48e2bf306fded35a1..4cafc6d48237ca58e50c46d222b233618a624ec7 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -1,5 +1,17 @@ { "/api": { "target": "http://localhost:3000" + }, + "/base-adresse/base-adresse-nationale/streets": { + "target": "https://passerelle.formulaireextranet.grandlyon.com", + "secure": false, + "changeOrigin": true, + "logLevel": "info" + }, + "/geocoding/photon/api": { + "target": "https://download.data.grandlyon.com", + "secure": false, + "changeOrigin": true, + "logLevel": "info" } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1e828f0aa11e738f756b954440143e43cfeac8b5..7cb7bc0d7c75be17e6764b046868be03f9677a46 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,20 +1,32 @@ import { LOCALE_ID, NgModule } from '@angular/core'; - -import { AppRoutingModule } from './app-routing.module'; +import { HttpClientModule } from '@angular/common/http'; import { BrowserModule } from '@angular/platform-browser'; import { FlexLayoutModule } from '@angular/flex-layout'; +import { AppRoutingModule } from './app-routing.module'; + import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { CustomBreakPointsProvider } from './config/custom-breakpoint'; import { FooterComponent } from './footer/footer.component'; import { HeaderComponent } from './header/header.component'; import { SharedModule } from './shared/shared.module'; -import { StructureListModule } from './structure-list/structure-list.module'; +import { MapModule } from './map/map.module'; +import { RechercheComponent } from './structure-list/components/recherche/recherche.component'; +import { StructureListComponent } from './structure-list/structure-list.component'; +import { CardComponent } from './structure-list/components/card/card.component'; @NgModule({ - declarations: [AppComponent, HeaderComponent, FooterComponent, HomeComponent], - imports: [BrowserModule, AppRoutingModule, FlexLayoutModule, SharedModule, StructureListModule], + declarations: [ + AppComponent, + HeaderComponent, + FooterComponent, + HomeComponent, + StructureListComponent, + CardComponent, + RechercheComponent, + ], + imports: [BrowserModule, HttpClientModule, AppRoutingModule, FlexLayoutModule, SharedModule, MapModule], providers: [{ provide: LOCALE_ID, useValue: 'fr' }, CustomBreakPointsProvider], bootstrap: [AppComponent], }) diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 22f5aeb0ff7922810678c2c6f4ad8f3a49578842..6016ec21f8ebd3846f0cd7a74da2c57a158730ac 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,5 +1,4 @@ -<div class="content-container"> - <div class="section-container"> - <app-structure-list></app-structure-list> - </div> +<div fxLayout="row"> + <app-structure-list [structureList]="structures" class="left-pane"></app-structure-list> + <app-map [structures]="structures" fxFlex="100"></app-map> </div> diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1a2d33f8e571499f47bc5460d7a6117d04543a6d 100644 --- a/src/app/home/home.component.scss +++ b/src/app/home/home.component.scss @@ -0,0 +1,5 @@ +.left-pane { + padding: 0 25px; + width: 590px; + min-width: 590px; +} diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts index 2c5a172682f8320efbe7d66f3373cd6221a7ae94..8c0ce9e1243e588f86c013549b02032ace2a3b04 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/home/home.component.spec.ts @@ -1,3 +1,5 @@ +import { HttpClientModule } from '@angular/common/http'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HomeComponent } from './home.component'; @@ -8,9 +10,9 @@ describe('HomeComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ HomeComponent ] - }) - .compileComponents(); + declarations: [HomeComponent], + imports: [HttpClientTestingModule], + }).compileComponents(); }); beforeEach(() => { diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 73acf06f0d3597e3f5aeaa830d8568769a446cb5..2264953e0f22d50182b61573a534eb32bad32607 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,15 +1,23 @@ import { Component, OnInit } from '@angular/core'; +import { mergeMap } from 'rxjs/operators'; +import { Structure } from '../models/structure.model'; +import { StructureService } from '../services/structure-list.service'; +const { DateTime } = require('luxon'); @Component({ selector: 'app-home', templateUrl: './home.component.html', - styleUrls: ['./home.component.scss'] + styleUrls: ['./home.component.scss'], }) export class HomeComponent implements OnInit { - - constructor() { } + public structures: Structure[] = []; + constructor(private structureService: StructureService) {} ngOnInit(): void { + this.structureService.getStructures().subscribe((structures) => { + this.structures = structures.map((structure) => + this.structureService.updateOpeningStructure(structure, DateTime.local()) + ); + }); } - } diff --git a/src/app/map/components/index.ts b/src/app/map/components/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..45941a73cbaa1d10fc7670c9d256c3b051669356 --- /dev/null +++ b/src/app/map/components/index.ts @@ -0,0 +1,6 @@ +import { MapComponent } from './map.component'; + +export { MapComponent }; + +// tslint:disable-next-line:variable-name +export const MapComponents = [MapComponent]; diff --git a/src/app/map/components/map.component.html b/src/app/map/components/map.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4ed0d90d50bac4bf1baf0b64472986f93bc7d62a --- /dev/null +++ b/src/app/map/components/map.component.html @@ -0,0 +1,2 @@ +<div id="map" leaflet [leafletOptions]="mapOptions" (leafletMapReady)="onMapReady($event)"></div> +<leaflet-locate-control [map]="map" [options]="locateOptions"></leaflet-locate-control> diff --git a/src/app/map/components/map.component.scss b/src/app/map/components/map.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..7c09074d9886cf24080ce0077526625ef2cb3f2b --- /dev/null +++ b/src/app/map/components/map.component.scss @@ -0,0 +1,85 @@ +@import '../../../assets/scss/color'; +@import '../../../assets/scss/layout'; +@import '../../../assets/scss/icons'; +@import '../../../assets/scss/typography'; + +#map { + height: calc(100vh - #{$header-height} - #{$footer-height}); + width: 100%; +} + +::ng-deep .fa-map-marker { + color: $black; + position: absolute; + margin-left: 4px; + margin-top: 2px; + width: 12px; + height: 12px; + border: solid 1px currentColor; + border-radius: 7px 7px 7px 0; + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + margin-left: -6px; + margin-top: 6px; +} + +::ng-deep .fa-map-marker:before { + content: ''; + position: absolute; + left: 3px; + top: 3px; + width: 4px; + height: 4px; + border: solid 1px currentColor; + border-radius: 3px; +} + +::ng-deep .leaflet-control-locate-circle { + &.leaflet-interactive { + stroke: $white !important; + fill: $black !important; + stroke-width: 2px !important; + } +} + +::ng-deep .leaflet-marker-icon { + &.leaflet-interactive { + circle { + fill: $primary-color !important; + stroke-width: unset !important; + } + } +} + +::ng-deep .leaflet-tooltip { + padding: 8px 10px 8px 10px; + h1 { + color: $purple; + @include cn-bold-20; + margin: 0; + } + p { + color: $grey-3; + @include cn-regular-16; + margin: 0 0 13px 0; + } + div { + align-items: center; + display: flex; + } + // Find somthing better than redeclare class. Mixins ? + .ico-dot-available { + height: 12px; + width: 12px; + background-color: $green; + border-radius: 50%; + margin-right: 8px; + } + .ico-dot-unavailable { + height: 12px; + width: 12px; + background-color: $grey; + border-radius: 50%; + margin-right: 8px; + } +} diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..f63314cb98888b7b38cc9d8c1593fb6cec90894b --- /dev/null +++ b/src/app/map/components/map.component.ts @@ -0,0 +1,139 @@ +import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { latLng, MapOptions, tileLayer, Map, CRS, TileLayer, LatLngBounds } 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'; + +@Component({ + selector: 'app-map', + templateUrl: './map.component.html', + styleUrls: ['./map.component.scss'], +}) +export class MapComponent implements OnChanges { + @Input() public structures: Structure[] = []; + @Input() public toogleToolTipIds: Array<number> = []; + public map: Map; + public mapOptions: MapOptions; + // Init locate options + public locateOptions = { + flyTo: false, + keepCurrentZoomLevel: false, + locateOptions: { + enableHighAccuracy: true, + }, + icon: 'fa-map-marker', + clickBehavior: { inView: 'stop', outOfView: 'setView', inViewNotFollowing: 'setView' }, + }; + + constructor(private mapService: MapService, private geoJsonService: GeojsonService) { + this.initializeMapOptions(); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.structures) { + this.getStructurePosition(); + } + } + + /** + * Get structures positions and add marker corresponding to those positons on the map + */ + private getStructurePosition(): void { + this.structures.forEach((element: Structure) => { + this.getCoord(element.voie).subscribe((coord: GeoJson) => { + this.mapService + .createMarker(coord.geometry.getLon(), coord.geometry.getLat(), 1, this.buildToolTip(element)) + .addTo(this.map); + }); + }); + } + + /** + * Create tooltip for display + * @param structure Structure + */ + private buildToolTip(structure: Structure): string { + const cssAvailabilityClass = structure.isOpen ? 'available' : 'unavailable'; + return ( + '<h1>' + + structure.nomDeVotreStructure + + '</h1>' + + '<p>' + + structure.typeDeStructure + + '</p><div>' + + '<span class="ico-dot-' + + cssAvailabilityClass + + '"></span><span>' + + structure.openDisplay() + + '</span></div>' + ); + } + + /** + * Get coord with a street reference + * @param idVoie Street reference + */ + public getCoord(idVoie: number): Observable<GeoJson> { + return this.geoJsonService.getAddress(idVoie).pipe(mergeMap((res) => this.geoJsonService.getCoord(res))); + } + + /** + * Add marker when map is ready to be showned + * @param map map + */ + public onMapReady(map: Map): void { + this.map = map; + } + + /** + * Init map options : + * - Metropole bounds based on a WMS service hosted by data.grandlyon.com + * - Map Layer based on open street maps + */ + private initializeMapOptions(): void { + // Init WMS service with param from data.grandlyon.com + const metroMaps = new TileLayer.WMS('https://download.data.grandlyon.com/wms/grandlyon', { + crs: CRS.EPSG4326, + transparent: true, + format: 'image/png', + attribution: 'Map data © OpenStreetMap contributors', + version: '1.3.0', + bounds: new LatLngBounds([45.437, 4.568], [46.03, 5.18]), + }); + metroMaps.wmsParams = { + format: 'image/png', + transparent: true, + version: '1.3.0', + layers: 'adr_voie_lieu.adrmetropole', + service: 'WMS', + request: 'GetMap', + width: 256, + height: 256, + }; + const carteLayer = tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + maxZoom: 20, + attribution: 'Map data © OpenStreetMap contributors', + }); + // Center is set on townhall + // Zoom is blocked on 11 to prevent people to zoom out from metropole + this.mapOptions = { + center: latLng(45.764043, 4.835659), + maxZoom: 19, + zoom: 10, + minZoom: 10, + layers: [carteLayer, metroMaps], + }; + } + + /** + * Toogle all tooltips given in parameters + */ + public toggleToolTip(ids: Array<number>): void { + ids.forEach((id) => { + this.mapService.toogleToolTip(id); + }); + } +} diff --git a/src/app/map/map.module.ts b/src/app/map/map.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..3487eb49d47035b00a1bd66a07ac50c5feb7fdfa --- /dev/null +++ b/src/app/map/map.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule, DatePipe } from '@angular/common'; +import { SharedModule } from '../shared/shared.module'; +import { MapComponents } from './components'; +import { LeafletModule } from '@asymmetrik/ngx-leaflet'; +import { NgxLeafletLocateModule } from '@runette/ngx-leaflet-locate'; +@NgModule({ + imports: [CommonModule, SharedModule, NgxLeafletLocateModule, LeafletModule], + declarations: [MapComponents], + providers: [DatePipe], + exports: [MapComponents], +}) +export class MapModule {} diff --git a/src/app/map/models/address.model.ts b/src/app/map/models/address.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..b91f488bb703e051124ac05efda68173fb4c87b7 --- /dev/null +++ b/src/app/map/models/address.model.ts @@ -0,0 +1,16 @@ +export class Address { + public id: number; + public text: string; + public type: string; + public city: string; + public citycode: string; + public zipcode: string; + + constructor(obj?: any) { + Object.assign(this, obj); + } + + public queryString(): string { + return this.text + ' ' + this.citycode; + } +} diff --git a/src/app/map/models/addressGeometry.model.ts b/src/app/map/models/addressGeometry.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..71e1ce346c39c7c7c52861356026d8fc4bba74ed --- /dev/null +++ b/src/app/map/models/addressGeometry.model.ts @@ -0,0 +1,16 @@ +export class AddressGeometry { + public coordinates: Array<number>; + public type: string; + + constructor(obj?: any) { + Object.assign(this, obj); + } + + public getLat(): number { + return this.coordinates[0]; + } + + public getLon(): number { + return this.coordinates[1]; + } +} diff --git a/src/app/map/models/geojson.model.ts b/src/app/map/models/geojson.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..3407c70da879424646eeff1ad4ad17893ac9ed7b --- /dev/null +++ b/src/app/map/models/geojson.model.ts @@ -0,0 +1,13 @@ +import { AddressGeometry } from './addressGeometry.model'; + +export class GeoJson { + public geometry: AddressGeometry; + public type: string; + public properties: object; + + constructor(obj?: any) { + Object.assign(this, obj, { + geometry: obj && obj.geometry ? new AddressGeometry(obj.geometry) : null, + }); + } +} diff --git a/src/app/map/services/geojson.service.spec.ts b/src/app/map/services/geojson.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..359674551c7ffd2fab202edd37c6013365bfedb9 --- /dev/null +++ b/src/app/map/services/geojson.service.spec.ts @@ -0,0 +1,56 @@ +import { TestBed } from '@angular/core/testing'; +import { Address } from '../models/address.model'; + +import { GeojsonService } from './geojson.service'; +import { HttpClientModule } from '@angular/common/http'; + +describe('GeojsonService', () => { + let service: GeojsonService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HttpClientModule], + }).compileComponents(); + }); + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(GeojsonService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should get address for id 26061 ', async () => { + await new Promise((resolve) => { + service.getAddress(26061).subscribe( + (val) => { + expect(val.zipcode).toEqual('69800'); + expect(val.text).toEqual('13ème Rue Cité Berliet'); + resolve(); + }, + (err) => { + console.log(err); + resolve(); + } + ); + }); + }); + + it('should get coord with query string avenue foch 69006 ', async () => { + await new Promise((resolve) => { + service.getCoord(new Address({ text: 'avenue foch', citycode: '69006' })).subscribe( + (val) => { + expect(val.geometry.getLat()).toEqual(4.8429024); + expect(val.geometry.getLon()).toEqual(45.7733884); + resolve(); + }, + (err) => { + console.log(err); + resolve(); + } + ); + }); + }); +}); diff --git a/src/app/map/services/geojson.service.ts b/src/app/map/services/geojson.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..7af9e34814df849adb8a6829c1ab4dc3979154f8 --- /dev/null +++ b/src/app/map/services/geojson.service.ts @@ -0,0 +1,33 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Address } from '../models/address.model'; +import { GeoJson } from '../models/geojson.model'; + +@Injectable({ + providedIn: 'root', +}) +export class GeojsonService { + constructor(private http: HttpClient) {} + + /** + * Retrive an address with a street national reference + * @param idVoie Number + */ + public getAddress(idVoie: number): Observable<Address> { + return this.http + .get('/base-adresse/base-adresse-nationale/streets' + '?id=' + idVoie) + .pipe(map((data: { data: any[]; err: number }) => new Address(data.data[0]))); + } + + /** + * Get GeoLocation with an address + * @param address Address + */ + public getCoord(address: Address): Observable<GeoJson> { + return this.http + .get('/geocoding/photon/api' + '?q=' + address.queryString()) + .pipe(map((data: { features: any[]; type: string }) => new GeoJson(data.features[0]))); + } +} diff --git a/src/app/map/services/map.service.spec.ts b/src/app/map/services/map.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..de3b0e0faebbcb5dfd143909227a334c629a1fde --- /dev/null +++ b/src/app/map/services/map.service.spec.ts @@ -0,0 +1,47 @@ +import { TestBed } from '@angular/core/testing'; +import { Map } from 'leaflet'; + +import { MapService } from './map.service'; + +describe('MapService', () => { + let service: MapService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(MapService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('should create marke with coord {lat: 45.764043, lng: 4.835659}', () => { + const marker = service.createMarker(45.764043, 4.835659, 1); + + expect(marker.getLatLng().lat).toEqual(45.764043); + expect(marker.getLatLng().lng).toEqual(4.835659); + }); + it('should add marker to map with icon ic_marker.png', () => { + const marker = service.createMarker(45.764043, 4.835659, 1); + expect(marker.getIcon().options.iconSize).toEqual([35, 41]); + expect(marker.getIcon().options.iconAnchor).toEqual([13, 41]); + }); + + it('should cerate marker with tooltip', () => { + const marker = service.createMarker(45.764043, 4.835659, 1, '<p>Hello <br/>World !</p>'); + + expect(marker.getTooltip().getContent()).toEqual('<p>Hello <br/>World !</p>'); + }); + + it('should get marker', () => { + const marker = service.createMarker(45.764043, 4.835659, 1, '<p>Hello <br/>World !</p>'); + expect(marker).toEqual(service.getMarker(1)); + }); + it('should not get marker, with missing id', () => { + service.createMarker(45.764043, 4.835659, 1, '<p>Hello <br/>World !</p>'); + expect(service.getMarker(2)).toEqual(null); + }); + it('should not get marker, empty', () => { + expect(service.getMarker(1)).toEqual(null); + }); +}); diff --git a/src/app/map/services/map.service.ts b/src/app/map/services/map.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..f13895072bd120ae9b9c12d114b0c483c2d95e44 --- /dev/null +++ b/src/app/map/services/map.service.ts @@ -0,0 +1,51 @@ +import { Injectable } from '@angular/core'; +import { divIcon } from 'leaflet'; +import { icon, Marker, Map } from 'leaflet'; + +@Injectable({ + providedIn: 'root', +}) +export class MapService { + private markersList = {}; + constructor() {} + + public createMarker(lat: number, lon: number, id: number, tooltip?: string): Marker { + const icone = divIcon({ + className: null, + html: "<div class='ico-marker-pin'></div>", + iconSize: [35, 41], + iconAnchor: [13, 41], + }); + const marker = new Marker([lat, lon], { icon: icone }); + + if (tooltip) { + marker.bindTooltip(tooltip); + } + this.markersList[id] = marker; + return marker; + } + + /** + * Toogle a tooltip + * @param id marker id + */ + public toogleToolTip(id: number): void { + this.getMarker(id).toggleTooltip(); + } + + /** + * Set a tooltip + * @param id markerId + * @param html html to display + */ + public setToolTip(id: number, html: string): void { + this.getMarker(id).bindTooltip(html); + } + + /** + * Get marker by id + */ + public getMarker(id: number): Marker { + return this.markersList[id] ? this.markersList[id] : null; + } +} diff --git a/src/app/structure-list/models/day.model.ts b/src/app/models/day.model.ts similarity index 100% rename from src/app/structure-list/models/day.model.ts rename to src/app/models/day.model.ts diff --git a/src/app/structure-list/models/openingDay.model.ts b/src/app/models/openingDay.model.ts similarity index 100% rename from src/app/structure-list/models/openingDay.model.ts rename to src/app/models/openingDay.model.ts diff --git a/src/app/structure-list/models/structure.model.ts b/src/app/models/structure.model.ts similarity index 81% rename from src/app/structure-list/models/structure.model.ts rename to src/app/models/structure.model.ts index 92ceb326dc9ffdc62d6529152e989a10fec6c121..191917321f193d1b9442d5e66fa2a8eeecac84d6 100644 --- a/src/app/structure-list/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -1,6 +1,6 @@ +import { Weekday } from '../structure-list/enum/weekday.enum'; import { Day } from './day.model'; import { OpeningDay } from './openingDay.model'; -import { Weekday } from '../enum/weekday.enum'; import { Week } from './week.model'; export class Structure { @@ -13,7 +13,7 @@ export class Structure { public typeDeStructure: string; public description: string; public n: string; - public voie: string; + public voie: number; public telephone: string; public courriel: string; public siteWeb: string; @@ -58,4 +58,14 @@ export class Structure { return null; } } + + public openDisplay(): string { + if (this.isOpen) { + return 'Ouvert actuellement '; + } else if (this.openedOn.day) { + return 'Fermé - Ouvre ' + this.openedOn.day + ' à ' + this.openedOn.schedule; + } else { + return 'Fermé - Aucun horaire disponible '; + } + } } diff --git a/src/app/structure-list/models/time.model.ts b/src/app/models/time.model.ts similarity index 100% rename from src/app/structure-list/models/time.model.ts rename to src/app/models/time.model.ts diff --git a/src/app/structure-list/models/week.model.ts b/src/app/models/week.model.ts similarity index 100% rename from src/app/structure-list/models/week.model.ts rename to src/app/models/week.model.ts diff --git a/src/app/structure-list/services/structure-list.service.spec.ts b/src/app/services/structure-list.service.spec.ts similarity index 100% rename from src/app/structure-list/services/structure-list.service.spec.ts rename to src/app/services/structure-list.service.spec.ts diff --git a/src/app/structure-list/services/structure-list.service.ts b/src/app/services/structure-list.service.ts similarity index 97% rename from src/app/structure-list/services/structure-list.service.ts rename to src/app/services/structure-list.service.ts index 9394b38c11432f98155e28f5934db539a88faf5c..82d06039ce5c73e24caa37fcadf2b20eb56560d7 100644 --- a/src/app/structure-list/services/structure-list.service.ts +++ b/src/app/services/structure-list.service.ts @@ -1,16 +1,14 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { WeekDay } from '@angular/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -const { DateTime } = require('luxon'); +import { Structure } from '../models/structure.model'; import { Day } from '../models/day.model'; import { OpeningDay } from '../models/openingDay.model'; -import { Structure } from '../models/structure.model'; +import { Weekday } from '../structure-list/enum/weekday.enum'; import { Time } from '../models/time.model'; -import { Weekday } from '../enum/weekday.enum'; -import { Week } from '../models/week.model'; -import { WeekDay } from '@angular/common'; @Injectable({ providedIn: 'root', diff --git a/src/app/shared/components/card/card.component.html b/src/app/shared/components/card/card.component.html deleted file mode 100644 index f2fda252a00e00222fcb897e4b51d94fd9a9c12c..0000000000000000000000000000000000000000 --- a/src/app/shared/components/card/card.component.html +++ /dev/null @@ -1 +0,0 @@ -<p>card works!</p> diff --git a/src/app/shared/components/card/card.component.scss b/src/app/shared/components/card/card.component.scss deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/shared/components/card/card.component.spec.ts b/src/app/shared/components/card/card.component.spec.ts deleted file mode 100644 index 3093fd5a30b9312cf88096bb93f28699e5be088f..0000000000000000000000000000000000000000 --- a/src/app/shared/components/card/card.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CardComponent } from './card.component'; - -describe('CardComponent', () => { - let component: CardComponent; - let fixture: ComponentFixture<CardComponent>; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ CardComponent ] - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(CardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/shared/components/card/card.component.ts b/src/app/shared/components/card/card.component.ts deleted file mode 100644 index 07a9ab07eff2b4b7ff193ff9906138d2f43366cd..0000000000000000000000000000000000000000 --- a/src/app/shared/components/card/card.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-card', - templateUrl: './card.component.html', - styleUrls: ['./card.component.scss'] -}) -export class CardComponent implements OnInit { - - constructor() { } - - ngOnInit(): void { - } - -} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index d8ec6de94aaed2d9b059b9ce0d40e7db8787ed2e..b86c35354cfa44dbd986d1eca61d20097d795bde 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -1,7 +1,7 @@ -import { CardComponent } from './card/card.component'; +// import { CardComponent } from './card/card.component'; // tslint:disable-next-line: max-line-length -export { CardComponent }; +// export { CardComponent }; // tslint:disable-next-line:variable-name -export const SharedComponents = [CardComponent]; +export const SharedComponents = []; diff --git a/src/app/structure-list/components/card/card.component.html b/src/app/structure-list/components/card/card.component.html index 490d59af86742b34d403015a62d1424ba17c5ccc..b8d0c1dc5ddb3535aaf7aac8648c35965c0b45b2 100644 --- a/src/app/structure-list/components/card/card.component.html +++ b/src/app/structure-list/components/card/card.component.html @@ -1,6 +1,5 @@ -<span class="nbStructuresLabel">{{ structures.length }} structures</span> -<div class="structure" fxLayout="column" *ngFor="let structure of structures"> - <span class="nomStructure">{{ structure.nom }}</span> +<div class="structure" fxLayout="column"> + <span class="nomStructure">{{ structure.nomDeVotreStructure }}</span> <div class="headerStructure" fxLayout="row" fxLayoutAlign="space-between center"> <span class="typeStructure">{{ structure.typeDeStructure }}</span> @@ -8,18 +7,12 @@ </div> <br /> <div class="statusStructure" fxLayout="row" fxLayoutAlign="start center"> - <div *ngIf="structure.isOpen; else closed"> - <span class="ico-dot-available"></span> - <span>Ouvert actuellement</span> + <div> + <span *ngIf="structure.isOpen; else closed" class="ico-dot-available"></span> + <span>{{ structure.openDisplay() }}</span> </div> <ng-template #closed> <span class="ico-dot-unavailable"></span> - <span *ngIf="structure.openedOn.day; else noTime"> - Fermé - Ouvre {{ structure.openedOn.day }} à {{ structure.openedOn.schedule }}</span - > - </ng-template> - <ng-template #noTime> - <span> Fermé - Aucun horaire disponible</span> </ng-template> </div> </div> diff --git a/src/app/structure-list/components/card/card.component.scss b/src/app/structure-list/components/card/card.component.scss index 732c84c034f667e2decae4e6234e78c028b93e68..3fe9e2edd479c5dfeb456ff2e069299ba1a0b598 100644 --- a/src/app/structure-list/components/card/card.component.scss +++ b/src/app/structure-list/components/card/card.component.scss @@ -2,15 +2,9 @@ @import '../../../../assets/scss/color'; @import '../../../../assets/scss/typography'; -.nbStructuresLabel { - color: $grey; - @include cn-regular-16; - display: flex; - align-items: center; -} .structure { padding: 12px 0 12px 0; - border-bottom: 1px dashed $grey; + border-bottom: 1px dashed $grey !important; .typeStructure { color: $grey; @include cn-regular-16; diff --git a/src/app/structure-list/components/card/card.component.spec.ts b/src/app/structure-list/components/card/card.component.spec.ts index c0787da7b20f605785da5d180179d53737a572ef..cf8f87ef3a62bcc106e6bf53255baf104b7bef92 100644 --- a/src/app/structure-list/components/card/card.component.spec.ts +++ b/src/app/structure-list/components/card/card.component.spec.ts @@ -2,6 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CardComponent } from './card.component'; import { HttpClientModule } from '@angular/common/http'; +import { Structure } from '../../../models/structure.model'; +import { OpeningDay } from '../../../models/openingDay.model'; + describe('CardComponent', () => { let component: CardComponent; let fixture: ComponentFixture<CardComponent>; @@ -15,8 +18,152 @@ describe('CardComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(CardComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + component = fixture.debugElement.componentInstance; + const structure = new Structure({ + id: 1, + numero: '26-63', + dateDeCreation: '2020-10-08T15:17:00.000Z', + derniereModification: '2020-10-08T15:17:00.000Z', + nomDeLusager: 'Erwan Le luron', + votreStructureEstElle: 'Un établissement principal (siège social)', + nomDeVotreStructure: 'Régie de Quartier Armstrong', + typeDeStructure: 'Tiers-lieu & coworking, FabLab', + description: "Association loi 1901 dont l'objet est l'insertion par l'économie social et solidaire", + n: 2, + voie: 21356, + telephone: '04 72 21 03 07', + courriel: 'sguillet@rqa.fr', + siteWeb: '', + facebook: '', + twitter: '@rqainfo69', + instagram: '', + civilite: 'Madame', + nom: 'GUILLET', + prenom: 'Séverine', + fonction: 'Autres', + accessibilitePersonnesAMobiliteReduitePmr: '', + choixMultiples: 'Tout public', + fermeturesExceptionnelles: '', + jaccompagneLesUsagersDansLeursDemarchesEnLigne: 'True', + accompagnementDesDemarches: 'Accompagnant CAF', + autresAccompagnements: '', + lesCompetencesDeBase: 260, + accesAuxDroits: 176, + insertionSocialeEtProfessionnelle: 254, + aideALaParentalite: '', + cultureEtSecuriteNumerique: 264, + wifiEnAccesLibre: 'True', + ordinateurs: '', + nombre: '', + tablettes: '', + bornesNumeriques: '', + imprimantes: '', + autresEspacesProposesParLaStructure: 'Espace libre service', + statutJuridique: '', + appartenezVousAUnReseauDeMediation: '', + precisezLequel: '', + idDeLitemStructureDansDirectus: 123, + statutDeLitemStructureDansDirectus: '', + idDeLitemOffreDansDirectus: '', + statut: 'Erreur lors du versement des données offre', + hours: { + monday: { + open: true, + time: [ + { + openning: 1330, + closing: 1630, + }, + { + openning: null, + closing: null, + }, + ], + }, + tuesday: { + open: true, + time: [ + { + openning: 830, + closing: 1130, + }, + { + openning: 1330, + closing: 1630, + }, + ], + }, + wednesday: { + open: true, + time: [ + { + openning: 1330, + closing: 1630, + }, + { + openning: null, + closing: null, + }, + ], + }, + thursday: { + open: true, + time: [ + { + openning: 830, + closing: 1130, + }, + { + openning: 1330, + closing: 1630, + }, + ], + }, + friday: { + open: true, + time: [ + { + openning: 830, + closing: 1130, + }, + { + openning: 1330, + closing: 1530, + }, + ], + }, + saturday: { + open: false, + time: [ + { + openning: null, + closing: null, + }, + { + openning: null, + closing: null, + }, + ], + }, + sunday: { + open: false, + time: [ + { + openning: null, + closing: null, + }, + { + openning: null, + closing: null, + }, + ], + }, + openedOn: new OpeningDay('monday', null), + }, + openedOn: new OpeningDay('monday', null), + }); + component.structure = structure; + fixture.detectChanges(); // calls NgOnit }); it('should create', () => { diff --git a/src/app/structure-list/components/card/card.component.ts b/src/app/structure-list/components/card/card.component.ts index 5594b507fe864c027fba39552a8f54326956915b..9049bcfa750403198f6e1383b1ef24d980a6f5b5 100644 --- a/src/app/structure-list/components/card/card.component.ts +++ b/src/app/structure-list/components/card/card.component.ts @@ -1,7 +1,5 @@ -import { Component, OnInit } from '@angular/core'; -import { Structure } from '../../models/structure.model'; -import { StructureService } from '../../services/structure-list.service'; -const { DateTime } = require('luxon'); +import { Component, Input, OnInit } from '@angular/core'; +import { Structure } from '../../../models/structure.model'; @Component({ selector: 'app-card', @@ -9,14 +7,8 @@ const { DateTime } = require('luxon'); styleUrls: ['./card.component.scss'], }) export class CardComponent implements OnInit { - structures: Structure[] = []; - constructor(private structureService: StructureService) {} + @Input() public structure: Structure; + constructor() {} - ngOnInit(): void { - this.structureService.getStructures().subscribe((structures) => { - structures.forEach((s: Structure) => { - this.structures.push(this.structureService.updateOpeningStructure(s, DateTime.local())); - }); - }); - } + ngOnInit(): void {} } diff --git a/src/app/structure-list/structure-list.component.html b/src/app/structure-list/structure-list.component.html index a6dbe19b7db682a545bc9d1336ade5f38bbc32cf..017b985182311ed9b97da4b95c058bdbfb21e700 100644 --- a/src/app/structure-list/structure-list.component.html +++ b/src/app/structure-list/structure-list.component.html @@ -1,2 +1,3 @@ <app-recherche></app-recherche> -<app-card></app-card> +<span class="nbStructuresLabel">{{ structureList.length }} structures</span> +<app-card *ngFor="let structure of structureList" [structure]="structure"></app-card> diff --git a/src/app/structure-list/structure-list.component.spec.ts b/src/app/structure-list/structure-list.component.spec.ts index bbd876b553a0be69caa55eb4b2b3ac895f96e6ed..b00f05ee5b5fe64013449fe33fb9145ad13f448f 100644 --- a/src/app/structure-list/structure-list.component.spec.ts +++ b/src/app/structure-list/structure-list.component.spec.ts @@ -1,8 +1,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { StyleUtils } from '@angular/flex-layout'; +import { OpeningDay } from '../models/openingDay.model'; +import { Structure } from '../models/structure.model'; import { StructureListComponent } from './structure-list.component'; -describe('StructureComponent', () => { +describe('StructureListComponent', () => { let component: StructureListComponent; let fixture: ComponentFixture<StructureListComponent>; @@ -14,8 +17,155 @@ describe('StructureComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(StructureListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + component = fixture.debugElement.componentInstance; + const structureList = new Array<Structure>( + new Structure({ + id: 1, + numero: '26-63', + dateDeCreation: '2020-10-08T15:17:00.000Z', + derniereModification: '2020-10-08T15:17:00.000Z', + nomDeLusager: 'Erwan Le luron', + votreStructureEstElle: 'Un établissement principal (siège social)', + nomDeVotreStructure: 'Régie de Quartier Armstrong', + typeDeStructure: 'Tiers-lieu & coworking, FabLab', + description: "Association loi 1901 dont l'objet est l'insertion par l'économie social et solidaire", + n: 2, + voie: 21356, + telephone: '04 72 21 03 07', + courriel: 'sguillet@rqa.fr', + siteWeb: '', + facebook: '', + twitter: '@rqainfo69', + instagram: '', + civilite: 'Madame', + nom: 'GUILLET', + prenom: 'Séverine', + fonction: 'Autres', + accessibilitePersonnesAMobiliteReduitePmr: '', + choixMultiples: 'Tout public', + fermeturesExceptionnelles: '', + jaccompagneLesUsagersDansLeursDemarchesEnLigne: 'True', + accompagnementDesDemarches: 'Accompagnant CAF', + autresAccompagnements: '', + lesCompetencesDeBase: 260, + accesAuxDroits: 176, + insertionSocialeEtProfessionnelle: 254, + aideALaParentalite: '', + cultureEtSecuriteNumerique: 264, + wifiEnAccesLibre: 'True', + ordinateurs: '', + nombre: '', + tablettes: '', + bornesNumeriques: '', + imprimantes: '', + autresEspacesProposesParLaStructure: 'Espace libre service', + statutJuridique: '', + appartenezVousAUnReseauDeMediation: '', + precisezLequel: '', + idDeLitemStructureDansDirectus: 123, + statutDeLitemStructureDansDirectus: '', + idDeLitemOffreDansDirectus: '', + statut: 'Erreur lors du versement des données offre', + hours: { + monday: { + open: true, + time: [ + { + openning: 1330, + closing: 1630, + }, + { + openning: null, + closing: null, + }, + ], + }, + tuesday: { + open: true, + time: [ + { + openning: 830, + closing: 1130, + }, + { + openning: 1330, + closing: 1630, + }, + ], + }, + wednesday: { + open: true, + time: [ + { + openning: 1330, + closing: 1630, + }, + { + openning: null, + closing: null, + }, + ], + }, + thursday: { + open: true, + time: [ + { + openning: 830, + closing: 1130, + }, + { + openning: 1330, + closing: 1630, + }, + ], + }, + friday: { + open: true, + time: [ + { + openning: 830, + closing: 1130, + }, + { + openning: 1330, + closing: 1530, + }, + ], + }, + saturday: { + open: false, + time: [ + { + openning: null, + closing: null, + }, + { + openning: null, + closing: null, + }, + ], + }, + sunday: { + open: false, + time: [ + { + openning: null, + closing: null, + }, + { + openning: null, + closing: null, + }, + ], + }, + }, + openedOn: new OpeningDay('monday', null), + }) + ); + structureList.length = 4; + console.log(structureList.length); + component.structureList = structureList; + fixture.detectChanges(); // calls NgOnit }); it('should create', () => { diff --git a/src/app/structure-list/structure-list.component.ts b/src/app/structure-list/structure-list.component.ts index 276b325e4c01644fe5ac79783e456213acaa5801..37296692db16a7133f47f6cfab0db52dba29fb2e 100644 --- a/src/app/structure-list/structure-list.component.ts +++ b/src/app/structure-list/structure-list.component.ts @@ -1,10 +1,13 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; +import { Structure } from '../models/structure.model'; @Component({ selector: 'app-structure-list', templateUrl: './structure-list.component.html', + styleUrls: ['./structure-list.scss'], }) export class StructureListComponent implements OnInit { + @Input() public structureList: Structure[]; constructor() {} ngOnInit(): void {} diff --git a/src/app/structure-list/structure-list.module.ts b/src/app/structure-list/structure-list.module.ts deleted file mode 100644 index 7067e0fef4af112189021ec27b5de0b796e78c2d..0000000000000000000000000000000000000000 --- a/src/app/structure-list/structure-list.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { StructureListComponent } from './structure-list.component'; -import { CardComponent } from './components/card/card.component'; -import { RechercheComponent } from './components/recherche/recherche.component'; -import { HttpClientModule } from '@angular/common/http'; -import { FlexLayoutModule } from '@angular/flex-layout'; - -@NgModule({ - declarations: [StructureListComponent, CardComponent, RechercheComponent], - imports: [CommonModule, HttpClientModule, FlexLayoutModule], - exports: [StructureListComponent], -}) -export class StructureListModule {} diff --git a/src/app/structure-list/structure-list.scss b/src/app/structure-list/structure-list.scss new file mode 100644 index 0000000000000000000000000000000000000000..68c10dffe4b5a6c42bab3f90bd9513695bfdd215 --- /dev/null +++ b/src/app/structure-list/structure-list.scss @@ -0,0 +1,10 @@ +@import '../../assets/scss/icons'; +@import '../../assets/scss/color'; +@import '../../assets/scss/typography'; + +.nbStructuresLabel { + color: $grey; + @include cn-regular-16; + display: flex; + align-items: center; +} diff --git a/src/assets/scss/_icons.scss b/src/assets/scss/_icons.scss index de969d5b3d2e94beafa7bc7cc461fb2414b3527f..f907cb4d0d71766362b40d36cea9f43ce33d1b07 100644 --- a/src/assets/scss/_icons.scss +++ b/src/assets/scss/_icons.scss @@ -78,3 +78,25 @@ border-radius: 50%; display: inline-block; } + +.ico-marker-pin { + width: 30px; + height: 30px; + border-radius: 50% 50% 50% 0; + background: $purple; + position: absolute; + transform: rotate(-45deg); + left: 50%; + top: 50%; + margin: -15px 0 0 -15px; +} + +.ico-marker-pin::after { + content: ''; + width: 10px; + height: 10px; + margin: 10px 0 0 10px; + background: #fff; + position: absolute; + border-radius: 50%; +} diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 79b25c9003972a9962763fdbc373514b09f0d65a..9ad8ed69b43131641f17d380ccfcc202f272e3c0 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -5,6 +5,8 @@ export const environment = { production: false, registrationForm: 'https://services.formulaireextranet.grandlyon.com/saisie-fiche-structure', + addressApi: 'https://passerelle.formulaireextranet.grandlyon.com/base-adresse/base-adresse-nationale/streets', + geojsonApi: 'https://download.data.grandlyon.com/geocoding/photon/api', VERSION: require('../../package.json').version, }; diff --git a/src/index.html b/src/index.html index 761c288f29ddf7adb06477a7511fe0ee4a0ee9a9..e3de07cb0d1ac1c7fed3b97dfb751f37fd28efd6 100644 --- a/src/index.html +++ b/src/index.html @@ -1,13 +1,19 @@ -<!doctype html> +<!DOCTYPE html> <html lang="en"> -<head> - <meta charset="utf-8"> - <title>Pamn</title> - <base href="/"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="icon" type="image/x-icon" href="favicon.ico"> -</head> -<body> - <app-root></app-root> -</body> + <head> + <meta charset="utf-8" /> + <title>Pamn</title> + <base href="/" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <link rel="icon" type="image/x-icon" href="favicon.ico" /> + <link rel="stylesheet" href="https://openlayers.org/en/v4.6.5/css/ol.css" type="text/css" /> + <link + rel="stylesheet" + href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol@0.72.0/dist/L.Control.Locate.min.css" + /> + <script src="https://openlayers.org/en/v4.6.5/build/ol.js" type="text/javascript"></script> + </head> + <body> + <app-root></app-root> + </body> </html> diff --git a/src/locale/messages.en.xlf b/src/locale/messages.en.xlf index 0a4b401a0ec44f79b709230acad0bb17ed978c2b..c5c8d8d50c2fa118944532fee1c5cf76593e1221 100644 --- a/src/locale/messages.en.xlf +++ b/src/locale/messages.en.xlf @@ -58,6 +58,10 @@ <context context-type="linenumber">18</context> </context-group> </trans-unit> + <trans-unit id="monday" datatype="html"> + <source>monday</source> + <target>lundi</target> + </trans-unit> </body> </file> </xliff> diff --git a/src/main.ts b/src/main.ts index c7b673cf44b388e9989fe908b78d7d73cd2e1409..d9a2e7e4a582e265db779363bd8b2492c43c141b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,5 +8,6 @@ if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/styles.scss b/src/styles.scss index 96329efae6b58606d3cbc3ec830fab25f923f0d3..0a1dafb138e69161d599a9e59e67c1a9c0c8c734 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -3,6 +3,8 @@ @import 'assets/scss/typography'; @import 'assets/scss/color'; @import 'assets/scss/breakpoint'; +@import 'assets/scss/icons'; +@import '../node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css'; html, body { diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 6c31866e90b3f1f1d201ecc8e715590ba70dc1de..1fde99220b26798b39cfc44013c710d00ef00853 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -3,7 +3,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", - "types": ["jasmine", "node"] + "types": ["node", "jasmine"] }, "files": ["src/test.ts", "src/polyfills.ts"], "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]