Files
Krow-workspace/validation_staff_mock_dataconecct_last_update.md
2025-12-26 15:14:51 -05:00

334 lines
15 KiB
Markdown

# Análisis Comparativo: Mocks de Staff App vs. Schema de Data Connect
Este documento detalla el análisis comparativo entre las estructuras de datos mock utilizadas en la aplicación de Staff y las entidades definidas en el schema de Firebase Data Connect.
El objetivo es identificar discrepancias, proponer alineaciones y asegurar la consistencia del modelo de datos.
---
## 1. `_calculateDuration()` (de `lib/widgets/shift_card.dart`)
- **Estructura Mock:**
```dart
{
'hours': int,
'breakTime': String,
}
```
- **Entidad Data Connect más cercana:** No aplica.
- **Análisis:**
- Esta estructura es un tipo de retorno de una función de utilidad en la UI, no representa una entidad persistente.
- Calcula valores derivados (`horas`, `breakTime`) a partir de los datos de un turno (`Shift`).
- **Recomendación:**
- No se requiere ninguna acción. Es correcto que la lógica de la UI maneje este tipo de cálculos derivados.
---
## 2. `createWorkerProfile` y `_profile` (Múltiples archivos)
- **Estructura Mock (agregada):**
```dart
{
'full_name': String, // Corresponde a 'employeeName' en Staff
'bio': String, // No existe en Staff
'preferred_locations': List<String>, // Podría ser 'hubLocation' o 'eventLocation'
'max_distance_miles': double, // No existe en Staff
'skills': List<String>, // No existe en Staff, ¿quizás 'track' o 'position'?
'industries': List<String>, // No existe en Staff
'level': String, // No existe en Staff
'photo_url': String, // No existe en Staff
'total_shifts': int, // Existe como 'totalShifts'
'average_rating': double, // Existe como 'rating'
'on_time_rate': int, // No existe en Staff
'no_show_count': int, // Existe como 'noShowCount'
'cancellation_count': int, // Existe como 'cancellationCount'
'reliability_score': int, // Existe como 'reliabilityScore'
'phone': String, // Existe como 'phone'
}
```
- **Entidad Data Connect más cercana:** `Staff` (`staff.gql`)
- **Comparación Campo por Campo:**
| Campo Mock | Campo `Staff` (Data Connect) | Coincidencia | Notas |
| --- | --- | --- | --- |
| `full_name` | `employeeName` | Parcial | El nombre es diferente pero el propósito es el mismo. |
| `bio` | `notes` | Parcial | `notes` es un campo genérico, `bio` es más específico. |
| `preferred_locations` | `hubLocation`, `eventLocation` | Parcial | El schema tiene campos de texto, el mock una lista. |
| `max_distance_miles`| - | No | No existe un campo equivalente. |
| `skills` | `track`, `position`, `position2`| Parcial | `skills` es una lista, mientras que el schema tiene campos fijos. |
| `industries` | - | No | No existe un campo equivalente. |
| `level` | `profileType` | Parcial | `level` es un string, `profileType` es un `Enum`. El propósito es similar. |
| `photo_url` | - | No | No existe un campo para la foto. |
| `total_shifts` | `totalShifts` | **Directa** | Coincidencia exacta. |
| `average_rating` | `rating` | **Directa** | El propósito es el mismo. |
| `on_time_rate` | - | No | No existe un campo equivalente. |
| `no_show_count` | `noShowCount` | **Directa** | Coincidencia exacta. |
| `cancellation_count`| `cancellationCount` | **Directa** | Coincidencia exacta. |
| `reliability_score`| `reliabilityScore` | **Directa** | Coincidencia exacta. |
| `phone` | `phone`, `contactNumber` | **Directa** | Existen dos campos de teléfono. Alinear a uno. |
- **Diferencias y Recomendaciones:**
1. **Nombres de Campos:** Se recomienda que la app **se alinee con los nombres de Data Connect** para mantener la consistencia (e.g., usar `employeeName` en lugar de `full_name`).
2. **Campos Faltantes en `Staff`:**
- `photo_url`: **Crítico.** Se debe agregar un campo `photoUrl: String` a la entidad `Staff`.
- `bio`: Se puede usar el campo `notes` o agregar un campo `bio: String` si se considera una propiedad fundamental.
- `max_distance_miles`: Recomendar agregar `maxTravelDistance: Float`.
- `on_time_rate`: Recomendar agregar `onTimeRate: Float`.
3. **Manejo de Listas (skills, industries, preferred_locations):**
- **Opción A (Recomendada):** Usar campos de tipo `jsonb` para almacenar estas listas.
```graphql
scalar Any
# En Staff
skills: Any @col(dataType: "jsonb")
industries: Any @col(dataType: "jsonb")
preferredLocations: Any @col(dataType: "jsonb")
```
- **Opción B:** Crear nuevas entidades y relaciones (e.g., `StaffSkill`, `StaffIndustry`), lo cual es más complejo pero más estructurado.
---
## 3. `_user` (de `worker_profile_screen.dart` y `personal_info_screen.dart`)
- **Estructura Mock:**
```dart
{
'full_name': String,
'email': String,
'photo_url': String,
}
```
- **Entidad Data Connect más cercana:** `User` (`user.gql`)
- **Comparación Campo por Campo:**
| Campo Mock | Campo `User` (Data Connect) | Coincidencia | Notas |
| --- | --- | --- | --- |
| `full_name` | `fullName` | **Directa** | Coincidencia de propósito, diferencia de naming. |
| `email` | `email` | **Directa** | Coincidencia exacta. |
| `photo_url` | - | No | No existe un campo para la foto en `User`. |
- **Diferencias y Recomendaciones:**
- El mock `_user` parece ser una combinación de datos de la entidad `User` (auth) y `Staff` (perfil).
- `photo_url` pertenece al perfil del trabajador, no a su registro de autenticación. Debería estar en la entidad `Staff` (ver punto anterior).
- La app debe obtener el `email` y `fullName` de la entidad `User` y el resto de la información de perfil de la entidad `Staff`.
---
## 4. `_contacts` (de `emergency_contact_screen.dart`)
- **Estructura Mock:**
```dart
{
'name': String,
'phone': String,
'relationship': String
}
```
- **Entidad Data Connect más cercana:** No existe.
- **Análisis:**
- Esta información es específica del perfil de un trabajador.
- No justifica una nueva tabla en la base de datos, ya que un contacto de emergencia no es una entidad independiente.
- **Recomendación:**
- Agregar un campo `emergencyContacts: Any @col(dataType: "jsonb")` a la entidad `Staff`. Este campo almacenaría un array de objetos JSON con la estructura del mock.
---
## 5. `_conversations` y `messages` (de `messages_screen.dart`)
- **Estructura Mock:**
```dart
// Conversación
{
'sender_id': String,
'sender_name': String,
'lastMessage': String,
'lastTime': DateTime,
'unread': int,
'messages': [ ... ] // Lista de mensajes
}
// Mensaje
{
'content': String,
'sender_id': String,
}
```
- **Entidades Data Connect más cercanas:** `Conversation` (`conversation.gql`) y `Message` (`message.gql`).
- **Análisis y Comparación:**
- Data Connect tiene un modelo normalizado con dos entidades separadas: `Conversation` y `Message`, relacionadas por `conversationId`.
- El mock presenta un modelo desnormalizado, donde los mensajes están anidados dentro de la conversación.
- **Discrepancias:**
- `Conversation`:
- El mock tiene `sender_id` y `sender_name`, mientras que el schema tiene `participants` (un array de IDs).
- El mock tiene `lastMessage`, `lastTime` y `unread`, que son datos derivados. El schema de `Conversation` no los tiene, y deberían calcularse o recuperarse del último mensaje asociado.
- `Message`:
- El schema tiene `senderName` y `createdBy` (ID), mientras que el mock solo tiene `sender_id`.
- **Recomendaciones:**
1. **Alinear la App:** La app debe adaptarse al modelo de Data Connect.
- Primero, obtener la lista de `Conversation` para el usuario actual.
- Luego, para cada conversación, obtener el último `Message` para mostrar `lastMessage` y `lastTime`.
- El conteo de `unread` deberá calcularse en la app o agregar un campo derivado en `Conversation`.
2. **Schema `Message`:** El schema de `Message` está bien, pero podría beneficiarse de un campo `senderId` explícito además de `createdBy` para mayor claridad.
---
## 6. `_timesheets` (de `time_card_screen.dart`)
- **Estructura Mock:**
```dart
{
'id': String,
'shift_id': String,
'date': String,
'actual_start': String,
'actual_end': String,
'total_hours': double,
'hourly_rate': double,
'total_pay': double,
'status': String,
'shift_title': String,
'client_name': String,
'location': String,
}
```
- **Entidad Data Connect más cercana:** `Assignment` (`assignment.gql`) y `Shift` (`shift.gql`).
- **Análisis:**
- Un `timesheet` parece ser el resultado de un `Assignment` completado.
- Combina información de múltiples entidades: el `Assignment` (estado, rol), el `Shift` (fechas, título) y `Staff` (tarifa/rate).
- **Recomendaciones:**
1. **Crear una nueva entidad `TimeCard`:** Esta es la opción más limpia para representar una hoja de tiempo.
```graphql
type TimeCard @table(name: "time_cards") {
id: UUID! @default(expr: "uuidV4()")
assignmentId: UUID!
staffId: UUID!
actualStart: Timestamp!
actualEnd: Timestamp!
totalHours: Float!
hourlyRate: Float!
totalPay: Float!
status: String # Podría ser un Enum (PENDING, APPROVED, PAID)
# ... otros campos relevantes
}
```
2. **Extender `Assignment`:** Agregar los campos `actualStart`, `actualEnd`, `totalHours`, etc., a la entidad `Assignment`. Esto es menos ideal, ya que mezcla la planificación (`scheduledStart`) con la ejecución.
3. **App:** La app deberá construir la vista del `timesheet` combinando datos de `TimeCard` (o `Assignment` extendido), `Shift` y `Order` (para `client_name`).
---
## 7. `_certificates` y `_courses` (Múltiples archivos)
- **Estructura Mock (agregada):**
```dart
{
'id': String,
'name': String, // o 'title'
'description': String,
'status': String, // 'CURRENT', 'EXPIRED', 'PENDING'
'expiry': String?,
'duration_minutes': int,
'xp_reward': int,
'progress_percent': int,
'completed': bool,
}
```
- **Entidad Data Connect más cercana:** `Certification` (`certification.gql`).
- **Análisis y Comparación:**
- La entidad `Certification` de Data Connect ya cubre los aspectos más importantes: nombre, tipo, estado y fecha de expiración.
- Los mocks `_courses` y `_trainings` introducen conceptos de gamificación (`xp_reward`) y seguimiento del progreso (`progress_percent`, `duration_minutes`) que no están en `Certification`.
- **Recomendaciones:**
1. **Diferenciar Entidades:** Es probable que se necesiten dos entidades:
- `Certification`: Para documentos oficiales y de cumplimiento (la entidad actual funciona bien).
- `TrainingCourse`: Una nueva entidad para los cursos de "Krow University".
```graphql
type TrainingCourse @table(name: "training_courses") {
id: UUID! @default(expr: "uuidV4()")
title: String!
description: String
durationMinutes: Int
xpReward: Int
# ... otros campos
}
type StaffTrainingProgress @table(name: "staff_training_progress") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID!
courseId: UUID!
progressPercent: Int
completed: Boolean
}
```
2. **App:** La app deberá consumir de `Certification` para la pantalla de "Certificados" y de las nuevas entidades para "Krow University".
---
## 8. `_recentPayments` (de `payments_screen.dart` y `earnings_screen.dart`)
- **Estructura Mock:**
```dart
{
'date': String,
'amount': double,
'status': String,
'title': String, // Nombre del turno/evento
'location': String,
'workedTime': String,
'hours': int,
'rate': int,
}
```
- **Entidad Data Connect más cercana:** `Invoice` (`invoice.gql`).
- **Análisis:**
- La entidad `Invoice` representa una factura con un monto total y un estado, pero no el detalle de los turnos que la componen.
- El mock `_recentPayments` es una vista agregada que combina datos de un pago/factura con detalles del trabajo realizado.
- **Recomendaciones:**
- El modelo de datos debería incluir una relación entre `Invoice` y los `TimeCard` (o `Assignment`) que la componen.
```graphql
# En la nueva entidad TimeCard
invoiceId: UUID # Se asigna cuando se incluye en una factura
```
- La app deberá:
1. Obtener los `Invoice` pagados para un `Staff`.
2. Para cada `Invoice`, obtener los `TimeCard` asociados.
3. Con los `TimeCard`, obtener los detalles del `Shift` y `Order` para construir la vista completa que se ve en el mock.
---
## 9. `_recentActivity` (de `clock_in_screen.dart`)
- **Estructura Mock:**
```dart
{
'date': DateTime,
'start': String,
'end': String,
'hours': String,
}
```
- **Entidad Data Connect más cercana:** `ActivityLog` (`activityLog.gql`)
- **Análisis:**
- La entidad `ActivityLog` está diseñada para registrar eventos genéricos (`EVENT_CREATED`, `STAFF_ASSIGNED`), pero no específicamente los eventos de "clock-in" y "clock-out".
- Los datos del mock (`start`, `end`, `hours`) se solapan con la entidad `TimeCard` propuesta anteriormente.
- **Recomendaciones:**
1. **Usar `TimeCard`:** Los eventos de `clock-in` y `clock-out` deberían **crear o actualizar** un registro en la entidad `TimeCard`.
- `Clock-in` → Crea un `TimeCard` con `actualStart`.
- `Clock-out` → Actualiza el `TimeCard` existente con `actualEnd` y calcula `totalHours`.
2. **`ActivityLog` para Notificaciones:** Se puede usar `ActivityLog` para *notificar* al usuario sobre acciones importantes (e.g., "Tu hoja de tiempo ha sido aprobada"), pero no para almacenar los datos crudos del fichaje.
3. La app debería mostrar el historial de `TimeCard` en la pantalla de `clock_in`.
---
## Conclusión General y Pasos a Seguir
La validación revela que, si bien hay una base sólida, se necesitan varias actualizaciones en el schema de Data Connect para dar soporte completo a las funcionalidades de la app de Staff.
**Acciones recomendadas (en orden de prioridad):**
1. **Actualizar `Staff`:**
- Añadir campos faltantes: `photoUrl`, `maxTravelDistance`, `onTimeRate`, `bio`.
- Añadir campos `jsonb` para `skills`, `industries`, `preferredLocations` y `emergencyContacts`.
- Alinear el naming en la app a `employeeName`.
2. **Crear `TimeCard`:**
- Definir e implementar la nueva entidad `TimeCard` para gestionar los fichajes y las horas trabajadas.
3. **Crear Entidades de `Training`:**
- Crear `TrainingCourse` y `StaffTrainingProgress` para la funcionalidad de "Krow University".
4. **Alinear la App:**
- Refactorizar los servicios de la app para que consuman el SDK de Data Connect según el modelo de datos actualizado (especialmente para conversaciones y pagos).
- Ajustar la UI para construir las vistas a partir de múltiples entidades relacionadas en lugar de un único mock desnormalizado.