diff --git a/backend/app/Http/Controllers/DocumentsController.php b/backend/app/Http/Controllers/DocumentsController.php index c3a9591..d2eda5c 100644 --- a/backend/app/Http/Controllers/DocumentsController.php +++ b/backend/app/Http/Controllers/DocumentsController.php @@ -7,6 +7,7 @@ use Illuminate\Support\Str; use Illuminate\Validation\Rules\File; use App\Models\Document; use App\Models\DocumentFile; +use App\Models\User; use App\Utils\Logger; class DocumentsController extends Controller @@ -36,7 +37,7 @@ class DocumentsController extends Controller $document->uploadedBy()->associate(auth()->user()); $document->save(); - Logger::log("Caricamento scansione patente", auth()->user()->id); + Logger::log("Caricamento scansione patente", auth()->user()); return response()->json([ "uuid" => $document->uuid @@ -94,7 +95,8 @@ class DocumentsController extends Controller $document->date = $request->input('date'); $document->save(); - Logger::log("Aggiunta corso di formazione", $request->input('user')); + $user = User::findOrFail($request->input('user')); + Logger::log("Aggiunta corso di formazione", $user); return response()->json([ "id" => $document->id @@ -132,7 +134,7 @@ class DocumentsController extends Controller $document = new Document(); $document->type = 'medical_examination'; - $document->doc_certifier = $request->input('doctor'); + $document->doc_certifier = $request->input('certifier'); $document->user = $request->input('user'); $document->added_by = auth()->user()->id; if($request->hasFile('file')) { @@ -152,7 +154,8 @@ class DocumentsController extends Controller $document->expiration_date = $request->input('expiration_date'); $document->save(); - Logger::log("Aggiunta visita medica", $request->input('user')); + $user = User::findOrFail($request->input('user')); + Logger::log("Aggiunta visita medica", $user); return response()->json([ "id" => $document->id diff --git a/backend/app/Http/Controllers/ServiceController.php b/backend/app/Http/Controllers/ServiceController.php index 45549fa..649f78c 100644 --- a/backend/app/Http/Controllers/ServiceController.php +++ b/backend/app/Http/Controllers/ServiceController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers; +use App\Http\Resources\ServiceResource; +use App\Http\Resources\ServicesListResource; use App\Models\Place; use App\Models\PlaceMunicipality; use App\Models\PlaceProvince; @@ -63,7 +65,7 @@ class ServiceController extends Controller $p = $service->place; unset($p->lat, $p->lon, $p->place_id, $p->osm_id, $p->osm_type, $p->licence, $p->addresstype, $p->country, $p->country_code, $p->display_name, $p->road, $p->house_number, $p->postcode, $p->state, $p->suburb, $p->city, $p->municipality_id); } - return response()->json($result); + return ServicesListResource::collection($result); } /** @@ -74,7 +76,7 @@ class ServiceController extends Controller if (!$request->user()->hasPermission("services-read")) abort(401); User::where('id', $request->user()->id)->update(['last_access' => now()]); - return response()->json( + return ServiceResource::make( Service::join('users', 'users.id', '=', 'chief_id') ->join('services_types', 'services_types.id', '=', 'type_id') ->select('services.*', DBTricks::nameSelect("chief", "users"), 'services_types.name as type') @@ -124,15 +126,15 @@ class ServiceController extends Controller if ($is_map_picker) { //Find Place by lat lon - $place = Place::where('lat', $request->place->lat)->where('lon', $request->place->lon)->first(); + $place = Place::where('lat', $request->place["lat"])->where('lon', $request->place["lon"])->first(); if (!$place) { $place = new Place(); - $place->lat = $request->place->lat; - $place->lon = $request->place->lon; + $place->lat = $request->place["lat"]; + $place->lon = $request->place["lon"]; $response = Http::withUrlParameters([ - 'lat' => $request->place->lat, - 'lon' => $request->place->lon, + 'lat' => $request->place["lat"], + 'lon' => $request->place["lon"], ])->get('https://nominatim.openstreetmap.org/reverse?format=json&lat={lat}&lon={lon}'); if (!$response->ok()) abort(500); @@ -252,6 +254,8 @@ class ServiceController extends Controller User::whereIn('id', $usersToIncrement)->increment('services'); Logger::log($adding ? "Intervento aggiunto" : "Intervento modificato"); + + return response()->noContent(); } /** @@ -265,5 +269,7 @@ class ServiceController extends Controller User::whereIn('id', $usersToDecrement)->decrement('services'); $service->delete(); Logger::log("Intervento eliminato"); + + return response()->noContent(); } } diff --git a/backend/app/Http/Controllers/UserController.php b/backend/app/Http/Controllers/UserController.php index 4c8a615..036a9d1 100644 --- a/backend/app/Http/Controllers/UserController.php +++ b/backend/app/Http/Controllers/UserController.php @@ -10,12 +10,20 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Facades\URL; +use Illuminate\Http\Resources\Json\AnonymousResourceCollection; + +use App\Http\Resources\UsersListResource; +use App\Http\Resources\UserResource; + + class UserController extends Controller { /** - * Return the list of users, used in main list. + * Return users list. + * + * Used in main list */ - public function index(Request $request) + public function index(Request $request): AnonymousResourceCollection { $requestedCols = ['id', 'chief', 'last_access', 'name', 'surname', 'available', 'driver', 'services', 'availability_minutes']; if($request->user()->isAbleTo("users-read")) $requestedCols[] = "phone_number"; @@ -42,13 +50,13 @@ class UserController extends Controller unset($user->last_access); } - return response()->json($list); + return UsersListResource::collection($list); } /** * Return a single user with all the details. */ - public function show(Request $request, User $user) + public function show(Request $request, User $user): UserResource { if($request->user()->id != $user->id && !$request->user()->hasPermission("users-read")) abort(401); @@ -74,12 +82,11 @@ class UserController extends Controller if($tc_tmp->count() > 0) { $user->training_courses = $tc_tmp; foreach($user->training_courses as $tc) { - if(!is_null($tc->doc_uuid)) { - $tc->doc_url = URL::temporarySignedRoute( + $tc->doc_url = !is_null($tc->doc_uuid) ? + URL::temporarySignedRoute( 'training_course_serve', now()->addHours(1), ['uuid' => $tc->doc_uuid] - ); - unset($tc->doc_uuid); - } + ) : null; + unset($tc->doc_uuid); } } else { $user->training_courses = []; @@ -94,12 +101,11 @@ class UserController extends Controller if($me_tmp->count() > 0) { $user->medical_examinations = $me_tmp; foreach($user->medical_examinations as $me) { - if(!is_null($me->cert_uuid)) { - $me->cert_url = URL::temporarySignedRoute( + $me->cert_url = !is_null($me->cert_uuid) ? + URL::temporarySignedRoute( 'medical_examination_serve', now()->addHours(1), ['uuid' => $me->cert_uuid] - ); - unset($me->cert_uuid); - } + ) : null; + unset($me->cert_uuid); } } else { $user->medical_examinations = []; @@ -111,7 +117,7 @@ class UserController extends Controller ); } - return response()->json($user); + return UserResource::make($user); } /** @@ -224,7 +230,7 @@ class UserController extends Controller Logger::log("Modifica profilo utente", $user); - return response()->json($user); + return response()->noContent(); } /** @@ -244,6 +250,6 @@ class UserController extends Controller Logger::log("Modifica password utente", $user); - return response()->json($user); + return response()->noContent(); } } diff --git a/backend/app/Http/Resources/DrivingLicenseResource.php b/backend/app/Http/Resources/DrivingLicenseResource.php new file mode 100644 index 0000000..c83abab --- /dev/null +++ b/backend/app/Http/Resources/DrivingLicenseResource.php @@ -0,0 +1,23 @@ + $this->doc_type, + 'doc_number' => $this->doc_number, + 'expiration_date' => $this->expiration_date, + 'scan_uuid' => $this->scan_uuid, + 'scan_url' => $this->scan_url, + ]; + } +} diff --git a/backend/app/Http/Resources/MedicalExaminationsListResource.php b/backend/app/Http/Resources/MedicalExaminationsListResource.php new file mode 100644 index 0000000..17897e0 --- /dev/null +++ b/backend/app/Http/Resources/MedicalExaminationsListResource.php @@ -0,0 +1,23 @@ + $this->certifier, + "date" => $this->date, + "expiration_date" => $this->expiration_date, + /** @var string|null */ + "cert_url" => $this->cert_url, + ]; + } +} diff --git a/backend/app/Http/Resources/MunicipalityResource.php b/backend/app/Http/Resources/MunicipalityResource.php new file mode 100644 index 0000000..a230d42 --- /dev/null +++ b/backend/app/Http/Resources/MunicipalityResource.php @@ -0,0 +1,31 @@ + $this->id, + 'code' => $this->code, + 'name' => $this->name, + 'postal_code' => $this->postal_code, + 'province' => [ + 'id' => $this->province->id, + 'code' => $this->province->code, + 'name' => $this->province->name, + 'short_name' => $this->province->short_name, + 'region' => $this->province->region, + ], + ]; + } +} diff --git a/backend/app/Http/Resources/PlaceCompleteResource.php b/backend/app/Http/Resources/PlaceCompleteResource.php new file mode 100644 index 0000000..a5a41b4 --- /dev/null +++ b/backend/app/Http/Resources/PlaceCompleteResource.php @@ -0,0 +1,60 @@ + $this->id, + 'name' => $this->name !== "" ? $this->name : $this["display_name"], + 'village' => $this->village, + /** @var string */ + 'created_at' => $this->created_at, + /** @var string */ + 'updated_at' => $this->updated_at, + /** @var MunicipalityResource|null */ + 'municipality' => $this->municipality, + + // OSM data, if selected using map selector + + /** @var number|null */ + 'lat' => $this->lat, + /** @var number|null */ + 'lon' => $this->lon, + /** @var number */ + 'place_id' => $this->place_id, + /** @var number|null */ + 'osm_id' => $this->osm_id, + /** @var string|null */ + 'osm_type' => $this->osm_type, + /** @var string|null */ + 'licence' => $this->licence, + /** @var string|null */ + 'addresstype' => $this->addresstype, + /** @var string|null */ + 'country' => $this->country, + /** @var string|null */ + 'country_code' => $this->country_code, + /** @var string|null */ + 'road' => $this->road, + /** @var string|null */ + 'house_number' => $this->house_number, + /** @var string|null */ + 'postcode' => $this->postcode, + /** @var string|null */ + 'state' => $this->state, + /** @var string|null */ + 'suburb' => $this->suburb, + /** @var string|null */ + 'city' => $this->city, + ]; + } +} diff --git a/backend/app/Http/Resources/PlaceResource.php b/backend/app/Http/Resources/PlaceResource.php new file mode 100644 index 0000000..85ad22e --- /dev/null +++ b/backend/app/Http/Resources/PlaceResource.php @@ -0,0 +1,27 @@ + $this->id, + 'name' => $this->name, + 'village' => $this->village, + /** @var string */ + 'created_at' => $this->created_at, + /** @var string */ + 'updated_at' => $this->updated_at, + /** @var MunicipalityResource|null */ + 'municipality' => $this->municipality, + ]; + } +} diff --git a/backend/app/Http/Resources/ServiceOrTrainingUsersListResource.php b/backend/app/Http/Resources/ServiceOrTrainingUsersListResource.php new file mode 100644 index 0000000..355c674 --- /dev/null +++ b/backend/app/Http/Resources/ServiceOrTrainingUsersListResource.php @@ -0,0 +1,23 @@ + $this->name, + "surname" => $this->surname, + "pivot" => [ + "user_id" => $this->pivot->user_id + ], + ]; + } +} diff --git a/backend/app/Http/Resources/ServiceResource.php b/backend/app/Http/Resources/ServiceResource.php new file mode 100644 index 0000000..3e3087e --- /dev/null +++ b/backend/app/Http/Resources/ServiceResource.php @@ -0,0 +1,32 @@ + $this->id, + "code" => $this->code, + "notes" => $this->notes, + "start" => $this->start, + "end" => $this->end, + "added_by_id" => $this->added_by_id, + "updated_by_id" => $this->updated_by_id, + "created_at" => $this->created_at, + "updated_at" => $this->updated_at, + "chief" => $this->chief, + "type" => $this->type, + "drivers" => ServiceOrTrainingUsersListResource::collection($this->drivers), + "crew" => ServiceOrTrainingUsersListResource::collection($this->crew), + "place" => PlaceCompleteResource::make($this->place), + ]; + } +} diff --git a/backend/app/Http/Resources/ServicesListResource.php b/backend/app/Http/Resources/ServicesListResource.php new file mode 100644 index 0000000..9d538f7 --- /dev/null +++ b/backend/app/Http/Resources/ServicesListResource.php @@ -0,0 +1,32 @@ + $this->id, + "code" => $this->code, + "notes" => $this->notes, + "start" => $this->start, + "end" => $this->end, + "added_by_id" => $this->added_by_id, + "updated_by_id" => $this->updated_by_id, + "created_at" => $this->created_at, + "updated_at" => $this->updated_at, + "chief" => $this->chief, + "type" => $this->type, + "drivers" => ServiceOrTrainingUsersListResource::collection($this->drivers), + "crew" => ServiceOrTrainingUsersListResource::collection($this->crew), + "place" => PlaceResource::make($this->place), + ]; + } +} diff --git a/backend/app/Http/Resources/TrainingCoursesListResource.php b/backend/app/Http/Resources/TrainingCoursesListResource.php new file mode 100644 index 0000000..defad89 --- /dev/null +++ b/backend/app/Http/Resources/TrainingCoursesListResource.php @@ -0,0 +1,23 @@ + $this->doc_number, + "date" => $this->date, + "type" => $this->type, + /** @var string|null */ + "doc_url" => $this->doc_url, + ]; + } +} diff --git a/backend/app/Http/Resources/UserResource.php b/backend/app/Http/Resources/UserResource.php new file mode 100644 index 0000000..a892d1a --- /dev/null +++ b/backend/app/Http/Resources/UserResource.php @@ -0,0 +1,55 @@ + + */ + public function toArray(Request $request): array + { + return [ + "id" => $this->id, + "name" => $this->name, + "surname" => $this->surname, + "birthplace" => $this->birthplace, + "birthplace_province" => $this->birthplace_province, + "ssn" => $this->ssn, + "address" => $this->address, + "address_zip_code" => $this->address_zip_code, + "suit_size" => $this->suit_size, + "boot_size" => $this->boot_size, + "email" => $this->email, + "email_verified_at" => $this->email_verified_at, + /** @var string|null */ + "created_at" => $this->created_at, + /** @var string|null */ + "updated_at" => $this->updated_at, + "username" => $this->username, + "phone_number" => $this->phone_number, + "available" => $this->available, + "availability_manual_mode" => $this->availability_manual_mode, + "last_availability_change" => $this->last_availability_change, + "birthday" => $this->birthday, + "course_date" => $this->course_date, + "availability_minutes" => $this->availability_minutes, + "chief" => $this->chief, + "driver" => $this->driver, + "services" => $this->services, + "trainings" => $this->trainings, + "last_access" => $this->last_access, + "banned" => $this->banned, + "hidden" => $this->hidden, + /** @var DrivingLicenseResource|null */ + "driving_license" => is_null($this->driving_license) ? null : DrivingLicenseResource::make($this->driving_license), + "training_courses" => TrainingCoursesListResource::collection($this->training_courses), + "medical_examinations" => MedicalExaminationsListResource::collection($this->medical_examinations), + ]; + } +} diff --git a/backend/app/Http/Resources/UsersListResource.php b/backend/app/Http/Resources/UsersListResource.php new file mode 100644 index 0000000..f7c49ac --- /dev/null +++ b/backend/app/Http/Resources/UsersListResource.php @@ -0,0 +1,30 @@ + + */ + public function toArray(Request $request): array + { + return [ + "id" => $this->id, + "chief" => $this->chief, + "name" => $this->name, + "surname" => $this->surname, + "available" => $this->available, + "driver" => $this->driver, + "services" => $this->services, + "availability_minutes" => $this->availability_minutes, + "phone_number" => $this->phone_number, + "online" => false + ]; + } +} diff --git a/backend/app/Utils/Helpers.php b/backend/app/Utils/Helpers.php index 963c47d..0a1d1b7 100644 --- a/backend/app/Utils/Helpers.php +++ b/backend/app/Utils/Helpers.php @@ -10,7 +10,7 @@ class Helpers { if($option) { // Cast to correct type if($option->type == "boolean") { - return $option->value == "true"; + return ($option->value == "1" || $option->value == "true"); } else if($option->type == "number") { return floatval($option->value); } else { diff --git a/backend/composer.json b/backend/composer.json index 8c0ba67..43a83a2 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -8,7 +8,7 @@ "php": "^8.1", "dedoc/scramble": "^0.8.5", "defstudio/telegraph": "^1.38", - "doctrine/dbal": "3.8.2", + "doctrine/dbal": "*", "guzzlehttp/guzzle": "^7.8", "lab404/laravel-impersonate": "^1.7", "laravel/framework": "^10.0", diff --git a/backend/composer.lock b/backend/composer.lock index 1da7941..bbbf1d6 100644 --- a/backend/composer.lock +++ b/backend/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3622c3b9cec25c888177bb27450e0076", + "content-hash": "e3c0fed07ce4fe383d479d1c9dcc412f", "packages": [ { "name": "brick/math", @@ -471,16 +471,16 @@ }, { "name": "doctrine/dbal", - "version": "3.8.2", + "version": "3.8.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb" + "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c", + "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c", "shasum": "" }, "require": { @@ -496,12 +496,12 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.57", + "phpstan/phpstan": "1.10.58", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "9.6.16", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.8.1", + "squizlabs/php_codesniffer": "3.9.0", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" @@ -564,7 +564,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.2" + "source": "https://github.com/doctrine/dbal/tree/3.8.3" }, "funding": [ { @@ -580,7 +580,7 @@ "type": "tidelift" } ], - "time": "2024-02-12T18:36:36+00:00" + "time": "2024-03-03T15:55:06+00:00" }, { "name": "doctrine/deprecations", @@ -9504,5 +9504,5 @@ "php": "^8.1" }, "platform-dev": [], - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.3.0" } diff --git a/backend/database/migrations/2024_01_23_011936_drop_old_municipality_column_from_places.php b/backend/database/migrations/2024_01_23_011936_drop_old_municipality_column_from_places.php index 7e410a5..e181ac9 100644 --- a/backend/database/migrations/2024_01_23_011936_drop_old_municipality_column_from_places.php +++ b/backend/database/migrations/2024_01_23_011936_drop_old_municipality_column_from_places.php @@ -13,7 +13,7 @@ return new class extends Migration { Schema::table('places', function (Blueprint $table) { $table->dropColumn('municipality'); - $table->foreignId('municipality_id')->constrained('place_municipalities')->nullable(); + $table->foreignId('municipality_id')->constrained('place_municipalities')->nullable()->default(null); $table->float('lat', 10, 6)->nullable()->change(); $table->float('lon', 10, 6)->nullable()->change(); }); diff --git a/frontend/src/app/_components/table/table.component.ts b/frontend/src/app/_components/table/table.component.ts index f79e4dd..2073261 100644 --- a/frontend/src/app/_components/table/table.component.ts +++ b/frontend/src/app/_components/table/table.component.ts @@ -25,7 +25,6 @@ export class TableComponent implements OnInit, OnDestroy { "type_id", "pivot", "place_id", - "display_name", "licence", "lat", "lon", @@ -50,7 +49,7 @@ export class TableComponent implements OnInit, OnDestroy { _maxPaginationSize: number = 10; _rowsPerPage: number = 20; - + @Input('maxPaginationSize') get maxPaginationSize(): any { return this._maxPaginationSize; @@ -58,7 +57,7 @@ export class TableComponent implements OnInit, OnDestroy { set maxPaginationSize(value: any) { if(!isNaN(value)) this._maxPaginationSize = value; } - + @Input('rowsPerPage') get rowsPerPage(): any { return this._rowsPerPage; @@ -240,7 +239,7 @@ export class TableComponent implements OnInit, OnDestroy { } editService(id: number) { - this.router.navigate(['/services', id]); + this.router.navigate(['/services', id]); } deleteService(id: number) { @@ -272,7 +271,7 @@ export class TableComponent implements OnInit, OnDestroy { } editTraining(id: number) { - this.router.navigate(['/trainings', id]); + this.router.navigate(['/trainings', id]); } deleteTraining(id: number) { diff --git a/frontend/src/app/_services/api-client.service.ts b/frontend/src/app/_services/api-client.service.ts index 8056f21..c228e5e 100644 --- a/frontend/src/app/_services/api-client.service.ts +++ b/frontend/src/app/_services/api-client.service.ts @@ -60,6 +60,14 @@ export class ApiClientService { constructor(private http: HttpClient) { } + private returnResponseData(body: any): any { + if(body === null || body === undefined) return null; + if(body.data !== undefined) { + return body.data; + } + return body; + } + public apiEndpoint(endpoint: string): string { if(endpoint.startsWith('http') || endpoint.startsWith('//')) { return endpoint; @@ -80,7 +88,9 @@ export class ApiClientService { next: (v: any) => { this.lastEtag = v.headers.get("etag"); this.isLastSame = etag === this.lastEtag && etag !== ""; - resolve(v.body); + resolve( + this.returnResponseData(v.body) + ); }, error: (e) => reject(e) });