mirror of https://github.com/Lukibeg/OmniBoard.git
Compare commits
2 Commits
cefc446221
...
162c751cd7
| Author | SHA1 | Date |
|---|---|---|
|
|
162c751cd7 | |
|
|
6ca995b0b4 |
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Inertia\Inertia;
|
||||
use App\Models\User;
|
||||
use App\Models\Tenant;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
||||
class TenantController extends Controller
|
||||
{
|
||||
|
||||
public function create()
|
||||
{
|
||||
// Renderiza a view Vue (vamos criar no passo 4)
|
||||
return Inertia::render('Admin/Tenants/Create');
|
||||
}
|
||||
/**
|
||||
* Processa o salvamento
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
abort(401, 'Não autorizado a realizar tal ação.');
|
||||
}
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255|unique:tenants,name',
|
||||
]);
|
||||
|
||||
do {
|
||||
$apiKey = 'sk_' . Str::random(60);
|
||||
} while (Tenant::where('api_key', $apiKey)->exists());
|
||||
|
||||
Tenant::create([
|
||||
'name' => $request->name,
|
||||
'api_key' => $apiKey,
|
||||
]);
|
||||
|
||||
// MUDANÇA AQUI: back() em vez de route('dashboard')
|
||||
// O preserveScroll no Vue garante que a tela não pule.
|
||||
return back()->with('message', 'Tenant criado com sucesso! Chave gerada.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\Rules;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function store(Request $request)
|
||||
{
|
||||
|
||||
if (Auth::user()->role !== 'admin') {
|
||||
abort(401, 'Não autorizado a realizar tal ação.');
|
||||
}
|
||||
// 1. Validação
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|string|email|max:255|unique:users',
|
||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||
'role' => 'required|in:admin,supervisor', // Limitamos as roles
|
||||
// O tenant é obrigatório para supervisors, mas opcional para admins (depende da sua regra)
|
||||
// Aqui vou assumir que todo usuário pertence a um tenant por segurança
|
||||
'tenant_id' => 'required|exists:tenants,id',
|
||||
]);
|
||||
|
||||
// 2. Criação
|
||||
User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => $request->role,
|
||||
'tenant_id' => $request->tenant_id,
|
||||
]);
|
||||
|
||||
// 3. Retorno (Mantém na mesma página)
|
||||
return back()->with('message', 'Usuário criado com sucesso!');
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,10 @@ class QueueController extends Controller
|
|||
{
|
||||
public function setQueueName(Request $request)
|
||||
{
|
||||
if (!Auth::user()->role !== "supervisor") {
|
||||
abort(403, 'Apenas administradores podem gerenciar setores.');
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
'queue_number' => 'required|numeric',
|
||||
'friendly_name' => 'required|string|max:255'
|
||||
|
|
@ -30,6 +34,10 @@ public function setQueueName(Request $request)
|
|||
|
||||
public function updateSectors(Request $request)
|
||||
{
|
||||
if (!Auth::user()->role !== "admin") {
|
||||
abort(403, 'Apenas administradores podem gerenciar setores.');
|
||||
}
|
||||
|
||||
$data = $request->validate([
|
||||
'queues' => 'required|array',
|
||||
'queues.*.id' => 'required|exists:queues,id', // Garante que a fila existe
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
use App\Models\Tenant;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
|
|
@ -34,6 +35,13 @@ public function share(Request $request): array
|
|||
'auth' => [
|
||||
'user' => $request->user(),
|
||||
],
|
||||
|
||||
'tenants' => function () use ($request) {
|
||||
if ($request->user() && $request->user()->role === 'admin') {
|
||||
return Tenant::select('id', 'name')->orderBy('name')->get();
|
||||
}
|
||||
return [];
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
class Tenant extends Model
|
||||
{
|
||||
use HasFactory, BelongsToTenant;
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['name', 'api_key'];
|
||||
|
||||
|
|
|
|||
|
|
@ -1231,16 +1231,16 @@
|
|||
},
|
||||
{
|
||||
"name": "inertiajs/inertia-laravel",
|
||||
"version": "v2.0.14",
|
||||
"version": "v2.0.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/inertiajs/inertia-laravel.git",
|
||||
"reference": "c4a7bbefbfb9995ce2189f1665ba73276cb3cb6f"
|
||||
"reference": "c060177e1b5120e70d1c26fe7c1007a8c8238f0d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/c4a7bbefbfb9995ce2189f1665ba73276cb3cb6f",
|
||||
"reference": "c4a7bbefbfb9995ce2189f1665ba73276cb3cb6f",
|
||||
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/c060177e1b5120e70d1c26fe7c1007a8c8238f0d",
|
||||
"reference": "c060177e1b5120e70d1c26fe7c1007a8c8238f0d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1295,22 +1295,22 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
|
||||
"source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.14"
|
||||
"source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.16"
|
||||
},
|
||||
"time": "2025-12-10T13:29:20+00:00"
|
||||
"time": "2025-12-17T21:57:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v12.42.0",
|
||||
"version": "v12.43.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/framework.git",
|
||||
"reference": "509b33095564c5165366d81bbaa0afaac28abe75"
|
||||
"reference": "195b893593a9298edee177c0844132ebaa02102f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/509b33095564c5165366d81bbaa0afaac28abe75",
|
||||
"reference": "509b33095564c5165366d81bbaa0afaac28abe75",
|
||||
"url": "https://api.github.com/repos/laravel/framework/zipball/195b893593a9298edee177c0844132ebaa02102f",
|
||||
"reference": "195b893593a9298edee177c0844132ebaa02102f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1519,7 +1519,7 @@
|
|||
"issues": "https://github.com/laravel/framework/issues",
|
||||
"source": "https://github.com/laravel/framework"
|
||||
},
|
||||
"time": "2025-12-09T15:51:23+00:00"
|
||||
"time": "2025-12-16T18:53:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
|
|
@ -3503,16 +3503,16 @@
|
|||
},
|
||||
{
|
||||
"name": "psy/psysh",
|
||||
"version": "v0.12.16",
|
||||
"version": "v0.12.18",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bobthecow/psysh.git",
|
||||
"reference": "ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67"
|
||||
"reference": "ddff0ac01beddc251786fe70367cd8bbdb258196"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67",
|
||||
"reference": "ee6d5028be4774f56c6c2c85ec4e6bc9acfe6b67",
|
||||
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/ddff0ac01beddc251786fe70367cd8bbdb258196",
|
||||
"reference": "ddff0ac01beddc251786fe70367cd8bbdb258196",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3576,9 +3576,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/bobthecow/psysh/issues",
|
||||
"source": "https://github.com/bobthecow/psysh/tree/v0.12.16"
|
||||
"source": "https://github.com/bobthecow/psysh/tree/v0.12.18"
|
||||
},
|
||||
"time": "2025-12-07T03:39:01+00:00"
|
||||
"time": "2025-12-17T14:35:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pusher/pusher-php-server",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
// Adicionamos a coluna 'role' com valor padrão 'supervisor'
|
||||
// Assim, qualquer novo usuário criado será supervisor, a menos que digamos o contrário.
|
||||
$table->string('role')->default('supervisor')->after('email');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('role');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -22,6 +22,7 @@ public function run(): void
|
|||
'name' => 'Admin OmniBoard',
|
||||
'email' => 'admin@omniboard.com',
|
||||
'password' => bcrypt('password'), // A senha será 'password'
|
||||
'role' => 'admin',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,84 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import Modal from '@/Components/Modal.vue';
|
||||
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||
import SecondaryButton from '@/Components/SecondaryButton.vue';
|
||||
import TextInput from '@/Components/TextInput.vue';
|
||||
import InputLabel from '@/Components/InputLabel.vue';
|
||||
import InputError from '@/Components/InputError.vue';
|
||||
|
||||
const showModal = ref(false);
|
||||
|
||||
const form = useForm({
|
||||
name: '',
|
||||
});
|
||||
|
||||
const openModal = () => {
|
||||
showModal.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
showModal.value = false;
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
form.post(route('tenants.store'), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => {
|
||||
closeModal();
|
||||
// Opcional: Se quiser resetar apenas se der sucesso
|
||||
form.reset();
|
||||
},
|
||||
onError: () => {
|
||||
// Se der erro (ex: nome duplicado), o modal continua aberto
|
||||
// e o InputError mostra a mensagem.
|
||||
document.getElementById('tenant_name').focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
defineExpose({ openModal });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<Modal :show="showModal" @close="closeModal">
|
||||
<div class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 mb-4">
|
||||
Cadastrar Novo Tenant
|
||||
</h2>
|
||||
|
||||
<p class="text-sm text-gray-600 mb-6">
|
||||
Informe o nome da empresa. A <strong>API Key</strong> será gerada automaticamente.
|
||||
</p>
|
||||
|
||||
<form @submit.prevent="submit">
|
||||
|
||||
<div class="mb-6">
|
||||
<InputLabel for="tenant_name" value="Nome da Empresa" />
|
||||
|
||||
<TextInput id="tenant_name" ref="nameInput" v-model="form.name" type="text"
|
||||
class="mt-1 block w-full" placeholder="Ex: Call Center Filial Sul" required
|
||||
@keyup.enter="submit" />
|
||||
|
||||
<InputError :message="form.errors.name" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end gap-3">
|
||||
<SecondaryButton @click="closeModal">
|
||||
Cancelar
|
||||
</SecondaryButton>
|
||||
|
||||
<PrimaryButton class="bg-indigo-600 hover:bg-indigo-700"
|
||||
:class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||
Gerar Tenant
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useForm, usePage } from '@inertiajs/vue3';
|
||||
import Modal from '@/Components/Modal.vue';
|
||||
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||
import SecondaryButton from '@/Components/SecondaryButton.vue';
|
||||
import TextInput from '@/Components/TextInput.vue';
|
||||
import InputLabel from '@/Components/InputLabel.vue';
|
||||
import InputError from '@/Components/InputError.vue';
|
||||
|
||||
const showModal = ref(false);
|
||||
const page = usePage();
|
||||
|
||||
// Assumindo que você passará a lista de tenants globalmente ou na página
|
||||
// Se não tiver tenants na props, retorna array vazio para não quebrar
|
||||
const tenants = page.props.tenants || [];
|
||||
|
||||
const form = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
password_confirmation: '',
|
||||
role: 'supervisor', // Padrão
|
||||
tenant_id: '',
|
||||
});
|
||||
|
||||
const openModal = () => {
|
||||
showModal.value = true;
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
showModal.value = false;
|
||||
form.reset();
|
||||
form.clearErrors();
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
form.post(route('admin.users.store'), {
|
||||
preserveScroll: true,
|
||||
onSuccess: () => closeModal(),
|
||||
});
|
||||
};
|
||||
|
||||
// Expõe a função para o Layout Pai chamar
|
||||
defineExpose({ openModal });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal :show="showModal" @close="closeModal">
|
||||
<div class="p-6">
|
||||
<h2 class="text-lg font-medium text-gray-900 mb-4">
|
||||
Cadastrar Novo Usuário
|
||||
</h2>
|
||||
|
||||
<form @submit.prevent="submit">
|
||||
|
||||
<div class="mb-4">
|
||||
<InputLabel for="name" value="Nome Completo" />
|
||||
<TextInput id="name" v-model="form.name" type="text" class="mt-1 block w-full" required />
|
||||
<InputError :message="form.errors.name" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<InputLabel for="email" value="E-mail" />
|
||||
<TextInput id="email" v-model="form.email" type="email" class="mt-1 block w-full" required />
|
||||
<InputError :message="form.errors.email" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<InputLabel for="tenant_id" value="Vincular ao Cliente (Tenant)" />
|
||||
<select id="tenant_id" v-model="form.tenant_id"
|
||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm"
|
||||
required>
|
||||
<option value="" disabled>Selecione uma empresa...</option>
|
||||
<option v-for="tenant in tenants" :key="tenant.id" :value="tenant.id">
|
||||
{{ tenant.name }}
|
||||
</option>
|
||||
</select>
|
||||
<InputError :message="form.errors.tenant_id" class="mt-2" />
|
||||
<p v-if="tenants.length === 0" class="text-xs text-red-500 mt-1">
|
||||
Nenhum tenant encontrado. Crie um tenant primeiro.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<InputLabel for="role" value="Função / Permissão" />
|
||||
<select id="role" v-model="form.role"
|
||||
class="mt-1 block w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm">
|
||||
<option value="supervisor">Supervisor</option>
|
||||
<option value="admin">Administrador</option>
|
||||
</select>
|
||||
<InputError :message="form.errors.role" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<div>
|
||||
<InputLabel for="password" value="Senha" />
|
||||
<TextInput id="password" v-model="form.password" type="password" class="mt-1 block w-full"
|
||||
required />
|
||||
<InputError :message="form.errors.password" class="mt-2" />
|
||||
</div>
|
||||
<div>
|
||||
<InputLabel for="password_confirmation" value="Confirmar Senha" />
|
||||
<TextInput id="password_confirmation" v-model="form.password_confirmation" type="password"
|
||||
class="mt-1 block w-full" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end gap-3">
|
||||
<SecondaryButton @click="closeModal"> Cancelar </SecondaryButton>
|
||||
<PrimaryButton :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
|
||||
Criar Usuário
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
|
@ -6,9 +6,22 @@ import DropdownLink from '@/Components/DropdownLink.vue';
|
|||
import { Link } from '@inertiajs/vue3';
|
||||
import SetQueueNameForm from '@/Components/SetQueueNameForm.vue';
|
||||
import SetQueueSectorForm from '@/Components/SetQueueSectorForm.vue';
|
||||
import CreateTenantModal from '@/Components/CreateTenantModal.vue';
|
||||
import CreateUserModal from '@/Components/CreateUserModal.vue';
|
||||
|
||||
const showingSidebar = ref(false);
|
||||
|
||||
const tenantModalRef = ref(null);
|
||||
const userModalRef = ref(null);
|
||||
|
||||
const openTenant = () => {
|
||||
tenantModalRef.value?.openModal();
|
||||
};
|
||||
|
||||
const openUser = () => {
|
||||
userModalRef.value?.openModal();
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
{ name: 'Dashboard', route: 'dashboard', icon: 'M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z' },
|
||||
{ name: 'Monitoramento', route: '#', icon: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z' },
|
||||
|
|
@ -37,6 +50,7 @@ const menuItems = [
|
|||
</div>
|
||||
|
||||
<nav class="mt-6 px-3 space-y-1">
|
||||
|
||||
<Link v-for="item in menuItems" :key="item.name" :href="item.route === '#' ? '#' : route(item.route)"
|
||||
class="group flex items-center px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200"
|
||||
:class="item.active || route().current(item.route)
|
||||
|
|
@ -50,8 +64,40 @@ const menuItems = [
|
|||
</svg>
|
||||
{{ item.name }}
|
||||
</Link>
|
||||
|
||||
<div v-if="$page.props.auth.user.role === 'admin'" class="pt-4 mt-4 border-t border-gray-100">
|
||||
|
||||
<p class="px-4 text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2">
|
||||
Administração
|
||||
</p>
|
||||
|
||||
<button @click="openTenant"
|
||||
class="w-full group flex items-center px-4 py-3 text-sm font-medium rounded-lg text-gray-600 hover:bg-gray-50 hover:text-ingline-600 transition-all duration-200">
|
||||
<svg class="mr-3 flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-ingline-500" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
||||
</svg>
|
||||
Novo Cliente
|
||||
</button>
|
||||
|
||||
<button @click="openUser"
|
||||
class="w-full group flex items-center px-4 py-3 text-sm font-medium rounded-lg text-gray-600 hover:bg-gray-50 hover:text-ingline-600 transition-all duration-200">
|
||||
<svg class="mr-3 flex-shrink-0 h-5 w-5 text-gray-400 group-hover:text-ingline-500" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" />
|
||||
</svg>
|
||||
Novo Usuário
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
||||
<CreateTenantModal ref="tenantModalRef" />
|
||||
<CreateUserModal ref="userModalRef" />
|
||||
|
||||
<div class="absolute bottom-0 w-full p-4 bg-white border-t border-gray-100">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div
|
||||
|
|
@ -113,10 +159,11 @@ const menuItems = [
|
|||
</button>
|
||||
</template>
|
||||
<template #content>
|
||||
|
||||
<DropdownLink :href="route('profile.edit')"> Perfil </DropdownLink>
|
||||
<SetQueueNameForm />
|
||||
<SetQueueSectorForm />
|
||||
|
||||
|
||||
<div class="border-t border-gray-100 my-1"></div>
|
||||
<DropdownLink :href="route('logout')" method="post" as="button"> Sair </DropdownLink>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
use App\Http\Controllers\QueueController;
|
||||
use App\Http\Controllers\Admin\TenantController;
|
||||
use App\Http\Controllers\Admin\UserController;
|
||||
|
||||
|
||||
Route::get('/', function () {
|
||||
return Inertia::render('Welcome', [
|
||||
|
|
@ -20,6 +23,8 @@
|
|||
Route::put('/queues/sectors', [QueueController::class, 'updateSectors'])
|
||||
->name('queues.update-sectors')
|
||||
->middleware('auth');
|
||||
Route::post('Admin/tenants', [TenantController::class, 'store'])->name('tenants.store')->middleware('auth');
|
||||
Route::post('Admin/users', [UserController::class, 'store'])->name('users.store')->middleware('auth');
|
||||
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])->middleware(['auth'])->name('dashboard');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue