Compare commits

...

11 Commits

Author SHA1 Message Date
lukibeg 0dc2c8d88c feat: Lista os clientes dinâmicos. 2025-11-16 21:55:17 -03:00
lukibeg b1207b3ed9 Revert "feat: Lista os clientes din"
This reverts commit aa4d6d2ccb.
2025-11-16 21:51:40 -03:00
lukibeg aa4d6d2ccb feat: Lista os clientes din 2025-11-16 21:50:40 -03:00
lukibeg 3363e9eede feat: Carrega clientes dinâmicamente. 2025-11-16 21:50:15 -03:00
lukibeg 5451f5c9ab feat: Deleta clientes. 2025-11-16 21:49:50 -03:00
lukibeg 6abd11dce7 refactor: Apenas carrega componentes dinâmicos. 2025-11-16 21:49:18 -03:00
lukibeg 4afc2b4e0a refactor|feat: Aprimora transições e apaga cliente de forma permanente. 2025-11-16 21:48:26 -03:00
lukibeg c788a80d03 feat: Agora verifica se o usuário está logado antes de concluir uma ação. 2025-11-16 21:47:22 -03:00
lukibeg a7b0de06f1 Sem mudanças. 2025-11-16 21:45:54 -03:00
lukibeg 73bc359885 Sem mudanças. 2025-11-16 21:45:29 -03:00
lukibeg 16b3e0dd17 refactor: Renderiza o dashboard geral. 2025-11-16 21:42:45 -03:00
9 changed files with 124 additions and 164 deletions

View File

@ -13,7 +13,6 @@ class ClientController extends Controller
public function __construct(ClientService $userService) {}
public function dashboard(Request $request): View
{
$clients = Client::all();
return view('dashboard', ['clients' => $clients]);
return view('dashboard');
}
}

View File

@ -5,6 +5,7 @@
use App\Models\Client;
use App\Services\ClientService;
use App\Livewire\Forms\ClientForm; // 1. Importa seu Form Object
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\Crypt;

View File

@ -0,0 +1,30 @@
<?php
namespace App\Livewire\Admin;
use Livewire\Component;
use Livewire\Attributes\On;
use App\Models\Client;
class DeleteClient extends Component
{
#[On('confirm-delete')]
public function deleteClient($payload)
{
$deletedClient = Client::findOrFail($payload);
if ($deletedClient) {
$deletedClient->delete();
}
$this->dispatch('clientDeleted');
// (Opcional) Envia uma notificação de sucesso
$this->dispatch('notify', message: 'Cliente excluído com sucesso!');
}
public function render()
{
return '<div></div>';
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Livewire\Admin;
use App\Models\Client;
use Livewire\Component;
use Livewire\Attributes\On;
class ShowClient extends Component
{
#[On('clientDeleted')]
public function refreshClientList() {}
public function render()
{
$clients = Client::all();
return view('livewire.admin.show-client', [
'clients' => $clients
]);
}
}

View File

@ -8,7 +8,6 @@
class ClientForm extends Form
{
// 2. ATRIBUTOS REMOVIDOS: Os #[Rule(...)] foram removidos daqui
public $client_name = '';
public $legal_name = '';
public $cnpj = '';

View File

@ -4,6 +4,7 @@
use App\Models\Client;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
class ClientService
@ -12,6 +13,10 @@ class ClientService
public function __construct(protected Client $client) {}
public function addClient(array $client)
{
if (!Auth::check()) {
return redirect()->to('/login');
}
return Client::create($client);
}
}

View File

@ -3,42 +3,35 @@
- 'showQuestion' controla a visibilidade.
- '@sure.window' é o evento que dispara a abertura.
-->
<div x-data="{showQuestion: false}"
@sure.window="showQuestion = true"
x-cloak {{-- x-cloak é bom para evitar "piscadas" na tela --}}
>
<div x-data="{showQuestion: false, clientId: null}" @sure.window="showQuestion = true; clientId = $event.detail.id"
x-cloak {{-- x-cloak é bom para evitar "piscadas" na tela --}}>
<!--
1. O OVERLAY (Fundo)
Copiado 1-para-1 do seu 'create-user'
-->
<div x-show="showQuestion"
class="modal-overlay"
x-transition:enter.duration.300ms
x-transition:leave.duration.300ms
>
<div x-show="showQuestion" class="modal-overlay" x-transition:enter.duration.300ms
x-transition:leave.duration.300ms>
<!--
2. O CONTAINER (Card)
Copiado 1-para-1 do seu 'create-user'
-->
<div x-on:click.outside="showQuestion = false"
x-show="showQuestion"
x-transition:enter="transition-enter"
x-transition:enter-start="transition-enter-start"
x-transition:enter-end="transition-enter-end"
x-transition:leave="transition-leave"
x-transition:leave-start="transition-leave-start"
x-transition:leave-end="transition-leave-end"
class="modal-container max-w-sm" {{-- Adicionei 'max-w-sm' para um modal de alerta menor --}}
>
<div x-on:click.outside="showQuestion = false" x-show="showQuestion" x-transition:enter="transition-enter"
x-transition:enter-start="transition-enter-start" x-transition:enter-end="transition-enter-end"
x-transition:leave="transition-leave" x-transition:leave-start="transition-leave-start"
x-transition:leave-end="transition-leave-end" class="modal-container max-w-sm" {{-- Adicionei 'max-w-sm'
para um modal de alerta menor --}}>
<!-- 3. O NOVO CONTEÚDO (Confirmação) -->
<!-- Ícone de Alerta -->
<div class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
<div
class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
</div>
@ -65,19 +58,12 @@ class="modal-container max-w-sm" {{-- Adicionei 'max-w-sm' para um modal de aler
O botão "Sim, Excluir" deve vir primeiro no HTML
para o 'flex-row-reverse' funcionar.
-->
<button type="button"
{{--
Aqui, ele dispara o evento 'confirm-delete'
(que seu Livewire pode ouvir) e fecha o modal.
--}}
@click="$dispatch('confirm-delete'); showQuestion = false"
<button type="button" @click="$dispatch('confirm-delete', [clientId]); showQuestion = false"
class="btn-submit bg-red-600 hover:bg-red-700 focus:ring-red-500 w-full sm:w-auto">
Sim, Excluir
</button>
<button type="button"
@click="showQuestion = false"
class="btn-cancel mt-3 sm:mt-0 w-full sm:w-auto">
<button type="button" @click="showQuestion = false" class="btn-cancel mt-3 sm:mt-0 w-full sm:w-auto">
Cancelar
</button>
</div>

View File

@ -5,128 +5,7 @@
<livewire:admin.create-user />
<livewire:admin.add-client />
<livewire:admin.show-users />
<livewire:admin.delete-client />
<x-are-you-sure />
<div class="container grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
@foreach ($clients as $client)
<div class="client-card">
<div class="client-card-header">
<div class="client-avatar">
<img src="{{ Vite::asset('resources/images/mr-distribuidora.svg') }}" alt="Avatar do Cliente"
class="w-32 h-32 rounded-full object-cover">
</div>
<div x-data="{ open: false }" @click.outside="open = false" class="client-options-menu">
<button @click="open = !open" class="client-options-button">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 hover:text-gray-700"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg>
</button>
<ul x-show="open" class="client-options-list" x-transition>
<li><a href="#" class="client-option-item">Ver Detalhes</a></li>
<li><a href="#" class="client-option-item">Editar Cliente</a></li>
<li>
<a href="#" class="client-option-item text-red-600" x-on:click.prevent="$dispatch('sure')">
Excluir Cliente
</a>
</li>
</ul>
</div>
</div>
<div class="client-card-name">
{{ $client->name }}
</div>
</div>
@endforeach
<div class="client-card">
<div class="client-card-header">
<div class="client-avatar">
<img src="{{ Vite::asset('resources/images/maissaude.svg') }}" alt="Avatar do Cliente"
class="w-32 h-32 rounded-full object-cover">
</div>
<div x-data="{ open: false }" @click.outside="open = false" class="client-options-menu">
<button @click="open = !open" class="client-options-button">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 hover:text-gray-700"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg>
</button>
<ul x-show="open" class="client-options-list" x-transition>
<li><a href="#" class="client-option-item">Ver Detalhes</a></li>
<li><a href="#" class="client-option-item">Editar Cliente</a></li>
<li><a href="#" class="client-option-item text-red-600">Excluir Cliente</a></li>
</ul>
</div>
</div>
<div class="client-card-name">
Cliente 2
</div>
</div>
<div class="client-card">
<div class="client-card-header">
<div class="client-avatar">
<img src="{{ Vite::asset('resources/images/logo.png') }}" alt="Avatar do Cliente"
class="w-32 h-32 rounded-full object-cover">
</div>
<div x-data="{ open: false }" @click.outside="open = false" class="client-options-menu">
<button @click="open = !open" class="client-options-button">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 hover:text-gray-700"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg>
</button>
<ul x-show="open" class="client-options-list" x-transition>
<li><a href="#" class="client-option-item">Ver Detalhes</a></li>
<li><a href="#" class="client-option-item">Editar Cliente</a></li>
<li><a href="#" class="client-option-item text-red-600">Excluir Cliente</a></li>
</ul>
</div>
</div>
<div class="client-card-name">
Cliente 3
</div>
</div>
<div class="client-card">
<div class="client-card-header">
<div class="client-avatar">
<img src="{{ Vite::asset('resources/images/logo.png') }}" alt="Avatar do Cliente"
class="w-32 h-32 rounded-full object-cover">
</div>
<div x-data="{ open: false }" @click.outside="open = false" class="client-options-menu">
<button @click="open = !open" class="client-options-button">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 hover:text-gray-700"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg>
</button>
<ul x-show="open" class="client-options-list" x-transition>
<li><a href="#" class="client-option-item">Ver Detalhes</a></li>
<li><a href="#" class="client-option-item">Editar Cliente</a></li>
<li><a href="#" class="client-option-item text-red-600">Excluir Cliente</a></li>
</ul>
</div>
</div>
<div class="client-card-name">
Cliente 4
</div>
</div>
</div>
<livewire:admin.show-client />
@endsection

View File

@ -0,0 +1,38 @@
<div class="container grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
@foreach ($clients as $client)
<div class="client-card">
<div class="client-card-header">
<div class="client-avatar">
<img src="{{ Vite::asset('resources/images/mr-distribuidora.svg') }}" alt="Avatar do Cliente"
class="w-32 h-32 rounded-full object-cover">
</div>
<div x-data="{ open: false }" @click.outside="open = false" class="client-options-menu">
<button @click="open = !open" class="client-options-button">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400 hover:text-gray-700"
viewBox="0 0 20 20" fill="currentColor">
<path
d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg>
</button>
<ul x-show="open" class="client-options-list" x-transition>
<li><a href="#" class="client-option-item">Ver Detalhes</a></li>
<li><a href="#" class="client-option-item">Editar Cliente</a></li>
<li>
<a href="#" class="client-option-item text-red-600"
x-on:click.prevent="$dispatch('sure', { id: '{{ $client->id }}' })">
Excluir Cliente
</a>
</li>
</ul>
</div>
</div>
<div class="client-card-name">
{{ $client->name }}
</div>
</div>
@endforeach
</div>