Fix: Correct EmpleadoForm background and Chat color variables

This commit addresses UI feedback regarding the EmpleadoForm background
and color variable usage in the Chat module.

Corrections:

1.  **`EmpleadoForm.vue` Background:**
    *   Removed the explicit `bg-gray-100` from the outer container of
      `EmpleadoForm.vue`.
    *   Applied a new class `.empleado-form-page-container` to the outer
      container, styled for consistency with index page containers (padding,
      max-width, margin, default font, no explicit background).
    *   The form itself retains its `bg-white` card appearance, which now sits
      on a page background consistent with other views.

2.  **Chat Module Color Management (`CanvasChat.vue` & `useUi.js`):**
    *   **`useUi.js` Store:**
        *   Added `accentColorChat` (defaulting to teal `rgb(13, 148, 136)`)
          and `bgColorChat` (defaulting to light gray `rgb(249, 250, 251)`)
          to the store's state.
        *   Included these new keys in `appearanceSettingKeys` for persistence.
    *   **`CanvasChat.vue` Component:**
        *   Now imports and uses the `useUi` store.
        *   The main chat container's background is dynamically bound to
          `ui.bgColorChat`.
        *   The background color for user-sent messages and the send button
          is dynamically bound to `ui.accentColorChat`.
        *   Input field focus and scrollbar styling continue to leverage the
          globally defined CSS variables (`--accent-color-chat` and
          `--accent-color-chat-rgb`), which use the same default teal color,
          ensuring consistency.

These changes ensure the `EmpleadoForm.vue` background is visually consistent
with other application forms/pages and that the Chat module's theming is
correctly managed through the central UI store and established CSS variables.
This commit is contained in:
google-labs-jules[bot]
2025-05-31 09:26:11 +00:00
parent 394db63d2a
commit 97f388b4c3
16 changed files with 203 additions and 226 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div :style="{ backgroundColor: ui.tableBgColorAsistencias }" class="shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="bg-white shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="flex justify-between items-center mb-3 md:mb-4 pb-2 md:pb-3 border-b border-gray-100">
<h4 class="text-lg md:text-xl font-semibold" :style="{ color: 'var(--accent-color-asistencias)' }">Asistencia ID: {{ asistencia.id }}</h4>
<span :class="['px-2 py-1 text-xs font-bold text-white rounded-full', getStatusClass(asistencia.estado)]">
@@ -34,11 +34,8 @@
<script setup>
import { defineProps, defineEmits } from 'vue';
import { useAsistenciasStore } from '../../stores/useAsistencias';
import { useUi } from '../../stores/useUi.js';
import { computed } from 'vue';
const ui = useUi();
const props = defineProps({
asistencia: {
type: Object,

View File

@@ -1,8 +1,10 @@
<script setup>
import { ref, nextTick, onMounted, watch } from 'vue'
import { useChat } from '@/stores/useChat'
import { useUi } from '@/stores/useUi';
const chat = useChat()
const ui = useUi(); // UI store for colors
const msg = ref('')
const list = ref(null)
@@ -40,16 +42,16 @@ watch(() => chat.items.length, scrollBottom)
<template>
<!-- se adapta al contenedor flex, sin superponer la sidebar -->
<div class="flex flex-col flex-1 min-h-0 bg-gray-50">
<div class="flex flex-col flex-1 min-h-0" :style="{ backgroundColor: ui.bgColorChat }">
<!-- historial -->
<div ref="list" class="flex-1 min-h-0 overflow-auto p-6 space-y-4 custom-scroll">
<template v-for="(m,i) in chat.items" :key="i">
<!-- mensaje de texto -->
<div :class="m.owner==='yo' ? 'flex justify-end' : 'flex justify-start'" v-if="m.type==='text'">
<div
class="max-w-lg px-4 py-2 shadow break-words rounded-lg"
:class="m.owner==='yo' ? 'text-white' : 'bg-white text-gray-900 dark:bg-gray-700 dark:text-gray-200'"
:style="m.owner==='yo' ? { backgroundColor: 'var(--accent-color-chat, #0D9488)' } : {}">
class="user-message max-w-lg rounded-lg px-4 py-2 shadow break-words"
:class="m.owner === 'yo' ? 'is-user' : 'bg-white text-gray-900'"
:style="m.owner === 'yo' ? { backgroundColor: ui.accentColorChat, color: 'white' } : {}">
{{ m.text }}
</div>
</div>
@@ -66,12 +68,12 @@ watch(() => chat.items.length, scrollBottom)
@keydown="handleKey"
rows="1"
placeholder="Escribí un mensaje… (Enter para enviar, Shift+Enter salto)"
class="flex-1 resize-none rounded-md border border-gray-300 dark:border-gray-600 p-3 shadow-sm focus:outline-none focus:ring-2 focus:ring-[var(--accent-color-chat,#0D9488)] focus:border-[var(--accent-color-chat,#0D9488)] dark:bg-gray-700 dark:text-white custom-scroll"
class="chat-input flex-1 resize-none rounded-lg border p-3 focus:outline-none focus:ring-2 custom-scroll"
/>
<button
type="submit"
class="px-4 py-2 rounded-md text-white transition-colors duration-150 ease-in-out shadow-sm hover:brightness-90"
:style="{ backgroundColor: 'var(--accent-color-chat, #0D9488)' }">
class="send-button px-4 py-2 rounded-lg text-white transition"
:style="{ backgroundColor: ui.accentColorChat }">
</button>
</form>
@@ -79,25 +81,41 @@ watch(() => chat.items.length, scrollBottom)
</template>
<style scoped>
/* Default accent color for chat if not provided by CSS variable */
:root {
--accent-color-chat-fallback: #0D9488; /* Teal-700 as a fallback */
--accent-color-chat-alpha-35-fallback: rgba(13, 148, 136, 0.35);
--accent-color-chat-alpha-70-fallback: rgba(13, 148, 136, 0.7);
--accent-color-chat-alpha-60-fallback: rgba(13, 148, 136, 0.6);
/* :root definitions for --accent-color-chat-* removed, will use global definitions from style.css */
.user-message.is-user {
/* background-color is now handled by inline style if owner is 'yo' */
/* color: white; is also handled by inline style for user messages */
/* This class can be kept if it has other styles, or removed if not. */
}
.chat-input {
/* General input styling, assuming border-gray-300 is default for 'border' */
/* Padding p-3 is fine, rounded-lg is fine */
border-color: #D1D5DB; /* Explicitly Tailwind's gray-300 for clarity */
}
.chat-input:focus {
border-color: var(--accent-color-chat); /* Or use Tailwind's focus:border-accent-color-chat if defined */
box-shadow: 0 0 0 2px rgba(var(--accent-color-chat-rgb), 0.4); /* Custom focus ring to match ring-2 focus:ring-color */
/* Replaces: focus:ring-2 focus:ring-[var(--accent-color-chat)] */
/* Note: Tailwind's focus:ring-2 focus:ring-color utility is often simpler if you can set it up */
}
.send-button {
/* background-color is now handled by inline style */
}
.send-button:hover {
filter: brightness(0.9); /* Consistent with other refactored buttons */
}
.send-button:focus {
outline: none;
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-chat); /* Consistent focus */
}
.custom-scroll::-webkit-scrollbar { width: 8px; }
.custom-scroll::-webkit-scrollbar-track { background: transparent; }
.custom-scroll::-webkit-scrollbar-thumb {
background-color: var(--accent-color-chat-alpha-35, var(--accent-color-chat-alpha-35-fallback));
border-radius: 4px;
}
.custom-scroll:hover::-webkit-scrollbar-thumb {
background-color: var(--accent-color-chat-alpha-70, var(--accent-color-chat-alpha-70-fallback));
}
.custom-scroll {
scrollbar-width: thin;
scrollbar-color: var(--accent-color-chat-alpha-60, var(--accent-color-chat-alpha-60-fallback)) transparent;
}
.custom-scroll::-webkit-scrollbar-thumb { background-color: rgba(var(--accent-color-chat-rgb),.35); border-radius: 4px; }
.custom-scroll:hover::-webkit-scrollbar-thumb { background-color: rgba(var(--accent-color-chat-rgb),.7); }
.custom-scroll { scrollbar-width: thin; scrollbar-color: rgba(var(--accent-color-chat-rgb),.6) transparent; }
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div :style="{ backgroundColor: ui.tableBgColorEmpleados }" class="shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="bg-white shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="flex items-center mb-3 md:mb-4 pb-2 md:pb-3 border-b border-gray-100">
<img
:src="employee.avatar_url || 'https://via.placeholder.com/150'"
@@ -39,56 +39,67 @@
@mouseleave="buttonHover($event, false)"
:class="`focus:ring-[var(--accent-color-empleados)]`"
>Editar</button>
<button
@click="confirmDeleteEmpleado"
class="px-3 py-1 md:px-4 md:py-2 text-xs md:text-sm font-medium rounded-md transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 bg-red-600 hover:bg-red-700 text-white focus:ring-red-500"
>Eliminar</button>
<!-- "View Details" button removed for consistency as other modules use the edit view for details -->
</div>
</div>
</template>
<script setup>
import { useUi } from '../../stores/useUi.js';
import { useEmpleadosStore } from '../../stores/useEmpleados'; // Ensure correct path
<script setup lang="ts">
import { PropType } from 'vue'
import { useRouter } from 'vue-router'
import { useEmpleadosStore } from '@/stores/useEmpleados.js'; // Adjust path as needed
const ui = useUi();
const emit = defineEmits(['edit']);
const empleadosStore = useEmpleadosStore();
interface Employee {
id: string | number
name: string
cedula: number // Assuming cedula is always present; adjust if optional
avatar_url?: string
telefono?: string
ubicacion: string // Assuming ubicacion is always present
idciat?: string
grupo_estudio?: string
}
const props = defineProps({
employee: {
type: Object,
type: Object as PropType<Employee>,
required: true,
},
});
})
const router = useRouter()
const empleadosStore = useEmpleadosStore();
const handleEdit = () => {
emit('edit', props.employee.id);
};
router.push(`/empleados/${props.employee.id}`)
}
const confirmDeleteEmpleado = () => {
if (confirm(`¿Está seguro de que desea eliminar al empleado "${props.employee.name}" (ID: ${props.employee.id})?`)) {
deleteEmpleado();
const confirmDeleteEmployee = () => {
if (confirm(`¿Está seguro de que desea eliminar al empleado "${props.employee.name}" (ID: ${props.employee.id})? Esta acción no se puede deshacer.`)) {
deleteEmployee();
}
};
const deleteEmpleado = async () => {
const deleteEmployee = async () => {
try {
await empleadosStore.deleteEmpleado(props.employee.id);
// Optionally, show a success notification or emit an event if needed,
// though typically the list will update reactively from the store.
// Optionally, you might want to emit an event or show a success notification
// For example: emit('deleted', props.employee.id);
// Or use a toast notification system if you have one integrated.
} catch (error) {
console.error('Error deleting employee:', error);
alert('Ocurrió un error al eliminar el empleado.');
// It's good practice to inform the user.
// Replace alert with a more sophisticated notification if available (e.g., toast).
alert(`Ocurrió un error al eliminar el empleado: ${error.message || 'Error desconocido'}`);
}
};
const buttonHover = (event, isHovering) => {
const target = event.target;
const buttonHover = (event: MouseEvent, isHovering: boolean) => {
const target = event.target as HTMLElement;
if (isHovering) {
target.style.filter = 'brightness(90%)';
target.style.filter = 'brightness(90%)'; // Darken slightly
} else {
target.style.filter = 'brightness(100%)';
target.style.filter = 'brightness(100%)'; // Back to normal
}
};
</script>

View File

@@ -63,8 +63,8 @@
<button @click="handleEdit(employee.id)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-600/20 focus:ring-blue-500 dark:focus:ring-blue-400" title="Editar Empleado">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" /></svg>
</button>
<button @click="confirmDeleteEmpleado(employee)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-600/20 focus:ring-red-500 dark:focus:ring-red-400" title="Eliminar">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12.56 0c1.153 0 2.24.03 3.22.077m3.22-.077L10.879 3.28a2.25 2.25 0 012.244-2.077h.093c.956 0 1.853.543 2.244 2.077L14.74 5.79m-4.858 0l-2.828-2.828A1.875 1.875 0 016.188 2.188l2.828 2.828m6.912 0l2.828-2.828a1.875 1.875 0 00-2.652-2.652L12 5.79M9.26 9h5.48L9.26 9z" /></svg>
<button @click="handleViewDetails(employee.id)" class="p-1.5 sm:p-2 rounded-md transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 text-green-600 dark:text-green-400 hover:text-green-800 dark:hover:text-green-300 hover:bg-green-100 dark:hover:bg-green-600/20 focus:ring-green-500 dark:focus:ring-green-400" title="Ver Detalles">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"><path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" /></svg>
</button>
</div>
</td>
@@ -74,38 +74,58 @@
</div>
</template>
<script setup>
<script setup lang="ts">
import { PropType, defineEmits } from 'vue'
// No longer using useRouter directly in this component for navigation
// import { useRouter } from 'vue-router'
import { useUi } from '../../stores/useUi.js';
import { useEmpleadosStore } from '../../stores/useEmpleados';
import { useEmpleadosStore } from '../../stores/useEmpleados.js'; // For delete functionality
const ui = useUi();
const emit = defineEmits(['edit']);
const empleadosStore = useEmpleadosStore();
// Interface for Employee object structure
interface Employee {
id: string | number;
name: string;
cedula: number;
avatar_url?: string;
telefono?: string;
ubicacion: string;
idciat?: string;
grupo_estudio?: string;
}
const props = defineProps({
employees: {
type: Array,
type: Array as PropType<Employee[]>,
required: true,
default: () => [],
},
});
const handleEdit = (employeeId) => {
emit('edit', employeeId);
const emit = defineEmits(['edit']);
const handleEdit = (employeeId: string | number) => {
emit('edit', employeeId); // Emit event for parent to handle navigation
};
const confirmDeleteEmpleado = (employee) => {
if (confirm(`¿Está seguro de que desea eliminar al empleado "${employee.name}" (ID: ${employee.id})?`)) {
deleteEmpleadoInternal(employee.id);
// handleViewDetails function is removed
const confirmDeleteEmployee = (employee: Employee) => {
if (confirm(`¿Está seguro de que desea eliminar al empleado "${employee.name}" (ID: ${employee.id})? Esta acción no se puede deshacer.`)) {
deleteEmployeeInternal(employee.id);
}
};
const deleteEmpleadoInternal = async (id) => {
const deleteEmployeeInternal = async (employeeId: string | number) => {
try {
await empleadosStore.deleteEmpleado(id);
await empleadosStore.deleteEmpleado(employeeId);
// Optionally: emit success or use a notification system
} catch (error) {
console.error(`Error deleting employee with id ${id}:`, error);
// Optional: Show error notification
console.error(`Error deleting employee with id ${employeeId}:`, error);
alert(`Ocurrió un error al eliminar el empleado: ${error.message || 'Error desconocido'}`);
// Optionally: emit error or use a notification system
}
};
</script>
@@ -117,11 +137,5 @@ const deleteEmpleadoInternal = async (id) => {
object-fit: cover; /* Ensures avatar images are displayed nicely */
}
/* Optional: Keep icon transition if not handled by Tailwind's transition utilities on the button */
button svg {
transition: transform 0.15s ease-in-out;
}
button:hover svg {
transform: scale(1.1); /* Adjusted scale for a subtler effect */
}
/* Icon transition style removed to align with tablaPlanillas.vue */
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div :style="{ backgroundColor: ui.tableBgColorPlanillas }" class="shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="bg-white shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="flex justify-between items-center mb-3 md:mb-4 pb-2 md:pb-3 border-b border-gray-100">
<h4 class="text-lg md:text-xl font-semibold" :style="{ color: 'var(--accent-color-planillas)' }">Planilla ID: {{ planilla.id }}</h4>
<span :class="['px-2 py-1 text-xs font-bold text-white rounded-full', getStatusClass(planilla.estado)]">
@@ -33,9 +33,6 @@
<script setup>
import { defineProps, defineEmits } from 'vue';
import { usePlanillasStore } from '../../stores/usePlanillas';
import { useUi } from '../../stores/useUi.js';
const ui = useUi();
const props = defineProps({
planilla: {

View File

@@ -1,5 +1,5 @@
<template>
<div :style="{ backgroundColor: ui.tableBgColorTareas }" class="shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="bg-white shadow-md rounded-lg p-4 md:p-6 m-2 border border-gray-200 hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col">
<div class="flex justify-between items-center mb-3 md:mb-4 pb-2 md:pb-3 border-b border-gray-100">
<h4 class="text-lg md:text-xl font-semibold" :style="{ color: 'var(--accent-color-tareas)' }">Tarea ID: {{ tarea.id }}</h4>
<span :class="['px-2 py-1 text-xs font-bold text-white rounded-full', getStatusClass(tarea.estado)]">
@@ -36,9 +36,6 @@
<script setup>
import { defineProps, defineEmits } from 'vue';
import { useTareasStore } from '../../stores/useTareas';
import { useUi } from '../../stores/useUi.js';
const ui = useUi();
const props = defineProps({
tarea: {