feat: Implementa novo componente do LiveWire.

This commit is contained in:
lukibeg 2025-11-01 23:13:36 -03:00
parent ac3ed58dae
commit 4d6d21da6f
3 changed files with 59 additions and 85 deletions

View File

@ -1,31 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use App\Models\User;
use App\Services\UserService;
class CreateUserController extends Controller
{
public function __construct(protected UserService $userService) {}
public function createUsers(Request $request): RedirectResponse
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|string|min:8',
'password_confirm' => 'required|string|min:8'
]);
try {
$user = $this->userService->createUser($validated);
return redirect()->route('users.view', ['user' => $user], 200)->with('message', 'Usuário cadastrado com sucesso!');
} catch (\Exception $e) {
return redirect()->route('users.create', status: 403)->with('error', $e->getMessage());
}
}
}

View File

@ -39,7 +39,7 @@ class CreateUser extends Component
* Note como injetamos o UserService direto no método! * Note como injetamos o UserService direto no método!
* O Livewire cuida disso para você, assim como o Laravel faz nos controllers. * O Livewire cuida disso para você, assim como o Laravel faz nos controllers.
*/ */
public function save(UserService $userService) public function createUser(UserService $userService)
{ {
// 3. Valida as propriedades públicas ($this->name, $this->email, etc.) // 3. Valida as propriedades públicas ($this->name, $this->email, etc.)

View File

@ -1,5 +1,5 @@
<div x-data="{ showModal: false }" x-on:create-user.window="showModal = false"> <div x-data="{ showModal: false }" x-on:create-user.window="showModal = false">
<button x-on:click="showModal = true" class="px-4 py-2 bg-blue-600 text-white rounded ..."> <button x-on:click="showModal = true" class="cursor-pointer px-4 py-2 bg-blue-600 text-white rounded ...">
Criar Novo Usuário Criar Novo Usuário
</button> </button>
@ -9,13 +9,16 @@
</div> </div>
@endif @endif
<div x-show="showModal" class="fixed inset-0 z-50 flex items-center justify-center bg-white/35" <div x-show="showModal" class="fixed inset-0 z-50 flex items-center justify-center bg-white/35">
style="display: none;"> <div x-on:click.outside="showModal = false" x-show="showModal"
<div x-on:click.outside="showModal = false" class="bg-white rounded-lg shadow-xl w-full max-w-lg p-6"> x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-90"
class="bg-white rounded-lg shadow-xl w-full max-w-lg p-6">
<h3 class="text-lg font-medium text-gray-900">Novo Usuário Nexus</h3> <h3 class="text-lg font-medium text-gray-900">Novo Usuário Nexus</h3>
<form wire:submit.prevent="save" class="mt-4 space-y-4"> <form wire:submit.prevent="createUser" class="mt-4 space-y-4">
@error('general') @error('general')
<div class="p-3 bg-red-100 text-red-700 rounded"> <div class="p-3 bg-red-100 text-red-700 rounded">
@ -25,21 +28,21 @@
<div> <div>
<label for="name" class="block text-sm ...">Nome</label> <label for="name" class="block text-sm ...">Nome</label>
<input type="text" wire:model="name" id="name" <input type="text" wire:model.defer="name" id="name"
class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:border-blue-200 transition delay-150 duration-300 ease-in-out focus:border-blue-200"> class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:border-blue-200 transition delay-150 duration-300 ease-in-out focus:border-blue-200">
@error('name') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror @error('name') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
</div> </div>
<div> <div>
<label for="email" class="block text-sm ...">Email</label> <label for="email" class="block text-sm ...">Email</label>
<input type="email" wire:model="email" id="email" <input type="email" wire:model.defer="email" id="email"
class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:border-blue-200 transition delay-150 duration-300 ease-in-out focus:border-blue-200"> class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:border-blue-200 transition delay-150 duration-300 ease-in-out focus:border-blue-200">
@error('email') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror @error('email') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
</div> </div>
<div> <div>
<label for="password" class="block text-sm ...">Senha</label> <label for="password" class="block text-sm ...">Senha</label>
<input type="password" wire:model="password" id="password" <input type="password" wire:model.defer="password" id="password"
class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:border-blue-200 transition delay-150 duration-300 ease-in-out focus:border-blue-200"> class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:border-blue-200 transition delay-150 duration-300 ease-in-out focus:border-blue-200">
@error('password') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror @error('password') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
</div> </div>
@ -54,53 +57,54 @@ class="mt-1 block w-full border-2 border-gray-200 rounded-md outline-none hover:
<div> <div>
<label for="permissions" class="block text-sm font-medium text-gray-700">Permissões</label> <label for="permissions" class="block text-sm font-medium text-gray-700">Permissões</label>
<div <div x-data="{
x-data="{
open: false, open: false,
selected: @entangle('permission_level'), selected: @entangle('permission_level'),
options: { '0': 'Usuário', '1': 'Admin' } options: { '0': 'Usuário', '1': 'Admin' }
}" }" x-on:click.outside="open = false" class="relative mt-1">
x-on:click.outside="open = false" class="relative mt-1" <button type="button" x-on:click="open = true"
> class="relative h-10 w-40 cursor-default rounded-lg border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm p-2">
<button
type="button"
x-on:click="open = true" class="relative h-10 w-40 cursor-default rounded-lg border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm p-2"
>
<span class="block truncate" x-text="options[selected]"></span> <span class="block truncate" x-text="options[selected]"></span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg"
<path fill-rule="evenodd" d="M10 3a.75.75 0 01.53.22l3.5 3.5a.75.75 0 01-1.06 1.06L10 4.81 6.53 8.28a.75.75 0 01-1.06-1.06l3.5-3.5A.75.75 0 0110 3zm-3.5 9.28a.75.75 0 011.06 0L10 15.19l3.47-3.47a.75.75 0 111.06 1.06l-3.5 3.5a.75.75 0 01-1.06 0l-3.5-3.5a.75.75 0 010-1.06z" clip-rule="evenodd" /> viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd"
d="M10 3a.75.75 0 01.53.22l3.5 3.5a.75.75 0 01-1.06 1.06L10 4.81 6.53 8.28a.75.75 0 01-1.06-1.06l3.5-3.5A.75.75 0 0110 3zm-3.5 9.28a.75.75 0 011.06 0L10 15.19l3.47-3.47a.75.75 0 111.06 1.06l-3.5 3.5a.75.75 0 01-1.06 0l-3.5-3.5a.75.75 0 010-1.06z"
clip-rule="evenodd" />
</svg> </svg>
</span> </span>
</button> </button>
<div <div x-show="open" x-transition style="display: none;"
x-show="open" class="absolute z-10 mt-1 w-full max-h-60 overflow-auto rounded-md bg-white py-1 shadow-lg">
x-transition <div x-on:click="selected = '0'; open = false"
style="display: none;" class="relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900 hover:bg-blue-100"
class="absolute z-10 mt-1 w-full max-h-60 overflow-auto rounded-md bg-white py-1 shadow-lg"
>
<div
x-on:click="selected = '0'; open = false" class="relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900 hover:bg-blue-100"
:class="{ 'bg-blue-50': selected == '0' }"> :class="{ 'bg-blue-50': selected == '0' }">
<span class="block truncate" :class="{ 'font-semibold': selected == '0' }">Usuário</span> <span class="block truncate"
<span x-show="selected == '0'" class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600"> :class="{ 'font-semibold': selected == '0' }">Usuário</span>
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> <span x-show="selected == '0'"
<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd" /> class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600">
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd" />
</svg> </svg>
</span> </span>
</div> </div>
<div <div x-on:click="selected = '1'; open = false"
x-on:click="selected = '1'; open = false"
class="relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900 hover:bg-blue-100" class="relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900 hover:bg-blue-100"
:class="{ 'bg-blue-50': selected == '1' }" :class="{ 'bg-blue-50': selected == '1' }">
>
<span class="block truncate" :class="{ 'font-semibold': selected == '1' }">Admin</span> <span class="block truncate" :class="{ 'font-semibold': selected == '1' }">Admin</span>
<span x-show="selected == '1'" class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600"> <span x-show="selected == '1'"
<svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"> class="absolute inset-y-0 left-0 flex items-center pl-3 text-blue-600">
<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd" /> <svg class="h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
fill="currentColor">
<path fill-rule="evenodd"
d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
clip-rule="evenodd" />
</svg> </svg>
</span> </span>
</div> </div>
@ -112,12 +116,13 @@ class="relative cursor-default select-none py-2 pl-10 pr-4 text-gray-900 hover:b
</div> </div>
<div class="flex justify-end space-x-3 pt-4"> <div class="flex justify-end space-x-3 pt-4">
<button type="button" @click="showModal = false" class="px-4 py-2 rounded-md bg-gray-200 ..."> <button type="button" @click="showModal = false"
class="cursor-pointer px-4 py-2 rounded-md bg-gray-200 ...">
Cancelar Cancelar
</button> </button>
<button type="submit" class="px-4 py-2 bg-blue-600 rounded-md text-white ..."> <button type="submit" class="cursor-pointer px-4 py-2 bg-blue-600 rounded-md text-white ...">
<span wire:loading.remove wire:target="save">Salvar</span> <span wire:loading.remove wire:target="createUser">Salvar</span>
<span wire:loading wire:target="save">Salvando...</span> <span wire:loading wire:target="createUser" class="cursor-progress">Salvando...</span>
</button> </button>
</div> </div>
</form> </form>