import {Component, OnDestroy, OnInit} from '@angular/core'; import {CommonModule} from '@angular/common'; import {RouterOutlet} from '@angular/router'; import {FormsModule} from "@angular/forms"; interface Topic { id: number, // numéro du sujet title: string, // titre du sujet à aborder notes: string, // titre du sujet à aborder duration: number, // nombre de minutes du sujet author: string,// auteur du sujet qui anime la discussion startDate: Date, // date de début du sujet endDate: Date,// date de fin du sujet finished: boolean,// si le sujet est terminé spentSeconds: number, // nombre de secondes passées au sujet } @Component({ selector: 'app-root', standalone: true, imports: [CommonModule, RouterOutlet, FormsModule], templateUrl: './app.component.html', styleUrl: './app.component.scss', }) export class AppComponent implements OnInit, OnDestroy { title = 'odj'; subjects: Array = []; currentSubjectId = 0; startTime: string = "20:30"; // startTime: string = "17:00"; endTime: string = "22:00"; today: any = new Date(); // champs habituels pour le compte rendu: scribe: string = 'tykayn'; private timekeeper: string = 'Chuck Norris'; private animator: string = 'Covid Copperfield'; presents: string = '- tykayn'; pasteLand: string = "* Présentation du suivi sur Nextcloud - 5min (tykayn)\n" + "* Réduction de bus factor - 5min (tykayn)\n" + "* Réunion avec Wikimedia France - 5min (tykayn)\n" + ""; statsExplication: string = '' hints: string = ""; showDebug: boolean = true; startDate: Date = new Date(); endDate: Date = new Date(); private topicChangeDate: Date = new Date(); updateTopicChangeDate(): void { const currentTime = new Date(); const timeDifferenceInMilliseconds = this.topicChangeDate.getTime() - currentTime.getTime(); const timeDifferenceInSeconds = Math.abs(timeDifferenceInMilliseconds / 1000); console.log(`Spent ${timeDifferenceInSeconds} seconds`); this.subjects[this.currentSubjectId].spentSeconds += timeDifferenceInSeconds; this.topicChangeDate = currentTime; this.makeStatisticsOnTopicsSpentSeconds() } round(val: number) { if (val < 60) { return Math.round(val) + ' s' } return (Math.round(val / 60) * 60) + ' min' } parseTheListOfTopicsInPasteLand() { let newTopics: any = [] let topics: any = this.pasteLand.split('*') let accumulatedDuration: number = 0; let ii = 0; topics.forEach((topic: string) => { let boom = topic.split('-') if (boom[0]) { accumulatedDuration += this.findMinutesDurationInDescription(topic) | 0 newTopics.push({ id: ii, title: boom[0], duration: 15, spentSeconds: 0, author: this.findAuthorInDescription(topic), notes: '', finished: false, startDate: this.getStartDateAfterDuration(accumulatedDuration + ''), endDate: this.getEndDateAfterDuration(accumulatedDuration + ''), }) } ii += 1; }) this.subjects = newTopics } /** * Creates a new Date object set to the specified time in the current time zone. * * @param {string} hour - A string representation of the hour, in 24-hour format. * @param {string} [minute='00'] - A string representation of the minute (00-59). * @returns {Date} A new Date object set to the specified time. */ makeDateFromHourToday(hourInput: string, minuteInput: string = '00') { let [hour, minute] = hourInput.split(":"); let date = new Date(); date.setHours(parseInt(hour)); date.setMinutes(minuteInput ? parseInt(minuteInput) : parseInt(minute)); return date; } /** * Computes the remaining time for a given topic, based on the current time and the start and end times of the topic. * * @param {Topic} topic - The topic for which to compute the remaining time. */ computeResteTopicMinutes(topic: Topic): number { let currentSubjectDuration = topic.duration; // The duration of the current subject, in minutes let currentSubjectStartTime = new Date(this.startTime); // The start time of the current subject let currentSubjectEndTime = new Date(currentSubjectStartTime.getTime() + currentSubjectDuration * 60000); // The end time of the current subject return currentSubjectEndTime.getTime() - new Date().getTime(); // The remaining time, in milliseconds } interval: any; ngOnInit() { this.parseTheListOfTopicsInPasteLand() this.startDate = this.makeDateFromHourToday(this.startTime + ''); this.endDate = this.makeDateFromHourToday(this.endTime + ''); } ngOnDestroy(): void { clearInterval(this.interval); } nextSubject() { this.updateTopicChangeDate() if(this.currentSubjectId < this.subjects.length-1) { this.currentSubjectId++ } } findMinutesDurationInDescription(topic: string): number { let durationRegex = /-\s(\d+)min/g; let matches = durationRegex.exec(topic); if (matches) { return parseInt(matches[1]); } return 0; } private getEndDateAfterDuration(accumulatedDuration: string) { return this.makeDateFromHourToday(accumulatedDuration) } private getStartDateAfterDuration(accumulatedDuration: string) { return this.makeDateFromHourToday(accumulatedDuration) } isTopicRunning(topic: Topic) { let now = new Date(); return now >= topic.startDate && now <= topic.endDate && !topic.finished; } countRemainingMinutes(topic: Topic) { let now = new Date(); return Math.floor((topic.endDate.getTime() - now.getTime()) / 60000); } getPercentProgressTimeForTopic(topic: Topic) { let now = new Date(); return Math.floor((now.getTime() - topic.startDate.getTime()) / (topic.endDate.getTime() - topic.startDate.getTime()) * 100); } finishTopic(currentSubjectId: number) { this.subjects[currentSubjectId].finished = true } /** * only had one second to the current topic */ updateProgressEveryPeriod() { this.subjects[this.currentSubjectId].spentSeconds += 1; console.log('ajout au topic', this.currentSubjectId) } resteTopicMinutes(topic: Topic) { return this.round(topic.spentSeconds / topic.duration * 60) + ' min' } convertTextToHTMLLinks(text: string): string { let urlRegex = /(https?:\/\/[^\s<]+[^<.,:;\"\'\]\s])(?!["\])\s])/g; let matches = urlRegex.exec(text); while (matches) { let url = matches[1]; let linkText = url; if (url.length > 30) { linkText = url.substring(0, 30) + "..."; } text = text.replace(url, `${linkText}`); matches = urlRegex.exec(text); } return text; } previousSubject() { this.updateTopicChangeDate() this.currentSubjectId < 1 ? this.currentSubjectId = 0 : this.currentSubjectId -= 1; } makeStatisticsOnTopicsSpentSeconds() { let totalSeconds = 0; for (const topic of this.subjects) { totalSeconds += topic.spentSeconds; } let averageSeconds = totalSeconds / this.subjects.length; let longestTopic = this.subjects[0]; let longestSeconds = longestTopic.spentSeconds; for (const topic of this.subjects) { if (topic.spentSeconds > longestSeconds) { longestSeconds = topic.spentSeconds; longestTopic = topic; } } let shortestTopic = this.subjects[0]; let shortestSeconds = shortestTopic.spentSeconds; for (const topic of this.subjects) { if (topic.spentSeconds < shortestSeconds) { shortestSeconds = topic.spentSeconds; shortestTopic = topic; } } this.statsExplication = `Temps passé: ${this.round(totalSeconds)} Moyenne par sujet: ${this.round(averageSeconds)} Sujet le plus long: ${longestTopic.title.replace('*','')} (${this.round(longestSeconds)} ) Sujet le plus court : ${shortestTopic.title.replace('*','')} (${this.round(shortestSeconds)})`; } setSubject(i: number) { this.currentSubjectId = i; this.updateTopicChangeDate() } compteRendu: string = '' formatDateYMD(date: Date) { const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); return `${year}-${month}-${day}`; } buildCompteRendu() { let compteRendu = ` Compte rendu du ${this.formatDateYMD(this.today)}\n\n`; compteRendu += `début: ${this.startTime}, fin:${this.endTime}.\n`; compteRendu += `Présents:\n${this.formatPresentLines()}.\n`; if (this.scribe) { compteRendu += `Scribe: ${this.scribe}.\n`; } if (this.timekeeper) { compteRendu += `Gardien du temps: ${this.timekeeper}.\n`; } if (this.animator) { compteRendu += `Animation: ${this.animator}.\n`; } compteRendu += `Statistiques:\n ${this.statsExplication}.`; for (const topic of this.subjects) { compteRendu += `* ${topic.title}-`; compteRendu += ` ${topic.duration} min`; compteRendu += ` (${topic.author})\n`; compteRendu += ` ${topic.notes}\n`; if(topic.spentSeconds){ compteRendu += `Temps écoulé : ${this.round(topic.spentSeconds)}\n\n`; } } this.compteRendu = compteRendu return compteRendu } /** * returns each present person for each line with a dash at the beggining if missing */ formatPresentLines(){ let lines = '' if (this.presents) { lines += this.presents.split('\n').map(line => { if(!line.length){ return ''; } if (!line.startsWith('- ')) { return `- ${line}`; } return line; }).join('\n') + '\n' } return lines.trim(); } copyCompteRenduToClipboard() { navigator.clipboard.writeText(this.compteRendu); } private findAuthorInDescription(topic: string): string { let authorRegex = /\(([^)]+)\)/g; let matches = authorRegex.exec(topic); if (matches) { return matches[1]; } return ''; } countLinesInPresent() { return this.presents.split('\n').length; } downloadCompteRendu() { let fichier = new Blob([this.buildCompteRendu()], {type: 'text/plain'}); let lien = document.createElement('a'); lien.setAttribute('href', window.URL.createObjectURL(fichier)); lien.setAttribute('download', 'compte-rendu.txt'); lien.click(); } getMinutesBetweenTwoDates(date1: Date, date2: Date) { return Math.floor((date2.getTime() - date1.getTime()) / 60000); } }