From d91f23c39e353b69529d4ee51fca5f00d11a2386 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 31 May 2025 07:23:55 +0000 Subject: [PATCH] feat(ui): Standardize card components and update README I've standardized the visual appearance, structure, and functionality of card components across different modules (Asistencias, Empleados, Planillas, Tareas) to ensure a consistent user experience for you. Key changes include: - **Consistent HTML Structure:** All cards now follow a common layout with `card-header`, `card-body`, and `card-actions` sections. - **Tailwind CSS Styling:** I've refactored card styling to use Tailwind CSS utility classes, removing scoped CSS where possible. This promotes maintainability and a unified design language. Tailwind CSS has been configured for the `ui` package. - **CSS Variables for Theming:** I've introduced global CSS variables in `ui/src/style.css` for module-specific accent colors and common UI elements (e.g., warning colors, shadows), allowing for easier theming and consistent color usage. - **Standardized Functionality:** - I've ensured "Edit" and "Delete" buttons have a consistent appearance and behavior. - Delete confirmation dialogs now use a standard message format. - I've removed the "View Details" button from `cardEmpleado.vue` for consistency, as other modules integrate detail viewing within their "Edit" forms. - **README Update:** I've added a comprehensive section to `ui/README.md` documenting the standardized card components, including their structure, styling approach with Tailwind, theming via CSS variables, and basic usage guidelines. These changes improve the visual consistency and maintainability of the UI's card elements. --- ui/README.md | 94 +++++++++ ui/postcss.config.js | 6 + .../components/asistencias/cardAsistencia.vue | 196 ++++++------------ ui/src/components/empleados/cardEmpleado.vue | 139 +++++-------- ui/src/components/planillas/cardPlanilla.vue | 143 +++++-------- ui/src/components/tareas/cardTarea.vue | 162 ++++++--------- ui/src/style.css | 30 +-- ui/tailwind.config.js | 11 + 8 files changed, 353 insertions(+), 428 deletions(-) create mode 100644 ui/postcss.config.js create mode 100644 ui/tailwind.config.js diff --git a/ui/README.md b/ui/README.md index 1511959..779c0be 100644 --- a/ui/README.md +++ b/ui/README.md @@ -3,3 +3,97 @@ This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` +``` +The specific prop name matches the module (e.g., `asistencia` for `cardAsistencia`, `employee` for `cardEmpleado`, `planilla` for `cardPlanilla`, `tarea` for `cardTarea`). diff --git a/ui/postcss.config.js b/ui/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/ui/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/ui/src/components/asistencias/cardAsistencia.vue b/ui/src/components/asistencias/cardAsistencia.vue index b7426a4..5a2c389 100644 --- a/ui/src/components/asistencias/cardAsistencia.vue +++ b/ui/src/components/asistencias/cardAsistencia.vue @@ -1,24 +1,32 @@ @@ -26,6 +34,7 @@ diff --git a/ui/src/components/empleados/cardEmpleado.vue b/ui/src/components/empleados/cardEmpleado.vue index d25fede..1abb0cf 100644 --- a/ui/src/components/empleados/cardEmpleado.vue +++ b/ui/src/components/empleados/cardEmpleado.vue @@ -1,41 +1,45 @@ @@ -44,18 +48,15 @@ import { PropType } from 'vue' import { useRouter } from 'vue-router' -// Define the structure of the employee object based on the Prisma schema interface Employee { - id: string | number // Changed from BigInt to string | number for easier handling in frontend + id: string | number name: string - cedula: number // Changed from BigInt + cedula: number avatar_url?: string telefono?: string ubicacion: string idciat?: string grupo_estudio?: string - // created_at and updated_at are usually not displayed directly in a summary card - // empleado: boolean // This is implicit as it's an employee card } const props = defineProps({ @@ -68,65 +69,29 @@ const props = defineProps({ const router = useRouter() const handleEdit = () => { - // Ensure employee.id is available and correctly typed for URL - router.push(`/empleados/edit/${props.employee.id}`) + // The router pushes to `/empleados/:id` as per current router config, + // which maps to `EmpleadoForm.vue`. This form serves for both editing and viewing details. + router.push(`/empleados/${props.employee.id}`) } -const handleViewDetails = () => { - // This could navigate to a more detailed employee page if one exists - // For now, it can also navigate to an edit page or a specific detail view - // Depending on the application's routing structure, this might be the same as edit or a different view - router.push(`/empleados/view/${props.employee.id}`) // Assuming a dedicated view route exists or will be created -} +// handleViewDetails method removed for consistency + +const buttonHover = (event: MouseEvent, isHovering: boolean) => { + const target = event.target as HTMLElement; + if (isHovering) { + target.style.filter = 'brightness(90%)'; + } else { + target.style.filter = 'brightness(100%)'; + } +}; diff --git a/ui/src/components/planillas/cardPlanilla.vue b/ui/src/components/planillas/cardPlanilla.vue index 40bcda9..92f7fe0 100644 --- a/ui/src/components/planillas/cardPlanilla.vue +++ b/ui/src/components/planillas/cardPlanilla.vue @@ -1,15 +1,31 @@ @@ -36,9 +52,7 @@ const formatDate = (dateString) => { }; const formatCurrency = (value) => { - if (value == null) return 'N/A'; // Handle null or undefined totals - // Assuming the value is a number or can be converted to one. - // Adjust 'es-PY' and currency 'PYG' (Paraguayan Guarani) as needed. + if (value == null) return 'N/A'; return Number(value).toLocaleString('es-PY', { style: 'currency', currency: 'PYG' @@ -50,8 +64,7 @@ const editPlanilla = () => { }; const confirmDeletePlanilla = () => { - // In a real app, you'd use a confirmation dialog here - if (confirm(`¿Está seguro de que desea eliminar la planilla "${props.planilla.titulo}"?`)) { + if (confirm(`¿Está seguro de que desea eliminar la planilla "${props.planilla.titulo}" (ID: ${props.planilla.id})?`)) { deletePlanilla(); } }; @@ -59,85 +72,35 @@ const confirmDeletePlanilla = () => { const deletePlanilla = async () => { try { await planillasStore.deletePlanilla(props.planilla.id); - // Optionally, emit an event or show a notification upon successful deletion - // For example: emit('deleted', props.planilla.id); } catch (error) { console.error('Error deleting planilla:', error); - // Handle error (e.g., show a notification to the user) + alert('Ocurrió un error al eliminar la planilla.'); } }; + +const getStatusClass = (status) => { + if (!status) return 'bg-gray-400'; + const statusNormalized = status.toLowerCase().replace(/\s+/g, '-'); + switch (statusNormalized) { + case 'pagado': return 'bg-green-500'; + case 'pendiente': return 'bg-yellow-500 text-gray-800'; + case 'anulado': return 'bg-red-500'; + case 'borrador': return 'bg-gray-500'; + default: return 'bg-gray-400'; + } +}; + +const buttonHover = (event, isHovering) => { + if (isHovering) { + event.target.style.filter = 'brightness(90%)'; + } else { + event.target.style.filter = 'brightness(100%)'; + } +}; + diff --git a/ui/src/components/tareas/cardTarea.vue b/ui/src/components/tareas/cardTarea.vue index bac3921..3e15c10 100644 --- a/ui/src/components/tareas/cardTarea.vue +++ b/ui/src/components/tareas/cardTarea.vue @@ -1,17 +1,34 @@ @@ -39,7 +56,7 @@ const formatDate = (dateString) => { const formatCurrency = (value) => { if (value == null) return ''; - return Number(value).toLocaleString('es-PY', { // Paraguayan Guarani + return Number(value).toLocaleString('es-PY', { style: 'currency', currency: 'PYG', }); @@ -50,7 +67,7 @@ const editTarea = () => { }; const confirmDeleteTarea = () => { - if (confirm(`¿Está seguro de que desea eliminar la tarea "${props.tarea.titulo}"?`)) { + if (confirm(`¿Está seguro de que desea eliminar la tarea "${props.tarea.titulo}" (ID: ${props.tarea.id})?`)) { deleteTareaInternal(); } }; @@ -58,99 +75,42 @@ const confirmDeleteTarea = () => { const deleteTareaInternal = async () => { try { await tareasStore.deleteTarea(props.tarea.id); - // Optionally, emit a 'deleted' event: emit('deleted', props.tarea.id); - // Or show a success notification. } catch (error) { console.error('Error deleting tarea:', error); alert('Ocurrió un error al eliminar la tarea. Por favor, intente de nuevo.'); - // Potentially show a more user-friendly notification. } }; + +const getStatusClass = (status) => { + if (!status) return 'bg-gray-400'; + const statusNormalized = status.toLowerCase().replace(/\s+/g, '-'); + switch (statusNormalized) { + case 'pendiente': return 'bg-yellow-500 text-gray-800'; + case 'realizada': + case 'completada': + case 'hecho': return 'bg-green-500'; + case 'en-progreso': return 'bg-blue-500'; + case 'anulada': + case 'cancelada': return 'bg-red-500'; + case 'archivada': return 'bg-gray-500'; + default: return 'bg-gray-400'; + } +}; + +const buttonHover = (event, isHovering) => { + if (isHovering) { + event.target.style.filter = 'brightness(90%)'; + } else { + event.target.style.filter = 'brightness(100%)'; + } +}; + diff --git a/ui/src/style.css b/ui/src/style.css index 7e95078..c0b0f78 100644 --- a/ui/src/style.css +++ b/ui/src/style.css @@ -4,20 +4,22 @@ @tailwind utilities; :root { - --primary-color: #1976D2; - --secondary-color: #424242; - --warning-color: #FFC107; - --background-color: #FFFFFF; - --font-family: 'Roboto', sans-serif; - --font-size: 16px; - /* Add other variables as needed, e.g., text colors for themes */ - --text-color: #212121; /* Default text color for light theme */ - - /* Module-specific accent colors - Default Fallbacks */ - --accent-color-empleados: #2196F3; - --accent-color-tareas: #4CAF50; - --accent-color-planillas: #FF9800; - --accent-color-asistencias: #E91E63; + --accent-color-asistencias: #4CAF50; /* Example Green */ + --accent-color-empleados: #2196F3; /* Example Blue */ + --accent-color-planillas: #FF9800; /* Example Orange */ + --accent-color-tareas: #9C27B0; /* Example Purple */ + --warning-color: #dc3545; /* Example Red for delete buttons */ + --background-color: #ffffff; + --text-color: #333333; + --muted-text-color: #555555; + --border-color: #e0e0e0; + --card-shadow: 0 2px 5px rgba(0,0,0,0.05); + --card-hover-shadow: 0 4px 10px rgba(0,0,0,0.1); + /* Retaining existing theme variables that don't conflict */ + --primary-color: #1976D2; /* Kept from original */ + --secondary-color: #424242; /* Kept from original */ + --font-family: 'Roboto', sans-serif; /* Kept from original */ + --font-size: 16px; /* Kept from original */ } html.theme-dark { diff --git a/ui/tailwind.config.js b/ui/tailwind.config.js new file mode 100644 index 0000000..bd3eb14 --- /dev/null +++ b/ui/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{vue,js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +}