Add support for trainings and small fixes
This commit is contained in:
parent
3f7be4beb8
commit
1f61d2e96a
|
@ -118,7 +118,7 @@ class ServiceController extends Controller
|
||||||
$service->chief()->associate($request->chief);
|
$service->chief()->associate($request->chief);
|
||||||
$service->type()->associate($request->type);
|
$service->type()->associate($request->type);
|
||||||
$service->notes = $request->notes;
|
$service->notes = $request->notes;
|
||||||
$service->start = $request->start/1000; //TODO: fix client-side
|
$service->start = $request->start/1000;
|
||||||
$service->end = $request->end/1000;
|
$service->end = $request->end/1000;
|
||||||
$service->place()->associate($place);
|
$service->place()->associate($place);
|
||||||
$service->addedBy()->associate($request->user());
|
$service->addedBy()->associate($request->user());
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Training;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Utils\Logger;
|
||||||
|
|
||||||
|
class TrainingController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
User::where('id', $request->user()->id)->update(['last_access' => now()]);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
Training::join('users', 'users.id', '=', 'chief_id')
|
||||||
|
->select('trainings.*', 'users.name as chief')
|
||||||
|
->with('crew:name')
|
||||||
|
->orderBy('start', 'desc')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get single Training
|
||||||
|
*/
|
||||||
|
public function show(Request $request, $id)
|
||||||
|
{
|
||||||
|
User::where('id', $request->user()->id)->update(['last_access' => now()]);
|
||||||
|
|
||||||
|
return response()->json(
|
||||||
|
Training::join('users', 'users.id', '=', 'chief_id')
|
||||||
|
->select('trainings.*', 'users.name as chief')
|
||||||
|
->with('crew:name')
|
||||||
|
->find($id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function extractTrainingUsers($training)
|
||||||
|
{
|
||||||
|
$usersList = [$training->chief_id];
|
||||||
|
foreach($training->crew as $crew) {
|
||||||
|
$usersList[] = $crew->id;
|
||||||
|
}
|
||||||
|
return array_unique($usersList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or update Training.
|
||||||
|
*/
|
||||||
|
public function createOrUpdate(Request $request)
|
||||||
|
{
|
||||||
|
$adding = !isset($request->id) || is_null($request->id);
|
||||||
|
|
||||||
|
$training = $adding ? new Training() : Training::where("id",$request->id)->with('crew')->first();
|
||||||
|
|
||||||
|
if(is_null($training)) abort(404);
|
||||||
|
|
||||||
|
if(!$adding) {
|
||||||
|
$usersToDecrement = $this->extractTrainingUsers($training);
|
||||||
|
User::whereIn('id', $usersToDecrement)->decrement('trainings');
|
||||||
|
|
||||||
|
$training->crew()->detach();
|
||||||
|
$training->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$training->name = $request->name;
|
||||||
|
$training->chief()->associate($request->chief);
|
||||||
|
$training->notes = $request->notes;
|
||||||
|
$training->start = $request->start/1000;
|
||||||
|
$training->end = $request->end/1000;
|
||||||
|
$training->place = $request->place;
|
||||||
|
$training->addedBy()->associate($request->user());
|
||||||
|
$training->updatedBy()->associate($request->user());
|
||||||
|
$training->save();
|
||||||
|
|
||||||
|
$training->crew()->attach(array_unique($request->crew));
|
||||||
|
$training->save();
|
||||||
|
|
||||||
|
$usersToIncrement = array_unique(array_merge(
|
||||||
|
[$request->chief],
|
||||||
|
$request->crew
|
||||||
|
));
|
||||||
|
User::whereIn('id', $usersToIncrement)->increment('trainings');
|
||||||
|
|
||||||
|
Logger::log($adding ? "Esercitazione aggiunta" : "Esercitazione modificata");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
$training = Training::find($id);
|
||||||
|
$usersToDecrement = $this->extractTrainingUsers($training);
|
||||||
|
User::whereIn('id', $usersToDecrement)->decrement('trainings');
|
||||||
|
$training->delete();
|
||||||
|
Logger::log("Esercitazione eliminata");
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,12 +82,4 @@ class Service extends Model
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the user that added the service.
|
|
||||||
*/
|
|
||||||
public function deletedBy(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
|
||||||
|
class Training extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that are mass assignable.
|
||||||
|
*
|
||||||
|
* @var array<int, string>
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'place',
|
||||||
|
'notes'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attributes that should be cast.
|
||||||
|
*
|
||||||
|
* @var array<string, string>
|
||||||
|
*/
|
||||||
|
protected $casts = [
|
||||||
|
'start' => 'datetime',
|
||||||
|
'end' => 'datetime'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function chief(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function crew(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(
|
||||||
|
User::class,
|
||||||
|
'trainings_crew',
|
||||||
|
'training_id',
|
||||||
|
'user_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user that added the service.
|
||||||
|
*/
|
||||||
|
public function addedBy(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the user that added the service.
|
||||||
|
*/
|
||||||
|
public function updatedBy(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,7 +51,6 @@ return new class extends Migration
|
||||||
$table->dateTime('end');
|
$table->dateTime('end');
|
||||||
$table->foreignId('added_by_id')->constrained('users');
|
$table->foreignId('added_by_id')->constrained('users');
|
||||||
$table->foreignId('updated_by_id')->constrained('users');
|
$table->foreignId('updated_by_id')->constrained('users');
|
||||||
$table->foreignId('deleted_by_id')->nullable()->constrained('users');
|
|
||||||
$table->softDeletes();
|
$table->softDeletes();
|
||||||
$table->unique(['code']);
|
$table->unique(['code']);
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('trainings', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name');
|
||||||
|
$table->dateTime('start');
|
||||||
|
$table->dateTime('end');
|
||||||
|
$table->foreignId('chief_id')->constrained('users');
|
||||||
|
$table->string('place');
|
||||||
|
$table->string('notes')->nullable();
|
||||||
|
$table->foreignId('added_by_id')->constrained('users');
|
||||||
|
$table->foreignId('updated_by_id')->constrained('users');
|
||||||
|
$table->unique(['name']);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('trainings_crew', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->unsignedBigInteger('user_id')->unsigned();
|
||||||
|
$table->foreign('user_id')
|
||||||
|
->references('id')
|
||||||
|
->on('users')->onDelete('cascade');
|
||||||
|
$table->unsignedBigInteger('training_id')->unsigned();
|
||||||
|
$table->foreign('training_id')
|
||||||
|
->references('id')
|
||||||
|
->on('trainings')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('trainings');
|
||||||
|
}
|
||||||
|
};
|
|
@ -10,6 +10,7 @@ use App\Http\Controllers\TelegramController;
|
||||||
use App\Http\Controllers\ServiceController;
|
use App\Http\Controllers\ServiceController;
|
||||||
use App\Http\Controllers\PlacesController;
|
use App\Http\Controllers\PlacesController;
|
||||||
use App\Http\Controllers\ServiceTypeController;
|
use App\Http\Controllers\ServiceTypeController;
|
||||||
|
use App\Http\Controllers\TrainingController;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
@ -53,6 +54,11 @@ Route::middleware('auth:sanctum')->group( function () {
|
||||||
Route::get('/places/search', [PlacesController::class, 'search']);
|
Route::get('/places/search', [PlacesController::class, 'search']);
|
||||||
Route::get('/places/{id}', [PlacesController::class, 'show']);
|
Route::get('/places/{id}', [PlacesController::class, 'show']);
|
||||||
|
|
||||||
|
Route::get('/trainings', [TrainingController::class, 'index']);
|
||||||
|
Route::post('/trainings', [TrainingController::class, 'createOrUpdate']);
|
||||||
|
Route::get('/trainings/{id}', [TrainingController::class, 'show']);
|
||||||
|
Route::delete('/trainings/{id}', [TrainingController::class, 'destroy']);
|
||||||
|
|
||||||
Route::get('/logs', [LogsController::class, 'index']);
|
Route::get('/logs', [LogsController::class, 'index']);
|
||||||
|
|
||||||
Route::post('/telegram_login_token', [TelegramController::class, 'loginToken']);
|
Route::post('/telegram_login_token', [TelegramController::class, 'loginToken']);
|
||||||
|
|
|
@ -129,14 +129,14 @@
|
||||||
<tr *ngFor="let row of displayedData; index as i">
|
<tr *ngFor="let row of displayedData; index as i">
|
||||||
<td>{{ data.length - (rowsPerPage * (currentPage-1) + i) }}</td>
|
<td>{{ data.length - (rowsPerPage * (currentPage-1) + i) }}</td>
|
||||||
<td>{{ row.name }}</td>
|
<td>{{ row.name }}</td>
|
||||||
<td>{{ row.beginning }}</td>
|
<td>{{ row.start | date:'dd/MM/YYYY, HH:mm' }}</td>
|
||||||
<td>{{ row.end }}</td>
|
<td>{{ row.end | date:'dd/MM/YYYY, HH:mm' }}</td>
|
||||||
<td>{{ row.chief }}</td>
|
<td>{{ row.chief }}</td>
|
||||||
<td>{{ row.crew }}</td>
|
<td>{{ extractNamesFromObject(row.crew).join(', ') }}</td>
|
||||||
<td>{{ row.place }}</td>
|
<td>{{ row.place }}</td>
|
||||||
<td>{{ row.notes }}</td>
|
<td>{{ row.notes }}</td>
|
||||||
<td><i class="fa fa-edit"></i></td>
|
<td (click)="editTraining(row.id)"><i class="fa fa-edit"></i></td>
|
||||||
<td><i class="fa fa-trash"></i></td>
|
<td (click)="deleteTraining(row.id)"><i class="fa fa-trash"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -215,6 +215,38 @@ export class TableComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editTraining(id: number) {
|
||||||
|
this.router.navigate(['/trainings', id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteTraining(id: number) {
|
||||||
|
this.translate.get(['table.yes_remove', 'table.cancel', 'table.remove_training_confirm', 'table.remove_training_text']).subscribe((res: { [key: string]: string; }) => {
|
||||||
|
Swal.fire({
|
||||||
|
title: res['table.remove_training_confirm'],
|
||||||
|
text: res['table.remove_training_confirm_text'],
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#3085d6',
|
||||||
|
cancelButtonColor: '#d33',
|
||||||
|
confirmButtonText: res['table.yes_remove'],
|
||||||
|
cancelButtonText: res['table.cancel']
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
this.api.delete(`trainings/${id}`).then((response) => {
|
||||||
|
this.translate.get('table.training_deleted_successfully').subscribe((res: string) => {
|
||||||
|
this.toastr.success(res);
|
||||||
|
});
|
||||||
|
this.loadTableData();
|
||||||
|
}).catch((e) => {
|
||||||
|
this.translate.get('table.training_deleted_error').subscribe((res: string) => {
|
||||||
|
this.toastr.error(res);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
extractNamesFromObject(obj: any) {
|
extractNamesFromObject(obj: any) {
|
||||||
return obj.flatMap((e: any) => e.name);
|
return obj.flatMap((e: any) => e.name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { EditTrainingComponent } from './edit-training.component';
|
||||||
|
|
||||||
|
const routes: Routes = [{ path: '', component: EditTrainingComponent }];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class EditTrainingRoutingModule { }
|
|
@ -0,0 +1,73 @@
|
||||||
|
<back-btn></back-btn>
|
||||||
|
<br>
|
||||||
|
<form method="post" [formGroup]="trainingForm" (ngSubmit)="formSubmit()">
|
||||||
|
<div class="container">
|
||||||
|
<div class="form-group has-validation">
|
||||||
|
<label for="date-picker-start">{{ 'start'|translate|titlecase }}</label>
|
||||||
|
<datetime-picker formControlName="start" [class.is-invalid]="!isFieldValid('start')" id="date-picker-start"></datetime-picker>
|
||||||
|
<div class="invalid-feedback" *ngIf="start.errors?.['required']" translate>
|
||||||
|
edit_training.select_start_datetime
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-validation">
|
||||||
|
<label for="date-picker-end">{{ 'end'|translate|titlecase }}</label>
|
||||||
|
<datetime-picker formControlName="end" [class.is-invalid]="!isFieldValid('end')" id="date-picker-end"></datetime-picker>
|
||||||
|
<div class="invalid-feedback" *ngIf="end.errors?.['required']" translate>
|
||||||
|
edit_training.select_end_datetime
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-validation">
|
||||||
|
<label for="name">{{ 'name'|translate|titlecase }}</label>
|
||||||
|
<input formControlName="name" [class.is-invalid]="!isFieldValid('name')" id="name" class="form-control"
|
||||||
|
type="text" [placeholder]="'edit_training.name_placeholder'|translate">
|
||||||
|
<div class="invalid-feedback" *ngIf="name.errors?.['required']" translate>
|
||||||
|
edit_training.insert_name
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-validation" [class.is-invalid-div]="!isFieldValid('chief')">
|
||||||
|
<label>{{ 'chief'|translate|titlecase }}</label>
|
||||||
|
<br>
|
||||||
|
<ng-container *ngFor="let user of users">
|
||||||
|
<div class="form-check">
|
||||||
|
<input formControlName="chief" [class.is-invalid]="!isFieldValid('chief')"
|
||||||
|
class="form-check-input" id="chief-{{ user.id }}" type="radio" value='{{ user.id }}'>
|
||||||
|
<label class="form-check-label" for="chief-{{ user.id }}">
|
||||||
|
{{ user.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-validation" [class.is-invalid-div]="!isFieldValid('crew')">
|
||||||
|
<label translate>edit_training.other_crew_members</label>
|
||||||
|
<br>
|
||||||
|
<ng-container *ngFor="let user of users">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" id="crew-{{ user.id }}" [class.is-invalid]="!isFieldValid('crew')"
|
||||||
|
(change)="onCrewCheckboxChange($event)" [checked]="isCrewSelected(user.id)" type="checkbox" value='{{ user.id }}'>
|
||||||
|
<label class="form-check-label" for="crew-{{ user.id }}">
|
||||||
|
{{ user.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div class="form-group has-validation">
|
||||||
|
<label for="place">{{ 'place'|translate|titlecase }}</label>
|
||||||
|
<input formControlName="place" [class.is-invalid]="!isFieldValid('place')" id="place" class="form-control"
|
||||||
|
type="text">
|
||||||
|
<div class="invalid-feedback" *ngIf="place.errors?.['required']" translate>
|
||||||
|
edit_training.insert_place
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="notes">{{ 'notes'|translate|titlecase }}</label><br>
|
||||||
|
<textarea formControlName="notes" class="form-control" id="notes"></textarea>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<button id="submit_button" type="submit" class="btn btn-primary" [disabled]="submittingForm">{{ 'submit'|translate|titlecase }}</button>
|
||||||
|
<button class="btn" type="button" (click)="formReset()" [disabled]="submittingForm">{{ 'reset'|translate|titlecase }}</button>
|
||||||
|
<div class="d-flex justify-content-center mt-2 pt-2 mb-3" *ngIf="submittingForm">
|
||||||
|
<div class="spinner spinner-border"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
|
@ -0,0 +1,9 @@
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.form-check-input[type="checkbox"] {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
}
|
||||||
|
.is-invalid-div {
|
||||||
|
border: 1px solid #dc3545;
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
|
import { ApiClientService } from 'src/app/_services/api-client.service';
|
||||||
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-edit-training',
|
||||||
|
templateUrl: './edit-training.component.html',
|
||||||
|
styleUrls: ['./edit-training.component.scss']
|
||||||
|
})
|
||||||
|
export class EditTrainingComponent implements OnInit {
|
||||||
|
addingTraining = false;
|
||||||
|
trainingId: string | undefined;
|
||||||
|
loadedTraining = {
|
||||||
|
start: '',
|
||||||
|
end: '',
|
||||||
|
name: '',
|
||||||
|
chief: '',
|
||||||
|
crew: [],
|
||||||
|
place: '',
|
||||||
|
notes: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
users: any[] = [];
|
||||||
|
|
||||||
|
trainingForm: any;
|
||||||
|
private formSubmitAttempt: boolean = false;
|
||||||
|
submittingForm = false;
|
||||||
|
|
||||||
|
get start() { return this.trainingForm.get('start'); }
|
||||||
|
get end() { return this.trainingForm.get('end'); }
|
||||||
|
get name() { return this.trainingForm.get('name'); }
|
||||||
|
get chief() { return this.trainingForm.get('chief'); }
|
||||||
|
get crew() { return this.trainingForm.get('crew'); }
|
||||||
|
get place() { return this.trainingForm.get('place'); }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.trainingForm = this.fb.group({
|
||||||
|
start: [this.loadedTraining.start, [Validators.required]],
|
||||||
|
end: [this.loadedTraining.end, [Validators.required]],
|
||||||
|
name: [this.loadedTraining.name, [Validators.required, Validators.minLength(3)]],
|
||||||
|
chief: [this.loadedTraining.chief, [Validators.required]],
|
||||||
|
crew: [this.loadedTraining.crew, [Validators.required]],
|
||||||
|
place: [this.loadedTraining.place, [Validators.required, Validators.minLength(3)]],
|
||||||
|
notes: [this.loadedTraining.notes]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private api: ApiClientService,
|
||||||
|
private toastr: ToastrService,
|
||||||
|
private fb: UntypedFormBuilder,
|
||||||
|
private translate: TranslateService
|
||||||
|
) {
|
||||||
|
this.route.paramMap.subscribe(params => {
|
||||||
|
this.trainingId = params.get('id') || undefined;
|
||||||
|
if (this.trainingId === "new") {
|
||||||
|
this.addingTraining = true;
|
||||||
|
} else {
|
||||||
|
this.api.get(`trainings/${this.trainingId}`).then((training) => {
|
||||||
|
this.loadedTraining = training;
|
||||||
|
|
||||||
|
this.chief.setValue(training.chief_id);
|
||||||
|
|
||||||
|
console.log(training);
|
||||||
|
|
||||||
|
let patch = Object.assign({}, training);
|
||||||
|
patch.start = new Date(patch.start);
|
||||||
|
patch.end = new Date(patch.end);
|
||||||
|
patch.chief = patch.chief_id;
|
||||||
|
patch.crew = patch.crew.map((e: any) => e.pivot.user_id+"");
|
||||||
|
this.trainingForm.patchValue(patch);
|
||||||
|
}).catch((err) => {
|
||||||
|
this.toastr.error("Errore nel caricare l'esercitazione. Ricarica la pagina e riprova.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(this.trainingId);
|
||||||
|
});
|
||||||
|
this.api.get("list").then((users) => {
|
||||||
|
this.users = users;
|
||||||
|
console.log(this.users);
|
||||||
|
}).catch((err) => {
|
||||||
|
this.toastr.error("Errore nel caricare la lista degli utenti. Ricarica la pagina e riprova.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCrewCheckboxChange(event: any) {
|
||||||
|
if (event.target.checked) {
|
||||||
|
this.crew.setValue([...this.crew.value, event.target.value]);
|
||||||
|
} else {
|
||||||
|
this.crew.setValue(this.crew.value.filter((x: number) => x !== event.target.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isCrewSelected(id: number) {
|
||||||
|
return this.crew.value.find((x: number) => x == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//https://loiane.com/2017/08/angular-reactive-forms-trigger-validation-on-submit/
|
||||||
|
isFieldValid(field: string) {
|
||||||
|
return this.formSubmitAttempt ? this.trainingForm.get(field).valid : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
formSubmit() {
|
||||||
|
console.log("form values", this.trainingForm.value);
|
||||||
|
this.formSubmitAttempt = true;
|
||||||
|
if (this.trainingForm.valid) {
|
||||||
|
this.submittingForm = true;
|
||||||
|
let values = Object.assign({}, this.trainingForm.value);
|
||||||
|
values.start = values.start.getTime();
|
||||||
|
values.end = values.end.getTime();
|
||||||
|
console.log(values);
|
||||||
|
if (this.trainingId !== "new") {
|
||||||
|
values.id = this.trainingId;
|
||||||
|
this.api.post("trainings", values).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
this.translate.get('edit_training.training_added_successfully').subscribe((res: string) => {
|
||||||
|
this.toastr.success(res);
|
||||||
|
});
|
||||||
|
this.submittingForm = false;
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.translate.get('edit_training.training_add_failed').subscribe((res: string) => {
|
||||||
|
this.toastr.error(res);
|
||||||
|
});
|
||||||
|
this.submittingForm = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.api.post("trainings", values).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
this.translate.get('edit_training.training_added_successfully').subscribe((res: string) => {
|
||||||
|
this.toastr.success(res);
|
||||||
|
});
|
||||||
|
this.submittingForm = false;
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
this.translate.get('edit_training.training_add_failed').subscribe((res: string) => {
|
||||||
|
this.toastr.error(res);
|
||||||
|
});
|
||||||
|
this.submittingForm = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formReset() {
|
||||||
|
this.formSubmitAttempt = false;
|
||||||
|
this.trainingForm.reset();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
|
||||||
|
import { MapPickerModule } from '../../_components/map-picker/map-picker.module';
|
||||||
|
import { DatetimePickerModule } from '../../_components/datetime-picker/datetime-picker.module';
|
||||||
|
import { BackBtnModule } from '../../_components/back-btn/back-btn.module';
|
||||||
|
import { TranslationModule } from '../../translation.module';
|
||||||
|
|
||||||
|
import { EditTrainingRoutingModule } from './edit-training-routing.module';
|
||||||
|
import { EditTrainingComponent } from './edit-training.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
EditTrainingComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
EditTrainingRoutingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
BsDatepickerModule.forRoot(),
|
||||||
|
MapPickerModule,
|
||||||
|
DatetimePickerModule,
|
||||||
|
BackBtnModule,
|
||||||
|
TranslationModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class EditTrainingModule { }
|
|
@ -1,8 +1,5 @@
|
||||||
<owner-image></owner-image>
|
<owner-image></owner-image>
|
||||||
<div *ngIf="false" class="text-center mb-4">
|
<div class="text-center mb-4">
|
||||||
<button type="button" class="btn btn-primary" disabled>{{ 'add'|translate|titlecase }} {{ 'training'|translate }}</button>
|
<button type="button" class="btn btn-primary" (click)="addTraining()">{{ 'add'|translate|titlecase }} {{ 'training'|translate }}</button>
|
||||||
</div>
|
|
||||||
<app-table *ngIf="false" [sourceType]="'trainings'" [refreshInterval]="1200000"></app-table>
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
Questa sezione di AllertaVVF è stata temporaneamente disabilitata per motivi di manutenzione. Ci scusiamo per il disagio.
|
|
||||||
</div>
|
</div>
|
||||||
|
<app-table [sourceType]="'trainings'" [refreshInterval]="1200000"></app-table>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-trainings',
|
selector: 'app-trainings',
|
||||||
|
@ -7,9 +8,13 @@ import { Component, OnInit } from '@angular/core';
|
||||||
})
|
})
|
||||||
export class TrainingsComponent implements OnInit {
|
export class TrainingsComponent implements OnInit {
|
||||||
|
|
||||||
constructor() { }
|
constructor(private router: Router) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addTraining() {
|
||||||
|
this.router.navigate(['trainings', 'new']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,11 @@ const routes: Routes = [
|
||||||
canActivate: [AuthorizeGuard]
|
canActivate: [AuthorizeGuard]
|
||||||
},
|
},
|
||||||
{ path: 'trainings', component: TrainingsComponent, canActivate: [AuthorizeGuard] },
|
{ path: 'trainings', component: TrainingsComponent, canActivate: [AuthorizeGuard] },
|
||||||
|
{
|
||||||
|
path: 'trainings/:id',
|
||||||
|
loadChildren: () => import('./_routes/edit-training/edit-training.module').then(m => m.EditTrainingModule),
|
||||||
|
canActivate: [AuthorizeGuard]
|
||||||
|
},
|
||||||
{ path: "login/:redirect/:extraParam", component: LoginComponent },
|
{ path: "login/:redirect/:extraParam", component: LoginComponent },
|
||||||
{ path: "login/:redirect", component: LoginComponent },
|
{ path: "login/:redirect", component: LoginComponent },
|
||||||
//
|
//
|
||||||
|
|
|
@ -56,7 +56,16 @@
|
||||||
"type_already_exists": "Type already exists",
|
"type_already_exists": "Type already exists",
|
||||||
"type_added_successfully": "Type added successfully",
|
"type_added_successfully": "Type added successfully",
|
||||||
"service_added_successfully": "Service added successfully",
|
"service_added_successfully": "Service added successfully",
|
||||||
"service_add_error": "Service could not be added. Please try again"
|
"service_add_failed": "Service could not be added. Please try again"
|
||||||
|
},
|
||||||
|
"edit_training": {
|
||||||
|
"select_start_datetime": "Select start date and time for the training",
|
||||||
|
"select_end_datetime": "Select end date and time for the training",
|
||||||
|
"insert_name": "Insert training name",
|
||||||
|
"name_placeholder": "Training name",
|
||||||
|
"other_crew_members": "Other crew members",
|
||||||
|
"training_added_successfully": "Training added successfully",
|
||||||
|
"training_add_failed": "Training could not be added. Please try again"
|
||||||
},
|
},
|
||||||
"update_availability_schedule": "Update availability schedule",
|
"update_availability_schedule": "Update availability schedule",
|
||||||
"save_changes": "Save changes",
|
"save_changes": "Save changes",
|
||||||
|
|
|
@ -56,7 +56,16 @@
|
||||||
"type_already_exists": "La tipologia è già presente",
|
"type_already_exists": "La tipologia è già presente",
|
||||||
"type_added_successfully": "Tipologia aggiunta con successo",
|
"type_added_successfully": "Tipologia aggiunta con successo",
|
||||||
"service_added_successfully": "Intervento aggiunto con successo",
|
"service_added_successfully": "Intervento aggiunto con successo",
|
||||||
"service_add_error": "Errore durante l'aggiunta dell'intervento. Riprovare più tardi"
|
"service_add_failed": "Errore durante l'aggiunta dell'intervento. Riprovare più tardi"
|
||||||
|
},
|
||||||
|
"edit_training": {
|
||||||
|
"select_start_datetime": "Seleziona data e ora di inizio dell'esercitazione",
|
||||||
|
"select_end_datetime": "Seleziona data e ora di fine dell'esercitazione",
|
||||||
|
"insert_name": "Inserisci il nome dell'esercitazione",
|
||||||
|
"name_placeholder": "Esercitazione di gennaio",
|
||||||
|
"other_crew_members": "Altri membri della squadra",
|
||||||
|
"training_added_successfully": "Esercitazione aggiunta con successo",
|
||||||
|
"training_add_failed": "Errore durante l'aggiunta dell'esercitazione. Riprovare più tardi"
|
||||||
},
|
},
|
||||||
"update_availability_schedule": "Aggiorna programmazione disponibilità",
|
"update_availability_schedule": "Aggiorna programmazione disponibilità",
|
||||||
"save_changes": "Salva modifiche",
|
"save_changes": "Salva modifiche",
|
||||||
|
|
Loading…
Reference in New Issue