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

15 KiB

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:
    {
      '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):

    {
      '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.
        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:

    {
      '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:
    {
      '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:
    // 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:
    {
      '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.
      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):
    {
      '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".
        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:
    {
      '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.
      # 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:
    {
      '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.