diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 9adc0b8..c5de508 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,13 +4,17 @@
-
+
-
-
-
+
-
+
+
+
+
+
+
+
@@ -132,7 +136,7 @@
1774643312599
-
+
@@ -286,7 +290,15 @@
1774715983233
-
+
+
+ 1774716452045
+
+
+
+ 1774716452045
+
+
@@ -314,6 +326,7 @@
-
+
+
\ No newline at end of file
diff --git a/apps/web/src/lib/components/BookingModal.svelte b/apps/web/src/lib/components/BookingModal.svelte
index 32c6ff9..5c9542f 100644
--- a/apps/web/src/lib/components/BookingModal.svelte
+++ b/apps/web/src/lib/components/BookingModal.svelte
@@ -17,7 +17,8 @@
let loadingReservations = false;
$: dateLocale = $locale === 'es-ES' ? es : enUS;
- $: formattedDate = format(selectedDate, 'PPP', { locale: dateLocale });
+ $: dateFormat = $locale === 'es-ES' ? 'dd/MM/yyyy' : 'PPP';
+ $: formattedDate = format(selectedDate, dateFormat, { locale: dateLocale });
$: {
if (selectedDate) {
@@ -82,7 +83,7 @@
end.setHours(eh, em, 0, 0);
if (end <= start) {
- error = "End time must be after start time";
+ error = $t('booking.errorTime');
loading = false;
return;
}
@@ -100,7 +101,7 @@
console.log('Booking created successfully');
dispatch('close');
} catch (e: any) {
- error = e.message || "Failed to create booking";
+ error = e.message || $t('booking.errorCreating');
} finally {
loading = false;
}
@@ -121,7 +122,7 @@
});
dispatch('close');
} catch (e: any) {
- error = e.message || "Failed to delete booking";
+ error = e.message || $t('booking.errorDeleting');
confirmDelete = false;
} finally {
deleting = false;
@@ -252,19 +253,25 @@
{$t('booking.bookingDate')}
-
+
+
+ {format(new Date(bookingDate + 'T00:00:00'), dateFormat, { locale: dateLocale })}
+
+
+
+
{#if space.type === 'MEETING_ROOM'}
{#if loadingReservations}
@@ -327,7 +334,7 @@
{:else}
-
Desks are booked for the full day.
+
{$t('booking.deskNote')}
{/if}
@@ -338,9 +345,9 @@
>
{#if loading}
- Booking...
+ {$t('booking.booking')}
{:else}
- Confirm Booking
+ {$t('booking.confirm')}
{/if}
diff --git a/apps/web/src/lib/components/FloorplanViewer.svelte b/apps/web/src/lib/components/FloorplanViewer.svelte
index 0099b5e..1ec49bf 100644
--- a/apps/web/src/lib/components/FloorplanViewer.svelte
+++ b/apps/web/src/lib/components/FloorplanViewer.svelte
@@ -5,6 +5,7 @@
import BookingModal from './BookingModal.svelte';
import Avatar from './Avatar.svelte';
import { resolveAssetUrl } from '$lib/utils/url';
+ import { t } from 'svelte-i18n';
export let floorId: string;
export let spaces: any[] = [];
@@ -151,7 +152,7 @@
on:click={() => handleSpaceClick(space)}
class="absolute p-2 border-2 rounded-lg shadow-md transition-all overflow-hidden flex items-center justify-center {space.type !== 'AMENITY' ? 'hover:scale-105 cursor-pointer' : 'cursor-default opacity-80'} {status.isOccupied ? 'bg-yellow-50 border-yellow-500' : 'bg-green-50 border-green-500'}"
style="left: 0; top: 0; width: {(space.width / 100) * containerWidth}px; height: {(space.height / 100) * containerHeight}px; transform: translate({(space.x / 100) * containerWidth}px, {(space.y / 100) * containerHeight}px)"
- title="{space.name} ({status.isOccupied ? 'Booked' : 'Available'})"
+ title="{space.name} ({status.isOccupied ? $t('booking.currentlyBooked') : $t('booking.available')})"
>
{role} in your organization.",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "done": "Done",
+ "error": "Error generating invite: {error}"
+ },
+ "table": {
+ "user": "User",
+ "role": "Role",
+ "joined": "Joined",
+ "actions": "Actions",
+ "id": "ID"
+ },
+ "roles": {
+ "EMPLOYEE": "Employee",
+ "SUPERVISOR": "Supervisor",
+ "ADMIN": "Admin"
+ },
+ "password": {
+ "change": "Change Password",
+ "title": "Change Password",
+ "description": "Set a new password for {email}",
+ "new": "New Password",
+ "placeholder": "At least 6 characters",
+ "save": "Save Password",
+ "success": "Password updated successfully",
+ "error": "Error updating password: {error}"
+ },
+ "updateRoleError": "Error updating role: {error}",
+ "loading": "Loading users..."
}
}
\ No newline at end of file
diff --git a/apps/web/src/lib/i18n/locales/es-ES.json b/apps/web/src/lib/i18n/locales/es-ES.json
index 15c2a13..ae16fb2 100644
--- a/apps/web/src/lib/i18n/locales/es-ES.json
+++ b/apps/web/src/lib/i18n/locales/es-ES.json
@@ -27,9 +27,31 @@
"title": "Panel de control",
"welcome": "Bienvenido/a de nuevo",
"selectBuilding": "Seleccione un edificio para ver las plantas disponibles",
- "noBuildings": "No se han encontrado edificios"
+ "noBuildings": "No se han encontrado edificios",
+ "noBuildingsDesc": "Su organización aún no tiene edificios asignados.",
+ "addBuilding": "Añadir edificio",
+ "viewBuilding": "Ver edificio",
+ "floors": "{count, plural, =1 {1 Planta} other {# Plantas}}",
+ "loading": "Cargando edificios..."
+ },
+ "building": {
+ "back": "Volver al panel",
+ "loading": "Cargando detalles del edificio...",
+ "selectDate": "Seleccionar fecha",
+ "selectFloor": "Seleccionar planta",
+ "floor": "Planta {number}",
+ "editFloorplan": "Editar plano",
+ "switching": "Cambiando de planta...",
+ "interactiveView": "Vista interactiva - Haga clic en un espacio para ver detalles o reservar.",
+ "noFloorplan": "No se ha subido ninguna imagen del plano. Mostrando cuadrícula predeterminada.",
+ "selectToSee": "Seleccione una planta para ver la distribución",
+ "notFound": "Edificio no encontrado",
+ "notFoundDesc": "El edificio que busca no existe o no tiene acceso.",
+ "return": "Volver al panel",
+ "selectDate": "Seleccionar fecha"
},
"booking": {
+ "available": "Disponible",
"book": "Reservar",
"cancel": "Cancelar",
"confirm": "Confirmar reserva",
@@ -49,6 +71,12 @@
"desk": "Puesto",
"amenity": "Servicio",
"currentlyBooked": "Reservado actualmente",
+ "editBooking": "Editar reserva",
+ "deskNote": "Los puestos se reservan para el día completo.",
+ "booking": "Reservando...",
+ "errorCreating": "Error al crear la reserva",
+ "errorDeleting": "Error al eliminar la reserva",
+ "errorTime": "La hora de finalización debe ser posterior a la de inicio",
"spaceType": {
"DESK": "Puesto",
"MEETING_ROOM": "Sala de reuniones",
@@ -64,6 +92,67 @@
"cancelConfirm": "¿Está seguro/a de que desea cancelar esta reserva?",
"cancelFailed": "Error al cancelar la reserva",
"updateFailed": "Error al actualizar la reserva",
- "loadFailed": "Error al cargar las reservas"
+ "loadFailed": "Error al cargar las reservas",
+ "editBooking": "Editar reserva",
+ "space": "Espacio",
+ "floor": "Planta {number}",
+ "deskNote": "Nota: Las reservas de puestos son para todo el día. Las horas se ajustarán automáticamente para abarcar el día completo.",
+ "saving": "Guardando...",
+ "editBookingTitle": "Editar reserva",
+ "saveChanges": "Guardar cambios"
+ },
+ "reports": {
+ "title": "Panel de Control de Informes",
+ "downloadData": "Descargar Datos XLSX",
+ "overallUtilization": "Ocupación General",
+ "totalSpaces": "Total de Espacios",
+ "occupiedNow": "{count} ocupados actualmente",
+ "totalEmployees": "Total de Empleados",
+ "acrossRoles": "En todos los roles",
+ "utilizationByBuilding": "Ocupación por Edificio",
+ "utilizationBySpaceType": "Ocupación por Tipo de Espacio",
+ "employeeRoleDistribution": "Distribución de Empleados por Rol",
+ "topBookers": "Usuarios con más reservas",
+ "noBookingsFound": "Aún no se han encontrado reservas.",
+ "bookingsCount": "{count, plural, =1 {1 reserva} other {# reservas}}",
+ "exportFailed": "Error al exportar",
+ "downloadError": "Error al descargar el informe: {error}"
+ },
+ "users": {
+ "title": "Gestión de Usuarios",
+ "invite": {
+ "role": "Rol",
+ "createLink": "Crear enlace de invitación",
+ "generated": "Enlace de invitación generado",
+ "description": "Cualquier persona con este enlace puede registrarse como {role} en su organización.",
+ "copy": "Copiar",
+ "copied": "¡Copiado!",
+ "done": "Hecho",
+ "error": "Error al generar la invitación: {error}"
+ },
+ "table": {
+ "user": "Usuario",
+ "role": "Rol",
+ "joined": "Unido el",
+ "actions": "Acciones",
+ "id": "ID"
+ },
+ "roles": {
+ "EMPLOYEE": "Empleado",
+ "SUPERVISOR": "Supervisor",
+ "ADMIN": "Administrador"
+ },
+ "password": {
+ "change": "Cambiar contraseña",
+ "title": "Cambiar contraseña",
+ "description": "Establecer una nueva contraseña para {email}",
+ "new": "Nueva contraseña",
+ "placeholder": "Al menos 6 caracteres",
+ "save": "Guardar contraseña",
+ "success": "Contraseña actualizada con éxito",
+ "error": "Error al actualizar la contraseña: {error}"
+ },
+ "updateRoleError": "Error al actualizar el rol: {error}",
+ "loading": "Cargando usuarios..."
}
}
\ No newline at end of file
diff --git a/apps/web/src/routes/admin/reports/+page.svelte b/apps/web/src/routes/admin/reports/+page.svelte
index 679437f..09305b8 100644
--- a/apps/web/src/routes/admin/reports/+page.svelte
+++ b/apps/web/src/routes/admin/reports/+page.svelte
@@ -1,5 +1,6 @@