diff --git a/CHANGELOG.md b/CHANGELOG.md index 54bd7acc8a91b0027f90f1697acaaca3d96d6c3a..7f6e3c6be131d30d1f76d11768430618ff579fff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.12.0](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/compare/v1.11.1...v1.12.0) (2022-02-01) + + +### Features + +* **contact:** Add a contact form ([ecfa689](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/ecfa68943176727234e531d58e556bb9283ff54a)) +* **news:** redesign of news page, filter system on tag as 'Allophone'... is now disabled for display. ([de858d8](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/de858d864cc79ff6ca160c5bb6def2490b6a5003)) +* **structure:** change addresse api in order to cover the all department. Filtering duplicated addresses in order to dispaly one occurence for user. Brignais is now removed from carto. ([17e97fd](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/17e97fdb4571ffa2dfb55f59e761a12ac00e2299)) +* **stucture-details:** sticky close button and structure name ([6ddb95d](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/6ddb95d2a4e491b17b7c2641cf8be07e7c8b6e79)) +* **user-management:** add table with all users in admin user management page ([461fed5](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/461fed507396b5d988faae9bd0e3171633197e2e)) +* **version:** Display app version ([3c5f554](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/3c5f554b0a00de8e2dfef0cb76b76ed30d090036)) + + +### Bug Fixes + +* better handling of scroll reset inside the app in case of navigation ([560955b](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/560955bb189a879e0095f9e3cb0e6d7a1d21fd4e)) +* **heder:** logo position was not centered perfectly ([391e4d4](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/391e4d4e7108306be1b62dab46a05eb10b26eba8)) +* **news:** clean code and remove unecesary api call for 'a-la-une' tag ([5ada347](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/5ada347d12c24339a1fa1ee4d2da2a30eeb5c4bf)) +* **news:** fix scroll issue and css display on mobile ([7a2fa9a](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/7a2fa9aa8c9ff65d9770a554434bd607ec8af321)) +* set missing validation form on phone input ([95ffed1](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/95ffed17fd618e963fa037e80d2f2e59c7c0748f)) +* **structure-list:** border-bottom is now correctly removed on last element ([1f5a501](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/1f5a50128b1f8521a85c302468783a45c51f2b03)) +* **style:** header display on mobile. Logo is now properly centered ([667288f](https://forge.grandlyon.com/web-et-numerique/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client/commit/667288fd4d58e58fa97aba86f68dd4324df01d51)) + ### [1.11.1](https://forge.grandlyon.com///compare/v1.11.0...v1.11.1) (2022-01-31) diff --git a/karma.conf.js b/karma.conf.js index 5cee0fd8e3f3150dc8c6a2886e2dbb9556dad15b..e0feaefc765ff9a114afb9babfec76106204a163 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -30,7 +30,7 @@ module.exports = function (config) { target: 'https://passerelle.formulaireextranet.grandlyon.com', changeOrigin: true, }, - '/geocoding/photon-bal/api': { + '/geocoding/photon/api': { target: 'https://download.data.grandlyon.com', changeOrigin: true, }, diff --git a/nginx/dev.conf b/nginx/dev.conf index 38db77c594a2a93b823addfc1721b26b37a59764..7609f90b77cf963d3628bce01b5bf1e677de4952 100644 --- a/nginx/dev.conf +++ b/nginx/dev.conf @@ -52,8 +52,8 @@ server { proxy_pass https://passerelle.formulaireextranet.grandlyon.com/base-adresse/base-adresse-nationale/streets; } - location /geocoding/photon-bal/api { - proxy_pass https://download.data.grandlyon.com/geocoding/photon-bal/api; + location /geocoding/photon/api { + proxy_pass https://download.data.grandlyon.com/geocoding/photon/api; } location /reverse { diff --git a/package-lock.json b/package-lock.json index 4017a21dad15b16f31e0eeb9f43ffa5c3fbb2cf5..4049fb11c11f197d252f0b5f0f11147e8bef2d13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pamn", - "version": "1.11.1", + "version": "1.12.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3412,6 +3412,31 @@ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", "dev": true }, + "ag-grid-angular": { + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/ag-grid-angular/-/ag-grid-angular-26.2.0.tgz", + "integrity": "sha512-IJYNniJkQXQhEMdsZ50MFMY80K3PQGsh4Jh1Nu7G1Det5Pq2QNPgZ/FwNucsxYDPn32VICVVlUEQTwtEl63FZQ==", + "requires": { + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "ag-grid-community": { + "version": "26.2.1", + "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-26.2.1.tgz", + "integrity": "sha512-aChSGNdPkBda4BhOUUEAmAkRlIG7rFU8UTXx3NPStavrCOHKLDRV90djIKuiXfM6ONBqKmeqw2as0yuLnSN8dw==" + }, + "ag-grid-enterprise": { + "version": "26.2.1", + "resolved": "https://registry.npmjs.org/ag-grid-enterprise/-/ag-grid-enterprise-26.2.1.tgz", + "integrity": "sha512-DSf+PPRHv9JXhWTg7KSEm9+ixYAEPuo3LKBUzowUCQWvygGiWBrZXEB67MnjW+Z9zULeFhU4Z6OuHbnQUDhCag==" + }, "agent-base": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", @@ -12046,6 +12071,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngx-toastr": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-13.2.1.tgz", + "integrity": "sha512-UAzp7/xWK9IXA2LsOmhpaaIGCqscvJokoQpBNpAMrjEkDeSlFf8PWQAuQY795KW0mJb3qF9UG/s23nsXfMYKmg==", + "requires": { + "tslib": "^2.0.0" + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/package.json b/package.json index 9d177a23d14821053d957ab09a26226e884ff8b0..1cf2250c8db838bb501d59ff2261088f2b46ba68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pamn", - "version": "1.11.1", + "version": "1.12.0", "scripts": { "ng": "ng", "start": "ng serve --configuration=fr --proxy-config proxy.conf.json", @@ -29,12 +29,16 @@ "@angular/router": "~11.2.12", "@asymmetrik/ngx-leaflet": "^8.1.0", "@ngx-translate/core": "^13.0.0", + "ag-grid-angular": "^26.2.0", + "ag-grid-community": "^26.2.1", + "ag-grid-enterprise": "^26.2.1", "json-server": "^0.16.2", "jwt-decode": "^3.1.2", "leaflet": "^1.7.1", "leaflet.locatecontrol": "^0.72.0", "lodash": "^4.17.20", "luxon": "^1.25.0", + "ngx-toastr": "^13.2.1", "npx": "^10.2.2", "rxjs": "~6.6.0", "tslib": "^2.0.0", diff --git a/proxy.conf.json b/proxy.conf.json index 09812b4a54ece1f43066d1fd7ae5f71675623c58..f5d9180245f7dc46708cd6947716af80568f6e29 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -26,7 +26,7 @@ "changeOrigin": true, "logLevel": "info" }, - "/geocoding/photon-bal/api": { + "/geocoding/photon/api": { "target": "https://download.data.grandlyon.com", "secure": false, "changeOrigin": true, diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html index 678e05776b3dc974c4a02cd3f3b6c3d6c2bafb54..23034e3aa7f0e9b1a4436b3bcf9d8a0fab2830de 100644 --- a/src/app/about/about.component.html +++ b/src/app/about/about.component.html @@ -27,6 +27,7 @@ acteurs. </p> <p>N'hésitez pas à contribuer à cet espace en partageant vos ressources</p> + <p class="version" fxLayoutAlign="center center">Version : {{ version }}</p> </div> <div fxLayout="column" fxLayoutAlign="center center" fxLayoutGap="20px"> <img src="/assets/logos/logo_europe.png" width="220" height="168" alt="logo de l'union européenne" /> diff --git a/src/app/about/about.component.scss b/src/app/about/about.component.scss index 08f1b70f14040182e176340db2ee075f95697129..a5d775dd2534796b5115efd28d9d95529993bdbe 100644 --- a/src/app/about/about.component.scss +++ b/src/app/about/about.component.scss @@ -13,3 +13,9 @@ h2 { .about-container { max-width: 760px; } + +.version { + padding-top: 80px; + font-size: $font-size-xxsmall; + color: $grey-3; +} diff --git a/src/app/about/about.component.ts b/src/app/about/about.component.ts index 9898f7a23864d3a2f7863283d9a870ddd6049906..1368672d65859aefbca327f58baa2b6ff44c8550 100644 --- a/src/app/about/about.component.ts +++ b/src/app/about/about.component.ts @@ -1,15 +1,15 @@ import { Component, OnInit } from '@angular/core'; +import { version } from '../../../package.json'; @Component({ selector: 'app-about', templateUrl: './about.component.html', - styleUrls: ['./about.component.scss'] + styleUrls: ['./about.component.scss'], }) export class AboutComponent implements OnInit { + public version: string = version; - constructor() { } - - ngOnInit(): void { - } + constructor() {} + ngOnInit(): void {} } diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts index e100d9c52db3bddf5f9770fbe53b5e9d31e6667f..29e6689e76c172fe4492ad8f8c8000fb9b792e6c 100644 --- a/src/app/admin/admin.module.ts +++ b/src/app/admin/admin.module.ts @@ -2,14 +2,30 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { PanelComponent } from './components/panel/panel.component'; import { ClaimStructureComponent } from './components/claim-structure/claim-structure.component'; -import { DeleteUserComponent } from './components/delete-user/delete-user.component'; +import { ManageUsersComponent } from './components/manage-users/manage-users.component'; import { SharedModule } from '../shared/shared.module'; import { NewsletterUsersComponent } from './components/newsletter-users/newsletter-users.component'; import { AdminStructuresListComponent } from './components/structures-list/admin-structures-list.component'; import { AdminRoutingModule } from './admin-routing.module'; +import { AgGridModule } from 'ag-grid-angular'; +import { DeleteUserComponent } from './components/manage-users/delete-user/delete-user.component'; +import { AdministredStructuresComponent } from './components/manage-users/administred-structures/administred-structures.component'; @NgModule({ - declarations: [PanelComponent, ClaimStructureComponent, DeleteUserComponent, NewsletterUsersComponent, AdminStructuresListComponent], - imports: [CommonModule, AdminRoutingModule, SharedModule], + declarations: [ + PanelComponent, + ClaimStructureComponent, + NewsletterUsersComponent, + AdminStructuresListComponent, + ManageUsersComponent, + DeleteUserComponent, + AdministredStructuresComponent, + ], + imports: [ + CommonModule, + AdminRoutingModule, + SharedModule, + AgGridModule.withComponents([DeleteUserComponent, AdministredStructuresComponent]), + ], }) export class AdminModule {} diff --git a/src/app/admin/components/delete-user/delete-user.component.html b/src/app/admin/components/delete-user/delete-user.component.html deleted file mode 100644 index 5649bff291a72590b0f0f6d65fbce1f30fb3458b..0000000000000000000000000000000000000000 --- a/src/app/admin/components/delete-user/delete-user.component.html +++ /dev/null @@ -1,22 +0,0 @@ -<div fxLayout="column" fxLayoutGap="5px" fxLayoutAlign="center center" class="userBlock"> - <h4>Suppression d'utilisateurs</h4> - <div fxLayout="row"> - <input #searchstring (keyup)="(0)" /> - <button (click)="searchUsers(searchstring.value)">Rechercher</button> - </div> - <div class="userList"> - <tr *ngFor="let user of users"> - <td>{{ user.email }}</td> - <td> - <button (click)="toggleDeleteModal(user)">Supprimer</button> - </td> - </tr> - <app-modal-confirmation - *ngIf="userToDelete" - [openned]="deleteModalOpenned" - [content]="'Voulez-vous vraiment supprimer cet utilisateur ? (' + userToDelete.email + ')'" - (closed)="deleteUser(userToDelete, $event)" - ></app-modal-confirmation> - <div *ngIf="users && users.length == 0">Aucun résultat</div> - </div> -</div> diff --git a/src/app/admin/components/delete-user/delete-user.component.ts b/src/app/admin/components/delete-user/delete-user.component.ts deleted file mode 100644 index 293324fe75e915db8f30a2c891097a7f6a0508ed..0000000000000000000000000000000000000000 --- a/src/app/admin/components/delete-user/delete-user.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Component } from '@angular/core'; -import { User } from '../../../models/user.model'; -import { ProfileService } from '../../../profile/services/profile.service'; -import { AdminService } from '../../services/admin.service'; - -@Component({ - selector: 'app-admin-delete-user', - templateUrl: './delete-user.component.html', -}) -export class DeleteUserComponent { - public users: User[]; - public deleteModalOpenned = false; - public userToDelete: User = null; - - constructor(private adminService: AdminService, private profileService: ProfileService) {} - - public deleteUser(user: User, shouldDelete: boolean): void { - this.toggleDeleteModal(user); - if (shouldDelete) { - this.adminService.deleteUser(user._id).subscribe((data) => { - this.users = this.users.filter((obj) => obj.email !== data.email); - }); - } - } - - public toggleDeleteModal(userToDelete: User): void { - this.userToDelete = userToDelete; - this.deleteModalOpenned = !this.deleteModalOpenned; - } - - public searchUsers(searchString: string): void { - this.adminService.searchUsers(searchString).subscribe((users) => { - this.profileService.getProfile().then((profile) => { - this.users = users.filter((obj) => obj.email != profile.email); - }); - }); - } -} diff --git a/src/app/admin/components/manage-users/administred-structures/administred-structures.component.html b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.html new file mode 100644 index 0000000000000000000000000000000000000000..5943c02508cc249d4af1f7e050cf1602f76b7585 --- /dev/null +++ b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.html @@ -0,0 +1,5 @@ +<div id="structure-list" *ngIf="structures.data.structures && structures.data.structures.length != 0"> + <p *ngFor="let structure of structures.data.structures"> + <a href="/acteurs?id={{ structure._id }}" target="_blank">{{ structure.structureName }}</a> + </p> +</div> diff --git a/src/app/admin/components/manage-users/administred-structures/administred-structures.component.scss b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..b00c527073b4897ce1f61c3d5e7ca3ebfb8f21a9 --- /dev/null +++ b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.scss @@ -0,0 +1,4 @@ +p { + margin-top: 0px; + margin-bottom: 0px; +} diff --git a/src/app/admin/components/manage-users/administred-structures/administred-structures.component.spec.ts b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..37b12c7b44b509c2d55fce5b860958ffb988acaa --- /dev/null +++ b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdministredStructuresComponent } from './administred-structures.component'; + +describe('AdministredStructuresComponent', () => { + let component: AdministredStructuresComponent; + let fixture: ComponentFixture<AdministredStructuresComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdministredStructuresComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AdministredStructuresComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/components/manage-users/administred-structures/administred-structures.component.ts b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ea76eb873355a8f4fca820c2134da4313d2a171 --- /dev/null +++ b/src/app/admin/components/manage-users/administred-structures/administred-structures.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-administred-structures', + templateUrl: './administred-structures.component.html', + styleUrls: ['./administred-structures.component.scss'], +}) +export class AdministredStructuresComponent { + public structures: any; + + public agInit(params: any): void { + this.structures = params; + } +} diff --git a/src/app/admin/components/manage-users/delete-user/delete-user.component.html b/src/app/admin/components/manage-users/delete-user/delete-user.component.html new file mode 100644 index 0000000000000000000000000000000000000000..9c0b02eff6867621545a7bd8cbea54913ee03629 --- /dev/null +++ b/src/app/admin/components/manage-users/delete-user/delete-user.component.html @@ -0,0 +1 @@ +<button type="button" (click)="onClick()">{{ label }}</button> diff --git a/src/app/admin/components/manage-users/delete-user/delete-user.component.spec.ts b/src/app/admin/components/manage-users/delete-user/delete-user.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f09901c99926b570f87c1c9e7774550f0339d6f6 --- /dev/null +++ b/src/app/admin/components/manage-users/delete-user/delete-user.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeleteUserComponent } from './delete-user.component'; + +describe('DeleteUserComponent', () => { + let component: DeleteUserComponent; + let fixture: ComponentFixture<DeleteUserComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DeleteUserComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DeleteUserComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/components/manage-users/delete-user/delete-user.component.ts b/src/app/admin/components/manage-users/delete-user/delete-user.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8c7b0b2b3389def052bc3231d4a6817785310ea --- /dev/null +++ b/src/app/admin/components/manage-users/delete-user/delete-user.component.ts @@ -0,0 +1,21 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-delete-user', + templateUrl: './delete-user.component.html', +}) +export class DeleteUserComponent { + public params: any; + public label: string; + + public agInit(params: any): void { + this.params = params; + this.label = this.params.label || null; + } + + public onClick(): void { + if (this.params.onClick instanceof Function) { + this.params.onClick(this.params); + } + } +} diff --git a/src/app/admin/components/manage-users/manage-users.component.html b/src/app/admin/components/manage-users/manage-users.component.html new file mode 100644 index 0000000000000000000000000000000000000000..3f3dde87fd964bbcfee5fe6e6509bbdaed0b1456 --- /dev/null +++ b/src/app/admin/components/manage-users/manage-users.component.html @@ -0,0 +1,59 @@ +<div fxLayout="column" fxLayoutGap="5px" fxLayoutAlign="center center" class="userBlock"> + <h3> + Gestion des utilisateurs + <span *ngIf="unVerifiedUsers && unAttachedUsers && attachedUsers"> + ({{ unVerifiedUsers.length + unAttachedUsers.length + attachedUsers.length }}) + </span> + </h3> +</div> + +<div class="tables-list"> + <div> + <h3 class="title" *ngIf="unVerifiedUsers">Utilisateurs non vérifiés ({{ unVerifiedUsers.length }})</h3> + <ag-grid-angular + class="ag-theme-alpine user-table" + [rowData]="unVerifiedUsers" + [columnDefs]="columnDefs" + rowSelection="multiple" + domLayout="autoHeight" + [getRowHeight]="getRowHeight" + [defaultColDef]="unAttachedColDef" + [frameworkComponents]="frameworkComponents" + > + </ag-grid-angular> + </div> + <div> + <h3 class="title" *ngIf="unAttachedUsers">Utilisateurs non rattachés ({{ unAttachedUsers.length }})</h3> + <ag-grid-angular + class="ag-theme-alpine user-table" + [rowData]="unAttachedUsers" + [columnDefs]="columnDefs" + rowSelection="multiple" + domLayout="autoHeight" + [getRowHeight]="getRowHeight" + [defaultColDef]="unAttachedColDef" + [frameworkComponents]="frameworkComponents" + > + </ag-grid-angular> + </div> + <div> + <h3 class="title" *ngIf="attachedUsers">Utilisateurs rattachés ({{ attachedUsers.length }})</h3> + <ag-grid-angular + class="ag-theme-alpine user-table" + [rowData]="attachedUsers" + [columnDefs]="columnDefs" + rowSelection="multiple" + domLayout="autoHeight" + [getRowHeight]="getRowHeight" + [defaultColDef]="defaultColDef" + [frameworkComponents]="frameworkComponents" + > + </ag-grid-angular> + </div> +</div> +<app-modal-confirmation + *ngIf="userToDelete" + [openned]="deleteModalOpenned" + [content]="'Voulez-vous vraiment supprimer cet utilisateur ? (' + userToDelete.email + ')'" + (closed)="deleteUser(userToDelete, $event)" +></app-modal-confirmation> diff --git a/src/app/admin/components/manage-users/manage-users.component.scss b/src/app/admin/components/manage-users/manage-users.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..27ad71aba310d2d37158fcf0c4021718959b8323 --- /dev/null +++ b/src/app/admin/components/manage-users/manage-users.component.scss @@ -0,0 +1,20 @@ +@import '../../../../assets/scss/color'; + +.user-table { + width: 100%; + height: 100%; + text-overflow: clip; +} + +.tables-list { + height: 100%; + width: 80%; + display: flex; + flex-direction: column; + margin-left: 10%; + margin-bottom: 2%; +} + +app-delete-user { + text-align: center; +} diff --git a/src/app/admin/components/delete-user/delete-user.component.spec.ts b/src/app/admin/components/manage-users/manage-users.component.spec.ts similarity index 76% rename from src/app/admin/components/delete-user/delete-user.component.spec.ts rename to src/app/admin/components/manage-users/manage-users.component.spec.ts index b57ae62303160ae43f5bd64f32d05e39fedac10d..68c53fe5de1aa558741b7368e8d68fee044a0d7c 100644 --- a/src/app/admin/components/delete-user/delete-user.component.spec.ts +++ b/src/app/admin/components/manage-users/manage-users.component.spec.ts @@ -2,11 +2,11 @@ import { HttpClientModule } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; -import { DeleteUserComponent } from './delete-user.component'; +import { ManageUsersComponent } from './manage-users.component'; -describe('DeleteUserComponent', () => { - let component: DeleteUserComponent; - let fixture: ComponentFixture<DeleteUserComponent>; +describe('ManageUsersComponent', () => { + let component: ManageUsersComponent; + let fixture: ComponentFixture<ManageUsersComponent>; let USERS; let service; @@ -18,17 +18,17 @@ describe('DeleteUserComponent', () => { ]; await TestBed.configureTestingModule({ imports: [HttpClientModule], - declarations: [DeleteUserComponent], + declarations: [ManageUsersComponent], }).compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(DeleteUserComponent); + fixture = TestBed.createComponent(ManageUsersComponent); component = fixture.componentInstance; fixture.detectChanges(); - service = jasmine.createSpyObj(['searchUsers', 'deleteUser']); - component = new DeleteUserComponent(service); + service = jasmine.createSpyObj(['searchUsers', 'manageUsers']); + component = new ManageUsersComponent(service, null); }); it('should create', () => { @@ -61,7 +61,7 @@ describe('DeleteUserComponent', () => { ) ) ); - component.deleteUser(component.users[0]); + component.deleteUser(component.users[0], true); expect(component.users.length).toBe(2); }); }); diff --git a/src/app/admin/components/manage-users/manage-users.component.ts b/src/app/admin/components/manage-users/manage-users.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e89e6f043864ff6a5403c400c4bac9b8ad9a316 --- /dev/null +++ b/src/app/admin/components/manage-users/manage-users.component.ts @@ -0,0 +1,131 @@ +import { Component } from '@angular/core'; +import { User } from '../../../models/user.model'; +import { AdminService } from '../../services/admin.service'; +import { DeleteUserComponent } from './delete-user/delete-user.component'; +import { AdministredStructuresComponent } from './administred-structures/administred-structures.component'; + +@Component({ + selector: 'app-admin-manage-users', + templateUrl: './manage-users.component.html', + styleUrls: ['./manage-users.component.scss'], +}) +export class ManageUsersComponent { + public attachedUsers: User[] = []; + public unAttachedUsers: User[] = []; + public unVerifiedUsers: User[] = []; + public deleteModalOpenned = false; + public userToDelete: User = null; + + public columnDefs; + public frameworkComponents; + public defaultColDef = { + editable: true, + sortable: true, + flex: 1, + minWidth: 100, + filter: true, + resizable: true, + }; + public unAttachedColDef = { + ...this.defaultColDef, + cellStyle: { + color: '#da6c2e', + }, + }; + + constructor(private adminService: AdminService) { + this.columnDefs = [ + { + headerName: 'Nom', + field: 'surname', + }, + { + headerName: 'Prénom', + field: 'name', + }, + { + minWidth: 150, + headerName: 'Mail', + field: 'email', + }, + { + minWidth: 150, + headerName: 'Téléphone', + field: 'phone', + }, + { + headerName: 'Structures administrées', + cellRenderer: 'administredStructuresComponent', + cellRendererParams: { + structures: 'structures', + }, + minWidth: 350, + }, + { + headerName: 'Actions', + minWidth: 150, + cellRenderer: 'deleteUserComponent', + cellRendererParams: { + onClick: this.onDeleteButtonClick.bind(this), + label: 'Supprimer', + }, + cellStyle: { 'text-align': 'center' }, + }, + ]; + this.frameworkComponents = { + deleteUserComponent: DeleteUserComponent, + administredStructuresComponent: AdministredStructuresComponent, + }; + this.findAttachedUsers(); + this.findUnAttachedUsers(); + this.findUnVerifiedUsers(); + } + + public onDeleteButtonClick(arg): void { + this.deleteUser(arg.data, false); + } + + public deleteUser(user: User, shouldDelete: boolean): void { + this.toggleDeleteModal(user); + if (shouldDelete) { + this.adminService.deleteUser(user._id).subscribe((data) => { + this.unAttachedUsers = this.unAttachedUsers.filter((obj) => obj._id !== data._id); + this.unVerifiedUsers = this.unVerifiedUsers.filter((obj) => obj._id !== data._id); + this.attachedUsers = this.attachedUsers.filter((obj) => obj._id !== data._id); + }); + } + } + + public toggleDeleteModal(userToDelete: User): void { + this.userToDelete = userToDelete; + this.deleteModalOpenned = !this.deleteModalOpenned; + } + + public findAttachedUsers(): void { + this.adminService.getAttachedUsers().subscribe((users) => { + this.attachedUsers = users; + this.attachedUsers.map((user) => { + user._id = user['id']; + }); + }); + } + + public findUnAttachedUsers(): void { + this.adminService.getUnAttachedUsers().subscribe((users) => { + this.unAttachedUsers = users; + }); + } + + public findUnVerifiedUsers(): void { + this.adminService.getUnVerifiedUsers().subscribe((users) => { + this.unVerifiedUsers = users; + this.unVerifiedUsers.map((user) => { + user._id = user['id']; + }); + }); + } + + public getRowHeight(params): number { + return params.data.structures ? (params.data.structures.length != 0 ? params.data.structures.length * 40 : 40) : 40; + } +} diff --git a/src/app/admin/components/panel/panel.component.html b/src/app/admin/components/panel/panel.component.html index 1693406e92a14d24e492a0ef7ec9dd4cb66405ed..0443ed9b817fe673bf23a02565b2d6747c8d3e41 100644 --- a/src/app/admin/components/panel/panel.component.html +++ b/src/app/admin/components/panel/panel.component.html @@ -3,7 +3,7 @@ <div fxLayout="row" fxLayoutGap="20px" fxLayoutAlign="center center"> <button (click)="changeActiveFeature(features.pendingStructures)">Revendication structure</button> <button (click)="changeActiveFeature(features.structuresList)">Liste structures</button> - <button (click)="changeActiveFeature(features.deleteUsers)">Suppression d'utilisateurs</button> + <button (click)="changeActiveFeature(features.deleteUsers)">Gestion des utilisateurs</button> <button (click)="changeActiveFeature(features.newsletterUsers)">Newsletter</button> <a target="_blank" class="custom-link" rel="noopener noreferrer" [href]="ghostLink">Ghost</a> </div> @@ -11,7 +11,7 @@ <app-admin-structures-list></app-admin-structures-list> </div> <div *ngIf="selectedFeature === features.deleteUsers"> - <app-admin-delete-user></app-admin-delete-user> + <app-admin-manage-users></app-admin-manage-users> </div> <div *ngIf="selectedFeature === features.pendingStructures"> <app-admin-claim-structure></app-admin-claim-structure> diff --git a/src/app/admin/services/admin.service.ts b/src/app/admin/services/admin.service.ts index 77ca77a305e782bf4c8e2c34091120e939b106fa..65403bedd787f80d0dcbfd1e3407bd263c5bb622 100644 --- a/src/app/admin/services/admin.service.ts +++ b/src/app/admin/services/admin.service.ts @@ -29,6 +29,18 @@ export class AdminService { return this.http.get<User[]>(`api/admin/searchUsers`); } + public getAttachedUsers(): Observable<User[]> { + return this.http.get<User[]>(`api/admin/getAttachedUsers`); + } + + public getUnAttachedUsers(): Observable<User[]> { + return this.http.get<User[]>(`api/admin/getUnAttachedUsers`); + } + + public getUnVerifiedUsers(): Observable<User[]> { + return this.http.get<User[]>(`api/admin/getUnVerifiedUsers`); + } + public searchUsers(searchString: string): Observable<User[]> { return this.http.post<User[]>(`${this.baseUrl}/searchUsers`, { searchString }); } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d4fdc3edb27a07a4a389b134adedfa2c4afcedb0..4aa08599e8d1d2c34747436da1b3a08be9cf767e 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AboutComponent } from './about/about.component'; +import { ContactComponent } from './contact/contact.component'; import { FormComponent } from './form/structure-form/form.component'; import { AdminGuard } from './guards/admin.guard'; import { AuthGuard } from './guards/auth.guard'; @@ -48,6 +49,10 @@ const routes: Routes = [ path: 'about', component: AboutComponent, }, + { + path: 'contact', + component: ContactComponent, + }, { path: 'users/verify/:id', component: UserVerificationComponent, diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 4767e95fd2ec32077c07df8cb7b6bed4fb93d704..e825951f8193b92a93589cca7b20bec2aeeb0cda 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { NavigationEnd, Router } from '@angular/router'; import { ProfileService } from './profile/services/profile.service'; import { AuthService } from './services/auth.service'; import { RouterListenerService } from './services/routerListener.service'; @@ -10,7 +11,7 @@ import { WindowScrollService } from './shared/service/windowScroll.service'; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], }) -export class AppComponent { +export class AppComponent implements OnInit { title = 'pamn'; constructor( @@ -18,7 +19,8 @@ export class AppComponent { private authService: AuthService, private profilService: ProfileService, private windowScrollService: WindowScrollService, - private routerListener: RouterListenerService + private routerListener: RouterListenerService, + private router: Router ) { if (this.authService.isLoggedIn()) { this.profilService.getProfile(); @@ -30,6 +32,18 @@ export class AppComponent { this.routerListener.loadRouting(); } + ngOnInit(): void { + /** + * Reset scroll to top for article reading + */ + this.router.events.subscribe((evt) => { + if (!(evt instanceof NavigationEnd)) { + return; + } + document.getElementsByClassName('app-body')[0].scrollTo(0, 0); + }); + } + private setHeightApp(): void { const vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4111e2b27575bf8c920a36f80997213fcf1d0f24..f142abc82c7541b8e005776793936cddd1467982 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,8 @@ import { LOCALE_ID, NgModule } from '@angular/core'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; import { AppRoutingModule } from './app-routing.module'; @@ -19,6 +21,7 @@ import { StructureOpeningStatusComponent } from './structure-list/components/str import { ModalFilterComponent } from './structure-list/components/modal-filter/modal-filter.component'; import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; import { AboutComponent } from './about/about.component'; +import { ContactComponent } from './contact/contact.component'; import { FormComponent } from './form/structure-form/form.component'; import { UserVerificationComponent } from './user-verification/user-verification.component'; import { AuthGuard } from './guards/auth.guard'; @@ -53,6 +56,7 @@ import { RoleGuard } from './guards/role.guard'; StructureOpeningStatusComponent, LegalNoticeComponent, AboutComponent, + ContactComponent, UserVerificationComponent, ResetEmailComponent, ResetPasswordComponent, @@ -65,7 +69,15 @@ import { RoleGuard } from './guards/role.guard'; StructureListPrintComponent, StructurePrintHeaderComponent, ], - imports: [BrowserModule, HttpClientModule, AppRoutingModule, SharedModule, MapModule], + imports: [ + BrowserModule, + HttpClientModule, + AppRoutingModule, + SharedModule, + MapModule, + BrowserAnimationsModule, + ToastrModule.forRoot(), + ], providers: [ { provide: LOCALE_ID, useValue: 'fr' }, { provide: HTTP_INTERCEPTORS, useClass: CustomHttpInterceptor, multi: true }, diff --git a/src/app/contact/contact.component.html b/src/app/contact/contact.component.html new file mode 100644 index 0000000000000000000000000000000000000000..500329f5afd2cb1b0050debabc01d483408263cb --- /dev/null +++ b/src/app/contact/contact.component.html @@ -0,0 +1,125 @@ +<div fxLayout="column" class="form content-container full-screen"> + <div class="section-container"> + <div class="contactForm"> + <form [formGroup]="contactForm" (ngSubmit)="onSubmit()"> + <div class="form-fields"> + <h2>Nous contacter</h2> + <div class="form-group"> + <label for="name">Prénom et Nom</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="name" class="form-input" /> + <app-svg-icon + *ngIf="contactForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('name').value && !contactForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group"> + <label for="email">Adresse mail</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="email" class="form-input" /> + <app-svg-icon + *ngIf="contactForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('email').value && !contactForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group"> + <label for="phone">N° de téléphone</label> + <p class="notRequired">facultatif</p> + <div fxLayout="row" fxLayoutGap="15px"> + <input + type="text" + autocomplete="on" + formControlName="phone" + class="form-input phone" + (input)="utils.modifyPhoneInput(contactForm, 'phone', $event.target.value)" + /> + <app-svg-icon + *ngIf="contactForm.get('phone').value && contactForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('phone').value && !contactForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group"> + <label for="subject">Objet du message</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" maxlength="100" formControlName="subject" class="form-input subject" /> + <app-svg-icon + *ngIf="contactForm.get('subject').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('subject').value && !contactForm.get('subject').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group" fx-layout="column"> + <label for="message">Message</label> + <div class="textareaBlock" fxLayout="row" fxLayoutGap="15px"> + <textarea + rows="8" + placeholder="Exemple : J'aimerais avoir de l'aide sur Rés'IN." + maxlength="500" + formControlName="message" + ></textarea> + <app-svg-icon + *ngIf="contactForm.get('message').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('message').value && !contactForm.get('message').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + <p>{{ contactForm.get('message').value ? contactForm.get('message').value.length : 0 }}/500</p> + </div> + </div> + <div class="button" fxLayout="row" fxLayoutAlign="center center"> + <a routerLink="../home" class="btn btn-link">Annuler</a> + <button type="submit" class="btn btn-primary" [ngClass]="{ invalid: !contactForm.valid || loading }"> + <span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span> + Envoyer + </button> + </div> + </form> + </div> + </div> +</div> diff --git a/src/app/contact/contact.component.scss b/src/app/contact/contact.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..0755cc8a34a5c561b4875228560b9ab473ac5a09 --- /dev/null +++ b/src/app/contact/contact.component.scss @@ -0,0 +1,45 @@ +@import '../../assets/scss/color'; +@import '../../assets/scss/layout'; +@import '../../assets/scss/breakpoint'; +@import '../../assets/scss/typography'; +@import '../../assets/scss/shapes'; +@import '../../assets/scss/z-index'; + +.form-fields { + max-width: 960px; + padding: 20px; + background: $white; +} +.phone { + width: 200px; +} +.subject { + width: 600px; +} +.textareaBlock { + flex-direction: column; + box-sizing: border-box; + display: flex; + textarea { + font-family: $text-font; + width: 94%; + margin-top: 4px; + &:focus { + border: 1px solid $blue; + outline: none !important; + } + } +} +.button { + max-width: 960px; + padding: 35px; + gap: 18px; +} +p { + text-align: right; + width: 96%; + &.notRequired { + text-align: left; + margin: 0; + } +} diff --git a/src/app/contact/contact.component.spec.ts b/src/app/contact/contact.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c74f0165bb293c6c567af9fc77732e8664923499 --- /dev/null +++ b/src/app/contact/contact.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContactComponent } from './contact.component'; + +describe('ContactComponent', () => { + let component: ContactComponent; + let fixture: ComponentFixture<ContactComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ContactComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ContactComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contact/contact.component.ts b/src/app/contact/contact.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..367ec8eca317d810079db2f3aed8218312e7aff3 --- /dev/null +++ b/src/app/contact/contact.component.ts @@ -0,0 +1,74 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { ContactMessage } from '../models/contact-message.model'; +import { AuthService } from '../services/auth.service'; +import { ContactService } from '../services/contact.service'; +import { NotificationService } from '../services/notification.service'; +import { CustomRegExp } from '../utils/CustomRegExp'; +import { Utils } from '../utils/utils'; + +@Component({ + selector: 'app-contact', + templateUrl: './contact.component.html', + styleUrls: ['./contact.component.scss'], +}) +export class ContactComponent implements OnInit { + public contactForm: FormGroup; + public loading = false; + + constructor( + private formBuilder: FormBuilder, + private contactService: ContactService, + private router: Router, + private authService: AuthService, + private notificationService: NotificationService, + public utils: Utils + ) {} + + ngOnInit(): void { + this.contactForm = this.formBuilder.group({ + name: [ + this.isLoggedIn ? this.displayFullname : '', + [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)], + ], + phone: ['', [Validators.pattern(CustomRegExp.PHONE)]], + email: [this.isLoggedIn ? this.displayEmail : '', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + subject: ['', Validators.required], + message: ['', Validators.required], + }); + } + + public get isLoggedIn(): boolean { + return this.authService.isLoggedIn(); + } + public get displayFullname(): string { + return this.authService.getUsernameDisplay() + ' ' + this.authService.getUsersurnameDisplay(); + } + public get displayEmail(): string { + return this.authService.getUserEmailDisplay(); + } + + public onSubmit(): void { + if (!this.contactForm.valid) { + return; + } + this.loading = true; + + let contactMessage: ContactMessage = this.contactForm.value; + this.contactService.sendMessage(contactMessage).subscribe( + () => { + this.loading = false; + this.notificationService.showSuccess('Votre message a bien été envoyé', 'Demande de contact'); + this.router.navigate(['']); + }, + () => { + this.loading = false; + this.notificationService.showError( + 'Merci de réessayer plus tard', + "Votre demande de contact n'a pas pu être envoyée" + ); + } + ); + } +} diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html index b6de162784a7e1fde622c48d205e021c4cb1f83e..e778892c8768eb0db1451f9a3f0183d766360ae9 100644 --- a/src/app/footer/footer.component.html +++ b/src/app/footer/footer.component.html @@ -3,7 +3,7 @@ <a class="clickable text-align-center" routerLink="/legal-notice" i18n>Mentions légales</a> <a class="clickable text-align-center" routerLink="/newsletter" i18n>Newsletter</a> <!-- <a class="clickable text-align-center" routerLink="/sitemap" i18n>Plan du site</a> --> - <a class="clickable text-align-center" href="mailto:inclusionnumerique@grandlyon.com">Contact</a> + <a class="clickable text-align-center" routerLink="/contact" i18n>Contact</a> </div> <a class="metro-link" diff --git a/src/app/form/orientation-form/component/structure-detail-print/structure-detail-print.component.scss b/src/app/form/orientation-form/component/structure-detail-print/structure-detail-print.component.scss index 664e18f7aaff83dfca747e86a3e6c72a547faafe..8e3d713f56d01ddf9364710bc9634e373594c4ca 100644 --- a/src/app/form/orientation-form/component/structure-detail-print/structure-detail-print.component.scss +++ b/src/app/form/orientation-form/component/structure-detail-print/structure-detail-print.component.scss @@ -48,7 +48,7 @@ a { } .structure-details-block:last-child { - border-bottom: none; + border-bottom: none !important; } .opening-time { @@ -157,4 +157,4 @@ p, @media #{$large-phone} { flex-direction: column; } -} \ No newline at end of file +} diff --git a/src/app/form/structure-form/form.component.html b/src/app/form/structure-form/form.component.html index 827a2522eb9ace02ee8be6f17ab4e6ae8684356d..8c2aee1319362521bd2b20ac067668de7fbdd5bc 100644 --- a/src/app/form/structure-form/form.component.html +++ b/src/app/form/structure-form/form.component.html @@ -179,7 +179,7 @@ type="text" formControlName="phone" class="form-input phone" - (input)="modifyPhoneInput(accountForm, 'phone', $event.target.value)" + (input)="utils.modifyPhoneInput(accountForm, 'phone', $event.target.value); setValidationsForm()" /> <app-svg-icon *ngIf="accountForm.get('phone').valid" @@ -483,7 +483,7 @@ type="text" formControlName="contactPhone" class="form-input" - (input)="modifyPhoneInput(structureForm, 'contactPhone', $event.target.value)" + (input)="utils.modifyPhoneInput(structureForm, 'contactPhone', $event.target.value); setValidationsForm()" /> <app-svg-icon *ngIf="getStructureControl('contactPhone').valid" diff --git a/src/app/form/structure-form/form.component.scss b/src/app/form/structure-form/form.component.scss index c21116ce490e1ec6b4c2a0eb6025a769abcac45f..79d9304e1114bb5358e5b5f0a359acc905a4f8b4 100644 --- a/src/app/form/structure-form/form.component.scss +++ b/src/app/form/structure-form/form.component.scss @@ -191,11 +191,6 @@ h4 { color: $orange-warning; } } - &.notRequired { - margin-top: 0px; - font-style: italic; - color: $secondary-color; - } &.informationEndForm { margin-top: 18px; color: $grey-2; @@ -206,14 +201,6 @@ h4 { @media #{$tablet} { max-width: 90%; } - textarea { - padding: 13px 8px; - background: $grey-6; - border: 1px solid $grey-4; - border-radius: 1px; - resize: none; - @include cn-regular-16; - } p { text-align: right; } @@ -275,10 +262,6 @@ h4 { } } .form-group { - margin-bottom: 26px; - label { - color: $grey-2; - } &.facebook, &.twitter, &.instagram, @@ -321,8 +304,8 @@ h4 { } } } + input { - margin-top: 4px; &.email-placeholder::placeholder { color: #cacccb; font-style: italic; diff --git a/src/app/form/structure-form/form.component.ts b/src/app/form/structure-form/form.component.ts index 939f1f52eb59bcf0e0be4ab5d324d764bbacc0b1..c454231a2b91d92d21b946726565a9f0cdd0bb84 100644 --- a/src/app/form/structure-form/form.component.ts +++ b/src/app/form/structure-form/form.component.ts @@ -21,6 +21,8 @@ import { CustomRegExp } from '../../utils/CustomRegExp'; import { StructureWithOwners } from '../../models/structureWithOwners.model'; import { RouterListenerService } from '../../services/routerListener.service'; import { NewsletterService } from '../../services/newsletter.service'; +import { Utils } from '../../utils/utils'; + @Component({ selector: 'app-structure-form', templateUrl: './form.component.html', @@ -90,7 +92,8 @@ export class FormComponent implements OnInit { private router: Router, private route: ActivatedRoute, private routerListener: RouterListenerService, - private newsletterService: NewsletterService + private newsletterService: NewsletterService, + public utils: Utils ) {} async ngOnInit(): Promise<void> { @@ -409,17 +412,6 @@ export class FormComponent implements OnInit { return this.structureForm.get('address').get(nameControl); } - public modifyPhoneInput(form: FormGroup, controlName: string, phoneNumber: string): void { - // Take length of phone number without spaces. - const phoneNoSpace = phoneNumber.replace(/\s/g, ''); - // Check to refresh every 2 number. - if (phoneNoSpace.length % 2 === 0) { - // Add space every 2 number - form.get(controlName).setValue(phoneNoSpace.replace(/(?!^)(?=(?:\d{2})+$)/g, ' ')); //NOSONAR - } - this.setValidationsForm(); - } - private createDay(day: Day): FormGroup { return new FormGroup({ open: new FormControl(day.open, Validators.required), diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 590c7925e1db7da471356a62138e8b6147ec7bbf..3883b1dc7652d737dc25d187292d1dc5e3552c00 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -32,7 +32,9 @@ margin: 0; } @media #{$tablet} { + margin-right: unset; text-align: center; + margin-left: 32px; width: 100%; } } @@ -69,7 +71,6 @@ a { .containerIconMenu { display: none; - padding-right: 20px; @media #{$tablet} { display: block; } diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts index 13335af944c9d9007955575b6678f7e88dfa3ade..de19908091eb228988dcfe4f5861fe84c2010f51 100644 --- a/src/app/map/components/map.component.ts +++ b/src/app/map/components/map.component.ts @@ -7,7 +7,6 @@ import * as _ from 'lodash'; import { GeoJsonProperties } from '../models/geoJsonProperties.model'; import { MarkerType } from './markerType.enum'; import metropole from '../../../assets/geojson/metropole.json'; -import brignais from '../../../assets/geojson/brignais.json'; import L from 'leaflet'; import 'leaflet.locatecontrol'; @@ -295,7 +294,6 @@ export class MapComponent implements OnChanges { ) .addTo(this.map); }); - this.initBrignaisLayer(); this.initMetropoleLayer(); }); } @@ -308,18 +306,6 @@ export class MapComponent implements OnChanges { this.map.fitBounds(markerBounds, { paddingTopLeft: [300, 0] }); } - private initBrignaisLayer(): void { - this.map.addLayer( - geoJSON( - { - type: brignais.features[0].geometry.type, - coordinates: brignais.features[0].geometry.coordinates, - } as any, - { style: () => ({ color: '#a00000', fillOpacity: 0, weight: 1 }) } - ) - ); - } - private initMetropoleLayer(): void { this.map.addLayer( geoJSON( diff --git a/src/app/models/contact-message.model.ts b/src/app/models/contact-message.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2b4ea2bec98d096bbcb82f7ef6c4f07e1ccc424 --- /dev/null +++ b/src/app/models/contact-message.model.ts @@ -0,0 +1,9 @@ +export class ContactMessage { + public _id: string = null; + public name: string = null; + public surname: string = null; + public email: string = null; + public phone: string = null; + public subject: string = null; + public message: string = null; +} diff --git a/src/app/post/components/post-card/post-card.component.html b/src/app/post/components/post-card/post-card.component.html index ff06f423aac5debc155bdf5c269e02d9500e6e45..22ebc401e4b4a26874a180af62597c9cf2c30f97 100644 --- a/src/app/post/components/post-card/post-card.component.html +++ b/src/app/post/components/post-card/post-card.component.html @@ -1,34 +1,37 @@ <div fxLayout="column" *ngIf="post" class="post" [ngClass]="class" fxLayoutGap="12px" (click)="showDetails(post)"> - <div fxLayout="column" fxLayoutGap="4px"> - <div - fxLayout="row" - class="tag" - fxLayoutAlign=" center" - fxLayoutGap="12px" - *ngIf="post.tags[0] && post.tags[0].slug != tagEnum.appels" - > - <app-svg-icon - [iconClass]="'icon-32'" - [iconColor]="'inherit'" - [type]="'post'" - [icon]="post.tags[0].slug" - ></app-svg-icon> + <div fxLayout="column" class="imageContainer" *ngIf="post.feature_image"> + <img class="image" alt="image about the news" [src]="post.feature_image" /> + </div> + <div fxLayout="column" class="imageContainer" *ngIf="!post.feature_image"> + <img + *ngIf="isAppelAProjet()" + class="image" + alt="image about appels a projets" + src="../../../../assets/post/aap.png" + /> + <img + *ngIf="!isAppelAProjet()" + class="image" + alt="image about appels a projets" + src="../../../../assets/post/placeholder.png" + /> + </div> + <div fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="center"> + <div fxLayout="row" class="tag" fxLayoutAlign=" center" fxLayoutGap="12px" *ngIf="post.tags[0]"> <span>{{ post.tags[0].name }}</span> </div> - <div fxLayout="row" class="imageContainer" *ngIf="post.feature_image"> - <img class="image" alt="image about the news" [src]="post.feature_image" /> + <div fxLayout="row" class="tag" fxLayoutAlign=" center" fxLayoutGap="12px" *ngIf="!post.tags[0]"> + <span>info</span> </div> + <div fxLayout="row" class="title"> {{ post.title }} </div> - </div> - <div fxLayout="row" class="description"> - {{ post.excerpt }} - </div> - <div fxLayout="column" class="informations"> - <div fxLayout="row"> - {{ post.updated_at | date: 'shortDate' }} + <div fxLayout="column" class="informations"> + <div>{{ post.author }}</div> + <div> + {{ post.updated_at | date: 'd MMM y' }} + </div> </div> - <div fxLayout="row">par {{ post.author }}</div> </div> </div> diff --git a/src/app/post/components/post-card/post-card.component.scss b/src/app/post/components/post-card/post-card.component.scss index 16ed503c9849d0b76bdf76c89f4a66e8110715bb..68ff9e687b92e8a7bd271972ac59470a774c877b 100644 --- a/src/app/post/components/post-card/post-card.component.scss +++ b/src/app/post/components/post-card/post-card.component.scss @@ -5,36 +5,11 @@ .post { cursor: pointer; padding: 16px 0px; - border-bottom: 1px dashed $grey-3; - &.bigNew { - border: 0; - .imageContainer { - .image { - object-fit: cover; - height: 360px; - width: 100%; - @media #{$large-phone} { - height: 147px; - } - } - } - .title { - @media #{$large-phone} { - @include cn-bold-22; - } - @include cn-bold-30; - } - .description { - @include cn-regular-18; - color: $grey-1; - } - } .imageContainer { margin-bottom: 12px !important; + max-width: 600px; .image { object-fit: cover; - max-height: 180px; - max-width: 90%; @media #{$large-phone} { height: 70px; } @@ -51,7 +26,7 @@ @media #{$large-phone} { @include cn-bold-18; } - @include cn-bold-20; + @include cn-bold-22; color: $grey-1; } .description { @@ -68,8 +43,9 @@ } .informations { @include cn-regular-16; - color: $grey-3; - font-style: italic; + div:nth-child(2n) { + color: $grey-3; + } } } .project { diff --git a/src/app/post/components/post-card/post-card.component.ts b/src/app/post/components/post-card/post-card.component.ts index 78970f17fde1248ad7c9604de683217b361ce432..36ce0ea85b5ab0cb5e0bc0a370ba37806d55ca64 100644 --- a/src/app/post/components/post-card/post-card.component.ts +++ b/src/app/post/components/post-card/post-card.component.ts @@ -8,14 +8,17 @@ import { Post } from '../../models/post.model'; templateUrl: './post-card.component.html', styleUrls: ['./post-card.component.scss'], }) -export class PostCardComponent implements OnInit { +export class PostCardComponent { @Input() post: Post; @Input() class: string; public tagEnum = TagEnum; constructor(private router: Router) {} - ngOnInit(): void {} public showDetails(post: Post): void { this.router.navigateByUrl('news/details/' + post.id, { state: { data: post } }); } + + public isAppelAProjet(): boolean { + return this.post.tags[0].slug === this.tagEnum.appels; + } } diff --git a/src/app/post/components/post-details/post-details.component.html b/src/app/post/components/post-details/post-details.component.html index 3de9a5c0406951f7630379e0f7d627b65dcd584b..8877f2920cbe669594c18c3792f2f224f5765598 100644 --- a/src/app/post/components/post-details/post-details.component.html +++ b/src/app/post/components/post-details/post-details.component.html @@ -4,34 +4,33 @@ <svg class="chevronLeft" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> </svg> - <span>Retour à la liste d'actualités</span> + <span>Retour</span> </div> </div> - <div fxLayout="column"> - <div *ngIf="post.tags[0]" fxLayout="row" class="tag" fxLayoutAlign=" center" fxLayoutGap="12px"> - <app-svg-icon - [iconClass]="'icon-32'" - [iconColor]="'inherit'" - [type]="'post'" - [icon]="post.tags[0].slug" - ></app-svg-icon> - <span>{{ post.tags[0].name }}</span> + <div class="gh-canvas"> + <div fxLayout="column" fxLayoutAlign="center none"> + <div *ngIf="post.tags[0]" fxLayout="row" class="tag" fxLayoutAlign=" center" fxLayoutGap="12px"> + <span>{{ post.tags[0].name }}</span> + </div> + <div *ngIf="!post.tags[0]" fxLayout="row" class="tag" fxLayoutAlign=" center" fxLayoutGap="12px"> + <span>Infos</span> + </div> + <div fxLayout="row" class="title"> + {{ post.title }} + </div> + <div fxLayout="column" class="informations"> + <div>{{ post.author }}</div> + <div> + {{ post.updated_at | date: 'dd MMM y' }} + </div> + </div> </div> - <div fxLayout="row" class="title"> - {{ post.title }} - </div> - </div> - <div fxLayout="column" class="informations" *ngIf="post.tags[0] && post.tags[0].slug != 'appels'"> - <div fxLayout="row"> - {{ post.updated_at | date: 'shortDate' }} + <div fxLayout="row" class="article-image" *ngIf="post.feature_image"> + <img class="image" alt="image about the news" [src]="post.feature_image" /> + </div> + <div fxLayout="row" class="description"> + <div [innerHtml]="post.safeHtml"></div> </div> - <div fxLayout="row">par {{ post.author }}</div> - </div> - <div fxLayout="row" class="imageContainer" *ngIf="post.feature_image"> - <img class="image" alt="image about the news" [src]="post.feature_image" /> - </div> - <div fxLayout="row" class="description"> - <div [innerHtml]="post.safeHtml"></div> </div> </div> diff --git a/src/app/post/components/post-details/post-details.component.scss b/src/app/post/components/post-details/post-details.component.scss index a52df2d916b766cf0bfe737122c20f327c63ec78..59262dbe3668b6e74455df14ca504f8982339a12 100644 --- a/src/app/post/components/post-details/post-details.component.scss +++ b/src/app/post/components/post-details/post-details.component.scss @@ -7,7 +7,7 @@ $margin-post: 20px; .postContainer { - max-width: 860px; + max-width: 1200px; margin: $margin-post 0; min-height: calc( var(--vh, 1vh) * 100 - #{$header-height} - #{$footer-height} - #{$header-post-height} - #{$margin-post} * 3 @@ -18,14 +18,6 @@ $margin-post: 20px; stroke: $black; margin-right: 10px; } - .backLink { - cursor: pointer; - color: $grey-2; - @include cn-bold-16; - &:hover { - opacity: 0.4; - } - } } .tag { @include cn-bold-16; @@ -37,25 +29,18 @@ $margin-post: 20px; .title { @include cn-bold-30; color: $grey-1; + margin-bottom: 8px; } .informations { @include cn-regular-16; - color: $grey-3; - font-style: italic; -} -.imageContainer { - .image { - object-fit: cover; - height: 360px; - width: 80%; - @media #{$large-phone} { - height: 147px; - } + div:nth-child(2n) { + color: $grey-3; } } + .description { div { - width: 80%; + max-width: 860px; line-height: 180%; } ::ng-deep hr { @@ -99,10 +84,12 @@ $margin-post: 20px; padding: 0; } ::ng-deep p { - @include cn-regular-16; + @include cn-regular-20; + line-height: 1.6em; } ::ng-deep li { - @include cn-regular-16; + @include cn-regular-20; + line-height: 1.6em; margin-bottom: 10px; } ::ng-deep h2 { @@ -198,3 +185,26 @@ $margin-post: 20px; } } } + +.gh-canvas { + display: grid; + grid-template-columns: + [full-start] minmax(4vmin, auto) [wide-start] minmax(auto, 240px) [main-start] min(720px, calc(100% - 8vw)) + [main-end] minmax(auto, 240px) [wide-end] minmax(4vmin, auto) [full-end]; +} + +.gh-canvas > * { + grid-column: main-start/main-end; +} + +.gh-canvas .article-image { + grid-column: wide-start/wide-end; + width: 100%; + margin: 4vmin 0 0; + img { + display: block; + margin-left: auto; + margin-right: auto; + width: 100%; + } +} diff --git a/src/app/post/components/post-details/post-details.component.ts b/src/app/post/components/post-details/post-details.component.ts index 856e877660f8bbd653b5f18b0e753f5e46a2eb1c..f42eaa73b9cfb7f330ddb98d4954ce2b39d8ef16 100644 --- a/src/app/post/components/post-details/post-details.component.ts +++ b/src/app/post/components/post-details/post-details.component.ts @@ -21,7 +21,6 @@ export class PostDetailsComponent implements OnInit { ) {} ngOnInit(): void { - this.resetScroll(); if (history.state.data) { this.post = new Post(history.state.data); this.post.safeHtml = this.sanitizer.bypassSecurityTrustHtml(this.post.html); @@ -37,10 +36,4 @@ export class PostDetailsComponent implements OnInit { public backToPosts(): void { this.routerListener.goToPreviousUrl(); } - - private resetScroll(): void { - if (window.scrollY) { - window.scroll(0, 0); // reset the scroll position to the top left of the document. - } - } } diff --git a/src/app/post/components/post-header/post-header.component.html b/src/app/post/components/post-header/post-header.component.html index bc6e70a8fe2a9e87fdc3a572313e6f9d94c35160..f9b127809e471441cd4f1f4ad1526a3fc5e0b4df 100644 --- a/src/app/post/components/post-header/post-header.component.html +++ b/src/app/post/components/post-header/post-header.component.html @@ -1,9 +1,12 @@ <div class="header-container"> - <div class="section-container" fxLayout="column" fxLayoutAlign="space-between"> - <h1> - Fil d’actualité - <p class="onlyOnDesktop">du réseau d’inclusion numérique</p> - </h1> + <div class="section-container news" fxLayout="column" fxLayoutAlign="space-between"> + <div class="section-container title" fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="8px"> + <app-svg-icon class="icon" [type]="'ico'" [iconClass]="'icon-80'" [icon]="'news-header'"></app-svg-icon> + <div> + <h1 class="no-margin">Fil d’actualité</h1> + <p class="subtitle no-margin onlyOnDesktop">du réseau d’inclusion numérique</p> + </div> + </div> <div fxLayout="row" fxLayoutGap="5px" fxLayoutAlign="space-between flex-end" class="overflow"> <div fxLayout="row" class="row-mobile"> <div fxLayout="row" fxLayoutAlign="center center" *ngFor="let tag of tags.others"> @@ -17,7 +20,16 @@ > </div> </div> - <div + <div fxLayout="row" class="row-mobile"> + <app-button + class="publish-button hide-on-mobile" + [type]="'button'" + [style]="'buttonWithHash'" + [text]="'Publier votre actu'" + (action)="togglePublishNews()" + ></app-button> + </div> + <!-- <div class="btnSection" fxLayout="row" fxLayoutAlign="space-between center" @@ -74,7 +86,7 @@ (closeEvent)="closeModal()" ></app-post-modal-filters> </div> - </div> + </div> --> </div> </div> </div> diff --git a/src/app/post/components/post-header/post-header.component.scss b/src/app/post/components/post-header/post-header.component.scss index 118fe3ac17c4813485139adad3af6a47085c0d10..333772a3cc7cc86edf24d18466e387268fee2a45 100644 --- a/src/app/post/components/post-header/post-header.component.scss +++ b/src/app/post/components/post-header/post-header.component.scss @@ -5,8 +5,6 @@ @import '../../../../assets/scss/layout'; h1 { - margin-top: 25px; - margin-bottom: 0px; .onlyOnDesktop { margin: 0; } @@ -19,11 +17,19 @@ h1 { } .header-container { - height: #{$header-post-height}; - @media #{$large-phone} { - height: #{$header-post-height-mobile}; - } background: $white; + .title { + margin: 32px 0; + width: 100%; + } + .subtitle { + @include cn-regular-24; + color: $grey-3; + + @media #{$large-phone} { + @include cn-regular-18; + } + } } .section-container { @@ -97,11 +103,14 @@ h1 { .tag-button { padding: 8px 10px; @include cn-regular-16; + color: $grey-3; cursor: pointer; white-space: nowrap; &.active { - background-color: $secondary-color; - color: $white; + @include cn-bold-16; + color: $grey-2; + text-decoration: underline $button-secondary; + text-underline-offset: 3px; } &:focus { outline-color: $secondary-color; @@ -115,7 +124,6 @@ h1 { .row-mobile { @media #{$tablet} { - width: 100%; justify-content: space-between; } } @@ -124,3 +132,7 @@ h1 { overflow-x: auto; } } + +.publish-button { + white-space: nowrap; +} diff --git a/src/app/post/components/post-header/post-header.component.ts b/src/app/post/components/post-header/post-header.component.ts index 1a7ca628d1c9a293626be12dc9d36b7271e21a72..5d1d62907212bb7b01514f4814a6889433c0057d 100644 --- a/src/app/post/components/post-header/post-header.component.ts +++ b/src/app/post/components/post-header/post-header.component.ts @@ -1,11 +1,10 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Tag } from '../../models/tag.model'; import { TagWithMeta } from '../../models/tagWithMeta.model'; import { TypeModalNews } from '../../enum/typeModalNews.enum'; import { ActivatedRoute, Router } from '@angular/router'; import { TagEnum } from '../../enum/tag.enum'; import { parseSlugToTag } from '../utils/NewsUtils'; - @Component({ selector: 'app-post-header', templateUrl: './post-header.component.html', @@ -19,13 +18,13 @@ export class PostHeaderComponent implements OnInit { public checkedPublicTags: Tag[] = []; public checkedLocationTags: Tag[] = []; - constructor(private route: ActivatedRoute, private router: Router) {} ngOnInit(): void { this.route.data.subscribe((data) => { if (data.tags) { this.tags = data.tags; + // this.tags = data.tags.filter((tag) => tag.slug === TagEnum.aLaUne); this.tags.others.forEach((tag) => { if (tag.slug == TagEnum.aLaUne) { tag.name = 'Les + récentes'; @@ -111,11 +110,22 @@ export class PostHeaderComponent implements OnInit { this.router.navigate(['/news'], { relativeTo: this.route, queryParams: { - mainTag: this.mainActiveTag.slug == this.tagEnum.etudes ? this.mainActiveTag.name : this.mainActiveTag.slug, + mainTag: this.getMainTag(), publicTags: this.checkedPublicTags.map((tag) => tag.slug), locationTags: this.checkedLocationTags.map((tag) => tag.slug), }, queryParamsHandling: 'merge', }); } + + public getMainTag(): string { + if (this.mainActiveTag.slug === TagEnum.aLaUne) { + return ''; + } + return this.mainActiveTag.slug == this.tagEnum.etudes ? this.mainActiveTag.name : this.mainActiveTag.slug; + } + + public togglePublishNews(): void { + this.router.navigate(['publish'], { relativeTo: this.route }); + } } diff --git a/src/app/post/components/post-list/post-list.component.html b/src/app/post/components/post-list/post-list.component.html index c5577e7c1021c71bfd98f967ed6b60652b9e25fd..01056ea4fe41fb4ec3421d2dbcc05e297ae26ec5 100644 --- a/src/app/post/components/post-list/post-list.component.html +++ b/src/app/post/components/post-list/post-list.component.html @@ -1,9 +1,9 @@ -<div *ngIf="!isPublishMode" class="section-container" fxLayout="row" fxLayoutGap="32px"> +<div class="section-container no-padding" fxLayout="row" fxLayoutGap="32px"> <div *ngIf="isLoading" class="loader"> <img class="loader-gif" src="/assets/gif/loader_circle.gif" alt /> </div> <div *ngIf="!isLoading" fxLayout="column" class="list-container" fxLayoutGap="16px"> - <div fxLayout="column" *ngIf="displayTags()"> + <!-- <div fxLayout="column" *ngIf="displayTags()"> <div fxLayout="row wrap" fxLayoutAlign="none center" fxLayoutGap="8px"> <div fxLayout="row" @@ -17,86 +17,16 @@ <app-svg-icon [type]="'ico'" [iconColor]="'currentColor'" [icon]="'cancel'"></app-svg-icon> </div> </div> - </div> - <div fxLayout="column" *ngIf="isALaUneTag() && !displayTags()"> - <div fxLayout="row" class="row-border" fxLayoutAlign="space-between center"> - <h2>dernières actualités</h2> - <app-button - [type]="'button'" - [style]="'buttonWithHash'" - [text]="'Publier votre actu'" - (action)="togglePublishNews()" - ></app-button> - </div> - <app-post-card *ngIf="bigNews" [class]="'bigNew'" [post]="bigNews"></app-post-card> - </div> - <div fxLayout="column" fxLayoutAlign=" center" class="project-container mobile"> - <div class="background-project-container"> - <div class="project-content mobile" fxLayout="column"> - <h2>appels à projets</h2> - <div *ngIf="projectsNew.length !== 0"> - <app-post-card - [post]="news" - [class]="'project'" - [ngClass]="{ 'last-child': last }" - *ngFor="let news of projectsNew; let last = last" - ></app-post-card> - </div> - <div *ngIf="projectsNew.length === 0"> - <p>Aucun appels à projet pour le moment.</p> - </div> - </div> - </div> - </div> + </div> --> <div fxLayout="column"> - <div - *ngIf="!isALaUneTag() || displayTags()" - fxLayout="row" - class="row-border" - fxLayoutAlign="space-between center" - > - <h2>{{ getDisplayedTag() }}</h2> - <app-button - [type]="'button'" - [style]="'buttonWithHash'" - [text]="'Publier votre actu'" - (action)="togglePublishNews()" - ></app-button> - </div> - <div *ngIf="leftColumnPosts.length <= 0" fxLayout="column"> + <div *ngIf="allPosts.length <= 0 && !isLoading" fxLayout="column"> <p>Aucun résultat ne correspond à votre recherche.</p> </div> <div fxLayout="row" fxLayoutGap="33px"> - <div fxLayout="column" class="columnPosts"> - <app-post-card [post]="news" *ngFor="let news of leftColumnPosts"></app-post-card> - </div> - <div fxLayout="column" class="columnPosts"> - <app-post-card [post]="news" *ngFor="let news of rightColumnPosts"></app-post-card> - </div> - <div fxLayout="column" class="columnPostsMobile"> - <app-post-card [post]="news" *ngFor="let news of postsMobileView"></app-post-card> - </div> - </div> - </div> - </div> - <div *ngIf="!isLoading" fxLayout="column" fxLayoutAlign=" center" class="project-container desktop"> - <div class="background-project-container"> - <div class="project-content" fxLayout="column"> - <app-svg-icon [iconClass]="'icon-80'" [iconColor]="'inherit'" [type]="'post'" [icon]="'appels'"></app-svg-icon> - <h2>appels à projets</h2> - <div *ngIf="projectsNew.length !== 0"> - <app-post-card - [post]="news" - [class]="'project'" - [ngClass]="{ 'last-child': last }" - *ngFor="let news of projectsNew; let last = last" - ></app-post-card> - </div> - <div *ngIf="projectsNew.length === 0"> - <p>Aucun appels à projet pour le moment.</p> + <div class="posts-container"> + <app-post-card [post]="news" class="col" *ngFor="let news of allPosts"></app-post-card> </div> </div> </div> </div> </div> -<app-post-publish *ngIf="isPublishMode" (closePublish)="togglePublishNews()"></app-post-publish> diff --git a/src/app/post/components/post-list/post-list.component.scss b/src/app/post/components/post-list/post-list.component.scss index 43d3381e4e5a290b96e83a43c57ae0335d204566..805a63eed491779b723932ff421cca726bc6cda3 100644 --- a/src/app/post/components/post-list/post-list.component.scss +++ b/src/app/post/components/post-list/post-list.component.scss @@ -30,7 +30,7 @@ h2 { @media #{$tablet} { width: 100%; } - width: 862px; + h2 { @media #{$large-phone} { @include cn-bold-22; @@ -45,10 +45,138 @@ h2 { } width: 50%; } - .columnPostsMobile { - display: none !important; - @media #{$large-phone} { - display: flex !important; + + @mixin big-container { + grid-column: 1 / span 12; + ::ng-deep .post { + flex-direction: row !important; + .title { + @include cn-regular-32; + } + } + ::ng-deep .post .imageContainer { + margin-right: 36px !important; + margin-bottom: unset !important; + .image { + height: 40vw; + max-height: 320px; + @media #{$news-max} { + height: unset; + max-height: unset; + } + } + } + } + + @mixin small-container { + margin-right: unset; // remove margin from other cotnainers + grid-column: 1 / span 12; + ::ng-deep .post { + flex-direction: column !important; + .title { + @include cn-regular-32; + } + } + ::ng-deep .post .imageContainer { + margin-right: unset !important; + margin-bottom: 36px !important; + } + @media #{$news-max} { + ::ng-deep .post .imageContainer .image { + height: unset; + max-height: 320px; + } + } + } + + @mixin twin-container($start) { + margin-right: 40px; + grid-column: $start / span 6; + ::ng-deep .post .imageContainer .image { + height: 40vw; + max-height: 250px; + } + @media #{$news-max} { + margin-right: unset; + } + } + + @mixin triple-container-first { + margin-right: 40px; + grid-column: 1 / span 4; + ::ng-deep .post .imageContainer .image { + height: 40vw; + max-height: 180px; + } + @media #{$news-max} { + margin-right: unset; + } + } + @mixin triple-container { + margin-right: 40px; + grid-column: span 4; + ::ng-deep .post .imageContainer .image { + height: 40vw; + max-height: 180px; + } + @media #{$news-max} { + margin-right: unset; + } + } + + .posts-container { + display: grid; + padding: 4vmin 0; + grid-template-columns: repeat(auto-fill, minmax(7%, 1fr)); + grid-column-gap: 1%; + grid-row-gap: 40px; + .col:nth-child(6n + 1) { + @include big-container; + @media #{$news-max} { + @include small-container; + } + @media #{$large-phone} { + @include small-container; + } + } + .col:nth-child(6n + 2) { + @include triple-container-first; + @media #{$news-max} { + @include twin-container(1); + } + @media #{$large-phone} { + @include small-container; + } + } + .col:nth-child(6n + 3) { + @include triple-container; + @media #{$news-max} { + @include twin-container(7); + } + @media #{$large-phone} { + @include small-container; + } + } + .col:nth-child(6n + 4) { + @include triple-container; + @media #{$news-max} { + @include small-container; + } + @media #{$large-phone} { + @include small-container; + } + } + .col:nth-child(6n + 5) { + @include twin-container(1); + @media #{$large-phone} { + @include small-container; + } + } + .col:nth-child(6n + 6) { + @include twin-container(7); + @media #{$large-phone} { + @include small-container; + } } } } diff --git a/src/app/post/components/post-list/post-list.component.ts b/src/app/post/components/post-list/post-list.component.ts index 14f617978e24fc07755fc88058d4e9c4958c1097..77a3b17dae26ccf3d7035ecfeeefcfa2836215d4 100644 --- a/src/app/post/components/post-list/post-list.component.ts +++ b/src/app/post/components/post-list/post-list.component.ts @@ -20,11 +20,7 @@ export class PostListComponent implements OnInit { public selectedLocationTagSlug = []; public selectedPublicTagsSlug = []; public filters: Tag[]; - public postsMobileView: Post[] = []; - public leftColumnPosts: Post[] = []; - public rightColumnPosts: Post[] = []; - public projectsNew: Post[] = []; - public bigNews: Post; + public allPosts: Post[] = []; public pagination: Pagination; public isLoading = false; public isPublishMode = false; @@ -47,15 +43,6 @@ export class PostListComponent implements OnInit { ngOnInit(): void { this.isLoading = true; // Init APP news list - this.postService.getPosts(1, [TagEnum.appels]).subscribe((news) => { - let projectNews = news.posts.map((newsData) => this.addAuthorToPost(newsData)); - this.projectsNew = projectNews; - }); - this.postService.getPosts(1, [TagEnum.aLaUne]).subscribe((news) => { - if (news.posts[0]) { - this.bigNews = this.addAuthorToPost(news.posts[0]); - } - }); this.route.queryParams.subscribe((queryParams) => { this.isPublishMode = false; // If main tag is in route, set it @@ -75,14 +62,13 @@ export class PostListComponent implements OnInit { // Init default news list this.postService.getPosts(1).subscribe((news) => { this.setNews(news); + const headLineTag = this.allPosts.find((post: Post) => post.tags.some((tag) => tag.slug === TagEnum.aLaUne)); + this.allPosts.unshift(headLineTag); }); } }); } - public togglePublishNews(): void { - this.isPublishMode = !this.isPublishMode; - } public getPosts(filters?: Tag[]): void { // Parse filter let parsedFilters = null; @@ -91,11 +77,6 @@ export class PostListComponent implements OnInit { return filter.slug; }); - // remove 'a la une' filter - parsedFilters = parsedFilters.filter((item) => { - return item !== TagEnum.aLaUne; - }); - if (parsedFilters.length <= 0) { parsedFilters = null; } @@ -104,6 +85,7 @@ export class PostListComponent implements OnInit { // Reset posts this.resetPosts(); + this.isLoading = true; this.postService.getPosts(1, parsedFilters).subscribe((news) => { this.setNews(news); }); @@ -124,14 +106,10 @@ export class PostListComponent implements OnInit { } public resetPosts(): void { - this.leftColumnPosts = []; - this.rightColumnPosts = []; - this.postsMobileView = []; + this.allPosts = []; } - public publishNews(): void {} - - //Transform excerpt post to have a custom author. + // Transform excerpt post to have a custom author. private addAuthorToPost(post: Post): Post { post.author = post.excerpt; post.excerpt = post.html.replace(/<[^>]*>/g, ''); @@ -150,22 +128,12 @@ export class PostListComponent implements OnInit { // Split news on two columns on desktop mode or one column in mobile mode. private setNews(news: PostWithMeta): void { - if (this.bigNews) { - news.posts = news.posts.filter((elem) => { - return elem.id != this.bigNews.id; - }); - } this.pagination = news.meta.pagination; - const customIndex = this.postsMobileView.length; // For scroll loading, start with previous index. - news.posts.forEach((val, index) => { + const customIndex = this.allPosts.length; // For scroll loading, start with previous index. + this.allPosts = news.posts.map((val, index) => { val = this.addAuthorToPost(val); index += customIndex; - if (index % 2 == 0) { - this.leftColumnPosts.push(val); - } else { - this.rightColumnPosts.push(val); - } - this.postsMobileView.push(val); + return val; }); this.isLoading = false; } diff --git a/src/app/post/components/post-publish/post-publish.component.html b/src/app/post/components/post-publish/post-publish.component.html index 36d4d13b0b2d21afaba645b7f200ee2daa49e8b0..e18d6cfc7fa614a1a2205e2328255530975cc7d9 100644 --- a/src/app/post/components/post-publish/post-publish.component.html +++ b/src/app/post/components/post-publish/post-publish.component.html @@ -4,10 +4,10 @@ <svg class="chevronLeft" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> </svg> - <span>Retour à la liste d'actualités</span> + <span>Retour</span> </div> </div> - <div fxLayout="column" class="content" fxLayoutGap="16px"> + <div fxLayout="column" class="content" fxLayoutGap="16px" fxLayoutAlign="center center"> <h2>Publier votre actualité</h2> <div class="image"> <svg aria-hidden="true"> diff --git a/src/app/post/components/post-publish/post-publish.component.scss b/src/app/post/components/post-publish/post-publish.component.scss index 1868e8d4a32642747876d0497836cb35a85987e2..79d50ca95648ba700fc446796bfd4fb3666900aa 100644 --- a/src/app/post/components/post-publish/post-publish.component.scss +++ b/src/app/post/components/post-publish/post-publish.component.scss @@ -17,14 +17,7 @@ $margin-post: 20px; stroke: $black; margin-right: 10px; } -.backLink { - cursor: pointer; - color: $grey-2; - @include cn-bold-16; - &:hover { - opacity: 0.4; - } -} + .content { color: $grey-1; h2 { diff --git a/src/app/post/components/post-publish/post-publish.component.ts b/src/app/post/components/post-publish/post-publish.component.ts index 061bc66988fdb53f99e288c6f559dc7ecd04bfe6..ce704b0e93fd0c10ea1fd4b0eebe2e1016bc62df 100644 --- a/src/app/post/components/post-publish/post-publish.component.ts +++ b/src/app/post/components/post-publish/post-publish.component.ts @@ -1,18 +1,19 @@ -import { Component, EventEmitter, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Output } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; @Component({ selector: 'app-post-publish', templateUrl: './post-publish.component.html', styleUrls: ['./post-publish.component.scss'], }) -export class PostPublishComponent implements OnInit { +export class PostPublishComponent { @Output() closePublish = new EventEmitter<boolean>(); - constructor() {} + constructor(private route: ActivatedRoute, private router: Router) {} public bodyMail = "Bonjour,%0D Je souhaite ajouter cette publication sur Rés'in :%0D- Titre :%0D- Texte :%0D- Auteur :%0D- Image : à joindre en pièce jointe"; - ngOnInit(): void {} public backToPosts(): void { this.closePublish.emit(true); + this.router.navigate(['items'], { relativeTo: this.route }); } } diff --git a/src/app/post/news.component.html b/src/app/post/news.component.html index 34a02820651599afa85d024b6bdffe8dcc3591c4..06566da7fdb831b20c78fe7265b780c2bb2df46b 100644 --- a/src/app/post/news.component.html +++ b/src/app/post/news.component.html @@ -1,4 +1,4 @@ <app-post-header (filterTags)="setFilters($event)"></app-post-header> -<div class="section-container"> +<div class="section-container no-padding news"> <router-outlet></router-outlet> </div> diff --git a/src/app/post/post-routing.module.ts b/src/app/post/post-routing.module.ts index c95c0ba7cbea2c96e03ae08ecea77819b367b348..75eae7cad093f042fc4a7919d8e6159299c81d79 100644 --- a/src/app/post/post-routing.module.ts +++ b/src/app/post/post-routing.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { PostDetailsComponent } from './components/post-details/post-details.component'; import { PostListComponent } from './components/post-list/post-list.component'; +import { PostPublishComponent } from './components/post-publish/post-publish.component'; import { NewsComponent } from './news.component'; import { TagResolver } from './resolvers/tags.resolver'; @@ -21,6 +22,10 @@ const routes: Routes = [ path: 'details/:id', component: PostDetailsComponent, }, + { + path: 'publish', + component: PostPublishComponent, + }, ], }, ]; diff --git a/src/app/post/services/post.service.ts b/src/app/post/services/post.service.ts index 70423382e57f4f42a0fb51173fcd98b0624732a2..89020397d8274a59afb75c72d212dd211ba20f0d 100644 --- a/src/app/post/services/post.service.ts +++ b/src/app/post/services/post.service.ts @@ -3,7 +3,6 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { Post } from '../models/post.model'; -import { TagEnum } from '../enum/tag.enum'; import { PostWithMeta } from '../models/postWithMeta.model'; import { TagWithMeta } from '../models/tagWithMeta.model'; @@ -26,7 +25,7 @@ export class PostService { public getPosts(page: number, tags?: string[]): Observable<PostWithMeta> { if (!tags) { return this.http - .get<PostWithMeta>(`${this.baseUrl}?page=${page}&include=tags,authors&filter=tag:-[${TagEnum.appels}]`) + .get<PostWithMeta>(`${this.baseUrl}?page=${page}&include=tags,authors`) .pipe(map((item: PostWithMeta) => new PostWithMeta(item))); } let tagsString = ''; diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index ba2da5e7537e6731cc2fa12201f839bb605d4d40..3b19a0dbdc8521e89b625bf0ace2bdef3695589d 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -46,6 +46,14 @@ export class AuthService { return `${this.userValue.name}`; } + public getUsersurnameDisplay(): string { + return `${this.userValue.surname}`; + } + + public getUserEmailDisplay(): string { + return `${this.userValue.username}`; + } + private getExpiration(): DateTime { return DateTime.fromISO(this.userValue.expiresAt, { zone: 'Europe/Paris' }); } diff --git a/src/app/services/contact.service.ts b/src/app/services/contact.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..8fb8cd52356b225cd3e7f564951281890600f50a --- /dev/null +++ b/src/app/services/contact.service.ts @@ -0,0 +1,15 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { ContactMessage } from '../models/contact-message.model'; + +@Injectable({ + providedIn: 'root', +}) +export class ContactService { + constructor(private http: HttpClient) {} + + public sendMessage(contactMessage: ContactMessage): Observable<any> { + return this.http.post('/api/contact/message', { contactMessage }); + } +} diff --git a/src/app/services/geojson.service.ts b/src/app/services/geojson.service.ts index 5b4670c864e51c7923f17c4b817357cef3bf39c1..b598e926a79fc6c9d463dbdaa8cbd401a32859a9 100644 --- a/src/app/services/geojson.service.ts +++ b/src/app/services/geojson.service.ts @@ -49,7 +49,7 @@ export class GeojsonService { */ public getCoord(numero: string, address: string, zipcode: string): Observable<GeoJson> { return this.http - .get('/geocoding/photon-bal/api' + '?q=' + numero + ' ' + address + ' ' + zipcode, { headers: { skip: 'true' } }) + .get('/geocoding/photon/api' + '?q=' + numero + ' ' + address + ' ' + zipcode, { headers: { skip: 'true' } }) .pipe(map((data: { features: any[]; type: string }) => new GeoJson(data.features[0]))); } diff --git a/src/app/services/notification.service.spec.ts b/src/app/services/notification.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4f2cd67eeb877c523ad095e23448e51603286ce --- /dev/null +++ b/src/app/services/notification.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { NotificationService } from './notification.service'; + +describe('NotificationService', () => { + let service: NotificationService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(NotificationService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..998656c9c7cb6385906eb6b8ca743e9068086469 --- /dev/null +++ b/src/app/services/notification.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { ToastrService } from 'ngx-toastr'; + +@Injectable({ + providedIn: 'root', +}) +export class NotificationService { + constructor(private toastr: ToastrService) {} + + showSuccess(message: string, title: string, timespan: number = 10000): void { + this.toastr.success(message, title, { + timeOut: timespan, + }); + } + + // Par defaut, l'erreur reste affichée jusqu'à ce qu'on clique dessus + showError(message: string, title: string, timespan: number = 0): void { + this.toastr.error(message, title, { + timeOut: timespan, + disableTimeOut: timespan ? false : true, + }); + } +} diff --git a/src/app/shared/components/address-autocomplete/address-autocomplete.component.html b/src/app/shared/components/address-autocomplete/address-autocomplete.component.html index 82ccf794dea1b2e93517baf7a60ba8b7e6231b11..c107fc91692ae4c90630a890410fad8331c0adf2 100644 --- a/src/app/shared/components/address-autocomplete/address-autocomplete.component.html +++ b/src/app/shared/components/address-autocomplete/address-autocomplete.component.html @@ -12,7 +12,7 @@ </div> <div class="autocomplete-items" *ngIf="!isAlreadySearching"> <p *ngFor="let hit of data" (click)="selectedResult(hit)" class="autocomplete-item"> - {{ parseHitToAddress(hit) }} + {{ hit.displayedName }} </p> </div> </div> diff --git a/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts b/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts index 8670ecc1f3133fb1ce73723f9b0319f14d7fab13..10f2efed1de1e07cdf8e838ab8bac1ba735fb01a 100644 --- a/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts +++ b/src/app/shared/components/address-autocomplete/address-autocomplete.component.ts @@ -19,18 +19,14 @@ export class AddressAutocompleteComponent implements OnInit { constructor(private addressService: AddressService) {} ngOnInit(): void { - if (this.address) { - let address_str = null; - if (this.address.numero) { - address_str = this.address.numero + ' ' + this.address.street + ' ' + this.address.commune; - } else { - address_str = this.address.street + ' ' + this.address.commune; - } - this.searchAddress.nativeElement.value = address_str; - } + this.lauchSearch(); } ngOnChanges(): void { + this.lauchSearch(); + } + + public lauchSearch(): void { if (this.address) { let address_str = null; if (this.address.numero) { @@ -46,7 +42,12 @@ export class AddressAutocompleteComponent implements OnInit { if (!this.isAlreadySearching) { this.isAlreadySearching = true; this.addressService.searchAddress(searchString).subscribe((data) => { - this.data = data.features; + data.features = data.features.map((el) => { + el.displayedName = this.parseHitToAddress(el); + return el; + }); + // Filtering duplicate displayed string. This duplication is caused by the API used for gathering addresse info. + this.data = [...new Map(data.features.map((item) => [item['displayedName'], item])).values()]; this.isAlreadySearching = false; }); } @@ -67,10 +68,17 @@ export class AddressAutocompleteComponent implements OnInit { this.selectedAddress.emit(address); } - public parseHitToAddress(hit: any): string { + private parseHitToAddress(hit: any): string { + let parsedAddress = ''; if (hit.properties.housenumber) { - return `${hit.properties.housenumber} ${hit.properties.street} ${hit.properties.city}`; + parsedAddress += `${hit.properties.housenumber} `; + } + if (hit.properties.street) { + parsedAddress += `${hit.properties.street}, `; + } else { + parsedAddress += `${hit.properties.name}, `; } - return `${hit.properties.street} ${hit.properties.city}`; + parsedAddress += `${hit.properties.city}`; + return parsedAddress; } } diff --git a/src/app/shared/components/svg-icon/svg-icon.component.scss b/src/app/shared/components/svg-icon/svg-icon.component.scss index 51458d05e5c669593064271c8b2756a6850c87d3..bed7e3e0d741f9d08735c2df410fbda5c61eff4c 100644 --- a/src/app/shared/components/svg-icon/svg-icon.component.scss +++ b/src/app/shared/components/svg-icon/svg-icon.component.scss @@ -3,6 +3,10 @@ display: inline-block; height: 2em; width: 1.5em; + &.icon-full { + width: unset; + height: unset; + } &.icon-28 { width: 28px; height: 28px; diff --git a/src/app/structure-list/components/structure-details/structure-details.component.html b/src/app/structure-list/components/structure-details/structure-details.component.html index 3f043a99d471d8d93e5c9c6bc2e54071d66ea18b..3e30ac6647185550e8d3aff85e822e5d7a4e14e6 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.html +++ b/src/app/structure-list/components/structure-details/structure-details.component.html @@ -1,13 +1,15 @@ <div class="structrue-details-container" *ngIf="structure && !isLoading"> <!-- Header info --> - <div fxLayout="row" fxLayoutAlign="end center"> - <div (click)="close()" class="ico-close-details"></div> + <div fxLayout="space-between wrap-reverse" class="sticky-title"> + <div fxLayout="column" class="no-margin" fxLayoutAlign="end end"> + <h2 class="bold">{{ structure.structureName }}</h2> + </div> + <div fxLayout="column" fxLayoutAlign="end start" class="ico-close"> + <div (click)="close()" class="ico-close-details"></div> + </div> </div> <div fxLayout="row" class="structure-details-block" fxLayoutAlign="baseline baseline" fxLayoutGap="8px"> <div fxLayout="column" fxLayoutGap="10px" fxFlex="100%"> - <div fxLayout="column" class="no-margin" fxLayoutAlign="space-between start"> - <h2 class="bold">{{ structure.structureName }}</h2> - </div> <div fxLayout="row" fxLayoutAlign="space-between center"> <div class="typeInformationHeader" fxLayout="column"> <h3>{{ structure.getLabelTypeStructure() }}</h3> diff --git a/src/app/structure-list/components/structure-details/structure-details.component.scss b/src/app/structure-list/components/structure-details/structure-details.component.scss index eeeb766c4ac0994aaa242314cff65085e6c8e8f6..de1fd0ab8c32a1deac7791a88c6ede405516a3dc 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.scss +++ b/src/app/structure-list/components/structure-details/structure-details.component.scss @@ -18,13 +18,14 @@ a { left: 0; max-width: 980px; width: 100%; - height: calc(100vh - #{$header-height} - #{$footer-height} - 20px); - padding: 10px 24px; + height: calc(100vh - #{$header-height} - #{$footer-height}); + padding: 0px 24px; overflow: auto; @media #{$tablet} { width: calc(100% - 2 * 24px); position: inherit; height: 100%; + overflow: unset; .printButton { display: none !important; } @@ -50,7 +51,7 @@ a { } .structure-details-block:last-child { - border-bottom: none; + border-bottom: none !important; } .opening-time { @@ -131,3 +132,30 @@ p, gap: 20px 30px; grid-template-columns: 1fr 1fr; } +.ico-close { + margin-left: auto; + padding-top: 10px; +} +.sticky-title { + position: sticky; + top: 0px; + max-width: 980px; + width: 100%; + background-color: $white; + height: 60px; + z-index: 1; + border-bottom: solid 1px $grey-4; + padding-bottom: 2px; + @media #{$tablet} { + height: auto; + } + @media #{$small-phone} { + width: 111%; + padding-right: 20px; + } + h2 { + @media #{$large-phone} { + font-size: $font-size-medium; + } + } +} diff --git a/src/app/utils/utils.ts b/src/app/utils/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..e37ab99dd002bdcbe9cc54dd725e2f88e14349c3 --- /dev/null +++ b/src/app/utils/utils.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +@Injectable({ + providedIn: 'root', +}) +export class Utils { + public modifyPhoneInput(form: FormGroup, controlName: string, phoneNumber: string): void { + // Take length of phone number without spaces. + const phoneNoSpace = phoneNumber.replace(/\s/g, ''); + // Check to refresh every 2 number. + if (phoneNoSpace.length % 2 === 0) { + // Add space every 2 number + form.get(controlName).setValue(phoneNoSpace.replace(/(?!^)(?=(?:\d{2})+$)/g, ' ')); //NOSONAR + } + } +} diff --git a/src/assets/geojson/brignais.json b/src/assets/geojson/brignais.json deleted file mode 100644 index 1ee59c4ca408dc5f077d5d347957955838c0bd6f..0000000000000000000000000000000000000000 --- a/src/assets/geojson/brignais.json +++ /dev/null @@ -1,256 +0,0 @@ -{ - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": { - "type": "Polygon", - "coordinates": [ - [ - [4.725423572838992, 45.67523492540029], - [4.725556205086396, 45.67536799809434], - [4.72593053033344, 45.67665926826292], - [4.725897408725562, 45.67724523002701], - [4.724498364095213, 45.67894195987662], - [4.724136912235676, 45.67971308733658], - [4.724174998822853, 45.680523132991055], - [4.724394519781781, 45.680970121651505], - [4.7253742587434235, 45.684368766945326], - [4.726012834038375, 45.68589017252987], - [4.728784560500518, 45.687405878667384], - [4.72848312066644, 45.68804099459657], - [4.728627880181792, 45.68979513877095], - [4.728809686695279, 45.691089357368675], - [4.7291322400729285, 45.69112944192117], - [4.729902992424156, 45.69111760886067], - [4.730600785997002, 45.690827674081646], - [4.730837815634237, 45.69059885727743], - [4.731204878749306, 45.69000776130103], - [4.73139897139636, 45.69004981338818], - [4.731662645758953, 45.690261928769566], - [4.732023405137734, 45.69111204879683], - [4.732488514263196, 45.69160028260697], - [4.732940942926815, 45.691683394012664], - [4.734099621090595, 45.691746627435876], - [4.73434948614329, 45.69151760548787], - [4.734075638062059, 45.69098140009308], - [4.7343768828349795, 45.69075158713616], - [4.734521008493132, 45.690839438290624], - [4.734719058126453, 45.6910075220864], - [4.73505121379295, 45.691353680573016], - [4.735606549566667, 45.691029880343955], - [4.736235995079525, 45.69102018090333], - [4.737120505040019, 45.690538179796015], - [4.738317135484041, 45.69058277024951], - [4.739604079560944, 45.69022964404202], - [4.740797406330327, 45.689760858170914], - [4.741113249907205, 45.6904044834425], - [4.74169253500404, 45.691251197126086], - [4.743756539358913, 45.69067884477155], - [4.745787361237172, 45.69027810209865], - [4.74597720467782, 45.69018508940489], - [4.7459672618832425, 45.689869996869504], - [4.746732024821265, 45.68966899316945], - [4.7468675851401905, 45.68989206732954], - [4.747075902224951, 45.6903842244931], - [4.748746972517031, 45.690394324128704], - [4.748913884913081, 45.69120236785876], - [4.749881307355184, 45.691313443446006], - [4.750266682621687, 45.69130745660553], - [4.75034087972368, 45.69162155085391], - [4.750980041352062, 45.69151254047945], - [4.750833350277468, 45.6909383685811], - [4.750960383323937, 45.690891358869365], - [4.752247522979393, 45.69095240896389], - [4.7520834134073064, 45.69023439647343], - [4.753175002560322, 45.6902084063704], - [4.75319928549635, 45.68935235728534], - [4.754283721018561, 45.689101290815316], - [4.754480682609426, 45.68923332935833], - [4.75465195760291, 45.6901764038525], - [4.755291379643046, 45.690076371654506], - [4.755418406923607, 45.69002935691955], - [4.755388424697647, 45.68948939989777], - [4.756477139743565, 45.689373351606974], - [4.756255937661885, 45.690097366294154], - [4.755758685048976, 45.690627527691774], - [4.7568813848931875, 45.690366832941145], - [4.757649546809886, 45.690273786526994], - [4.757752907713235, 45.69069550639201], - [4.758773120845915, 45.690445398852184], - [4.758964374007231, 45.690397377334136], - [4.759429714788022, 45.69088549987699], - [4.759622400423712, 45.69088249029222], - [4.760566922030581, 45.69027326534666], - [4.760890926169576, 45.69035827201069], - [4.761283457897996, 45.69057731264884], - [4.7620467414810825, 45.690331191963], - [4.762730363945926, 45.690005248751916], - [4.764257182723493, 45.68952198522801], - [4.765205780326249, 45.688237124358565], - [4.768167149814534, 45.68881216982346], - [4.770277909252294, 45.68931043794418], - [4.770730713369078, 45.688600766684736], - [4.771480342785376, 45.68873309432069], - [4.77193136502855, 45.68877103624345], - [4.772205568931809, 45.68930714947986], - [4.772463628393492, 45.6893391178], - [4.772913213953938, 45.6893320429134], - [4.773703568347919, 45.688328818358265], - [4.7746336027736085, 45.68767466501034], - [4.774950393746933, 45.687534566990124], - [4.774193555047427, 45.68637556484896], - [4.774691747769191, 45.68588133254685], - [4.7748034452827195, 45.68535715987414], - [4.773808079621646, 45.685174682308215], - [4.773805051924615, 45.68467933870828], - [4.773956010522631, 45.683776249949695], - [4.774792025710137, 45.683799108658405], - [4.774636306084858, 45.68335120641176], - [4.775605376060246, 45.68351607645404], - [4.775926185622656, 45.683502011630814], - [4.776104397271003, 45.6830488457035], - [4.776486822059296, 45.682952743560215], - [4.7778351319935375, 45.68292246280935], - [4.778737091105642, 45.68299829393771], - [4.778906775391038, 45.68268036475559], - [4.779160466215576, 45.6825772793951], - [4.779351674493231, 45.68252922330023], - [4.780010484689922, 45.68143795840417], - [4.779189696950867, 45.68028900776877], - [4.779048241725939, 45.67988592162209], - [4.779862670961972, 45.679638868976355], - [4.779908065588893, 45.67905268841031], - [4.780027807598072, 45.67878058222422], - [4.779218635843911, 45.677991733987284], - [4.779207058904248, 45.6776316319511], - [4.779750128780947, 45.676938509964614], - [4.779832415604619, 45.67590139035096], - [4.779538415080308, 45.675149437991905], - [4.779854832445264, 45.67500032376799], - [4.781560873442728, 45.674513984782806], - [4.781547833073028, 45.67410887037405], - [4.781236435009352, 45.672816770058226], - [4.779990236096869, 45.67281846013053], - [4.779668356221317, 45.671598578939054], - [4.779206942552515, 45.67163289129457], - [4.778745750915616, 45.67087457192298], - [4.7782528164999505, 45.669927602756324], - [4.777540551502218, 45.67055133186952], - [4.775529743361716, 45.6715468149248], - [4.775274363821313, 45.671595876680605], - [4.7751808286750155, 45.66948068088587], - [4.775048091273325, 45.669347666713165], - [4.774662871571942, 45.66935373830436], - [4.773637349458038, 45.66942393812807], - [4.773185041670769, 45.66934099029003], - [4.772406241968541, 45.66909204510558], - [4.772337715040516, 45.66895801718882], - [4.771868249713578, 45.668740227731874], - [4.771349154747806, 45.66857725966596], - [4.771175149240492, 45.66876013863497], - [4.770519195438096, 45.669941376444186], - [4.770328022971875, 45.66998941749999], - [4.76966047647199, 45.66960359791103], - [4.76842076011509, 45.66900158424274], - [4.76777441727613, 45.668876626441644], - [4.766430753412082, 45.669041825409025], - [4.765847181522396, 45.66887083651429], - [4.76512544693445, 45.66839576972117], - [4.764905647005159, 45.66794886093501], - [4.765083898794231, 45.667495712998836], - [4.765885284015383, 45.66684364558067], - [4.7662974300263965, 45.666071579427125], - [4.766158977366456, 45.66575850330744], - [4.766337211845127, 45.66530535346818], - [4.765471412252013, 45.66474247875827], - [4.765278817909219, 45.66474549833979], - [4.763831164052034, 45.66527258093708], - [4.762811437637273, 45.6655227337891], - [4.761523158650894, 45.665407788976516], - [4.761145215259903, 45.66402661250905], - [4.760952623171668, 45.66402962469188], - [4.758959929173959, 45.66397970796059], - [4.75916954801526, 45.66289558578311], - [4.759136683116868, 45.66226560499678], - [4.7586861715644595, 45.66223661452678], - [4.758677591471066, 45.661966536766236], - [4.758773121288069, 45.66133455043102], - [4.759075212500354, 45.66073536520345], - [4.7593575921304385, 45.659920317686144], - [4.7585847066096605, 45.659851328893375], - [4.756455211153323, 45.659533295372896], - [4.7563068481391095, 45.65931043355558], - [4.754567101508066, 45.65913039705785], - [4.75154780564835, 45.659105352958626], - [4.751469373071419, 45.65865622119097], - [4.750488825819278, 45.65851835130557], - [4.748934830133477, 45.658524487946195], - [4.747950264726445, 45.65866587642288], - [4.747970682982501, 45.659719381930906], - [4.747696712339127, 45.66039916230322], - [4.747920607095225, 45.66098114366396], - [4.747825806721737, 45.66164012862579], - [4.747259471385523, 45.66160388350693], - [4.746623999017551, 45.66222622116816], - [4.746056967942222, 45.66298259915003], - [4.745567023045599, 45.66333246283877], - [4.745690682366697, 45.66440238199589], - [4.745562286214036, 45.66440437253454], - [4.744977692531974, 45.66419726506753], - [4.744525470411494, 45.66411420217368], - [4.743614191503186, 45.66536227946326], - [4.744227592544672, 45.665668025185646], - [4.743622256781896, 45.66643399042955], - [4.743856781825376, 45.66653844292826], - [4.743574140240937, 45.66735345200182], - [4.743134085012219, 45.667657496767916], - [4.742725855794389, 45.668564517774435], - [4.742391735935124, 45.669785636321016], - [4.742054767883991, 45.67091672759077], - [4.741343123760939, 45.670756600745676], - [4.741032007343306, 45.67107665693612], - [4.740987721924457, 45.67130251712942], - [4.741841950343499, 45.67191079217109], - [4.741977447470289, 45.672133871909935], - [4.741549337153295, 45.672410704036544], - [4.740783378089855, 45.67256665906743], - [4.739619434613251, 45.67232344151881], - [4.738656323818675, 45.672338315084986], - [4.737700561249222, 45.672587249382026], - [4.736867273381311, 45.6726451393355], - [4.736286861425061, 45.67257302643793], - [4.734556355669604, 45.672698770900475], - [4.7342339057562945, 45.67265870193287], - [4.7331398347676, 45.6725944818514], - [4.732757406343585, 45.67269043675427], - [4.731731769421744, 45.672760255017], - [4.731233762765472, 45.67285798191712], - [4.729887079598486, 45.672932718252156], - [4.729247809710637, 45.67303260613895], - [4.727912361852489, 45.67346742583567], - [4.726629868391131, 45.673541138039994], - [4.726138864959061, 45.67386390944757], - [4.725602171277048, 45.67478183974645], - [4.725423572838992, 45.67523492540029] - ] - ] - }, - "properties": { - "code_epci": "246900757", - "insee_reg": 84, - "insee_arr": "1", - "nom_epci": "CC de la Vall\u00e9e du Garon (Ccvg)", - "statut": "Commune simple", - "nom_reg": "AUVERGNE-RHONE-ALPES", - "geo_point_2d": [45.6778041264, 4.75264949198], - "insee_com": "69027", - "nom_com": "Brignais", - "id": "BDCSURCO0000000009605506", - "nom_dep": "RHONE", - "insee_dep": "69", - "population": 11381 - } - } - ] -} diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index c86bb8603c610a3b47d673534f2eff2a86e9cdf4..e7b918e0ff25552775f070c554ab7edc1a0ed435 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -247,6 +247,16 @@ <path d="M16.9696 9.5759C18.0037 8.95863 18.6962 7.82861 18.6962 6.53684C18.6962 4.5835 17.1127 3 15.1593 3C13.206 3 11.6225 4.5835 11.6225 6.53684C11.6225 7.82861 12.315 8.95863 13.349 9.5759C11.81 10.2675 10.7383 11.8139 10.7383 13.6105V15.1579H19.5804V13.6105C19.5804 11.8139 18.5087 10.2675 16.9696 9.5759Z" stroke="none"/> </symbol> +<symbol id="news-header" viewBox="0 0 89 88" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M63.5 72H27.0391C29.1476 70.3259 30.5 67.7505 30.5 64.8571V17H70.5V65C70.5 68.866 67.3659 72 63.5 72Z" stroke="#828282" stroke-width="2"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M36.5 52C36.5 51.4477 36.9477 51 37.5 51H63.5C64.0523 51 64.5 51.4477 64.5 52C64.5 52.5523 64.0523 53 63.5 53H37.5C36.9477 53 36.5 52.5523 36.5 52ZM36.5 58C36.5 57.4477 36.9477 57 37.5 57H63.5C64.0523 57 64.5 57.4477 64.5 58C64.5 58.5523 64.0523 59 63.5 59H37.5C36.9477 59 36.5 58.5523 36.5 58ZM37.5 63C36.9477 63 36.5 63.4477 36.5 64C36.5 64.5523 36.9477 65 37.5 65H52.5C53.0523 65 53.5 64.5523 53.5 64C53.5 63.4477 53.0523 63 52.5 63H37.5Z" fill="#828282"/> +<path d="M30.5 72C27 72 27 72 23 72C18.8055 71.702 15.5 68.3806 15.5 64.327V55H30" stroke="#828282" stroke-width="2"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M15.9211 67.663C16.1618 68.4235 16.5012 69.1312 16.9283 69.7667L29.5 57.195V55L28.5841 55L15.9211 67.663ZM20.9667 72.7284C20.1942 72.5073 19.4861 72.1679 18.8539 71.7302L29.5 61.0841V64.195L20.9667 72.7284ZM25.4046 72.1795C26.7069 71.4951 27.7523 70.4308 28.4608 69.1232L25.4046 72.1795ZM24.695 55L21.5841 55L15.5 61.0841V64.195L24.695 55ZM15.5 55L17.695 55L15.5 57.195V55Z" fill="#828282"/> +<rect x="34.5" y="24" width="32" height="20" rx="2" fill="#BDBDBD"/> +</symbol> + + + <symbol id="calendar" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> <path d="M8 10H5V13H8V10Z" fill="#333333"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M6 2C5.44772 2 5 2.44772 5 3V4H3C2.44772 4 2 4.44772 2 5V19C2 19.5523 2.44772 20 3 20H19C19.5523 20 20 19.5523 20 19V5C20 4.44772 19.5523 4 19 4H17V3C17 2.44772 16.5523 2 16 2C15.4477 2 15 2.44772 15 3V4H7V3C7 2.44772 6.55229 2 6 2ZM4 9V18H18V9H4Z" fill="#333333"/> diff --git a/src/assets/post/aap.png b/src/assets/post/aap.png new file mode 100644 index 0000000000000000000000000000000000000000..7ab7602ddaf21ff14857f119b921e6f15704bb6a Binary files /dev/null and b/src/assets/post/aap.png differ diff --git a/src/assets/post/placeholder.png b/src/assets/post/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..fb86cc23f8b92246c7d2bb91a479b8b093a0226e Binary files /dev/null and b/src/assets/post/placeholder.png differ diff --git a/src/assets/post/sprite.svg b/src/assets/post/sprite.svg index 945e0fda5c0373d3e0c804b87b4fd42a59110c68..bbaa197c5e06c4e4681c81a89534688604b8eba7 100644 --- a/src/assets/post/sprite.svg +++ b/src/assets/post/sprite.svg @@ -1,5 +1,8 @@ <svg xmlns="http://www.w3.org/2000/svg"> + +<symbol id="aap" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 322 180"><path fill="#fff" d="M0 0h322v180H0z"/><path d="m0 0 83.5.5L108 84l7-12.5L147.5 180H0V0Z" fill="#348899"/><path d="M29.5 107.8c2.9-.8 6.4-2.5 10.3-4.6A154.8 154.8 0 0 0 77 74.5v.1l.1.1V75l.1.1V75.5l.1.1V75.9l.1.1V76.2l.1.1v.4h.1v.4l.1.1v.2l.1.2V78l.1.1V78.3l.1.1v.2l.1.2v.4h.1v.4h.1V80l.1.1V80.3l.1.1v.4l.1.1V81.2l.1.1V81.6l.1.1V82l.1.2v.2l.1.2v.2h.1v.4l.1.1V83.6l.1.1v.4h.1v.4h.1v.4l.1.1v.2l.1.2v.2l.1.2V86.1l.1.1v.2l.1.2V87l.1.1V87.3l.1.2v.2l.1.1v.4h.1v.4l.1.1V89h.1v.4l.1.1v.2l.1.2V90.2l.1.2v.2l.1.1v.4h.1V91.5l.1.1v.4l.1.1v.2l.1.2V92.8l.1.1v.2l.1.2v.2l.1.2V94l.1.1v.2l.1.2v.4h.1V95.2l.1.2v.2l.1.1V96l.1.2v.2l.1.2V96.9l.1.2v.2l.1.1V97.6h.1v.5h.1V98.5l.1.1v.2l.1.2V99.2l.1.2v.2l.1.2v.2l.1.1v.4l.1.1v.2l.1.2v.2l.1.2V101.8l.1.2v.2l.1.1V102.5h.1v.5h.1V103.4l.1.2v.2l.1.1V104.1l.1.2V104.6h.1V105l.1.1v.4l.1.1v.2l.1.2v.4h.1V106.7l.1.1v.2l.1.2v.4h.1V107.9l.1.2V108.4l.1.1v.2l.1.2v.2l.1.2V109.6l.1.1V110h.1v.4l.1.2v.2l.1.1v.4h.1V111.6l.1.1v.4l.1.1v.2l.1.2V112.9l.1.1v.2l.1.2V113.7l.1.1V114l.1.2v.2l.1.1v.4h.1V115.3l.1.1V115.6l.1.2v.2l.1.2v.2l.1.1V116.8l.1.2V117.4l.1.1V117.7l.1.1V118.1l.1.2v.2h.1v.4l.1.1V119.3l.1.1V119.7l.1.1v.4h.1v.4h.1v.4h.1v.4h.1v.4l.1.2v.2l.1.1V122.6l.1.1V123l.1.1V123.4l.1.2v.2l.1.2V124.3l.1.1V124.7h.1v.4l.1.1V125.5a179.1 179.1 0 0 0-56.6-4l-3.3-13.7Z" stroke="#fff" stroke-width="2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M77.6 72.4C66.3 85.4 40 104.2 28.3 107l3.8 15.6c2.4-.6 7.4-1 11.3-1.1L41 136a5.4 5.4 0 1 0 10.9-.8l-.5-14-.6-1.5-.2-.4 14-34.3 3.9-1-15.3 37.5c13.1.6 28.2 2.6 37.6 5.3l-6.6-27-9.1 22.2-4 1 10.6-26 2.4 2.7-4-16.5-15.6 36.5-3.9 1 17.3-41 2 2.3-2.4-9.7ZM51.4 97.2l-4 1-5.3 14.7 1.4 5.8 7.9-21.5Z" fill="#fff"/><path d="m157.3 77.1-1.5-3.8-7 1.7-1.3 4.5-3.6 1 7-22.7 1.2-.3 8.8 18.8-3.6.8ZM152 63.8l-2.2 8.3 5-1.3-2.8-7Zm13.7 3.8.5 7.4-3.6.9-1-20.3 4.1-1.4c5.2-1.2 8.2 0 9.1 4 .6 2.1.2 4-1 5.7a9.7 9.7 0 0 1-5.7 3.3l-2.4.4Zm-.6-9.8.4 6.7c.5 0 1.1 0 1.8-.3 1.5-.3 2.6-1 3.4-1.8.7-.9 1-1.9.6-3.1-.5-2-1.9-2.8-4.2-2.2-.6.1-1.3.4-2 .7Zm16.5 5.9.4 7.5-3.5.8-1-20.3a45 45 0 0 1 4.1-1.3c5.1-1.3 8.2 0 9.1 3.9.6 2.2.2 4-1 5.7a9.7 9.7 0 0 1-5.7 3.3c-.8.2-1.6.4-2.4.4Zm-.6-9.8.4 6.7c.5 0 1.1 0 1.8-.2 1.5-.4 2.6-1 3.4-1.9.7-.8.9-1.9.6-3-.5-2.1-1.9-2.9-4.2-2.3-.6.1-1.3.4-2 .7Zm24.8-6-9 2.2.3 4.7 6.5-1.6.1 3-6.4 1.6.4 6.3 8.8-2.1.2 3.2-12.4 3-1.2-20.4 12.6-3 .1 3.1Zm17 13.3-12.2 3-1.2-20.3 3.5-.9 1 17.2 8.7-2.2.2 3.2Zm-58 46-1.6-3.7-7 1.7-1.3 4.5-3.6.8 7-22.6 1.3-.3 8.8 18.8-3.6.9ZM159.2 94l-2.2 8.2 5.1-1.2-2.9-7Zm-2-7.2-3.2-4 3.8-.8 1.5 4.3-2.2.5Zm24 8.9.4 7.4-3.5 1-1.1-20.4a45 45 0 0 1 4.2-1.3c5.1-1.3 8.2 0 9 3.9.6 2.2.3 4-1 5.7a9.7 9.7 0 0 1-5.6 3.3l-2.4.4Zm-.6-9.8.4 6.7c.5 0 1 0 1.8-.2 1.5-.4 2.6-1 3.3-1.9.7-.8 1-1.9.7-3.1-.5-2-2-2.8-4.3-2.2-.6.1-1.2.4-2 .7Zm21.8 3 6.5 7.6-4 1-5.6-7.2-2.2.4.4 8.6-3.5.8-1.2-20.3a132 132 0 0 1 5-1.4 9 9 0 0 1 5.3 0 4 4 0 0 1 2.7 3c.4 1.6.3 3-.4 4.4a6.3 6.3 0 0 1-3 3.1Zm-5.9-6.8.4 5.6 1.4-.2c3.3-.8 4.7-2.3 4.2-4.4-.2-1-.7-1.5-1.4-1.7-.7-.2-1.8-.1-3.3.2l-1.3.5Zm13.3 6.5c-.8-3.5-.6-6.7.7-9.6 1.3-2.9 3.5-4.7 6.5-5.5 2.3-.5 4.3-.3 5.8.6 1.6 1 2.6 2.5 3.2 4.9 1 3.8.7 7.2-.6 10a10 10 0 0 1-7 5.5c-2.1.5-4 .3-5.5-.8a8.4 8.4 0 0 1-3-5.1Zm3.5-1.2c.4 1.5 1 2.6 1.8 3.3a3 3 0 0 0 3 .7 5.8 5.8 0 0 0 4.2-4c.8-2.2.8-4.7.2-7.3-.7-3-2.4-4.1-5-3.5-1.9.5-3.2 1.8-3.9 4a12 12 0 0 0-.3 6.8Zm15-.4 3-.7c.3 1.1.9 1.6 1.8 1.4.8-.2 1.4-.5 1.8-.9.4-.3.7-.8.8-1.3.2-.6.2-1.7 0-3.3l-.7-12.7 3.5-.8.7 12.7c.2 3.2-.2 5.4-1.1 6.7a7.6 7.6 0 0 1-4.6 2.7c-1.3.3-2.4.2-3.3-.3-1-.5-1.5-1.3-1.8-2.5l-.1-1ZM255 67.8l-9 2.2.4 4.7 6.4-1.6.2 3-6.5 1.6.4 6.3 8.8-2.1.2 3.1-12.4 3-1.1-20.3 12.5-3 .1 3.1Zm18.6-4.5-6.2 1.5 1 17.2-3.4.8-1-17.1-6.2 1.5-.2-3.2 15.8-3.8.2 3.1Zm12.8-2.3a5.4 5.4 0 0 0-3.4-.3c-2.1.5-3 1.6-2.6 3.2.2.7 1 1.3 2.3 2l2.4 1.2 1.6.9 1 .8 1 1.2.5 1.3a5 5 0 0 1-1 4.6 8.6 8.6 0 0 1-4.9 2.8c-1.7.4-3.4.5-5.2 0l.3-3.6c1.4.6 3 .7 4.5.3 1-.2 1.7-.6 2.2-1.1a2 2 0 0 0 .5-2c-.2-.7-1-1.4-2.3-2l-2.5-1.2-1.6-.8a5.7 5.7 0 0 1-1.5-7.9 7 7 0 0 1 4.3-2.7 17.4 17.4 0 0 1 4.7-.4l-.3 3.7Z" fill="#348899"/></symbol> + <symbol id="appels" viewBox="0 0 80 60" xmlns="http://www.w3.org/2000/svg"> <path d="M1.66016 23.2006C4.67429 23.0597 8.52282 22.3132 12.7892 21.1547C17.5608 19.8591 22.9197 18.0302 28.3314 15.8906C38.369 11.9223 48.6614 6.85659 55.7703 2.08884V2.14843V2.22928V2.31051V2.39211V2.47407V2.5564V2.6391V2.72216V2.80559V2.88938V2.97352V3.05802V3.14288V3.2281V3.31367V3.39959V3.48586V3.57248V3.65944V3.74676V3.83441V3.92241V4.01075V4.09943V4.18845V4.27781V4.3675V4.45752V4.54788V4.63856V4.72958V4.82092V4.91259V5.00458V5.0969V5.18954V5.2825V5.37577V5.46937V5.56327V5.6575V5.75203V5.84688V5.94203V6.03749V6.13326V6.22933V6.32571V6.42239V6.51937V6.61664V6.71422V6.81209V6.91025V7.0087V7.10745V7.20649V7.30581V7.40542V7.50532V7.6055V7.70596V7.8067V7.90772V8.00901V8.11059V8.21243V8.31455V8.41694V8.5196V8.62253V8.72573V8.82918V8.93291V9.03689V9.14114V9.24565V9.35041V9.45543V9.5607V9.66622V9.772V9.87803V9.9843V10.0908V10.1976V10.3046V10.4119V10.5194V10.6271V10.7351V10.8433V10.9517V11.0604V11.1693V11.2784V11.3878V11.4974V11.6072V11.7172V11.8275V11.938V12.0487V12.1596V12.2708V12.3821V12.4937V12.6054V12.7174V12.8296V12.942V13.0546V13.1675V13.2805V13.3937V13.5071V13.6207V13.7345V13.8485V13.9628V14.0771V14.1917V14.3065V14.4215V14.5366V14.6519V14.7674V14.8831V14.999V15.1151V15.2313V15.3477V15.4643V15.581V15.6979V15.815V15.9323V16.0497V16.1673V16.2851V16.403V16.521V16.6393V16.7577V16.8762V16.9949V17.1137V17.2327V17.3519V17.4712V17.5906V17.7102V17.83V17.9498V18.0698V18.19V18.3103V18.4307V18.5513V18.672V18.7928V18.9137V19.0348V19.156V19.2774V19.3988V19.5204V19.6421V19.7639V19.8858V20.0079V20.1301V20.2523V20.3747V20.4972V20.6198V20.7425V20.8654V20.9883V21.1113V21.2344V21.3576V21.481V21.6044V21.7279V21.8515V21.9752V22.099V22.2229V22.3468V22.4709V22.595V22.7192V22.8435V22.9679V23.0923V23.2169V23.3415V23.4661V23.5909V23.7157V23.8406V23.9655V24.0905V24.2156V24.3408V24.466V24.5912V24.7166V24.8419V24.9674V25.0929V25.2184V25.344V25.4696V25.5953V25.7211V25.8468V25.9726V26.0985V26.2244V26.3503V26.4763V26.6023V26.7284V26.8544V26.9806V27.1067V27.2328V27.359V27.4852V27.6115V27.7377V27.864V27.9903V28.1166V28.243V28.3693V28.4956V28.622V28.7484V28.8748V29.0012V29.1275V29.2539V29.3803V29.5067V29.6331V29.7595V29.8859V30.0123V30.1387V30.265V30.3914V30.5177V30.6441V30.7704V30.8967V31.023V31.1492V31.2754V31.4017V31.5279V31.654V31.7802V31.9063V32.0323V32.1584V32.2844V32.4104V32.5363V32.6622V32.7881V32.9139V33.0397V33.1654V33.2911V33.4168V33.5424V33.6679V33.7934V33.9188V34.0442V34.1696V34.2948V34.42V34.5452V34.6703V34.7953V34.9203V35.0452V35.17V35.2947V35.4194V35.544V35.6686V35.793V35.9174V36.0417V36.1659V36.2901V36.4141V36.5381V36.662V36.7858V36.9095V37.0331V37.1566V37.2801V37.4034V37.5266V37.6498V37.7728V37.8957V38.0186V38.1413V38.2639V38.3864V38.5088V38.6311V38.7533V38.8754V38.9973V39.1191V39.2408V39.3624V39.4839V39.6053V39.7265V39.8476V39.9685V40.0894V40.2101V40.3307V40.4511V40.5714V40.6916V40.8116V40.9315V41.0512V41.1708V41.2903V41.4096V41.5288V41.6478V41.7667V41.8854V42.004V42.1224V42.2406V42.3587V42.4766V42.5944V42.712V42.8294V42.9467V43.0638V43.1808V43.2975V43.4141V43.5306V43.6468V43.7629V43.8788V43.9945V44.11V44.2254V44.3406V44.4555V44.5703V44.6849V44.7993V44.9136V45.0276V45.1414V45.2551V45.3685V45.4818V45.5948V45.7076V45.8203V45.9327V46.0449V46.1569V46.2687V46.3803V46.4917V46.6029V46.7138V46.8245V46.9351V47.0453V47.1554V47.2653V47.3749V47.4843V47.5934V47.7024V47.8111V47.9195V48.0278V48.1358V48.2435V48.3511V48.4583V48.5654V48.6722V48.7787V48.885V48.9911V49.0969V49.2024V49.3078V49.4128V49.5176V49.6221V49.7264V49.8304V49.9342V50.0377V50.1409V50.2439V50.3465V50.449V50.5511V50.653V50.7546V50.8559V50.957V51.0578V51.1582V51.2585V51.3584V51.458V51.5574V51.6565V51.7552V51.8537V51.9519V52.0498V52.1474V52.2448V52.3418V52.4385V52.5349V52.631V52.7268V52.8223V52.9175V53.0124V53.1069V53.2012V53.2952V53.3888V53.4821V53.5751V53.6678V53.7601V53.8521V53.9439V54.0352V54.1263V54.217V54.3074V54.3975V54.4872V54.5766V54.5932C48.6675 50.7584 38.3441 46.5962 28.2733 43.3173C18.0609 39.9923 7.95384 37.5244 1.66016 37.2769V23.2006Z" fill="none" stroke-width="2"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M56.7703 0.181641C42.7428 10.2013 12.6838 22.2249 0.660156 22.2249V38.2564C3.19182 38.2564 8.06936 39.0833 11.8822 39.8595L6.27117 53.4863C5.78047 56.4517 7.78661 59.2534 10.752 59.7441C13.7174 60.2349 16.5192 58.2287 17.0099 55.2633L19.8979 41.4627L19.6442 39.9086L19.5098 39.51L41.2658 9.45105H45.2737L21.5122 42.2809C34.1261 45.936 48.342 51.4756 56.7703 56.2918V28.5373L42.6898 47.8979H38.682L55.0188 25.106L56.7703 28.386V11.4099L32.9349 43.1831H28.9271L55.4659 7.44562L56.7703 10.1451V0.181641ZM25.4388 18.068H21.4309L12.6838 31.1271V37.1106L25.4388 18.068Z" stroke="none"/> diff --git a/src/assets/scss/_breakpoint.scss b/src/assets/scss/_breakpoint.scss index d58c83c3fb8a88245ea232ed63dabf134a8a6916..2c0f772ee77d438d7e838d15be2a298cbd87b379 100644 --- a/src/assets/scss/_breakpoint.scss +++ b/src/assets/scss/_breakpoint.scss @@ -4,10 +4,12 @@ $width-large-phone: 600px; $width-tablet: 980px; $width-desktop: 1280px; $width-large-desktop: 1201px; +$width-news-max: 830px; $small-phone: 'only screen and (max-width : #{$width-small-phone})'; $phone: 'only screen and (max-width : #{$width-phone})'; $large-phone: 'only screen and (max-width : #{$width-large-phone})'; +$news-max: 'only screen and (max-width : #{$width-news-max})'; $tablet: 'only screen and (max-width : #{$width-tablet})'; $desktop: 'only screen and (max-width : #{$width-desktop})'; $large-desktop: 'only screen and (min-width : #{$width-large-desktop})'; diff --git a/src/assets/scss/_typography.scss b/src/assets/scss/_typography.scss index 658823519f422e22bfd7c68b609b95b705302417..76fc1257d2f0856d2bd3ca592d4f233eaa0df5ee 100644 --- a/src/assets/scss/_typography.scss +++ b/src/assets/scss/_typography.scss @@ -9,10 +9,11 @@ $font-size-small: 1em; // 16px $font-size-smedium: 1.125em; // 18px $font-size-medium: 1.25em; // 20px $font-size-xmedium: 1.375em; // 22px - +$font-size-xxmedium: 1.5em; // 24px $font-size-large: 1.625em; // 26px $font-size-xlarge: 1.75em; // 28px -$font-size-xxlarge: 1.875em; // 28px +$font-size-xxlarge: 1.875em; // 30px +$font-size-xcxlarge: 2em; // 32px html, body, @@ -45,6 +46,12 @@ h6, font-size: $font-size-small; } +@mixin cn-regular-32 { + font-family: $title-font; + font-style: normal; + font-weight: bold; + font-size: $font-size-xcxlarge; +} @mixin cn-bold-30 { font-family: $title-font; font-style: normal; @@ -72,6 +79,12 @@ h6, font-size: $font-size-large; } +@mixin cn-regular-24 { + font-family: $text-font; + font-style: normal; + font-weight: normal; + font-size: $font-size-xxmedium; +} @mixin cn-regular-22 { font-family: $text-font; font-style: normal; diff --git a/src/index.html b/src/index.html index 865dbc0a11b20140011dc5ae517b1f7d7bfceedd..f41edc5de6db58877bb3f2c6d893d19419f18ae3 100644 --- a/src/index.html +++ b/src/index.html @@ -48,6 +48,8 @@ rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol@0.72.0/dist/L.Control.Locate.min.css" /> + <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-grid.css" /> + <link rel="stylesheet" href="https://unpkg.com/ag-grid-community/dist/styles/ag-theme-alpine.css" /> <script src="https://openlayers.org/en/v4.6.5/build/ol.js" integrity="sha256-VC4mGHI/SvHwjGxD7oBob8kzwzbHy1MsgiCcrR5SbHg= sha384-/UhW2uuxuN/GcGRUL3CJA5ftmLinDAWEV5khyWrOKJ04xesUihAW/UNXf4VMvAuS sha512-WZO+8H/x0lzH/hIMaAan179GT2iyGNzbEA9nCGS7ju1jPPkUUr7uTHJGh3kxVG2GhDlxItuH1gbmFzHZiMul1Q==" diff --git a/src/styles.scss b/src/styles.scss index ed277ef13c5f961ff8ba4316e9e4713f5d450520..05073c59bc7a2b5abcdd5763d1bf3618defde45d 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -10,6 +10,7 @@ @import 'assets/scss/layout'; @import 'assets/scss/buttons'; @import '../node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css'; +@import '~ngx-toastr/toastr'; html { height: -webkit-fill-available; @@ -88,6 +89,42 @@ a { margin-bottom: 1rem; width: unset; } + &.news { + max-width: 1080px; + } + &.no-padding { + padding: 0; + } +} + +// Forms +.form-group { + margin-bottom: 26px; + label { + color: $grey-2; + } +} +form p.notRequired { + margin-top: 0px; + font-style: italic; + color: $secondary-color; +} + +/** Inputs **/ +input { + margin-top: 4px; +} + +/** Textarea **/ +.textareaBlock { + textarea { + padding: 13px 8px; + background: $grey-6; + border: 1px solid $grey-4; + border-radius: $input-radius; + resize: none; + @include cn-regular-16; + } } /** Buttons **/ @@ -98,7 +135,6 @@ button { } /** Checkboxes **/ - .checkbox { list-style-type: none; width: 100%; @@ -278,6 +314,16 @@ button { margin: 0 !important; } +.backLink { + cursor: pointer; + color: $grey-2; + margin-bottom: 40px; + @include cn-bold-16; + &:hover { + opacity: 0.4; + } +} + .userList { max-width: 50%; } @@ -301,3 +347,9 @@ button { color: $white; box-shadow: 0 2px 1px rgba(0, 0, 0, 0.6); } + +.hide-on-mobile { + @media #{$tablet} { + display: none !important; + } +}