Add edit-user page WIP

This commit is contained in:
Matteo Gheza 2024-01-07 02:02:50 +01:00
parent 80b4c2ac91
commit dbc50da95a
18 changed files with 766 additions and 13 deletions

View File

@ -0,0 +1,46 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\File;
use App\Models\DocumentFile;
class DocumentsController extends Controller
{
public function uploadDrivingLicenseScan(Request $request)
{
$request->validate([
'file' => [
'required',
File::image()
->max(5 * 1024)
]
]);
$fileName = time() . '_' . $request->file->getClientOriginalName();
$filePath = $request->file('file')->storeAs('driving_license_scans', $fileName, 'public');
$uuid = Str::uuid()->toString();
$document = new DocumentFile();
$document->uuid = $uuid;
$document->type = 'driving_license';
$document->file_path = $filePath;
$document->uploadedBy()->associate(auth()->user());
$document->save();
return response()->json([
"uuid" => $document->uuid
]);
}
public function serveDrivingLicenseScan($uuid)
{
//TODO: check if the user has access to the document
$document = DocumentFile::where('uuid', $uuid)->firstOrFail();
return response()->file(storage_path('app/public/' . $document->file_path));
}
}

View File

@ -3,8 +3,11 @@
namespace App\Http\Controllers;
use App\Models\User;
use App\Models\Document;
use App\Models\DocumentFile;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\URL;
class UserController extends Controller
{
@ -37,6 +40,8 @@ class UserController extends Controller
unset($user->last_access);
}
//TODO: support for more data selections, follow user permissions, do not share information that should not be shared to that user etc. see notes
return response()->json($list);
}
@ -51,11 +56,23 @@ class UserController extends Controller
/**
* Display the specified resource.
*/
public function show(Request $request, $id)
public function show(Request $request, User $user)
{
User::where('id', $request->user()->id)->update(['last_access' => now()]);
return response()->json(User::findOrFail($id));
//TODO: do not display useless or hidden info to the user about documentFile (like filePath and id) but only the url (see below)
$user->driving_license = Document::where('added_by', $user->id)
->where('type', 'driving_license')
->with('documentFile')
->first();
if(!is_null($user->driving_license) && !is_null($user->driving_license->documentFile)) {
$user->driving_license->documentFile->url = URL::temporarySignedRoute(
'driving_license_scan_serve', now()->addMinutes(2), ['uuid' => $user->driving_license->documentFile->uuid]
);
}
return response()->json($user);
}
/**
@ -63,7 +80,87 @@ class UserController extends Controller
*/
public function update(Request $request, User $user)
{
//
$request->validate([
'name' => 'required|string|max:255',
'surname' => 'string|max:255',
'username' => 'required|string|max:255|unique:users,username,' . $user->id,
'birthday' => 'date',
'birthplace' => 'string|max:255',
'birthplace_province' => 'string|max:255',
'ssn' => 'string|max:255',
'course_date' => 'date',
'driver' => 'required|boolean',
'chief' => 'required|boolean',
'banned' => 'boolean',
'hidden' => 'boolean',
'driving_license' => 'array',
'driving_license.number' => 'alpha_num|max:255',
'driving_license.type' => 'string|max:255',
'driving_license.expiration_date' => 'date',
'driving_license.scan' => 'string|max:255',
'address' => 'string|max:255',
'address_zip_code' => 'integer|max:255',
'phone_number' => 'string|max:255',
'email' => 'string|max:255',
'suit_size' => 'string|max:255',
'boot_size' => 'string|max:255'
]);
/*
//TODO: new user permissions
if($request->user()->isAbleTo("users-update")) {
$user->update($request->all());
} else {
$user->update($request->except(['chief', 'driver', 'banned', 'hidden']));
}
*/
$user->update($request->all());
if($request->has('birthday')) {
$user->birthday = \Carbon\Carbon::parse($request->birthday);
$user->save();
}
if($request->has('course_date')) {
$user->course_date = \Carbon\Carbon::parse($request->course_date);
$user->save();
}
//Check if driving license is present
if($request->has('driving_license')) {
$drivingLicense = Document::where('added_by', $user->id)
->where('type', 'driving_license')
->first();
if(is_null($drivingLicense)) {
$drivingLicense = new Document();
$drivingLicense->added_by = $user->id;
$drivingLicense->type = 'driving_license';
}
if ($request->has('driving_license.number')) {
$drivingLicense->doc_number = $request->driving_license['number'];
}
if ($request->has('driving_license.type')) {
$drivingLicense->doc_type = $request->driving_license['type'];
}
if ($request->has('driving_license.expiration_date')) {
$drivingLicense->expiration_date = \Carbon\Carbon::parse($request->driving_license['expiration_date']);
}
if($request->has('driving_license.scan') && !is_null($request->driving_license['scan'])) {
$documentFile = DocumentFile::where('uuid', $request->driving_license['scan'])->first();
//Ensure that the document file exists
if(is_null($documentFile)) {
return response()->json([
'message' => 'Document file not found'
], Response::HTTP_NOT_FOUND);
}
$drivingLicense->documentFile()->associate($documentFile);
}
$drivingLicense->save();
}
return response()->json($user);
}
/**

View File

@ -0,0 +1,42 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Document extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'type',
'doc_number',
'doc_type'
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'expiration_date' => 'datetime'
];
public function addedBy(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function documentFile(): BelongsTo
{
return $this->belongsTo(DocumentFile::class);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class DocumentFile extends Model
{
use HasFactory;
public $timestamps = ["created_at"];
const UPDATED_AT = null;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'uuid',
'type',
'file_path'
];
public function uploadedBy(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@ -41,7 +41,10 @@ class User extends Authenticatable implements LaratrustUser
'birthplace',
'birthplace_province',
'ssn',
'address'
'address',
'address_zip_code',
'suit_size',
'boot_size'
];
/**

View File

@ -17,6 +17,9 @@ return new class extends Migration
$table->string('birthplace_province')->nullable()->after('birthplace');
$table->string('ssn')->nullable()->after('birthplace_province');
$table->string('address')->nullable()->after('ssn');
$table->string('address_zip_code')->nullable()->after('address');
$table->string('suit_size')->nullable()->after('address_zip_code');
$table->string('boot_size')->nullable()->after('suit_size');
$table->timestamp('birthday')->nullable()->after('last_availability_change');
$table->timestamp('course_date')->nullable()->after('birthday');
});
@ -33,6 +36,9 @@ return new class extends Migration
$table->dropColumn('birthplace_province');
$table->dropColumn('ssn');
$table->dropColumn('address');
$table->dropColumn('address_zip_code');
$table->dropColumn('suit_size');
$table->dropColumn('boot_size');
$table->dropColumn('birthday');
$table->dropColumn('course_date');
});

View File

@ -0,0 +1,42 @@
<?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('document_files', function (Blueprint $table) {
$table->id();
$table->string('uuid');
$table->string('type');
$table->string('file_path');
$table->foreignId('uploaded_by_id')->constrained('users');
$table->timestamps();
});
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->string('type');
$table->string('doc_number')->nullable();
$table->string('doc_type')->nullable();
$table->foreignId('added_by')->constrained('users');
$table->foreignId('document_file_id')->nullable()->constrained('document_files');
$table->dateTime('expiration_date')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('documents');
Schema::dropIfExists('document_files');
}
};

View File

@ -6,6 +6,7 @@ use App\Http\Controllers\UserController;
use App\Http\Controllers\ScheduleSlotsController;
use App\Http\Controllers\AvailabilityController;
use App\Http\Controllers\AlertController;
use App\Http\Controllers\DocumentsController;
use App\Http\Controllers\LogsController;
use App\Http\Controllers\TelegramController;
use App\Http\Controllers\ServiceController;
@ -45,7 +46,10 @@ Route::middleware('auth:sanctum')->group( function () {
Route::get('/list', [UserController::class, 'index'])->middleware(ETag::class);
Route::get('/users/{id}', [UserController::class, 'show']);
Route::get('/users/{user}', [UserController::class, 'show']);
Route::put('/users/{user}', [UserController::class, 'update']);
Route::post('/documents/driving_license', [DocumentsController::class, 'uploadDrivingLicenseScan']);
Route::get('/schedules', [ScheduleSlotsController::class, 'index']);
Route::post('/schedules', [ScheduleSlotsController::class, 'store']);
@ -84,6 +88,10 @@ Route::middleware('auth:sanctum')->group( function () {
Route::post('/logout', [AuthController::class, 'logout']);
});
Route::middleware('signed')->group( function () {
Route::get('/documents/driving_license/{uuid}', [DocumentsController::class, 'serveDrivingLicenseScan'])->name('driving_license_scan_serve');
});
Route::get('/owner_image', function() {
return response()
->file(

View File

@ -53,7 +53,7 @@
<td>Indirizzo</td>
<td>
<a href="https://maps.google.com/?q={{ user.address }}" target="_blank">
{{ user.address }}
{{ user.address }}<ng-container *ngIf="user.address_zip_code"> (CAP <i>{{ user.address_zip_code }}</i>)</ng-container>
</a>
</td>
</tr>
@ -88,6 +88,7 @@
</td>
</tr>
<tr>
<!--TODO: translate -->
<td>Autista</td>
<td>
<i class="fa fa-check" style="color:green" *ngIf="user.driver"></i>
@ -108,7 +109,7 @@
</table>
</div>
<div class="modal-footer">
<button *ngIf="false" type="button" class="btn btn-primary" (click)="goToEditPage()" [disabled]="!canGoToEditPage">
<button type="button" class="btn btn-primary" (click)="goToEditPage()" [disabled]="!canGoToEditPage">
{{ "edit" | translate | titlecase }}
</button>
<button type="button" class="btn btn-secondary" (click)="bsModalRef.hide()">

View File

@ -1,10 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { ApiClientService } from 'src/app/_services/api-client.service';
import { AuthService } from 'src/app/_services/auth.service';
import { ToastrService } from 'ngx-toastr';
import { TranslateService } from '@ngx-translate/core';
import Swal from 'sweetalert2';
@Component({
selector: 'modal-user-info',
@ -23,8 +21,7 @@ export class ModalUserInfoComponent implements OnInit {
public bsModalRef: BsModalRef,
private api: ApiClientService,
public auth: AuthService,
private toastr: ToastrService,
private translate: TranslateService
private router: Router
) { }
ngOnInit() {
@ -41,6 +38,6 @@ export class ModalUserInfoComponent implements OnInit {
goToEditPage() {
if(!this.canGoToEditPage) return;
this.bsModalRef.hide();
this.router.navigate(['/users', this.id]);
}
}

View File

@ -21,6 +21,8 @@
</ng-container>
<th>{{ 'services'|translate|titlecase }}</th>
<th>{{ 'availability_minutes'|translate|titlecase }}</th>
<th>{{ 'edit'|translate|titlecase }}</th>
<!-- TODO: hide if user can't access -->
</tr>
</thead>
<tbody id="table_body">
@ -48,6 +50,8 @@
</td>
<td>{{ row.services }}</td>
<td>{{ row.availability_minutes }}</td>
<td (click)="editUser(row.id)"><i class="fa fa-edit"></i></td>
<!-- TODO: hide if user can't access -->
</tr>
</tbody>
</table>

View File

@ -204,6 +204,10 @@ export class TableComponent implements OnInit, OnDestroy {
this.moreDetails.emit({rowId});
}
editUser(id: number) {
this.router.navigate(['/users', id]);
}
openPlaceDetails(id: number) {
this.router.navigate(['/place-details', id]);
}

View File

@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { EditUserComponent } from './edit-user.component';
const routes: Routes = [{ path: '', component: EditUserComponent }];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class EditUserRoutingModule { }

View File

@ -0,0 +1,208 @@
<back-btn></back-btn>
<div class="page-header">
<h1 class="text-center">ANAGRAFICA PERSONALE</h1>
</div>
<h2 class="text-center mb-3">{{ user.surname }} {{ user.name }}</h2>
<form [formGroup]="profileForm" (ngSubmit)="formSubmit()" class="row g-4 m-3">
<div class="col-md-6">
<label for="name" class="form-label">Nome</label>
<input formControlName="name" autocomplete="name" type="text" class="form-control" id="name">
</div>
<div class="col-md-6">
<label for="surname" class="form-label">Cognome</label>
<input formControlName="surname" autocomplete="additional-name" type="text" class="form-control" id="surname">
</div>
<div class="col-md-6">
<label for="username" class="form-label">Username</label>
<input formControlName="username" autocomplete="username" type="text" class="form-control" id="username">
</div>
<div class="col-md-6">
<label for="ssn" class="form-label">Codice fiscale</label>
<input formControlName="ssn" autocomplete="ssn" type="text" class="form-control" id="ssn">
</div>
<div class="col-md-6">
<label for="birthday" class="form-label">Data di nascita</label>
<input formControlName="birthday" autocomplete="bday" type="text" class="form-control" placeholder="Premi per selezionare" id="birthday" bsDatepicker [bsConfig]="{ adaptivePosition: true, dateInputFormat: 'DD/MM/YYYY' }" [maxDate]="birthdayMaxDate">
</div>
<div class="col-md-6">
<label for="birthplace" class="form-label">Luogo di nascita</label>
<div class="input-group" id="birthplace">
<input formControlName="birthplace" type="text" class="form-control" placeholder="Luogo">
<input formControlName="birthplace_province" type="text" class="form-control" placeholder="Provincia">
</div>
</div>
<h2 class="text-center mt-5"><i class="fas fa-fire-extinguisher"></i> Informazioni di servizio</h2>
<div class="col-md-12">
<label for="course_date" class="form-label">Data corso</label>
<input formControlName="course_date" type="text" class="form-control" placeholder="Premi per selezionare" id="course_date" bsDatepicker [bsConfig]="{ adaptivePosition: true, dateInputFormat: 'DD/MM/YYYY' }" [maxDate]="birthdayMaxDate">
</div>
<div class="col-md-6">
<label for="driver" class="form-label">Autista</label>
<div class="form-check form-switch">
<input formControlName="driver" class="form-check-input custom-check-input" type="checkbox" role="switch" id="driver">
</div>
</div>
<div class="col-md-6">
<label for="chief" class="form-label">Caposquadra</label>
<div class="form-check form-switch">
<input formControlName="chief" class="form-check-input custom-check-input" type="checkbox" role="switch" id="chief">
</div>
</div>
<div class="col-md-6">
<label for="banned" class="form-label">Bannato (non potrà accedere o interagire)</label>
<div class="form-check form-switch">
<input formControlName="banned" class="form-check-input custom-check-input" type="checkbox" role="switch" id="banned">
</div>
</div>
<div class="col-md-6">
<label for="hidden" class="form-label">Nascosto nella lista</label>
<div class="form-check form-switch">
<input formControlName="hidden" class="form-check-input custom-check-input" type="checkbox" role="switch" id="hidden">
</div>
</div>
<div class="col-md-6">
<label for="creation_date" class="form-label">Data aggiunta utente</label>
<input type="text" class="form-control" disabled id="creation_date" [value]="creation_date">
</div>
<div class="col-md-6">
<label for="update_date" class="form-label">Data ultima modifica all'utente</label>
<input type="text" class="form-control" disabled id="update_date" [value]="update_date">
</div>
<div class="col-md-12">
<label for="last_access_date" class="form-label">Ultimo accesso</label>
<input type="text" class="form-control" disabled id="last_access_date" [value]="last_access_date">
</div>
<h2 class="text-center mt-5"><i class="fas fa-phone"></i> Recapiti</h2>
<div class="col-md-8">
<label for="address" class="form-label">Indirizzo di residenza, civico</label>
<input formControlName="address" type="text" class="form-control" id="address" placeholder="Via/Piazza...">
</div>
<div class="col-md-4">
<label for="address_zip_code" class="form-label">CAP</label>
<input formControlName="address_zip_code" type="number" class="form-control" id="address_zip_code" placeholder="12345" pattern="[0-9]{5}">
</div>
<div class="col-md-6">
<label for="phone_number" class="form-label">Numero di telefono</label>
<input formControlName="phone_number" type="number" class="form-control" id="phone_number" placeholder="Fisso o cellulare">
</div>
<div class="col-md-6">
<label for="email" class="form-label">Indirizzo email</label>
<input formControlName="email" type="email" class="form-control" id="email" placeholder="">
</div>
<h2 class="text-center mt-5"><i class="fas fa-id-card"></i> Documenti</h2>
<ng-container formGroupName="driving_license">
<h3 class="text-center mt-3">Patente <i class="fas fa-car-side"></i></h3>
<div class="col-md-12" *ngIf="dlCurrScanUrl !== null">
<img [src]="dlCurrScanUrl" class="img-fluid rounded mx-auto d-block w-50">
</div>
<div class="col-md-6">
<label for="dl_number" class="form-label">Numero patente</label>
<input formControlName="number" autocomplete="dl-number" type="text" class="form-control" id="dl_number" placeholder="AB1234567">
</div>
<div class="col-md-6">
<label for="dl_type" class="form-label">Grado</label>
<input formControlName="type" autocomplete="dl-type" type="text" class="form-control" id="dl_type" placeholder="">
</div>
<div class="col-md-6">
<label for="dl_expiration_date" class="form-label">Scadenza patente</label>
<input formControlName="expiration_date" autocomplete="dl-expiration-date" type="text" class="form-control" placeholder="Premi per selezionare" id="dl_expiration_date" bsDatepicker [bsConfig]="{ adaptivePosition: true, dateInputFormat: 'DD/MM/YYYY' }" [minDate]="dlExpirationMinDate">
</div>
<div class="col-md-6">
<label for="dl_scan" class="form-label">Scansione patente</label>
<input class="form-control" type="file" id="dl_scan" (change)="onDrivingLicenseScanSelected($event)" #drivingLicenseFileUpload>
<div *ngIf="tmpDrivingLicenseImgData !== null">
<img [src]="tmpDrivingLicenseImgData" class="img-fluid p-1 w-75">
<button type="button" class="btn btn-primary" *ngIf="dlScanNotUploadedYet" (click)="uploadDrivingLicenseScan(drivingLicenseFileUpload)">
<i class="fas fa-upload"></i> Carica scansione
</button>
</div>
</div>
</ng-container>
<h2 class="text-center mt-5"><i class="fas fa-tshirt"></i> Indumenti</h2>
<div class="col-md-6">
<label for="suit_size" class="form-label">Taglia tuta</label>
<input formControlName="suit_size" type="text" class="form-control" id="suit_size" placeholder="M, L, XL...">
</div>
<div class="col-md-6">
<label for="boot_size" class="form-label">Taglia scarponi</label>
<input formControlName="boot_size" type="text" class="form-control" id="boot_size" placeholder="41, 42, 43...">
</div>
<div class="col-12 m-3 mt-4">
<button type="submit" class="btn btn-lg btn-primary">Aggiorna</button>
</div>
</form>
<hr>
<div class="text-center">
<h3>Corsi di formazione</h3>
<button disabled class="btn btn-sm btn-outline-info m-2">
<i class="fas fa-download"></i> LIFM
</button>
<table class="mx-auto table table-striped table-bordered table-responsive">
<thead>
<tr>
<th>TIPOLOGIA</th>
<th>NOME</th>
<th>DATA</th>
<th>SCADENZA</th>
<th>CERT.</th>
</tr>
</thead>
<tbody>
<tr *ngIf="false">
<td>TIPOCORSO</td>
<td>Nome</td>
<td>31/12/2024</td>
<td>31/12/2027</td>
<td>
<a href=''><i class='fas fa-graduation-cap'></i></a>
</td>
</tr>
</tbody>
</table>
<br>
</div>
<hr>
<div class="text-center">
<h3>Visite mediche</h3>
<table class="mx-auto table table-striped table-bordered table-responsive">
<thead>
<tr>
<th>DATA</th>
<th>MEDICO</th>
<th>SCADENZA</th>
<th>CERT.</th>
</tr>
</thead>
<tbody>
<tr *ngIf="false">
<td>31/12/2024</td>
<td>Medico</td>
<td>31/12/2027</td>
<td>
<a href=''><i class='fas fa-clipboard-list'></i></a>
</td>
</tr>
</tbody>
</table>
</div>

View File

@ -0,0 +1,10 @@
.custom-check-input {
width: 3.6rem;
height: 1.8rem;
}
@media (max-width: 767px) {
.page-header {
margin-top: 3.5rem;
}
}

View File

@ -0,0 +1,212 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ApiClientService } from 'src/app/_services/api-client.service';
import { AuthService } from 'src/app/_services/auth.service';
import Swal from 'sweetalert2';
@Component({
selector: 'app-edit-user',
templateUrl: './edit-user.component.html',
styleUrls: ['./edit-user.component.scss']
})
export class EditUserComponent implements OnInit {
id: number | undefined;
user: any = {};
profileForm: FormGroup = this.formBuilder.group({
name: ['', [Validators.required, Validators.minLength(3)]],
surname: [''],
username: ['', [Validators.required, Validators.minLength(3)]],
birthday: [null],
birthplace: [''],
birthplace_province: [''],
ssn: [''],
course_date: [null],
driver: [false, [Validators.required]],
chief: [false, [Validators.required]],
banned: [false, [Validators.required]],
hidden: [false, [Validators.required]],
address: [''],
address_zip_code: [''],
phone_number: [''],
email: [''],
driving_license: this.formBuilder.group({
number: [''],
type: [''],
expiration_date: [null],
scan: [null]
}),
suit_size: [''],
boot_size: ['']
});
birthdayMaxDate = new Date(new Date().setFullYear(new Date().getFullYear() - 18)); //18 years ago
dlExpirationMinDate = new Date(new Date().setDate(new Date().getDate() + 1)); //Tomorrow
allowedImageTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
maxImageSize = 1024 * 1024 * 5; //5MB
creation_date: string = "MAI";
update_date: string = "MAI";
last_access_date: string = "MAI";
tmpDrivingLicenseImgData: string | null = null;
dlScanNotUploadedYet = true;
dlCurrScanUrl: string | null = null;
constructor(
private route: ActivatedRoute,
private formBuilder: FormBuilder,
private api: ApiClientService,
private auth: AuthService
) {
this.route.paramMap.subscribe(params => {
this.id = typeof params.get('id') === 'string' ? parseInt(params.get('id') || '') : undefined;
if (this.id) {
this.api.get(`users/${this.id}`).then((response) => {
this.user = response;
console.log(response);
this.profileForm.patchValue({
name: this.user.name,
surname: this.user.surname,
username: this.user.username,
birthday: this.user.birthday ? new Date(this.user.birthday) : null,
birthplace: this.user.birthplace,
birthplace_province: this.user.birthplace_province,
ssn: this.user.ssn,
course_date: this.user.course_date ? new Date(this.user.course_date) : null,
driver: this.user.driver,
chief: this.user.chief,
banned: this.user.banned,
hidden: this.user.hidden,
address: this.user.address,
address_zip_code: this.user.address_zip_code,
phone_number: this.user.phone_number,
email: this.user.email,
driving_license: {
number: this.user.driving_license ? this.user.driving_license.doc_number : null,
type: this.user.driving_license ? this.user.driving_license.doc_type : null,
expiration_date: (this.user.driving_license && this.user.driving_license.expiration_date) ? new Date(this.user.driving_license.expiration_date) : null,
scan: this.user.driving_license ? this.user.driving_license.uuid : null
},
suit_size: this.user.suit_size,
boot_size: this.user.boot_size
});
const convertToItalianDate = (date: string | null): string => {
if(!date) return "MAI";
const dateObj = new Date(date);
return dateObj.toLocaleString('it-IT', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
}
this.creation_date = convertToItalianDate(this.user.created_at);
this.update_date = convertToItalianDate(this.user.updated_at);
this.last_access_date = convertToItalianDate(this.user.last_access);
if(this.user.driving_license && this.user.driving_license.document_file) {
this.dlCurrScanUrl = this.api.apiEndpoint(this.user.driving_license.document_file.url);
}
}).catch((err) => {
console.log(err);
});
}
});
}
ngOnInit(): void {
if(!this.auth.profile.can('users-ban') || this.id === this.auth.profile.id) {
this.profileForm.get('banned')?.disable();
}
if(!this.auth.profile.can('users-hide') || (!this.auth.profile.can('user-hide') && this.id === this.auth.profile.id)) {
this.profileForm.get('hidden')?.disable();
}
}
onDrivingLicenseScanSelected(event: any) {
const file: File = event.target.files[0];
this.dlScanNotUploadedYet = true;
if (file) {
if(!this.allowedImageTypes.includes(file.type)) {
event.target.value = null;
Swal.fire({
title: 'Errore',
text: 'Formato immagine non supportato',
icon: 'error',
confirmButtonText: 'Ok'
});
return;
}
if(file.size > this.maxImageSize) {
event.target.value = null;
Swal.fire({
title: 'Errore',
text: 'File troppo grande',
icon: 'error',
confirmButtonText: 'Ok'
});
return;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (_event) => {
this.tmpDrivingLicenseImgData = reader.result as string;
}
}
}
uploadDrivingLicenseScan(input: HTMLInputElement) {
const filename = "test.png"
if(!input.files || !input.files[0]) return;
console.log(input.files[0]);
const formData = new FormData();
formData.append('file', input.files[0], input.files[0].name);
this.api.post("documents/driving_license", formData).then((response) => {
console.log(response);
this.dlScanNotUploadedYet = false;
this.profileForm.patchValue({
driving_license: {
scan: response.uuid
}
});
}).catch((err) => {
console.log(err);
});
}
formSubmit() {
let data = this.profileForm.value;
data.birthday = data.birthday ? new Date(data.birthday) : null;
data.course_date = data.course_date ? new Date(data.course_date) : null;
data.driving_license.expiration_date = data.driving_license.expiration_date ? new Date(data.driving_license.expiration_date) : null;
if (this.id) {
//TODO: translate
this.api.put(`users/${this.id}`, data).then((response) => {
console.log(response);
Swal.fire({
title: 'Utente modificato',
text: 'L\'utente è stato modificato con successo',
icon: 'success',
confirmButtonText: 'Ok'
});
}).catch((err) => {
console.log(err);
Swal.fire({
title: 'Errore',
text: 'Si è verificato un errore durante la modifica dell\'utente',
icon: 'error',
confirmButtonText: 'Ok'
});
});
}
}
}

View File

@ -0,0 +1,26 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
import { BackBtnModule } from '../../_components/back-btn/back-btn.module';
import { TranslationModule } from '../../translation.module';
import { EditUserRoutingModule } from './edit-user-routing.module';
import { EditUserComponent } from './edit-user.component';
@NgModule({
declarations: [
EditUserComponent
],
imports: [
CommonModule,
EditUserRoutingModule,
FormsModule,
ReactiveFormsModule,
BsDatepickerModule.forRoot(),
BackBtnModule,
TranslationModule
]
})
export class EditUserModule { }

View File

@ -11,6 +11,11 @@ import { LoginComponent } from './_routes/login/login.component';
const routes: Routes = [
{ path: 'list', component: ListComponent, canActivate: [AuthorizeGuard] },
{
path: 'users/:id',
loadChildren: () => import('./_routes/edit-user/edit-user.module').then(m => m.EditUserModule),
canActivate: [AuthorizeGuard]
},
{ path: 'logs', component: LogsComponent, canActivate: [AuthorizeGuard] },
{ path: 'services', component: ServicesComponent, canActivate: [AuthorizeGuard] },
{