Merge pull request #52 from josedario87/codex/remove-chat-interface-and-navbar,-add-floating-chat-button

Add floating chat widget
This commit is contained in:
josedario87
2025-06-11 04:52:00 -06:00
committed by GitHub
6 changed files with 191 additions and 43 deletions

View File

@@ -2,7 +2,9 @@
import { watchEffect, computed } from 'vue' // Added computed
import TopBar from '@/components/ui/TopBar.vue'
import NavBar from '@/components/ui/NavBar.vue'
import FloatingChat from '@/components/chat/FloatingChat.vue'
import SnackbarContainer from '@/components/ui/SnackbarContainer.vue'
import { useUi } from '@/stores/useUi'
const ui = useUi()
@@ -72,6 +74,7 @@ const transitionDurationStyle = computed(() => {
]" :style="transitionDurationStyle">
<!-- NavBar fija -->
<NavBar />
<FloatingChat />
<!-- contenido principal -->
<main class="min-h-[calc(100vh-56px)] flex flex-col overflow-hidden">

View File

@@ -0,0 +1,101 @@
<script setup>
import { ref } from 'vue'
import CanvasChat from './CanvasChat.vue'
const open = ref(false)
</script>
<template>
<div>
<button
v-if="!open"
@click="open = true"
class="chat-fab"
aria-label="Abrir chat"
>
💬
</button>
<transition name="fade">
<div v-if="open" class="chat-window">
<header class="chat-header">
<h3 class="title">Chat</h3>
<button
@click="open = false"
class="close-btn"
aria-label="Cerrar chat"
>
</button>
</header>
<CanvasChat class="flex-1 min-h-0" />
</div>
</transition>
</div>
</template>
<style scoped>
.chat-fab {
position: fixed;
bottom: 1rem;
right: 1rem;
width: 3rem;
height: 3rem;
border-radius: 50%;
background-color: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
z-index: 60;
}
.chat-window {
position: fixed;
bottom: 1rem;
right: 1rem;
width: 24rem; /* 384px */
height: 32rem; /* 512px */
background: var(--background-color);
background-image: linear-gradient(
135deg,
rgba(255, 255, 255, 0.6),
rgba(255, 255, 255, 0.2)
);
border: 1px solid var(--border-color);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
border-radius: 0.5rem;
backdrop-filter: blur(8px);
display: flex;
flex-direction: column;
z-index: 60;
}
.chat-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--border-color);
font-weight: 600;
color: var(--primary-color);
}
.close-btn {
background: var(--secondary-color);
border: none;
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
cursor: pointer;
color: var(--background-color);
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
}
.close-btn:hover {
filter: brightness(1.1);
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>

View File

@@ -10,7 +10,7 @@ const realtime = useRealtimeStore()
// enlaces de la app
const links = [
{ to: '/', label: 'Chat', icon: '💬' },
{ to: '/empleados', label: 'Empleados', icon: moduleInfo.Cliente.icon },
{ to: '/tareas', label: 'Tareas', icon: moduleInfo.TareaRealizada.icon },
{ to: '/planillas', label: 'Planillas', icon: moduleInfo.Planilla.icon },
@@ -39,7 +39,7 @@ const accentColorForPath = (path) => {
if (path.startsWith('/asistencias')) return ui.accentColorAsistencias
if (path.startsWith('/feed')) return ui.primaryColor
if (path.startsWith('/config')) return ui.accentColorConfiguracion
return ui.accentColorChat
return ui.primaryColor
}
const tableForPath = (path) => {

View File

@@ -1,8 +1,8 @@
import { createRouter, createMemoryHistory , createWebHashHistory} from 'vue-router'
const routes = [
// Chat principal y config
{ path: '/', name: 'chat', component: () => import('@/views/ChatView.vue') },
// Configuración
{ path: '/', name: 'home', component: () => import('@/views/HomeView.vue') },
{ path: '/config', name: 'settings', component: () => import('@/views/SettingsView.vue') },
// ────── Empleados ──────

View File

@@ -1,39 +0,0 @@
<script setup>
/* Vista raíz “/” → muestra el chat estilo ChatGPT */
import CanvasChat from '@/components/chat/CanvasChat.vue'
</script>
<template>
<div class="chat-view-container flex flex-col h-full">
<header class="page-header">
<h1>Chat</h1>
</header>
<CanvasChat class="flex-1 min-h-0" /> <!-- Added min-h-0 -->
</div>
</template>
<style scoped>
.chat-view-container {
display: flex;
flex-direction: column;
height: 100%;
background-color: var(--background-color-chat, #F0F0F0); /* Fallback to store default */
}
.page-header {
display: flex;
justify-content: space-between; /* Consistent with other headers, even if no button on right */
align-items: center;
margin-bottom: 25px; /* Consistent with PlanillasIndex */
padding: 10px 20px; /* Provides padding for the header itself */
border-bottom: 1px solid #eee; /* Consistent with PlanillasIndex */
}
.page-header h1 {
color: var(--accent-color-chat, #0D9488); /* Fallback to store default */
font-size: 2.2em; /* Consistent with PlanillasIndex */
font-weight: 600; /* Consistent with PlanillasIndex */
}
/* CanvasChat is expected to manage its own internal padding and scrolling */
</style>

83
ui/src/views/HomeView.vue Normal file
View File

@@ -0,0 +1,83 @@
<script setup>
import { onMounted } from 'vue'
const features = [
{ title: 'Empleados', text: 'Administra tu equipo y sus perfiles.' },
{ title: 'Tareas', text: 'Asigna tareas y realiza seguimiento.' },
{ title: 'Planillas', text: 'Gestiona las planillas de trabajo.' },
{ title: 'Asistencias', text: 'Controla la asistencia del personal.' }
]
onMounted(() => {
document.querySelectorAll('.feature').forEach((el, i) => {
el.style.animationDelay = `${i * 0.2}s`
})
})
</script>
<template>
<div class="landing">
<section class="hero">
<h1>Núcleo</h1>
<p>Gestión integral de tu organización</p>
<RouterLink to="/empleados" class="btn-start">Comenzar</RouterLink>
</section>
<section class="features">
<div v-for="f in features" :key="f.title" class="feature">
<h2>{{ f.title }}</h2>
<p>{{ f.text }}</p>
</div>
</section>
</div>
</template>
<style scoped>
.landing {
text-align: center;
padding: 2rem 1rem;
}
.hero h1 {
font-size: 3rem;
color: var(--primary-color);
animation: fade-in-down 0.6s ease both;
}
.hero p {
margin-top: 0.5rem;
animation: fade-in-down 0.8s ease both;
}
.btn-start {
margin-top: 1.5rem;
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: var(--primary-color);
color: white;
border-radius: 0.5rem;
transition: background-color 0.3s;
}
.btn-start:hover { background-color: var(--secondary-color); }
.features {
margin-top: 3rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.feature {
padding: 1rem;
border: 1px solid var(--border-color);
border-radius: 0.5rem;
background-color: var(--background-color);
opacity: 0;
animation: fade-in-up 0.5s forwards;
}
@keyframes fade-in-down {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: none; }
}
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: none; }
}
</style>