From c5b52764002149eff41dcd48cb876fa6ec79d562 Mon Sep 17 00:00:00 2001 From: Baptiste Lemoine Date: Wed, 5 Feb 2020 11:13:36 +0100 Subject: [PATCH] :gears: refacto date and poll utilities functions in a separate class --- src/app/config/DateUtilities.ts | 59 +++++++++ src/app/config/PollUtilities.ts | 61 +++++++++ src/app/pages/dates/dates.component.ts | 62 +-------- .../poll-display/poll-display.component.html | 15 ++- .../pages/visibility/visibility.component.ts | 6 +- src/app/services/config.service.ts | 119 +++++------------- 6 files changed, 168 insertions(+), 154 deletions(-) create mode 100644 src/app/config/DateUtilities.ts create mode 100644 src/app/config/PollUtilities.ts diff --git a/src/app/config/DateUtilities.ts b/src/app/config/DateUtilities.ts new file mode 100644 index 00000000..7184242d --- /dev/null +++ b/src/app/config/DateUtilities.ts @@ -0,0 +1,59 @@ +export class DateUtilities { + + /** + * add some days to a date, to compute intervals + * @param days + * @param date + */ + addDaysToDate(days: number, date: Date) { + date = new Date(date.valueOf()); + date.setDate(date.getDate() + days); + return date; + }; + + /** + * + * @param d1 + * @param d2 + * @param interval + */ + getDatesInRange(d1: Date, d2: Date, interval: number) { + d1 = new Date(d1); + d2 = new Date(d2); + const dates = []; + while (+d1 < +d2) { + dates.push({ + literal: this.formateDate(d1), + date_object: d1 + }); + d1.setDate(d1.getDate() + interval) + } + return dates.slice(0); + } + + /** + * get the number of days between two dates + * @param d1 + * @param d2 + */ + dayDiff(d1: Date, d2: Date): Number { + return Number(((d2.getTime()) - (d1.getTime()) / 31536000000)); + } + + /** + * format a date object to the date format used by the inputs of type date + * YYYY-MM-DD + * @param date + */ + formateDate(date) { + return [ + date.getFullYear(), + this.getDoubleDigits(date.getMonth() + 1), + this.getDoubleDigits(date.getDate()), + ].join('-') + } + + getDoubleDigits(str) { + return ("00" + str).slice(-2); + } +} diff --git a/src/app/config/PollUtilities.ts b/src/app/config/PollUtilities.ts new file mode 100644 index 00000000..02aec5b1 --- /dev/null +++ b/src/app/config/PollUtilities.ts @@ -0,0 +1,61 @@ +import {HttpHeaders} from "@angular/common/http"; +import {PollConfig} from "./PollConfig"; + +export class PollUtilities { + // utils functions + /** + * generate unique id to have a default url for future poll + */ + makeUuid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + /** + * make a uniq slug for the current poll creation + * @param str + */ + makeSlug(config: PollConfig) { + let str = ''; + str = config.creationDate.getFullYear() + '_' + (config.creationDate.getMonth() + 1) + '_' + config.creationDate.getDate() + '_' + config.myName + '_' + config.title; + str = str.replace(/^\s+|\s+$/g, ''); // trim + str = str.toLowerCase(); + + // remove accents, swap ñ for n, etc + var from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;"; + var to = "aaaaeeeeiiiioooouuuunc------"; + for (var i = 0, l = from.length; i < l; i++) { + str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); + } + + str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars + .replace(/\s+/g, '-') // collapse whitespace and replace by - + .replace(/-+/g, '-'); // collapse dashes + + return str; + } + + /** + * prepare headers like the charset and json type for any call to the backend + * @param bodyContent + */ + makeHeaders(bodyContent?: any) { + + const headerDict = { + 'Charset': 'UTF-8', + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Origin': '*' + }; + + const requestOptions = { + headers: new HttpHeaders(headerDict), + body: bodyContent + }; + + return requestOptions; + } +} diff --git a/src/app/pages/dates/dates.component.ts b/src/app/pages/dates/dates.component.ts index 25aa8676..ac115807 100644 --- a/src/app/pages/dates/dates.component.ts +++ b/src/app/pages/dates/dates.component.ts @@ -4,6 +4,7 @@ import {BaseComponent} from '../base-page/base.component'; import {DOCUMENT} from '@angular/common'; import {MessageService} from "primeng/api"; import {defaultTimeOfDay} from "../../config/defaultConfigs"; +import {DateUtilities} from "../../config/DateUtilities"; @Component({ selector: 'framadate-dates', @@ -20,6 +21,7 @@ export class DatesComponent extends BaseComponent implements OnInit { constructor(public config: ConfigService, private cd: ChangeDetectorRef, private messageService: MessageService, + private dateUtilities: DateUtilities, @Inject(DOCUMENT) private document: any ) { super(config); @@ -28,7 +30,7 @@ export class DatesComponent extends BaseComponent implements OnInit { countDays() { // compute the number of days in the date interval if (this.endDateInterval && this.startDateInterval) { - this.intervalDays = (this.dayDiff(this.endDateInterval, this.startDateInterval)).toFixed(0) + this.intervalDays = (this.dateUtilities.dayDiff(this.endDateInterval, this.startDateInterval)).toFixed(0) } } @@ -39,7 +41,7 @@ export class DatesComponent extends BaseComponent implements OnInit { let dateCurrent = new Date(); const dateJson = dateCurrent.toISOString(); this.startDateInterval = dateJson.substring(0, 10); - this.endDateInterval = this.addDaysToDate(this.intervalDaysDefault, dateCurrent).toISOString().substring(0, 10); + this.endDateInterval = this.dateUtilities.addDaysToDate(this.intervalDaysDefault, dateCurrent).toISOString().substring(0, 10); } addDate() { @@ -77,16 +79,6 @@ export class DatesComponent extends BaseComponent implements OnInit { this.config.timeList = defaultTimeOfDay; } - /** - * add some days to a date, to compute intervals - * @param days - * @param date - */ - addDaysToDate(days: number, date: Date) { - date = new Date(date.valueOf()); - date.setDate(date.getDate() + days); - return date; - }; /** * add a time period to a specific date choice, @@ -123,7 +115,7 @@ export class DatesComponent extends BaseComponent implements OnInit { * add all the dates between the start and end dates in the interval section */ addIntervalOfDates() { - let newIntervalArray = this.getDatesInRange(this.startDateInterval, this.endDateInterval, 1); + let newIntervalArray = this.dateUtilities.getDatesInRange(this.startDateInterval, this.endDateInterval, 1); const converted = []; newIntervalArray.forEach(element => { @@ -144,49 +136,5 @@ export class DatesComponent extends BaseComponent implements OnInit { } - /** - * - * @param d1 - * @param d2 - * @param interval - */ - getDatesInRange(d1: Date, d2: Date, interval: number) { - d1 = new Date(d1); - d2 = new Date(d2); - const dates = []; - while (+d1 < +d2) { - dates.push({ - literal: this.formateDate(d1), - date_object: d1 - }); - d1.setDate(d1.getDate() + interval) - } - return dates.slice(0); - } - /** - * get the number of days between two dates - * @param d1 - * @param d2 - */ - dayDiff(d1: Date, d2: Date): Number { - return Number(((d2.getTime()) - (d1.getTime()) / 31536000000)); - } - - /** - * format a date object to the date format used by the inputs of type date - * YYYY-MM-DD - * @param date - */ - formateDate(date) { - return [ - date.getFullYear(), - this.getDoubleDigits(date.getMonth() + 1), - this.getDoubleDigits(date.getDate()), - ].join('-') - } - - getDoubleDigits(str) { - return ("00" + str).slice(-2); - } } diff --git a/src/app/pages/poll-display/poll-display.component.html b/src/app/pages/poll-display/poll-display.component.html index 30e5c211..2cc90cf6 100644 --- a/src/app/pages/poll-display/poll-display.component.html +++ b/src/app/pages/poll-display/poll-display.component.html @@ -9,19 +9,18 @@
-
- - -
-
- -
- +
+ + +
+
+ +
diff --git a/src/app/pages/visibility/visibility.component.ts b/src/app/pages/visibility/visibility.component.ts index 78778c63..993a90fa 100644 --- a/src/app/pages/visibility/visibility.component.ts +++ b/src/app/pages/visibility/visibility.component.ts @@ -2,6 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {BaseComponent} from '../base-page/base.component'; import {ConfigService} from '../../services/config.service'; import {environment} from "../../../environments/environment"; +import {PollUtilities} from "../../config/PollUtilities"; @Component({ selector: 'framadate-visibility', @@ -13,12 +14,13 @@ export class VisibilityComponent extends BaseComponent implements OnInit { baseUrl = environment.baseApiHref; environment = environment; - constructor(public config: ConfigService) { + constructor(public config: ConfigService, + public utils: PollUtilities) { super(config); } ngOnInit() { - this.config.customUrl = this.config.makeSlug(); + this.config.customUrl = this.utils.makeSlug(this.config); this.config.expirationDate = (this.config.addDaysToDate(this.config.expiracyDateDefaultInDays, new Date())).toISOString().substring(0, 10); } diff --git a/src/app/services/config.service.ts b/src/app/services/config.service.ts index 3d5a0e6e..f849a5ed 100644 --- a/src/app/services/config.service.ts +++ b/src/app/services/config.service.ts @@ -1,6 +1,6 @@ import {Injectable} from '@angular/core'; import {PollConfig} from '../config/PollConfig'; -import {HttpClient, HttpHeaders} from "@angular/common/http"; +import {HttpClient} from "@angular/common/http"; import {environment} from "../../environments/environment"; import {ConfirmationService, MessageService} from 'primeng/api'; import {Router} from "@angular/router"; @@ -8,6 +8,7 @@ import {mockMyPolls} from "../config/mocks/mockmypolls"; import {defaultAnswers, defaultDates, defaultTimeOfDay} from "../config/defaultConfigs"; import {mockPoll3} from "../config/mocks/mock-poll3"; import {mockSuccessVote} from "../config/mocks/mock-success-vote"; +import {PollUtilities} from "../config/PollUtilities"; /** * le service transverse à chaque page qui permet de syncroniser la configuration de sondage souhaitée @@ -24,10 +25,20 @@ export class ConfigService extends PollConfig { constructor(private http: HttpClient, private messageService: MessageService, private router: Router, + private utils: PollUtilities, private confirmationService: ConfirmationService, ) { super(); - // fill in mock values if we are not in production environment + this.fillValuesOnDevEnv(); + } + + set(key, val) { + this[key] = val; + } + + // fill in mock values if we are not in production environment + fillValuesOnDevEnv() { + if (!environment.production) { console.info(' ######### framadate ######### we are not in production env, filling with mock values'); this.currentPoll = mockPoll3; @@ -38,50 +49,6 @@ export class ConfigService extends PollConfig { } } - set(key, val) { - this[key] = val; - } - - clear() { - this.messageService.clear(); - } - - // utils functions - /** - * generate unique id to have a default url for future poll - */ - makeUuid() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - - /** - * make a uniq slug for the current poll creation - * @param str - */ - makeSlug(str?: string) { - if (!str) { - str = this.creationDate.getFullYear() + '_' + (this.creationDate.getMonth() + 1) + '_' + this.creationDate.getDate() + '_' + this.myName + '_' + this.title; - } - str = str.replace(/^\s+|\s+$/g, ''); // trim - str = str.toLowerCase(); - - // remove accents, swap ñ for n, etc - var from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;"; - var to = "aaaaeeeeiiiioooouuuunc------"; - for (var i = 0, l = from.length; i < l; i++) { - str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); - } - - str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars - .replace(/\s+/g, '-') // collapse whitespace and replace by - - .replace(/-+/g, '-'); // collapse dashes - - return str; - } - /** * add some days to a date, to compute intervals * @param days @@ -128,39 +95,18 @@ export class ConfigService extends PollConfig { return jsonConfig } - /** - * prepare headers like the charset and json type for any call to the backend - * @param bodyContent - */ - makeHeaders(bodyContent?: any) { - - const headerDict = { - 'Charset': 'UTF-8', - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', - 'Access-Control-Allow-Origin': '*' - }; - - const requestOptions = { - headers: new HttpHeaders(headerDict), - body: bodyContent - }; - - return requestOptions; - } checkIfSlugIsUniqueInDatabase(slug: string = '') { this.customUrlIsUnique = null; if (!slug) { - slug = this.makeSlug(); + slug = this.utils.makeSlug(this); } this.loading = true; // TODO this.todo('check slug is unique'); this.http.get(`${this.baseHref}/check-slug-is-uniq/${slug}`, - this.makeHeaders({slug: this.customUrl}), + this.utils.makeHeaders({slug: this.customUrl}), ) .subscribe((res: any) => { @@ -177,7 +123,6 @@ export class ConfigService extends PollConfig { */ findPollsByEmail(email: string) { - this.findLocalStorageData(); // If no key is found in the localstorage, ask the backend to send an email to the user @@ -187,7 +132,7 @@ export class ConfigService extends PollConfig { this.todo('send email for real : TODO'); this.loading = true; this.http.get(`${this.baseHref}/send-polls-to-user/${this.myEmail}`, - this.makeHeaders(), + this.utils.makeHeaders(), ) .subscribe(res => { // message: 'Trouvé! Allez voir votre boite email', @@ -232,7 +177,7 @@ export class ConfigService extends PollConfig { getPollByURL(url: string) { this.todo(); - return this.http.get(`${this.baseHref}/poll/slug/${url}`, this.makeHeaders()) + return this.http.get(`${this.baseHref}/poll/slug/${url}`, this.utils.makeHeaders()) } /** @@ -244,7 +189,7 @@ export class ConfigService extends PollConfig { return this.http .get(`${this.baseHref}/poll/${id}`, - this.makeHeaders({body: password})) + this.utils.makeHeaders({body: password})) } fetchPollFromRoute(event) { @@ -259,7 +204,7 @@ export class ConfigService extends PollConfig { getMyPolls(ownerEmail: string) { this.http .get(`${this.baseHref}/my-polls`, - this.makeHeaders({ownerEmail: ownerEmail}) + this.utils.makeHeaders({ownerEmail: ownerEmail}) ) .subscribe( (res: any) => { @@ -313,7 +258,7 @@ export class ConfigService extends PollConfig { this.myEmail = voteStack.email; this.voteStackId = voteStack.id; this.myVoteStack = voteStack; - let keys = Object.keys(voteStack.votes) + let keys = Object.keys(voteStack.votes); console.log('voteStack', voteStack); this.resetCurrentChoicesAnswers(); keys.forEach((id: any) => { @@ -326,7 +271,7 @@ export class ConfigService extends PollConfig { let foundChoiceToModify = this.currentPoll.choices.find(choicesItem => { return voteItem.choice_id == choicesItem.id }); - console.log('foundChoiceToModify', foundChoiceToModify) + console.log('foundChoiceToModify', foundChoiceToModify); if (foundChoiceToModify) { foundChoiceToModify.answer = voteItem.value; } @@ -344,7 +289,7 @@ export class ConfigService extends PollConfig { console.log('config', config); return this.http.post(`${this.baseHref}/poll`, config, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { // redirect to the page to administrate the new poll this.messageService.add({severity: 'success', summary: 'Sondage Créé',}); @@ -406,7 +351,7 @@ export class ConfigService extends PollConfig { this.http.post( `${this.baseHref}/poll/${this.pollId}/vote`, voteStack, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { this.handleVoteAdded(res); @@ -439,7 +384,7 @@ export class ConfigService extends PollConfig { this.http.patch( `${this.baseHref}/vote-stack/${voteStack.id}/token/${this.owner_modifier_token}`, voteStack, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { this.messageService.add({severity: 'success', summary: 'Vote mis à jour'}); this.updateCurrentPollFromResponse(res); @@ -468,7 +413,7 @@ export class ConfigService extends PollConfig { this.http.post( `${this.baseHref}/poll/${this.pollId}/comment`, comment, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { this.messageService.add({ severity: 'success', @@ -499,7 +444,7 @@ export class ConfigService extends PollConfig { accept: () => { this.http.delete( `${this.baseHref}/poll/${this.pollId}/comments`, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { this.messageService.add({ severity: 'success', @@ -522,7 +467,7 @@ export class ConfigService extends PollConfig { accept: () => { this.http.delete( `${this.baseHref}/poll/${this.pollId}/votes`, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { this.messageService.add({ severity: 'success', @@ -553,7 +498,7 @@ export class ConfigService extends PollConfig { accept: () => { this.http.delete( `${this.baseHref}/poll/${this.pollId}`, - this.makeHeaders()) + this.utils.makeHeaders()) .subscribe((res: any) => { this.messageService.add({ severity: 'success', @@ -580,7 +525,7 @@ export class ConfigService extends PollConfig { this.http.put( `${this.baseHref}/poll/${this.pollId}`, voteStack, - this.makeHeaders() + this.utils.makeHeaders() ) .subscribe((res: any) => { this.messageService.add({ @@ -644,7 +589,7 @@ export class ConfigService extends PollConfig { rows = [headers, listOfChoices, rows, headersComments, comments]; let convertedCsv = rows.map(elem => { - console.log('elem', elem) + console.log('elem', elem); return elem.map(item => { console.log('item', item); if (typeof item === typeof Array) { @@ -658,11 +603,11 @@ export class ConfigService extends PollConfig { let csvContent = "data:text/csv;charset=utf-8," + convertedCsv; - console.log('csvContent', csvContent) + console.log('csvContent', csvContent); var encodedUri = encodeURI(csvContent); var link = document.createElement("a"); link.setAttribute("href", encodedUri); - let exportFileName = (this.urlPublic ? this.urlPublic : this.makeSlug()) + "_export_" + new Date() + ".csv"; + let exportFileName = (this.urlPublic ? this.urlPublic : this.utils.makeSlug(this)) + "_export_" + new Date() + ".csv"; link.setAttribute("download", exportFileName); document.body.appendChild(link); // Required for FF link.click(); // This will download the data file named "my_data.csv".