Versão 18h39 06/03
This commit is contained in:
parent
20fc4e91f5
commit
3d67602759
@ -1,33 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-card
|
|
||||||
class="pa-4"
|
|
||||||
:class="{ 'striped-bg': item.striped }"
|
|
||||||
:color="item.color"
|
|
||||||
outlined
|
|
||||||
>
|
|
||||||
<v-card-text class="text-center text-white text-h6 card">
|
|
||||||
{{ item.name }}
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
item: Object,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.card {
|
|
||||||
color: #6c00b5!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.striped-bg {
|
|
||||||
|
|
||||||
border-radius: 50px;
|
|
||||||
background: linear-gradient(145deg, #eeeded, #ffffff);
|
|
||||||
box-shadow: 20px 20px 60px #d9d9d9,
|
|
||||||
-20px -20px 60px #ffffff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,227 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref, onMounted } from 'vue'
|
|
||||||
import { useChatStore
|
|
||||||
|
|
||||||
} from '../stores/chatstore';
|
|
||||||
const chatStore = useChatStore()
|
|
||||||
|
|
||||||
// Variáveis de controle do Drawer e do Mini Variant
|
|
||||||
const isDrawerOpen = ref(true)
|
|
||||||
const isMiniVariant = ref(false)
|
|
||||||
const inputMessage = ref('')
|
|
||||||
|
|
||||||
// Funções de controle do Drawer
|
|
||||||
const toggleDrawer = () => {
|
|
||||||
isDrawerOpen.value = !isDrawerOpen.value
|
|
||||||
isMiniVariant.value = !isMiniVariant.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Selecionar um chat da lista
|
|
||||||
const selectChat = (index) => {
|
|
||||||
chatStore.selectedChatIndex = index
|
|
||||||
}
|
|
||||||
|
|
||||||
// Criar um novo chat
|
|
||||||
const createNewChat = () => {
|
|
||||||
const newChatName = `Chat ${chatStore.chatList.length + 1}`
|
|
||||||
chatStore.chatList.push({ id: `chat${chatStore.chatList.length + 1}`, name: newChatName, messages: [] })
|
|
||||||
chatStore.selectedChatIndex = chatStore.chatList.length - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Salvar nome do chat
|
|
||||||
const saveChatName = (index) => {
|
|
||||||
if (chatStore.chatList[index].name.trim() !== '') {
|
|
||||||
chatStore.chatList[index].name = chatStore.chatList[index].name.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enviar uma mensagem
|
|
||||||
const sendMessage = () => {
|
|
||||||
if (inputMessage.value.trim() !== '') {
|
|
||||||
chatStore.chatList[chatStore.selectedChatIndex].messages.push({ text: inputMessage.value, isUser: true })
|
|
||||||
inputMessage.value = ''
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
chatStore.chatList[chatStore.selectedChatIndex].messages.push({
|
|
||||||
text: 'Esta é uma resposta automática.',
|
|
||||||
isUser: false
|
|
||||||
})
|
|
||||||
}, 1000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Carregar os chats do usuário no onMounted
|
|
||||||
onMounted(async () => {
|
|
||||||
const userId = 'user123' // Esse id viria do sistema de autenticação
|
|
||||||
await chatStore.loadChats(userId)
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<v-container fluid no-gutters class="chat">
|
|
||||||
<!-- Drawer Lateral -->
|
|
||||||
<v-navigation-drawer v-model="isDrawerOpen" :mini-variant="isMiniVariant" app permanent>
|
|
||||||
<v-list>
|
|
||||||
<v-list-item v-if="!isMiniVariant">
|
|
||||||
<v-row align="center" justify="space-between" style="width: 100%">
|
|
||||||
<v-col cols="auto">
|
|
||||||
<v-list-item-title class="text-h6">Chats</v-list-item-title>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="auto">
|
|
||||||
<v-icon @click="createNewChat" style="padding-left: 25pt;">mdi-chat-plus-outline</v-icon>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<!-- Lista de chats -->
|
|
||||||
<v-list-item v-for="(chat, index) in chatStore.chatList" :key="chat.id" @click="selectChat(index)"
|
|
||||||
:class="{ 'chat-selected': chatStore.selectedChatIndex === index }" v-if="!isMiniVariant">
|
|
||||||
<v-list-item-content>
|
|
||||||
<v-list-item-title>
|
|
||||||
<v-textarea v-model="chat.name" @focus="chat.isUnderlined = true" @blur="chat.isUnderlined = false"
|
|
||||||
hide-details variant="plain" class="mr-2" :class="{ 'underline': chat.isUnderlined }"
|
|
||||||
@keyup.enter="saveChatName(index)" placeholder="Novo nome" auto-grow rows="1" max-rows="5" />
|
|
||||||
</v-list-item-title>
|
|
||||||
</v-list-item-content>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list>
|
|
||||||
</v-navigation-drawer>
|
|
||||||
|
|
||||||
<!-- Barra de aplicação com o título do chat selecionado -->
|
|
||||||
<v-app-bar app flat>
|
|
||||||
<template v-if="isMiniVariant">
|
|
||||||
<v-btn @click="createNewChat" icon>
|
|
||||||
<v-icon>mdi-chat-plus-outline</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<v-btn @click="toggleDrawer" icon>
|
|
||||||
<v-icon>mdi-menu</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<h2 class="text-h5 ml-2">{{ chatStore.chatList[chatStore.selectedChatIndex]?.name }}</h2>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<v-btn @click="toggleDrawer" icon>
|
|
||||||
<v-icon>mdi-menu</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
<h2 class="text-h5 ml-2">{{ chatStore.chatList[chatStore.selectedChatIndex]?.name }}</h2>
|
|
||||||
</template>
|
|
||||||
</v-app-bar>
|
|
||||||
|
|
||||||
<!-- Conteúdo do chat -->
|
|
||||||
<v-col cols="12" lg="6" xs="10" offset-lg="3" offset-xs="1" class="chat-container">
|
|
||||||
<div class="chat-messages flex-grow-1 overflow-y-auto">
|
|
||||||
<v-list>
|
|
||||||
<v-list-item-group>
|
|
||||||
<v-list-item v-for="(message, index) in chatStore.chatList[chatStore.selectedChatIndex]?.messages" :key="index">
|
|
||||||
<v-list-item-content>
|
|
||||||
<v-row>
|
|
||||||
<v-col cols="12" v-if="message.isUser">
|
|
||||||
<v-row justify="end">
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-card class="user-message" outlined>
|
|
||||||
<v-card-text>{{ message.text }}</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="12" v-else>
|
|
||||||
<v-row justify="start">
|
|
||||||
<v-col cols="8">
|
|
||||||
<v-card class="received-message" outlined>
|
|
||||||
<v-card-text>{{ message.text }}</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-list-item-content>
|
|
||||||
</v-list-item>
|
|
||||||
</v-list-item-group>
|
|
||||||
</v-list>
|
|
||||||
</div>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<!-- Barra de entrada para nova mensagem -->
|
|
||||||
<v-app-bar location="bottom" flat>
|
|
||||||
<v-row align="center">
|
|
||||||
<v-col cols="10" lg="6" offset="1" offset-lg="3">
|
|
||||||
<v-textarea class="input-message" v-model="inputMessage" @keyup.enter="sendMessage"
|
|
||||||
label="Digite sua mensagem" outlined hide-details prepend-inner-icon="mdi-send"
|
|
||||||
@click:append-outer="sendMessage" rows="1" />
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-app-bar>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* Estilo para as mensagens recebidas */
|
|
||||||
.input-message {
|
|
||||||
background-color: #f1f1f1;
|
|
||||||
border-top-left-radius: 16px;
|
|
||||||
border-bottom-left-radius: 0px;
|
|
||||||
border-top-right-radius: 16px;
|
|
||||||
border-bottom-right-radius: 0px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 15pt;
|
|
||||||
max-width: 100%;
|
|
||||||
/* Limita para que o texto não ocupe toda a linha */
|
|
||||||
box-shadow: none;
|
|
||||||
/* Remove a sombra */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilo para as mensagens do usuário */
|
|
||||||
.user-message {
|
|
||||||
background-color: #e3f2fd;
|
|
||||||
border-top-left-radius: 16px;
|
|
||||||
border-bottom-left-radius: 16px;
|
|
||||||
border-top-right-radius: 16px;
|
|
||||||
border-bottom-right-radius: 0px;
|
|
||||||
text-align: end;
|
|
||||||
max-width: 100%;
|
|
||||||
/* Limita para que o texto não ocupe toda a linha */
|
|
||||||
box-shadow: none;
|
|
||||||
/* Remove a sombra */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Estilo para as mensagens recebidas */
|
|
||||||
.received-message {
|
|
||||||
background-color: #f1f1f1;
|
|
||||||
border-top-left-radius: 16px;
|
|
||||||
border-bottom-left-radius: 0px;
|
|
||||||
border-top-right-radius: 16px;
|
|
||||||
border-bottom-right-radius: 16px;
|
|
||||||
text-align: start;
|
|
||||||
max-width: 100%;
|
|
||||||
/* Limita para que o texto não ocupe toda a linha */
|
|
||||||
box-shadow: none;
|
|
||||||
/* Remove a sombra */
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-selected {
|
|
||||||
background-color: #e0f7fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-navigation-drawer {
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-container {
|
|
||||||
height: calc(80vh - 70px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-messages {
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.underline {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,111 +1,105 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-navigation-drawer class="body sidebar-navigation" v-model="drawer" app>
|
<v-navigation-drawer
|
||||||
|
class="sidebar-navigation"
|
||||||
|
v-model="drawer"
|
||||||
|
app
|
||||||
|
>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
v-for="(item, index) in filteredMenuItems"
|
v-for="(item, index) in filteredMenuItems"
|
||||||
:key="index"
|
:key="index"
|
||||||
:to="{ path: item.path }"
|
:to="{ path: item.path }"
|
||||||
exact
|
exact
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
>
|
>
|
||||||
<v-icon>{{ item.icon }}</v-icon>
|
<template v-slot:prepend>
|
||||||
<!-- Alterado para usar v-list-item-content apenas com texto -->
|
<v-icon>{{ item.icon }}</v-icon>
|
||||||
<v-list-item-content>
|
</template>
|
||||||
<span>{{ item.title }}</span>
|
<v-list-item-title>{{ item.title }}</v-list-item-title>
|
||||||
</v-list-item-content>
|
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue';
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router';
|
||||||
import { useAuthStore } from '../stores/auth' // Importa a store de autenticação
|
import { useAuthStore } from '../stores/auth'; // Importa a store de autenticação
|
||||||
|
|
||||||
const drawer = ref(true)
|
const drawer = ref(true);
|
||||||
const router = useRouter()
|
const router = useRouter();
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
// A função getPermissionsByRole retorna as permissões específicas de um papel
|
// Debug: Verificar se a store está sendo carregada corretamente
|
||||||
// Aqui estamos pegando as permissões para "Dashboard"
|
console.log('Auth Store:', authStore);
|
||||||
const dashboardPermissions = authStore.getPermissionsByRole('Dashboard')
|
console.log('Função getPermissionsByRole:', authStore.getPermissionsByRole);
|
||||||
console.log('Permissões do Dashboard:', dashboardPermissions) // Log para verificar as permissões
|
|
||||||
|
|
||||||
// Itens do menu, agora com os itens atualizados: home, dashboard, analytics, reports, testing, users
|
// Obtém as permissões do usuário para "Dashboard"
|
||||||
|
const dashboardPermissions = authStore.getPermissionsByRole('Dashboard') || [];
|
||||||
|
console.log('Permissões do Dashboard:', dashboardPermissions); // Log para verificar as permissões
|
||||||
|
|
||||||
|
// Itens do menu
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ title: 'Home', path: '/dashboard/home', icon: 'mdi mdi-home-circle', requiredPermission: 'read' },
|
{ title: 'Home', path: '/dashboard/home', icon: 'mdi mdi-home-circle', requiredPermission: 'read' },
|
||||||
{ title: 'Dashboard', path: '/dashboard', icon: 'mdi mdi-view-dashboard', requiredPermission: 'read' },
|
{ title: 'Dashboard', path: '/dashboard', icon: 'mdi mdi-view-dashboard', requiredPermission: 'read' },
|
||||||
{ title: 'Analytics', path: '/dashboard/analytics', icon: 'mdi-chart-box', requiredPermission: 'read' },
|
{ title: 'Perfil', path: '/dashboard/profile', icon: 'mdi-account', requiredPermission: 'read' },
|
||||||
{ title: 'Relatórios', path: '/dashboard/reports', icon: 'mdi-file-chart', requiredPermission: 'read' },
|
{ title: 'Configurações', path: '/dashboard/settings', icon: 'mdi-cog', requiredPermission: 'write' },
|
||||||
{ title: 'Testes', path: '/dashboard/testing', icon: 'mdi-flask', requiredPermission: 'write' },
|
{ title: 'Usuários', path: '/dashboard/users', icon: 'mdi-account-multiple-outline', requiredPermission: 'write' },
|
||||||
{ title: 'Gerenciamento de Usuários', path: '/dashboard/users', icon: 'mdi-account-group', requiredPermission: 'write' }
|
{ title: 'Train', path: '/dashboard/train', icon: 'mdi mdi-thought-bubble', requiredPermission: 'write' },
|
||||||
]
|
];
|
||||||
|
|
||||||
// Função para filtrar os itens de menu com base nas permissões do usuário para 'Dashboard'
|
// Filtra os itens de menu com base nas permissões do usuário
|
||||||
const filteredMenuItems = computed(() => {
|
const filteredMenuItems = computed(() => {
|
||||||
const filteredItems = menuItems.filter(item => dashboardPermissions.includes(item.requiredPermission))
|
const filteredItems = menuItems.filter(item => dashboardPermissions.includes(item.requiredPermission));
|
||||||
console.log('Itens do Menu Filtrados:', filteredItems) // Log para verificar os itens filtrados
|
console.log('Itens do Menu Filtrados:', filteredItems); // Log para verificar os itens filtrados
|
||||||
return filteredItems
|
return filteredItems.length > 0 ? filteredItems : menuItems; // Exibir todos temporariamente se vazio
|
||||||
})
|
});
|
||||||
|
|
||||||
// Função para ativar/desativar o drawer
|
|
||||||
const toggleDrawer = () => {
|
|
||||||
drawer.value = !drawer.value
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Estilo para o menu lateral */
|
/* Estilo para o menu lateral */
|
||||||
.sidebar-navigation {
|
.sidebar-navigation {
|
||||||
background: linear-gradient(145deg, #1a237e 0%, #0d47a1 100%);
|
background: linear-gradient(145deg, #1a237e 0%, #0d47a1 100%);
|
||||||
}
|
border-right: none;
|
||||||
|
|
||||||
.v-navigation-drawer {
|
|
||||||
border-style: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilo para os itens de menu */
|
/* Estilo para os itens de menu */
|
||||||
.v-list-item {
|
.v-list-item {
|
||||||
color: rgba(108, 0, 181);
|
color: white;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
border-style: solid;
|
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
margin: 8px 0;
|
margin: 8px 16px;
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-list-item:hover {
|
.v-list-item:hover {
|
||||||
background-color: #f5f5f5;
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
color: rgba(76, 201, 240);
|
color: #4cc9f0;
|
||||||
border-radius: 15px !important;
|
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
border-radius: 50px;
|
margin-right: 16px;
|
||||||
background: linear-gradient(145deg, #eeeded, #ffffff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilo para o item de menu ativo */
|
/* Estilo para o item de menu ativo */
|
||||||
.v-list-item--active {
|
.v-list-item--active {
|
||||||
background-color: #4cc9f0 !important;
|
background-color: #4cc9f0 !important;
|
||||||
color: white !important;
|
color: white !important;
|
||||||
border-radius: 15px !important;
|
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilo para o conteúdo do item de menu */
|
/* Estilo para os ícones */
|
||||||
.v-list-item-content {
|
.v-list-item .v-icon {
|
||||||
margin-left: 16px;
|
margin-right: 16px;
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilo para o botão de recolher */
|
/* Estilo para o botão de recolher (se adicionado futuramente) */
|
||||||
.v-btn {
|
.v-btn {
|
||||||
background: linear-gradient(145deg, #2196f3 0%, #1565c0 100%);
|
background: linear-gradient(145deg, #2196f3 0%, #1565c0 100%);
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilo para o avatar */
|
/* Estilo para o avatar (se adicionado futuramente) */
|
||||||
.gradient-avatar {
|
.gradient-avatar {
|
||||||
background: linear-gradient(145deg, #2196f3 0%, #1565c0 100%);
|
background: linear-gradient(145deg, #2196f3 0%, #1565c0 100%);
|
||||||
}
|
}
|
||||||
@ -113,9 +107,11 @@ const toggleDrawer = () => {
|
|||||||
.profile-section {
|
.profile-section {
|
||||||
background-color: rgba(255, 255, 255, 0.05);
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: 16px;
|
||||||
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-section:hover {
|
.profile-section:hover {
|
||||||
background-color: rgba(0, 0, 0, 0.05);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -8,17 +8,13 @@ import Settings from '../views/Settings.vue'
|
|||||||
import Users from '../views/Users.vue'
|
import Users from '../views/Users.vue'
|
||||||
import UserEditPage from '../views/UserEditPage.vue'
|
import UserEditPage from '../views/UserEditPage.vue'
|
||||||
import Train from '../views/Train.vue'
|
import Train from '../views/Train.vue'
|
||||||
import DashboardView from '../views/DashboardView.vue'
|
|
||||||
import Chat from '../views/Chat.vue'
|
|
||||||
import Home from '../views/Home.vue'
|
import Home from '../views/Home.vue'
|
||||||
import Register from '../views/Register.vue'
|
|
||||||
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: '/', redirect: '/login' },
|
{ path: '/', redirect: '/login' },
|
||||||
{ path: '/login', component: Login },
|
{ path: '/login', component: Login },
|
||||||
|
|
||||||
{ path: '/register', component: Register},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
@ -29,10 +25,7 @@ const routes = [
|
|||||||
{ path: 'profile', name: 'profile', component: Profile },
|
{ path: 'profile', name: 'profile', component: Profile },
|
||||||
{ path: 'settings', name: 'settings', component: Settings },
|
{ path: 'settings', name: 'settings', component: Settings },
|
||||||
{ path: 'users', name: 'users', component: Users },
|
{ path: 'users', name: 'users', component: Users },
|
||||||
{ path: 'train', name: 'train', component: Train},
|
{ path: 'train', name: 'train', component: Train},
|
||||||
{ path: 'dashboardview', name: 'dashboard-view', component: DashboardView},
|
|
||||||
{ path: 'chat', name: 'chat', component: Chat },
|
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'users/edit/:id', // Removido `/` do início para alinhar ao filho de dashboard
|
path: 'users/edit/:id', // Removido `/` do início para alinhar ao filho de dashboard
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
|
|
||||||
<v-container>
|
|
||||||
<ChatComponent /> <!-- Adiciona o componente de chat aqui -->
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import ChatComponent from '../components/ChatComponent.vue' // Importa o componente de chat
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
/* Adicione estilos se necessário */
|
|
||||||
</style>
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container>
|
|
||||||
<v-row>
|
|
||||||
<v-col v-for="(item, index) in dashboardStore.items" :key="index" cols="12" md="4">
|
|
||||||
<CardItem :item="item" />
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useDashboardStore } from '../stores/useDashboardStore';
|
|
||||||
import CardItem from '../components/CardItem.vue'
|
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore();
|
|
||||||
</script>
|
|
||||||
@ -54,7 +54,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
quickStats: [
|
quickStats: [
|
||||||
{ title: 'Câmeras Ativas', value: 0, icon: 'mdi-cctv', color: 'success' },
|
{ title: 'Câmeras Ativas', value: 0, icon: 'mdi-cctv', color: 'success' },
|
||||||
{ title: 'Modelos AI', value: 0, icon: 'mdi-brain', color: 'primary' },
|
{ title: 'Modelos', value: 0, icon: 'mdi-brain', color: 'primary' },
|
||||||
{ title: 'Alertas 24h', value: 0, icon: 'mdi-bell', color: 'warning' },
|
{ title: 'Alertas 24h', value: 0, icon: 'mdi-bell', color: 'warning' },
|
||||||
{ title: 'Usuários', value: 0, icon: 'mdi-account-group', color: 'info' }
|
{ title: 'Usuários', value: 0, icon: 'mdi-account-group', color: 'info' }
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
<!-- src/views/Login.vue -->
|
|
||||||
<template>
|
<template>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<div class="login-wrapper">
|
<div class="login-wrapper">
|
||||||
@ -11,26 +10,11 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right Side: Login/Register Form -->
|
<!-- Login Form -->
|
||||||
<div class="login-card">
|
<div class="login-card">
|
||||||
<div class="tab-buttons">
|
<div class="form-container">
|
||||||
<button
|
<h3 class="login-title">Bem-vindo</h3>
|
||||||
:class="['tab-btn', { active: currentTab === 'login' }]"
|
<p class="login-sub-title">Faça login para continuar</p>
|
||||||
@click="currentTab = 'login'"
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
:class="['tab-btn', { active: currentTab === 'register' }]"
|
|
||||||
@click="currentTab = 'register'"
|
|
||||||
>
|
|
||||||
Register
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Login Form -->
|
|
||||||
<div v-if="currentTab === 'login'" class="form-container">
|
|
||||||
<h3 class="login-title">Bem-vindo</h3> <p class="login-sub-title">Faça login para continuar</p>
|
|
||||||
<form @submit.prevent="handleLogin" class="login-form">
|
<form @submit.prevent="handleLogin" class="login-form">
|
||||||
|
|
||||||
<!-- Email Input -->
|
<!-- Email Input -->
|
||||||
@ -87,92 +71,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Register Form -->
|
|
||||||
<div v-if="currentTab === 'register'" class="form-container">
|
|
||||||
<h3 class="register-title">Criar Conta</h3>
|
|
||||||
<p class="register-sub-title">Preencha os dados para se registrar</p>
|
|
||||||
<form @submit.prevent="handleRegister" class="login-form">
|
|
||||||
|
|
||||||
<!-- Name Input -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="register-name">Nome</label>
|
|
||||||
<input
|
|
||||||
id="register-name"
|
|
||||||
v-model="name"
|
|
||||||
type="text"
|
|
||||||
placeholder="Digite seu nome completo"
|
|
||||||
:class="{'input-error': nameError}"
|
|
||||||
@blur="validateName"
|
|
||||||
/>
|
|
||||||
<span v-if="nameError" class="error-message">
|
|
||||||
{{ nameError }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email Input -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="register-email">Email</label>
|
|
||||||
<input
|
|
||||||
id="register-email"
|
|
||||||
v-model="registerEmail"
|
|
||||||
type="email"
|
|
||||||
placeholder="Digite seu email"
|
|
||||||
:class="{'input-error': registerEmailError}"
|
|
||||||
@blur="validateRegisterEmail"
|
|
||||||
/>
|
|
||||||
<span v-if="registerEmailError" class="error-message">
|
|
||||||
{{ registerEmailError }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Password and Confirm Password Inputs (side by side) -->
|
|
||||||
<div class="password-row">
|
|
||||||
<!-- Password Input -->
|
|
||||||
<div class="form-group password-field">
|
|
||||||
<label for="register-password">Senha</label>
|
|
||||||
<input
|
|
||||||
id="register-password"
|
|
||||||
v-model="registerPassword"
|
|
||||||
type="password"
|
|
||||||
placeholder="Digite sua senha"
|
|
||||||
:class="{'input-error': registerPasswordError}"
|
|
||||||
@blur="validateRegisterPassword"
|
|
||||||
/>
|
|
||||||
<span v-if="registerPasswordError" class="error-message">
|
|
||||||
{{ registerPasswordError }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Confirm Password Input -->
|
|
||||||
<div class="form-group password-field">
|
|
||||||
<label for="confirm-password">Confirmar Senha</label>
|
|
||||||
<input
|
|
||||||
id="confirm-password"
|
|
||||||
v-model="confirmPassword"
|
|
||||||
type="password"
|
|
||||||
placeholder="Confirme sua senha"
|
|
||||||
:class="{'input-error': confirmPasswordError}"
|
|
||||||
@blur="validateConfirmPassword"
|
|
||||||
/>
|
|
||||||
<span v-if="confirmPasswordError" class="error-message">
|
|
||||||
{{ confirmPasswordError }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Register Button -->
|
|
||||||
<div class="button-group">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-primary"
|
|
||||||
:disabled="!isRegisterFormValid"
|
|
||||||
>
|
|
||||||
Registrar
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -188,25 +86,12 @@ import { useAuthStore } from '../stores/auth'
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
// Current tab
|
|
||||||
const currentTab = ref('login')
|
|
||||||
|
|
||||||
// Login Form Fields
|
// Login Form Fields
|
||||||
const email = ref('')
|
const email = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
const emailError = ref('')
|
const emailError = ref('')
|
||||||
const passwordError = ref('')
|
const passwordError = ref('')
|
||||||
|
|
||||||
// Register Form Fields
|
|
||||||
const name = ref('')
|
|
||||||
const registerEmail = ref('')
|
|
||||||
const registerPassword = ref('')
|
|
||||||
const confirmPassword = ref('')
|
|
||||||
const nameError = ref('')
|
|
||||||
const registerEmailError = ref('')
|
|
||||||
const registerPasswordError = ref('')
|
|
||||||
const confirmPasswordError = ref('')
|
|
||||||
|
|
||||||
// Validation Methods - Login
|
// Validation Methods - Login
|
||||||
const validateEmail = () => {
|
const validateEmail = () => {
|
||||||
const emailRegex = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$|^[a-zA-Z0-9_.-]+$/
|
const emailRegex = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$|^[a-zA-Z0-9_.-]+$/
|
||||||
@ -233,80 +118,15 @@ const validatePassword = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validation Methods - Register
|
|
||||||
const validateName = () => {
|
|
||||||
if (!name.value) {
|
|
||||||
nameError.value = 'Nome é obrigatório'
|
|
||||||
return false
|
|
||||||
} else if (name.value.length < 3) {
|
|
||||||
nameError.value = 'Nome deve ter pelo menos 3 caracteres'
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
nameError.value = ''
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateRegisterEmail = () => {
|
|
||||||
const emailRegex = /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/
|
|
||||||
|
|
||||||
if (!registerEmail.value) {
|
|
||||||
registerEmailError.value = 'Email é obrigatório'
|
|
||||||
return false
|
|
||||||
} else if (!emailRegex.test(registerEmail.value)) {
|
|
||||||
registerEmailError.value = 'Formato de email inválido'
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
registerEmailError.value = ''
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateRegisterPassword = () => {
|
|
||||||
if (!registerPassword.value) {
|
|
||||||
registerPasswordError.value = 'Senha é obrigatória'
|
|
||||||
return false
|
|
||||||
} else if (registerPassword.value.length < 6) {
|
|
||||||
registerPasswordError.value = 'Senha deve ter pelo menos 6 caracteres'
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
registerPasswordError.value = ''
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateConfirmPassword = () => {
|
|
||||||
if (!confirmPassword.value) {
|
|
||||||
confirmPasswordError.value = 'Confirme sua senha'
|
|
||||||
return false
|
|
||||||
} else if (confirmPassword.value !== registerPassword.value) {
|
|
||||||
confirmPasswordError.value = 'As senhas não correspondem'
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
confirmPasswordError.value = ''
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch for changes to validate in real-time
|
// Watch for changes to validate in real-time
|
||||||
watch(email, () => { if (email.value) validateEmail() })
|
watch(email, () => { if (email.value) validateEmail() })
|
||||||
watch(password, () => { if (password.value) validatePassword() })
|
watch(password, () => { if (password.value) validatePassword() })
|
||||||
watch(name, () => { if (name.value) validateName() })
|
|
||||||
watch(registerEmail, () => { if (registerEmail.value) validateRegisterEmail() })
|
|
||||||
watch(registerPassword, () => { if (registerPassword.value) validateRegisterPassword() })
|
|
||||||
watch(confirmPassword, () => { if (confirmPassword.value) validateConfirmPassword() })
|
|
||||||
|
|
||||||
// Form Validation
|
// Form Validation
|
||||||
const isLoginFormValid = computed(() => {
|
const isLoginFormValid = computed(() => {
|
||||||
return email.value && password.value && !emailError.value && !passwordError.value
|
return email.value && password.value && !emailError.value && !passwordError.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const isRegisterFormValid = computed(() => {
|
|
||||||
return name.value && registerEmail.value && registerPassword.value &&
|
|
||||||
confirmPassword.value && !nameError.value && !registerEmailError.value &&
|
|
||||||
!registerPasswordError.value && !confirmPasswordError.value
|
|
||||||
})
|
|
||||||
|
|
||||||
// Login Handlers
|
// Login Handlers
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
if (isLoginFormValid.value) {
|
if (isLoginFormValid.value) {
|
||||||
@ -329,29 +149,10 @@ const handleSSO = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRegister = async () => {
|
|
||||||
if (isRegisterFormValid.value) {
|
|
||||||
try {
|
|
||||||
await authStore.register(name.value, registerEmail.value, registerPassword.value)
|
|
||||||
alert('Registro realizado com sucesso! Faça login para continuar.')
|
|
||||||
|
|
||||||
// Clear register form
|
|
||||||
name.value = ''
|
|
||||||
registerEmail.value = ''
|
|
||||||
registerPassword.value = ''
|
|
||||||
confirmPassword.value = ''
|
|
||||||
|
|
||||||
// Switch to login tab
|
|
||||||
currentTab.value = 'login'
|
|
||||||
} catch (error) {
|
|
||||||
alert('Falha no registro. Por favor, tente novamente.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* Mantendo os estilos do login */
|
||||||
.login-container {
|
.login-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -365,7 +166,7 @@ const handleRegister = async () => {
|
|||||||
|
|
||||||
.login-wrapper {
|
.login-wrapper {
|
||||||
width: 70%;
|
width: 70%;
|
||||||
max-width: 600px; /* Reduzido para focar na área do formulário */
|
max-width: 600px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
@ -383,11 +184,11 @@ const handleRegister = async () => {
|
|||||||
.logo-top {
|
.logo-top {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 0rem;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-image {
|
.logo-image {
|
||||||
max-width: 150px;
|
max-width: 250px;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,99 +196,28 @@ const handleRegister = async () => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-buttons {
|
|
||||||
display: flex;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
border-bottom: 1px solid #e5e7eb;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn {
|
|
||||||
flex: 1;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0.75rem;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #6b7280;
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-btn.active {
|
|
||||||
color: #36a8ca;
|
|
||||||
border-bottom-color: #4cc9f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
text-align: light;
|
text-align: flex;
|
||||||
color: #2eaacf;
|
color: #2eaacf;
|
||||||
margin-bottom: 0px; /* Aumente esse valor se precisar de mais espaço entre o título e o subtítulo */
|
font-size: 23px;
|
||||||
font-size: 20px;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-sub-title {
|
.login-sub-title {
|
||||||
text-align: light;
|
text-align: flex;
|
||||||
color: #2eaacf;
|
color: #2eaacf;
|
||||||
margin-top: 0; /* Pode ajustar se quiser controlar o espaçamento entre título e subtítulo */
|
|
||||||
margin-bottom: 20px; /* Ajuste esse valor conforme necessário */
|
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
text-transform: lowercase;
|
margin-bottom: 1rem;
|
||||||
}
|
|
||||||
|
|
||||||
.register-title {
|
|
||||||
text-align: light;
|
|
||||||
color: #2eaacf;
|
|
||||||
margin-bottom: 0px; /* Aumente esse valor se precisar de mais espaço entre o título e o subtítulo */
|
|
||||||
font-size: 20px;
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
.register-sub-title {
|
|
||||||
text-align: light;
|
|
||||||
color: #2eaacf;
|
|
||||||
margin-top: 0; /* Pode ajustar se quiser controlar o espaçamento entre título e subtítulo */
|
|
||||||
margin-bottom: 20px; /* Ajuste esse valor conforme necessário */
|
|
||||||
font-size: 15px;
|
|
||||||
text-transform: lowercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Estilos para campos de senha lado a lado */
|
|
||||||
.password-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.password-field {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
color: #4cc9f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input {
|
.form-group input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
outline: none;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group input:focus {
|
|
||||||
border-color: #4cc9f0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-error {
|
.input-error {
|
||||||
@ -497,40 +227,27 @@ const handleRegister = async () => {
|
|||||||
.error-message {
|
.error-message {
|
||||||
color: red;
|
color: red;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
margin-top: 0.25rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-group {
|
.button-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin-top: 1rem;
|
margin-top: 0rem;
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 0.75rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s, transform 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover:not(:disabled) {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:active:not(:disabled) {
|
|
||||||
transform: translateY(1px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
|
padding: 3px;
|
||||||
background-color: #4cc9f0;
|
background-color: #4cc9f0;
|
||||||
color: white;
|
color: white;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
|
padding: 3px;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
color: #4cc9f0;
|
color: #4cc9f0;
|
||||||
|
border-radius: 10px;
|
||||||
border: 1px solid #4cc9f0;
|
border: 1px solid #4cc9f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,25 +255,4 @@ const handleRegister = async () => {
|
|||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.login-wrapper {
|
|
||||||
flex-direction: column;
|
|
||||||
width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-section {
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-image {
|
|
||||||
max-width: 500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ajuste responsivo para campos de senha em telas pequenas */
|
|
||||||
.password-row {
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user