Browse Source

Merge branch 'development' into feature/DIARIA-kay-optimização-dashboard-cliente

Gustavo Zanatta 1 month ago
parent
commit
0dfbf666f3

+ 23 - 0
app/Http/Controllers/ClientCalendarController.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Services\ClientCalendarService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Log;
+
+class ClientCalendarController extends Controller
+{
+  public function __construct(private readonly ClientCalendarService $service) {}
+
+  public function index(): JsonResponse
+  {
+    try {
+      $dados = $this->service->getCalendar();
+      return $this->successResponse(payload: $dados);
+    } catch (\Exception $e) {
+      Log::error("Error fetching client calendar: " . $e->getMessage());
+      return $this->errorResponse(message: __("messages.error_fetching_data"), code: 500);
+    }
+  }
+}

+ 23 - 0
app/Http/Controllers/ProviderCalendarController.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Services\ProviderCalendarService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Log;
+
+class ProviderCalendarController extends Controller
+{
+  public function __construct(private readonly ProviderCalendarService $service) {}
+
+  public function index(): JsonResponse
+  {
+    try {
+      $dados = $this->service->getCalendar();
+      return $this->successResponse(payload: $dados);
+    } catch (\Exception $e) {
+      Log::error("Error fetching provider calendar: " . $e->getMessage());
+      return $this->errorResponse(message: __("messages.error_fetching_data"), code: 500);
+    }
+  }
+}

+ 10 - 0
app/Http/Controllers/UserController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
 use App\Http\Resources\UserTypeResource;
 use App\Services\UserService;
 use App\Http\Requests\UserRequest;
+use App\Http\Requests\UpdateMeRequest;
 use App\Http\Resources\UserResource;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Support\Facades\Log;
@@ -61,6 +62,15 @@ class UserController extends Controller
     );
   }
 
+  public function updateMe(UpdateMeRequest $request): JsonResponse
+  {
+    $user = $this->service->updateMe($request->validated());
+    return $this->successResponse(
+      payload: new UserResource($user),
+      message: __("messages.updated"),
+    );
+  }
+
   public function getUserTypes(): JsonResponse
   {
     $user_types = $this->service->getUserTypes();

+ 26 - 0
app/Http/Requests/UpdateMeRequest.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Validation\Rule;
+
+class UpdateMeRequest extends FormRequest
+{
+  public function authorize(): bool
+  {
+    return true;
+  }
+
+  public function rules(): array
+  {
+    return [
+      'name'     => 'sometimes|string|nullable|max:255',
+      'email'    => ['sometimes', 'email', 'nullable', Rule::unique('users', 'email')->ignore(Auth::id())],
+      'phone'    => 'sometimes|string|nullable',
+      'language' => 'sometimes|string|nullable',
+      'document' => 'sometimes|string|nullable',
+    ];
+  }
+}

+ 3 - 2
app/Http/Resources/DashboardClienteResource.php

@@ -22,8 +22,9 @@ class DashboardClienteResource extends JsonResource
       'lastDoneSchedules' => $this['lastDoneSchedules'],
       'favoriteProviders' => $this['favoriteProviders'],
       'providersClose' => $this['providersClose'],
-      'schedulesProposals' => $this['schedulesProposals'],
-      'todaySchedules' => $this['todaySchedules'],
+      'schedulesProposals'  => $this['schedulesProposals'],
+      'todaySchedules'      => $this['todaySchedules'],
+      'has_payment_methods' => $this['has_payment_methods'],
     ];
   }
 }

+ 2 - 0
app/Http/Resources/UserResource.php

@@ -29,6 +29,8 @@ class UserResource extends JsonResource
       'provider_daily_price_4h' => $this->provider?->daily_price_4h,
       'provider_daily_price_2h' => $this->provider?->daily_price_2h,
       'client_id' => $this->client?->id,
+      'client_document' => $this->client?->document,
+      'registration_complete' => $this->registration_complete,
       'provider' => $this->whenLoaded('provider'),
       'client' => $this->whenLoaded('client'),
       'created_at' => Carbon::parse($this->created_at)->format('Y-m-d H:i'),

+ 28 - 11
app/Jobs/FinishScheduleJob.php

@@ -11,6 +11,7 @@ use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Foundation\Bus\Dispatchable;
 use Illuminate\Queue\InteractsWithQueue;
 use Illuminate\Queue\SerializesModels;
+use App\Services\EmailService;
 use Illuminate\Support\Facades\Log;
 
 class FinishScheduleJob implements ShouldQueue
@@ -26,22 +27,22 @@ class FinishScheduleJob implements ShouldQueue
     try {
       $schedule = Schedule::find($this->scheduleId);
       $date_cleaned = Carbon::parse($schedule->date)->format('Y-m-d');
-  
+
       if (!$schedule) {
         return;
       }
-  
+
       Log::channel('schedule_end_jobs')->info('Verificando status do agendamento id: ' . $schedule->id);
       Log::channel('schedule_end_jobs')->info('Status do agendamento: ' . $schedule->status);
-  
+
       if ($schedule->status !== 'started') {
         return;
       }
-  
+
       Log::channel('schedule_end_jobs')->info('Verificando data');
       Log::channel('schedule_end_jobs')->info('Data do agendamento: ' . $date_cleaned);
       Log::channel('schedule_end_jobs')->info('Data atual: ' . now()->toDateString());
-      
+
 
       if ($date_cleaned > now()->toDateString()) {
         return;
@@ -50,15 +51,15 @@ class FinishScheduleJob implements ShouldQueue
       Log::channel('schedule_end_jobs')->info('Verificando horário');
       Log::channel('schedule_end_jobs')->info('Horário do agendamento: ' . $schedule->end_time);
       Log::channel('schedule_end_jobs')->info('Horário atual: ' . now()->toTimeString());
-  
+
       $end_date_time = Carbon::parse($date_cleaned . ' ' . $schedule->end_time);
-      
+
       if ($end_date_time > now()) {
         return;
       }
-  
+
       Log::channel('schedule_end_jobs')->info('Validado com sucesso, atualizado agendamento id: ' . $schedule->id . ' para status finalizado');
-  
+
       $schedule->update([
         'status' => 'finished'
       ]);
@@ -67,12 +68,28 @@ class FinishScheduleJob implements ShouldQueue
       $provider->update([
         'total_services' => $provider->total_services + 1,
       ]);
-      
+
       $client = Client::find($schedule->client_id);
       $client->update([
         'total_services' => $client->total_services + 1,
       ]);
-
+      $emailService = new EmailService();
+      $serviceAmount = (float) $schedule->total_amount;
+      $serviceFee = 7.00;
+      $finalAmount = $serviceAmount + $serviceFee;
+      $emailService->sendEmailReceipt(
+        email: $schedule->client->user->email,
+        schedule: $schedule,
+        client_name: $schedule->client->user->name,
+        service_date: $schedule->date,
+        start_time: $schedule->start_time,
+        end_time: $schedule->end_time,
+        address: $schedule->address->address,
+        total_amount: $serviceAmount,
+        service_fee: $serviceFee,
+        final_amount: $finalAmount,
+        payment_method: 'PIX'
+      );
     } catch (\Exception $e) {
       Log::channel('schedule_end_jobs')->error('Erro ao finalizar agendamento id: ' . $this->scheduleId . '. Erro: ' . $e->getMessage());
       return;

+ 61 - 0
app/Mail/EmailReceipt.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace App\Mail;
+
+use Illuminate\Bus\Queueable;
+use Illuminate\Mail\Mailable;
+use Illuminate\Mail\Mailables\Content;
+use Illuminate\Mail\Mailables\Envelope;
+use Illuminate\Queue\SerializesModels;
+
+class EmailReceipt extends Mailable
+{
+    use Queueable, SerializesModels;
+
+    public function __construct(
+
+        public readonly object $schedule,
+
+        public readonly string $client_name,
+
+        public readonly string $service_date,
+
+        public readonly string $start_time,
+
+        public readonly string $end_time,
+
+        public readonly string $address,
+
+        public readonly string $total_amount,
+
+        public readonly string $service_fee,
+
+        public readonly string $final_amount,
+
+        public readonly string $payment_method,
+
+    ) {}
+
+    public function envelope(): Envelope
+    {
+        return new Envelope(
+
+            subject: __('mail.service_completed.subject'),
+
+        );
+    }
+
+    public function content(): Content
+    {
+        return new Content(
+
+            view: 'emails.email_receipt',
+
+        );
+    }
+
+    public function attachments(): array
+    {
+        return [];
+    }
+}

+ 1 - 0
app/Models/User.php

@@ -67,6 +67,7 @@ class User extends Authenticatable
             "password" => "hashed",
             "type" => UserTypeEnum::class,
             "language" => LanguageEnum::class,
+            "registration_complete" => "boolean",
         ];
     }
 

+ 10 - 1
app/Services/AuthService.php

@@ -2,6 +2,8 @@
 
 namespace App\Services;
 
+use App\Models\Address;
+use App\Models\Client;
 use App\Models\User;
 use App\Models\PersonalAccessToken;
 use App\Enums\UserTypeEnum;
@@ -142,7 +144,12 @@ class AuthService
         $user->code = $code;
         $user->validated_code = false;
         $user->save();
-        $isLogin = true;
+
+        $client = Client::where('user_id', $user->id)->first();
+        $hasAddress = $client && Address::where('source', 'client')
+          ->where('source_id', $client->id)
+          ->exists();
+        $isLogin = $client && $hasAddress;
       } else {
         $user = new User();
         $user->fill($data);
@@ -150,6 +157,8 @@ class AuthService
         $user->name = $data['name'] ?? 'Usuário';
         $user->type = $data['type'] ?? UserTypeEnum::CLIENT->value;
         $user->save();
+
+        Client::create(['user_id' => $user->id]);
       }
 
       if (!empty($data['email'])) {

+ 80 - 0
app/Services/ClientCalendarService.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Client;
+use App\Models\Schedule;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+
+class ClientCalendarService
+{
+  public function __construct() {}
+
+  public function getCalendar(): array
+  {
+    $user = Auth::user();
+    $client = Client::where('user_id', $user->id)->first();
+
+    $selectFields = [
+      'schedules.id',
+      'schedules.provider_id',
+      'schedules.date',
+      'schedules.start_time',
+      'schedules.end_time',
+      'schedules.total_amount',
+      'schedules.period_type',
+      'schedules.schedule_type',
+      'schedules.address_id',
+      'schedules.status',
+      'custom_schedules.offers_meal',
+      'provider_user.name as provider_name',
+      'providers.average_rating',
+      'providers.total_services',
+      DB::raw("(SELECT media.url FROM media WHERE media.source_id = providers.id AND media.source = 'provider' AND media.deleted_at IS NULL LIMIT 1) as provider_photo"),
+      DB::raw("EXISTS(
+        SELECT 1 FROM reviews
+        WHERE reviews.schedule_id = schedules.id
+          AND reviews.origin = 'client'
+          AND reviews.origin_id = {$client->id}
+          AND reviews.deleted_at IS NULL
+      ) as client_reviewed"),
+      DB::raw("(
+        SELECT reviews.stars FROM reviews
+        WHERE reviews.schedule_id = schedules.id
+          AND reviews.origin = 'client'
+          AND reviews.origin_id = {$client->id}
+          AND reviews.deleted_at IS NULL
+        LIMIT 1
+      ) as client_stars"),
+    ];
+
+    $upcomingSchedules = Schedule::with('address:district,address,number,source_id,source,id')
+      ->where('schedules.client_id', $client->id)
+      ->whereIn('schedules.status', ['pending', 'accepted', 'paid', 'started'])
+      ->whereDate('schedules.date', '>=', now()->toDateString())
+      ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
+      ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+      ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+      ->select($selectFields)
+      ->orderBy('schedules.date', 'asc')
+      ->orderBy('schedules.start_time', 'asc')
+      ->get();
+
+    $completedSchedules = Schedule::with('address:district,address,number,source_id,source,id')
+      ->where('schedules.client_id', $client->id)
+      ->whereIn('schedules.status', ['finished', 'cancelled'])
+      ->leftJoin('providers', 'providers.id', '=', 'schedules.provider_id')
+      ->leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id')
+      ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+      ->select($selectFields)
+      ->orderBy('schedules.date', 'desc')
+      ->orderBy('schedules.start_time', 'desc')
+      ->get();
+
+    return [
+      'upcomingSchedules' => $upcomingSchedules,
+      'completedSchedules' => $completedSchedules,
+    ];
+  }
+}

+ 36 - 21
app/Services/ClientService.php

@@ -69,37 +69,52 @@ class ClientService
         $user->save();
       }
 
-      $client = new Client();
-      $client->user_id = $user->id;
-      $client->document = $data['document'] ?? null;
-      $client->save();
+      $client = Client::firstOrCreate(
+        ['user_id' => $user->id],
+        ['document' => $data['document'] ?? null]
+      );
+      if (!empty($data['document'])) {
+        $client->document = $data['document'];
+        $client->save();
+      }
       $client->refresh();
 
-      $address = new Address();
-      $address->source = 'client';
-      $address->source_id = $client->id;
-      $address->zip_code = $data['zip_code'] ?? null;
-      $address->address = $data['address'] ?? null;
-      $address->number = $data['number'] ?? null;
-      $address->district = $data['district'] ?? null;
-      $address->has_complement = $data['has_complement'] ?? false;
-      $address->complement = $data['complement'] ?? null;
-      $address->nickname = $data['nickname'] ?? null;
-      $address->instructions = $data['instructions'] ?? null;
-      $address->address_type = $data['address_type'] ?? 'home';
-      $address->latitude = $data['latitude'] ?? null;
-      $address->longitude = $data['longitude'] ?? null;
+      $addressData = [
+        'zip_code'       => $data['zip_code'] ?? null,
+        'address'        => $data['address'] ?? null,
+        'number'         => $data['number'] ?? null,
+        'district'       => $data['district'] ?? null,
+        'has_complement' => $data['has_complement'] ?? false,
+        'complement'     => $data['complement'] ?? null,
+        'nickname'       => $data['nickname'] ?? null,
+        'instructions'   => $data['instructions'] ?? null,
+        'address_type'   => $data['address_type'] ?? 'home',
+        'latitude'       => $data['latitude'] ?? null,
+        'longitude'      => $data['longitude'] ?? null,
+      ];
 
       if (!empty($data['state']) && !empty($data['city'])) {
         $state = State::where('code', $data['state'])->first();
         if ($state) {
           $city = City::where('name', $data['city'])->where('state_id', $state->id)->first();
-          $address->state_id = $state->id;
-          $address->city_id = $city?->id;
+          $addressData['state_id'] = $state->id;
+          $addressData['city_id']  = $city?->id;
         }
       }
 
-      $address->save();
+      Address::updateOrCreate(
+        ['source' => 'client', 'source_id' => $client->id],
+        $addressData
+      );
+
+      $registrationComplete = !empty($user->name)
+        && !empty($client->document)
+        && Address::where('source', 'client')->where('source_id', $client->id)->exists();
+
+      if ($registrationComplete !== $user->registration_complete) {
+        $user->registration_complete = $registrationComplete;
+        $user->save();
+      }
 
       $result = $this->authService->loginWithEmail(
         email: $user->email,

+ 12 - 8
app/Services/DashboardService.php

@@ -5,6 +5,7 @@ namespace App\Services;
 use App\Models\Address;
 use App\Models\Client;
 use App\Models\ClientFavoriteProvider;
+use App\Models\ClientPaymentMethod;
 use App\Models\Provider;
 use App\Models\ProviderSpeciality;
 use App\Models\Review;
@@ -245,16 +246,19 @@ class DashboardService
       ->orderBy('schedules.start_time', 'asc')
       ->get();
 
+    $hasPaymentMethods = ClientPaymentMethod::where('client_id', $cliente->id)->exists();
+
     return [
-      'headerBar'        => $headerBar,
-      'summaryInfos'     => $summaryInfos,
-      'pendingSchedules' => $pendingSchedules,
-      'nextSchedules'    => $nextSchedules,
-      'lastDoneSchedules' => $lastDoneSchedules,
-      'favoriteProviders' => $favoriteProviders,
-      'providersClose'   => $providersClose,
-      'todaySchedules'   => $todaySchedules,
+      'headerBar'          => $headerBar,
+      'summaryInfos'       => $summaryInfos,
+      'pendingSchedules'   => $pendingSchedules,
+      'nextSchedules'      => $nextSchedules,
+      'lastDoneSchedules'  => $lastDoneSchedules,
+      'favoriteProviders'  => $favoriteProviders,
+      'providersClose'     => $providersClose,
+      'todaySchedules'     => $todaySchedules,
       'schedulesProposals' => $schedulesProposals,
+      'has_payment_methods' => $hasPaymentMethods,
     ];
   }
 

+ 17 - 0
app/Services/EmailService.php

@@ -4,6 +4,7 @@ namespace App\Services;
 
 use App\Mail\SendCodeMail;
 use Illuminate\Support\Facades\Mail;
+use App\Mail\EmailReceipt;
 
 class EmailService
 {
@@ -18,4 +19,20 @@ class EmailService
     {
         Mail::to($email)->send(new SendCodeMail($code, $recipientName));
     }
+
+    public function sendEmailReceipt(
+        string $email, 
+        object $schedule, 
+        string $client_name, 
+        string $service_date, 
+        string $start_time, 
+        string $end_time, 
+        string $address, 
+        string $total_amount, 
+        string $service_fee, 
+        string $final_amount,
+        string $payment_method): void
+    {
+        Mail::to($email)->send(new EmailReceipt($schedule, $client_name, $service_date, $start_time, $end_time, $address, $total_amount, $service_fee, $final_amount, $payment_method));
+    }
 }

+ 77 - 0
app/Services/ProviderCalendarService.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\Provider;
+use App\Models\Schedule;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+
+class ProviderCalendarService
+{
+  public function __construct() {}
+
+  public function getCalendar(): array
+  {
+    $user = Auth::user();
+    $provider = Provider::where('user_id', $user->id)->first();
+
+    $selectFields = [
+      'schedules.id',
+      'schedules.client_id',
+      'client_user.name as client_name',
+      'schedules.date',
+      'schedules.start_time',
+      'schedules.end_time',
+      'schedules.total_amount',
+      'schedules.period_type',
+      'schedules.schedule_type',
+      'schedules.address_id',
+      'schedules.status',
+      'custom_schedules.offers_meal',
+      DB::raw("EXISTS(
+        SELECT 1 FROM reviews
+        WHERE reviews.schedule_id = schedules.id
+          AND reviews.origin = 'provider'
+          AND reviews.origin_id = {$provider->id}
+          AND reviews.deleted_at IS NULL
+      ) as provider_reviewed"),
+      DB::raw("(
+        SELECT reviews.stars FROM reviews
+        WHERE reviews.schedule_id = schedules.id
+          AND reviews.origin = 'provider'
+          AND reviews.origin_id = {$provider->id}
+          AND reviews.deleted_at IS NULL
+        LIMIT 1
+      ) as provider_stars"),
+    ];
+
+    $upcomingSchedules = Schedule::with('address:district,address,number,source_id,source,id')
+      ->where('schedules.provider_id', $provider->id)
+      ->whereIn('schedules.status', ['pending', 'accepted', 'paid', 'started'])
+      ->whereDate('schedules.date', '>=', now()->toDateString())
+      ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+      ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+      ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+      ->select($selectFields)
+      ->orderBy('schedules.date', 'asc')
+      ->orderBy('schedules.start_time', 'asc')
+      ->get();
+
+    $completedSchedules = Schedule::with('address:district,address,number,source_id,source,id')
+      ->where('schedules.provider_id', $provider->id)
+      ->whereIn('schedules.status', ['finished', 'cancelled'])
+      ->leftJoin('clients', 'clients.id', '=', 'schedules.client_id')
+      ->leftJoin('users as client_user', 'client_user.id', '=', 'clients.user_id')
+      ->leftJoin('custom_schedules', 'custom_schedules.schedule_id', '=', 'schedules.id')
+      ->select($selectFields)
+      ->orderBy('schedules.date', 'desc')
+      ->orderBy('schedules.start_time', 'desc')
+      ->get();
+
+    return [
+      'upcomingSchedules' => $upcomingSchedules,
+      'completedSchedules' => $completedSchedules,
+    ];
+  }
+}

+ 46 - 0
app/Services/UserService.php

@@ -3,9 +3,13 @@
 namespace App\Services;
 
 use App\Enums\UserTypeEnum;
+use App\Models\Address;
+use App\Models\Client;
 use App\Models\User;
 use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
 
 class UserService
 {
@@ -52,6 +56,48 @@ class UserService
     return $model->delete();
   }
 
+  public function updateMe(array $data): User
+  {
+    try {
+      DB::beginTransaction();
+
+      $user = User::with(['client'])->findOrFail(Auth::id());
+
+      $userFields = array_filter(
+        array_intersect_key($data, array_flip(['name', 'email', 'phone', 'language'])),
+        fn($v) => $v !== null,
+      );
+
+      if (!empty($userFields)) {
+        $user->update($userFields);
+      }
+
+      if (array_key_exists('document', $data)) {
+        $client = $user->client ?? Client::create(['user_id' => $user->id]);
+        $client->document = $data['document'];
+        $client->save();
+      }
+
+      $user->load('client');
+
+      $registrationComplete = !empty($user->name)
+        && !empty($user->client?->document)
+        && Address::where('source', 'client')->where('source_id', $user->client->id)->exists();
+
+      if ($user->registration_complete !== $registrationComplete) {
+        $user->registration_complete = $registrationComplete;
+        $user->save();
+      }
+
+      DB::commit();
+      return $user->fresh(['client']);
+    } catch (\Exception $e) {
+      DB::rollBack();
+      Log::error('Erro ao atualizar perfil.', ['error' => $e->getMessage()]);
+      throw $e;
+    }
+  }
+
   public function getUserTypes(): array
   {
     return UserTypeEnum::toArray();

+ 22 - 0
database/migrations/2026_05_12_000001_alter_clients_document_nullable.php

@@ -0,0 +1,22 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('clients', function (Blueprint $table) {
+            $table->string('document')->nullable()->change();
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('clients', function (Blueprint $table) {
+            $table->string('document')->nullable(false)->change();
+        });
+    }
+};

+ 22 - 0
database/migrations/2026_05_12_000002_add_registration_complete_to_users_table.php

@@ -0,0 +1,22 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->boolean('registration_complete')->default(false)->after('validated_code');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('registration_complete');
+        });
+    }
+};

+ 27 - 1
lang/en/mail.php

@@ -5,10 +5,36 @@ return [
         'subject'           => 'Your verification code',
         'header_subtitle'   => 'Access verification',
         'greeting'          => 'Hello, :name!',
-        'greeting_anonymous'=> 'Hello!',
+        'greeting_anonymous' => 'Hello!',
         'body_intro'        => 'Use the code below to access your account',
         'expiry_notice'     => 'This code expires in 15 minutes.',
         'ignore_notice'     => 'If you did not request this code, please ignore this email.',
         'footer_note'       => 'This is an automated email. Please do not reply.',
+        'footer_title' => 'Thank you for using the Diaria App ',
+    ],
+    'service_completed' => [
+        'subject' => 'Service completed successfully',
+        'title' => 'Service Completed',
+        'subtitle' => 'Your payment has been successfully confirmed.',
+        'payment_confirmed' => 'Payment confirmed',
+        'payment_confirmed_subtitle' => 'The service has been completed and payment approved.',
+        'service_resume' => 'Service summary',
+        'client' => 'Client',
+        'service_date' => 'Service date',
+        'service_time' => 'Time',
+        'service_address' => 'Address',
+        'payment_title' => 'Payment',
+        'total_amount' => 'Total amount',
+        'payment_method' => 'Payment method',
+        'paid' => 'Paid',
+        'payment_success' => 'Payment processed and confirmed successfully.',
+        'footer_title' => 'Thank you for using Diaria App 💜',
+        'footer_description' => 'We hope your experience was amazing. Count on us for your next services.',
+        'footer_help' => 'Need help?',
+        'footer_contact' => 'Contact our support',
+        'footer_note' => 'This is an automatic email. Please do not reply.',
+        'service_fee' => 'Service fee',
+        'final_amount' => 'Final amount',
+        'currency' => '$',
     ],
 ];

+ 27 - 1
lang/es/mail.php

@@ -5,10 +5,36 @@ return [
         'subject'           => 'Tu código de verificación',
         'header_subtitle'   => 'Verificación de acceso',
         'greeting'          => '¡Hola, :name!',
-        'greeting_anonymous'=> '¡Hola!',
+        'greeting_anonymous' => '¡Hola!',
         'body_intro'        => 'Usa el código a continuación para acceder a tu cuenta',
         'expiry_notice'     => 'Este código expira en 15 minutos.',
         'ignore_notice'     => 'Si no solicitaste este código, ignora este correo.',
         'footer_note'       => 'Este es un correo automático. Por favor, no respondas.',
+        'footer_title' => 'Gracias por usar la aplicación Diaria ',
+    ],
+    'service_completed' => [
+        'subject' => 'Servicio finalizado con éxito',
+        'title' => 'Servicio Finalizado',
+        'subtitle' => 'Su pago fue confirmado con éxito.',
+        'payment_confirmed' => 'Pago confirmado',
+        'payment_confirmed_subtitle' => 'El servicio fue completado y el pago aprobado.',
+        'service_resume' => 'Resumen del servicio',
+        'client' => 'Cliente',
+        'service_date' => 'Fecha del servicio',
+        'service_time' => 'Horario',
+        'service_address' => 'Dirección',
+        'payment_title' => 'Pago',
+        'total_amount' => 'Valor total',
+        'payment_method' => 'Método de pago',
+        'paid' => 'Pagado',
+        'payment_success' => 'Pago procesado y confirmado exitosamente.',
+        'footer_title' => 'Gracias por usar Diaria App 💜',
+        'footer_description' => 'Esperamos que su experiencia haya sido increíble. Cuente con nosotros para sus próximos servicios.',
+        'footer_help' => '¿Necesita ayuda?',
+        'footer_contact' => 'Contacte nuestro soporte',
+        'footer_note' => 'Este es un correo automático. Por favor no responda.',
+        'service_fee' => 'Tarifa del servicio',
+        'final_amount' => 'Valor final',
+        'currency' => 'US$',
     ],
 ];

+ 27 - 1
lang/pt/mail.php

@@ -5,10 +5,36 @@ return [
         'subject'           => 'Seu código de verificação',
         'header_subtitle'   => 'Verificação de acesso',
         'greeting'          => 'Olá, :name!',
-        'greeting_anonymous'=> 'Olá!',
+        'greeting_anonymous' => 'Olá!',
         'body_intro'        => 'Use o código abaixo para acessar sua conta',
         'expiry_notice'     => 'Este código expira em 15 minutos.',
         'ignore_notice'     => 'Se você não solicitou este código, ignore este e-mail.',
         'footer_note'       => 'Este é um e-mail automático. Por favor, não responda.',
+        'footer_title'      => 'Obrigado por utilizar o Diaria App ',
+    ],
+    'service_completed' => [
+        'subject' => 'Serviço finalizado com sucesso',
+        'title' => 'Serviço Finalizado',
+        'subtitle' => 'Seu pagamento foi confirmado com sucesso.',
+        'payment_confirmed' => 'Pagamento confirmado',
+        'payment_confirmed_subtitle' => 'O serviço foi concluído e o pagamento aprovado.',
+        'service_resume' => 'Resumo do serviço',
+        'client' => 'Cliente',
+        'service_date' => 'Data do serviço',
+        'service_time' => 'Horário',
+        'service_address' => 'Endereço',
+        'payment_title' => 'Pagamento',
+        'total_amount' => 'Valor total',
+        'payment_method' => 'Forma de pagamento',
+        'paid' => 'Pago',
+        'payment_success' => 'Pagamento processado e confirmado com sucesso.',
+        'footer_title' => 'Obrigado por utilizar o Diaria App 💜',
+        'footer_description' => 'Esperamos que sua experiência tenha sido incrível. Conte sempre com a gente para seus próximos serviços.',
+        'footer_help' => 'Precisa de ajuda?',
+        'footer_contact' => 'Fale com nosso suporte',
+        'footer_note' => 'Este é um e-mail automático. Não responda esta mensagem.',
+        'service_fee' => 'Taxa de serviço',
+        'final_amount' => 'Valor final',
+        'currency' => 'R$',
     ],
 ];

File diff suppressed because it is too large
+ 2 - 0
public/images/diarinho_email_code.svg


File diff suppressed because it is too large
+ 2 - 0
public/images/diarinho_email_welcome.svg


File diff suppressed because it is too large
+ 6 - 0
public/images/diarinho_perfil_cliente_favoritos.svg


+ 0 - 0
storage/app/public/.gitignore → public/storage/.gitignore


+ 639 - 0
resources/views/emails/email_receipt.blade.php

@@ -0,0 +1,639 @@
+<!DOCTYPE html>
+<html lang="pt-BR">
+
+<head>
+
+  <meta charset="UTF-8" />
+
+  <meta
+    name="viewport"
+    content="width=device-width, initial-scale=1.0"
+  />
+
+  <title>
+    {{ __('mail.service_completed.subject') }}
+  </title>
+
+  <!-- CSS -->
+
+  <style>
+
+    * {
+      margin: 0;
+      padding: 0;
+      box-sizing: border-box;
+      font-family: Arial, Helvetica, sans-serif;
+    }
+
+    body {
+      background: #f5f5f7;
+      padding: 20px;
+    }
+
+    .email-container {
+      max-width: 650px;
+      margin: 0 auto;
+      background: #ffffff;
+      border-radius: 24px;
+      overflow: hidden;
+      box-shadow: 0 10px 40px rgba(0,0,0,0.08);
+    }
+
+    /* HEADER */
+
+    .header {
+      background: linear-gradient(135deg, #7b2cff, #4d14d1);
+      padding: 50px 30px 30px;
+      color: white;
+    }
+
+    .header-content {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: 20px;
+    }
+
+    .header-text {
+      flex: 1;
+    }
+
+    .title {
+      font-size: 52px;
+      font-weight: 800;
+      line-height: 1;
+      margin-bottom: 20px;
+    }
+
+    .subtitle {
+      font-size: 20px;
+      line-height: 1.5;
+      opacity: 0.95;
+    }
+
+    .diarinho {
+      width: 220px;
+    }
+
+    /* CONTENT */
+
+    .content {
+      padding: 30px;
+      background: #f5f5f7;
+    }
+
+    /* STATUS */
+
+    .success-card {
+      background: white;
+      border-radius: 22px;
+      padding: 22px;
+      display: flex;
+      align-items: center;
+      gap: 18px;
+      margin-bottom: 24px;
+    }
+
+    .success-icon {
+      width: 60px;
+      height: 60px;
+      border-radius: 999px;
+      background: #4cd964;
+      color: white;
+      font-size: 32px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-weight: bold;
+    }
+
+    .success-title {
+      font-size: 30px;
+      color: #5a22d6;
+      font-weight: 700;
+      margin-bottom: 6px;
+    }
+
+    .success-subtitle {
+      color: #6b6b6b;
+      font-size: 18px;
+    }
+
+    /* CARD */
+
+    .card {
+      background: white;
+      border-radius: 24px;
+      padding: 30px;
+      margin-bottom: 24px;
+    }
+
+    .card-title {
+      font-size: 34px;
+      font-weight: 700;
+      color: #5a22d6;
+      margin-bottom: 30px;
+    }
+
+    .service-grid {
+      display: flex;
+      justify-content: space-between;
+      gap: 25px;
+    }
+
+    .service-column {
+      flex: 1;
+    }
+
+    .item {
+      margin-bottom: 28px;
+    }
+
+    .item-label {
+      color: #777;
+      font-size: 15px;
+      margin-bottom: 8px;
+    }
+
+    .item-value {
+      color: #1f1f1f;
+      font-size: 26px;
+      font-weight: 700;
+      line-height: 1.4;
+    }
+
+    .divider {
+      width: 1px;
+      background: #ececec;
+    }
+
+    /* PAYMENT */
+
+    .payment-wrapper {
+      display: flex;
+      justify-content: space-between;
+      gap: 25px;
+      align-items: center;
+    }
+
+    .payment-total {
+      flex: 1;
+    }
+
+    .payment-summary {
+      margin-top: 18px;
+    }
+
+    .payment-row {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 18px;
+    }
+
+    .payment-row span {
+      font-size: 18px;
+      color: #666;
+    }
+
+    .payment-row strong {
+      font-size: 22px;
+      color: #1f1f1f;
+    }
+
+    .payment-final {
+      border-top: 1px solid #ececec;
+      padding-top: 22px;
+      margin-top: 10px;
+
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    .payment-final-label {
+      font-size: 24px;
+      font-weight: 700;
+      color: #5a22d6;
+    }
+
+    .payment-final-value {
+      font-size: 42px;
+      font-weight: 800;
+      color: #5a22d6;
+    }
+
+    .payment-method {
+      flex: 1;
+    }
+
+    .payment-badge {
+      display: inline-flex;
+      align-items: center;
+      gap: 12px;
+      border: 1px solid #e5e5e5;
+      padding: 14px 20px;
+      border-radius: 16px;
+      margin-top: 12px;
+      font-size: 26px;
+      font-weight: bold;
+      color: #222;
+    }
+
+    .paid-status {
+      margin-left: 15px;
+      background: #dff7e7;
+      color: #1ea75d;
+      padding: 6px 14px;
+      border-radius: 999px;
+      font-size: 14px;
+      font-weight: bold;
+    }
+
+    /* FOOTER CARD */
+
+    .footer-card {
+      background: #f1ebff;
+      border-radius: 24px;
+      padding: 25px;
+      display: flex;
+      align-items: center;
+      gap: 20px;
+      margin-bottom: 24px;
+    }
+
+    .footer-diarinho {
+      width: 120px;
+    }
+
+    .footer-title {
+      font-size: 36px;
+      color: #5a22d6;
+      font-weight: 700;
+      margin-bottom: 10px;
+    }
+
+    .footer-text {
+      color: #666;
+      line-height: 1.7;
+      font-size: 18px;
+    }
+
+    /* FOOTER */
+
+    .footer {
+      background: linear-gradient(135deg, #5d1ce0, #3f0fb3);
+      padding: 35px 30px;
+      color: white;
+    }
+
+    .footer-content {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      gap: 20px;
+      flex-wrap: wrap;
+    }
+
+    .footer-app {
+      font-size: 30px;
+      font-weight: 700;
+      color: #6de0ff;
+    }
+
+    .footer-help {
+      text-align: right;
+      font-size: 18px;
+      line-height: 1.6;
+    }
+
+    .footer-bottom {
+      margin-top: 25px;
+      text-align: center;
+      font-size: 14px;
+      opacity: 0.7;
+    }
+
+    /* RESPONSIVE */
+
+    @media(max-width: 640px) {
+
+      .header-content,
+      .service-grid,
+      .payment-wrapper,
+      .footer-card {
+        flex-direction: column;
+      }
+
+      .divider {
+        width: 100%;
+        height: 1px;
+      }
+
+      .title {
+        font-size: 42px;
+      }
+
+      .diarinho {
+        width: 160px;
+      }
+
+      .card-title {
+        font-size: 28px;
+      }
+
+      .item-value {
+        font-size: 22px;
+      }
+
+      .payment-final {
+        flex-direction: column;
+        align-items: flex-start;
+        gap: 10px;
+      }
+
+      .footer-help {
+        text-align: left;
+      }
+    }
+
+  </style>
+
+</head>
+
+<body>
+
+<div class="email-container">
+
+  <!-- HEADER -->
+
+  <div class="header">
+
+    <div class="header-content">
+
+      <div class="header-text">
+
+        <div class="title">
+          {{ __('mail.service_completed.title') }}
+        </div>
+
+        <div class="subtitle">
+          {{ __('mail.service_completed.subtitle') }}
+        </div>
+
+      </div>
+
+      <img
+        src="{{ asset('images/diarinho.png') }}"
+        class="diarinho"
+        alt="Diarinho"
+      />
+
+    </div>
+
+  </div>
+
+  <!-- CONTENT -->
+
+  <div class="content">
+
+    <!-- STATUS -->
+
+    <div class="success-card">
+
+      <div class="success-icon">
+        ✓
+      </div>
+
+      <div>
+
+        <div class="success-title">
+          {{ __('mail.service_completed.payment_confirmed') }}
+        </div>
+
+        <div class="success-subtitle">
+          {{ __('mail.service_completed.payment_confirmed_subtitle') }}
+        </div>
+
+      </div>
+
+    </div>
+
+    <!-- SERVICE -->
+
+    <div class="card">
+
+      <div class="card-title">
+        {{ __('mail.service_completed.service_resume') }}
+      </div>
+
+      <div class="service-grid">
+
+        <div class="service-column">
+
+          <div class="item">
+
+            <div class="item-label">
+              {{ __('mail.service_completed.client') }}
+            </div>
+
+            <div class="item-value">
+              {{ $client_name }}
+            </div>
+
+          </div>
+
+          <div class="item">
+
+            <div class="item-label">
+              {{ __('mail.service_completed.service_date') }}
+            </div>
+
+            <div class="item-value">
+              {{ \Carbon\Carbon::parse($service_date)->format('d/m/Y') }}
+            </div>
+
+          </div>
+
+          <div class="item">
+
+            <div class="item-label">
+              {{ __('mail.service_completed.service_time') }}
+            </div>
+
+            <div class="item-value">
+              {{ substr($start_time, 0, 5) }}
+              às
+              {{ substr($end_time, 0, 5) }}
+            </div>
+
+          </div>
+
+        </div>
+
+        <div class="divider"></div>
+
+        <div class="service-column">
+
+          <div class="item">
+
+            <div class="item-label">
+              {{ __('mail.service_completed.service_address') }}
+            </div>
+
+            <div class="item-value">
+              {{ $address }}
+            </div>
+
+          </div>
+
+        </div>
+
+      </div>
+
+    </div>
+
+    <!-- PAYMENT -->
+
+    <div class="card">
+
+      <div class="card-title">
+        {{ __('mail.service_completed.payment_title') }}
+      </div>
+
+      <div class="payment-wrapper">
+
+        <div class="payment-total">
+
+          <div class="payment-summary">
+
+            <div class="payment-row">
+
+              <span>
+                {{ __('mail.service_completed.total_amount') }}
+              </span>
+
+              <strong>
+                {{ __('mail.currency') }}
+                {{ number_format($total_amount, 2, ',', '.') }}
+              </strong>
+
+            </div>
+
+            <div class="payment-row">
+
+              <span>
+                {{ __('mail.service_completed.service_fee') }}
+              </span>
+
+              <strong>
+                {{ __('mail.currency') }}
+                {{ number_format($service_fee, 2, ',', '.') }}
+              </strong>
+
+            </div>
+
+            <div class="payment-final">
+
+              <span class="payment-final-label">
+                {{ __('mail.service_completed.final_amount') }}
+              </span>
+
+              <span class="payment-final-value">
+                {{ __('mail.currency') }}
+                {{ number_format($final_amount, 2, ',', '.') }}
+              </span>
+
+            </div>
+
+          </div>
+
+        </div>
+
+        <div class="divider"></div>
+
+        <div class="payment-method">
+
+          <div class="item-label">
+            {{ __('mail.service_completed.payment_method') }}
+          </div>
+
+          <div class="payment-badge">
+
+            {{ $payment_method }}
+
+            <span class="paid-status">
+              {{ __('mail.service_completed.paid') }}
+            </span>
+
+          </div>
+
+        </div>
+
+      </div>
+
+    </div>
+
+    <!-- FOOTER CARD -->
+
+    <div class="footer-card">
+
+      <img
+        src="{{ asset('images/diarinho.png') }}"
+        class="footer-diarinho"
+        alt="Diarinho"
+      />
+
+      <div>
+
+        <div class="footer-title">
+          {{ __('mail.service_completed.footer_title') }}
+        </div>
+
+        <div class="footer-text">
+          {{ __('mail.service_completed.footer_description') }}
+        </div>
+
+      </div>
+
+    </div>
+
+  </div>
+
+  <!-- FOOTER -->
+
+  <div class="footer">
+
+    <div class="footer-content">
+
+      <div>
+
+        <div class="footer-app">
+          diariaapp
+        </div>
+
+      </div>
+
+      <div class="footer-help">
+
+        {{ __('mail.service_completed.footer_help') }}<br>
+
+        {{ __('mail.service_completed.footer_contact') }}<br>
+
+        (11) 99999-9999
+
+      </div>
+
+    </div>
+
+    <div class="footer-bottom">
+      {{ __('mail.service_completed.footer_note') }}
+    </div>
+
+  </div>
+
+</div>
+
+</body>
+
+</html>

+ 212 - 130
resources/views/emails/send_code.blade.php

@@ -1,138 +1,220 @@
 <!DOCTYPE html>
 <html lang="pt-BR">
+
 <head>
     <meta charset="UTF-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>{{ __('mail.send_code.subject') }}</title>
-    <style>
-        * { box-sizing: border-box; margin: 0; padding: 0; }
-
-        body {
-            background-color: #f4f4f7;
-            font-family: 'Segoe UI', Arial, sans-serif;
-            color: #333333;
-        }
-
-        .wrapper {
-            width: 100%;
-            padding: 40px 16px;
-        }
-
-        .card {
-            max-width: 520px;
-            margin: 0 auto;
-            background-color: #ffffff;
-            border-radius: 12px;
-            overflow: hidden;
-            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
-        }
-
-        .header {
-            background-color: #1a6fa8;
-            padding: 32px 40px;
-            text-align: center;
-        }
-
-        .header h1 {
-            color: #ffffff;
-            font-size: 22px;
-            font-weight: 700;
-            letter-spacing: 0.5px;
-        }
-
-        .header p {
-            color: #cce3f5;
-            font-size: 13px;
-            margin-top: 4px;
-        }
-
-        .body {
-            padding: 36px 40px;
-            text-align: center;
-        }
-
-        .body p {
-            font-size: 15px;
-            line-height: 1.6;
-            color: #555555;
-        }
-
-        .code-box {
-            display: inline-block;
-            margin: 28px 0;
-            padding: 18px 40px;
-            background-color: #f0f7ff;
-            border: 2px dashed #1a6fa8;
-            border-radius: 10px;
-        }
-
-        .code-box span {
-            display: block;
-            font-size: 42px;
-            font-weight: 800;
-            letter-spacing: 10px;
-            color: #1a6fa8;
-        }
-
-        .expiry {
-            font-size: 13px;
-            color: #999999;
-            margin-top: 8px;
-        }
-
-        .footer {
-            background-color: #f9f9fb;
-            border-top: 1px solid #eeeeee;
-            padding: 24px 40px;
-            text-align: center;
-        }
-
-        .footer p {
-            font-size: 12px;
-            color: #aaaaaa;
-            line-height: 1.6;
-        }
-
-        .footer strong {
-            color: #888888;
-        }
-    </style>
+
+    <title>
+        {{ __('mail.send_code.subject') }}
+    </title>
 </head>
-<body>
-    <div class="wrapper">
-        <div class="card">
-
-            <div class="header">
-                <h1>Diaria App</h1>
-                <p>{{ __('mail.send_code.header_subtitle') }}</p>
-            </div>
-
-            <div class="body">
-                @if (!empty($recipientName))
-                    <p>{{ __('mail.send_code.greeting', ['name' => $recipientName]) }}</p>
-                @else
-                    <p>{{ __('mail.send_code.greeting_anonymous') }}</p>
-                @endif
-
-                <p style="margin-top: 12px;">{{ __('mail.send_code.body_intro') }}</p>
-
-                <div class="code-box">
-                    <span>{{ $code }}</span>
-                </div>
-
-                <p style="margin-top: 20px; font-size: 13px; color: #999;">
-                    {{ __('mail.send_code.ignore_notice') }}
-                </p>
-            </div>
-
-            <div class="footer">
-                <p>
-                    &copy; {{ date('Y') }} <strong>Diaria App</strong><br />
-                    {{ __('mail.send_code.footer_note') }}
-                </p>
-            </div>
-
-        </div>
-    </div>
+
+<body style="margin:0;padding:0;background:#f5f5f7;font-family:Arial,Helvetica,sans-serif;">
+
+    <table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f5f5f7">
+        <tr>
+            <td align="center" style="padding:25px;">
+
+                <!-- CONTAINER -->
+                <table width="650" cellpadding="0" cellspacing="0" border="0" bgcolor="#ffffff"
+                    style="border-radius:24px;overflow:hidden;">
+
+                    <!-- HEADER -->
+                    <tr>
+                        <td style="background:#5a22d6;padding:50px 40px;color:white;">
+
+                            <table width="100%">
+                                <tr>
+
+                                    <!-- LEFT -->
+                                    <td valign="middle">
+
+                                        <div style="font-size:52px;font-weight:bold;line-height:1.1;">
+                                            {{ __('mail.send_code.subject') }}
+                                        </div>
+
+                                        <div style="font-size:20px;margin-top:20px;line-height:1.6;">
+                                            {{ __('mail.send_code.header_subtitle') }}
+                                        </div>
+
+                                    </td>
+
+                                    <!-- RIGHT -->
+                                    <td width="220" align="right" valign="middle">
+
+                                        <img src="{{ asset('images/diarinho_email_code.svg') }}" width="110" alt="Diarinho"
+                                            style="display:block;border:0;">
+
+                                    </td>
+
+                                </tr>
+                            </table>
+
+                        </td>
+                    </tr>
+
+                    <!-- CONTENT -->
+                    <tr>
+                        <td style="padding:30px;background:#f5f5f7;">
+
+                            <!-- MAIN CARD -->
+                            <table width="100%" bgcolor="#ffffff"
+                                style="border-radius:24px;padding:35px;margin-bottom:25px;">
+
+                                <!-- GREETING -->
+                                <tr>
+                                    <td>
+
+                                        @if (!empty($recipientName))
+                                            <div style="font-size:34px;font-weight:bold;color:#222;line-height:1.4;">
+                                                {{ __('mail.send_code.greeting', ['name' => $recipientName]) }}
+                                            </div>
+                                        @else
+                                            <div style="font-size:34px;font-weight:bold;color:#222;line-height:1.4;">
+                                                {{ __('mail.send_code.greeting_anonymous') }}
+                                            </div>
+                                        @endif
+
+                                    </td>
+                                </tr>
+
+                                <!-- BODY -->
+                                <tr>
+                                    <td style="padding-top:25px;font-size:20px;color:#666;line-height:1.8;">
+                                        {{ __('mail.send_code.body_intro') }}
+                                    </td>
+                                </tr>
+
+                                <!-- CODE -->
+                                <tr>
+                                    <td align="center" style="padding-top:35px;">
+
+                                        <table cellpadding="0" cellspacing="0" border="0" bgcolor="#f3ecff"
+                                            style="border-radius:24px;padding:35px 45px;">
+
+                                            <tr>
+
+                                                <td align="center">
+
+                                                    <div
+                                                        style="font-size:68px;font-weight:800;color:#5a22d6;letter-spacing:16px;">
+                                                        {{ $code }}
+                                                    </div>
+
+                                                </td>
+
+                                            </tr>
+
+                                        </table>
+
+                                    </td>
+                                </tr>
+
+                                <!-- EXPIRATION -->
+                                <tr>
+                                    <td align="center"
+                                        style="padding-top:28px;font-size:17px;color:#777;line-height:1.7;">
+
+                                        {{ __('mail.send_code.expiry_notice') }}
+
+                                    </td>
+                                </tr>
+
+                                <!-- WARNING -->
+                                <tr>
+                                    <td style="padding-top:30px;">
+
+                                        <table width="100%" bgcolor="#fff8e8"
+                                            style="border-radius:18px;padding:22px;">
+
+                                            <tr>
+
+                                                <td style="font-size:17px;color:#9a6a00;line-height:1.8;">
+
+                                                    {{ __('mail.send_code.ignore_notice') }}
+
+                                                </td>
+
+                                            </tr>
+
+                                        </table>
+
+                                    </td>
+                                </tr>
+
+                            </table>
+
+                            <!-- FOOTER CARD -->
+                            <table width="100%" bgcolor="#f1ebff"
+                                style="border-radius:24px;padding:25px;margin-bottom:25px;">
+
+                                <tr>
+
+                                    <td width="130" valign="middle">
+
+                                        <img src="{{ asset('images/diarinho_email_welcome.svg') }}" class="diarinho"
+                                            alt="Diarinho" />
+
+                                    </td>
+
+                                    <td valign="middle">
+
+                                        <div style="font-size:34px;font-weight:bold;color:#5a22d6;">
+
+                                            {{ __('mail.send_code.footer_title') }}
+
+                                        </div>
+
+                                        <div style="font-size:18px;color:#666;line-height:1.7;margin-top:10px;">
+
+                                            {{ __('mail.send_code.footer_note') }}
+
+                                        </div>
+
+                                    </td>
+
+                                </tr>
+
+                            </table>
+
+                        </td>
+                    </tr>
+
+                    <!-- FOOTER -->
+                    <tr>
+                        <td style="background:#5a22d6;padding:35px;color:white;">
+
+                            <table width="100%">
+
+                                <tr>
+                                    <!-- CONTACT -->
+                                    <td align="right" style="font-size:18px;line-height:1.7;">
+
+                                        {{ __('mail.send_code.footer_note') }}
+
+                                    </td>
+
+                                </tr>
+
+                            </table>
+
+                            <!-- BOTTOM -->
+                            <div style="margin-top:25px;text-align:center;font-size:13px;opacity:0.7;">
+
+                                © {{ date('Y') }}
+
+                            </div>
+
+                        </td>
+                    </tr>
+
+                </table>
+
+            </td>
+        </tr>
+    </table>
+
 </body>
+
 </html>

+ 276 - 0
resources/views/emails/send_code.html

@@ -0,0 +1,276 @@
+<!DOCTYPE html>
+<html lang="pt-BR">
+
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>{{ __('mail.send_code.subject') }}</title>
+</head>
+
+<body style="margin:0;padding:0;background:#f4f4f7;font-family:Arial,Helvetica,sans-serif;">
+
+  <table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f4f4f7">
+    <tr>
+      <td align="center" style="padding:30px 15px;">
+
+        <!-- CONTAINER -->
+        <table width="650" cellpadding="0" cellspacing="0" border="0"
+          style="background:#ffffff;border-radius:24px;overflow:hidden;">
+
+          <!-- HEADER -->
+          <tr>
+            <td
+              style="background:linear-gradient(135deg,#4f1fd6,#7b2cff);padding:50px 40px;">
+
+              <table width="100%">
+
+                <tr>
+
+                  <!-- LEFT -->
+                  <td valign="middle">
+
+                    <div
+                      style="font-size:56px;font-weight:800;line-height:1.1;color:#ffffff;">
+
+                      {{ __('mail.send_code.title') }}
+
+                    </div>
+
+                    <div
+                      style="font-size:22px;line-height:1.6;color:#e8dbff;margin-top:22px;">
+
+                      {{ __('mail.send_code.body_intro') }}
+
+                    </div>
+
+                  </td>
+
+                  <!-- RIGHT -->
+                  <td width="240" align="right" valign="middle">
+
+                    <img
+                      src="https://i.imgur.com/8Km9tLL.png"
+                      width="220"
+                      alt="Diarinho"
+                      style="display:block;border:0;"
+                    >
+
+                  </td>
+
+                </tr>
+
+              </table>
+
+            </td>
+          </tr>
+
+          <!-- CONTENT -->
+          <tr>
+            <td style="padding:35px;background:#f5f5f7;">
+
+              <!-- CARD -->
+              <table width="100%" cellpadding="0" cellspacing="0" border="0"
+                style="background:#ffffff;border-radius:24px;padding:35px;">
+
+                <!-- GREETING -->
+                <tr>
+                  <td>
+
+                    @if (!empty($recipientName))
+
+                    <div
+                      style="font-size:42px;font-weight:700;color:#1f1f1f;line-height:1.3;">
+
+                      {{ __('mail.send_code.greeting', ['name' => $recipientName]) }}
+
+                    </div>
+
+                    @else
+
+                    <div
+                      style="font-size:42px;font-weight:700;color:#1f1f1f;line-height:1.3;">
+
+                      {{ __('mail.send_code.greeting_anonymous') }}
+
+                    </div>
+
+                    @endif
+
+                  </td>
+                </tr>
+
+                <!-- TEXT -->
+                <tr>
+                  <td
+                    style="padding-top:25px;font-size:22px;line-height:1.8;color:#555555;">
+
+                    {{ __('mail.send_code.code_instruction') }}
+
+                  </td>
+                </tr>
+
+                <!-- CODE BOX -->
+                <tr>
+                  <td align="center" style="padding-top:35px;">
+
+                    <table cellpadding="0" cellspacing="0" border="0"
+                      style="background:#f5efff;border-radius:24px;padding:35px 40px;width:100%;">
+
+                      <tr>
+
+                        <td align="center">
+
+                          <div
+                            style="font-size:82px;font-weight:800;letter-spacing:18px;color:#5a22d6;">
+
+                            {{ $code }}
+
+                          </div>
+
+                        </td>
+
+                      </tr>
+
+                    </table>
+
+                  </td>
+                </tr>
+
+                <!-- EXPIRATION -->
+                <tr>
+                  <td align="center"
+                    style="padding-top:30px;font-size:18px;color:#666666;line-height:1.7;">
+
+                    ⏰ {{ __('mail.send_code.expiry_notice') }}
+
+                  </td>
+                </tr>
+
+                <!-- ALERT -->
+                <tr>
+                  <td style="padding-top:30px;">
+
+                    <table width="100%"
+                      style="background:#fff8e7;border-radius:18px;padding:22px;">
+
+                      <tr>
+
+                        <td
+                          style="font-size:18px;color:#9c6a00;line-height:1.7;">
+
+                          ⚠️ {{ __('mail.send_code.ignore_notice') }}
+
+                        </td>
+
+                      </tr>
+
+                    </table>
+
+                  </td>
+                </tr>
+
+              </table>
+
+              <!-- FOOTER CARD -->
+              <table width="100%" cellpadding="0" cellspacing="0" border="0"
+                style="background:#f3ecff;border-radius:24px;padding:25px;margin-top:25px;">
+
+                <tr>
+
+                  <!-- IMAGE -->
+                  <td width="140" valign="middle">
+
+                    <img
+                      src="https://i.imgur.com/7uX7N4F.png"
+                      width="120"
+                      alt="Diarinho"
+                      style="display:block;border:0;"
+                    >
+
+                  </td>
+
+                  <!-- TEXT -->
+                  <td valign="middle">
+
+                    <div
+                      style="font-size:34px;font-weight:700;color:#5a22d6;">
+
+                      {{ __('mail.send_code.footer_title') }}
+
+                    </div>
+
+                    <div
+                      style="font-size:20px;line-height:1.7;color:#666666;margin-top:10px;">
+
+                      {{ __('mail.send_code.footer_description') }}
+
+                    </div>
+
+                  </td>
+
+                </tr>
+
+              </table>
+
+            </td>
+          </tr>
+
+          <!-- FOOTER -->
+          <tr>
+            <td
+              style="background:linear-gradient(135deg,#4f1fd6,#7b2cff);padding:35px 30px;">
+
+              <table width="100%">
+
+                <tr>
+
+                  <!-- BRAND -->
+                  <td valign="top">
+
+                    <div
+                      style="font-size:34px;font-weight:800;color:#6de0ff;">
+
+                      diariaapp
+
+                    </div>
+
+                  </td>
+
+                  <!-- CONTACT -->
+                  <td align="right"
+                    style="font-size:18px;color:#ffffff;line-height:1.8;">
+
+                    {{ __('mail.send_code.footer_help') }}<br>
+
+                    {{ __('mail.send_code.footer_contact') }}<br>
+
+                    (11) 99999-9999
+
+                  </td>
+
+                </tr>
+
+              </table>
+
+              <!-- BOTTOM -->
+              <div
+                style="margin-top:28px;text-align:center;font-size:14px;color:#d6c5ff;line-height:1.7;">
+
+                © {{ date('Y') }} Diaria App<br>
+
+                {{ __('mail.send_code.footer_note') }}
+
+              </div>
+
+            </td>
+          </tr>
+
+        </table>
+
+      </td>
+    </tr>
+  </table>
+
+</body>
+
+</html>

+ 6 - 0
routes/authRoutes/client_calendar.php

@@ -0,0 +1,6 @@
+<?php
+
+use App\Http\Controllers\ClientCalendarController;
+use Illuminate\Support\Facades\Route;
+
+Route::get('/dados-agenda-cliente', [ClientCalendarController::class, 'index'])->middleware('permission:dashboard,view');

+ 6 - 0
routes/authRoutes/provider_calendar.php

@@ -0,0 +1,6 @@
+<?php
+
+use App\Http\Controllers\ProviderCalendarController;
+use Illuminate\Support\Facades\Route;
+
+Route::get('/dados-agenda-prestador', [ProviderCalendarController::class, 'index'])->middleware('permission:dashboard,view');

+ 1 - 0
routes/authRoutes/user.php

@@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Route;
 use App\Http\Controllers\UserController;
 
 Route::get('/user/me', [UserController::class, 'me']);
+Route::put('/me', [UserController::class, 'updateMe'])->middleware('permission:config.user,edit');
 Route::get('/user', [UserController::class, 'index'])->middleware('permission:config.user,view');
 Route::post('/user', [UserController::class, 'store'])->middleware('permission:config.user,add');
 Route::get('/user/{id}', [UserController::class, 'show'])->middleware('permission:config.user,view');

Some files were not shown because too many files changed in this diff