Commit 7ddc3edc authored by Léonard Treille's avatar Léonard Treille
Browse files

Start contribution feature

parent 65469718
......@@ -4,6 +4,10 @@ import { HorairesLignesComponent } from '@pages/horaires-lignes/horaires-lignes.
import { DetailLigneComponent } from '@pages/detail-ligne/detail-ligne.component';
import { LinesResolver } from '@features/lines/lines.resolver';
import { LineResolver } from '@features/line/line.resolver';
import { ContributionFirstStepComponent } from '@pages/contribution/contribution-first-step/contribution-first-step.component';
import { ContributionSecondStepComponent } from '@pages/contribution/contribution-second-step/contribution-second-step.component';
import { ContributionFinalStepComponent } from '@pages/contribution/contribution-final-step/contribution-final-step.component';
import { ContributionWrapperComponent } from '@pages/contribution/contribution-wrapper/contribution-wrapper.component';
const routes: Routes = [
{
......@@ -18,6 +22,14 @@ const routes: Routes = [
clusters: LineResolver
}
},
{
path: 'contribution', component: ContributionWrapperComponent,
children: [
{ path: '', component: ContributionFirstStepComponent },
{ path: ':stop', component: ContributionSecondStepComponent },
{ path: ':stop/:line/:dir', component: ContributionFinalStepComponent },
]
},
];
@NgModule({
......
......@@ -16,6 +16,7 @@ import { MatRippleModule } from '@angular/material/core';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatToolbarModule } from '@angular/material/toolbar';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
......@@ -40,6 +41,14 @@ import { OccupancyDatasetPipe } from '@features/occupancy/occupancy-dataset.pipe
import { VerticalBarChartType } from '@components/chart/types/vertical-bar.chart-type';
import { OccupancyChartType } from '@components/chart/types/occupancy.chart-type';
import { OccupancyChartDialogComponent } from '@features/occupancy/occupancy-chart-dialog/occupancy-chart-dialog.component';
import { ContributionFirstStepComponent } from './pages/contribution/contribution-first-step/contribution-first-step.component';
import { ContributionSecondStepComponent } from './pages/contribution/contribution-second-step/contribution-second-step.component';
import { ContributionFinalStepComponent } from './pages/contribution/contribution-final-step/contribution-final-step.component';
import { ContributionWrapperComponent } from './pages/contribution/contribution-wrapper/contribution-wrapper.component';
import { BackButtonComponent } from './components/back-button/back-button.component';
import { SearchInputComponent } from './features/search/search-input/search-input.component';
import { SearchListComponent } from './features/search/search-list/search-list.component';
import { SearchItemDirective } from './features/search/search-item.directive';
@NgModule({
declarations: [
......@@ -63,7 +72,15 @@ import { OccupancyChartDialogComponent } from '@features/occupancy/occupancy-cha
TimeSchedulePipe,
ChartComponent,
OccupancyDatasetPipe,
OccupancyChartDialogComponent
OccupancyChartDialogComponent,
ContributionFirstStepComponent,
ContributionSecondStepComponent,
ContributionFinalStepComponent,
ContributionWrapperComponent,
BackButtonComponent,
SearchInputComponent,
SearchListComponent,
SearchItemDirective
],
imports: [
BrowserModule,
......@@ -84,9 +101,10 @@ import { OccupancyChartDialogComponent } from '@features/occupancy/occupancy-cha
MatFormFieldModule,
MatInputModule,
MatProgressBarModule,
MatToolbarModule,
],
entryComponents: [
DialogRecherchePointComponent
DialogRecherchePointComponent,
],
providers: [
WINDOW_PROVIDERS,
......
<button *ngIf="!isMobile" mat-button (click)="back()">
<mat-icon class="icon-left">arrow_back</mat-icon>
<span>Retour</span>
</button>
<button *ngIf="isMobile" mat-icon-button (click)="back()">
<mat-icon>arrow_back</mat-icon>
<span class="cdk-visually-hidden">Retour</span>
</button>
@import "../../../styles/variables";
:host {
display: block;
margin-right: $spacing * 2;
}
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { BreakpointService } from '@services/breakpoint.service';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-back-button',
templateUrl: './back-button.component.html',
styleUrls: ['./back-button.component.scss'],
})
export class BackButtonComponent implements OnInit, OnDestroy {
isMobile: boolean;
private unsubscriber = new Subject<void>();
constructor(private breakpointService: BreakpointService) { }
ngOnInit(): void {
this.breakpointService.breakpoint.pipe(takeUntil(this.unsubscriber)).subscribe((result) => {
this.isMobile = !result.matches;
});
}
ngOnDestroy(): void {
this.unsubscriber.next();
this.unsubscriber.complete();
}
back() {
history.back();
}
}
......@@ -87,7 +87,7 @@
<app-chart type="vertical-bar" [dataset]="obj | occupancyDataset | async" [tooltipDisabled]="true"></app-chart>
</button>
<div class="layout content-center">
<a href="#" mat-button color="primary" [disabled]="!enableContrib">Contribuer</a>
<a routerLink="/contribution/lorem/ipsum/dolor" mat-button color="primary" [disabled]="!enableContrib">Contribuer</a>
</div>
</div>
</ng-template>
......
......@@ -19,12 +19,6 @@
</button>
</mat-action-list>
</app-m-list-wrapper>
<mat-action-list *ngIf="!myInput.value?.length && estPresentRue && geolocationEnable" class="m-theme flat important">
<button mat-list-item (click)="selectPosition()" class="dark-overlay-8 light-overlay">
<mat-icon matListIcon>my_location</mat-icon>
<span matLine>Ma Position</span>
</button>
</mat-action-list>
<app-m-list-wrapper *ngIf="!myInput.value?.length && favoris.length > 0" [seeMoreEnable]="favoris.length > 3" #favoriteList="mListWrapper">
<mat-action-list class="m-theme flat important">
<span matSubheader>Favoris</span>
......
......@@ -11,7 +11,7 @@
right: $spacing * 2;
}
mat-spinner {
[matSuffix] {
position: absolute;
top: $spacing;
right: $spacing * 2 + ($spacing / 2);
......
......@@ -2,14 +2,8 @@ import { Component, OnInit, ViewChild, Input } from '@angular/core';
import { FormControl } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { debounceTime, switchMap, map, filter, tap, finalize } from 'rxjs/operators';
// import { GeolocationService } from '@services/geolocation.service';
// import { ApiMService } from '@services/api-m.service';
// import { DataService } from '@services/data.service';
// import { FavorisService } from '@services/favoris.service';
import { trigger, transition, style, animate } from '@angular/animations';
import { SearchService } from '@features/search/search.service';
import { HttpClient } from '@angular/common/http';
import { domain } from '@helpers/domain.helpers';
@Component({
selector: 'app-recherche-point',
......@@ -42,22 +36,12 @@ export class RecherchePointComponent implements OnInit {
displayLoader = false;
constructor(
// private apiMService: ApiMService,
// private geolocationService: GeolocationService,
// public dataService: DataService,
// public favorisService: FavorisService,
private searchService: SearchService,
private dialogRef?: MatDialog) {
}
ngOnInit() {
// this.geolocationEnable = this.geolocationService.isAvailable() && this.geolocationService.isEnable();
// this.geolocationService.enable$.subscribe((enable) => this.geolocationEnable = enable);
this.favoris = [];
// const fclust = this.favorisService.getFavoris('clusters');
// const favorisAdresses = this.favorisService.getFavoris('adresses');
// fclust.forEach(f => this.favoris.push(f.object));
// favorisAdresses.forEach(f => this.favoris.push(f.object));
this.favoris = this.favoris.filter((f) => !this.state.types || this.state.types.includes(f.properties.type));
this.recherchesRecentes = this.searchService.recherchesRecentes.filter((f) => !this.state.types || this.state.types.includes(f.properties.type));
......@@ -105,19 +89,4 @@ export class RecherchePointComponent implements OnInit {
}
this.dialogRef.getDialogById(this.idDialog).close({ feature: point });
}
selectPosition() {
// this.geolocationService.getCurrentPosition().then((coordinates) => {
// const positionFeature = {
// geometry: {
// coordinates: coordinates,
// type: 'point'
// },
// properties: this.geolocationService.position,
// type: 'position'
// };
// (positionFeature.properties as any).LIBELLE = 'Position';
// (positionFeature.properties as any).type = 'position';
// this.dialogRef.getDialogById(this.idDialog).close({ feature: positionFeature });
// });
}
}
<mat-form-field>
<mat-label>
<ng-content slot="label"></ng-content>
</mat-label>
<input class="search-bar-input" type="text" #input matInput [formControl]="searchControl" autocomplete="off" cdkFocusInitial [required]="required" />
<mat-icon class="fixed" matSuffix @fade *ngIf="input.value === ''">search</mat-icon>
<div matSuffix @fade *ngIf="displayLoader" class="fixed">
<mat-spinner mode="indeterminate" diameter="24"></mat-spinner>
</div>
<button matSuffix @fade class="fixed clear-button" mat-icon-button *ngIf="input.value!=''" mat-icon-button aria-label="Vider" (click)="input.value=''">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
@import "../../../../styles/variables";
:host {
display: block;
width: 100%;
}
mat-form-field {
width: 100%;
}
.fixed {
position: absolute;
top: -4px;
left: -10px;
transform: translate(-50%, -50%);
}
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, filter, tap, switchMap, finalize, map } from 'rxjs/operators';
import { SearchService } from '../search.service';
import { SearchResponse } from '../search.model';
import { trigger, transition, style, animate } from '@angular/animations';
@Component({
selector: 'app-search-input',
templateUrl: './search-input.component.html',
styleUrls: ['./search-input.component.scss'],
exportAs: 'searchInput',
animations: [
trigger('fade', [
transition(':enter', [
style({ opacity: 0 }),
animate('250ms', style({ opacity: 1 }))
]),
transition(':leave', [
style({ opacity: 1 }),
animate('250ms', style({ opacity: 0 }))
]),
])
]
})
export class SearchInputComponent implements OnInit {
@Input() types: string;
@Input() set required(value: boolean) {
this._required = value !== undefined;
}
get required(): boolean {
return this._required;
}
@Output() search = new EventEmitter<any>();
searchControl = new FormControl();
displayLoader: boolean;
private _required: boolean;
constructor(private searchService: SearchService) { }
ngOnInit(): void {
this.searchControl.valueChanges
.pipe(
debounceTime(300),
filter(query => query.length >= 3),
tap(() => this.displayLoader = true),
switchMap((value: string) => this.searchService.search(value, this.types)),
map((data: SearchResponse) => {
if (data && Array.isArray(data.features)) {
data.features.sort((a: any, b: any) => {
if (parseInt(a.properties.dist, 10) < parseInt(b.properties.dist, 10))
return 1; // tri decroissant on inverse
if (parseInt(a.properties.dist, 10) > parseInt(b.properties.dist, 10))
return -1;
// a doit être égal à b
return 0;
});
data.features = data.features.slice(0, 30);
return data;
}
return { type: '', features: [] };
}),
).subscribe((data: SearchResponse) => {
this.search.emit(data.features);
this.displayLoader = false;
});
}
}
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appSearchItem], [searchItem], [search-item]'
})
export class SearchItemDirective {
constructor(public elementRef: ElementRef) { }
}
<app-m-list-wrapper *ngIf="searchItems.length" [seeMoreEnable]="searchItems.length > 3" #searchList="mListWrapper">
<mat-action-list class="m-theme flat">
<ng-container *ngFor="let option of searchItems | slice:0:searchList.seeMore ? searchItems.length : 3">
<ng-container *ngTemplateOutlet="searchItemTemplate; context:{$implicit: option}"></ng-container>
</ng-container>
<!-- <button mat-list-item "
class="dark-overlay-8 light-overlay">
<app-icon-type matListIcon [type]="option.properties.type"></app-icon-type>
<span matLine>{{ option | libelle }}</span>
<span matLine class="texteCommune">{{ option | commune }}</span>
</button> -->
</mat-action-list>
</app-m-list-wrapper>
import { Component, OnInit, ContentChild, TemplateRef } from '@angular/core';
import { SearchService } from '../search.service';
import { SearchFeature } from '../search.model';
import { SearchItemDirective } from '../search-item.directive';
@Component({
selector: 'app-search-list',
templateUrl: './search-list.component.html',
styleUrls: ['./search-list.component.scss'],
exportAs: 'searchList'
})
export class SearchListComponent implements OnInit {
searchItems = [];
@ContentChild(SearchItemDirective, { read: TemplateRef }) searchItemTemplate;
constructor(private searchService: SearchService) { }
ngOnInit(): void {
console.log('ngOnInit', this.searchItemTemplate);
}
udpate(data: SearchFeature[]) {
this.searchItems = data;
}
}
export interface SearchResponse {
type: string;
features: SearchFeature[];
}
export interface SearchFeature {
type: string;
properties: SearchProperties;
geometry: SearchGeometry;
}
export interface SearchGeometry {
type: string;
coordinates: number[];
}
export interface SearchProperties {
LIBELLE: string;
rue: string;
COMMUNE: string;
id: string;
importance: number;
numero: { [key: number]: { lat: number, lon: number } };
type: string;
LaMetro: boolean;
LeGresivaudan: boolean;
PaysVoironnais: boolean;
dist: string;
numeroRue: string;
}
......@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { domain } from '@helpers/domain.helpers';
import { Observable } from 'rxjs';
import { SearchResponse } from './search.model';
@Injectable({
providedIn: 'root'
......@@ -15,12 +16,12 @@ export class SearchService {
this.load();
}
search(query: string, types: string): Observable<any> {
search(query: string, types: string): Observable<SearchResponse> {
let params = new HttpParams({ fromObject: { query } });
if (types !== '') {
params = params.set('types', types);
}
return this.http.get(`${domain}/api/find/json`, { params });
return this.http.get<SearchResponse>(`${domain}/api/find/json`, { params });
}
addRecherchesRecentes(obj) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment