Merge pull request #427 from allerta-vvf/master

Add manual mode
This commit is contained in:
Matteo Gheza 2022-01-06 22:48:02 +01:00 committed by GitHub
commit d7b6258f43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 373 additions and 22 deletions

View File

@ -207,11 +207,13 @@ function apiRouter (FastRoute\RouteCollector $r) {
global $users, $db;
requireLogin() || accessDenied();
$users->online_time_update();
$row = $db->selectRow(
"SELECT `available`, `manual_mode` FROM `".DB_PREFIX."_profiles` WHERE `id` = ?",
[$users->auth->getUserId()]
);
apiResponse([
"available" => $db->selectValue(
"SELECT `available` FROM `".DB_PREFIX."_profiles` WHERE `id` = ?",
[$users->auth->getUserId()]
)
"available" => $row["available"],
"manual_mode" => $row["manual_mode"]
]);
}
);
@ -227,12 +229,31 @@ function apiRouter (FastRoute\RouteCollector $r) {
}
$user_id = is_numeric($_POST["id"]) ? $_POST["id"] : $users->auth->getUserId();
apiResponse([
"response" => $availability->change($_POST["available"], $user_id),
"response" => $availability->change($_POST["available"], $user_id, true),
"updated_user" => $user_id,
"updated_user_name" => $users->getName($user_id)
]);
}
);
$r->addRoute(
"POST",
"/manual_mode",
function ($vars) {
global $users, $db;
requireLogin() || accessDenied();
$users->online_time_update();
$db->update(
DB_PREFIX."_profiles",
[
"manual_mode" => $_POST["manual_mode"]
],
[
"id" => $users->auth->getUserId()
]
);
apiResponse(["status" => "success"]);
}
);
$r->addRoute(
['GET'],
@ -258,6 +279,29 @@ function apiRouter (FastRoute\RouteCollector $r) {
}
);
$r->addRoute(
['GET'],
'/service_types',
function ($vars) {
global $users, $db;
requireLogin() || accessDenied();
$users->online_time_update();
$response = $db->select("SELECT * FROM `".DB_PREFIX."_type`");
apiResponse(is_null($response) ? [] : $response);
}
);
$r->addRoute(
['POST'],
'/service_types',
function ($vars) {
global $users, $db;
requireLogin() || accessDenied();
$users->online_time_update();
$response = $db->insert(DB_PREFIX."_type", ["name" => $_POST["name"]]);
apiResponse($response);
}
);
$r->addRoute(
['POST'],
'/telegram_login_token',

View File

@ -108,17 +108,14 @@ function job_schedule_availability() {
"minutes" => (int) date("i")
];
$manual_mode = $db->select("SELECT manual_mode FROM `".DB_PREFIX."_profiles` WHERE `id` = ?", [$user_id]);
if(
!$manual_mode &&
$schedule["day"] == $now["day"] &&
$schedule["hour"] == $now["hour"] &&
$schedule["minutes"] <= $now["minutes"] &&
$now["minutes"] - $schedule["minutes"] <= 30
){
$availability_last_change = $db->select("SELECT availability_last_change FROM `".DB_PREFIX."_profiles` WHERE `id` = ?", [$user_id]);
if($availability_last_change === "manual" && $last_exec["day"] === $now["day"]){
break;
}
if(!in_array($user_id,$schedules_users)) $schedules_users[] = $user_id;
if(is_null($last_exec) || (is_array($last_exec) && $schedule["hour"] == $last_exec["hour"] ? $schedule["minutes"] !== $last_exec["minutes"] : true)/* && !in_array(date('Y-m-d'), $selected_holidays_dates)*/){
$last_exec_new = $schedule["day"].";".sprintf("%02d", $schedule["hour"]).":".sprintf("%02d", $schedule["minutes"]);
@ -127,7 +124,7 @@ function job_schedule_availability() {
["last_exec" => $last_exec_new],
["id" => $id]
);
$availability->change(1, $user_id, "cron");
$availability->change(1, $user_id, false);
$schedules_check["schedules"][] = [
"schedule" => $schedule,
"now" => $now,
@ -143,7 +140,7 @@ function job_schedule_availability() {
$profiles = $db->select("SELECT id FROM `".DB_PREFIX."_profiles`");
foreach ($profiles as $profile) {
if(!in_array($profile["id"],$schedules_users)){
$availability->change(0, $profile["id"], "cron");
$availability->change(0, $profile["id"], false);
}
}
$output = $schedules_check;

View File

@ -162,7 +162,7 @@ function telegramBotRouter() {
requireBotLogin($message);
if(count(explode(" ", $message->text)) > 3) return;
$user_id = getUserIdByMessage($message);
$availability->change(1, $user_id);
$availability->change(1, $user_id, true);
$Bot->sendMessage($message->from->id, "Disponibilità aggiorata con successo.\nOra sei <b>operativo</b>.");
});
@ -171,7 +171,7 @@ function telegramBotRouter() {
requireBotLogin($message);
if(count(explode(" ", $message->text)) > 4) return;
$user_id = getUserIdByMessage($message);
$availability->change(0, $user_id);
$availability->change(0, $user_id, true);
$Bot->sendMessage($message->from->id, "Disponibilità aggiorata con successo.\nOra sei <b>non operativo</b>.");
});

View File

@ -261,13 +261,16 @@ class Availability {
$this->users = $users;
}
public function change($availability, $user_id, $change_type="manual")
public function change($availability, $user_id, $is_manual_mode=true)
{
if($change_type === "manual") logger("Disponibilità cambiata in ".($availability ? '"disponibile"' : '"non disponibile"'), $user_id, $this->users->auth->getUserId());
if($is_manual_mode) logger("Disponibilità cambiata in ".($availability ? '"disponibile"' : '"non disponibile"'), $user_id, $this->users->auth->getUserId());
$change_values = ["available" => $availability];
if($is_manual_mode) $change_values["manual_mode"] = 1;
$response = $this->db->update(
DB_PREFIX."_profiles",
["available" => $availability, 'availability_last_change' => $change_type],
$change_values,
["id" => $user_id]
);

View File

@ -17,10 +17,13 @@
"@angular/platform-browser-dynamic": "~13.0.0",
"@angular/router": "~13.0.0",
"@angular/service-worker": "~13.0.0",
"@asymmetrik/ngx-leaflet": "^8.1.0",
"@fortawesome/fontawesome-free": "^5.15.4",
"@ng-bootstrap/ng-bootstrap": "11.0.0",
"bootstrap": "^5.1.3",
"jwt-decode": "^3.1.2",
"leaflet": "^1.7.1",
"leaflet.locatecontrol": "^0.76.0",
"ngx-bootstrap": "^7.1.2",
"ngx-toastr": "^14.2.1",
"rxjs": "~7.4.0",
@ -32,6 +35,7 @@
"@angular/cli": "~13.0.3",
"@angular/compiler-cli": "~13.0.0",
"@types/jasmine": "~3.10.0",
"@types/leaflet": "^1.7.8",
"@types/node": "16.11.17",
"jasmine-core": "4.0.0",
"karma": "~6.3.0",
@ -622,6 +626,17 @@
"integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==",
"dev": true
},
"node_modules/@asymmetrik/ngx-leaflet": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@asymmetrik/ngx-leaflet/-/ngx-leaflet-8.1.0.tgz",
"integrity": "sha512-lq7LduBP/vXcaSEmKnx7mzCR8WsoYqh9pB6BNnq53yeCwsqRbG3GdKye1/i8VvoRzjDsmQBPQsIFZ9uclXrtgg==",
"peerDependencies": {
"@angular/common": ">=10",
"@angular/core": ">=10",
"leaflet": "1",
"tslib": "2"
}
},
"node_modules/@babel/code-frame": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz",
@ -2665,6 +2680,12 @@
"integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
"dev": true
},
"node_modules/@types/geojson": {
"version": "7946.0.8",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
"integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==",
"dev": true
},
"node_modules/@types/http-proxy": {
"version": "1.17.8",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
@ -2686,6 +2707,15 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true
},
"node_modules/@types/leaflet": {
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.8.tgz",
"integrity": "sha512-prZwhmUznkwTYCTZVGTR4U1GzgPP3PAWYYQ3wDgVkIoiuQTheWoycsXx4Rz9ARYhlDTl0Ycd8lvhH2/rNSkqIg==",
"dev": true,
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/node": {
"version": "16.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.17.tgz",
@ -7119,6 +7149,16 @@
"node": ">= 8"
}
},
"node_modules/leaflet": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz",
"integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
},
"node_modules/leaflet.locatecontrol": {
"version": "0.76.0",
"resolved": "https://registry.npmjs.org/leaflet.locatecontrol/-/leaflet.locatecontrol-0.76.0.tgz",
"integrity": "sha512-Mx8uiihBi8KrrW3LgblsNL/pS8HR0gj60m8VFDFrnhSvDuitChazc095XcMSscf/XqZW+TSqQMCTe+AUy/4/eA=="
},
"node_modules/less": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz",
@ -12979,6 +13019,12 @@
"integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==",
"dev": true
},
"@asymmetrik/ngx-leaflet": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/@asymmetrik/ngx-leaflet/-/ngx-leaflet-8.1.0.tgz",
"integrity": "sha512-lq7LduBP/vXcaSEmKnx7mzCR8WsoYqh9pB6BNnq53yeCwsqRbG3GdKye1/i8VvoRzjDsmQBPQsIFZ9uclXrtgg==",
"requires": {}
},
"@babel/code-frame": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz",
@ -14453,6 +14499,12 @@
"integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==",
"dev": true
},
"@types/geojson": {
"version": "7946.0.8",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
"integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==",
"dev": true
},
"@types/http-proxy": {
"version": "1.17.8",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.8.tgz",
@ -14474,6 +14526,15 @@
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==",
"dev": true
},
"@types/leaflet": {
"version": "1.7.8",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.7.8.tgz",
"integrity": "sha512-prZwhmUznkwTYCTZVGTR4U1GzgPP3PAWYYQ3wDgVkIoiuQTheWoycsXx4Rz9ARYhlDTl0Ycd8lvhH2/rNSkqIg==",
"dev": true,
"requires": {
"@types/geojson": "*"
}
},
"@types/node": {
"version": "16.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.17.tgz",
@ -17838,6 +17899,16 @@
"integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==",
"dev": true
},
"leaflet": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.7.1.tgz",
"integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
},
"leaflet.locatecontrol": {
"version": "0.76.0",
"resolved": "https://registry.npmjs.org/leaflet.locatecontrol/-/leaflet.locatecontrol-0.76.0.tgz",
"integrity": "sha512-Mx8uiihBi8KrrW3LgblsNL/pS8HR0gj60m8VFDFrnhSvDuitChazc095XcMSscf/XqZW+TSqQMCTe+AUy/4/eA=="
},
"less": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/less/-/less-4.1.2.tgz",

View File

@ -21,10 +21,13 @@
"@angular/platform-browser-dynamic": "~13.0.0",
"@angular/router": "~13.0.0",
"@angular/service-worker": "~13.0.0",
"@asymmetrik/ngx-leaflet": "^8.1.0",
"@fortawesome/fontawesome-free": "^5.15.4",
"@ng-bootstrap/ng-bootstrap": "11.0.0",
"bootstrap": "^5.1.3",
"jwt-decode": "^3.1.2",
"leaflet": "^1.7.1",
"leaflet.locatecontrol": "^0.76.0",
"ngx-bootstrap": "^7.1.2",
"ngx-toastr": "^14.2.1",
"rxjs": "~7.4.0",
@ -36,6 +39,7 @@
"@angular/cli": "~13.0.3",
"@angular/compiler-cli": "~13.0.0",
"@types/jasmine": "~3.10.0",
"@types/leaflet": "^1.7.8",
"@types/node": "16.11.17",
"jasmine-core": "4.0.0",
"karma": "~6.3.0",

View File

@ -0,0 +1,89 @@
<form method="post">
<div class="container">
<div class="form-group">
<label for="date-picker">Giorno</label>
<input id="date-picker" type="text" placeholder="Premi per selezionare una data" class="form-control" bsDatepicker [bsConfig]="{ adaptivePosition: true }">
</div>
<div class="form-group">
<label for="progressivo">Progressivo</label>
<input id="progressivo" class="form-control" type="text" placeholder="1234/5" required>
</div>
<div class="form-group">
<label for="timePicker1">Ora inizio</label>
<input id="timePicker1" class="form-control" type="time" required>
</div>
<div class="form-group">
<label for="timePicker2">Ora fine</label>
<input id="timePicker2" class="form-control" type="time" required>
</div>
<div class="form-group">
<label>Caposquadra</label>
<br>
<ng-container *ngFor="let user of users">
<div class="form-check" *ngIf="user.chief">
<input class="form-check-input chief-{{ user.id }}" type="checkbox" value='{{ user.id }}'>
<label class="form-check-label" for="chief-{{ user.id }}">
{{ user.name }}
</label>
</div>
</ng-container>
</div>
<div class="form-group">
<label>Autisti</label>
<br>
<ng-container *ngFor="let user of users">
<div class="form-check" *ngIf="user.driver">
<input class="form-check-input driver-{{ user.id }}" type="checkbox" value='{{ user.id }}'>
<label class="form-check-label" for="driver-{{ user.id }}">
{{ user.name }}
</label>
</div>
</ng-container>
</div>
<div class="form-group">
<label>Altri membri della squadra</label>
<br>
<ng-container *ngFor="let user of users">
<div class="form-check" *ngIf="!user.chief && !user.driver">
<input class="form-check-input crew-{{ user.id }}" type="checkbox" value='{{ user.id }}'>
<label class="form-check-label" for="crew-{{ user.id }}">
{{ user.name }}
</label>
</div>
</ng-container>
</div>
<label>Luogo dell'intervento</label>
<div id="map" style="height: 300px;" leaflet [leafletOptions]="options" (leafletMapReady)="mapReady($event)"></div>
<div id="search" class="mt-2">
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Luogo">
<button class="btn btn-outline-secondary" type="button">Cerca</button>
</div>
<div id="results"></div>
</div>
<div class="form-group">
<label for="notes">Note (es. altre informazioni)</label><br>
<textarea class="form-control" id="notes"></textarea>
</div>
<br>
<div class="form-group">
<label>Tipologia</label>
<br>
<div class="input-group">
<select class="form-control mr-2">
<option selected disabled>Seleziona tipologia..</option>
<option *ngFor="let type of types" value="{{ type.id }}">{{ type.name }}</option>
</select>
<button class="btn btn-outline-secondary" type="button" tabindex="-1" (click)="addingType = true">
Aggiungi
</button>
</div>
<div class="input-group mb-2 mt-2" *ngIf="addingType">
<input type="text" class="form-control" placeholder="Nome della tipologia" [(ngModel)]="newType" [ngModelOptions]="{standalone: true}">
<button class="btn btn-secondary" type="button" (click)="addType()">Invia</button>
</div>
</div>
<br>
<button id="submit_button" type="submit" class="btn btn-primary">Invia</button>
</div>
</form>

View File

@ -0,0 +1,6 @@
.form-group {
margin-bottom: 1em;
}
.form-check-input[type="checkbox"] {
margin-top: 0.5em;
}

View File

@ -0,0 +1,77 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ApiClientService } from 'src/app/_services/api-client.service';
import { ToastrService } from 'ngx-toastr';
import { latLng, tileLayer } from 'leaflet';
import "leaflet.locatecontrol";
@Component({
selector: 'app-edit-service',
templateUrl: './edit-service.component.html',
styleUrls: ['./edit-service.component.scss']
})
export class EditServiceComponent implements OnInit {
public serviceId: string | undefined;
public users: any[] = [];
public types: any[] = [];
public addingType = false;
public newType = "";
public options = {
layers: [
tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' })
],
zoom: 5,
center: latLng(46.879966, -121.726909)
};
constructor(
private route: ActivatedRoute,
private api: ApiClientService,
private toastr: ToastrService
) {
this.route.paramMap.subscribe(params => {
this.serviceId = params.get('id') || undefined;
console.log(this.serviceId);
});
this.api.get("users").then((users) => {
this.users = users;
console.log(this.users);
});
this.loadTypes();
}
loadTypes() {
this.api.get("service_types").then((types) => {
console.log(types);
this.types = types;
});
}
ngOnInit(): void { }
addType() {
if(this.newType.length < 2) {
this.toastr.error("Il nome della tipologia deve essere lungo almeno 2 caratteri");
return;
}
if(this.types.find(t => t.name == this.newType)) {
this.toastr.error("Il nome della tipologia è già in uso");
return;
}
this.api.post("service_types", {
name: this.newType
}).then((type) => {
this.addingType = false;
this.newType = "";
console.log(type);
if(type === 1) this.toastr.success("Tipologia di servizio aggiunta con successo.");
this.loadTypes();
});
}
mapReady(map: any) {
console.log(map);
(window as any).L.control.locate().addTo(map);
}
}

View File

@ -1,9 +1,18 @@
<div class="text-center">
<p>Sei disponibile in caso di allerta?</p>
<h3 *ngIf="available !== undefined">Attualmente sei: <b>{{ available ? "Disponibile" : "Non disponibile" }}{{ manual_mode ? "" : " (programmato)" }}</b></h3>
<div id="availability-btn-group">
<button (click)="changeAvailibility(1)" type="button" [delay]="1000" tooltip="Cambia la tua disponibilità in 'attivo'" id="activate-btn" class="btn btn-lg btn-success me-1">Attiva</button>
<button (click)="changeAvailibility(0)" type="button" [delay]="1000" tooltip="Cambia la tua disponibilità in 'non attivo'" id="deactivate-btn" class="btn btn-lg btn-danger">Disattiva</button>
</div>
<ng-container *ngIf="manual_mode !== undefined">
<button type="button" class="btn btn-secondary" *ngIf="manual_mode" (click)="updateManualMode(0)">
Attiva programmazione oraria
</button>
<button type="button" class="btn btn-secondary" *ngIf="!manual_mode" (click)="updateManualMode(1)">
Disattiva programmazione oraria
</button>
<br>
</ng-container>
<button type="button" class="btn btn-lg" (click)="openScheduleModal()">
Modifica orari disponibilità
</button>

View File

@ -1,4 +1,4 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { TableComponent } from '../table/table.component';
import { ModalAvailabilityScheduleComponent } from '../modal-availability-schedule/modal-availability-schedule.component';
import { ApiClientService } from 'src/app/_services/api-client.service';
@ -15,12 +15,27 @@ export class ListComponent implements OnInit {
scheduleModalRef?: BsModalRef;
@ViewChild('table') table!: TableComponent;
public loadAvailabilityInterval: NodeJS.Timer | undefined = undefined;
public available: boolean | undefined = undefined;
public manual_mode: boolean | undefined = undefined;
constructor(
private api: ApiClientService,
private auth: AuthService,
private toastr: ToastrService,
private modalService: BsModalService
) {}
) {
this.loadAvailability();
}
loadAvailability() {
this.api.get("availability").then((response) => {
this.available = response.available;
this.manual_mode = response.manual_mode;
console.log(this.available, this.manual_mode);
});
}
changeAvailibility(available: 0|1, id?: number|undefined) {
this.api.post("availability", {
@ -30,15 +45,35 @@ export class ListComponent implements OnInit {
let changed_user_msg = parseInt(response.updated_user) === parseInt(this.auth.profile.auth_user_id) ? "La tua disponibilità" : `La disponibilità di ${response.updated_user_name}`;
let msg = available === 1 ? `${changed_user_msg} è stata impostata con successo.` : `${changed_user_msg} è stata rimossa con successo.`;
this.toastr.success(msg);
this.loadAvailability();
this.table.loadTableData();
});
}
updateManualMode(manual_mode: 0|1) {
this.api.post("manual_mode", {
manual_mode: manual_mode
}).then((response) => {
this.toastr.success("Modalità manuale aggiornata con successo.");
this.loadAvailability();
});
}
openScheduleModal() {
this.scheduleModalRef = this.modalService.show(ModalAvailabilityScheduleComponent, Object.assign({}, { class: 'modal-custom' }));
}
ngOnInit(): void {
this.loadAvailabilityInterval = setInterval(() => {
console.log("Refreshing availability...");
this.loadAvailability();
}, 10000);
}
ngOnDestroy(): void {
if(typeof this.loadAvailabilityInterval !== 'undefined') {
clearInterval(this.loadAvailabilityInterval);
}
}
requestTelegramToken() {

View File

@ -1,5 +1,5 @@
<owner-image></owner-image>
<div class="text-center mb-4">
<button type="button" class="btn btn-primary" disabled>Aggiungi intervento</button>
<button type="button" class="btn btn-primary" (click)="addService()" disabled>Aggiungi intervento</button>
</div>
<app-table [sourceType]="'services'"></app-table>

View File

@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-services',
@ -7,9 +8,13 @@ import { Component, OnInit } from '@angular/core';
})
export class ServicesComponent implements OnInit {
constructor() { }
constructor(private router: Router) { }
ngOnInit(): void {
}
addService() {
this.router.navigate(['services', 'new']);
}
}

View File

@ -4,6 +4,7 @@ import { RouterModule, Routes } from '@angular/router';
import { ListComponent } from './_components/list/list.component';
import { LogsComponent } from './_components/logs/logs.component';
import { ServicesComponent } from './_components/services/services.component';
//import { EditServiceComponent } from './_components/edit-service/edit-service.component';
import { TrainingsComponent } from './_components/trainings/trainings.component';
import { AuthorizeGuard } from './_guards/authorize.guard';
@ -13,6 +14,7 @@ const routes: Routes = [
{ path: 'list', component: ListComponent, canActivate: [AuthorizeGuard] },
{ path: 'logs', component: LogsComponent, canActivate: [AuthorizeGuard] },
{ path: 'services', component: ServicesComponent, canActivate: [AuthorizeGuard] },
//{ path: 'services/:id', component: EditServiceComponent, canActivate: [AuthorizeGuard] },
{ path: 'trainings', component: TrainingsComponent, canActivate: [AuthorizeGuard] },
{ path: "login/:redirect/:extraParam", component: LoginComponent },
{ path: "login/:redirect", component: LoginComponent },

View File

@ -6,6 +6,8 @@ import { FormsModule } from '@angular/forms';
import { ToastrModule } from 'ngx-toastr';
import { ModalModule } from 'ngx-bootstrap/modal';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { LeafletModule } from '@asymmetrik/ngx-leaflet';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@ -21,6 +23,7 @@ import { LoginComponent } from './_components/login/login.component';
import { ListComponent } from './_components/list/list.component';
import { LogsComponent } from './_components/logs/logs.component';
import { ServicesComponent } from './_components/services/services.component';
//import { EditServiceComponent } from './_components/edit-service/edit-service.component';
import { TrainingsComponent } from './_components/trainings/trainings.component';
import { UnauthorizedInterceptor } from './_providers/unauthorized-interceptor.provider';
@ -38,6 +41,7 @@ import { UnauthorizedInterceptor } from './_providers/unauthorized-interceptor.p
ListComponent,
LogsComponent,
ServicesComponent,
//EditServiceComponent,
TrainingsComponent
],
imports: [
@ -54,6 +58,8 @@ import { UnauthorizedInterceptor } from './_providers/unauthorized-interceptor.p
}),
ModalModule.forRoot(),
TooltipModule.forRoot(),
BsDatepickerModule.forRoot(),
//LeafletModule,
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: false && environment.production,
// Register the ServiceWorker as soon as the app is stable

View File

@ -3,6 +3,9 @@
@import "~bootstrap/scss/bootstrap.scss";
@import "~@fortawesome/fontawesome-free/css/all.css";
@import '~ngx-toastr/toastr';
@import '~ngx-bootstrap/datepicker/bs-datepicker.scss';
//@import '~leaflet/dist/leaflet.css';
//@import '~leaflet.locatecontrol/dist/L.Control.Locate.min.css';
.fa {
vertical-align: middle;