# 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.
