Revisar Solicitud
Exportar contenido
Acepta o rechaza una solicitud de acceso enviada por un asesor.
- Ve a Configuración → Asesores → Recibidas.
- Localiza la solicitud con estado “Pendiente”.
- Clic en “Revisar” — navega a página de revisión detallada.
- Revisa información del asesor:
- Nombre del asesor
- Email (si disponible)
- Permisos solicitados
- Ajusta los permisos si es necesario.
- Clic en “Aceptar Solicitud” o “Rechazar”.
Información mostrada
Sección titulada «Información mostrada»| Campo | Descripción |
|---|---|
| Nombre del asesor | Nombre proporcionado en la solicitud |
| Negocio | Nombre del negocio al que solicita acceso |
| Fecha de solicitud | Cuándo se envió la solicitud |
| Permisos solicitados | Lista de secciones y nivel de acceso pedido |
| Expira | Días restantes antes de expiración (7 días) |
Opciones de respuesta
Sección titulada «Opciones de respuesta»Aceptar solicitud
Sección titulada «Aceptar solicitud»- Revisa/ajusta los permisos.
- Clic en “Aceptar Solicitud” — se crea vínculo activo.
- Asesor recibe notificación de aceptación.
- Asesor obtiene acceso inmediato al negocio.
Rechazar solicitud
Sección titulada «Rechazar solicitud»- Clic en “Rechazar” — se abre confirmación.
- Confirma rechazo — solicitud marcada como rechazada.
- Asesor recibe notificación de rechazo.
- Solicitud ya no aparece en pendientes.
Cómo sé que lo hice bien
Sección titulada «Cómo sé que lo hice bien»Si aceptaste
Sección titulada «Si aceptaste»- Toast: “Solicitud aceptada correctamente”.
- Asesor aparece en pestaña “Activos”.
- Solicitud desaparece de “Recibidas”.
- Asesor puede acceder al negocio desde su panel.
Si rechazaste
Sección titulada «Si rechazaste»- Toast: “Solicitud rechazada”.
- Solicitud desaparece de “Recibidas”.
- Asesor recibe notificación del rechazo.
Errores comunes
Sección titulada «Errores comunes»| Mensaje | Causa | Solución |
|---|---|---|
| ”Solicitud no encontrada” | Token inválido o solicitud eliminada | Verifica en lista de recibidas |
| ”Solicitud expirada” | Pasaron más de 7 días | Asesor debe enviar nueva solicitud |
| ”Error al procesar solicitud” | Error de servidor | Reintenta o contacta soporte |
Detalles técnicos — Revisar Solicitud
Sección titulada «Detalles técnicos — Revisar Solicitud»Ver implementación técnica
Página de revisión: src/panels/owner/pages/ReviewAdvisorRequest.tsx
Edge Function: respond-advisor-to-owner-invitation
Navegación:
// Desde OwnerAdvisorInvitationsManagerconst handleReviewRequest = (invitation: ReceivedInvitation) => { navigate(`/review-advisor-request/${invitation.token}`);};Input de respuesta:
{ token: string; // Token de la solicitud response: 'accepted' | 'rejected'; finalPermissions?: Array<{ // Solo si accepted section: string; access: 'none' | 'read' | 'edit'; }>;}Lógica de aceptación:
// 1. Valida token y estadoconst { data: invitation } = await supabase .from('advisor_to_owner_invitations') .select('*') .eq('token', token) .eq('status', 'pending') .single();
if (!invitation || new Date(invitation.expires_at) < new Date()) { throw new Error('Solicitud inválida o expirada');}
// 2. Actualiza estado de solicitudawait supabase .from('advisor_to_owner_invitations') .update({ status: 'accepted', accepted_at: new Date().toISOString(), final_permissions: finalPermissions }) .eq('token', token);
// 3. Crea vínculo activoconst { data: link } = await supabase .from('advisor_client_links') .insert({ advisor_id: invitation.advisor_id, client_business_id: invitation.business_id, invited_by: invitation.owner_id, status: 'active', linked_at: new Date().toISOString() }) .select() .single();
// 4. Crea permisos granularesconst permissionsToInsert = finalPermissions.map(p => ({ advisor_id: invitation.advisor_id, client_business_id: invitation.business_id, section: p.section, access_level: p.access}));
await supabase .from('advisor_section_permissions') .insert(permissionsToInsert);
// 5. Asigna rol de asesor si no lo tieneconst { data: existingRole } = await supabase .from('user_roles') .select('*') .eq('user_id', invitation.advisor_id) .eq('role', 'advisor') .maybeSingle();
if (!existingRole) { await supabase .from('user_roles') .insert({ user_id: invitation.advisor_id, role: 'advisor' });}
// 6. Notifica al asesorawait supabase .from('advisor_notifications') .insert({ advisor_id: invitation.advisor_id, client_business_id: invitation.business_id, notification_type: 'access_granted', title: 'Solicitud aceptada', message: `Tu solicitud de acceso a ${businessName} ha sido aceptada` });Lógica de rechazo:
// Actualiza estado a rejectedawait supabase .from('advisor_to_owner_invitations') .update({ status: 'rejected', rejected_at: new Date().toISOString() }) .eq('token', token);
// Notifica al asesorawait supabase .from('advisor_notifications') .insert({ advisor_id: invitation.advisor_id, client_business_id: invitation.business_id, notification_type: 'access_denied', title: 'Solicitud rechazada', message: `Tu solicitud de acceso a ${businessName} ha sido rechazada` });Tabla: advisor_to_owner_invitations
{ id: UUID; advisor_id: UUID; // FK a auth.users owner_id: UUID; // FK a auth.users business_id: UUID; // FK a businesses status: 'pending' | 'accepted' | 'rejected' | 'expired'; token: string; // UUID único permissions: JSONB; // Permisos solicitados final_permissions: JSONB; // Permisos finales aceptados expires_at: TIMESTAMPTZ; // +7 días sent_at: TIMESTAMPTZ; accepted_at: TIMESTAMPTZ; // Cuando se acepta rejected_at: TIMESTAMPTZ; // Cuando se rechaza advisor_name: string; // Nombre del asesor}Query de diagnóstico:
-- Solicitud completa con detallesSELECT aoi.id, aoi.status, aoi.sent_at, aoi.expires_at, aoi.advisor_name, aoi.permissions as requested_permissions, aoi.final_permissions, b.name as business_name, p_owner.first_name || ' ' || p_owner.last_name as owner_name, p_advisor.email as advisor_emailFROM advisor_to_owner_invitations aoiJOIN businesses b ON aoi.business_id = b.idJOIN profiles p_owner ON aoi.owner_id = p_owner.user_idLEFT JOIN profiles p_advisor ON aoi.advisor_id = p_advisor.user_idWHERE aoi.token = '<token>';
-- Verificar vínculo creado tras aceptaciónSELECT acl.*, b.name as business_nameFROM advisor_client_links aclJOIN businesses b ON acl.client_business_id = b.idWHERE acl.advisor_id = '<advisor_id>' AND acl.client_business_id = '<business_id>' AND acl.status = 'active';