Skip to content

Commit

Permalink
321 Added export notes button (#322)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaes authored Dec 4, 2020
1 parent ccd1302 commit c5d9546
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 51 deletions.
74 changes: 44 additions & 30 deletions src/app/answers/answers/answers.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,31 @@ <h3 class="mb-0 c-answers__header-title">{{ 'ANSWERS_HEADER_TITLE' | translate }
<i class="h-vertical-line"></i>
<span class="font-weight-bold h-is-muted">{{ (answerState$ | async).totalItems }} total</span>
</div>

<div class="c-answers__buttons" role="group" aria-label="Sort">
<app-base-button (click)="downloadAnswers(filtersForm.value);" class="c-answers__button">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83337 14.1663L14.1667 5.83301" stroke="#707070" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M5.83337 5.83301H14.1667V14.1663" stroke="#707070" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<span>{{ 'EXPORT' | translate }}</span>
</app-base-button>

<div class="d-flex">
<div class="c-answers__buttons" role="group" aria-label="Sort">
<app-base-button (click)="downloadAnswers(filtersForm.value);" class="c-answers__button">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83337 14.1663L14.1667 5.83301" stroke="#707070" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M5.83337 5.83301H14.1667V14.1663" stroke="#707070" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<span>{{ 'EXPORT' | translate }}</span>
</app-base-button>
</div>

<div class="c-answers__buttons ml-2" role="group" aria-label="Sort">
<app-base-button (click)="downloadNotes(filtersForm.value);" class="c-answers__button">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.83337 14.1663L14.1667 5.83301" stroke="#707070" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M5.83337 5.83301H14.1667V14.1663" stroke="#707070" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<span>{{ 'EXPORT_NOTES' | translate }}</span>
</app-base-button>
</div>
</div>
</div>

Expand All @@ -32,13 +46,13 @@ <h3 class="mb-0 c-answers__header-title">{{ 'ANSWERS_HEADER_TITLE' | translate }
<form #f="ngForm" [formGroup]="filtersForm" (ngSubmit)="requestFilteredData(f.value)" novalidate class="row">
<div class="form-group col-auto">
<label for="form-name">{{ 'FILTER_BY' | translate }} {{ 'COUNTY_CODE' | translate | lowercase }}</label>
<select
class="form-control"
<select
class="form-control"
id="form-name"
formControlName="county"
name="county"
>
<option
<option
*ngFor="let county of (counties$ | async); first as isFirst"
[value]="isFirst ? '' : county.code"
>
Expand All @@ -49,8 +63,8 @@ <h3 class="mb-0 c-answers__header-title">{{ 'ANSWERS_HEADER_TITLE' | translate }

<div class="form-group col-auto">
<label for="form-county">{{ 'FILTER_BY' | translate }} {{ 'POLLING_STATION_NUMBER' | translate | lowercase }}</label>
<input
[placeholder]="'TYPE' | translate"
<input
[placeholder]="'TYPE' | translate"
type="text"
name="pollingStationNumber"
formControlName="pollingStationNumber"
Expand All @@ -60,13 +74,13 @@ <h3 class="mb-0 c-answers__header-title">{{ 'ANSWERS_HEADER_TITLE' | translate }
</div>

<div class="form-group col-auto">
<label for="form-county">{{ 'FILTER_BY' | translate }} {{ 'OBSERVER_PHONE' | translate | lowercase }}</label>
<input
[placeholder]="'TYPE' | translate"
<label for="form-phone">{{ 'FILTER_BY' | translate }} {{ 'OBSERVER_PHONE' | translate | lowercase }}</label>
<input
[placeholder]="'TYPE' | translate"
type="text"
name="observerPhoneNumber"
formControlName="observerPhoneNumber"
id="form-county"
id="form-phone"
class="form-control"
/>
</div>
Expand All @@ -87,8 +101,8 @@ <h3 class="mb-0 c-answers__header-title">{{ 'ANSWERS_HEADER_TITLE' | translate }
<!-- FIXME: type `date` -->
<!-- <div class="form-group col-auto">
<label for="form-county">{{ 'FILTER_BY' | translate }} {{ 'FROM' | translate | lowercase }}</label>
<input
[placeholder]="'TYPE' | translate"
<input
[placeholder]="'TYPE' | translate"
ngModel
type="text"
name="fromTime"
Expand All @@ -100,32 +114,32 @@ <h3 class="mb-0 c-answers__header-title">{{ 'ANSWERS_HEADER_TITLE' | translate }
<!-- FIXME: type `date` -->
<!-- <div class="form-group col-auto">
<label for="form-county">{{ 'FILTER_BY' | translate }} {{ 'TO' | translate | lowercase }}</label>
<input
[placeholder]="'TYPE' | translate"
<input
[placeholder]="'TYPE' | translate"
ngModel
type="text"
name="toTime"
id="form-county"
class="form-control"
/>
</div> -->

<div class="col-auto form-group align-self-end">
<div class="btn-group d-flex align-items-center" role="group" aria-label="Filter">
<!-- in `height: '2.8rem'`, the value is from `.scss` file -->
<app-base-button
<app-base-button
[disabled]="false"
[variant]="BaseButtonVariants.PURPLE"
[variant]="BaseButtonVariants.PURPLE"
[custom-styles]="{ height: '2.8rem', padding: '0.5rem 1.2rem' }"
type="submit"
>
{{ 'FILTER' | translate }}
</app-base-button>
&nbsp;
<app-base-button
<app-base-button
(click)="f.reset({ county: '' }); onResetFilters()"
[disabled]="false"
[variant]="BaseButtonVariants.PURPLE"
[variant]="BaseButtonVariants.PURPLE"
[custom-styles]="{ height: '2.8rem', border: 'none' }"
>
{{ 'RESET' | translate }}
Expand Down Expand Up @@ -154,4 +168,4 @@ <h4 class="text-center pb-2 c-no-msg__title">{{ 'NO_RESULTS' | translate }}</h4>
<p class="text-center pb-3 c-no-msg__content">{{ 'TRY_AGAIN_MSG' | translate }}</p>
</div>
</app-table-container>
</div>
</div>
51 changes: 35 additions & 16 deletions src/app/answers/answers/answers.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { map, take, shareReplay, filter } from 'rxjs/operators';
import {map, shareReplay, finalize} from 'rxjs/operators';
import { LoadAnswerDetailsAction, LoadAnswerPreviewAction, updateFilters, updatePageInfo } from '../../store/answer/answer.actions';
import { AnswerState } from '../../store/answer/answer.reducer';
import { FormState } from '../../store/form/form.reducer';
Expand All @@ -22,17 +22,16 @@ import { getCounties } from 'src/app/store/county/county.selectors';
import { AnswerExtra } from 'src/app/models/answer.extra.model';
import { FormLoadAction } from 'src/app/store/form/form.actions';
import { FormBuilder } from '@angular/forms';
import { toFinite } from 'lodash';

const TABLE_COLUMNS = new InjectionToken('TABLE_COLUMNS', {
providedIn: 'root',
factory: () => {
const columns: TableColumn[] = [
{ name: 'ANSWERS_POLLING_STATION', propertyName: 'pollingStationName', },
{ name: 'ANSWERS_NAME', propertyName: 'observerName', },
{ name: 'ANSWERS_PHONE', propertyName: 'observerPhoneNumber', },
{ name: 'ANSWERS_PHONE', propertyName: 'observerPhoneNumber', },
{ name: 'ANSWERS_DATE_AND_TIME', propertyName: 'observerArrivalTime', canBeSorted: true },
{ name: 'ANSWERS_LOCATION_TYPE', propertyName: 'locationType', },
{ name: 'ANSWERS_LOCATION_TYPE', propertyName: 'locationType', },
];

return columns;
Expand Down Expand Up @@ -123,6 +122,31 @@ export class AnswersComponent implements OnInit {
return;
}

const filter = this.mapFilterKeys(rawFilters);

this.isLoading = true;
return this.answersService.downloadAnswers(filter).subscribe(res => {
this.isLoading = false;
FileSaver.saveAs(res, 'data.csv');
}, error => {
this.isLoading = false;
});
}

downloadNotes(rawFilters) {
if (!confirm(this.translate.instant('ANSWERS_DOWNLOAD_CONFIRMATION'))) {
return;
}

const filter = this.mapFilterKeys(rawFilters);

this.isLoading = true;
return this.answersService.downloadNotes(filter)
.pipe(finalize(() => this.isLoading = false))
.subscribe(res => FileSaver.saveAs(res, 'notes-data.csv'));
}

private mapFilterKeys(rawFilters) {
const filterWordsDict = {
county: 'county',
pollingStationNumber: 'pollingStationNumber',
Expand All @@ -135,21 +159,16 @@ export class AnswersComponent implements OnInit {
const filter: AnswersPackFilter = {};

for (const rawKey in rawFilters) {
const rawValue = rawFilters[rawKey];
const key = filterWordsDict[rawKey];
if (rawFilters.hasOwnProperty(rawKey)) {
const rawValue = rawFilters[rawKey];
const key = filterWordsDict[rawKey];

if (this.isValidValue(rawValue)) {
filter[key] = rawValue;
if (this.isValidValue(rawValue)) {
filter[key] = rawValue;
}
}
}

this.isLoading = true;
return this.answersService.downloadAnswers(filter).subscribe(res => {
this.isLoading = false;
FileSaver.saveAs(res, 'data.csv');
}, error => {
this.isLoading = false;
});
return filter;
}

private translateColumnNames(rawTableColumns: TableColumn[]) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/answer/answer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</div>
<div class="col-md-2 mb-3">
<label for="observerPhone" class="control-label">{{'OBSERVER_PHONE' | translate}}</label>
<input [(ngModel)]="observerPhone" name="observerPhone" class="form-control" id="observerId">
<input [(ngModel)]="observerPhone" name="observerPhone" class="form-control" id="observerPhone">
</div>
<div class="col-md-2 mb-3">
<label for="fromTime" class="control-label">{{'FROM' | translate }}</label>
Expand Down
19 changes: 15 additions & 4 deletions src/app/services/answers.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Location } from '@angular/common';
import { Injectable } from '@angular/core';
import { ApiService, QueryParamBuilder } from '../core/apiService/api.service';
import { AnswerExtraConstructorData } from '../models/answer.extra.model';
import {HttpParams} from '@angular/common/http';
import {first} from 'rxjs/operators';

@Injectable({
providedIn: 'root'
Expand All @@ -17,16 +19,25 @@ export class AnswersService {
let paramBuilder = QueryParamBuilder
.Instance('/api/v1/export/all');

for (const key in filter) {
const value = filter[key];
paramBuilder = paramBuilder.withParam(key, value);
for (const key in filter) {
if (filter.hasOwnProperty(key)) {
const value = filter[key];
paramBuilder = paramBuilder.withParam(key, value);
}
}
const urlWithParams = paramBuilder.build();

const url: string = Location.joinWithSlash(this.baseUrl, urlWithParams);
return this.http.get<Blob>(url, {responseType: 'blob' as 'json'});
return this.http.get<Blob>(url, {responseType: 'blob' as 'json'}).pipe(first());
}

downloadNotes(filter: AnswersPackFilter) {
const filterStringEntries = Object.entries(filter).map(([k, v]) => [k, v.toString()]);
const params = new HttpParams({fromObject: Object.fromEntries(filterStringEntries)});
const url = Location.joinWithSlash(this.baseUrl, '/api/v1/export/all/notes');
return this.http.get<Blob>(url, {params, responseType: 'blob' as 'json'}).pipe(first());
}

fetchExtraDetailsForObserver (observerId: number, sectionId: number) {
return this.http.get<AnswerExtraConstructorData>(this.extraDetailsURL, {
body: {
Expand Down
1 change: 1 addition & 0 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
"VALIDATION_REQUIRED": "%s is required",

"EXPORT": "Export answers",
"EXPORT_NOTES": "Export notes",
"NO_RESULTS": "No results",
"TRY_AGAIN_MSG": "Please make sure you've typed everything correctly and try again",
"VIEW_NOTE": "View Note",
Expand Down
1 change: 1 addition & 0 deletions src/assets/i18n/ro.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
"VALIDATION_REQUIRED": "%s este necesar",

"EXPORT": "Exportă răspunsurile",
"EXPORT_NOTES": "Exportă notele",
"NO_RESULTS": "Niciun rezultat",
"TRY_AGAIN_MSG": "Asigură-te că ai scris totul corect și încearcă din nou",
"VIEW_NOTE": "Vezi nota",
Expand Down

1 comment on commit c5d9546

@vercel
Copy link

@vercel vercel bot commented on c5d9546 Dec 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.