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:
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { watchEffect, computed } from 'vue' // Added computed
|
import { watchEffect } from 'vue'
|
||||||
import TopBar from '@/components/ui/TopBar.vue'
|
import TopBar from '@/components/ui/TopBar.vue'
|
||||||
import NavBar from '@/components/ui/NavBar.vue'
|
import NavBar from '@/components/ui/NavBar.vue'
|
||||||
import { useUi } from '@/stores/useUi'
|
import { useUi } from '@/stores/useUi'
|
||||||
@@ -46,15 +46,6 @@ watchEffect(() => {
|
|||||||
root.classList.add('animations-disabled')
|
root.classList.add('animations-disabled')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const transitionDurationStyle = computed(() => {
|
|
||||||
// Assuming base duration of 0.3s for normal speed (transitionSpeed = 1)
|
|
||||||
const baseDuration = 0.3
|
|
||||||
const effectiveDuration = baseDuration * ui.transitionSpeed
|
|
||||||
return {
|
|
||||||
'--current-transition-duration': `${effectiveDuration}s`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -68,17 +59,13 @@ const transitionDurationStyle = computed(() => {
|
|||||||
// The global style.css will handle base background and text color via body styling
|
// The global style.css will handle base background and text color via body styling
|
||||||
// but we can keep specific overrides here if needed or theme classes.
|
// but we can keep specific overrides here if needed or theme classes.
|
||||||
// ui.theme === 'dark' ? 'bg-gray-800 text-gray-100' : 'bg-gray-100 text-gray-900'
|
// ui.theme === 'dark' ? 'bg-gray-800 text-gray-100' : 'bg-gray-100 text-gray-900'
|
||||||
]" :style="transitionDurationStyle">
|
]">
|
||||||
<!-- NavBar fija -->
|
<!-- NavBar fija -->
|
||||||
<NavBar />
|
<NavBar />
|
||||||
|
|
||||||
<!-- contenido principal -->
|
<!-- contenido principal -->
|
||||||
<main class="min-h-[calc(100vh-56px)] flex flex-col overflow-hidden">
|
<main class="min-h-[calc(100vh-56px)] flex flex-col overflow-hidden">
|
||||||
<router-view v-slot="{ Component }">
|
<RouterView class="flex-1 overflow-auto" />
|
||||||
<transition name="slide-fade" mode="out-in">
|
|
||||||
<component :is="Component" class="flex-1 overflow-auto" />
|
|
||||||
</transition>
|
|
||||||
</router-view>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -86,26 +73,4 @@ const transitionDurationStyle = computed(() => {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
/* Scoped styles remain, global styles are in style.css */
|
/* Scoped styles remain, global styles are in style.css */
|
||||||
/* We can add specific App.vue styling here if needed, that doesn't rely on theme variables directly */
|
/* We can add specific App.vue styling here if needed, that doesn't rely on theme variables directly */
|
||||||
|
|
||||||
.slide-fade-enter-active,
|
|
||||||
.slide-fade-leave-active {
|
|
||||||
transition: all var(--current-transition-duration) ease-out; /* Use the CSS variable */
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-fade-enter-from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-20px); /* Slide in from the left */
|
|
||||||
}
|
|
||||||
|
|
||||||
.slide-fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(20px); /* Slide out to the right */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure content is fully opaque and in place when active/not transitioning */
|
|
||||||
.slide-fade-enter-to,
|
|
||||||
.slide-fade-leave-from {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(0);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<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>
|
<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)]">
|
<span :class="['px-2 py-1 text-xs font-bold text-white rounded-full', getStatusClass(asistencia.estado)]">
|
||||||
@@ -34,11 +34,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from 'vue';
|
import { defineProps, defineEmits } from 'vue';
|
||||||
import { useAsistenciasStore } from '../../stores/useAsistencias';
|
import { useAsistenciasStore } from '../../stores/useAsistencias';
|
||||||
import { useUi } from '../../stores/useUi.js';
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const ui = useUi();
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
asistencia: {
|
asistencia: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, nextTick, onMounted, watch } from 'vue'
|
import { ref, nextTick, onMounted, watch } from 'vue'
|
||||||
import { useChat } from '@/stores/useChat'
|
import { useChat } from '@/stores/useChat'
|
||||||
|
import { useUi } from '@/stores/useUi';
|
||||||
|
|
||||||
const chat = useChat()
|
const chat = useChat()
|
||||||
|
const ui = useUi(); // UI store for colors
|
||||||
const msg = ref('')
|
const msg = ref('')
|
||||||
const list = ref(null)
|
const list = ref(null)
|
||||||
|
|
||||||
@@ -40,16 +42,16 @@ watch(() => chat.items.length, scrollBottom)
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- se adapta al contenedor flex, sin superponer la sidebar -->
|
<!-- 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 -->
|
<!-- historial -->
|
||||||
<div ref="list" class="flex-1 min-h-0 overflow-auto p-6 space-y-4 custom-scroll">
|
<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">
|
<template v-for="(m,i) in chat.items" :key="i">
|
||||||
<!-- mensaje de texto -->
|
<!-- mensaje de texto -->
|
||||||
<div :class="m.owner==='yo' ? 'flex justify-end' : 'flex justify-start'" v-if="m.type==='text'">
|
<div :class="m.owner==='yo' ? 'flex justify-end' : 'flex justify-start'" v-if="m.type==='text'">
|
||||||
<div
|
<div
|
||||||
class="max-w-lg px-4 py-2 shadow break-words rounded-lg"
|
class="user-message max-w-lg rounded-lg px-4 py-2 shadow break-words"
|
||||||
:class="m.owner==='yo' ? 'text-white' : 'bg-white text-gray-900 dark:bg-gray-700 dark:text-gray-200'"
|
:class="m.owner === 'yo' ? 'is-user' : 'bg-white text-gray-900'"
|
||||||
:style="m.owner==='yo' ? { backgroundColor: 'var(--accent-color-chat, #0D9488)' } : {}">
|
:style="m.owner === 'yo' ? { backgroundColor: ui.accentColorChat, color: 'white' } : {}">
|
||||||
{{ m.text }}
|
{{ m.text }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,12 +68,12 @@ watch(() => chat.items.length, scrollBottom)
|
|||||||
@keydown="handleKey"
|
@keydown="handleKey"
|
||||||
rows="1"
|
rows="1"
|
||||||
placeholder="Escribí un mensaje… (Enter para enviar, Shift+Enter salto)"
|
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
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="px-4 py-2 rounded-md text-white transition-colors duration-150 ease-in-out shadow-sm hover:brightness-90"
|
class="send-button px-4 py-2 rounded-lg text-white transition"
|
||||||
:style="{ backgroundColor: 'var(--accent-color-chat, #0D9488)' }">
|
:style="{ backgroundColor: ui.accentColorChat }">
|
||||||
➤
|
➤
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -79,25 +81,41 @@ watch(() => chat.items.length, scrollBottom)
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Default accent color for chat if not provided by CSS variable */
|
/* :root definitions for --accent-color-chat-* removed, will use global definitions from style.css */
|
||||||
:root {
|
|
||||||
--accent-color-chat-fallback: #0D9488; /* Teal-700 as a fallback */
|
.user-message.is-user {
|
||||||
--accent-color-chat-alpha-35-fallback: rgba(13, 148, 136, 0.35);
|
/* background-color is now handled by inline style if owner is 'yo' */
|
||||||
--accent-color-chat-alpha-70-fallback: rgba(13, 148, 136, 0.7);
|
/* color: white; is also handled by inline style for user messages */
|
||||||
--accent-color-chat-alpha-60-fallback: rgba(13, 148, 136, 0.6);
|
/* 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 { width: 8px; }
|
||||||
.custom-scroll::-webkit-scrollbar-track { background: transparent; }
|
.custom-scroll::-webkit-scrollbar-track { background: transparent; }
|
||||||
.custom-scroll::-webkit-scrollbar-thumb {
|
.custom-scroll::-webkit-scrollbar-thumb { background-color: rgba(var(--accent-color-chat-rgb),.35); border-radius: 4px; }
|
||||||
background-color: var(--accent-color-chat-alpha-35, var(--accent-color-chat-alpha-35-fallback));
|
.custom-scroll:hover::-webkit-scrollbar-thumb { background-color: rgba(var(--accent-color-chat-rgb),.7); }
|
||||||
border-radius: 4px;
|
.custom-scroll { scrollbar-width: thin; scrollbar-color: rgba(var(--accent-color-chat-rgb),.6) transparent; }
|
||||||
}
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<div class="flex items-center mb-3 md:mb-4 pb-2 md:pb-3 border-b border-gray-100">
|
||||||
<img
|
<img
|
||||||
:src="employee.avatar_url || 'https://via.placeholder.com/150'"
|
:src="employee.avatar_url || 'https://via.placeholder.com/150'"
|
||||||
@@ -39,56 +39,67 @@
|
|||||||
@mouseleave="buttonHover($event, false)"
|
@mouseleave="buttonHover($event, false)"
|
||||||
:class="`focus:ring-[var(--accent-color-empleados)]`"
|
:class="`focus:ring-[var(--accent-color-empleados)]`"
|
||||||
>Editar</button>
|
>Editar</button>
|
||||||
<button
|
<!-- "View Details" button removed for consistency as other modules use the edit view for details -->
|
||||||
@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>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { useUi } from '../../stores/useUi.js';
|
import { PropType } from 'vue'
|
||||||
import { useEmpleadosStore } from '../../stores/useEmpleados'; // Ensure correct path
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useEmpleadosStore } from '@/stores/useEmpleados.js'; // Adjust path as needed
|
||||||
|
|
||||||
const ui = useUi();
|
interface Employee {
|
||||||
const emit = defineEmits(['edit']);
|
id: string | number
|
||||||
const empleadosStore = useEmpleadosStore();
|
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({
|
const props = defineProps({
|
||||||
employee: {
|
employee: {
|
||||||
type: Object,
|
type: Object as PropType<Employee>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const empleadosStore = useEmpleadosStore();
|
||||||
|
|
||||||
const handleEdit = () => {
|
const handleEdit = () => {
|
||||||
emit('edit', props.employee.id);
|
router.push(`/empleados/${props.employee.id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
const confirmDeleteEmpleado = () => {
|
const confirmDeleteEmployee = () => {
|
||||||
if (confirm(`¿Está seguro de que desea eliminar al empleado "${props.employee.name}" (ID: ${props.employee.id})?`)) {
|
if (confirm(`¿Está seguro de que desea eliminar al empleado "${props.employee.name}" (ID: ${props.employee.id})? Esta acción no se puede deshacer.`)) {
|
||||||
deleteEmpleado();
|
deleteEmployee();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteEmpleado = async () => {
|
const deleteEmployee = async () => {
|
||||||
try {
|
try {
|
||||||
await empleadosStore.deleteEmpleado(props.employee.id);
|
await empleadosStore.deleteEmpleado(props.employee.id);
|
||||||
// Optionally, show a success notification or emit an event if needed,
|
// Optionally, you might want to emit an event or show a success notification
|
||||||
// though typically the list will update reactively from the store.
|
// For example: emit('deleted', props.employee.id);
|
||||||
|
// Or use a toast notification system if you have one integrated.
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting employee:', 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 buttonHover = (event: MouseEvent, isHovering: boolean) => {
|
||||||
const target = event.target;
|
const target = event.target as HTMLElement;
|
||||||
if (isHovering) {
|
if (isHovering) {
|
||||||
target.style.filter = 'brightness(90%)';
|
target.style.filter = 'brightness(90%)'; // Darken slightly
|
||||||
} else {
|
} else {
|
||||||
target.style.filter = 'brightness(100%)';
|
target.style.filter = 'brightness(100%)'; // Back to normal
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -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">
|
<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>
|
<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>
|
||||||
<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">
|
<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="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>
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -74,38 +74,58 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</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 { useUi } from '../../stores/useUi.js';
|
||||||
import { useEmpleadosStore } from '../../stores/useEmpleados';
|
import { useEmpleadosStore } from '../../stores/useEmpleados.js'; // For delete functionality
|
||||||
|
|
||||||
const ui = useUi();
|
const ui = useUi();
|
||||||
const emit = defineEmits(['edit']);
|
|
||||||
const empleadosStore = useEmpleadosStore();
|
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({
|
const props = defineProps({
|
||||||
employees: {
|
employees: {
|
||||||
type: Array,
|
type: Array as PropType<Employee[]>,
|
||||||
required: true,
|
required: true,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleEdit = (employeeId) => {
|
const emit = defineEmits(['edit']);
|
||||||
emit('edit', employeeId);
|
|
||||||
|
const handleEdit = (employeeId: string | number) => {
|
||||||
|
emit('edit', employeeId); // Emit event for parent to handle navigation
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmDeleteEmpleado = (employee) => {
|
// handleViewDetails function is removed
|
||||||
if (confirm(`¿Está seguro de que desea eliminar al empleado "${employee.name}" (ID: ${employee.id})?`)) {
|
|
||||||
deleteEmpleadoInternal(employee.id);
|
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 {
|
try {
|
||||||
await empleadosStore.deleteEmpleado(id);
|
await empleadosStore.deleteEmpleado(employeeId);
|
||||||
|
// Optionally: emit success or use a notification system
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error deleting employee with id ${id}:`, error);
|
console.error(`Error deleting employee with id ${employeeId}:`, error);
|
||||||
// Optional: Show error notification
|
alert(`Ocurrió un error al eliminar el empleado: ${error.message || 'Error desconocido'}`);
|
||||||
|
// Optionally: emit error or use a notification system
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -117,11 +137,5 @@ const deleteEmpleadoInternal = async (id) => {
|
|||||||
object-fit: cover; /* Ensures avatar images are displayed nicely */
|
object-fit: cover; /* Ensures avatar images are displayed nicely */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optional: Keep icon transition if not handled by Tailwind's transition utilities on the button */
|
/* Icon transition style removed to align with tablaPlanillas.vue */
|
||||||
button svg {
|
|
||||||
transition: transform 0.15s ease-in-out;
|
|
||||||
}
|
|
||||||
button:hover svg {
|
|
||||||
transform: scale(1.1); /* Adjusted scale for a subtler effect */
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<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>
|
<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)]">
|
<span :class="['px-2 py-1 text-xs font-bold text-white rounded-full', getStatusClass(planilla.estado)]">
|
||||||
@@ -33,9 +33,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from 'vue';
|
import { defineProps, defineEmits } from 'vue';
|
||||||
import { usePlanillasStore } from '../../stores/usePlanillas';
|
import { usePlanillasStore } from '../../stores/usePlanillas';
|
||||||
import { useUi } from '../../stores/useUi.js';
|
|
||||||
|
|
||||||
const ui = useUi();
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
planilla: {
|
planilla: {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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">
|
<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>
|
<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)]">
|
<span :class="['px-2 py-1 text-xs font-bold text-white rounded-full', getStatusClass(tarea.estado)]">
|
||||||
@@ -36,9 +36,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from 'vue';
|
import { defineProps, defineEmits } from 'vue';
|
||||||
import { useTareasStore } from '../../stores/useTareas';
|
import { useTareasStore } from '../../stores/useTareas';
|
||||||
import { useUi } from '../../stores/useUi.js';
|
|
||||||
|
|
||||||
const ui = useUi();
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tarea: {
|
tarea: {
|
||||||
|
|||||||
@@ -17,13 +17,20 @@ const appearanceSettingKeys = [
|
|||||||
'accentColorPlanillas',
|
'accentColorPlanillas',
|
||||||
'accentColorAsistencias',
|
'accentColorAsistencias',
|
||||||
'accentColorConfiguracion',
|
'accentColorConfiguracion',
|
||||||
'accentColorChat',
|
'accentColorChat', // Added
|
||||||
// Per-module table background colors
|
// Per-module table background colors
|
||||||
'tableBgColorEmpleados',
|
'tableBgColorEmpleados',
|
||||||
'tableBgColorTareas',
|
'tableBgColorTareas',
|
||||||
'tableBgColorPlanillas',
|
'tableBgColorPlanillas',
|
||||||
'tableBgColorAsistencias',
|
'tableBgColorAsistencias',
|
||||||
'tableBgColorConfiguracion',
|
'tableBgColorConfiguracion',
|
||||||
|
'bgColorChat', // Added
|
||||||
|
'desktopNavbarPersistent',
|
||||||
|
// Default module views
|
||||||
|
'tableBgColorTareas',
|
||||||
|
'tableBgColorPlanillas',
|
||||||
|
'tableBgColorAsistencias',
|
||||||
|
'tableBgColorConfiguracion',
|
||||||
'desktopNavbarPersistent',
|
'desktopNavbarPersistent',
|
||||||
// Default module views
|
// Default module views
|
||||||
'defaultViewEmpleados',
|
'defaultViewEmpleados',
|
||||||
@@ -31,7 +38,6 @@ const appearanceSettingKeys = [
|
|||||||
'defaultViewPlanillas',
|
'defaultViewPlanillas',
|
||||||
'defaultViewAsistencias',
|
'defaultViewAsistencias',
|
||||||
'defaultViewConfiguracion',
|
'defaultViewConfiguracion',
|
||||||
'transitionSpeed',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const loadSettingsFromLocalStorage = () => {
|
const loadSettingsFromLocalStorage = () => {
|
||||||
@@ -93,21 +99,21 @@ export const useUi = defineStore('ui', {
|
|||||||
accentColorPlanillas: '#FF9800', // Orange
|
accentColorPlanillas: '#FF9800', // Orange
|
||||||
accentColorAsistencias: '#E91E63', // Pink
|
accentColorAsistencias: '#E91E63', // Pink
|
||||||
accentColorConfiguracion: '#607D8B', // Blue Grey
|
accentColorConfiguracion: '#607D8B', // Blue Grey
|
||||||
accentColorChat: '#0D9488', // Teal - chosen as a default for chat
|
accentColorChat: 'rgb(13, 148, 136)', // Added - Default Teal
|
||||||
// Per-module table background colors - default to white
|
// Per-module table background colors - default to white
|
||||||
tableBgColorEmpleados: '#FFFFFF',
|
tableBgColorEmpleados: '#FFFFFF',
|
||||||
tableBgColorTareas: '#FFFFFF',
|
tableBgColorTareas: '#FFFFFF',
|
||||||
tableBgColorPlanillas: '#FFFFFF',
|
tableBgColorPlanillas: '#FFFFFF',
|
||||||
tableBgColorAsistencias: '#FFFFFF',
|
tableBgColorAsistencias: '#FFFFFF',
|
||||||
tableBgColorConfiguracion: '#FFFFFF',
|
tableBgColorConfiguracion: '#FFFFFF',
|
||||||
|
bgColorChat: 'rgb(249, 250, 251)', // Added - Default Light Gray (gray-50)
|
||||||
desktopNavbarPersistent: false,
|
desktopNavbarPersistent: false,
|
||||||
// Default module views
|
// Default module views
|
||||||
'defaultViewEmpleados': 'table',
|
'defaultViewEmpleados': 'card', // Changed from 'table' to 'card'
|
||||||
'defaultViewTareas': 'table',
|
'defaultViewTareas': 'table',
|
||||||
'defaultViewPlanillas': 'table',
|
'defaultViewPlanillas': 'table', // Already present
|
||||||
'defaultViewAsistencias': 'table',
|
'defaultViewAsistencias': 'table',
|
||||||
'defaultViewConfiguracion': 'table',
|
'defaultViewConfiguracion': 'table',
|
||||||
transitionSpeed: 1, // Default to normal speed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadedSettings = loadSettingsFromLocalStorage()
|
const loadedSettings = loadSettingsFromLocalStorage()
|
||||||
@@ -194,10 +200,6 @@ export const useUi = defineStore('ui', {
|
|||||||
this.accentColorConfiguracion = color
|
this.accentColorConfiguracion = color
|
||||||
_saveAppearanceState(this)
|
_saveAppearanceState(this)
|
||||||
},
|
},
|
||||||
setAccentColorChat(color) {
|
|
||||||
this.accentColorChat = color;
|
|
||||||
_saveAppearanceState(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Actions for per-module table background colors
|
// Actions for per-module table background colors
|
||||||
setTableBgColorEmpleados(color) {
|
setTableBgColorEmpleados(color) {
|
||||||
@@ -245,13 +247,7 @@ export const useUi = defineStore('ui', {
|
|||||||
setDefaultViewConfiguracion(view) {
|
setDefaultViewConfiguracion(view) {
|
||||||
this.defaultViewConfiguracion = view
|
this.defaultViewConfiguracion = view
|
||||||
_saveAppearanceState(this)
|
_saveAppearanceState(this)
|
||||||
},
|
}
|
||||||
|
|
||||||
// Action for transition speed
|
|
||||||
setTransitionSpeed(newSpeed) {
|
|
||||||
this.transitionSpeed = Number(newSpeed) // Ensure it's a number
|
|
||||||
_saveAppearanceState(this)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -24,10 +24,25 @@
|
|||||||
--card-hover-shadow: 0 4px 10px rgba(0,0,0,0.10);
|
--card-hover-shadow: 0 4px 10px rgba(0,0,0,0.10);
|
||||||
|
|
||||||
/* Colores de módulo */
|
/* Colores de módulo */
|
||||||
--accent-color-asistencias: #4CAF50;
|
/* Empleados - Blue 500 (Tailwind) */
|
||||||
--accent-color-empleados: #2196F3;
|
--accent-color-empleados-rgb: 59, 130, 246;
|
||||||
--accent-color-planillas: #FF9800;
|
--accent-color-empleados: rgb(var(--accent-color-empleados-rgb));
|
||||||
--accent-color-tareas: #9C27B0;
|
|
||||||
|
/* Chat - Teal 600 (Tailwind) */
|
||||||
|
--accent-color-chat-rgb: 13, 148, 136;
|
||||||
|
--accent-color-chat: rgb(var(--accent-color-chat-rgb));
|
||||||
|
|
||||||
|
/* Planillas - Emerald 500 (Tailwind) */
|
||||||
|
--accent-color-planillas-rgb: 16, 185, 129;
|
||||||
|
--accent-color-planillas: rgb(var(--accent-color-planillas-rgb));
|
||||||
|
|
||||||
|
/* Asistencias - Green 500 (#4CAF50) */
|
||||||
|
--accent-color-asistencias-rgb: 76, 175, 80;
|
||||||
|
--accent-color-asistencias: rgb(var(--accent-color-asistencias-rgb));
|
||||||
|
|
||||||
|
/* Tareas - Purple 500 (#9C27B0) */
|
||||||
|
--accent-color-tareas-rgb: 156, 39, 176;
|
||||||
|
--accent-color-tareas: rgb(var(--accent-color-tareas-rgb));
|
||||||
|
|
||||||
/* Fondos de tabla (light) */
|
/* Fondos de tabla (light) */
|
||||||
--table-container-bg-color-asistencias: #fdebee;
|
--table-container-bg-color-asistencias: #fdebee;
|
||||||
|
|||||||
@@ -4,34 +4,11 @@ import CanvasChat from '@/components/chat/CanvasChat.vue'
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="chat-view-container flex flex-col h-full">
|
<div class="h-full flex flex-col">
|
||||||
<header class="page-header">
|
<CanvasChat class="flex-1" />
|
||||||
<h1>Chat</h1>
|
|
||||||
</header>
|
|
||||||
<CanvasChat class="flex-1 min-h-0" /> <!-- Added min-h-0 -->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.chat-view-container {
|
/* nada por ahora */
|
||||||
/* Ensures the container itself takes full height if not already globally managed */
|
|
||||||
/* No padding or max-width needed here to allow CanvasChat to control its layout */
|
|
||||||
}
|
|
||||||
|
|
||||||
.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, var(--default-accent-color, #3498db)); /* Fallback color strategy */
|
|
||||||
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>
|
</style>
|
||||||
|
|||||||
@@ -26,26 +26,6 @@
|
|||||||
<input type="checkbox" id="desktopNavbarPersistent" v-model="ui.desktopNavbarPersistent" @change="ui.setDesktopNavbarPersistent($event.target.checked)"
|
<input type="checkbox" id="desktopNavbarPersistent" v-model="ui.desktopNavbarPersistent" @change="ui.setDesktopNavbarPersistent($event.target.checked)"
|
||||||
class="custom-checkbox relative w-10 h-5 appearance-none bg-gray-300 dark:bg-gray-600 rounded-full cursor-pointer transition-colors duration-300 ease-in-out checked:bg-[var(--primary-color)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--primary-color)] focus:ring-offset-[var(--background-color)]">
|
class="custom-checkbox relative w-10 h-5 appearance-none bg-gray-300 dark:bg-gray-600 rounded-full cursor-pointer transition-colors duration-300 ease-in-out checked:bg-[var(--primary-color)] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--primary-color)] focus:ring-offset-[var(--background-color)]">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Animation Speed Setting -->
|
|
||||||
<div class="setting-item mt-6 md:col-span-2"> <!-- md:col-span-2 to make it take full width on medium screens if the grid has 2 columns -->
|
|
||||||
<label class="block text-sm font-medium mb-1 text-[var(--text-color)]">Animation Speed</label>
|
|
||||||
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">
|
|
||||||
Adjust the speed of screen transitions (applied if animations are enabled).
|
|
||||||
</p>
|
|
||||||
<div class="flex flex-col space-y-1 sm:flex-row sm:space-y-0 sm:space-x-4">
|
|
||||||
<label v-for="option in speedOptions" :key="option.value"
|
|
||||||
class="flex items-center p-2 rounded-md hover:bg-gray-200/50 dark:hover:bg-gray-700/50 cursor-pointer transition-colors duration-150 ease-in-out border border-gray-300 dark:border-gray-600 hover:border-[var(--primary-color)]">
|
|
||||||
<input type="radio"
|
|
||||||
name="transitionSpeed"
|
|
||||||
:value="option.value"
|
|
||||||
:checked="ui.transitionSpeed === option.value"
|
|
||||||
@change="ui.setTransitionSpeed(option.value)"
|
|
||||||
class="form-radio h-4 w-4 text-[var(--primary-color)] focus:ring-1 focus:ring-[var(--primary-color)] border-gray-300 dark:border-gray-500 bg-white dark:bg-gray-800 focus:ring-offset-white dark:focus:ring-offset-gray-900">
|
|
||||||
<span class="ml-2 text-sm text-[var(--text-color)]">{{ option.label }} <span class="text-xs text-gray-500 dark:text-gray-400">({{ option.value }}x)</span></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -231,12 +211,6 @@ onMounted(() => {
|
|||||||
isMounted.value = true
|
isMounted.value = true
|
||||||
}, 50)
|
}, 50)
|
||||||
})
|
})
|
||||||
|
|
||||||
const speedOptions = [
|
|
||||||
{ label: 'Slow', value: 2 },
|
|
||||||
{ label: 'Normal', value: 1 },
|
|
||||||
{ label: 'Fast', value: 0.5 },
|
|
||||||
]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="asistencia-form-container" :style="{ backgroundColor: uiStore.tableBgColorAsistencias }">
|
<div class="asistencia-form-container">
|
||||||
<h2>{{ formTitle }}</h2>
|
<h2>{{ formTitle }}</h2>
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -48,7 +48,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue';
|
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||||
import { useAsistenciasStore } from '../../stores/useAsistencias';
|
import { useAsistenciasStore } from '../../stores/useAsistencias';
|
||||||
import { useUi } from '@/stores/useUi'; // Corrected UI store import
|
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -58,7 +57,6 @@ const props = defineProps({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const asistenciasStore = useAsistenciasStore();
|
const asistenciasStore = useAsistenciasStore();
|
||||||
const uiStore = useUi(); // Corrected UI store instantiation
|
|
||||||
|
|
||||||
const formatDateTimeForInput = (dateString) => {
|
const formatDateTimeForInput = (dateString) => {
|
||||||
const date = dateString ? new Date(dateString) : new Date();
|
const date = dateString ? new Date(dateString) : new Date();
|
||||||
@@ -209,7 +207,7 @@ const handleCancel = () => {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
/* background-color: #f9f9f9; */ /* Removed to use dynamic background */
|
background-color: #f9f9f9;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-6 bg-gray-100 min-h-screen">
|
<div class="empleado-form-page-container min-h-screen">
|
||||||
<h1 class="text-3xl font-bold mb-8 text-center text-gray-700">
|
<h1 class="text-3xl font-bold mb-8 text-center text-gray-700">
|
||||||
{{ isEditMode ? 'Editar Empleado' : 'Crear Empleado' }}
|
{{ isEditMode ? 'Editar Empleado' : 'Crear Empleado' }}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
@submit.prevent="handleSubmit"
|
@submit.prevent="handleSubmit"
|
||||||
class="max-w-lg mx-auto p-8 rounded-lg shadow-lg"
|
class="max-w-lg mx-auto bg-white p-8 rounded-lg shadow-lg"
|
||||||
:style="{ backgroundColor: uiStore.tableBgColorEmpleados }"
|
|
||||||
>
|
>
|
||||||
<!-- ───────── Nombre ───────── -->
|
<!-- ───────── Nombre ───────── -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
@@ -150,7 +149,6 @@ import { ref, onMounted, computed } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { useEmpleadosStore } from '@/stores/useEmpleados.js'
|
import { useEmpleadosStore } from '@/stores/useEmpleados.js'
|
||||||
import { useUi } from '@/stores/useUi'; // Corrected UI store import
|
|
||||||
|
|
||||||
/* ───── Tipos ───── */
|
/* ───── Tipos ───── */
|
||||||
interface EmpleadoForm {
|
interface EmpleadoForm {
|
||||||
@@ -181,7 +179,6 @@ const router = useRouter()
|
|||||||
/* ───── Store ───── */
|
/* ───── Store ───── */
|
||||||
const empleadosStore = useEmpleadosStore()
|
const empleadosStore = useEmpleadosStore()
|
||||||
const { currentEmpleado } = storeToRefs(empleadosStore)
|
const { currentEmpleado } = storeToRefs(empleadosStore)
|
||||||
const uiStore = useUi(); // Corrected UI store instantiation
|
|
||||||
|
|
||||||
/* ───── State ───── */
|
/* ───── State ───── */
|
||||||
const form = ref<EmpleadoForm>(defaultForm())
|
const form = ref<EmpleadoForm>(defaultForm())
|
||||||
@@ -234,6 +231,13 @@ const handleCancel = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.empleado-form-page-container {
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1200px; /* Or adjust if forms should be narrower centrally */
|
||||||
|
margin: 0 auto;
|
||||||
|
font-family: Arial, sans-serif; /* Consistent font */
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Validación rápida de inputs requeridos --- */
|
/* --- Validación rápida de inputs requeridos --- */
|
||||||
input:required:invalid {
|
input:required:invalid {
|
||||||
border-color: var(--warning-color); /* Using warning color for invalid fields */
|
border-color: var(--warning-color); /* Using warning color for invalid fields */
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="empleados-index-container">
|
<div class="empleados-index-container">
|
||||||
<!-- … encabezado … -->
|
|
||||||
<header class="page-header">
|
<header class="page-header">
|
||||||
<h1>Gestión de Empleados</h1>
|
<h1>Gestión de Empleados</h1>
|
||||||
<button @click="goToCreateEmployee" class="btn-create">
|
<button @click="goToCreateEmployee" class="btn-create">
|
||||||
|
<!-- ícono ➕ -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline-block mr-2" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
Crear Empleado
|
Crear Empleado
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
@@ -30,11 +33,12 @@
|
|||||||
|
|
||||||
<!-- contenido -->
|
<!-- contenido -->
|
||||||
<div>
|
<div>
|
||||||
<div v-if="loading" class="loading-message">Cargando empleados...</div>
|
<div v-if="loading" class="loading-message">
|
||||||
|
Cargando empleados...
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else-if="error" class="error-message-full">
|
<div v-else-if="error" class="error-message-full">
|
||||||
<p>Error al cargar los empleados. Por favor, intente de nuevo más tarde.</p>
|
<p>Error al cargar los empleados: {{ error }}</p>
|
||||||
<p v-if="error">Detalle: {{ error }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
@@ -48,15 +52,15 @@
|
|||||||
:key="employee.id"
|
:key="employee.id"
|
||||||
:employee="employee"
|
:employee="employee"
|
||||||
/>
|
/>
|
||||||
</div>
|
<div v-if="employees.length === 0" class="no-data-message">
|
||||||
<div v-if="currentView === 'card' && employees.length === 0 && !loading" class="no-data-message">
|
No hay empleados para mostrar en la vista de tarjetas.
|
||||||
No hay empleados para mostrar en la vista de tarjetas.
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- vista de tabla -->
|
<!-- vista de tabla -->
|
||||||
<div v-if="currentView === 'table'">
|
<div v-if="currentView === 'table'">
|
||||||
<TablaEmpleados :employees="employees" />
|
<TablaEmpleados :employees="employees" />
|
||||||
<div v-if="employees.length === 0 && !loading" class="no-data-message">
|
<div v-if="employees.length === 0" class="no-data-message">
|
||||||
No hay empleados para mostrar en la vista de tabla.
|
No hay empleados para mostrar en la vista de tabla.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,7 +98,8 @@ const employees = empleados;
|
|||||||
const btnViewClass = (viewType: 'card' | 'table') => {
|
const btnViewClass = (viewType: 'card' | 'table') => {
|
||||||
const base = 'p-2 rounded-md transition-colors duration-150 ease-in-out';
|
const base = 'p-2 rounded-md transition-colors duration-150 ease-in-out';
|
||||||
if (currentView.value === viewType) {
|
if (currentView.value === viewType) {
|
||||||
return `${base} bg-[var(--accent-color-empleados)] text-white shadow-lg`;
|
// Apply the dedicated class for active state which includes focus styles, and add shadow-lg for general active appearance
|
||||||
|
return `${base} view-toggle-active shadow-lg`;
|
||||||
}
|
}
|
||||||
return `${base} bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600`;
|
return `${base} bg-gray-200 text-gray-700 hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600`;
|
||||||
};
|
};
|
||||||
@@ -125,7 +130,6 @@ const goToCreateEmployee = () => router.push({ name: 'empleados-new' });
|
|||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
min-height: calc(100vh - var(--navbar-height, 0px)); /* Assuming --navbar-height is defined elsewhere or adjust */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-header {
|
.page-header {
|
||||||
@@ -157,14 +161,17 @@ const goToCreateEmployee = () => router.push({ name: 'empleados-new' });
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-create:hover {
|
.btn-create:hover {
|
||||||
filter: brightness(0.9);
|
filter: brightness(0.9); /* Darken, similar to PlanillasIndex */
|
||||||
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-create:focus {
|
.btn-create:focus {
|
||||||
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-empleados);
|
outline: none; /* Remove default outline */
|
||||||
|
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-empleados); /* Replicate Planillas focus */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Styles for loading, error, no-data messages, adapted from PlanillasIndex */
|
||||||
.loading-message,
|
.loading-message,
|
||||||
.error-message-full,
|
.error-message-full,
|
||||||
.no-data-message {
|
.no-data-message {
|
||||||
@@ -177,6 +184,7 @@ const goToCreateEmployee = () => router.push({ name: 'empleados-new' });
|
|||||||
|
|
||||||
.loading-message {
|
.loading-message {
|
||||||
color: #7f8c8d; /* Gray */
|
color: #7f8c8d; /* Gray */
|
||||||
|
/* Consider adding a subtle background if needed, e.g., background-color: #f8f9f9; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-message-full {
|
.error-message-full {
|
||||||
@@ -185,17 +193,24 @@ const goToCreateEmployee = () => router.push({ name: 'empleados-new' });
|
|||||||
border: 1px solid #f5b7b1; /* Light red border */
|
border: 1px solid #f5b7b1; /* Light red border */
|
||||||
}
|
}
|
||||||
.error-message-full p {
|
.error-message-full p {
|
||||||
margin: 5px 0;
|
margin: 5px 0; /* Consistent with Planillas */
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-data-message {
|
.no-data-message {
|
||||||
background-color: #eafaf1; /* Lighter green/blue */
|
background-color: #eafaf1; /* Lighter green/blue */
|
||||||
color: #2ecc71; /* Green */
|
color: #2ecc71; /* Green */
|
||||||
border: 1px solid #a3e4d7; /* Light green/blue border */
|
border: 1px solid #a3e4d7; /* Light green/blue border */
|
||||||
|
/* If using accent color for these messages:
|
||||||
|
background-color: var(--accent-color-empleados-light);
|
||||||
|
color: var(--accent-color-empleados-dark);
|
||||||
|
border: 1px solid var(--accent-color-empleados);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-toggle-active {
|
|
||||||
|
.view-toggle-active { /* Class for active view toggle button */
|
||||||
background-color: var(--accent-color-empleados);
|
background-color: var(--accent-color-empleados);
|
||||||
|
color: white; /* Ensure text is white for contrast */
|
||||||
/* For focus, assuming white text on accent. Adjust if needed. */
|
/* For focus, assuming white text on accent. Adjust if needed. */
|
||||||
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-empleados);
|
box-shadow: 0 0 0 2px var(--background-color, #fff), 0 0 0 4px var(--accent-color-empleados);
|
||||||
}
|
}
|
||||||
@@ -203,4 +218,7 @@ const goToCreateEmployee = () => router.push({ name: 'empleados-new' });
|
|||||||
|
|
||||||
.view-enter-active, .view-leave-active { transition: opacity .3s ease; }
|
.view-enter-active, .view-leave-active { transition: opacity .3s ease; }
|
||||||
.view-enter-from, .view-leave-to { opacity: 0; }
|
.view-enter-from, .view-leave-to { opacity: 0; }
|
||||||
|
|
||||||
|
/* Remove min-h-screen if .empleados-index-container handles height/layout sufficiently */
|
||||||
|
/* .min-h-screen { min-height: calc(100vh - var(--navbar-height, 0px)); } */
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="planilla-form-container" :style="{ backgroundColor: uiStore.tableBgColorPlanillas }">
|
<div class="planilla-form-container">
|
||||||
<h2>{{ formTitle }}</h2>
|
<h2>{{ formTitle }}</h2>
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -53,7 +53,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, computed, watch } from 'vue';
|
import { ref, reactive, onMounted, computed, watch } from 'vue';
|
||||||
import { usePlanillasStore } from '../../stores/usePlanillas';
|
import { usePlanillasStore } from '../../stores/usePlanillas';
|
||||||
import { useUi } from '@/stores/useUi'; // Corrected UI store import
|
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -64,7 +63,6 @@ const router = useRouter();
|
|||||||
const route = useRoute(); // Can also get id from route.params.id
|
const route = useRoute(); // Can also get id from route.params.id
|
||||||
|
|
||||||
const planillasStore = usePlanillasStore();
|
const planillasStore = usePlanillasStore();
|
||||||
const uiStore = useUi(); // Corrected UI store instantiation
|
|
||||||
|
|
||||||
const formData = reactive({
|
const formData = reactive({
|
||||||
titulo: '',
|
titulo: '',
|
||||||
@@ -217,7 +215,7 @@ const handleCancel = () => {
|
|||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
/* background-color: #f9f9f9; */ /* Removed to use dynamic background */
|
background-color: #f9f9f9;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tarea-form-container" :style="{ backgroundColor: uiStore.tableBgColorTareas }">
|
<div class="tarea-form-container">
|
||||||
<h2>{{ formTitle }}</h2>
|
<h2>{{ formTitle }}</h2>
|
||||||
<form @submit.prevent="handleSubmit">
|
<form @submit.prevent="handleSubmit">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -65,7 +65,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue';
|
import { ref, reactive, onMounted, onUnmounted, computed, watch } from 'vue';
|
||||||
import { useTareasStore } from '../../stores/useTareas';
|
import { useTareasStore } from '../../stores/useTareas';
|
||||||
import { useUi } from '@/stores/useUi'; // Corrected UI store import
|
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -75,7 +74,6 @@ const props = defineProps({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const tareasStore = useTareasStore();
|
const tareasStore = useTareasStore();
|
||||||
const uiStore = useUi(); // Corrected UI store instantiation
|
|
||||||
|
|
||||||
const formatDateForInput = (dateString) => {
|
const formatDateForInput = (dateString) => {
|
||||||
const date = dateString ? new Date(dateString) : new Date();
|
const date = dateString ? new Date(dateString) : new Date();
|
||||||
@@ -229,7 +227,7 @@ const handleCancel = () => {
|
|||||||
max-width: 650px;
|
max-width: 650px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
/* background-color: #f9f9f9; */ /* Removed to use dynamic background */
|
background-color: #f9f9f9;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user