diff --git a/README.md b/README.md index 9ed4356..07d1427 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,8 @@ source.onmessage = (e) => { }; ``` +La interfaz incluye una vista **Feed** accesible desde la barra lateral. Allí se muestran en tiempo real las tarjetas de cada módulo conforme llegan estos eventos. + #### Card Animation The data cards implemented in `ui/src/components/ui/NucleoDataCard.vue` now feature a subtle growing animation when hovered over. This animation is implemented purely with CSS using keyframes and transitions defined within the component's ` diff --git a/ui/src/components/ui/NavBar.vue b/ui/src/components/ui/NavBar.vue index 34f862b..06db6a1 100644 --- a/ui/src/components/ui/NavBar.vue +++ b/ui/src/components/ui/NavBar.vue @@ -12,6 +12,7 @@ const links = [ { to: '/tareas', label: 'Tareas', icon: '📋' }, { to: '/planillas', label: 'Planillas', icon: '📂' }, { to: '/asistencias', label: 'Asistencias', icon: '⏰' }, + { to: '/feed', label: 'Feed', icon: '📰' }, { to: '/config', label: 'Config', icon: '⚙️' }, ] @@ -24,6 +25,7 @@ const accentColorForPath = (path) => { if (path.startsWith('/tareas')) return ui.accentColorTareas if (path.startsWith('/planillas')) return ui.accentColorPlanillas if (path.startsWith('/asistencias')) return ui.accentColorAsistencias + if (path.startsWith('/feed')) return ui.primaryColor if (path.startsWith('/config')) return ui.accentColorConfiguracion return ui.accentColorChat } diff --git a/ui/src/router/index.js b/ui/src/router/index.js index ea9a22c..d4a9628 100644 --- a/ui/src/router/index.js +++ b/ui/src/router/index.js @@ -25,6 +25,9 @@ const routes = [ { path: '/asistencias/nuevo', name: 'asistencias-new', component: () => import('@/views/asistencias/AsistenciaForm.vue') }, { path: '/asistencias/:id', name: 'asistencias-edit', component: () => import('@/views/asistencias/AsistenciaForm.vue'), props: true }, + // ────── Feed en tiempo real ────── + { path: '/feed', name: 'feed', component: () => import('@/views/RealtimeFeedView.vue') }, + // 404 { path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/views/NotFound.vue') } ] diff --git a/ui/src/stores/useRealtime.js b/ui/src/stores/useRealtime.js index a567cdf..a46edd1 100644 --- a/ui/src/stores/useRealtime.js +++ b/ui/src/stores/useRealtime.js @@ -7,6 +7,7 @@ import { useAsistenciasStore } from './useAsistencias' export const useRealtimeStore = defineStore('realtime', { state: () => ({ _sse: null, + events: [], }), actions: { init() { @@ -19,13 +20,16 @@ export const useRealtimeStore = defineStore('realtime', { console.log('🟢 Conexión SSE establecida correctamente'); }; - this._sse.onmessage = (e) => { - console.log('📩 SSE message raw:', e.data); + this._sse.onmessage = (e) => { + console.log('📩 SSE message raw:', e.data); - try { - const payload = JSON.parse(e.data); - console.log('📦 Payload parseado:', payload); - console.log('🧪 Tabla recibida:', payload.table); + try { + const payload = JSON.parse(e.data); + console.log('📦 Payload parseado:', payload); + console.log('🧪 Tabla recibida:', payload.table); + + // store event for feed + this.events.unshift({ ...payload, receivedAt: new Date().toISOString() }); switch (payload.table) { case 'Planilla': diff --git a/ui/src/views/RealtimeFeedView.vue b/ui/src/views/RealtimeFeedView.vue new file mode 100644 index 0000000..16e4fd0 --- /dev/null +++ b/ui/src/views/RealtimeFeedView.vue @@ -0,0 +1,53 @@ + + + + +