Implement alerts on the backend

This commit is contained in:
Matteo Gheza 2022-03-11 22:04:47 +01:00
parent 409cfa2865
commit a1d4c04bd3
10 changed files with 192 additions and 34 deletions

105
backend/alerts.php Normal file
View File

@ -0,0 +1,105 @@
<?php
require_once 'utils.php';
function alertsRouter (FastRoute\RouteCollector $r) {
$r->addRoute(
'GET',
'',
function ($vars) {
global $db;
$alerts = $db->select("SELECT * FROM `".DB_PREFIX."_alerts`");
if(is_null($alerts)) $alerts = [];
foreach($alerts as &$alert) {
if(isset($_GET["load_less"])) {
$alert = [
"id" => $alert["id"],
"created_at" => $alert["created_at"]
];
} else {
$alert["crew"] = json_decode($alert["crew"], true);
}
}
apiResponse($alerts);
}
);
$r->addRoute(
'POST',
'',
function ($vars) {
global $db;
$crew = [
[
"name" => "Nome1",
"response" => "waiting"
],
[
"name" => "Nome2",
"response" => true
],
[
"name" => "Nome3",
"response" => false
]
];
$db->insert(
DB_PREFIX."_alerts",
[
"crew" => json_encode($crew),
"type" => $_POST["type"],
"created_at" => get_timestamp()
]
);
apiResponse([
"crew" => $crew,
"id" => $db->getLastInsertId()
]);
}
);
$r->addRoute(
'GET',
'/{id:\d+}',
function ($vars) {
global $db;
$alert = $db->selectRow("SELECT * FROM `".DB_PREFIX."_alerts` WHERE `id` = :id", [":id" => $vars["id"]]);
if(is_null($alert)) {
apiResponse(["error" => "alert not found"]);
return;
}
$alert["crew"] = json_decode($alert["crew"], true);
apiResponse($alert);
}
);
$r->addRoute(
'POST',
'/{id:\d+}/settings',
function ($vars) {
global $db;
$db->update(
DB_PREFIX."_alerts",
[
"notes" => $_POST["notes"]
],
[
"id" => $vars["id"]
]
);
}
);
$r->addRoute(
'DELETE',
'/{id:\d+}',
function ($vars) {
global $db;
$db->delete(
DB_PREFIX."_alerts",
[
"id" => $vars["id"]
]
);
}
);
}

View File

@ -2,12 +2,17 @@
require_once 'utils.php';
require_once 'telegramBotRouter.php';
require_once 'cronRouter.php';
require_once 'alerts.php';
function apiRouter (FastRoute\RouteCollector $r) {
$r->addGroup('/cron', function (FastRoute\RouteCollector $r) {
cronRouter($r);
});
$r->addGroup('/alerts', function (FastRoute\RouteCollector $r) {
alertsRouter($r);
});
$r->addRoute(
['GET', 'POST'],
'/bot/telegram',

View File

@ -91,6 +91,10 @@ final class Role
}
function get_timestamp() {
return round(microtime(true) * 1000);
}
function logger($action, $changed=null, $editor=null, $timestamp=null, $source_type="api")
{
global $db, $users;

View File

@ -10,23 +10,9 @@ import Swal from 'sweetalert2';
styleUrls: ['./modal-alert.component.scss']
})
export class ModalAlertComponent implements OnInit, OnDestroy {
type = "full";
id = 0;
users = [
{
name: "Nome1",
response: "waiting"
},
{
name: "Nome2",
response: true
},
{
name: "Nome3",
response: false
},
];
users: { name: string, response: string|boolean }[] = [];
isAdvancedCollapsed = true;
loadDataInterval: NodeJS.Timer | undefined = undefined;
@ -36,9 +22,12 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
constructor(public bsModalRef: BsModalRef, private api: ApiClientService, private toastr: ToastrService) { }
loadResponsesData() {
this.api.get(`alert/${this.id}`).then((response) => {
this.api.get(`alerts/${this.id}`).then((response) => {
console.log(response);
this.users = response.users;
this.users = response.crew;
if (response.notes !== "" && response.notes !== null) {
this.notes = response.notes;
}
});
}
@ -61,7 +50,7 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
}
saveAlertSettings() {
this.api.post(`alert/${this.id}/settings`, {
this.api.post(`alerts/${this.id}/settings`, {
notes: this.notes
}).then((response) => {
this.toastr.success("Impostazioni salvate con successo");
@ -80,9 +69,10 @@ export class ModalAlertComponent implements OnInit, OnDestroy {
cancelButtonText: "Annulla"
}).then((result: any) => {
if (result.isConfirmed) {
this.api.delete(`alert/${this.id}`).then((response) => {
this.api.delete(`alerts/${this.id}`).then((response) => {
console.log(response);
this.bsModalRef.hide();
this.api.alertsChanged.next();
/*
this.translate.get('table.service_deleted_successfully').subscribe((res: string) => {
this.toastr.success(res);

View File

@ -24,7 +24,7 @@
🚒 Richiedi squadra completa
</button>
<button type="button" class="btn btn-warning" (click)="addAlertSupport()">
Richiedi squadra di assistenza 🧯
Richiedi squadra di supporto 🧯
</button>
</div>
</div>

View File

@ -4,7 +4,7 @@ import { ModalAvailabilityScheduleComponent } from '../../_components/modal-avai
import { ModalAlertComponent } from 'src/app/_components/modal-alert/modal-alert.component';
import { ApiClientService } from 'src/app/_services/api-client.service';
import { ToastrService } from 'ngx-toastr';
import { BsModalService, BsModalRef, ModalOptions } from 'ngx-bootstrap/modal';
import { BsModalService, BsModalRef } from 'ngx-bootstrap/modal';
import { TranslateService } from '@ngx-translate/core';
import { AuthService } from 'src/app/_services/auth.service';
@ -13,7 +13,7 @@ import { AuthService } from 'src/app/_services/auth.service';
templateUrl: './list.component.html',
styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {
export class ListComponent implements OnInit, OnDestroy {
scheduleModalRef?: BsModalRef;
alertModalRef?: BsModalRef;
@ViewChild('table') table!: TableComponent;
@ -72,28 +72,28 @@ export class ListComponent implements OnInit {
}
addAlertFull() {
this.api.post("alert", {
alert_type: "full"
this.api.post("alerts", {
type: "full"
}).then((response) => {
this.alertModalRef = this.modalService.show(ModalAlertComponent, {
initialState: {
type: "full",
id: response.id
}
});
this.api.alertsChanged.next();
});
}
addAlertSupport() {
this.api.post("alert", {
alert_type: "support"
this.api.post("alerts", {
type: "support"
}).then((response) => {
this.alertModalRef = this.modalService.show(ModalAlertComponent, {
initialState: {
type: "support",
id: response.id
}
});
this.api.alertsChanged.next();
});
}

View File

@ -1,11 +1,13 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Subject } from "rxjs";
@Injectable({
providedIn: 'root'
})
export class ApiClientService {
private apiRoot = 'api/';
public alertsChanged = new Subject<void>();
constructor(private http: HttpClient) { }

View File

@ -7,11 +7,28 @@
<a class="icon" id="menuButton" (click)="menuButtonClicked = !menuButtonClicked"></a>
</div>
<div class="d-flex justify-content-center mt-4 pt-4 mb-3" *ngIf="loadingRoute">
<div class="spinner spinner-border"></div>
</div>
<div class="container">
<div class="d-flex justify-content-center mt-4 pt-4 mb-3" *ngIf="loadingRoute">
<div class="spinner spinner-border"></div>
</div>
<router-outlet></router-outlet>
<alert type="danger" *ngIf="alerts.length > 0">
<strong>Attenzione!</strong> Allertamento in corso.<br>
<ng-container *ngIf="alerts.length == 1">
Emergenza attuale: <a (click)="openAlert(alerts[0]['id'])"><b>{{ alerts[0]["created_at"] | date:'dd/MM/YYYY, HH:mm:ss' }}</b> (premi per ulteriori informazioni)</a>
</ng-container>
<ng-container *ngIf="alerts.length > 1">
Emergenze attuali:
<ul>
<li *ngFor="let alert of alerts">
<a (click)="openAlert(alert['id'])"><b>{{ alert["created_at"] | date:'dd/MM/YYYY, HH:mm:ss' }}</b> (premi per ulteriori informazioni)</a>
</li>
</ul>
</ng-container>
</alert>
<router-outlet></router-outlet>
</div>
<div id="footer" class="footer text-center p-3">
{{ 'footer_text' | translate }}<br>

View File

@ -3,6 +3,9 @@ import { AuthService } from './_services/auth.service';
import { LocationBackService } from 'src/app/_services/locationBack.service';
import { versions } from 'src/environments/versions';
import { Router, RouteConfigLoadStart, RouteConfigLoadEnd } from '@angular/router';
import { ApiClientService } from './_services/api-client.service';
import { ModalAlertComponent } from 'src/app/_components/modal-alert/modal-alert.component';
import { BsModalService } from 'ngx-bootstrap/modal';
@Component({
selector: 'app-root',
@ -14,15 +17,27 @@ export class AppComponent {
public revision_datetime_string;
public versions = versions;
public loadingRoute = false;
private loadAlertsInterval: NodeJS.Timer | undefined = undefined;
public alerts = [];
constructor(
public auth: AuthService,
private locationBackService: LocationBackService,
private router: Router
private router: Router,
private api: ApiClientService,
private modalService: BsModalService
) {
this.revision_datetime_string = new Date(versions.revision_timestamp).toLocaleString(undefined, { day: '2-digit', month: '2-digit', year: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' });
}
loadAlerts() {
if(this.auth.profile) {
this.api.get("alerts").then((response) => {
this.alerts = response;
});
}
}
ngOnInit () {
this.router.events.subscribe((event) => {
if (event instanceof RouteConfigLoadStart) {
@ -31,5 +46,23 @@ export class AppComponent {
this.loadingRoute = false;
}
});
this.loadAlertsInterval = setInterval(() => {
console.log("Refreshing alerts...");
this.loadAlerts();
}, 15000);
this.loadAlerts();
this.api.alertsChanged.subscribe(() => {
this.loadAlerts();
});
}
openAlert(id: number) {
this.modalService.show(ModalAlertComponent, {
initialState: {
id: id
}
});
}
}

View File

@ -10,6 +10,7 @@ import { ToastrModule } from 'ngx-toastr';
import { ModalModule } from 'ngx-bootstrap/modal';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { CollapseModule } from 'ngx-bootstrap/collapse';
import { AlertModule } from 'ngx-bootstrap/alert';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@ -61,6 +62,7 @@ import { AuthInterceptor } from './_providers/auth-interceptor.provider';
ModalModule.forRoot(),
TooltipModule.forRoot(),
CollapseModule.forRoot(),
AlertModule.forRoot(),
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: false && environment.production,
// Register the ServiceWorker as soon as the app is stable