Compare commits
11 Commits
692378d0cd
...
cc52c73f69
| Author | SHA1 | Date |
|---|---|---|
|
|
cc52c73f69 | |
|
|
578e26f5da | |
|
|
9141763f22 | |
|
|
765cf2bfa5 | |
|
|
c9cd343fcd | |
|
|
d15074b4b9 | |
|
|
76226ab7f7 | |
|
|
d89f8cf52e | |
|
|
f18486b4c9 | |
|
|
759c7d6624 | |
|
|
19577fb53b |
|
|
@ -23,6 +23,9 @@ public function login(Request $request)
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$request->flashOnly(['email']);
|
||||||
|
$teste = $request->old('email');
|
||||||
|
|
||||||
if (!Auth::attempt($validated)) {
|
if (!Auth::attempt($validated)) {
|
||||||
return redirect()->route('login', status: 302)->with('error', 'Credenciais inválidas');
|
return redirect()->route('login', status: 302)->with('error', 'Credenciais inválidas');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire\Admin\Client;
|
||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Services\ClientService;
|
use App\Services\ClientService;
|
||||||
|
|
@ -18,9 +18,11 @@ class AddClient extends Component
|
||||||
public function save(ClientService $clientService)
|
public function save(ClientService $clientService)
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->form->validate();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
$this->authorize('addClient', Auth::user());
|
||||||
|
$this->form->validate();
|
||||||
|
|
||||||
$data = $this->form->all();
|
$data = $this->form->all();
|
||||||
$data['name'] = $data['client_name'];
|
$data['name'] = $data['client_name'];
|
||||||
|
|
||||||
|
|
@ -36,13 +38,13 @@ public function save(ClientService $clientService)
|
||||||
$this->dispatch('client-added');
|
$this->dispatch('client-added');
|
||||||
$this->dispatch('notify', message: $client->name . ' adicionado com sucesso!');
|
$this->dispatch('notify', message: $client->name . ' adicionado com sucesso!');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->dispatch('notify', message: 'Ocorreu um erro inesperado ao salvar.', type: 'error');
|
$this->dispatch('notify', message: 'Ocorreu um erro inesperado ao salvar. ' . $e->getMessage(), type: 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.admin.add-client');
|
return view('livewire.admin.clients.add-client');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Admin\Client;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use App\Models\Client;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class DeleteClient extends Component
|
||||||
|
{
|
||||||
|
#[On('confirm-delete')]
|
||||||
|
public function deleteClient($payload)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Sua lógica de autorização e exclusão (Correta)
|
||||||
|
$this->authorize('deleteClient', Auth::user());
|
||||||
|
|
||||||
|
$deletedClient = Client::findOrFail($payload);
|
||||||
|
|
||||||
|
if ($deletedClient) {
|
||||||
|
$deletedClient->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sucesso (Dentro do try, onde deve estar)
|
||||||
|
$this->dispatch('client-deleted');
|
||||||
|
$this->dispatch('notify', message: 'Cliente excluído com sucesso!');
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Tratamento de erro
|
||||||
|
$this->dispatch('notify', message: 'Você não possui permissão para realizar essa ação.', type: 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return '<div></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Admin\Client;
|
||||||
|
|
||||||
|
use App\Livewire\Forms\ClientForm;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use App\Models\Client;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
|
|
||||||
|
class DetailClient extends Component
|
||||||
|
{
|
||||||
|
|
||||||
|
public ClientForm $clientForm;
|
||||||
|
public ?Client $client = null;
|
||||||
|
|
||||||
|
#[On('client-detail')]
|
||||||
|
public function detailClient($id)
|
||||||
|
{
|
||||||
|
$this->client = Client::findOrFail($id);
|
||||||
|
$decryptedPassword = Crypt::decryptString($this->client->root_password);
|
||||||
|
$this->client->root_password = $decryptedPassword;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.admin.clients.detail-client');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire\Admin\Client;
|
||||||
|
|
||||||
use App\Livewire\Forms\ClientForm;
|
use App\Livewire\Forms\ClientForm;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Services\ClientService;
|
use App\Services\ClientService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Crypt;
|
||||||
use Livewire\Attributes\On;
|
use Livewire\Attributes\On;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Livewire\WithFileUploads;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class EditClient extends Component
|
class EditClient extends Component
|
||||||
{
|
{
|
||||||
|
use WithFileUploads;
|
||||||
|
|
||||||
public Client $client;
|
public Client $client;
|
||||||
public ClientForm $clientForm;
|
public ClientForm $clientForm;
|
||||||
// public ClientService $clientService;
|
|
||||||
|
|
||||||
#[On('update-client')]
|
#[On('update-client')]
|
||||||
|
|
||||||
|
|
||||||
public function loadClient($id)
|
public function loadClient($id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
@ -33,9 +35,19 @@ public function loadClient($id)
|
||||||
}
|
}
|
||||||
public function edit(ClientService $clientService)
|
public function edit(ClientService $clientService)
|
||||||
{
|
{
|
||||||
$data = $this->clientForm->validate();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
$this->authorize('editClient', Auth::user());
|
||||||
|
$data = $this->clientForm->validate();
|
||||||
|
|
||||||
|
if ($this->clientForm->profile_image_path) {
|
||||||
|
$path = $this->clientForm->profile_image_path->store('client_logos', 'public');
|
||||||
|
$data['profile_image_path'] = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data['root_password'] = Crypt::encryptString($data['root_password']);
|
||||||
|
|
||||||
if (!$clientService->updateClient($this->client, $data)) {
|
if (!$clientService->updateClient($this->client, $data)) {
|
||||||
throw new Exception('O serviço não confirmou a atualização.');
|
throw new Exception('O serviço não confirmou a atualização.');
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +60,6 @@ public function edit(ClientService $clientService)
|
||||||
}
|
}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.admin.edit-client');
|
return view('livewire.admin.clients.edit-client');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<?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>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,10 +2,23 @@
|
||||||
|
|
||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire\Admin;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\UserService;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
|
||||||
class ShowUsers extends Component
|
class ShowUsers extends Component
|
||||||
{
|
{
|
||||||
|
public $users = [];
|
||||||
|
|
||||||
|
#[On('user-updated')]
|
||||||
|
#[On('user-created')]
|
||||||
|
|
||||||
|
public function mount(UserService $userService)
|
||||||
|
{
|
||||||
|
$this->users = $userService->showUsers();
|
||||||
|
}
|
||||||
|
public function show(UserService $userService) {}
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.admin.show-users');
|
return view('livewire.admin.show-users');
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire\Admin\User;
|
||||||
|
|
||||||
use App\Services\UserService;
|
use App\Services\UserService;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -34,11 +34,10 @@ class CreateUser extends Component
|
||||||
public function createUser(UserService $userService)
|
public function createUser(UserService $userService)
|
||||||
{
|
{
|
||||||
|
|
||||||
$validated = $this->validate($this->rules, $this->messages);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$this->authorize('createUser', Auth::user());
|
$this->authorize('createUser', Auth::user());
|
||||||
|
$validated = $this->validate($this->rules, $this->messages);
|
||||||
|
|
||||||
$user = $userService->createUser($validated);
|
$user = $userService->createUser($validated);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -49,11 +48,12 @@ public function createUser(UserService $userService)
|
||||||
$this->dispatch('notify', message: 'Usuário cadastrado com sucesso!');
|
$this->dispatch('notify', message: 'Usuário cadastrado com sucesso!');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->addError('general', $e->getMessage());
|
$this->addError('general', $e->getMessage());
|
||||||
|
$this->dispatch('notify', message: 'Ocorreu um erro ao criar o usuário. ' . $e->getMessage(), type: 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.admin.create-user');
|
return view('livewire.admin.users.create-user');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Admin\User;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\UserService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class DeleteUser extends Component
|
||||||
|
{
|
||||||
|
|
||||||
|
#[On('confirm-delete-user')]
|
||||||
|
public function deleteUser(UserService $userService, $payload)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
|
||||||
|
$this->authorize('deleteUser', Auth::user());
|
||||||
|
|
||||||
|
$deletedUser = User::findOrFail($payload);
|
||||||
|
if ($deletedUser) {
|
||||||
|
$deletedUser = $userService->deleteUser($deletedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dispatch('user-deleted');
|
||||||
|
$this->dispatch('notify', message: $deletedUser->name . ' Usuário excluído com sucesso!');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->dispatch('user-delete-error');
|
||||||
|
$this->dispatch('notify', message: $e->getMessage(), type: 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return '<div></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Admin\User;
|
||||||
|
|
||||||
|
use App\Livewire\Forms\UserForm;
|
||||||
|
use App\Models\User;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use App\Services\UserService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class EditUser extends Component
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public UserService $userService;
|
||||||
|
public User $user;
|
||||||
|
public UserForm $userForm;
|
||||||
|
#[On('update-user')]
|
||||||
|
public function loadUser($id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->user = User::find($id);
|
||||||
|
|
||||||
|
|
||||||
|
if ($this->user) {
|
||||||
|
$this->userForm->editUser($this->user);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->dispatch('notify', message: 'Ocorreu um erro inesperado ao editar o usuário. ' . $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function editUser(UserService $userService)
|
||||||
|
{
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->authorize('editUser', Auth::user());
|
||||||
|
$data = $this->userForm->validate();
|
||||||
|
|
||||||
|
if (!$userService->updateUser($this->user, $data)) {
|
||||||
|
throw new \Exception('O serviço não confirmou a atualização.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dispatch('user-updated');
|
||||||
|
$this->dispatch('notify', message: $this->userForm->name . ' atualizado com sucesso!');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->dispatch('notify', message: 'Falha na edição: ' . $e->getMessage(), type: 'error');
|
||||||
|
// $this->addError('general', $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.admin.users.edit-user');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire\Forms;
|
||||||
|
|
||||||
|
use Livewire\Attributes\Validate;
|
||||||
|
use Livewire\Form;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserForm extends Form
|
||||||
|
{
|
||||||
|
public string $name = '';
|
||||||
|
public string $email = '';
|
||||||
|
public string $password = '';
|
||||||
|
public string $password_confirm = '';
|
||||||
|
public string $permissions = '';
|
||||||
|
|
||||||
|
|
||||||
|
public function editUser(User $user)
|
||||||
|
{
|
||||||
|
$data = $user->toArray();
|
||||||
|
|
||||||
|
$data['permissions'] = $data['permissions'][0];
|
||||||
|
|
||||||
|
$this->fill($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $rules = [
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'email' => 'required|email',
|
||||||
|
'password' => 'string|min:8',
|
||||||
|
'password_confirm' => 'string|same:password',
|
||||||
|
'permissions' => 'required|string|in:user,admin'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $messages = [
|
||||||
|
'name' => 'Nome precisa ser informado.',
|
||||||
|
'email' => 'O email precisa ser informado.',
|
||||||
|
'password' => 'A senha precisa ter 8 ou mais caracteres.',
|
||||||
|
'password_confirm' => 'As senhas não coincidem.',
|
||||||
|
'permissions' => 'Escolha o nível de autorização do usuário.'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire;
|
||||||
|
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
@ -9,10 +9,9 @@
|
||||||
class ShowClient extends Component
|
class ShowClient extends Component
|
||||||
{
|
{
|
||||||
|
|
||||||
#[On('clientDeleted')]
|
#[On('client-deleted')]
|
||||||
|
#[On('client-updated')]
|
||||||
#[On('client-added')]
|
#[On('client-added')]
|
||||||
public function refreshClientList() {}
|
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
$clients = Client::all();
|
$clients = Client::all();
|
||||||
|
|
@ -28,6 +28,9 @@ class Client extends Model
|
||||||
'carrier',
|
'carrier',
|
||||||
'access_type',
|
'access_type',
|
||||||
'server_ip',
|
'server_ip',
|
||||||
|
'port',
|
||||||
|
'ddr_key',
|
||||||
|
'ddr_out',
|
||||||
'root_password', // Lembre-se de criptografar isso!
|
'root_password', // Lembre-se de criptografar isso!
|
||||||
'has_call_center',
|
'has_call_center',
|
||||||
'has_voice_gateway',
|
'has_voice_gateway',
|
||||||
|
|
@ -35,8 +38,18 @@ class Client extends Model
|
||||||
'modules',
|
'modules',
|
||||||
'whatsapp_number',
|
'whatsapp_number',
|
||||||
'whatsapp_activation_date',
|
'whatsapp_activation_date',
|
||||||
|
'observation',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
- Número chave, número de saída
|
||||||
|
- Credenciais de acesso VPN
|
||||||
|
- Anexos
|
||||||
|
- Porta de acesso
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Os atributos que devem ser convertidos para tipos nativos.
|
* Os atributos que devem ser convertidos para tipos nativos.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,21 @@ public function register(): void
|
||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
Gate::define('createUser', function (User $user) {
|
// Lista de todas as ações que são exclusivas de Admin
|
||||||
return isset($user->permissions) ? in_array('admin', $user->permissions) : false;
|
$adminActions = [
|
||||||
});
|
'createUser',
|
||||||
|
'editUser',
|
||||||
|
'deleteUser',
|
||||||
|
'addClient', // Exemplo
|
||||||
|
'deleteClient', // Exemplo
|
||||||
|
'editClient',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($adminActions as $action) {
|
||||||
|
Gate::define($action, function (User $user) {
|
||||||
|
// A lógica fica centralizada aqui. Se mudar, muda pra todos.
|
||||||
|
return isset($user->permissions) && in_array('admin', $user->permissions);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,10 @@ public function updateClient(Client $client, $data)
|
||||||
$data['name'] = $data['client_name'];
|
$data['name'] = $data['client_name'];
|
||||||
return $client->update($data);
|
return $client->update($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function detailClient(Client $client)
|
||||||
|
{
|
||||||
|
//Detail method
|
||||||
|
return $client->all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
class UserService
|
class UserService
|
||||||
|
|
@ -11,6 +13,29 @@ class UserService
|
||||||
public function __construct(protected User $user) {}
|
public function __construct(protected User $user) {}
|
||||||
public function createUser(array $user)
|
public function createUser(array $user)
|
||||||
{
|
{
|
||||||
|
$permissions = [$user['permissions']];
|
||||||
|
$user['permissions'] = $permissions;
|
||||||
return User::create($user);
|
return User::create($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function showUsers()
|
||||||
|
{
|
||||||
|
$users = User::all();
|
||||||
|
return $users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateUser(User $user, $data)
|
||||||
|
{
|
||||||
|
return $user->update($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteUser($user)
|
||||||
|
{
|
||||||
|
if ($user->id === Auth::user()->id) {
|
||||||
|
throw new Exception('Não é possível excluir seu próprio usuário.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->delete();
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->uuid('id')->primary();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->json('permissions')->nullable();
|
$table->json('permissions')->nullable();
|
||||||
$table->string('email')->unique();
|
$table->string('email')->unique();
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,13 @@
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('clients', function (Blueprint $table) {
|
Schema::create('clients', function (Blueprint $table) {
|
||||||
// 1. Alterado de $table->id() para $table->uuid()
|
|
||||||
$table->uuid('id')->primary();
|
$table->uuid('id')->primary();
|
||||||
|
|
||||||
// Informações de Cadastro
|
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('legal_name');
|
$table->string('legal_name');
|
||||||
$table->string('cnpj')->unique()->nullable();
|
$table->string('cnpj')->unique()->nullable();
|
||||||
|
|
||||||
// 2. Adicionado o path da imagem
|
$table->string('profile_image_path')->nullable();
|
||||||
$table->string('profile_image_path')->nullable(); // "path da imagem do cliente"
|
|
||||||
|
|
||||||
// Informações Técnicas
|
// Informações Técnicas
|
||||||
$table->string('pbx_hosting')->nullable();
|
$table->string('pbx_hosting')->nullable();
|
||||||
|
|
@ -30,13 +27,14 @@ public function up(): void
|
||||||
$table->string('access_type')->nullable();
|
$table->string('access_type')->nullable();
|
||||||
$table->ipAddress('server_ip')->nullable();
|
$table->ipAddress('server_ip')->nullable();
|
||||||
$table->text('root_password')->nullable();
|
$table->text('root_password')->nullable();
|
||||||
|
$table->text('ddr_out')->nullable();
|
||||||
|
$table->text('ddr_key')->nullable();
|
||||||
|
$table->longText('observation')->nullable();
|
||||||
|
|
||||||
// Módulos e Features
|
|
||||||
$table->boolean('has_call_center')->default(false);
|
$table->boolean('has_call_center')->default(false);
|
||||||
$table->boolean('has_voice_gateway')->default(false);
|
$table->boolean('has_voice_gateway')->default(false);
|
||||||
$table->boolean('has_fop2')->default(false);
|
$table->boolean('has_fop2')->default(false);
|
||||||
|
|
||||||
// Módulos
|
|
||||||
$table->json('modules')->nullable();
|
$table->json('modules')->nullable();
|
||||||
$table->string('whatsapp_number')->nullable();
|
$table->string('whatsapp_number')->nullable();
|
||||||
$table->date('whatsapp_activation_date')->nullable();
|
$table->date('whatsapp_activation_date')->nullable();
|
||||||
|
|
@ -46,9 +44,6 @@ public function up(): void
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*/
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
{
|
{
|
||||||
Schema::dropIfExists('clients');
|
Schema::dropIfExists('clients');
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ public function run(): void
|
||||||
User::factory()->create([
|
User::factory()->create([
|
||||||
'name' => 'admin',
|
'name' => 'admin',
|
||||||
'permissions' => ['admin'],
|
'permissions' => ['admin'],
|
||||||
'email' => 'inglinesystemsadmin@inglinesystems.com.br',
|
'email' => 'suporte@inglinesystems.com.br',
|
||||||
'password' => Hash::make('*Ingline.Sys#9420%SECURITY#')
|
'password' => Hash::make('*Ingline.Sys#9420%SECURITY#')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
@import './user-form-modal.css';
|
@import './user-form-modal.css';
|
||||||
@import './header.css';
|
@import './header.css';
|
||||||
@import './client-cards.css';
|
@import './client-cards.css';
|
||||||
|
@import './user-view-modal.css';
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
@layer components {
|
||||||
|
|
||||||
|
|
||||||
|
/* Adicione estas classes à camada @layer components do seu CSS */
|
||||||
|
|
||||||
|
/* --- Estrutura da Lista de Usuários --- */
|
||||||
|
.user-list-content {
|
||||||
|
/* Padding e limite de altura com scroll */
|
||||||
|
@apply p-4 max-h-[500px] overflow-y-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-grid {
|
||||||
|
@apply grid gap-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card-item {
|
||||||
|
/* Estilo básico de cada item na lista */
|
||||||
|
@apply flex justify-between items-center bg-gray-50 p-3 rounded-lg border border-gray-200 transition-shadow duration-150;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-card-item:hover {
|
||||||
|
@apply shadow-md border-blue-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
@apply flex flex-col;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
@apply font-semibold text-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email {
|
||||||
|
@apply text-sm text-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-details {
|
||||||
|
/* Garante que os itens fiquem alinhados e espaçados */
|
||||||
|
@apply flex items-center space-x-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-actions-group {
|
||||||
|
/* Define o grupo de botões de ação */
|
||||||
|
@apply flex space-x-2 ml-4;
|
||||||
|
/* Espaçamento à esquerda para separar dos detalhes */
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
/* Estilo base para os ícones clicáveis */
|
||||||
|
@apply p-1 rounded-full transition-colors duration-150;
|
||||||
|
/* Cores já definidas inline: text-blue-600, text-red-600 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button:hover {
|
||||||
|
@apply bg-gray-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-permission {
|
||||||
|
/* Estilo para a tag de permissão */
|
||||||
|
@apply text-xs font-medium px-2 py-0.5 rounded-full bg-blue-100 text-blue-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-status {
|
||||||
|
@apply text-xs text-green-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *IMPORTANTE*: Se o modal ficar muito estreito, adicione w-full max-w-2xl à classe .modal-container
|
||||||
|
ou crie uma classe específica para este modal.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
@ -3,8 +3,9 @@
|
||||||
- 'showQuestion' controla a visibilidade.
|
- 'showQuestion' controla a visibilidade.
|
||||||
- '@sure.window' é o evento que dispara a abertura.
|
- '@sure.window' é o evento que dispara a abertura.
|
||||||
-->
|
-->
|
||||||
<div x-data="{showQuestion: false, clientId: null}" @sure.window="showQuestion = true; clientId = $event.detail.id"
|
<div x-data="{showQuestion: false, clientId: null, user: false}"
|
||||||
x-cloak {{-- x-cloak é bom para evitar "piscadas" na tela --}}>
|
@sure.window="showQuestion = true; clientId = $event.detail.id; user = $event.detail.isUser;" x-cloak {{-- x-cloak é
|
||||||
|
bom para evitar "piscadas" na tela --}}>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
1. O OVERLAY (Fundo)
|
1. O OVERLAY (Fundo)
|
||||||
|
|
@ -58,12 +59,14 @@ class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-
|
||||||
O botão "Sim, Excluir" deve vir primeiro no HTML
|
O botão "Sim, Excluir" deve vir primeiro no HTML
|
||||||
para o 'flex-row-reverse' funcionar.
|
para o 'flex-row-reverse' funcionar.
|
||||||
-->
|
-->
|
||||||
<button type="button" @click="$dispatch('confirm-delete', [clientId]); showQuestion = false"
|
<button type="button"
|
||||||
|
@click="if(user){$dispatch('confirm-delete-user', [clientId]);}else{$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 cursor-pointer">
|
class="btn-submit bg-red-600 hover:bg-red-700 focus:ring-red-500 w-full sm:w-auto cursor-pointer">
|
||||||
Sim, Excluir
|
Sim, Excluir
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" @click="showQuestion = false" class="btn-cancel mt-3 sm:mt-0 w-full sm:w-auto cursor-pointer">
|
<button type="button" @click="showQuestion = false"
|
||||||
|
class="btn-cancel mt-3 sm:mt-0 w-full sm:w-auto cursor-pointer">
|
||||||
Cancelar
|
Cancelar
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
}
|
}
|
||||||
}" @notify.window="addToast($event.detail.message, $event.detail.type || 'success')"
|
}" @notify.window="addToast($event.detail.message, $event.detail.type || 'success')"
|
||||||
@notifyError.window="addToast($event.detail.message, $event.detail.type || 'error')"
|
@notifyError.window="addToast($event.detail.message, $event.detail.type || 'error')"
|
||||||
class="fixed top-5 right-5 z-50 flex w-full max-w-xs flex-col space-y-3">
|
class="fixed top-5 right-5 z-50000 flex w-full max-w-xs flex-col space-y-3">
|
||||||
<template x-for="toast in toasts" :key="toast.id">
|
<template x-for="toast in toasts" :key="toast.id">
|
||||||
<div x-show="true" x-transition:enter="transform ease-out duration-300 transition"
|
<div x-show="true" x-transition:enter="transform ease-out duration-300 transition"
|
||||||
x-transition:enter-start="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
|
x-transition:enter-start="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
|
||||||
|
|
@ -35,7 +35,7 @@ class="fixed top-5 right-5 z-50 flex w-full max-w-xs flex-col space-y-3">
|
||||||
|
|
||||||
<p class="toast-message" x-text="toast.message"></p>
|
<p class="toast-message" x-text="toast.message"></p>
|
||||||
|
|
||||||
<button @click="removeToast(toast.id)" class="toast-close-button">
|
<button @click="removeToast(toast.id)" class="toast-close-button cursor-pointer">
|
||||||
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path
|
<path
|
||||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
|
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@
|
||||||
<title> @yield('title', 'Nexus - Dashboard') </title>
|
<title> @yield('title', 'Nexus - Dashboard') </title>
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<livewire:admin.create-user />
|
<livewire:admin.user.create-user />
|
||||||
<livewire:admin.add-client />
|
<livewire:admin.client.edit-client />
|
||||||
|
<livewire:admin.user.edit-user />
|
||||||
|
<livewire:admin.client.add-client />
|
||||||
|
<livewire:admin.user.delete-user>
|
||||||
<livewire:admin.show-users />
|
<livewire:admin.show-users />
|
||||||
<livewire:admin.delete-client />
|
<livewire:admin.client.detail-client />
|
||||||
|
<livewire:admin.client.delete-client />
|
||||||
<x-are-you-sure />
|
<x-are-you-sure />
|
||||||
<livewire:admin.show-client />
|
<livewire:show-client />
|
||||||
<livewire:admin.edit-client />
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|
@ -69,12 +69,19 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
<li class="profile-items">
|
<li class="profile-items">
|
||||||
<a @click="$dispatch('open-add-client')" class="profile-link">
|
<a @click="$dispatch('open-add-client')" class="profile-link">
|
||||||
Adicionar clientes
|
Adicionar clientes
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="profile-items">
|
||||||
|
<a @click="$dispatch('vpn-show-links')" class="profile-link">
|
||||||
|
VPN's
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@endauth
|
@endauth
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class="fixed inset-y-0 right-0 flex max-w-full pl-10" @click.away="showClientMod
|
||||||
Adicionar Novo Cliente
|
Adicionar Novo Cliente
|
||||||
</h2>
|
</h2>
|
||||||
<button type="button" @click="showClientModal = false"
|
<button type="button" @click="showClientModal = false"
|
||||||
class="rounded-md text-blue-200 hover:text-white cursor-pointer focus:outline-none focus:ring-2 focus:ring-white">
|
class="rounded-md text-blue-400 hover:text-white cursor-pointer focus:outline-none focus:ring-2 focus:ring-white">
|
||||||
<span class="sr-only">Fechar painel</span>
|
<span class="sr-only">Fechar painel</span>
|
||||||
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor">
|
stroke-width="1.5" stroke="currentColor">
|
||||||
|
|
@ -44,7 +44,7 @@ class="rounded-md text-blue-200 hover:text-white cursor-pointer focus:outline-no
|
||||||
<div>
|
<div>
|
||||||
<label for="client_name" class="form-label">Nome Fantasia *</label>
|
<label for="client_name" class="form-label">Nome Fantasia *</label>
|
||||||
<input type="text" wire:model="form.client_name" id="client_name"
|
<input type="text" wire:model="form.client_name" id="client_name"
|
||||||
class="form-input">
|
class="form-input" placeholder="Eletrônicos Mota">
|
||||||
@error('form.client_name') <span
|
@error('form.client_name') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span>
|
class="text-red-500 text-sm">{{ $message }}</span>
|
||||||
@enderror
|
@enderror
|
||||||
|
|
@ -52,14 +52,14 @@ class="text-red-500 text-sm">{{ $message }}</span>
|
||||||
<div>
|
<div>
|
||||||
<label for="legal_name" class="form-label">Razão Social *</label>
|
<label for="legal_name" class="form-label">Razão Social *</label>
|
||||||
<input type="text" wire:model="form.legal_name" id="legal_name"
|
<input type="text" wire:model="form.legal_name" id="legal_name"
|
||||||
class="form-input">
|
class="form-input" placeholder="Mota Germano Eletrônicos LTDA">
|
||||||
@error('form.legal_name') <span
|
@error('form.legal_name') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<label for="cnpj" class="form-label">CNPJ *</label>
|
<label for="cnpj" class="form-label">CNPJ *</label>
|
||||||
<input type="text" wire:model="form.cnpj" id="cnpj" class="form-input">
|
<input type="text" wire:model="form.cnpj" id="cnpj" class="form-input" placeholder="54.505.316/0001-64">
|
||||||
@error('form.cnpj') <span class="text-red-500 text-sm">{{ $message }}</span>
|
@error('form.cnpj') <span class="text-red-500 text-sm">{{ $message }}</span>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -73,7 +73,7 @@ class="text-sm text-blue-600 mt-1">
|
||||||
Enviando...
|
Enviando...
|
||||||
</div>
|
</div>
|
||||||
@error('form.profile_image_path') <span
|
@error('form.profile_image_path') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -94,37 +94,61 @@ class="form-input">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="carrier" class="form-label">Operadora</label>
|
<label for="carrier" class="form-label">Operadora</label>
|
||||||
<input type="text" wire:model="form.carrier" id="carrier" class="form-input">
|
<input type="text" wire:model="form.carrier" id="carrier" class="form-input" placeholder="ALGAR">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="access_type" class="form-label">Tipo de Acesso</label>
|
<label for="access_type" class="form-label">Tipo de Acesso</label>
|
||||||
<input type="text" wire:model="form.access_type" id="access_type"
|
<input type="text" wire:model="form.access_type" id="access_type"
|
||||||
class.="form-input" placeholder="Ex: SSH, AnyDesk, VPN">
|
class.="form-input" placeholder="Ex: SSH, AnyDesk, VPN">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="ddr_out" class="form-label">Número de saída chave</label>
|
||||||
|
<input type="text" wire:model="form.ddr_out" id="ddr_out"
|
||||||
|
class="form-input" placeholder="(71) 3034-8350">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="ddr_key" class="form-label">Número de entrada chave</label>
|
||||||
|
<input type="text" wire:model="form.ddr_key" id="ddr_key"
|
||||||
|
class="form-input" placeholder="(71) 3034-8350">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="server_ip" class="form-label">IP do Servidor</label>
|
<label for="server_ip" class="form-label">IP do Servidor</label>
|
||||||
<input type="text" wire:model="form.server_ip" id="server_ip"
|
<input type="text" wire:model="form.server_ip" id="server_ip"
|
||||||
class="form-input">
|
class="form-input" placeholder="10.0.0.1">
|
||||||
@error('form.server_ip') <span
|
@error('form.server_ip') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="root_password" class="form-label">Senha (root)</label>
|
<label for="root_password" class="form-label">Senha (root)</label>
|
||||||
<input type="password" wire:model="form.root_password" id="root_password"
|
<input type="password" wire:model="form.root_password" id="root_password"
|
||||||
class="form-input">
|
class="form-input" placeholder="************">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="form-divider">
|
<hr class="form-divider">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<h2 class="form-section-title">Observações e Credenciais Internas</h2>
|
||||||
|
<div>
|
||||||
|
<label for="observation" class="form-label">Detalhes da Configuração, VPN, ou Notas</label>
|
||||||
|
<textarea wire:model="form.observation" id="observation" rows="5" class="form-input" placeholder="VPN: Fortclient, SENHA: *******"></textarea>
|
||||||
|
@error('form.observation') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="form-divider">
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<h2 class="form-section-title">WhatsApp</h2>
|
<h2 class="form-section-title">WhatsApp</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="whatsapp_number" class="form-label">Número do WhatsApp</label>
|
<label for="whatsapp_number" class="form-label">Número do WhatsApp</label>
|
||||||
<input type="text" wire:model="form.whatsapp_number" id="whatsapp_number"
|
<input type="text" wire:model="form.whatsapp_number" id="whatsapp_number"
|
||||||
class="form-input">
|
class="form-input" placeholder="(71) 3034-8350">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="whatsapp_activation_date" class="form-label">Data Ativação
|
<label for="whatsapp_activation_date" class="form-label">Data Ativação
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
<div x-data="{ showClientDetails: false }" x-cloak
|
||||||
|
@keydown.escape.window="showClientDetails = false"
|
||||||
|
x-on:client-detail.window="showClientDetails = true"
|
||||||
|
class="relative z-50"
|
||||||
|
>
|
||||||
|
<div x-show="showClientDetails"
|
||||||
|
x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" x-transition:enter-end="opacity-25"
|
||||||
|
x-transition:leave="ease-in duration-200" x-transition:leave-start="opacity-0" x-transition:leave-end="opacity-0"
|
||||||
|
class="fixed inset-0 bg-white/50 transition-opacity"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div x-show="showClientDetails"
|
||||||
|
x-transition:enter="transform transition ease-in-out duration-300 sm:duration-500" x-transition:enter-start="translate-x-full" x-transition:enter-end="translate-x-0"
|
||||||
|
x-transition:leave="transform transition ease-in-out duration-300 sm:duration-500" x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full"
|
||||||
|
class="fixed inset-y-0 right-0 flex max-w-full pl-10"
|
||||||
|
@click.away="showClientDetails = false"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="w-screen max-w-4xl">
|
||||||
|
|
||||||
|
<div class="flex h-full flex-col overflow-y-scroll bg-white shadow-xl">
|
||||||
|
|
||||||
|
<div class="bg-gray px-4 py-6 sm:px-6">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h2 class="text-lg font-semibold leading-6 text-black" id="slide-over-title">
|
||||||
|
Detalhes do cliente
|
||||||
|
</h2>
|
||||||
|
<button type="button" @click="showClientDetails = false"
|
||||||
|
class="rounded-md text-blue-400 hover:text-white cursor-pointer focus:outline-none focus:ring-2 focus:ring-white">
|
||||||
|
<span class="sr-only">Fechar painel</span>
|
||||||
|
<svg class="h-6 w-6" 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="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($client)
|
||||||
|
<div class="form-wrapper-modal">
|
||||||
|
<div class="form-grid-container">
|
||||||
|
|
||||||
|
<div class="form-main-panel-modal space-y-8"> <div class="form-group">
|
||||||
|
<h2 class="form-section-title">Informações Principais</h2>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Nome Fantasia</label>
|
||||||
|
<input type="text" value="{{ $client->name }}" class="form-input bg-gray-50" placeholder="Eletrônicos Mota" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Razão Social</label>
|
||||||
|
<input type="text" value="{{ $client->legal_name }}" class="form-input bg-gray-50" placeholder="Mota Germano Eletrônicos LTDA" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">CNPJ</label>
|
||||||
|
<input type="text" value="{{ $client->cnpj }}" class="form-input bg-gray-50" placeholder="54.505.316/0001-64" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Logo</label>
|
||||||
|
<div class="mt-1 flex items-center">
|
||||||
|
@if($client->profile_image_path)
|
||||||
|
<div class="h-10 w-10 rounded overflow-hidden border border-gray-200">
|
||||||
|
<img src="{{ asset('storage/' . $client->profile_image_path) }}" alt="Logo" class="h-full w-full object-cover">
|
||||||
|
</div>
|
||||||
|
<a href="{{ asset('storage/' . $client->profile_image_path) }}" target="_blank" class="ml-3 text-sm text-blue-600 hover:underline">Abrir imagem</a>
|
||||||
|
@else
|
||||||
|
<span class="text-sm text-gray-400 italic p-2 border border-dashed border-gray-300 rounded">Sem logo</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="form-divider">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<h2 class="form-section-title">Detalhes do Servidor PBX</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Hospedagem PBX</label>
|
||||||
|
<input type="text" value="{{ $client->pbx_hosting }}" placeholder="Ex: Vultr, Local, AWS" class="form-input bg-gray-50" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Data de Ativação</label>
|
||||||
|
<input type="text" value="{{ $client->activation_date ? $client->activation_date->format('d/m/Y') : '-' }}" class="form-input bg-gray-50" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Operadora</label>
|
||||||
|
<input type="text" value="{{ $client->carrier }}" placeholder="ALGAR" class="form-input bg-gray-50" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Tipo de Acesso</label>
|
||||||
|
<input type="text" value="{{ $client->access_type }}" placeholder="Ex: SSH, AnyDesk, VPN" class="form-input bg-gray-50" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Número de saída chave</label>
|
||||||
|
<input type="text" value="{{ $client->ddr_out }}" placeholder="(71) 3034-8350" class="form-input bg-gray-50 font-mono text-sm" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Número de entrada chave</label>
|
||||||
|
<input type="text" value="{{ $client->ddr_key }}" placeholder="(71) 3034-8350" class="form-input bg-gray-50 font-mono text-sm" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label">IP do Servidor</label>
|
||||||
|
<input type="text" value="{{ $client->server_ip }}" placeholder="10.0.0.1" class="form-input bg-gray-50 font-mono cursor-text select-all" readonly>
|
||||||
|
</div>
|
||||||
|
<div x-data="{ show: false }">
|
||||||
|
<label class="form-label">Senha (root)</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input placeholder="************" :type="show ? 'text' : 'password'" value="{{ $client->root_password }}" class="form-input bg-gray-50 font-mono pr-20" readonly>
|
||||||
|
<button type="button" @click="show = !show" class="absolute inset-y-0 right-0 px-3 flex items-center text-xs font-bold uppercase text-blue-600 hover:text-blue-800 cursor-pointer">
|
||||||
|
<span x-text="show ? 'Ocultar' : 'Mostrar'"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="form-divider">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<h2 class="form-section-title">Observações e Credenciais Internas</h2>
|
||||||
|
<div class="mt-4">
|
||||||
|
<textarea rows="5" class="form-input bg-gray-50 resize-none" placeholder="VPN: Fortclient, SENHA: *******" readonly>{{ $client->observation }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="form-divider">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<h2 class="form-section-title">WhatsApp</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Número do WhatsApp</label>
|
||||||
|
<input type="text" placeholder="(71) 3034-8350" value="{{ $client->whatsapp_number }}" class="form-input bg-gray-50 font-mono" readonly>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="form-label">Data Ativação WhatsApp</label>
|
||||||
|
<input type="text" value="{{ $client->whatsapp_activation_date ? $client->whatsapp_activation_date->format('d/m/Y') : '-' }}" class="form-input bg-gray-50" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <div class="form-sidebar-column">
|
||||||
|
<div class="form-sticky-sidebar-modal">
|
||||||
|
<div class="form-section-card">
|
||||||
|
<h2 class="form-section-title mb-4">Módulos & Recursos</h2>
|
||||||
|
|
||||||
|
<div class="form-checkbox-group">
|
||||||
|
<label class="form-checkbox-item opacity-80">
|
||||||
|
<input type="checkbox" disabled {{ $client->has_call_center ? 'checked' : '' }} class="form-checkbox">
|
||||||
|
<span>Possui Call Center?</span>
|
||||||
|
</label>
|
||||||
|
<label class="form-checkbox-item opacity-80">
|
||||||
|
<input type="checkbox" disabled {{ $client->has_voice_gateway ? 'checked' : '' }} class="form-checkbox">
|
||||||
|
<span>Possui Gateway de Voz?</span>
|
||||||
|
</label>
|
||||||
|
<label class="form-checkbox-item opacity-80">
|
||||||
|
<input type="checkbox" disabled {{ $client->has_fop2 ? 'checked' : '' }} class="form-checkbox">
|
||||||
|
<span>Possui FOP2?</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<label class="form-label">Módulos Adicionais (JSON)</label>
|
||||||
|
<textarea rows="4" placeholder="Ex: {"bi": true}" class="form-input bg-gray-50 text-xs font-mono" readonly>{{ json_encode($client->modules, JSON_PRETTY_PRINT) }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="flex-1 flex items-center justify-center h-full">
|
||||||
|
<div class="text-gray-500 flex flex-col items-center">
|
||||||
|
<svg class="animate-spin h-8 w-8 text-blue-600 mb-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
|
||||||
|
<span>Carregando...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="flex-shrink-0 border-t border-gray-200 px-4 py-4 sm:px-6">
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button type="button" @click="showClientDetails = false" class="form-button-secondary">
|
||||||
|
Fechar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -22,7 +22,7 @@ class="fixed inset-y-0 right-0 flex max-w-full pl-10" @click.away="showEditClien
|
||||||
Edição de cliente
|
Edição de cliente
|
||||||
</h2>
|
</h2>
|
||||||
<button type="button" @click="showEditClientModal = false"
|
<button type="button" @click="showEditClientModal = false"
|
||||||
class="rounded-md text-blue-200 hover:text-white cursor-pointer focus:outline-none focus:ring-2 focus:ring-white">
|
class="rounded-md text-blue-400 hover:text-white cursor-pointer focus:outline-none focus:ring-2 focus:ring-white">
|
||||||
<span class="sr-only">Fechar painel</span>
|
<span class="sr-only">Fechar painel</span>
|
||||||
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor">
|
stroke-width="1.5" stroke="currentColor">
|
||||||
|
|
@ -44,14 +44,14 @@ class="rounded-md text-blue-200 hover:text-white cursor-pointer focus:outline-no
|
||||||
<div>
|
<div>
|
||||||
<label for="client_name" class="form-label">Nome Fantasia *</label>
|
<label for="client_name" class="form-label">Nome Fantasia *</label>
|
||||||
<input type="text" wire:model="clientForm.client_name" id="client_name"
|
<input type="text" wire:model="clientForm.client_name" id="client_name"
|
||||||
class="form-input">
|
class="form-input" placeholder="Eletrônicos Mota">
|
||||||
@error('clientForm.client_name') <span
|
@error('clientForm.client_name') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span>
|
class="text-red-500 text-sm">{{ $message }}</span>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="legal_name" class="form-label">Razão Social *</label>
|
<label for="legal_name" class="form-label">Razão Social *</label>
|
||||||
<input type="text" wire:model="clientForm.legal_name" id="legal_name"
|
<input type="text" wire:model="clientForm.legal_name" placeholder="Mota Germano Eletrônicos LTDA" id="legal_name"
|
||||||
class="form-input">
|
class="form-input">
|
||||||
@error('clientForm.legal_name') <span
|
@error('clientForm.legal_name') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
|
|
@ -59,7 +59,7 @@ class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<label for="cnpj" class="form-label">CNPJ *</label>
|
<label for="cnpj" class="form-label">CNPJ *</label>
|
||||||
<input type="text" wire:model="clientForm.cnpj" id="cnpj" class="form-input">
|
<input placeholder="54.505.316/0001-64" type="text" wire:model="clientForm.cnpj" id="cnpj" class="form-input">
|
||||||
@error('clientForm.cnpj') <span class="text-red-500 text-sm">{{ $message }}</span>
|
@error('clientForm.cnpj') <span class="text-red-500 text-sm">{{ $message }}</span>
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -94,37 +94,57 @@ class="form-input">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="carrier" class="form-label">Operadora</label>
|
<label for="carrier" class="form-label">Operadora</label>
|
||||||
<input type="text" wire:model="clientForm.carrier" id="carrier" class="form-input">
|
<input type="text" wire:model="clientForm.carrier" id="carrier" placeholder="ALGAR" class="form-input">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="access_type" class="form-label">Tipo de Acesso</label>
|
<label for="access_type" class="form-label">Tipo de Acesso</label>
|
||||||
<input type="text" wire:model="clientForm.access_type" id="access_type"
|
<input type="text" wire:model="clientForm.access_type" id="access_type"
|
||||||
class.="form-input" placeholder="Ex: SSH, AnyDesk, VPN">
|
class.="form-input" placeholder="Ex: SSH, AnyDesk, VPN">
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="ddr_out" class="form-label">Número de saída chave</label>
|
||||||
|
<input type="text" wire:model="clientForm.ddr_out" id="ddr_out"
|
||||||
|
class="form-input" placeholder="(71) 3034-8350">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="ddr_key" class="form-label">Número de entrada chave</label>
|
||||||
|
<input type="text" wire:model="clientForm.ddr_key" id="ddr_key"
|
||||||
|
class="form-input" placeholder="(71) 3034-8350">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="server_ip" class="form-label">IP do Servidor</label>
|
<label for="server_ip" class="form-label">IP do Servidor</label>
|
||||||
<input type="text" wire:model="clientForm.server_ip" id="server_ip"
|
<input type="text" wire:model="clientForm.server_ip" id="server_ip"
|
||||||
class="form-input">
|
class="form-input" placeholder="10.0.0.1">
|
||||||
@error('clientForm.server_ip') <span
|
@error('form.server_ip') <span
|
||||||
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="root_password" class="form-label">Senha (root)</label>
|
<label for="root_password" class="form-label">Senha (root)</label>
|
||||||
<input type="password" wire:model="clientForm.root_password" id="root_password"
|
<input type="password" wire:model="clientForm.root_password" id="root_password"
|
||||||
class="form-input">
|
class="form-input" placeholder="************">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="form-divider">
|
<hr class="form-divider">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<h2 class="form-section-title">Observações e Credenciais Internas</h2>
|
||||||
|
<div>
|
||||||
|
<label for="observation" class="form-label">Detalhes da Configuração, VPN, ou Notas</label>
|
||||||
|
<textarea wire:model="clientForm.observation" id="observation" rows="5" class="form-input" placeholder="VPN: Fortclient, SENHA: *******"></textarea>
|
||||||
|
@error('clientForm.observation') <span class="text-red-500 text-sm">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<h2 class="form-section-title">WhatsApp</h2>
|
<h2 class="form-section-title">WhatsApp</h2>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="whatsapp_number" class="form-label">Número do WhatsApp</label>
|
<label for="whatsapp_number" class="form-label">Número do WhatsApp</label>
|
||||||
<input type="text" wire:model="clientForm.whatsapp_number" id="whatsapp_number"
|
<input type="text" wire:model="clientForm.whatsapp_number" id="whatsapp_number"
|
||||||
class="form-input">
|
class="form-input" placeholder="(71) 3034-8350">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="whatsapp_activation_date" class="form-label">Data Ativação
|
<label for="whatsapp_activation_date" class="form-label">Data Ativação
|
||||||
|
|
@ -1,38 +1,37 @@
|
||||||
<div class="container grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div class="container grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
@foreach ($clients as $client)
|
@foreach ($clients as $client)
|
||||||
<div class="client-card">
|
<div class="client-card">
|
||||||
<div class="client-card-header">
|
<div class="client-card-header">
|
||||||
<div class="client-avatar">
|
<div class="client-avatar">
|
||||||
<img src="{{ Vite::asset('resources/images/mr-distribuidora.svg') }}" alt="Avatar do Cliente"
|
<img src="{{ asset('storage/' . $client->profile_image_path) }}" alt="Avatar do Cliente"
|
||||||
class="w-32 h-32 rounded-full object-cover">
|
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" x-on:click.prevent="$dispatch('update-client', { id: '{{ $client->id }}' })">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>
|
||||||
<div class="client-card-name">
|
<div x-data="{ open: false }" @click.outside="open = false" class="client-options-menu">
|
||||||
{{ $client->name }}
|
<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"
|
||||||
|
x-on:click.prevent="$dispatch('client-detail', { id: '{{ $client->id }}' })">Ver Detalhes</a>
|
||||||
|
</li>
|
||||||
|
<li><a href="#" class="client-option-item"
|
||||||
|
x-on:click.prevent="$dispatch('update-client', { id: '{{ $client->id }}' })">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>
|
</div>
|
||||||
|
<div class="client-card-name">
|
||||||
|
{{ $client->name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,4 +1,61 @@
|
||||||
<div x-data="{showUsers: false}" x-on:show-users.window="showUsers = true">
|
<div x-data="{ showUsers: false}" x-cloak x-on:show-users.window="showUsers = true"
|
||||||
|
x-on:close-users-modal.window="showUsers = false" class="relative z-50">
|
||||||
|
<div x-show="showUsers" class="modal-overlay" x-transition:enter.duration.300ms x-transition:leave.duration.300ms>
|
||||||
|
<div x-on:click.outside="showUsers = false" x-show="showUsers" 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 w-full max-w-2xl">
|
||||||
|
<h3 class="modal-title">Usuários Cadastrados no Nexus</h3>
|
||||||
|
|
||||||
|
<div class="user-list-content">
|
||||||
|
<div class="user-grid">
|
||||||
|
@forelse ($users as $user)
|
||||||
|
<div class="user-card-item">
|
||||||
|
<div class="user-info">
|
||||||
|
<span class="user-name">{{ $user->name }}</span>
|
||||||
|
<span class="user-email">{{ $user->email }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="user-details">
|
||||||
|
{{-- <span class="user-permission">{{ $user->permissions }}</span> --}}
|
||||||
|
<span class="user-status">Ativo</span>
|
||||||
|
|
||||||
|
<div class="user-actions-group">
|
||||||
|
|
||||||
|
<button
|
||||||
|
@click="$dispatch('update-user', { id: '{{ $user->id }}' }); showUsers = false"
|
||||||
|
class="action-button text-blue-600 hover:text-blue-800 cursor-pointer"
|
||||||
|
title="Editar Usuário">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
@click="$dispatch('sure', { id: '{{ $user->id }}', isUser: true }); showUsers = false"
|
||||||
|
class="action-button text-red-600 hover:text-red-800 cursor-pointer"
|
||||||
|
title="Excluir Usuário">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none"
|
||||||
|
viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M19 7l-.86 11.2a2 2 0 01-2 1.8H7.86a2 2 0 01-2-1.8L4.01 7h14.98zM9 5h6" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@empty
|
||||||
|
<p class="text-gray-500 col-span-full p-4">Nenhum usuário cadastrado.</p>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-footer">
|
||||||
|
<button type="button" @click="showUsers = false" class="btn-cancel cursor-pointer">
|
||||||
|
Fechar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
<!-- create-user.blade.php -->
|
||||||
|
<div x-data="{ showModalUpdate: false }" x-on:user-created.window="showModalUpdate = false"
|
||||||
|
x-on:update-user.window="showModalUpdate = true">
|
||||||
|
|
||||||
|
<div x-show="showModalUpdate" class="modal-overlay" x-transition:enter.duration.300ms
|
||||||
|
x-transition:leave.duration.300ms>
|
||||||
|
<div x-on:click.outside="showModalUpdate = false" x-show="showModalUpdate" 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">
|
||||||
|
|
||||||
|
<h3 class="modal-title">Novo Usuário Nexus</h3>
|
||||||
|
|
||||||
|
<form wire:submit.prevent="editUser" class="form-wrapper">
|
||||||
|
@error('general')
|
||||||
|
<div class="error-box">
|
||||||
|
<strong>Erro:</strong> {{ $message }}
|
||||||
|
</div>
|
||||||
|
@enderror
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="name" class="form-label">Nome</label>
|
||||||
|
<input type="text" wire:model.defer="userForm.name" id="name" class="form-input">
|
||||||
|
@error('userForm.name') <span class="error-text">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" wire:model.defer="userForm.email" id="email" class="form-input">
|
||||||
|
@error('userForm.email') <span class="error-text">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="password" class="form-label">Senha</label>
|
||||||
|
<input type="password" wire:model.defer="userForm.password" id="password" class="form-input">
|
||||||
|
@error('userForm.password') <span class="error-text">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="password_confirm" class="form-label">Confirmar Senha</label>
|
||||||
|
<input type="password" wire:model="userForm.password_confirm" id="password_confirm"
|
||||||
|
class="form-input">
|
||||||
|
@error('userForm.password_confirm') <span class="error-text">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="permissions" class="form-label">Permissões</label>
|
||||||
|
|
||||||
|
<div x-data="{
|
||||||
|
open: false,
|
||||||
|
selected: @entangle('userForm.permissions'),
|
||||||
|
options: { 'user': 'Usuário', 'admin': 'Admin' }
|
||||||
|
}" x-on:click.outside="open = false" class="select-wrapper">
|
||||||
|
|
||||||
|
<button type="button" x-on:click="open = true" class="select-button">
|
||||||
|
<span class="select-text" x-text="options[selected]"></span>
|
||||||
|
<span class="select-icon">
|
||||||
|
<svg class="select-svg" xmlns="http://www.w3.org/2000/svg" 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>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div x-show="open" x-transition style="display: none;" class="select-options">
|
||||||
|
<div x-on:click="selected = 'user'; open = false" class="select-option"
|
||||||
|
:class="{ 'selected-option': selected == 'user' }">
|
||||||
|
<span class="option-label"
|
||||||
|
:class="{ 'font-semibold': selected == 'user' }">Usuário</span>
|
||||||
|
<span x-show="selected == 'user'"
|
||||||
|
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>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div x-on:click="selected = 'admin'; open = false" class="select-option"
|
||||||
|
:class="{ 'selected-option': selected == 'admin' }">
|
||||||
|
<span class="option-label"
|
||||||
|
:class="{ 'font-semibold': selected == 'admin' }">Admin</span>
|
||||||
|
<span x-show="selected == 'admin'"
|
||||||
|
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>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@error('userForm.permissions') <span class="error-text">{{ $message }}</span> @enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-footer">
|
||||||
|
<button type="button" @click="showModalUpdate = false" class="btn-cancel">
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-submit">
|
||||||
|
<span wire:loading.remove wire:target="editUser">Salvar</span>
|
||||||
|
<span wire:loading wire:target="editUser" class="loading">Salvando...</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<div>
|
||||||
|
{{-- A good traveler has no fixed plans and is not intent upon arriving. --}}
|
||||||
|
</div>
|
||||||
|
|
@ -2,42 +2,28 @@
|
||||||
@section('title', 'Nexus - Login')
|
@section('title', 'Nexus - Login')
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<!--
|
|
||||||
1. Container principal que centraliza o card
|
|
||||||
(Usa a classe .container-form do seu novo CSS)
|
|
||||||
-->
|
|
||||||
<div class="container-form">
|
<div class="container-form">
|
||||||
|
|
||||||
<!--
|
|
||||||
2. O Card de Login "Vidro Fosco"
|
|
||||||
(Usa a classe .form-class do seu novo CSS)
|
|
||||||
-->
|
|
||||||
<form action="{{ route('login-post') }}" class="form-class" method="POST">
|
<form action="{{ route('login-post') }}" class="form-class" method="POST">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<!-- 3. A Logo (Usa .container-title) -->
|
|
||||||
<div class="container-title">
|
<div class="container-title">
|
||||||
<!-- Coloque sua nova logo aqui! -->
|
|
||||||
<img src="{{ Vite::asset('resources/images/logo.png') }}" alt="Ingline Logo" class="h-10">
|
<img src="{{ Vite::asset('resources/images/logo.png') }}" alt="Ingline Logo" class="h-10">
|
||||||
<!-- (Ajuste o src e o h-10 se necessário) -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container-message">
|
<div class="container-message">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 4. Título (Usa .form-title) -->
|
|
||||||
<h1 class="form-title">
|
<h1 class="form-title">
|
||||||
Efetue seu login
|
Efetue seu login
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<!-- 5. Mensagens de Sessão -->
|
|
||||||
<x-session-messages />
|
<x-session-messages />
|
||||||
|
|
||||||
<!-- 6. Grupo de Inputs (Usa .input-group, .form-label, .form-input-class) -->
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label for="email" class="form-label">E-mail</label>
|
<label for="email" class="form-label">E-mail</label>
|
||||||
<input class="form-input-class" id="email" type="email" name="email" placeholder="seu@email.com" required>
|
<input class="form-input-class" id="email" type="email" name="email" value="{{ old('email') }}" placeholder="suporte@provedor.com" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
@ -45,12 +31,10 @@
|
||||||
<input class="form-input-class" id="password" type="password" name="password" placeholder="••••••••" required>
|
<input class="form-input-class" id="password" type="password" name="password" placeholder="••••••••" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 7. Botão de Login (Usa .form-button-class) -->
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<button class="form-button-class" type="submit">Login</button>
|
<button class="form-button-class" type="submit">Login</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 8. Link de Senha (Usa .forget-password) -->
|
|
||||||
<div class="forget-password">
|
<div class="forget-password">
|
||||||
<a href="#">Esqueceu sua senha?</a>
|
<a href="#">Esqueceu sua senha?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
@extends('layouts.app')
|
|
||||||
|
|
||||||
@section('title', 'View Users')
|
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<h1>View Users</h1>
|
|
||||||
{{ dd($users) }}
|
|
||||||
@endsection
|
|
||||||
Loading…
Reference in New Issue