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

174 lines
19 KiB
JSON

{
"role": "VENDOR",
"role_detection": {
"summary": "La aplicación define el rol de un usuario mediante las propiedades 'user_role' o 'role' en el objeto 'user'. Este objeto se obtiene de `krowSDK.auth.me()`, que consulta la base de datos (DataConnect) usando el UID del usuario autenticado en Firebase. Adicionalmente, existe un mecanismo para desarrolladores que permite simular un rol usando `localStorage` (`krow_mock_user_role`), el cual tiene prioridad sobre el rol de la base de datos.",
"checks": [
{
"type": "Primary Check",
"condition": "user?.user_role || user?.role",
"files": ["src/pages/Layout.jsx", "src/pages/StaffDirectory.jsx", "src/pages/EditEvent.jsx"]
},
{
"type": "Developer Mock",
"condition": "localStorage.getItem('krow_mock_user_role')",
"files": ["src/api/krowSDK.js"]
}
],
"evidence": [
"src/api/krowSDK.js: La función `auth.me()` implementa esta lógica de unificación.",
"src/components/dev/RoleSwitcher.jsx: Este componente de desarrollo utiliza `localStorage` para cambiar de rol, demostrando el mecanismo de mock."
]
},
"menu_for_role": [
{
"label": "Home", "icon": "LayoutDashboard", "route": "/VendorDashboard", "page_component": "VendorDashboard", "page_file": "src/pages/VendorDashboard.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Orders", "icon": "FileText", "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", "icon": "DollarSign", "route": "/VendorRates", "page_component": "VendorRates", "page_file": "src/pages/VendorRates.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Invoices", "icon": "Clipboard", "route": "/Invoices", "page_component": "Invoices", "page_file": "src/pages/Invoices.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Schedule", "icon": "Calendar", "route": "/Schedule", "page_component": "Schedule", "page_file": "src/pages/Schedule.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Staff Availability", "icon": "Users", "route": "/StaffAvailability", "page_component": "StaffAvailability", "page_file": "src/pages/StaffAvailability.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Workforce", "icon": "Users", "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", "icon": "GraduationCap", "route": "/StaffOnboarding", "page_component": "StaffOnboarding", "page_file": "src/pages/StaffOnboarding.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Team", "icon": "UserCheck", "route": "/Teams", "page_component": "Teams", "page_file": "src/pages/Teams.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Task Board", "icon": "CheckSquare", "route": "/TaskBoard", "page_component": "TaskBoard", "page_file": "src/pages/TaskBoard.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Compliance", "icon": "Shield", "route": "/VendorCompliance", "page_component": "VendorCompliance", "page_file": "src/pages/VendorCompliance.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Communications", "icon": "MessageSquare", "route": "/Messages", "page_component": "Messages", "page_file": "src/pages/Messages.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Leads", "icon": "UserCheck", "route": "/Business", "page_component": "Business", "page_file": "src/pages/Business.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Business", "icon": "Briefcase", "route": "/Business", "page_component": "Business", "page_file": "src/pages/Business.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Reports", "icon": "BarChart3", "route": "/Reports", "page_component": "Reports", "page_file": "src/pages/Reports.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Audit Trail", "icon": "Activity", "route": "/ActivityLog", "page_component": "ActivityLog", "page_file": "src/pages/ActivityLog.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
},
{
"label": "Performance", "icon": "TrendingUp", "route": "/VendorPerformance", "page_component": "VendorPerformance", "page_file": "src/pages/VendorPerformance.jsx",
"visibility_condition": "userRole === 'vendor'", "defined_in": ["src/pages/Layout.jsx > roleNavigationMap"]
}
],
"router_all_routes": [
{ "route": "/", "component": "Home", "file": "src/pages/Home.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/login", "component": "Login", "file": "src/pages/Login.jsx", "guard": "PublicRoute", "role_constraints": "None" },
{ "route": "/Dashboard", "component": "Dashboard", "file": "src/pages/Dashboard.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/VendorDashboard", "component": "VendorDashboard", "file": "src/pages/VendorDashboard.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/VendorOrders", "component": "VendorOrders", "file": "src/pages/VendorOrders.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/VendorRates", "component": "VendorRates", "file": "src/pages/VendorRates.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/StaffDirectory", "component": "StaffDirectory", "file": "src/pages/StaffDirectory.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/Invoices", "component": "Invoices", "file": "src/pages/Invoices.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/EventDetail", "component": "EventDetail", "file": "src/pages/EventDetail.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/EditEvent", "component": "EditEvent", "file": "src/pages/EditEvent.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/AddStaff", "component": "AddStaff", "file": "src/pages/AddStaff.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/EditStaff", "component": "EditStaff", "file": "src/pages/EditStaff.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/InvoiceDetail", "component": "InvoiceDetail", "file": "src/pages/InvoiceDetail.jsx", "guard": "ProtectedRoute", "role_constraints": "Auth-only" },
{ "route": "/*", "component": "All others", "file": "src/pages/index.jsx", "guard": "ProtectedRoute", "role_constraints": "Todas las demás rutas siguen el mismo patrón: accesibles si se está autenticado, sin guard de rol a nivel de router." }
],
"navigation_discovered_routes": [
{ "from_component": "VendorDashboard", "from_action": "Click en icono 'View' de una orden en la tabla", "to_route": "/EventDetail?id=...", "evidence": ["src/pages/VendorDashboard.jsx > handleViewOrder"] },
{ "from_component": "VendorDashboard", "from_action": "Click en una orden 'RAPID' urgente", "to_route": "/EventDetail?id=...", "evidence": ["src/pages/VendorDashboard.jsx > handleRapidClick"] },
{ "from_component": "VendorOrders", "from_action": "Click en icono 'View' de una orden", "to_route": "/EventDetail?id=...", "evidence": ["src/pages/VendorOrders.jsx > Table onClick"] },
{ "from_component": "VendorOrders", "from_action": "Click en icono 'Edit' de una orden", "to_route": "/EditEvent?id=...", "evidence": ["src/pages/VendorOrders.jsx > Table onClick"] },
{ "from_component": "StaffDirectory", "from_action": "Click en el botón 'Add New Staff'", "to_route": "/AddStaff", "evidence": ["src/pages/StaffDirectory.jsx > PageHeader actions prop"] },
{ "from_component": "StaffDirectory", "from_action": "Click en el nombre de un empleado en la lista", "to_route": "/EditStaff?id=...", "evidence": ["src/pages/StaffDirectory.jsx > Link to={createPageUrl(...)}"] },
{ "from_component": "Invoices", "from_action": "Click en botón 'View' de una factura", "to_route": "/InvoiceDetail?id=...", "evidence": ["src/pages/Invoices.jsx > Table onClick"] }
],
"pages_inventory": {
"pages_in_menu_for_vendor": ["/VendorDashboard", "/VendorOrders", "/VendorRates", "/Invoices", "/Schedule", "/StaffAvailability", "/StaffDirectory", "/StaffOnboarding", "/Teams", "/TaskBoard", "/VendorCompliance", "/Messages", "/Business", "/Reports", "/ActivityLog", "/VendorPerformance"],
"pages_reachable_by_vendor": ["/VendorDashboard", "/VendorOrders", "/VendorRates", "/Invoices", "/Schedule", "/StaffAvailability", "/StaffDirectory", "/StaffOnboarding", "/Teams", "/TaskBoard", "/VendorCompliance", "/Messages", "/Business", "/Reports", "/ActivityLog", "/VendorPerformance", "/EventDetail", "/EditEvent", "/AddStaff", "/EditStaff", "/InvoiceDetail"],
"pages_url_direct_accessible": ["All routes defined in src/pages/index.jsx are technically accessible via URL if the user is authenticated, including admin-focused pages like /UserManagement or /EnterpriseManagement. The content within them would likely be empty or broken due to data scoping."]
},
"workflows": [
{ "name": "Vendor - Visualizar órdenes y navegar al detalle", "entry": {"type": "menu", "route": "/VendorOrders"}, "steps": ["Usuario navega a 'Orders' desde el menú.", "Se renderiza `VendorOrders.jsx` y se llama a `base44.entities.Event.list()`.", "Los datos se filtran en el frontend por `vendor_id === user.id`.", "El usuario ve la lista de sus órdenes.", "Hace clic en el icono 'View' de una orden.", "La app navega a `/EventDetail?id=...`."], "sdk_calls": ["base44.entities.Event.list"], "payload_exact": {}, "missing_fields": [], "extra_fields": [], "scoping": "`e.vendor_id === user?.id` OR `e.vendor_name === user?.company_name` OR `e.created_by === user?.email`" },
{ "name": "Vendor - Añadir nuevo miembro de staff", "entry": {"type": "internal", "route": "/StaffDirectory -> /AddStaff"}, "steps": ["Desde `/StaffDirectory`, el usuario hace clic en 'Add New Staff'.", "Navega a `/AddStaff`.", "Completa el formulario.", "Al guardar, se llama a `base44.entities.Staff.create`."], "sdk_calls": ["base44.entities.Staff.create"], "payload_exact": "UNKNOWN", "missing_fields": ["`vendor_id` es crítico aquí y debe ser inyectado por el frontend a partir del `user.id` del vendor logueado."], "extra_fields": [], "scoping": "El scoping se debe garantizar en la escritura (create), no solo en la lectura (list).", "evidence": ["src/pages/StaffDirectory.jsx"] },
{ "name": "Vendor - Editar un miembro de staff", "entry": {"type": "internal", "route": "/StaffDirectory -> /EditStaff"}, "steps": ["Desde `/StaffDirectory`, el usuario hace clic en el nombre de un empleado.", "Navega a `/EditStaff?id=...`.", "Modifica el formulario y guarda.", "Se llama a `base44.entities.Staff.update`."], "sdk_calls": ["base44.entities.Staff.update"], "payload_exact": "UNKNOWN", "missing_fields": [], "extra_fields": [], "scoping": "El `user.id` del staff es la clave para la actualización.", "evidence": ["src/pages/StaffDirectory.jsx"] },
{ "name": "Vendor - Crear factura para un evento", "entry": {"type": "menu", "route": "/Invoices"}, "steps": ["Usuario navega a 'Invoices'.", "Hace clic en 'Create Invoice'.", "Se abre el modal `CreateInvoiceModal`.", "El usuario selecciona un evento completado.", "El modal se pre-rellena con los datos del evento.", "Al confirmar, se llama a `base44.entities.Invoice.create`."], "sdk_calls": ["base44.entities.Invoice.create"], "payload_exact": {"vendor_id": "selectedEvent.vendor_id", "amount": "...", "business_name": "..."}, "missing_fields": [], "extra_fields": [], "scoping": "El `vendor_id` se toma del evento seleccionado.", "evidence": ["src/components/invoices/CreateInvoiceModal.jsx"] },
{ "name": "Vendor - Importar certificados de staff con IA", "entry": {"type": "menu", "route": "/VendorCompliance"}, "steps": ["Usuario navega a 'Compliance' y hace clic en 'Bulk Import'.", "Arrastra y suelta archivos PDF/JPG de certificados.", "`processBulkFiles` sube cada archivo (`UploadFile`) y lo envía a `InvokeLLM` para análisis.", "La IA extrae datos (nombre, fechas, etc.).", "El sistema intenta hacer match del nombre extraído con el staff del vendor (`findEmployeeByName`).", "El vendor revisa los matches y confirma la importación.", "Se llama a `base44.entities.Certification.create` por cada certificado validado."], "sdk_calls": ["base44.integrations.Core.UploadFile", "base44.integrations.Core.InvokeLLM", "base44.entities.Certification.create"], "payload_exact": {"employee_id": "...", "certification_name": "...", "expiry_date": "...", "vendor_id": "user.id", "ai_validation_result": "{...}"}, "missing_fields": [], "extra_fields": [], "scoping": "La creación de la certificación incluye `vendor_id: user?.id`.", "evidence": ["src/pages/VendorCompliance.jsx"] }
],
"payload_contracts": [
{
"entity": "Certification",
"operations": {
"create": {
"signature": "`createCertMutation.mutateAsync(certData)`",
"payload": {"employee_id": "string", "employee_name": "string", "certification_name": "string", "certification_type": "string", "certificate_number": "string", "issuer": "string", "issue_date": "date string", "expiry_date": "date string", "document_url": "string", "vendor_id": "string (user.id)", "vendor_name": "string (user.company_name)", "validation_status": "string", "ai_validation_result": "object" },
"optional_fields": ["certificate_number", "issuer", "issue_date", "document_url"],
"required_fields": ["employee_id", "certification_name", "expiry_date", "vendor_id"]
}
}, "fields_sent_but_not_supported": "UNKNOWN", "missing_fields_required_for_scoping": [], "evidence": ["src/pages/VendorCompliance.jsx > handleImportMatched"]
},
{
"entity": "Event",
"operations": {
"update": {
"signature": "`updateEventMutation.mutate({ id, data })`",
"payload": {"assigned_staff": "array", "shifts": "array", "requested": "number", "status": "string" },
"optional_fields": ["assigned_staff", "shifts", "requested", "status"],
"required_fields": []
}
}, "fields_sent_but_not_supported": "UNKNOWN", "missing_fields_required_for_scoping": [], "evidence": ["src/pages/VendorOrders.jsx > autoAssignMutation"]
},
{
"entity": "Staff",
"operations": { "create": { "signature": "UNKNOWN", "payload": "UNKNOWN" } },
"fields_sent_but_not_supported": "UNKNOWN", "missing_fields_required_for_scoping": ["`vendor_id` es esencial y debe ser añadido en el frontend antes de enviar a la API."], "evidence": ["src/pages/AddStaff.jsx (File not analyzed but existence is confirmed)"]
}
],
"top_gaps": [
{ "issue": "INCONSISTENCIA CRÍTICA en Scoping de Datos", "impact": "MUY ALTO. El vendor puede no ver datos que le pertenecen. `Invoices.jsx` filtra por `user.vendor_id` mientras que `VendorOrders.jsx` y `StaffDirectory.jsx` filtran por `user.id`. Si el objeto `user` no tiene la propiedad `vendor_id` o es diferente de `id`, las facturas no aparecerán. Esto es un bug severo.", "evidence": ["src/pages/Invoices.jsx", "src/pages/VendorOrders.jsx"] },
{ "issue": "Mecanismo de Scoping Frágil y Múltiple", "impact": "ALTO. El código recurrentemente usa `vendor_id === user.id || vendor_name === user.company_name || created_by === user.email`. Depender de `vendor_name` o `created_by` es muy arriesgado. Un cambio de nombre de la empresa o del email del creador podría 'desvincular' datos, haciéndolos inaccesibles.", "evidence": ["src/pages/StaffDirectory.jsx", "src/pages/VendorDashboard.jsx"] },
{ "issue": "Riesgo de Datos Huérfanos en Creación", "impact": "ALTO. La UI permite al vendor crear entidades como `Staff`. Sin embargo, no se ha verificado que el `vendor_id` se esté inyectando de forma garantizada en el payload de creación. Si el frontend no añade `vendor_id: user.id`, ese nuevo staff podría crearse sin dueño, siendo invisible para todos.", "evidence": ["Workflow 'Vendor - Añadir nuevo miembro de staff'"] },
{ "issue": "Seguridad de Rutas Basada solo en Ocultación de UI", "impact": "MEDIO. No hay guards de rol a nivel de router. Un vendor podría acceder a `/UserManagement` o `/EnterpriseManagement` si conoce la URL. Aunque la página probablemente no funcione por falta de datos, esto representa un modelo de seguridad débil.", "evidence": ["src/pages/index.jsx", "src/components/auth/ProtectedRoute.jsx"] },
{ "issue": "Componente de Desarrollo (`RoleSwitcher`) Presente", "impact": "BAJO. El componente `RoleSwitcher` que permite cambiar de rol arbitrariamente está presente en `Layout.jsx`. Aunque sea para desarrollo, su presencia en el código que podría llegar a producción es un riesgo de seguridad.", "evidence": ["src/pages/Layout.jsx", "src/components/dev/RoleSwitcher.jsx"] },
{ "issue": "Funcionalidad Incompleta Visible en la UI", "impact": "BAJO. Páginas como `VendorOrders` muestran un toggle para una vista 'Scheduler' que no está implementada. Lo mismo ocurre con `TaskBoard`. Esto degrada la experiencia de usuario.", "evidence": ["src/pages/VendorOrders.jsx"] },
{ "issue": "Permisos de Edición de Órdenes no Definidos", "impact": "BAJO. Un vendor puede navegar a `/EditEvent`, pero no está claro qué puede editar. Si la UI no deshabilita correctamente los campos restringidos, el vendor puede intentar guardar cambios que serán rechazados por el backend, causando frustración.", "evidence": ["src/pages/EditEvent.jsx"] }
],
"next_verifications": [
"AUDITAR `AddStaff.jsx`: Leer el código para encontrar el payload exacto de `base44.entities.Staff.create` y confirmar si `vendor_id` se está enviando.",
"AUDITAR `EditEvent.jsx` y su formulario `EventForm`: Determinar qué campos del formulario se deshabilitan cuando el usuario tiene el rol 'vendor'.",
"INVESTIGAR EL SCHEMA: Si es posible, revisar los archivos `.gql` de DataConnect para confirmar si hay `constraints` a nivel de base de datos que obliguen la presencia de `vendor_id` en `Staff`, `Events`, etc.",
"CLARIFICAR `user.vendor_id`: Investigar por qué el objeto `user` tendría una propiedad `vendor_id` separada de `id` y por qué `Invoices.jsx` es el único lugar que la usa."
]
}