8 Commits

Author SHA1 Message Date
Vincent BENOIT
af19713b75 mise en prod 2023-01-03 13:56:26 +01:00
Vincent BENOIT
aab82c4dfb correction bug affichage au chargement de la date et heure sur la page infos 2023-01-03 13:54:29 +01:00
Vincent BENOIT
0747e09dd8 nouvelle prod 2023-01-03 11:43:15 +01:00
Vincent BENOIT
707f528c76 recuperation de la date et l'heure du système 2023-01-03 11:30:14 +01:00
d5c4538d21 compilation du projet en prod 2023-01-02 19:26:46 +01:00
ffab4b4efd modification du numéro de version 2023-01-02 19:20:06 +01:00
05a51f0a12 correction de bugs pour l'affichage ou la maj de la date et heure 2023-01-02 19:16:48 +01:00
83941978f3 ajout de la page de configuration de la date et l'heure du système 2023-01-02 17:45:31 +01:00
22 changed files with 6866 additions and 3143 deletions

View File

@@ -1 +1 @@
1.0.0
1.1.0

9669
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,9 @@
"font-awesome": "^4.7.0",
"jquery": "^3.6.1",
"material-icons": "^1.12.0",
"moment": "^2.29.4",
"ngx-cookie-service": "^13.2.0",
"ngx-mat-timepicker": "^13.2.6",
"ngx-toastr": "^14.3.0",
"rxjs": "~7.5.0",
"sha.js": "^2.4.11",

View File

@@ -369,9 +369,38 @@ PERFORMANCE OF THIS SOFTWARE.
moment
MIT
Copyright (c) JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
ngx-cookie-service
MIT
ngx-mat-timepicker
MIT
ngx-toastr
MIT
The MIT License (MIT)
@@ -681,6 +710,31 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
ts-luxon
MIT
MIT License
Copyright (c) 2022 JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
tslib
0BSD
Copyright (c) Microsoft Corporation.

View File

@@ -10,6 +10,6 @@
<style>html,body{height:100%}body{margin:0;font-family:Roboto,Helvetica Neue,sans-serif;overflow-y:hidden}</style><link rel="stylesheet" href="styles.fb868c61ba42478e.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.fb868c61ba42478e.css"></noscript></head>
<body>
<app-root></app-root>
<script src="runtime.f7ad02de45306a03.js" type="module"></script><script src="polyfills.a75a17ebd1634711.js" type="module"></script><script src="scripts.a05f8004f1b507ea.js" defer></script><script src="main.a082ff41f0003d73.js" type="module"></script>
<script src="runtime.740b9dcf0eb8a18e.js" type="module"></script><script src="polyfills.32d2eb6d01836962.js" type="module"></script><script src="scripts.a05f8004f1b507ea.js" defer></script><script src="main.4a488aa862984ceb.js" type="module"></script>
</body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
(()=>{"use strict";var e,v={},d={};function n(e){var a=d[e];if(void 0!==a)return a.exports;var r=d[e]={id:e,loaded:!1,exports:{}};return v[e].call(r.exports,r,r.exports,n),r.loaded=!0,r.exports}n.m=v,e=[],n.O=(a,r,c,t)=>{if(!r){var o=1/0;for(f=0;f<e.length;f++){for(var[r,c,t]=e[f],u=!0,l=0;l<r.length;l++)(!1&t||o>=t)&&Object.keys(n.O).every(p=>n.O[p](r[l]))?r.splice(l--,1):(u=!1,t<o&&(o=t));if(u){e.splice(f--,1);var s=c();void 0!==s&&(a=s)}}return a}t=t||0;for(var f=e.length;f>0&&e[f-1][2]>t;f--)e[f]=e[f-1];e[f]=[r,c,t]},n.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return n.d(a,{a}),a},n.d=(e,a)=>{for(var r in a)n.o(a,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:a[r]})},n.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),n.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e={666:0};n.O.j=c=>0===e[c];var a=(c,t)=>{var l,s,[f,o,u]=t,i=0;if(f.some(h=>0!==e[h])){for(l in o)n.o(o,l)&&(n.m[l]=o[l]);if(u)var _=u(n)}for(c&&c(t);i<f.length;i++)n.o(e,s=f[i])&&e[s]&&e[s][0](),e[s]=0;return n.O(_)},r=self.webpackChunkKine_frontend=self.webpackChunkKine_frontend||[];r.forEach(a.bind(null,0)),r.push=a.bind(null,r.push.bind(r))})()})();

View File

@@ -1 +0,0 @@
(()=>{"use strict";var e,_={},d={};function a(e){var n=d[e];if(void 0!==n)return n.exports;var r=d[e]={exports:{}};return _[e](r,r.exports,a),r.exports}a.m=_,e=[],a.O=(n,r,u,t)=>{if(!r){var c=1/0;for(f=0;f<e.length;f++){for(var[r,u,t]=e[f],s=!0,l=0;l<r.length;l++)(!1&t||c>=t)&&Object.keys(a.O).every(h=>a.O[h](r[l]))?r.splice(l--,1):(s=!1,t<c&&(c=t));if(s){e.splice(f--,1);var o=u();void 0!==o&&(n=o)}}return n}t=t||0;for(var f=e.length;f>0&&e[f-1][2]>t;f--)e[f]=e[f-1];e[f]=[r,u,t]},a.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return a.d(n,{a:n}),n},a.d=(e,n)=>{for(var r in n)a.o(n,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},a.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e={666:0};a.O.j=u=>0===e[u];var n=(u,t)=>{var l,o,[f,c,s]=t,v=0;if(f.some(b=>0!==e[b])){for(l in c)a.o(c,l)&&(a.m[l]=c[l]);if(s)var i=s(a)}for(u&&u(t);v<f.length;v++)a.o(e,o=f[v])&&e[o]&&e[o][0](),e[o]=0;return a.O(i)},r=self.webpackChunkKine_frontend=self.webpackChunkKine_frontend||[];r.forEach(n.bind(null,0)),r.push=n.bind(null,r.push.bind(r))})()})();

View File

@@ -5,6 +5,7 @@ import { LoginComponent } from './components/login/login.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { AccountComponent } from './components/home/workspace/account/account.component';
import { ParametresComponent } from './components/home/workspace/parametres/parametres.component';
import { DatetimeComponent } from './components/home/workspace/datetime/datetime.component';
import { InfosComponent } from './components/home/workspace/infos/infos.component';
import { HoursComponent } from './components/home/workspace/hours/hours.component';
import { LogsComponent } from './components/home/workspace/logs/logs.component';
@@ -28,6 +29,10 @@ const routes: Routes = [
path:"parameters",
component:ParametresComponent
},
{
path:"datetime",
component:DatetimeComponent
},
{
path:"infos",
component:InfosComponent

View File

@@ -31,8 +31,11 @@ import { MatGridListModule } from '@angular/material/grid-list';
import { MatTabsModule } from '@angular/material/tabs';
import { MatSliderModule } from '@angular/material/slider';
import { MatRadioModule } from '@angular/material/radio';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatNativeDateModule, MAT_DATE_LOCALE } from '@angular/material/core';
import { ToastrModule } from 'ngx-toastr';
import { CodeInputModule } from 'angular-code-input';
import { NgxMatTimepickerModule } from 'ngx-mat-timepicker';
import { HttpXsrfInterceptorService } from './interceptors/http-xsrf-interceptor/http-xsrf-interceptor.service';
import { AppRoutingModule } from './app-routing.module';
@@ -47,6 +50,7 @@ import { InfosComponent } from './components/home/workspace/infos/infos.componen
import { HoursComponent } from './components/home/workspace/hours/hours.component';
import { LogsComponent } from './components/home/workspace/logs/logs.component';
import { ConfirmComponent } from './components/dialog/confirm/confirm.component';
import { DatetimeComponent } from './components/home/workspace/datetime/datetime.component';
@NgModule({
declarations: [
@@ -60,7 +64,8 @@ import { ConfirmComponent } from './components/dialog/confirm/confirm.component'
InfosComponent,
HoursComponent,
LogsComponent,
ConfirmComponent
ConfirmComponent,
DatetimeComponent
],
imports: [
BrowserModule,
@@ -95,14 +100,18 @@ import { ConfirmComponent } from './components/dialog/confirm/confirm.component'
MatTabsModule,
MatSliderModule,
MatRadioModule,
MatDatepickerModule,
MatNativeDateModule,
FlexLayoutModule,
RouterModule,
FontAwesomeModule,
ToastrModule.forRoot(),
NgxMatTimepickerModule.setLocale('fr-FR'),
CodeInputModule
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptorService, multi: true }
{ provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptorService, multi: true },
{ provide: MAT_DATE_LOCALE, useValue: 'fr-FR' }
],
bootstrap: [AppComponent]
})

View File

@@ -9,6 +9,7 @@
<!-- The following menu items will be hidden on both SM and XS screen sizes -->
<a href="/compte" mat-button>Compte</a>
<a href="/parameters" mat-button>Paramètres</a>
<a href="/datetime" mat-button>Date & Heure</a>
<a href="/hours" mat-button>Horaires</a>
<a href="/infos" mat-button>Informations</a>
<a href="/logs" mat-button>Logs</a>
@@ -29,6 +30,10 @@
<mat-icon>build</mat-icon>
<span> Paramètres</span>
</button>
<button mat-button routerLink="/datetime" class="menu-button">
<mat-icon>update</mat-icon>
<span> Date & Heure</span>
</button>
<button mat-button routerLink="/hours" class="menu-button">
<mat-icon>calendar_today</mat-icon>
<span> Horaires</span>

View File

@@ -0,0 +1,24 @@
.container {
margin-bottom: 100px;
overflow-x: hidden;
overflow-y: auto;
}
.process {
width: 100%;
height: 75%;
}
.form-field {
width: 80%;
margin-bottom: 2rem;
}
.ngx-timepicker-field {
width: 80%;
margin-bottom: 2rem;
}
button {
width: 80%;
}

View File

@@ -0,0 +1,31 @@
<div *ngIf="isProcessing" class="process"
fxLayout="column"
fxLayoutAlign="space-around center">
<mat-spinner></mat-spinner>
</div>
<div *ngIf="!isProcessing" class="container"
fxLayout="column"
fxLayoutAlign="space-around center">
<mat-form-field appearance="fill" class="form-field">
<input matInput (dateChange)="dateChanged($event)" [matDatepicker]="datepicker" placeholder="Choisissez une date">
<mat-hint>DD/MM/YYYY</mat-hint>
<mat-datepicker-toggle matSuffix [for]="datepicker">
</mat-datepicker-toggle>
<mat-datepicker #datepicker>
<mat-datepicker-actions>
<button mat-button matDatepickerCancel>Annuler</button>
<button mat-raised-button color="primary" matDatepickerApply>Appliquer</button>
</mat-datepicker-actions>
</mat-datepicker>
</mat-form-field>
<mat-form-field appearance="fill" class="form-field">
<input matInput name="selected_time_B" [format]="24" [ngxMatTimepicker]="pickerB" placeholder="Choisissez une heure" readonly />
<mat-hint>HH:MM</mat-hint>
<mat-icon matSuffix (click)="pickerB.open()">
watch_later
</mat-icon>
</mat-form-field>
<ngx-mat-timepicker color="primary" (timeChanged)="timeChanged($event)" #pickerB></ngx-mat-timepicker>
<button mat-raised-button color="primary" [disabled]="isDisabled" (click)="onUpdate()">{{buttonText}}</button>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DatetimeComponent } from './datetime.component';
describe('DatetimeComponent', () => {
let component: DatetimeComponent;
let fixture: ComponentFixture<DatetimeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DatetimeComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DatetimeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,79 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
import { BackendService } from '../../../../services/backend/backend.service';
import { ToastrService } from 'ngx-toastr';
import * as moment from 'moment';
@Component({
selector: 'app-datetime',
templateUrl: './datetime.component.html',
styleUrls: ['./datetime.component.css']
})
export class DatetimeComponent implements OnInit {
isProcessing:boolean = true;
isDisabled:boolean = true;
timeLeft:number = 0;
buttonText:string = "Mise à jour";
interval:any;
time:string = "";
date:string = "";
constructor(private bs:BackendService,
private router:Router,
private toast:ToastrService) { }
ngOnInit(): void {
this.isProcessing = false;
}
timeChanged(time:string): void {
console.log("Nouvelle heure: ", time);
this.time = time;
if(this.time && this.date) {
this.isDisabled = false;
}
}
dateChanged(event:MatDatepickerInputEvent<Date>): void {
var date = moment(event.value);
this.date = date.format("YYYY-MM-DD").toString();
if(this.time && this.date) {
this.isDisabled = false;
}
}
setTimeoutDisabledButton(timeout:number): void {
this.timeLeft = timeout;
this.isDisabled = true;
this.buttonText = "Mise à jour" + " ... (" + this.timeLeft + ")";
this.interval = setInterval(() => {
if(this.timeLeft > 0) {
this.timeLeft--;
this.buttonText = "Mise à jour" + " ... (" + this.timeLeft + ")";
} else {
this.isDisabled = false;
this.buttonText = "Mise à jour";
clearInterval(this.interval);
}
},1000)
}
onUpdate(): void {
this.setTimeoutDisabledButton(5);
var newDateTime:string = this.date + " " + this.time + ":00";
console.log("Nouvelle date et heure: ", newDateTime);
this.bs.updateDatetime({'datetime':newDateTime}).subscribe(
data => {
this.toast.success("Mise à jour de la date et heure réussie");
}, err => {
if(err.status == 401) {
this.router.navigateByUrl("/login");
} else {
this.toast.error(err.error.description);
}
}
);
}
}

View File

@@ -8,6 +8,16 @@
fxLayoutAlign="space-around center">
<mat-card class="info-card">
<mat-card-content class="info-content">
<div fxLayout="row" style="width:100%; margin-bottom: 0.25rem;">
<span fxFlex="50" style="display: grid; align-items: center;"><b>Date:</b></span>
<!--<div style="display: grid; align-items: center;">{{today | date:'dd/MM/yyyy HH:mm:ss'}}</div>-->
<div style="display: grid; align-items: center;">{{dateStr}}</div>
</div>
<div fxLayout="row" style="width:100%; margin-bottom: 0.25rem;">
<span fxFlex="50" style="display: grid; align-items: center;"><b>Horloge matérielle:</b></span>
<div *ngIf="RTCisAlive" style="display: grid; align-items: center;"><mat-icon style="color: green;">check_circle</mat-icon></div>
<div *ngIf="!RTCisAlive" style="display: grid; align-items: center;"><mat-icon style="color: red;">cancel</mat-icon></div>
</div>
<div fxLayout="row" style="width:100%; margin-bottom: 0.25rem;">
<span fxFlex="50" style="display: grid; align-items: center;"><b>Processus InterCOM:</b></span>
<div *ngIf="isAlive" style="display: grid; align-items: center;"><mat-icon style="color: green;">check_circle</mat-icon></div>

View File

@@ -4,6 +4,7 @@ import { MatDialog } from '@angular/material/dialog';
import { ConfirmComponent } from '../../../dialog/confirm/confirm.component';
import { BackendService } from '../../../../services/backend/backend.service';
import { Info } from '../../../../models/info.model';
import { Datetime } from '../../../../models/datetime.model';
import { ToastrService } from 'ngx-toastr';
@Component({
@@ -19,6 +20,9 @@ export class InfosComponent implements OnInit {
qos_color:string = "green";
isProcessing:boolean = true;
isAlive:boolean = false;
RTCisAlive:boolean = false;
today:number;
dateStr:string;
constructor( private bs:BackendService,
private router:Router,
@@ -26,6 +30,31 @@ export class InfosComponent implements OnInit {
private toast:ToastrService ) { }
ngOnInit(): void {
this.bs.retreiveDatetime().subscribe(
(data:Datetime) => {
this.dateStr = data.today;
}, err => {
if(err.status == 401) {
this.router.navigateByUrl("/login");
} else {
this.toast.error(err.error.description);
}
}
);
setInterval(() => {
this.bs.retreiveDatetime().subscribe(
(data:Datetime) => {
this.dateStr = data.today;
}, err => {
if(err.status == 401) {
this.router.navigateByUrl("/login");
} else {
this.toast.error(err.error.description);
}
}
);
}, 60000);
this.bs.retreiveInfos().subscribe(
(data:Info) => {
this.infos = data;
@@ -60,7 +89,18 @@ export class InfosComponent implements OnInit {
this.bs.processAlive().subscribe(
data => {
this.isAlive = data['alive'];
this.isProcessing = false;
this.bs.RTCAlive().subscribe(
data => {
this.RTCisAlive = data['RTCalive'];
this.isProcessing = false;
}, err => {
if(err.status == 401) {
this.router.navigateByUrl("/login");
} else {
this.toast.error(err.error.description);
}
}
);
}, err => {
if(err.status == 401) {
this.router.navigateByUrl("/login");
@@ -68,7 +108,7 @@ export class InfosComponent implements OnInit {
this.toast.error(err.error.description);
}
}
)
);
}, err => {
if(err.status == 401) {
this.router.navigateByUrl("/login");

View File

@@ -0,0 +1,3 @@
export interface Datetime {
today:string;
}

View File

@@ -5,6 +5,7 @@ import { environment } from '../../../environments/environment';
import { Utilisateur } from '../../models/utilisateur.model';
import { Parameters } from '../../models/parameters.model';
import { Scheduler } from '../../models/scheduler.model';
import { Datetime } from '../../models/datetime.model';
import { Log } from '../../models/log.model';
import { Info } from '../../models/info.model';
@@ -182,4 +183,38 @@ export class BackendService {
};
return this.http.post<any>(host+"/api/configurateur/shutdown", {}, options);
}
updateDatetime(val:any):Observable<any> {
let host=environment.host;
const options = {
headers: new HttpHeaders({
'Content-Type' : 'application/json',
}),
withCredentials: true
};
return this.http.post<any>(host+"/api/configurateur/update_datetime", val, options);
}
RTCAlive():Observable<any> {
let host=environment.host;
const options = {
headers: new HttpHeaders({
'Content-Type' : 'application/json',
}),
withCredentials: true
};
return this.http.get<any>(host+"/api/configurateur/rtc_alive", options);
}
retreiveDatetime():Observable<Datetime> {
let host=environment.host;
const options = {
headers: new HttpHeaders({
'Content-Type' : 'application/json',
}),
withCredentials: true
};
return this.http.get<Datetime>(host+"/api/configurateur/datetime", options);
}
}