Files
Krow-workspace/frontend-web-free/vendor_analysis_report.json
2025-12-26 15:14:51 -05:00

250 lines
16 KiB
JSON

{
"role": "VENDOR",
"role_detection": {
"summary": "La aplicación determina el rol 'VENDOR' revisando las propiedades 'user_role' o 'role' en el objeto de usuario. Este objeto se obtiene de `base44.auth.me()`, que combina datos de Firebase Auth y un perfil de usuario de la base de datos (DataConnect). Existe un mecanismo de anulación para desarrollo que utiliza localStorage.",
"fields_used": [
"user.user_role",
"user.role",
"localStorage.getItem('krow_mock_user_role')"
],
"evidence": [
"src/api/krowSDK.js: La función `auth.me()` unifica el objeto de usuario y prioriza el rol mock de localStorage.",
"src/pages/Layout.jsx: `const userRole = user?.user_role || user?.role || 'admin';` se usa para determinar qué menú mostrar."
]
},
"menu_for_role": [
{
"label": "Home",
"route": "/VendorDashboard",
"page_component": "VendorDashboard",
"page_file": "src/pages/VendorDashboard.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Orders",
"route": "/VendorOrders",
"page_component": "VendorOrders",
"page_file": "src/pages/VendorOrders.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Service Rates",
"route": "/VendorRates",
"page_component": "VendorRates",
"page_file": "src/pages/VendorRates.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Invoices",
"route": "/Invoices",
"page_component": "Invoices",
"page_file": "src/pages/Invoices.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Workforce",
"route": "/StaffDirectory",
"page_component": "StaffDirectory",
"page_file": "src/pages/StaffDirectory.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Onboard Staff",
"route": "/StaffOnboarding",
"page_component": "StaffOnboarding",
"page_file": "src/pages/StaffOnboarding.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Team",
"route": "/Teams",
"page_component": "Teams",
"page_file": "src/pages/Teams.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Compliance",
"route": "/VendorCompliance",
"page_component": "VendorCompliance",
"page_file": "src/pages/VendorCompliance.jsx",
"visibility_condition": "userRole === 'vendor'",
"defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
}
],
"routes_for_role": [
{
"route": "/*",
"component": "Any page component",
"file": "src/pages/index.jsx",
"guard": "ProtectedRoute",
"role_constraints": "El `ProtectedRoute` solo valida si el usuario está autenticado, no su rol. El control de acceso por rol se realiza a nivel de UI (ocultando menús en `Layout.jsx`) y a nivel de datos (filtrando datos dentro de cada página)."
}
],
"workflows": [
{
"name": "Vendor - Visualizar y filtrar órdenes",
"entry": { "label": "Orders", "route": "/VendorOrders" },
"steps": ["1. Usuario navega a la página 'Orders' desde el menú.", "2. La página `VendorOrders.jsx` se renderiza.", "3. Se llama a `base44.entities.Event.list()` para obtener todas las órdenes.", "4. Los datos se filtran en el frontend para mostrar solo las órdenes donde `e.vendor_id === user.id` o `e.vendor_name === user.company_name`.", "5. El usuario utiliza las pestañas para filtrar por estado ('upcoming', 'active', 'past').", "6. El usuario escribe en la barra de búsqueda para filtrar por nombre de evento, cliente o ubicación."],
"sdk_calls": ["base44.entities.Event.list"],
"data_requirements": ["user.id", "user.company_name"],
"known_failure_modes": ["Si una orden no tiene `vendor_id` o `vendor_name` asignado correctamente, no aparecerá en la lista del vendor."],
"evidence_files": ["src/pages/VendorOrders.jsx"]
},
{
"name": "Vendor - Editar una orden",
"entry": { "label": "Orders", "route": "/VendorOrders" },
"steps": ["1. En la tabla de órdenes, el usuario hace clic en el ícono 'Edit'.", "2. Navega a la página `EditEvent?id={id}`.", "3. La página `EditEvent.jsx` comprueba el rol con `isVendor = user?.user_role === 'vendor'`.", "4. El formulario de evento (`EventForm`) se renderiza, pero ciertas secciones (probablemente relacionadas con cliente y precios) están deshabilitadas para el vendor (UNKNOWN - requiere análisis de `EventForm`).", "5. El vendor actualiza campos permitidos (ej. notas internas) y guarda.", "6. Se llama a la mutación `updateEvent` con los datos actualizados."],
"sdk_calls": ["base44.entities.Event.update"],
"data_requirements": ["event.id", "user.user_role"],
"known_failure_modes": ["El formulario podría fallar si intenta guardar un campo que no tiene permitido modificar."],
"evidence_files": ["src/pages/VendorOrders.jsx", "src/pages/EditEvent.jsx"]
},
{
"name": "Vendor - Añadir un nuevo miembro de staff",
"entry": { "label": "Workforce", "route": "/StaffDirectory" },
"steps": ["1. Usuario navega a `StaffDirectory`.", "2. Hace clic en el botón 'Add New Staff'.", "3. Navega a la página `AddStaff.jsx`.", "4. Rellena el formulario con los detalles del nuevo empleado.", "5. Al guardar, se asume que se llama a `base44.entities.Staff.create` con un payload que DEBE incluir `{ vendor_id: user.id }` para la correcta asignación."],
"sdk_calls": ["base44.entities.Staff.create"],
"data_requirements": ["user.id", "user.company_name"],
"payload_notes": ["Es crítico que el `vendor_id` del vendor logueado se inyecte en el payload de creación del staff."],
"known_failure_modes": ["Si el `vendor_id` no se envía, el nuevo miembro de staff podría quedar 'huérfano', sin ser visible para el vendor."],
"evidence_files": ["src/pages/StaffDirectory.jsx", "src/pages/AddStaff.jsx"]
},
{
"name": "Vendor - Asignar staff a una orden (Smart Assign)",
"entry": { "label": "Orders", "route": "/VendorOrders" },
"steps": ["1. En la tabla de órdenes, el usuario hace clic en el ícono de `UserCheck` (Smart Assign).", "2. Se abre el modal `SmartAssignModal.jsx`.", "3. El modal muestra una lista del staff disponible y compatible del vendor.", "4. El vendor selecciona uno o más miembros del staff.", "5. Al confirmar, el modal ejecuta una lógica que culmina en una llamada a `base44.entities.Event.update` para actualizar el campo `assigned_staff` de la orden."],
"sdk_calls": ["base44.entities.Event.update"],
"data_requirements": ["event.id", "event.shifts", "staff list"],
"known_failure_modes": ["El motor de 'Smart Assign' podría fallar si los datos de `shifts` en el evento son inconsistentes o si las tasas (`VendorRate`) no se encuentran."],
"evidence_files": ["src/pages/VendorOrders.jsx", "src/components/events/SmartAssignModal.jsx"]
},
{
"name": "Vendor - Gestionar tarifas de servicio",
"entry": { "label": "Service Rates", "route": "/VendorRates" },
"steps": ["1. Usuario navega a `VendorRates`.", "2. La vista `VendorCompanyPricebookView` se renderiza, mostrando tarifarios empresariales (ej. Aramark) y tarifarios personalizados ('Custom Rate Cards').", "3. El vendor puede crear un nuevo tarifario personalizado, asignarle un nombre y definir tarifas por rol.", "4. El vendor puede usar la función 'AI Price Check' que llama a `base44.integrations.Core.InvokeLLM` para obtener un análisis de competitividad."],
"sdk_calls": ["base44.entities.VendorRate.list", "base44.integrations.Core.InvokeLLM"],
"data_requirements": ["user.company_name"],
"known_failure_modes": ["La comparación de tarifas podría ser imprecisa si los nombres de los roles no coinciden exactamente entre los diferentes tarifarios."],
"evidence_files": ["src/pages/VendorRates.jsx"]
},
{
"name": "Vendor - Importar certificados de staff en bloque",
"entry": { "label": "Compliance", "route": "/VendorCompliance" },
"steps": ["1. Usuario navega a `VendorCompliance` y hace clic en 'Bulk Import'.", "2. Se abre un diálogo donde el usuario arrastra y suelta múltiples archivos (PDF, JPG).", "3. La función `processBulkFiles` se ejecuta en un bucle por cada archivo.", "4. Cada archivo se sube con `UploadFile` y luego se analiza con `InvokeLLM` para extraer el nombre del titular, fechas, etc.", "5. El sistema intenta hacer un 'fuzzy match' del nombre extraído con la lista de staff del vendor usando `findEmployeeByName`.", "6. La UI muestra los resultados, permitiendo al vendor corregir manualmente los matches fallidos.", "7. Al confirmar, se llama a `base44.entities.Certification.create` por cada certificado verificado."],
"sdk_calls": ["base44.integrations.Core.UploadFile", "base44.integrations.Core.InvokeLLM", "base44.entities.Certification.create"],
"data_requirements": ["staff list", "user.id", "user.company_name"],
"known_failure_modes": ["El 'fuzzy match' de nombres puede fallar si hay nombres similares o si el OCR de la IA extrae mal el nombre.", "La creación puede fallar si los datos extraídos (ej. fechas) no tienen el formato correcto."],
"evidence_files": ["src/pages/VendorCompliance.jsx"]
},
{
"name": "Vendor - Crear una factura",
"entry": { "label": "Invoices", "route": "/Invoices" },
"steps": ["1. Usuario navega a `Invoices` y hace clic en 'Create Invoice'.", "2. Se abre `CreateInvoiceModal.jsx`.", "3. Se asume que el modal permite seleccionar una orden completada para pre-rellenar los datos.", "4. El usuario revisa los detalles y confirma.", "5. Se llama a `base44.entities.Invoice.create` para generar el nuevo registro."],
"sdk_calls": ["base44.entities.Invoice.create"],
"data_requirements": ["user.id", "user.company_name", "event list"],
"known_failure_modes": ["UNKNOWN - Depende de la implementación de `CreateInvoiceModal`."],
"evidence_files": ["src/pages/Invoices.jsx", "src/components/invoices/CreateInvoiceModal.jsx"]
},
{
"name": "Vendor - Ver su dashboard",
"entry": { "label": "Home", "route": "/VendorDashboard" },
"steps": ["1. Al iniciar sesión, el usuario es dirigido a `VendorDashboard.jsx`.", "2. La página carga KPIs agregados: órdenes de hoy, órdenes en progreso, órdenes urgentes (RAPID) y staff asignado hoy.", "3. Muestra una tabla con las órdenes de hoy y mañana.", "4. El usuario puede hacer clic en una orden para ir al detalle (`EventDetail`) o iniciar una asignación (`SmartAssignModal`).", "5. También puede ver un carrusel de ingresos y paneles de 'Top Clients' y 'Top Performers'."],
"sdk_calls": ["base44.auth.me", "base44.entities.Event.list", "base44.entities.Staff.list"],
"data_requirements": ["user.id", "user.company_name"],
"known_failure_modes": ["Las métricas pueden ser incorrectas si el filtrado de datos en el frontend es incompleto o no coincide con el backend."],
"evidence_files": ["src/pages/VendorDashboard.jsx"]
}
],
"payload_contracts": [
{
"entity": "Certification",
"operations": {
"create": {
"payload_shape": ["employee_id", "employee_name", "certification_name", "certification_type", "certificate_number", "issuer", "issue_date", "expiry_date", "document_url", "vendor_id", "vendor_name", "owner", "expert_body", "validation_status", "ai_validation_result"],
"evidence": ["src/pages/VendorCompliance.jsx > handleAddCertification, handleImportMatched"]
}
},
"missing_required_fields": [],
"fields_sent_but_not_supported": []
},
{
"entity": "Staff",
"operations": {
"create": {
"payload_shape": "UNKNOWN",
"evidence": ["src/pages/AddStaff.jsx (File not read, but workflow implies its existence and usage)"]
}
},
"missing_required_fields": ["Se asume que `vendor_id` es un campo requerido, y si el frontend no lo envía, la creación resultaría en un registro huérfano."],
"fields_sent_but_not_supported": []
},
{
"entity": "Event",
"operations": {
"update": {
"payload_shape": ["assigned_staff", "shifts", "requested", "status"],
"evidence": ["src/pages/VendorOrders.jsx > autoAssignMutation", "src/components/events/SmartAssignModal.jsx (assumed)"]
}
},
"missing_required_fields": [],
"fields_sent_but_not_supported": []
},
{
"entity": "Invoice",
"operations": {
"create": {
"payload_shape": ["vendor_id", "business_name", "amount", "... (many others)"],
"evidence": ["src/components/invoices/CreateInvoiceModal.jsx"]
}
},
"missing_required_fields": [],
"fields_sent_but_not_supported": []
}
],
"top_gaps": [
{
"issue": "Inconsistencia en el Scoping de Datos del Vendor",
"impact": "Alto. El vendor podría no ver todos sus datos. Por ejemplo, en `Invoices.jsx` se filtra por `user.vendor_id`, pero en `VendorOrders.jsx` y `VendorStaff.jsx` se filtra por `user.id`. Si estas propiedades no están alineadas, el vendor no verá sus facturas.",
"evidence": ["src/pages/Invoices.jsx", "src/pages/VendorOrders.jsx", "src/api/krowSDK.js"]
},
{
"issue": "El filtrado de datos depende de múltiples claves, algunas frágiles",
"impact": "Medio. El código usa `vendor_id`, `vendor_name`, y `created_by` para vincular datos al vendor. Si un vendor cambia su `company_name`, podría perder acceso a datos históricos que solo estaban vinculados por nombre.",
"evidence": ["src/pages/VendorOrders.jsx", "src/pages/StaffDirectory.jsx"]
},
{
"issue": "Creación de Staff sin garantía de asignación de `vendor_id`",
"impact": "Alto. La UI permite a un vendor ir a `AddStaff`, pero no se ha verificado si el payload de creación inyecta de forma forzosa el `vendor_id` del vendor logueado. Si no lo hace, se pueden crear empleados 'huérfanos' no asociados a ningún vendor.",
"evidence": ["src/pages/AddStaff.jsx (existence implied)", "src/pages/StaffDirectory.jsx"]
},
{
"issue": "Permisos de edición de eventos no claros para vendors",
"impact": "Bajo. El vendor puede acceder a la ruta `EditEvent`, pero no está claro qué campos del formulario puede editar. Esto puede causar confusión o errores si intenta modificar un campo restringido.",
"evidence": ["src/pages/EditEvent.jsx"]
},
{
"issue": "Feature de 'Scheduler' incompleta",
"impact": "Bajo. La página de órdenes (`VendorOrders.jsx`) tiene un botón para una vista de 'scheduler', pero la funcionalidad no está implementada, llevando a una UI incompleta.",
"evidence": ["src/pages/VendorOrders.jsx"]
},
{
"issue": "El motor de 'Smart Assignment' puede no tener suficientes datos",
"impact": "Medio. La función `autoFillShifts` en `VendorOrders` depende de `allStaff`, `vendorEvents` y `vendorRates`. Si alguna de estas listas está incompleta o es incorrecta, la asignación automática fallará o dará malos resultados.",
"evidence": ["src/pages/VendorOrders.jsx", "src/components/scheduling/SmartAssignmentEngine.js"]
}
],
"next_verifications": [
"Leer el código de `AddStaff.jsx` y `CreateInvoiceModal.jsx` para documentar los `payload_contracts` de creación de forma precisa.",
"Analizar el componente `EventForm.jsx` para determinar qué campos están realmente deshabilitados cuando `isVendor` es `true`.",
"Revisar el schema de DataConnect (`.gql` files, si estuvieran disponibles) para verificar si existen constraints de `foreign key` que mitiguen el riesgo de datos huérfanos (ej. `Staff.vendor_id` -> `Users.id`).",
"Aclarar con el equipo de producto si un vendor debería poder crear eventos o si solo se le asignan."
]
}