chore: remove obsolete documentation files

These files contained outdated analysis and planning
related to the mobile app and data modeling. They are
no longer relevant to the current state of the project
and were removed to reduce clutter and confusion.

docs: remove outdated mock data analysis document

The file `validation_staff_mock_dataconecct_last_update.md`
contained an outdated analysis of mock data structures
compared to the Data Connect schema. This information is
no longer relevant and the file has been removed to
maintain a clean and up-to-date repository.
docs: remove outdated mock data analysis document v2

The file `validation_staff_mock_dataconecct_v2.md`
contained an outdated analysis of mock data structures
compared to the Data Connect schema. This information is
no longer relevant and the file has been removed to
maintain a clean and up-to-date repository.
This commit is contained in:
bwnyasse
2026-01-10 12:20:22 -05:00
parent 01158b8a89
commit d0a536ffa4
15 changed files with 0 additions and 3541 deletions

View File

@@ -1,253 +0,0 @@
# DataConnect Inconsistencies between the Base44 frontend and the backend (Firebase Data Connect + PostgreSQL)
**Author:** José Salazar
**Date:** Dec 2025
---
## 📌 Purpose of this document
Document all findings during the integration of the new backend
(**Firebase Data Connect + PostgreSQL**) with the frontend generated by **Base44 AI**.
This document summarizes:
- Issues found
- camelCase vs snake_case inconsistencies
- Enum inconsistencies (uppercase/lowercase and dashes)
- Differences between what DataConnect returns vs what the frontend expects
- Recommended fixes
- Suggestions for Base44 AI to update its model
- Impact on queries, mutations, and the generated SDK
---
## 1⃣ Enums Different formats between Front and Backend
### Observation
In the frontend, enum values are in mixed formats, for example:
- UPPERCASE: SKILLED
- camelCase: fullTime
In the backend (DataConnect schema), enums are defined only in UPPERCASE, for example:
- FULL_TIME
- CROSS_TRAINED
- NOT_REQUIRED
- PENDING
DataConnect only accepts these exact values.
### Problem
When the frontend sends:
- "crossTrained" instead of CROSS_TRAINED
- "fluent" instead of FLUENT
### Impact
- Mutations can fail or return enum validation errors.
- Filters using enums return no results.
- Behavior changes depending on how Base44 AI generated the object.
### Recommendation
- Define a single standard: **ALL enums must be UPPERCASE** on the frontend and backend.
- Before sending to the backend, normalize enum values to uppercase.
### Suggestion for Base44 AI
- Adjust models so they always generate enums in UPPERCASE.
---
## 2⃣ Enums with dashes (“-”) Not valid in GraphQL
### Observation
In the legacy frontend, some enum values contain dashes, for example:
- CUSTOMER-SERVICE
- CROSS-TRAINED
- PART-TIME
But in GraphQL enums only allow letters, numbers, and underscores.
The backend had to define them as:
- CUSTOMER_SERVICE
- CROSS_TRAINED
- PART_TIME
### Problem
When the frontend sends "CUSTOMER-SERVICE" or "CROSS-TRAINED":
- The backend expects CUSTOMER_SERVICE or CROSS_TRAINED.
- There is no match between the frontend value and the DataConnect enum.
### Impact
- Enum filters return nothing.
- Mutations fail when trying to save invalid enum values.
- Compatibility between the Base44 model and the DataConnect schema breaks.
### Recommendation
- Standardize all enums to UPPERCASE SNAKE_CASE (e.g., CUSTOMER_SERVICE).
- Never use dashes “-” in enum values.
### Suggestion for Base44 AI
- Update models so enum values are always generated as
UPPERCASE_WITH_UNDERSCORE (e.g., CUSTOMER_SERVICE), without dashes.
---
## 3⃣ Field names Front in snake_case vs DataConnect in camelCase
### Observation
The original Base44 frontend uses snake_case field names, for example:
- contact_number
- vendor_id
- background_check_status
- hub_location
In DataConnect the schema is camelCase, and although you can map to the actual PostgreSQL column using @col, the GraphQL type remains camelCase, for example:
- contactNumber (mapped to "contact_number" in Postgres)
- vendorId (mapped to "vendor_id")
- backgroundCheckStatus (mapped to "background_check_status")
- hubLocation (mapped to "hub_location")
Meaning:
- In the database (PostgreSQL) names remain snake_case.
- In DataConnect and the SDK they are exposed as camelCase.
### Problem
The frontend still expects/reads fields like contact_number, but the SDK returns contactNumber.
A similar issue happens when the frontend sends payloads in snake_case:
- The GraphQL schema does not recognize contact_number.
- It only accepts contactNumber.
### Impact
- UI fails to show data because it reads keys that dont exist (snake_case).
- Mutations fail or ignore fields due to mismatched names.
- Filters with snake_case are invalid in GraphQL.
### Recommendation
- Agree that **all communication with DataConnect (frontend + SDK) uses camelCase**.
- Keep snake_case only at PostgreSQL level using @col, for example:
employeeName: String @col(name: "employee_name")
Thus:
- Frontend / SDK / GraphQL → camelCase (employeeName)
- PostgreSQL → snake_case (employee_name)
### Suggestion for Base44 AI
- Adjust generated frontend code so it uses camelCase when consuming the new backend.
- If Supabase or another backend is still used, document all mappings clearly.
---
## 4⃣ Fields used by the frontend but not listed in API Spec v3
### Observation
During integration we found that Base44 frontend uses fields not defined in the official document:
Reference file:
docs/03-backend-api-specification-v3.md
Examples in the Staff entity:
The frontend sends and displays fields like:
- notes
- rate
But these fields were not defined in the original v3 specification for Staff.
### Problem
- The frontend assumes these fields exist because the old database had them.
- The DataConnect schema does not include them.
- Sending these values in mutations causes validation errors.
### Impact
- Inconsistency between what the UI shows/edits and what is actually persisted.
- Risk of losing data the user believes is being saved.
- Hard to maintain a 1:1 mapping between the previous Base44 model and the new backend.
### Recommendation
- Validate which fields should truly exist for each entity (e.g., Staff).
- Align these three items:
1. API Spec v3
2. DataConnect Schema
3. Base44 Frontend
- If a field is no longer needed, remove it from the frontend.
- If important, add it formally to the API Spec and to the DataConnect schema.
---
## 5⃣ DataConnect vs Front Observed behavior
1. DataConnect:
- Always exposes fields in camelCase.
- Enforces enum restrictions exactly as defined (UPPERCASE, no dashes).
- Allows mapping to Postgres column names using @col, but GraphQL names remain camelCase.
2. Base44 Frontend:
- Uses snake_case in many areas.
- Uses enums in mixed formats (uppercase, camelCase, with dashes).
- Contains extra fields not included in API Spec v3.
---
## 6⃣ Suggested fixes (personal criteria)
1. Enums
- Standardize to UPPERCASE_SNAKE_CASE for all enum values.
- Apply a normalization layer in the frontend to convert any format into the official one before hitting the backend.
2. Field names
- Migrate the frontend to camelCase for any interaction with DataConnect.
- Keep snake_case only at the database layer using @col.
3. Extra fields
- Review all fields the frontend sends and compare with API Spec v3.
- Remove or add fields depending on what becomes the “source of truth” (Spec v3).
4. Documentation
- Keep this file updated as the Base44 → DataConnect migration reference.
- Add valid payload examples for each entity (Staff, Vendor, Invoice, etc.).
---
## 7⃣ Summary
- Always generate:
- Enums: UPPERCASE_SNAKE_CASE (e.g., FULL_TIME, CUSTOMER_SERVICE).
- Fields: camelCase (e.g., contactNumber, hubLocation, backgroundCheckStatus).
- Avoid:
- Dashes “-” in enum values.
- Spaces in enum values.
---
This document captures the current findings and serves as a guide to fully align the Base44 frontend with the backend based on Firebase Data Connect + PostgreSQL.

View File

@@ -1,18 +0,0 @@
# 🚀 [CATEGORY] <Task Title>
Labels: <category>, <platform>, <priority>, sred-eligible
### 🎯 Objective
<Describe what will be built, added, or modified. State the purpose and outcome.>
### 🔬 SR&ED Justification
#### Technological Uncertainty
<Explain the specific technical unknowns. What is difficult? What must be discovered?>
#### Systematic Investigation
<Describe experiments, prototypes, or iterations needed to determine the optimal solution.>
### ✅ Acceptance Criteria
- [ ] <Criteria 1>
- [ ] <Criteria 2>
- [ ] <Criteria 3>

View File

@@ -1,53 +0,0 @@
# This file is the single source of truth for GitHub labels.
# Run 'make setup-labels' to apply these to the repository.
# By Type of Work
- name: "bug"
description: "Something isn't working"
color: "d73a4a"
- name: "feature"
description: "A new user-facing feature"
color: "0075ca"
- name: "enhancement"
description: "Minor improvement to an existing feature"
color: "a2eeef"
- name: "infra"
description: "Tasks for infrastructure, CI/CD, and project setup"
color: "a2eeef"
- name: "documentation"
description: "Tasks for creating or updating documentation"
color: "0075ca"
- name: "refactor"
description: "Code changes that neither fix a bug nor add a feature"
color: "f29513"
- name: "security"
description: "Tasks related to security enhancements, audits, or fixes"
color: "000000" # Black for security, to make it stand out
# By Platform
- name: "platform:web"
description: "Tasks specific to the React web app"
color: "5319e7"
- name: "platform:mobile"
description: "Tasks affecting both mobile apps"
color: "5319e7"
- name: "platform:backend"
description: "Tasks for Data Connect or Cloud Functions"
color: "5319e7"
- name: "platform:admin"
description: "Tasks specific to the Admin Console web app"
color: "5319e7"
# For Project Management
- name: "sred-eligible"
description: "Tasks identified as eligible for SR&ED claims"
color: "d876e3"
- name: "priority:high"
description: "Urgent or critical tasks"
color: "b60205"
- name: "priority:medium"
description: "Default priority"
color: "fbca04"
- name: "priority:low"
description: "Non-urgent, background tasks"
color: "0e8a16"

0
mobile-apps/.keep Normal file
View File

View File

@@ -1,662 +0,0 @@
He analizado lib/screens/auth/profile_setup_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _steps
* Propósito: Define los pasos del proceso de configuración del perfil.
* Estructura de cada elemento `Map<String, dynamic>`:
1 {
2 'id': String, // Ejemplo: 'basic'
3 'title': String, // Ejemplo: 'Basic Info'
4 'icon': IconData // Ejemplo: LucideIcons.user
5 }
Ahora, pasaré al siguiente archivo: lib/screens/worker/benefits_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/benefits_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/benefits_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _benefitsData
* Propósito: Representa una lista de beneficios disponibles para el trabajador.
* Estructura de cada elemento `Map<String, dynamic>`:
1 {
2 'id': String, // Ejemplo: 'sick', 'vacation'
3 'title': String, // Ejemplo: 'Sick Days', 'Vacation'
4 'current': int, // Horas/cantidad actuales
5 'total': int, // Horas/cantidad totales
6 'color': Color, // Color para la visualización
7 'description': String, // Descripción del beneficio
8 'history': List<Map<String, dynamic>>, // Lista anidada para el historial de beneficios
9 'requestLabel': String, // Texto para el botón de solicitud
10 'notice': String?, // Texto de aviso opcional
11 }
* Estructura Anidada (`history`): Cada elemento en la lista history tiene:
1 {
2 'date': String, // Ejemplo: '1 Jan, 2024'
3 'status': String, // Ejemplo: 'Pending', 'Submitted'
4 }
El siguiente archivo es: lib/screens/worker/worker_profile/finances/time_card_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../time_card_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/finances/time_card_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _timesheets
* Propósito: Representa una lista de hojas de tiempo registradas para los turnos trabajados por el empleado.
* Estructura de cada elemento `Map<String, dynamic>`:
1 {
2 'id': String, // Identificador único de la hoja de tiempo
3 'shift_id': String, // ID del turno asociado
4 'date': String, // Cadena ISO 8601 para la fecha, e.g., "2023-12-21T..."
5 'actual_start': String, // Hora de inicio real, e.g., '09:00'
6 'actual_end': String, // Hora de finalización real, e.g., '17:00'
7 'total_hours': double, // Horas totales trabajadas en el turno
8 'hourly_rate': double, // Tarifa por hora del turno
9 'total_pay': double, // Pago total por el turno
10 'status': String, // Estado de la hoja de tiempo, e.g., 'pending', 'approved', 'paid', 'disputed'
11 'shift_title': String, // Título del turno, e.g., 'Line Cook'
12 'client_name': String, // Nombre del cliente
13 'location': String, // Ubicación del turno
14 }
El siguiente archivo es: lib/screens/worker/worker_profile/support/faqs_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../support/faqs_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/support/faqs_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _faqData
* Propósito: Representa una lista categorizada de preguntas frecuentes.
* Estructura de cada elemento `Map<String, dynamic>` (que representa una categoría de FAQ):
1 {
2 'category': String, // Ejemplo: 'Getting Started', 'Shifts & Work'
3 'questions': List<Map<String, dynamic>>, // Lista anidada de preguntas y respuestas
4 }
* Estructura Anidada (`questions`): Cada elemento en la lista questions tiene:
1 {
2 'q': String, // El texto de la pregunta
3 'a': String, // El texto de la respuesta
4 }
El siguiente archivo es: lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile l/.../emergency_contact_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _contacts
* Propósito: Representa una lista de contactos de emergencia para el trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un contacto de emergencia):
1 {
2 'name': String, // Nombre del contacto
3 'phone': String, // Número de teléfono del contacto
4 'relationship': String, // Relación con el trabajador, e.g., 'family', 'spouse', 'friend', 'other'
5 }
El siguiente archivo es: lib/screens/worker/worker_profile/support/messages_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../messages_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/support/messages_screen.dart.
Este archivo contiene dos estructuras principales de List<Map<String, dynamic>>:
1. Nombre de la variable: _conversations
* Propósito: Representa una lista de conversaciones de chat.
* Estructura de cada elemento `Map<String, dynamic>` (que representa una conversación individual):
1 {
2 'sender_id': String, // ID del remitente/otra parte en la conversación
3 'sender_name': String, // Nombre visible del remitente/otra parte
4 'lastMessage': String, // Contenido del último mensaje en la conversación
5 'lastTime': DateTime, // Marca de tiempo del último mensaje
6 'unread': int, // Número de mensajes no leídos en la conversación
7 'messages': List<Map<String, dynamic>>, // Lista anidada de mensajes individuales dentro de la conversación
8 }
* Estructura Anidada (`messages`): Cada elemento en la lista messages tiene:
1 {
2 'content': String, // El contenido del mensaje
3 'sender_id': String, // El ID del remitente de este mensaje específico
4 }
El siguiente archivo es: lib/screens/worker/worker_profile/onboarding/attire_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../attire_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/onboarding/attire_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _attireOptions
* Propósito: Representa una lista de diferentes artículos de vestimenta que un trabajador podría necesitar o poseer.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un artículo de vestimenta):
1 {
2 'id': String, // Identificador único para el artículo de vestimenta, e.g., 'non_slip_shoes'
3 'label': String, // Nombre visible del artículo de vestimenta, e.g., 'Non Slip Shoes'
4 'icon': IconData, // Icono asociado con el artículo de vestimenta, e.g., LucideIcons.footprints
5 'imageUrl': String, // URL de una imagen del artículo de vestimenta
6 }
El siguiente archivo es: lib/screens/worker/worker_profile/level_up/trainings_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../trainings_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/level_up/trainings_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _courses
* Propósito: Representa una lista de cursos de capacitación disponibles para el trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo curso):
1 {
2 'id': String, // Identificador único para el curso
3 'title': String, // Título del curso
4 'description': String, // Descripción del curso
5 'duration_minutes': int, // Duración del curso en minutos
6 'xp_reward': int, // Puntos de experiencia otorgados al finalizar
7 'thumbnail_url': String?,// URL opcional para una miniatura del curso
8 'progress_percent': int, // Porcentaje de finalización del curso
9 'completed': bool, // Indica si el curso está completado
10 }
El siguiente archivo es: lib/screens/worker/worker_profile/compliance/documents_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../documents_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/compliance/documents_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _requiredDocs
* Propósito: Representa una lista de documentos requeridos para el cumplimiento del trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo documento):
1 {
2 'id': String, // Identificador único para el documento, e.g., 'id', 'ssn'
3 'name': String, // Nombre visible del documento, e.g., 'Government ID'
4 'description': String, // Breve descripción o ejemplos para el documento
5 'status': String, // Estado actual del documento, e.g., 'verified', 'pending', 'missing'
6 }
El siguiente archivo es: lib/screens/worker/worker_profile/finances/bank_account_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../bank_account_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/finances/bank_account_screen.screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _accounts
* Propósito: Representa una lista de cuentas bancarias vinculadas para el trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa una sola cuenta bancaria):
1 {
2 'id': int, // Identificador único para la cuenta
3 'bank': String, // Nombre del banco, e.g., 'Chase Bank'
4 'type': String, // Tipo de cuenta, e.g., 'Checking', 'Savings'
5 'last4': String, // Últimos 4 dígitos del número de cuenta
6 'isPrimary': bool, // Si esta es la cuenta principal
7 }
El siguiente archivo es: lib/screens/worker/worker_profile/compliance/tax_forms_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../tax_forms_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/compliance/tax_forms_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _forms
* Propósito: Representa una lista de formularios de impuestos requeridos al trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo formulario de impuestos):
1 {
2 'id': String, // Identificador único para el formulario, e.g., 'i9', 'w4'
3 'title': String, // Título oficial del formulario, e.g., 'Form I-9'
4 'subtitle': String, // Subtítulo o nombre completo del formulario
5 'description': String, // Breve descripción del propósito del formulario
6 'status': String, // Estado del formulario, e.g., 'submitted', 'not_started', 'draft', 'approved'
7 'icon': String, // Icono (emoji) o identificador para el icono del formulario
8 }
El siguiente archivo es: lib/screens/worker/worker_profile/compliance/certificates_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../certificates_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/compliance/certificates_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _certificates
* Propósito: Representa una lista de certificados (por ejemplo, verificación de antecedentes, manipulador de alimentos) requeridos para el trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo certificado):
1 {
2 'id': String, // Identificador único para el certificado, e.g., 'background', 'food_handler'
3 'name': String, // Nombre visible del certificado
4 'icon': IconData, // Icono asociado con el certificado
5 'color': Color, // Color para la visualización
6 'description': String, // Breve descripción del certificado
7 'status': String, // Estado del certificado, e.g., 'complete', 'expiring', 'not_started', 'pending'
8 'expiry': String?, // Cadena ISO 8601 para la fecha de vencimiento (puede ser nula)
9 }
El siguiente archivo es: lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../leaderboard_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _profiles
* Propósito: Representa una lista de perfiles de trabajadores para la tabla de clasificación. Esto es esencialmente un mock de datos de WorkerProfile para fines de visualización.
* Estructura de cada elemento `Map<String, dynamic>` (que representa la entrada de un trabajador en la tabla de clasificación):
1 {
2 'id': String, // Identificador único para el trabajador
3 'name': String, // Nombre visible del trabajador, e.g., 'Sarah Jenkins'
4 'photo_url': String?, // URL de la foto de perfil del trabajador (puede ser nula)
5 'xp': int, // Puntos de experiencia del trabajador
6 'level': String, // Nivel actual del trabajador, e.g., 'Krower III'
7 'user_id': String, // ID de usuario (correo electrónico en el mock), utilizado para identificar al usuario actual
8 }
El siguiente archivo es: lib/screens/worker/payments_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/payments_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/payments_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _recentPayments
* Propósito: Representa una lista de registros de pagos recientes por turnos trabajados.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo registro de pago):
1 {
2 'date': String, // Fecha del pago, e.g., 'Sat, Dec 6'
3 'title': String, // Título del turno, e.g., 'Cook'
4 'location': String, // Ubicación del turno
5 'address': String, // Dirección completa de la ubicación del turno
6 'workedTime': String, // Rango de tiempo trabajado, e.g., '2:00 PM - 10:00 PM'
7 'amount': double, // Cantidad de pago por el turno
8 'status': String, // Estado del pago, e.g., 'paid'
9 'hours': int, // Horas trabajadas en el turno
10 'rate': double, // Tarifa por hora del turno
11 }
El siguiente archivo es: lib/screens/worker/availability_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../availability_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/availability_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _timeSlots
* Propósito: Define los diferentes rangos horarios (mañana, tarde, noche) para los cuales un trabajador puede establecer su disponibilidad.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo rango horario):
1 {
2 'id': String, // Identificador único para el rango horario, e.g., 'morning'
3 'label': String, // Nombre visible, e.g., 'Morning'
4 'time': String, // Rango de tiempo, e.g., '4:00 AM - 12:00 PM'
5 'icon': IconData, // Icono asociado con el rango horario, e.g., LucideIcons.sunrise
6 'bg': Color, // Color de fondo para la visualización
7 'iconColor': Color, // Color del icono
8 }
El siguiente archivo es: lib/screens/worker/earnings_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/earnings_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ He analizado lib/screens/worker/earnings_screen.dart.
Aquí está la estructura del List<Map<String, dynamic>> que contiene:
* Nombre de la variable: _recentPayments
* Propósito: Representa una lista de resúmenes de pagos recientes para el trabajador.
* Estructura de cada elemento `Map<String, dynamic>` (que representa un solo resumen de pago):
1 {
2 'date': String, // Fecha del pago, e.g., 'Dec 15'
3 'amount': double, // Monto total pagado por este período
4 'shifts': int, // Número de turnos cubiertos por este pago
5 'status': String, // Estado del pago, e.g., 'paid'
6 }
El siguiente archivo es: lib/screens/worker/clock_in_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/clock_in_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ ¡He completado la búsqueda! He encontrado y analizado todas las instancias de List<Map<String, dynamic>> en los archivos .dart dentro de tu proyecto.
Aquí tienes la lista consolidada, junto con su propósito y la estructura de los Map internos:
---
1. lib/screens/auth/profile_setup_screen.dart
* Variable: _steps
* Propósito: Define los pasos del proceso de configuración del perfil.
* Estructura de cada elemento:
1 {
2 'id': String, // Ejemplo: 'basic'
3 'title': String, // Ejemplo: 'Basic Info'
4 'icon': IconData // Ejemplo: LucideIcons.user
5 }
---
2. lib/screens/worker/benefits_screen.dart
* Variable: _benefitsData
* Propósito: Lista de beneficios disponibles para el trabajador.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'current': int,
5 'total': int,
6 'color': Color,
7 'description': String,
8 'history': List<Map<String, dynamic>>, // <- Lista anidada
9 'requestLabel': String,
10 'notice': String?,
11 }
* Estructura anidada `history`:
1 {
2 'date': String,
3 'status': String,
4 }
---
3. lib/screens/worker/worker_profile/finances/time_card_screen.dart
* Variable: _timesheets
* Propósito: Lista de hojas de tiempo registradas para turnos trabajados.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'shift_id': String,
4 'date': String, // Formato ISO 8601
5 'actual_start': String,
6 'actual_end': String,
7 'total_hours': double,
8 'hourly_rate': double,
9 'total_pay': double,
10 'status': String,
11 'shift_title': String,
12 'client_name': String,
13 'location': String,
14 }
---
4. lib/screens/worker/worker_profile/support/faqs_screen.dart
* Variable: _faqData
* Propósito: Lista categorizada de preguntas frecuentes.
* Estructura de cada elemento (categoría):
1 {
2 'category': String,
3 'questions': List<Map<String, dynamic>>, // <- Lista anidada
4 }
* Estructura anidada `questions`:
1 {
2 'q': String, // Pregunta
3 'a': String, // Respuesta
4 }
---
5. lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart
* Variable: _contacts
* Propósito: Lista de contactos de emergencia para el trabajador.
* Estructura de cada elemento:
1 {
2 'name': String,
3 'phone': String,
4 'relationship': String, // Ejemplo: 'family', 'spouse', 'friend'
5 }
---
6. lib/screens/worker/worker_profile/support/messages_screen.dart
* Variable: _conversations
* Propósito: Lista de conversaciones de chat.
* Estructura de cada elemento (conversación):
1 {
2 'sender_id': String,
3 'sender_name': String,
4 'lastMessage': String,
5 'lastTime': DateTime,
6 'unread': int,
7 'messages': List<Map<String, dynamic>>, // <- Lista anidada
8 }
* Estructura anidada `messages`:
1 {
2 'content': String,
3 'sender_id': String,
4 }
---
7. lib/screens/worker/worker_profile/onboarding/attire_screen.dart
* Variable: _attireOptions
* Propósito: Lista de artículos de vestimenta que un trabajador podría necesitar o poseer.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'label': String,
4 'icon': IconData,
5 'imageUrl': String,
6 }
---
8. lib/screens/worker/worker_profile/level_up/trainings_screen.dart
* Variable: _courses
* Propósito: Lista de cursos de capacitación disponibles.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'description': String,
5 'duration_minutes': int,
6 'xp_reward': int,
7 'thumbnail_url': String?,
8 'progress_percent': int,
9 'completed': bool,
10 }
---
9. lib/screens/worker/worker_profile/compliance/documents_screen.dart
* Variable: _requiredDocs
* Propósito: Lista de documentos requeridos para el cumplimiento.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'name': String,
4 'description': String,
5 'status': String, // Ejemplo: 'verified', 'pending', 'missing'
6 }
---
10. lib/screens/worker/worker_profile/finances/bank_account_screen.dart
* Variable: _accounts
* Propósito: Lista de cuentas bancarias vinculadas.
* Estructura de cada elemento:
1 {
2 'id': int,
3 'bank': String,
4 'type': String, // Ejemplo: 'Checking', 'Savings'
5 'last4': String,
6 'isPrimary': bool,
7 }
---
11. lib/screens/worker/worker_profile/compliance/tax_forms_screen.dart
* Variable: _forms
* Propósito: Lista de formularios de impuestos requeridos.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'subtitle': String,
5 'description': String,
6 'status': String, // Ejemplo: 'submitted', 'not_started', 'draft'
7 'icon': String, // Icono (emoji)
8 }
---
12. lib/screens/worker/worker_profile/compliance/certificates_screen.dart
* Variable: _certificates
* Propósito: Lista de certificados del trabajador.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'name': String,
4 'icon': IconData,
5 'color': Color,
6 'description': String,
7 'status': String, // Ejemplo: 'complete', 'expiring', 'not_started'
8 'expiry': String?, // Formato ISO 8601, puede ser nulo
9 }
---
13. lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart
* Variable: _profiles
* Propósito: Lista de perfiles de trabajadores para la tabla de clasificación.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'name': String,
4 'photo_url': String?,
5 'xp': int,
6 'level': String, // Ejemplo: 'Krower III'
7 'user_id': String,
8 }
---
14. lib/screens/worker/payments_screen.dart
* Variable: _recentPayments
* Propósito: Lista de registros de pagos recientes (detallados).
* Estructura de cada elemento:
1 {
2 'date': String, // Ejemplo: 'Sat, Dec 6'
3 'title': String,
4 'location': String,
5 'address': String,
6 'workedTime': String,
7 'amount': double,
8 'status': String, // Ejemplo: 'paid'
9 'hours': int,
10 'rate': double,
11 }
---
15. lib/screens/worker/availability_screen.dart
* Variable: _timeSlots
* Propósito: Define los rangos horarios (mañana, tarde, noche) para configurar la disponibilidad.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'label': String,
4 'time': String,
5 'icon': IconData,
6 'bg': Color,
7 'iconColor': Color,
8 }
---
16. lib/screens/worker/earnings_screen.dart
* Variable: _recentPayments
* Propósito: Lista de resúmenes de pagos recientes (menos detallado que el de payments_screen.dart).
* Estructura de cada elemento:
1 {
2 'date': String, // Ejemplo: 'Dec 15'
3 'amount': double,
4 'shifts': int,
5 'status': String, // Ejemplo: 'paid'
6 }
---
Esta lista detallada te da una visión completa de cómo se están manejando los datos en diferentes partes de la aplicación. Como te mencioné antes, la mayoría de estos son mocks locales y
no clases formales.
Auto (Gemini 2.5) /model

View File

@@ -1,859 +0,0 @@
lib/services/mock_service.dart
En este archivo, el uso de Map<String, dynamic> se encuentra como un parámetro en una función.
1. Parámetro de Función: Future<void> createWorkerProfile(Map<String, dynamic> data)
* Propósito: Esta función simula la creación de un perfil de trabajador en el backend. Acepta un Map llamado data que contiene toda la información del perfil del nuevo trabajador.
* Estructura del `Map` esperado: Aunque la función en sí misma no revela la estructura, el lugar donde se la llama (profile_setup_screen.dart) nos muestra exactamente qué datos se envían:
1 {
2 'full_name': String,
3 'bio': String,
4 'preferred_locations': List<String>,
5 'max_distance_miles': double,
6 'skills': List<String>,
7 'industries': List<String>,
8 }
---
A continuación, analizaré el siguiente archivo: lib/widgets/shift_card.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/widgets/shift_card.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Continuando el análisis:
---
lib/widgets/shift_card.dart
En este widget, el uso de Map<String, dynamic> se encuentra como el tipo de retorno de una función.
1. Tipo de Retorno de Función: Map<String, dynamic> _calculateDuration()
* Propósito: Esta función calcula la duración de un turno en horas y devuelve también una cadena de texto para el tiempo de descanso.
* Estructura del `Map` devuelto:
1 {
2 'hours': int, // La duración calculada del turno en horas.
3 'breakTime': String, // Una cadena de texto que representa el tiempo de descanso, ej: '1 hour'.
4 }
---
Siguiente archivo: lib/screens/auth/profile_setup_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../profile_setup_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ De acuerdo, aquí está el análisis del archivo.
---
lib/screens/auth/profile_setup_screen.dart
Este archivo contiene dos usos distintos de Map<String, dynamic>:
1. Variable: static const List<Map<String, dynamic>> _steps
* Propósito: Define los pasos en el asistente de creación de perfiles.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'icon': IconData
5 }
2. Argumento de Función: El Map anónimo pasado a mockService.createWorkerProfile({ ... })
* Propósito: Recopilar y enviar los datos del perfil del nuevo usuario al servicio (en este caso, un mock).
* Estructura:
1 {
2 'full_name': String,
3 'bio': String,
4 'preferred_locations': List<String>,
5 'max_distance_miles': double,
6 'skills': List<String>,
7 'industries': List<String>
8 }
---
Siguiente archivo: lib/screens/worker/worker_profile_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker_profile_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Claro, aquí está el análisis del archivo.
---
lib/screens/worker/worker_profile_screen.dart
Este archivo contiene dos variables Map<String, dynamic> para datos de simulación (mock).
1. Variable: final Map<String, dynamic> _user
* Propósito: Almacena los datos básicos de identificación del usuario para mostrarlos en la pantalla de perfil.
* Estructura:
1 {
2 'full_name': String,
3 'email': String,
4 }
2. Variable: final Map<String, dynamic> _profile
* Propósito: Almacena estadísticas y atributos detallados del trabajador para mostrarlos en la pantalla de perfil.
* Estructura:
1 {
2 'level': String,
3 'photo_url': String?,
4 'total_shifts': int,
5 'average_rating': double,
6 'on_time_rate': int,
7 'no_show_count': int,
8 'cancellation_count': int,
9 'reliability_score': int,
10 'phone': String,
11 'skills': List, // Mockeado como una lista vacía
12 }
---
Siguiente archivo: lib/screens/worker/benefits_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/benefits_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ De acuerdo, aquí está el análisis del siguiente archivo.
---
lib/screens/worker/benefits_screen.dart
Este archivo utiliza Map<String, dynamic> para gestionar los datos de los beneficios del trabajador.
1. Variable: final List<Map<String, dynamic>> _benefitsData
* Propósito: Contiene los datos de simulación (mock) para los diferentes beneficios del trabajador, como días de enfermedad y vacaciones.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'current': int,
5 'total': int,
6 'color': Color,
7 'description': String,
8 'history': List<Map<String, dynamic>>, // <- Lista anidada
9 'requestLabel': String,
10 'notice': String?, // Opcional
11 }
* Estructura anidada de `history`:
1 {
2 'date': String,
3 'status': String
4 }
2. Parámetro de Función: void _handleRequest(Map<String, dynamic> benefit)
* Propósito: Maneja la acción cuando un usuario solicita un beneficio, tomando uno de los objetos Map de _benefitsData como entrada.
* Estructura: La misma que la de los elementos en _benefitsData.
3. Parámetro de Widget: final Map<String, dynamic> benefit (en _BenefitCard)
* Propósito: El widget _BenefitCard utiliza un Map de _benefitsData para mostrar los detalles de un beneficio.
* Estructura: La misma que la de los elementos en _benefitsData.
---
Siguiente archivo: lib/screens/worker/worker_profile/compliance/documents_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../documents_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/compliance/documents_screen.dart
Este archivo, similar a otros, usa Map<String, dynamic> para la gestión de datos de UI.
1. Variable: final List<Map<String, dynamic>> _requiredDocs
* Propósito: Almacena la lista de documentos de cumplimiento requeridos para el trabajador.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'name': String,
4 'description': String,
5 'status': String // Ejemplo: 'verified', 'pending', 'missing'
6 }
2. Parámetro de Función: Widget _buildDocumentCard(Map<String, dynamic> doc)
* Propósito: Es una función de construcción que crea una tarjeta de UI para un solo documento, tomando como entrada un Map de la lista _requiredDocs.
* Estructura: La estructura del parámetro doc es la misma que la de los elementos en la lista _requiredDocs.
---
Siguiente archivo: lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile l/.../emergency_contact_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart
Este archivo gestiona los contactos de emergencia del trabajador.
1. Variable: final List<Map<String, dynamic>> _contacts
* Propósito: Almacena una lista de los contactos de emergencia del trabajador.
* Estructura de cada elemento:
1 {
2 'name': String,
3 'phone': String,
4 'relationship': String // Ejemplo: 'family', 'spouse', 'friend', 'other'
5 }
2. Parámetro de Función: Widget _buildContactForm(int index, Map<String, dynamic> contact)
* Propósito: Es una función de construcción que toma un Map de un solo contacto de la lista _contacts para crear un formulario para ese contacto.
* Estructura: La estructura del parámetro contact es la misma que la de los elementos en la lista _contacts.
3. Parámetro de Función: void _updateContact(int index, String field, dynamic value)
* Propósito: Esta función actualiza un campo dentro de un Map de contacto específico en la lista _contacts. El value es dynamic ya que puede ser un String de un TextField o de un
DropdownButton.
* Estructura: No tiene una estructura de mapa específica, pero opera sobre los mapas dentro de la lista _contacts.
---
Siguiente archivo: lib/screens/worker/worker_profile/compliance/certificates_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../certificates_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Okay, aquí está el análisis del archivo certificates_screen.dart.
---
lib/screens/worker/worker_profile/compliance/certificates_screen.dart
Este archivo maneja los certificados de cumplimiento del trabajador.
1. Variable: final List<Map<String, dynamic>> _certificates
* Propósito: Almacena una lista de los certificados de cumplimiento del trabajador (por ejemplo, verificación de antecedentes, manipulador de alimentos).
* Estructura de cada elemento:
1 {
2 'id': String,
3 'name': String,
4 'icon': IconData,
5 'color': Color,
6 'description': String,
7 'status': String, // Ejemplo: 'complete', 'expiring', 'not_started'
8 'expiry': String?, // Cadena de fecha ISO 8601 que puede ser nula
9 }
2. Parámetro de Función: Widget _buildCertificateCard(Map<String, dynamic> cert)
* Propósito: Es una función de construcción que crea una tarjeta de UI para un solo certificado, tomando como entrada un Map de la lista _certificates.
* Estructura: La estructura del parámetro cert es la misma que la de los elementos en _certificates.
3. Parámetro de Función: void _showUploadModal(BuildContext context, Map<String, dynamic>? cert)
* Propósito: Esta función muestra una hoja modal para subir un certificado. Acepta un Map opcional cert. Si se proporciona cert, el modal es para renovar o ver ese certificado específico.
Si es null, es para subir un nuevo certificado sin categoría.
* Estructura: La estructura del parámetro cert es la misma que la de los elementos en _certificates.
---
Siguiente archivo: lib/screens/worker/worker_profile/compliance/tax_forms_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../tax_forms_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/compliance/tax_forms_screen.dart
Este archivo gestiona la lista de formularios de impuestos del trabajador.
1. Variable: final List<Map<String, dynamic>> _forms
* Propósito: Almacena una lista de los formularios de impuestos requeridos.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'subtitle': String,
5 'description': String,
6 'status': String, // Ejemplo: 'submitted', 'not_started'
7 'icon': String, // Un emoji usado como icono
8 }
2. Parámetro de Función: Widget _buildFormCard(Map<String, dynamic> form)
* Propósito: Es una función de construcción que crea una tarjeta de UI para un solo formulario de impuestos, tomando como entrada un Map de la lista _forms.
* Estructura: La estructura del parámetro form es la misma que la de los elementos en la lista _forms.
---
Siguiente archivo: lib/screens/worker/worker_profile/onboarding/personal_info_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../personal_info_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/onboarding/personal_info_screen.dart
Este archivo contiene los datos de simulación (mock) para el formulario de información personal.
1. Variable: final Map<String, dynamic> _user
* Propósito: Almacena los datos básicos del usuario para mostrarlos en el formulario.
* Estructura:
1 {
2 'full_name': String,
3 'email': String,
4 'photo_url': String?, // Admite valores nulos
5 }
* Nota: Esta versión del mock _user es ligeramente diferente a la de worker_profile_screen.dart, ya que incluye el campo photo_url.
---
Siguiente archivo: lib/screens/worker/worker_profile/onboarding/attire_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../attire_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/onboarding/attire_screen.dart
Este archivo maneja el "armario" o la vestimenta que posee el trabajador.
1. Variable: final List<Map<String, dynamic>> _attireOptions
* Propósito: Proporciona una lista de opciones de vestimenta seleccionables para el trabajador.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'label': String,
4 'icon': IconData,
5 'imageUrl': String,
6 }
(Nota: Este archivo también contiene `Map<String, String>` y `Map<String, bool>` para la gestión del estado, pero no coinciden con el tipo `Map<String, dynamic>`).
---
Siguiente archivo: lib/screens/worker/worker_profile/compliance/taxforms/form_i9_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../form_i9_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/compliance/taxforms/form_i9_screen.dart
Este archivo contiene el formulario I-9 y utiliza mapas para gestionar los datos del formulario y los pasos del asistente.
1. Variable: final Map<String, dynamic> _formData
* Propósito: Almacena el estado de los campos del formulario I-9 mientras el usuario los completa.
* Estructura:
1 {
2 'lastName': String,
3 'firstName': String,
4 'middleInitial': String,
5 'otherLastNames': String,
6 'address': String,
7 'aptNumber': String,
8 'city': String,
9 'state': String?, // Admite valores nulos
10 'zipCode': String,
11 'dateOfBirth': String,
12 'ssn': String,
13 'email': String,
14 'phone': String,
15 'citizenshipStatus': String,
16 'uscisNumber': String,
17 'i94Number': String,
18 'foreignPassportNumber': String,
19 'countryOfIssuance': String,
20 'expirationDate': String,
21 }
2. Variable: final List<Map<String, String>> _steps
* Nota: Aunque este no es un Map<String, dynamic>, sigue un patrón similar.
* Propósito: Define los pasos del asistente del formulario I-9.
* Estructura de cada elemento:
1 {
2 'title': String,
3 'subtitle': String,
4 }
3. Parámetro de Función: void _updateField(String key, dynamic value)
* Propósito: Esta función actualiza un campo en el mapa _formData. El value es dynamic porque puede ser un String de un TextField o de un DropdownButton.
---
Siguiente archivo: lib/screens/worker/worker_profile/compliance/taxforms/form_w4_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../form_w4_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Claro, aquí tienes el análisis.
---
lib/screens/worker/worker_profile/compliance/taxforms/form_w4_screen.dart
Este archivo contiene el formulario W-4 y, de manera similar al I-9, utiliza mapas para gestionar los datos.
1. Variable: final Map<String, dynamic> _formData
* Propósito: Almacena el estado de los campos del formulario W-4 mientras el usuario los completa.
* Estructura:
1 {
2 'firstName': String,
3 'lastName': String,
4 'address': String,
5 'cityStateZip': String,
6 'ssn': String,
7 'filingStatus': String,
8 'multipleJobs': bool,
9 'qualifyingChildren': int,
10 'otherDependents': int,
11 'otherIncome': String, // Representa una cantidad en dólares
12 'deductions': String, // Representa una cantidad en dólares
13 'extraWithholding': String, // Representa una cantidad en dólares
14 }
2. Variable: final List<Map<String, String>> _steps
* Nota: Aunque este es un List<Map<String, String>>, sigue un patrón de uso de mapas para definir metadatos.
* Propósito: Define los pasos del asistente del formulario W-4.
* Estructura de cada elemento:
1 {
2 'title': String,
3 'subtitle': String,
4 }
3. Parámetro de Función: void _updateField(String key, dynamic value)
* Propósito: Esta función actualiza un campo en el mapa _formData. El value es dynamic porque puede ser un String, bool, o int dependiendo del campo del formulario que se esté
actualizando.
---
Siguiente archivo: lib/screens/worker/worker_profile/level_up/trainings_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../trainings_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ De acuerdo.
---
lib/screens/worker/worker_profile/level_up/trainings_screen.dart
Este archivo contiene la lista de cursos de capacitación para los trabajadores.
1. Variable: final List<Map<String, dynamic>> _courses
* Propósito: Almacena una lista de los cursos de capacitación disponibles.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'description': String,
5 'duration_minutes': int,
6 'xp_reward': int,
7 'thumbnail_url': String?, // Admite valores nulos
8 'progress_percent': int,
9 'completed': bool,
10 }
---
Siguiente archivo: lib/screens/worker/worker_profile/finances/time_card_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../time_card_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Okay, analicemos este archivo.
---
lib/screens/worker/worker_profile/finances/time_card_screen.dart
Este archivo se utiliza para mostrar el historial de horas trabajadas del empleado.
1. Variable: final List<Map<String, dynamic>> _timesheets
* Propósito: Almacena una lista de registros de hojas de tiempo (timesheets) para los turnos trabajados.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'shift_id': String,
4 'date': String, // Cadena en formato ISO 8601
5 'actual_start': String,
6 'actual_end': String,
7 'total_hours': double,
8 'hourly_rate': double,
9 'total_pay': double,
10 'status': String, // Ejemplo: 'pending', 'approved', 'paid'
11 'shift_title': String,
12 'client_name': String,
13 'location': String,
14 }
2. Parámetro de Función: Widget _buildShiftHistory(List<Map<String, dynamic>> timesheets)
* Propósito: Es una función de construcción que toma una lista de mapas de hojas de tiempo para renderizar la sección de historial.
* Estructura: La estructura del parámetro timesheets es una lista de mapas, donde cada mapa tiene la misma estructura que los elementos en _timesheets.
3. Parámetro de Función: Widget _buildTimesheetCard(Map<String, dynamic> timesheet)
* Propósito: Es una función de construcción que crea una tarjeta de UI para un único registro de hoja de tiempo de la lista _timesheets.
* Estructura: La estructura del parámetro timesheet es la misma que la de los elementos en _timesheets.
---
Siguiente archivo: lib/screens/worker/worker_profile/support/faqs_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../support/faqs_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/support/faqs_screen.dart
Este archivo contiene los datos para la pantalla de Preguntas Frecuentes (FAQs).
1. Variable: final List<Map<String, dynamic>> _faqData
* Propósito: Almacena los datos para las preguntas frecuentes, organizados por categoría.
* Estructura de cada elemento (categoría):
1 {
2 'category': String,
3 'questions': List<Map<String, dynamic>>, // <- Lista anidada
4 }
* Estructura anidada de `questions`:
1 {
2 'q': String, // La pregunta
3 'a': String, // La respuesta
4 }
(Nota: Este archivo también contiene un `Map<String, bool>` llamado `_openItems` para gestionar el estado de la UI, pero no coincide con el tipo `Map<String, dynamic>`).
---
Siguiente archivo: lib/screens/worker/worker_profile/level_up/krow_university_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../krow_university_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/level_up/krow_university_screen.dart
Este archivo es para la sección de "KROW University" y contiene varios usos de Map<String, dynamic>.
1. Variable: final Map<String, dynamic> _profile
* Propósito: Almacena un subconjunto de los datos del perfil del trabajador que son relevantes para la sección de universidad/capacitación.
* Estructura:
1 {
2 'level': String,
3 'xp': int,
4 'nextLevelXp': int,
5 'completed_courses': int,
6 }
* Nota: Este mapa _profile es inconsistente con el que se encuentra en worker_profile_screen.dart, lo que demuestra la necesidad de un modelo de datos unificado.
2. Variable: final List<Map<String, dynamic>> _categories
* Propósito: Define las categorías para los cursos de la universidad.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'label': String,
4 'icon': IconData,
5 }
3. Variable: final List<Map<String, dynamic>> _courses
* Propósito: Almacena una lista de los cursos universitarios disponibles.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'title': String,
4 'description': String,
5 'category': String,
6 'duration_minutes': int,
7 'xp_reward': int,
8 'level_required': String,
9 'is_certification': bool,
10 'progress_percent': int,
11 'completed': bool,
12 }
4. Parámetro de Función: Widget _buildCoursesGrid(List<Map<String, dynamic>> courses)
* Propósito: Es una función de construcción que toma una lista de mapas de cursos (filtrados de _courses) para renderizarlos en una cuadrícula.
* Estructura: La estructura del parámetro courses es una lista de mapas, donde cada mapa tiene la misma estructura que los elementos en _courses.
---
Siguiente archivo: lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../leaderboard_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos con el siguiente archivo.
---
lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart
Este archivo es para la pantalla de la tabla de clasificación (Leaderboard) y utiliza mapas extensivamente para manejar los datos de los perfiles.
1. Variable: final List<Map<String, dynamic>> _profiles
* Propósito: Almacena los datos de simulación (mock) para la tabla de clasificación, representando diferentes perfiles de trabajadores.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'name': String,
4 'photo_url': String?, // Admite valores nulos
5 'xp': int,
6 'level': String,
7 'user_id': String,
8 }
2. Variable: final sortedProfiles = List<Map<String, dynamic>>.from(_profiles)
* Propósito: Crea una copia mutable de la lista _profiles que puede ser ordenada por XP.
* Estructura: Los elementos tienen la misma estructura que en _profiles.
3. Parámetro de Función: Widget _buildPodium(List<Map<String, dynamic>> topThree)
* Propósito: Es una función de construcción que toma los tres perfiles principales para renderizar la sección del podio.
* Estructura: La lista topThree contiene mapas con la misma estructura que en _profiles.
4. Parámetro de Función: Widget _buildPodiumItem(Map<String, dynamic> profile, ...)
* Propósito: Es una función de construcción para crear la UI de un solo perfil en el podio.
* Estructura: El parámetro profile tiene la misma estructura que un elemento en _profiles.
5. Parámetro de Función: Widget _buildRestList(List<Map<String, dynamic>> rest, ...)
* Propósito: Es una función de construcción que toma el resto de los perfiles (que no están en el top tres) para renderizarlos como una lista estándar.
* Estructura: La lista rest contiene mapas con la misma estructura que en _profiles.
---
Siguiente archivo: lib/screens/worker/worker_profile/finances/bank_account_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../bank_account_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/worker_profile/finances/bank_account_screen.dart
Este archivo maneja las cuentas bancarias del trabajador.
1. Variable: final List<Map<String, dynamic>> _accounts
* Propósito: Almacena una lista de las cuentas bancarias vinculadas del trabajador.
* Estructura de cada elemento:
1 {
2 'id': int,
3 'bank': String,
4 'type': String, // Ejemplo: 'Checking', 'Savings'
5 'last4': String,
6 'isPrimary': bool,
7 }
2. Parámetro de Función: Widget _buildAccountCard(Map<String, dynamic> account)
* Propósito: Es una función de construcción que crea una tarjeta de UI para una sola cuenta bancaria, tomando como entrada un Map de la lista _accounts.
* Estructura: La estructura del parámetro account es la misma que la de los elementos en la lista _accounts.
---
Siguiente archivo: lib/screens/worker/payments_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/payments_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Sigamos.
---
lib/screens/worker/payments_screen.dart
Este archivo muestra el historial de pagos y ganancias del trabajador.
1. Variable: final List<Map<String, dynamic>> _recentPayments
* Propósito: Almacena los datos de simulación (mock) para una lista de registros de pagos recientes.
* Estructura de cada elemento:
1 {
2 'date': String,
3 'title': String,
4 'location': String,
5 'address': String,
6 'workedTime': String,
7 'amount': double,
8 'status': String, // Ejemplo: 'paid'
9 'hours': int,
10 'rate': int, // Nota: Esto es un int en el mock, pero probablemente debería ser double como en otros lugares.
11 }
* Nota: El widget PaymentHistoryItem que consume estos datos espera que rate sea un double, por lo que el int en los datos del mock es probablemente una pequeña inconsistencia.
---
Siguiente archivo: lib/screens/worker/availability_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../availability_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Okay, aquí está el análisis del archivo de disponibilidad.
---
lib/screens/worker/availability_screen.dart
Este archivo maneja la configuración de la disponibilidad del trabajador.
1. Variable: final List<Map<String, dynamic>> _timeSlots
* Propósito: Define las propiedades de los diferentes rangos horarios (Mañana, Tarde, Noche) que el usuario puede seleccionar.
* Estructura de cada elemento:
1 {
2 'id': String,
3 'label': String,
4 'time': String, // Ejemplo: '4:00 AM - 12:00 PM'
5 'icon': IconData,
6 'bg': Color,
7 'iconColor': Color,
8 }
(Nota: Este archivo también utiliza `Map<String, bool>` y `Map<String, Map<String, bool>>` para gestionar el estado de la disponibilidad, pero no coinciden con el tipo `Map<String,
dynamic>`).
---
Siguiente archivo: lib/screens/worker/clock_in_screen.dart.
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ✓ ReadFile lib/.../worker/clock_in_screen.dart │
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✦ Okay, aquí está el análisis del último archivo de la lista.
---
lib/screens/worker/clock_in_screen.dart
Este archivo gestiona la pantalla de fichaje (check-in/check-out) del trabajador.
1. Variable: final List<Map<String, dynamic>> _recentActivity
* Propósito: Almacena una lista de las actividades recientes de fichaje del trabajador.
* Estructura de cada elemento:
1 {
2 'date': DateTime,
3 'start': String, // Ejemplo: '09:00 AM'
4 'end': String, // Ejemplo: '05:00 PM'
5 'hours': String, // Ejemplo: '8h'
6 }
---
✦ He analizado lib/screens/worker/worker_profile/support/messages_screen.dart.
Este archivo contiene dos estructuras principales de List<Map<String, dynamic>>:
1. Nombre de la variable: _conversations
* Propósito: Representa una lista de conversaciones de chat.
* Estructura de cada elemento `Map<String, dynamic>` (que representa una conversación individual):
1 {
2 'sender_id': String, // ID del remitente/otra parte en la conversación
3 'sender_name': String, // Nombre visible del remitente/otra parte
4 'lastMessage': String, // Contenido del último mensaje en la conversación
5 'lastTime': DateTime, // Marca de tiempo del último mensaje
6 'unread': int, // Número de mensajes no leídos en la conversación
7 'messages': List<Map<String, dynamic>>, // Lista anidada de mensajes individuales dentro de la conversación
8 }
* Estructura Anidada (`messages`): Cada elemento en la lista messages tiene:
1 {
2 'content': String, // El contenido del mensaje
3 'sender_id': String, // El ID del remitente de este mensaje específico
4 }
El siguiente archivo es: lib/screens/worker/worker_profile/onboarding/attire_screen.dart.
---
He finalizado el análisis de todos los archivos que contenían Map<String, dynamic>. Has podido ver que este tipo de mapa se usa de forma muy extendida en todo el proyecto para simular
datos, pasar parámetros y definir el estado de los widgets.
La principal conclusión es la falta de modelos de datos formales (clases Dart), lo que lleva a la duplicación e inconsistencia de las estructuras de datos en diferentes pantallas.
Mi recomendación principal, como ingeniero senior, sería refactorizar este código para usar clases de modelo fuertemente tipadas. Esto mejoraría enormemente la mantenibilidad, la
legibilidad y la seguridad del código, reduciendo errores a largo plazo.
¿Hay alguna de estas estructuras que te interese formalizar en una clase Dart ahora? O, ¿tienes alguna otra pregunta?
Using: 1 GEMINI.md file

View File

@@ -1,594 +0,0 @@
# Análisis Exhaustivo de `Map<String, dynamic>` en el Proyecto
Este documento detalla todos los usos del tipo `Map<String, dynamic>` encontrados en los archivos `.dart` del proyecto, incluyendo variables, listas, parámetros y tipos de retorno de funciones.
---
### `lib/widgets/shift_card.dart`
#### 1. Tipo de Retorno de Función: `Map<String, dynamic> _calculateDuration()`
* **Propósito:** Calcula la duración de un turno en horas y devuelve también una cadena de texto para el tiempo de descanso.
* **Estructura del `Map` devuelto:**
```dart
{
'hours': int,
'breakTime': String,
}
```
---
### `lib/services/mock_service.dart`
#### 1. Parámetro de Función: `Future<void> createWorkerProfile(Map<String, dynamic> data)`
* **Propósito:** Simula la creación de un perfil de trabajador. Acepta un `Map` llamado `data` que contiene la información del perfil del nuevo trabajador.
* **Estructura Inferida (por su uso):**
```dart
{
'full_name': String,
'bio': String,
'preferred_locations': List<String>,
'max_distance_miles': double,
'skills': List<String>,
'industries': List<String>,
}
```
---
### `lib/screens/auth/profile_setup_screen.dart`
#### 1. Variable: `static const List<Map<String, dynamic>> _steps`
* **Propósito:** Define los pasos en el asistente de creación de perfiles.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'title': String,
'icon': IconData
}
```
#### 2. Argumento de Función: (Anónimo) en `mockService.createWorkerProfile`
* **Propósito:** Recopila y envía los datos del perfil del nuevo usuario al servicio mock.
* **Estructura:**
```dart
{
'full_name': String,
'bio': String,
'preferred_locations': List<String>,
'max_distance_miles': double,
'skills': List<String>,
'industries': List<String>
}
```
---
### `lib/screens/worker/benefits_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _benefitsData`
* **Propósito:** Contiene los datos de simulación (mock) para los diferentes beneficios del trabajador.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'title': String,
'current': int,
'total': int,
'color': Color,
'description': String,
'history': List<Map<String, dynamic>>, // <- Lista anidada
'requestLabel': String,
'notice': String?,
}
```
* **Estructura anidada de `history`:**
```dart
{
'date': String,
'status': String
}
```
#### 2. Parámetro de Función: `void _handleRequest(Map<String, dynamic> benefit)`
* **Propósito:** Maneja la acción cuando un usuario solicita un beneficio.
* **Estructura:** La misma que los elementos de `_benefitsData`.
#### 3. Parámetro de Widget: `final Map<String, dynamic> benefit`
* **Propósito:** El widget `_BenefitCard` recibe un `Map` para mostrar los detalles de un beneficio.
* **Estructura:** La misma que los elementos de `_benefitsData`.
---
### `lib/screens/worker/worker_profile_screen.dart`
#### 1. Variable: `final Map<String, dynamic> _user`
* **Propósito:** Almacena datos básicos de identificación del usuario.
* **Estructura:**
```dart
{
'full_name': String,
'email': String,
}
```
#### 2. Variable: `final Map<String, dynamic> _profile`
* **Propósito:** Almacena estadísticas y atributos detallados del perfil del trabajador.
* **Estructura:**
```dart
{
'level': String,
'photo_url': String?,
'total_shifts': int,
'average_rating': double,
'on_time_rate': int,
'no_show_count': int,
'cancellation_count': int,
'reliability_score': int,
'phone': String,
'skills': List,
}
```
---
### `lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _contacts`
* **Propósito:** Almacena una lista de los contactos de emergencia.
* **Estructura de cada elemento:**
```dart
{
'name': String,
'phone': String,
'relationship': String
}
```
#### 2. Parámetro de Función: `Widget _buildContactForm(int index, Map<String, dynamic> contact)`
* **Propósito:** Construye el widget del formulario para un contacto.
* **Estructura:** La misma que los elementos de `_contacts`.
---
### `lib/screens/worker/worker_profile/onboarding/personal_info_screen.dart`
#### 1. Variable: `final Map<String, dynamic> _user`
* **Propósito:** Almacena los datos básicos del usuario para el formulario.
* **Estructura:**
```dart
{
'full_name': String,
'email': String,
'photo_url': String?,
}
```
---
### `lib/screens/worker/worker_profile/finances/time_card_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _timesheets`
* **Propósito:** Almacena una lista de registros de hojas de tiempo (timesheets).
* **Estructura de cada elemento:**
```dart
{
'id': String,
'shift_id': String,
'date': String, // ISO 8601
'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,
}
```
#### 2. Parámetro de Función: `Widget _buildShiftHistory(List<Map<String, dynamic>> timesheets)`
* **Propósito:** Construye la sección de historial de turnos.
* **Estructura:** Una lista donde cada elemento tiene la estructura de `_timesheets`.
#### 3. Parámetro de Función: `Widget _buildTimesheetCard(Map<String, dynamic> timesheet)`
* **Propósito:** Construye la tarjeta para una sola hoja de tiempo.
* **Estructura:** La misma que los elementos de `_timesheets`.
---
### `lib/screens/worker/worker_profile/finances/bank_account_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _accounts`
* **Propósito:** Almacena una lista de las cuentas bancarias vinculadas.
* **Estructura de cada elemento:**
```dart
{
'id': int,
'bank': String,
'type': String,
'last4': String,
'isPrimary': bool,
}
```
#### 2. Parámetro de Función: `Widget _buildAccountCard(Map<String, dynamic> account)`
* **Propósito:** Construye la tarjeta para una sola cuenta bancaria.
* **Estructura:** La misma que los elementos de `_accounts`.
---
### `lib/screens/worker/worker_profile/level_up/trainings_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _courses`
* **Propósito:** Almacena una lista de cursos de capacitación.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'title': String,
'description': String,
'duration_minutes': int,
'xp_reward': int,
'thumbnail_url': String?,
'progress_percent': int,
'completed': bool,
}
```
---
### `lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _profiles`
* **Propósito:** Almacena los datos de los perfiles para la tabla de clasificación.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'name': String,
'photo_url': String?,
'xp': int,
'level': String,
'user_id': String,
}
```
#### 2. Variable: `final sortedProfiles = List<Map<String, dynamic>>.from(_profiles)`
* **Propósito:** Crea una copia mutable de la lista de perfiles para ordenarla.
* **Estructura:** Los elementos tienen la misma estructura que `_profiles`.
#### 3. Parámetro de Función: `Widget _buildPodium(List<Map<String, dynamic>> topThree)`
* **Propósito:** Construye la sección del podio con los 3 mejores perfiles.
* **Estructura:** Una lista donde cada mapa tiene la estructura de un elemento de `_profiles`.
#### 4. Parámetro de Función: `Map<String, dynamic> profile` (en `_buildPodiumItem`)
* **Propósito:** Construye el item para un perfil en el podio.
* **Estructura:** La misma que un elemento de `_profiles`.
#### 5. Parámetro de Función: `Widget _buildRestList(List<Map<String, dynamic>> rest, ...)`
* **Propósito:** Construye la lista para el resto de los perfiles.
* **Estructura:** Una lista donde cada mapa tiene la estructura de un elemento de `_profiles`.
---
### `lib/screens/worker/worker_profile/onboarding/attire_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _attireOptions`
* **Propósito:** Define las opciones de vestimenta que un trabajador puede seleccionar.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'label': String,
'icon': IconData,
'imageUrl': String,
}
```
---
### `lib/screens/worker/worker_profile/support/faqs_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _faqData`
* **Propósito:** Almacena los datos de las preguntas frecuentes, organizados por categoría.
* **Estructura de cada elemento (categoría):**
```dart
{
'category': String,
'questions': List<Map<String, dynamic>>, // <- Lista anidada
}
```
* **Estructura anidada de `questions`:**
```dart
{
'q': String, // Pregunta
'a': String, // Respuesta
}
```
---
### `lib/screens/worker/worker_profile/support/messages_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _conversations`
* **Propósito:** Contiene los datos de simulación para las conversaciones de chat.
* **Estructura de cada elemento (conversación):**
```dart
{
'sender_id': String,
'sender_name': String,
'lastMessage': String,
'lastTime': DateTime,
'unread': int,
'messages': List<Map<String, dynamic>>, // <- Lista anidada
}
```
* **Estructura anidada de `messages`:**
```dart
{
'content': String,
'sender_id': String,
}
```
#### 2. Variable: `Map<String, dynamic>? _selectedChat`
* **Propósito:** Almacena la conversación que el usuario ha seleccionado para ver.
* **Estructura:** La misma que un elemento de `_conversations`.
---
### `lib/screens/worker/payments_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _recentPayments`
* **Propósito:** Almacena registros detallados de pagos recientes.
* **Estructura de cada elemento:**
```dart
{
'date': String,
'title': String,
'location': String,
'address': String,
'workedTime': String,
'amount': double,
'status': String,
'hours': int,
'rate': int, // Debería ser double
}
```
---
### `lib/screens/worker/worker_profile/compliance/documents_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _requiredDocs`
* **Propósito:** Almacena la lista de documentos de cumplimiento requeridos.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'name': String,
'description': String,
'status': String,
}
```
#### 2. Parámetro de Función: `Widget _buildDocumentCard(Map<String, dynamic> doc)`
* **Propósito:** Construye la tarjeta de UI para un solo documento.
* **Estructura:** La misma que los elementos de `_requiredDocs`.
---
### `lib/screens/worker/worker_profile/compliance/tax_forms_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _forms`
* **Propósito:** Almacena la lista de formularios de impuestos.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'title': String,
'subtitle': String,
'description': String,
'status': String,
'icon': String, // Emoji
}
```
#### 2. Parámetro de Función: `Widget _buildFormCard(Map<String, dynamic> form)`
* **Propósito:** Construye la tarjeta de UI para un solo formulario.
* **Estructura:** La misma que los elementos de `_forms`.
---
### `lib/screens/worker/availability_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _timeSlots`
* **Propósito:** Define las propiedades de los diferentes rangos horarios para la disponibilidad.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'label': String,
'time': String,
'icon': IconData,
'bg': Color,
'iconColor': Color,
}
```
---
### `lib/screens/worker/worker_profile/compliance/certificates_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _certificates`
* **Propósito:** Almacena la lista de certificados de cumplimiento del trabajador.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'name': String,
'icon': IconData,
'color': Color,
'description': String,
'status': String,
'expiry': String?, // ISO 8601
}
```
#### 2. Parámetro de Función: `Widget _buildCertificateCard(Map<String, dynamic> cert)`
* **Propósito:** Construye la tarjeta de UI para un solo certificado.
* **Estructura:** La misma que los elementos de `_certificates`.
#### 3. Parámetro de Función: `void _showUploadModal(BuildContext context, Map<String, dynamic>? cert)`
* **Propósito:** Muestra un modal para subir un certificado.
* **Estructura:** La misma que los elementos de `_certificates`.
---
### `lib/screens/worker/worker_profile/compliance/taxforms/form_i9_screen.dart`
#### 1. Variable: `final Map<String, dynamic> _formData`
* **Propósito:** Almacena el estado de los campos del formulario I-9.
* **Estructura:**
```dart
{
'lastName': String,
'firstName': String,
'middleInitial': String,
'otherLastNames': String,
'address': String,
'aptNumber': String,
'city': String,
'state': String?,
'zipCode': String,
'dateOfBirth': String,
'ssn': String,
'email': String,
'phone': String,
'citizenshipStatus': String,
'uscisNumber': String,
'i94Number': String,
'foreignPassportNumber': String,
'countryOfIssuance': String,
'expirationDate': String,
}
```
---
### `lib/screens/worker/worker_profile/compliance/taxforms/form_w4_screen.dart`
#### 1. Variable: `final Map<String, dynamic> _formData`
* **Propósito:** Almacena el estado de los campos del formulario W-4.
* **Estructura:**
```dart
{
'firstName': String,
'lastName': String,
'address': String,
'cityStateZip': String,
'ssn': String,
'filingStatus': String,
'multipleJobs': bool,
'qualifyingChildren': int,
'otherDependents': int,
'otherIncome': String,
'deductions': String,
'extraWithholding': String,
}
```
---
### `lib/screens/worker/worker_profile/level_up/krow_university_screen.dart`
#### 1. Variable: `final Map<String, dynamic> _profile`
* **Propósito:** Almacena un subconjunto de datos del perfil relevantes para la universidad.
* **Estructura:**
```dart
{
'level': String,
'xp': int,
'badges': List<String>,
}
```
#### 2. Variable: `final List<Map<String, dynamic>> _levels`
* **Propósito:** Define los diferentes niveles de Krower y sus propiedades.
* **Estructura de cada elemento:**
```dart
{
'name': String,
'xpRequired': int,
'icon': IconData,
'colors': List<Color>,
}
```
#### 3. Variable: `final List<Map<String, dynamic>> _categories`
* **Propósito:** Define las categorías para los cursos.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'label': String,
'icon': IconData,
}
```
#### 4. Variable: `final List<Map<String, dynamic>> _courses`
* **Propósito:** Almacena la lista de cursos disponibles.
* **Estructura de cada elemento:**
```dart
{
'id': String,
'title': String,
'description': String,
'category': String,
'duration_minutes': int,
'xp_reward': int,
'level_required': String,
'is_certification': bool,
'progress_percent': int,
'completed': bool,
}
```
#### 5. Parámetro de Función: `Widget _buildCoursesList(List<Map<String, dynamic>> courses)`
* **Propósito:** Construye la lista de widgets de cursos.
* **Estructura:** Una lista donde cada mapa tiene la estructura de un elemento de `_courses`.
---
### `lib/screens/worker/earnings_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _recentPayments`
* **Propósito:** Almacena resúmenes de pagos recientes.
* **Estructura de cada elemento:**
```dart
{
'date': String,
'amount': double,
'shifts': int,
'status': String,
}
```
---
### `lib/screens/worker/clock_in_screen.dart`
#### 1. Variable: `final List<Map<String, dynamic>> _recentActivity`
* **Propósito:** Almacena una lista de actividades recientes de fichaje.
* **Estructura de cada elemento:**
```dart
{
'date': DateTime,
'start': String,
'end': String,
'hours': String,
}
```

View File

View File

View File

@@ -1,286 +0,0 @@
# Análisis de Mocks de Staff App vs. Schema de Data Connect
Este documento analiza las estructuras de datos "mock" encontradas en el código de la aplicación de Staff (Flutter) y las compara con los schemas GraphQL de Data Connect. El objetivo es identificar discrepancias y proponer cambios para alinear el backend con las necesidades del frontend.
---
## 1. Conversaciones y Mensajes (`messages_screen.dart`)
El mock `_conversations` representa la lista de chats de un usuario. Se compara con las entidades `Conversation` y `Message` del schema.
### Estructura del Mock
```dart
// Estructura de un elemento en la lista `_conversations`
{
'sender_id': String,
'sender_name': String,
'lastMessage': String,
'lastTime': DateTime,
'unread': int,
'messages': [
{
'content': String,
'sender_id': String,
}
]
}
```
### Entidades de Data Connect
**`dataconnect/schema/conversation.gql`**
```graphql
type Conversation @table(name: "conversations") {
id: UUID!
participants: String! // Se espera un array de IDs de usuario en formato JSON string
conversationType: ConversationType!
// ... otros campos
}
```
**`dataconnect/schema/message.gql`**
```graphql
type Message @table(name: "messages") {
id: UUID!
conversationId: UUID!
senderName: String!
content: String!
readBy: String // Se espera un array de IDs de usuario que han leído el mensaje
// ... otros campos
}
```
### Análisis y Discrepancias
1. **Modelo de Datos**: El mock está diseñado para una vista de "bandeja de entrada", donde cada item es una conversación y muestra un resumen (último mensaje, hora, contador de no leídos). El schema de Data Connect, en cambio, normaliza los datos en dos tablas: `conversations` y `messages`, lo cual es correcto desde el punto de vista de la base de datos.
2. **Campos de Resumen Faltantes**: La entidad `Conversation` no tiene campos para `lastMessage`, `lastTime` (o `updatedDate` que podría servir para ordenar, pero no es lo mismo que la fecha del último mensaje), ni un contador `unread`. Para construir la pantalla de la app, el cliente tendría que:
* Obtener todas las conversaciones.
* Para CADA conversación, obtener el último mensaje para mostrar `lastMessage` y `lastTime`.
* Para CADA conversación, contar los mensajes donde el `readBy` no incluya al usuario actual para saber el `unread`.
Esto es muy ineficiente (problema N+1).
3. **Identificación de Participantes**:
* El mock tiene `sender_id` y `sender_name`, lo que sugiere una conversación 1-a-1.
* La entidad `Conversation` tiene un campo `participants` (un JSON string de un array de IDs), que es más flexible y soporta chats grupales. El frontend deberá adaptar su lógica para manejar una lista de participantes en lugar de un único `sender`.
### Recomendaciones
Para optimizar la carga de datos y alinear el backend con las necesidades de la UI, recomiendo **desnormalizar** la entidad `Conversation` añadiendo campos de resumen.
**Archivo a modificar**: `dataconnect/schema/conversation.gql`
**Cambios sugeridos**:
```graphql
# EN: dataconnect/schema/conversation.gql
type Conversation @table(name: "conversations") {
id: UUID! @default(expr: "uuidV4()")
participants: String! # participants (jsonb -> String, required array of strings)
conversationType: ConversationType!
relatedTo: UUID! # related_to (generic FK as string)
status: ConversationStatus
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
# --- CAMPOS AÑADIDOS PARA RESUMEN ---
lastMessage: String # Contenido del último mensaje
lastMessageSenderId: String # ID del remitente del último mensaje
lastMessageTimestamp: Timestamp # Fecha y hora del último mensaje
}
```
**Consideraciones Adicionales**:
* **Contador de no leídos (`unread`)**: Añadir un campo `unreadCount` a `Conversation` es complejo porque su valor depende de *quién* está mirando la conversación. Una mejor estrategia sería manejar esto en el lado del cliente o con una query más específica si el rendimiento lo permite. Por ahora, los campos de resumen del último mensaje son la mejora más crítica.
* **Lógica de Actualización**: Será necesario implementar una lógica (probablemente en una Cloud Function o en la mutación que crea mensajes) para que cada vez que se añada un `Message` nuevo, se actualicen los campos `lastMessage`, `lastMessageSenderId` y `lastMessageTimestamp` en la `Conversation` padre. Data Connect por sí solo no manejará esta lógica de desnormalización.
* **Alineación en la App**: La app de Flutter deberá cambiar su mock y su lógica para usar `participants` en lugar de `sender_id` y `sender_name`. El nombre del otro participante en un chat 1-a-1 se puede obtener buscando el ID en `participants` que no sea el del usuario actual.
---
## 2. Perfil de Staff y Leaderboard (`leaderboard_screen.dart`)
El mock `_profiles` se usa para mostrar la tabla de clasificación y parece ser un subconjunto de un perfil de trabajador (staff). Se compara con las entidades `Staff` y `User`.
### Estructura del Mock
```dart
// Estructura de un elemento en la lista `_profiles`
{
'id': String, // ID del perfil del trabajador (Staff)
'name': String,
'photo_url': String?,
'xp': int, // Puntos de experiencia
'level': String, // Nivel de gamificación, ej: 'Krower III'
'user_id': String, // ID de la entidad User (auth)
}
```
### Entidades de Data Connect
**`dataconnect/schema/staff.gql`**
```graphql
type Staff @table(name: "staffs") {
id: UUID!
employeeName: String!
# ... muchos otros campos relacionados con el trabajo
}
```
**`dataconnect/schema/user.gql`**
```graphql
type User @table(name: "users") {
id: String! // Uid de Firebase Auth
email: String!
fullName: String!
# ...
}
```
### Análisis y Discrepancias
1. **Relación `User` <-> `Staff` Faltante**: El mock contiene `id` (para el perfil `Staff`) y `user_id` (para la entidad `User`), lo cual es una arquitectura correcta. Sin embargo, la entidad `Staff` en el schema **no tiene un campo `userId`** para vincularlo con la tabla `User`. Esta es una relación fundamental que debe existir.
2. **Campos de Gamificación (`xp`, `level`)**: Estos campos son el núcleo de la funcionalidad del leaderboard, pero están completamente ausentes en la entidad `Staff`.
3. **Foto de Perfil (`photo_url`)**: Un campo para la URL de la foto de perfil es estándar en cualquier sistema con perfiles de usuario y falta en el schema de `Staff`.
4. **Convención de Nombres**: El mock usa `photo_url` y `user_id` (snake_case), mientras que la convención estándar de GraphQL y de los SDK generados por Data Connect es `camelCase`. La aplicación cliente debería usar `photoUrl` y `userId` para evitar inconsistencias.
### Recomendaciones
Para soportar la funcionalidad del leaderboard y establecer una arquitectura de datos robusta, se deben hacer los siguientes cambios.
**Archivo a modificar**: `dataconnect/schema/staff.gql`
**Cambios sugeridos**:
```graphql
# EN: dataconnect/schema/staff.gql
type Staff @table(name: "staffs") {
id: UUID! @default(expr: "uuidV4()")
userId: String! # --- AÑADIDO: Vínculo a la tabla User (FK a User.id)
employeeName: String! @col(name: "employee_name")
# ... (campos existentes)
# --- CAMPOS AÑADIDOS PARA PERFIL Y GAMIFICACIÓN ---
photoUrl: String # URL de la foto de perfil
xp: Int @default(expr: "0") # Puntos de experiencia, con valor inicial de 0
level: String # Nivel de gamificación (ej: "Beginner", "Krower I")
# ... (resto de campos existentes)
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}
```
**Consideraciones Adicionales**:
* **`userId` debe ser único**: Se debería añadir una constraint a nivel de base de datos para asegurar que no pueda haber dos perfiles de `Staff` con el mismo `userId`.
* **Alineación en la App**: Recomendar al equipo de Flutter que estandarice el uso de `camelCase` en las claves de sus `Map` para que coincidan con los nombres de campo de GraphQL (`photoUrl`, `userId`, `employeeName`).
* **`User.fullName` vs `Staff.employeeName`**: El sistema debe decidir cuál es la fuente de verdad para el nombre del usuario. Generalmente, `User.fullName` es el nombre legal/de registro, y `Staff.employeeName` podría ser un alias o nombre profesional. Para el leaderboard, `employeeName` parece más apropiado.
---
## 3. Hojas de Tiempo (Timesheets) y Pagos (`time_card_screen.dart`)
El mock `_timesheets` representa los registros de horas trabajadas que son la base para el cálculo de pagos. Esto es un concepto distinto al de un `Shift` (turno programado).
### Estructura del Mock
```dart
// Estructura de un elemento en la lista `_timesheets`
{
'id': String,
'shift_id': String,
'date': String,
'actual_start': String, // Hora de entrada real
'actual_end': String, // Hora de salida real
'total_hours': double,
'hourly_rate': double,
'total_pay': double,
'status': String, // ej: 'pending', 'approved', 'paid'
'shift_title': String, // Dato desnormalizado
'client_name': String, // Dato desnormalizado
'location': String, // Dato desnormalizado
}
```
### Entidad de Data Connect (`shift.gql`)
```graphql
// dataconnect/schema/shift.gql
type Shift @table(name: "shifts") {
id: UUID!
shiftName: String!
startDate: Timestamp! // Hora de inicio programada
endDate: Timestamp // Hora de fin programada
assignedStaff: String
# ...
}
```
### Análisis y Discrepancias
1. **Entidad Faltante: `Timesheet`**: La entidad `Shift` representa un evento *programado*. El mock `_timesheets`, sin embargo, describe el trabajo *realizado*. Es una práctica estándar separar estos dos conceptos. Se necesita una nueva entidad, que podríamos llamar `Timesheet` o `WorkLog`, que registre las horas reales y su estado de aprobación y pago.
2. **Campos del Mock**: El mock contiene todos los campos necesarios para la nueva entidad:
* Relación con el turno: `shift_id`.
* Tiempos reales: `actual_start`, `actual_end`.
* Cálculos de pago: `total_hours`, `hourly_rate`, `total_pay`.
* Ciclo de vida: `status`.
3. **Datos Desnormalizados**: El mock incluye `shift_title`, `client_name` y `location` para facilitar su visualización en la app. En el backend, estos datos no deben duplicarse en la tabla `Timesheet`. La nueva entidad solo debe contener el `shiftId`, y los datos relacionados se obtendrán mediante una query de GraphQL que una las entidades `Timesheet`, `Shift`, `Event`, `Client`, etc.
### Recomendaciones
La recomendación principal es **crear una nueva entidad `Timesheet`**.
**1. Crear nuevo archivo**: `dataconnect/schema/timesheet.gql`
**2. Añadir el siguiente schema**:
```graphql
# NUEVO ARCHIVO: dataconnect/schema/timesheet.gql
enum TimesheetStatus {
PENDING # El trabajador lo ha enviado, pendiente de aprobación
APPROVED # Aprobado por un manager, listo para ser pagado
DISPUTED # El trabajador o manager ha marcado una discrepancia
PAID # Incluido en una nómina y pagado
}
type Timesheet @table(name: "timesheets") {
id: UUID! @default(expr: "uuidV4()")
shiftId: UUID! # FK a la tabla Shift
staffId: UUID! # FK a la tabla Staff
actualStart: Timestamp # Hora de entrada real registrada
actualEnd: Timestamp # Hora de salida real registrada
totalHours: Float # Horas totales calculadas
hourlyRate: Float # Tarifa por hora (puede copiarla del turno por si cambia)
totalPay: Float # Pago total calculado (totalHours * hourlyRate)
status: TimesheetStatus! @default(expr: "'PENDING'")
notes: String # Notas del trabajador o del manager
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
createdBy: String @default(expr: "auth.uid")
}
```
**Consideraciones Adicionales**:
* **Relación**: La entidad `Timesheet` tendría una relación de uno-a-uno (o uno-a-muchos, si un turno puede tener varios registros de tiempo) con la entidad `Shift`.
* **Automatización**: La lógica para calcular `totalHours` y `totalPay` podría vivir en el backend (una Cloud Function que se dispara al crear/modificar un `Timesheet`) para asegurar consistencia.
* **Alineación en la App**: La app debe ser actualizada para hacer una query a `timesheets` y, a través de GraphQL, pedir los campos relacionados del turno (`shift { shiftName event { client { name } } }`).
---

View File

@@ -1,189 +0,0 @@
# Análisis de Mocks de Staff App vs. Schema de Data Connect
Este documento realiza una comparación detallada entre las estructuras de datos (mocks) utilizadas en la aplicación de Staff y las entidades definidas en el schema de GraphQL de Firebase Data Connect. El objetivo es identificar discrepancias, proponer soluciones y asegurar la consistencia entre el frontend y el backend.
---
## 1. Perfil de Leaderboard (`_profiles`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart`
- **Entidad Data Connect más cercana:** `Staff` (`dataconnect/schema/staff.gql`)
### Comparación Campo por Campo
| Campo (Mock App) | Campo (Schema GQL `Staff`) | Recomendación |
| :--- | :--- | :--- |
| `id` | `id` | ✅ **Match.** |
| `name` | `employeeName` | ⚠️ **Diferencia de nombre.** Renombrar `name` a `employeeName` en la app. |
| `photo_url` | `(No existe)` | ❌ **Campo faltante.** El schema de `Staff` no tiene un campo para URL de foto. Se debe agregar. |
| `xp` | `(No existe)` | ❌ **Campo faltante.** Relacionado con gamificación. Proponer `experiencePoints: Int`. |
| `level` | `(No existe)` | ❌ **Campo faltante.** Relacionado con gamificación. Proponer `level: String`. |
| `user_id` | `(No existe)` | ⚠️ **Diferencia conceptual.** El schema `Staff` tiene `vendorId` y `email`. Aclarar si `user_id` corresponde al `id` del `User` o `Staff`. |
### Recomendaciones
1. **En la app (Flutter):**
- Cambiar el nombre del campo `name` a `employeeName` para que coincida con el schema.
2. **En el schema (`staff.gql`):**
- Agregar los campos para la gamificación y el perfil.
```graphql
type Staff @table(name: "staffs") {
# ... campos existentes
profilePictureUrl: String
experiencePoints: Int @default(expr: "0")
level: String
}
```
---
## 2. Conversaciones (`_conversations`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/support/messages_screen.dart`
- **Entidades Data Connect más cercanas:** `Conversation` (`conversation.gql`) y `Message` (`message.gql`).
El mock aplana la información de ambas entidades.
### Comparación de `Conversation`
| Campo (Mock App) | Campo (Schema GQL `Conversation`) | Recomendación |
| :--- | :--- | :--- |
| `sender_id` | `participants` | ⚠️ **Diferencia de estructura.** El mock asume una conversación 1-a-1, mientras `participants` es un `String` (JSON) con un array de IDs. La app debe adaptarse para manejar múltiples participantes. |
| `sender_name` | (No existe directamente) | **Dato derivado.** Este dato debe obtenerse consultando la entidad `Staff` o `User` con el ID del participante. |
| `lastMessage` | (No existe directamente) | **Dato derivado.** Este debe ser el campo `content` del último `Message` asociado a la `conversationId`. La app debe consultar los mensajes. |
| `lastTime` | `updatedDate` | ✅ **Match conceptual.** `updatedDate` en `Conversation` debería actualizarse con cada nuevo mensaje. La app puede usar este campo. |
| `unread` | (No existe) | ❌ **Campo faltante y complejo.** El conteo de no leídos es por usuario. El schema actual no lo soporta. El campo `readBy` en `Message` podría usarse para calcular esto, pero es ineficiente. Se requiere un rediseño. |
| `messages` | (Corresponde a la entidad `Message`) | **Relación.** La app debe hacer una query separada para obtener los `Message` donde `conversationId` coincida. |
### Comparación de `Message` (anidado en el mock)
| Campo (Mock App) | Campo (Schema GQL `Message`) | Recomendación |
| :--- | :--- | :--- |
| `content` | `content` | ✅ **Match.** |
| `sender_id` | `createdBy` / `senderName` | ✅ **Match conceptual.** El schema tiene `createdBy` (ID del usuario) y `senderName`. La app debe usar estos campos. |
### Recomendaciones
1. **En la app (Flutter):**
- Adaptar la lógica para manejar `participants` como un array.
- Implementar la obtención de `sender_name` y `lastMessage` a través de queries adicionales.
- Eliminar la consulta de mensajes anidada y realizar una consulta separada por `conversationId`.
2. **En el schema (`conversation.gql` y `message.gql`):**
- El manejo de `unread` es un desafío. Una solución a largo plazo podría ser una nueva tabla `ConversationReadStatus` que vincule `(userId, conversationId, lastReadTimestamp)`. Por ahora, se puede calcular en el cliente.
- El schema actual es funcional pero requiere que el cliente orqueste varias llamadas.
---
## 3. Hojas de Tiempo (`_timesheets`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/finances/time_card_screen.dart`
- **Entidades Data Connect más cercanas:** `Shift` (`shift.gql`) y `Event` (`event.gql`).
El mock es una vista aplanada que combina información de un turno (`Shift`) y del evento (`Event`) al que pertenece.
### Comparación Campo por Campo
| Campo (Mock App) | Campo (Schema GQL `Shift`/`Event`) | Recomendación |
| :--- | :--- | :--- |
| `id` | `Shift.id` | ✅ **Match.** |
| `shift_id` | `Shift.id` | ✅ **Match.** (Redundante en el mock). |
| `date` | `Shift.startDate` | ✅ **Match conceptual.** Usar `startDate`. |
| `actual_start` | (No existe) | ❌ **Campo faltante.** El schema de `Shift` necesita campos para el check-in/out real. Proponer `actualStartTime: Timestamp`. |
| `actual_end` | (No existe) | ❌ **Campo faltante.** Proponer `actualEndTime: Timestamp`. |
| `total_hours` | (No existe) | **Dato derivado.** Debe calcularse en la app o en el backend a partir de `actualStartTime` y `actualEndTime`. |
| `hourly_rate` | `Staff.rate` / `Event.rate`? | ⚠️ **Requiere clarificación.** La tarifa puede depender del staff, del evento o del rol en el turno. El schema de `Shift` debería tener un campo `rate`. |
| `total_pay` | (No existe) | **Dato derivado.** Calcular `total_hours * hourly_rate`. |
| `status` | `Shift.status`? | ⚠️ **Ambigüedad.** `Shift` no tiene `status`, pero `Event` sí. El estado del pago (`pending`, `approved`, `paid`) es un concepto diferente. Se necesita un campo `paymentStatus` en `Shift`. |
| `shift_title` | `Shift.shiftName` | ✅ **Match.** |
| `client_name` | `Event.clientName` o `Business.name` | **Dato relacionado.** Se debe obtener a través del `eventId` en el `Shift` para luego consultar el `Event` o `Business` asociado. |
| `location` | `Event.eventLocation` | **Dato relacionado.** Obtener del `Event` asociado. |
### Recomendaciones
1. **En la app (Flutter):**
- La app debe realizar una consulta que una datos de `Shift` y su `Event` relacionado para construir esta vista.
- Calcular campos derivados como `total_hours` y `total_pay`.
2. **En el schema (`shift.gql`):**
- Agregar campos cruciales para el registro de tiempo y pago.
```graphql
type Shift @table(name: "shifts") {
# ... campos existentes
actualStartTime: Timestamp
actualEndTime: Timestamp
rate: Float
paymentStatus: String # O un enum: PENDING, APPROVED, PAID, DISPUTED
}
```
---
## 4. Certificados (`_certificates`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/compliance/certificates_screen.dart`
- **Entidad Data Connect más cercana:** `Certification` (`certification.gql`)
### Comparación Campo por Campo
| Campo (Mock App) | Campo (Schema GQL `Certification`) | Recomendación |
| :--- | :--- | :--- |
| `id` | `id` | ✅ **Match.** |
| `name` | `certificationName` | ⚠️ **Diferencia de nombre.** Renombrar `name` a `certificationName` en la app. |
| `icon` / `color` | (No existe) | **Lógica de UI.** Estos campos son para la visualización y deben manejarse en el frontend. No pertenecen al schema. |
| `description` | (No existe) | ❌ **Campo faltante.** El schema podría beneficiarse de un campo descriptivo. Proponer `description: String`. |
| `status` | `status` y `validationStatus` | ✅ **Match conceptual.** El schema tiene dos campos de estado. La app debe combinar o decidir cuál mostrar. `CertificationStatus` (`CURRENT`, `EXPIRED`) parece el correcto. |
| `expiry` | `expiryDate` | ✅ **Match conceptual.** El tipo de dato debe ser consistente (el mock usa String?, el schema `String!`). |
### Recomendaciones
1. **En la app (Flutter):**
- Cambiar `name` por `certificationName`.
- Mapear los valores de `status` del schema al estado que la UI espera.
2. **En el schema (`certification.gql`):**
- Considerar añadir un campo opcional para la descripción.
```graphql
type Certification @table(name: "certification") {
# ... campos existentes
description: String
}
```
---
## 5. Contactos de Emergencia (`_contacts`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart`
- **Entidad Data Connect más cercana:** **Ninguna.**
No existe una entidad para almacenar contactos de emergencia.
### Recomendaciones
1. **En el schema:**
- Crear una nueva entidad `EmergencyContact`. Debería estar vinculada a un `Staff`.
```graphql
# En un nuevo archivo: dataconnect/schema/emergencyContact.gql
type EmergencyContact @table(name: "emergency_contacts") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID! # FK a la tabla Staff
name: String!
phone: String!
relationship: String # Ejemplo: "Spouse", "Parent", "Friend"
createdDate: Timestamp @default(expr: "request.time")
updatedDate: Timestamp @default(expr: "request.time")
}
```
- Se debe decidir si esta información se almacena como un array de `jsonb` en la tabla `Staff` o como una tabla separada. Una tabla separada es más limpia y escalable.
---
## Conclusiones Generales
1. **Aplanamiento de Datos:** La app de Flutter tiende a usar estructuras de datos aplanadas que combinan campos de múltiples entidades de Data Connect. Esto es normal para las vistas de UI, pero requiere que la capa de datos de la app realice las consultas y uniones necesarias.
2. **Campos Faltantes Críticos:** Se han identificado campos faltantes importantes, especialmente en `Shift` (para el control de horas y pagos) y `Staff` (para gamificación y perfil).
3. **Nomenclatura:** Hay inconsistencias menores en los nombres de los campos. La recomendación general es **alinear la app con los nombres del schema de Data Connect**, que es la fuente de verdad.
4. **Nuevas Entidades:** Se necesitan nuevas entidades como `EmergencyContact`. Otras estructuras mock (`_benefitsData`, `_attireOptions`, `_courses`, etc.) también requerirán probablemente sus propias tablas y schemas en Data Connect.

View File

@@ -1,333 +0,0 @@
# 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.

View File

@@ -1,294 +0,0 @@
# Análisis Exhaustivo de Mocks de Staff App vs. Schema de Data Connect (V2)
Este documento realiza una comparación detallada y completa entre todas las estructuras de datos (mocks) utilizadas en la aplicación de Staff y las entidades definidas en el schema de GraphQL de Firebase Data Connect. El objetivo es asegurar la consistencia total y planificar la evolución del schema.
---
## 1. Perfil de Leaderboard (`_profiles`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/level_up/leaderboard_screen.dart`
- **Entidad Data Connect:** `Staff` (`dataconnect/schema/staff.gql`)
- **Análisis:** Existe una correspondencia directa pero incompleta.
- **Recomendaciones:**
- **App:** Renombrar `name` a `employeeName`.
- **Schema `staff.gql`:** Agregar campos para gamificación y perfil.
```graphql
type Staff @table(name: "staffs") {
# ... campos existentes
profilePictureUrl: String
experiencePoints: Int @default(expr: "0")
level: String
}
```
---
## 2. Conversaciones (`_conversations`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/support/messages_screen.dart`
- **Entidades Data Connect:** `Conversation` (`conversation.gql`) y `Message` (`message.gql`).
- **Análisis:** El mock es una vista aplanada de ambas entidades. El schema es más robusto pero requiere que la app orqueste las llamadas.
- **Recomendaciones:**
- **App:**
- Adaptar la lógica para manejar el campo `participants` (JSON array) en lugar de un `sender_id` simple.
- Obtener `sender_name` y `lastMessage` a través de consultas adicionales (al perfil del participante y al último mensaje).
- El campo `unread` es el más complejo. Debe calcularse en el cliente revisando la lista de `readBy` en cada mensaje.
- **Schema:** Para optimizar el conteo de no leídos a futuro, se podría crear una tabla `ConversationReadStatus (userId, conversationId, lastReadTimestamp)`.
---
## 3. Hojas de Tiempo (`_timesheets`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/finances/time_card_screen.dart`
- **Entidades Data Connect:** `Shift` (`shift.gql`) y `Event` (`event.gql`).
- **Análisis:** El mock combina información del turno y del evento. Faltan campos cruciales en el schema `Shift` para el registro de horas y pagos.
- **Recomendaciones:**
- **App:** Realizar una consulta que una datos de `Shift` y su `Event` relacionado para construir la vista.
- **Schema `shift.gql`:** Agregar los siguientes campos es **prioritario**.
```graphql
# En dataconnect/schema/shift.gql
enum ShiftPaymentStatus {
PENDING
APPROVED
PAID
DISPUTED
}
type Shift @table(name: "shifts") {
# ... campos existentes
actualStartTime: Timestamp
actualEndTime: Timestamp
rate: Float # Tarifa específica del turno
paymentStatus: ShiftPaymentStatus
}
```
---
## 4. Certificados (`_certificates`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/compliance/certificates_screen.dart`
- **Entidad Data Connect:** `Certification` (`certification.gql`).
- **Análisis:** Coincidencia conceptual fuerte.
- **Recomendaciones:**
- **App:** Renombrar `name` a `certificationName` y `expiry` a `expiryDate`.
- **Schema `certification.gql`:** Añadir un campo opcional `description: String`.
---
## 5. Contactos de Emergencia (`_contacts`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/onboarding/emergency_contact_screen.dart`
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** No existe una entidad para esta información vital.
- **Recomendaciones:**
- **Schema:** Crear una nueva entidad `EmergencyContact` vinculada al `Staff`. Almacenarla en una tabla separada es preferible a un campo JSON en `Staff` por escalabilidad.
```graphql
# En dataconnect/schema/emergencyContact.gql
type EmergencyContact @table(name: "emergency_contacts") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID! # FK a la tabla Staff
name: String!
phone: String!
relationship: String # Ejemplo: "Spouse", "Parent"
createdDate: Timestamp @default(expr: "request.time")
}
```
---
## 6. Opciones de Vestimenta (`_attireOptions`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/onboarding/attire_screen.dart`
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** Representa el inventario de vestimenta de un trabajador, que puede ser requisito para un turno.
- **Recomendaciones:**
- **Schema:** Crear dos nuevas entidades: una para definir la vestimenta estándar y otra para asociarla a cada trabajador.
```graphql
# En dataconnect/schema/attire.gql
type Attire @table(name: "attire") {
id: UUID! @default(expr: "uuidV4()")
label: String!
imageUrl: String
}
# En dataconnect/schema/staffAttire.gql
type StaffAttire @table(name: "staff_attire") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID!
attireId: UUID!
}
```
---
## 7. Cursos de Capacitación (`_courses`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/level_up/trainings_screen.dart`
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** Distinto de `Certification`, se enfoca en capacitación interna y gamificación.
- **Recomendaciones:**
- **Schema:** Crear entidades para los cursos y el progreso de cada trabajador.
```graphql
# En dataconnect/schema/trainingCourse.gql
type TrainingCourse @table(name: "training_courses") {
id: UUID! @default(expr: "uuidV4()")
title: String!
description: String
durationMinutes: Int
xpReward: Int
}
# En dataconnect/schema/staffTraining.gql
type StaffTraining @table(name: "staff_trainings") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID!
courseId: UUID!
progressPercent: Int @default(expr: "0")
completed: Boolean @default(expr: "false")
}
```
---
## 8. Documentos Requeridos (`_requiredDocs` y `_forms`)
- **Archivos Mock:** `documents_screen.dart` y `tax_forms_screen.dart`.
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** Ambos mocks se refieren a documentos de cumplimiento. Se pueden unificar.
- **Recomendaciones:**
- **Schema:** Crear una única entidad `StaffDocument` para manejar todos los documentos.
```graphql
# En dataconnect/schema/staffDocument.gql
enum DocumentType {
GOVERNMENT_ID
SSN
FORM_I9
FORM_W4
OTHER
}
enum DocumentStatus {
MISSING
PENDING_UPLOAD
PENDING_VERIFICATION
VERIFIED
REJECTED
}
type StaffDocument @table(name: "staff_documents") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID!
documentType: DocumentType!
status: DocumentStatus!
fileUrl: String # URL al archivo en Cloud Storage
notes: String # Para motivos de rechazo, etc.
}
```
---
## 9. Cuentas Bancarias (`_accounts`)
- **Archivo Mock:** `lib/screens/worker/worker_profile/finances/bank_account_screen.dart`
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** Esta es información altamente sensible. **No debe almacenarse directamente en la base de datos.** Debe ser manejada por un proveedor de pagos (ej. Stripe, Adyen).
- **Recomendaciones:**
- **Schema `staff.gql`:** Agregar un campo para almacenar el ID de cliente del proveedor de pagos, no los detalles de la cuenta.
```graphql
type Staff @table(name: "staffs") {
# ... campos existentes
paymentProviderCustomerId: String # ID del cliente en Stripe/Adyen
}
```
- **App:** Integrar el SDK del proveedor de pagos para registrar y mostrar la información bancaria de forma segura (tokenizada).
---
## 10. Pagos Recientes (`_recentPayments` - detallado y resumido)
- **Archivos Mock:** `payments_screen.dart` y `earnings_screen.dart`.
- **Entidad Data Connect:** **Ninguna (es una vista derivada).**
- **Análisis:** Estos mocks no representan una entidad, sino una vista de consulta sobre los `Shift` que han sido pagados.
- **Recomendaciones:**
- **Sin cambios en el schema,** asumiendo que las recomendaciones para `Shift` (punto 3) se implementen.
- **App:** La lógica para estas pantallas debe:
1. Consultar todos los `Shift` para el `staffId` con `paymentStatus: 'PAID'`.
2. Unir la información del `Event` relacionado para obtener la ubicación y nombre del cliente.
3. Para la vista de "Earnings", agrupar estos turnos por período de pago (ej. semanal) y sumar los montos.
---
## 11. Franjas Horarias (`_timeSlots` - Disponibilidad)
- **Archivo Mock:** `lib/screens/worker/availability_screen.dart`
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** Define la disponibilidad preferida del trabajador.
- **Recomendaciones:**
- **Schema:** Crear una nueva entidad `StaffAvailability`. Un campo JSON por día ofrece flexibilidad.
```graphql
# En dataconnect/schema/staffAvailability.gql
type StaffAvailability @table(name: "staff_availability") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID!
# Usar Any (jsonb) para guardar un array de strings: ["morning", "afternoon"]
monday: Any
tuesday: Any
wednesday: Any
thursday: Any
friday: Any
saturday: Any
sunday: Any
}
```
---
## 12. Datos de Beneficios (`_benefitsData`)
- **Archivo Mock:** `lib/screens/worker/benefits_screen.dart`
- **Entidad Data Connect:** **Ninguna.**
- **Análisis:** Gestiona los beneficios acumulados como días de vacaciones o enfermedad.
- **Recomendaciones:**
- **Schema:** Crear entidades para definir los tipos de beneficio y el balance de cada trabajador.
```graphql
# En dataconnect/schema/benefit.gql
enum BenefitType {
SICK_DAYS
VACATION
OTHER
}
type StaffBenefit @table(name: "staff_benefits") {
id: UUID! @default(expr: "uuidV4()")
staffId: UUID!
benefitType: BenefitType!
currentBalance: Float! # En horas o días
totalAllowance: Float! # Total anual/periodo
}
# Opcional: una tabla para el historial de uso
type BenefitHistory @table(name: "benefit_history") {
id: UUID! @default(expr: "uuidV4()")
staffBenefitId: UUID!
amount: Float! # Horas/días usados
date: Timestamp!
status: String # PENDING, APPROVED
}
```
---
## 13. Actividad Reciente (`ActivityLog`)
- **Mock:** No hay un mock específico, pero el concepto es universal.
- **Entidad Data Connect:** `ActivityLog` (`activityLog.gql`)
- **Análisis:** El schema `activityLog.gql` ya existe y es perfecto para registrar eventos importantes (ej. "Turno aceptado", "Perfil actualizado", "Certificado validado").
- **Recomendaciones:**
- **Backend:** Implementar la lógica para que las mutaciones importantes (actualizar staff, aceptar un turno, etc.) generen una entrada en la tabla `ActivityLog`.
- **App:** Crear una pantalla de "Actividad Reciente" que consulte esta tabla para el `staffId` logueado, mostrando un historial de eventos.
---
## 14. Pasos de Setup y FAQs (`_steps` y `_faqData`)
- **Archivos Mock:** `profile_setup_screen.dart` y `faqs_screen.dart`.
- **Análisis:** Estas estructuras son puramente para la UI (navegación de un wizard) o contienen contenido estático (FAQs).
- **Recomendaciones:**
- **Sin cambios en el schema.** Esta información no necesita ser gestionada a través de Data Connect. Puede permanecer como contenido estático dentro de la aplicación o, si requiere ser dinámico, ser gestionado por un CMS externo.