Ir al contenido

Documentos

Ruta: /employee/my-portal/documents Componente: Documents.tsx Permiso requerido: my_portal

Acceso centralizado a documentación personal del empleado organizada en carpetas temáticas con navegación visual tipo explorador de archivos.


Vista principal 2x2:

  • 📄 Contratos — Documentos de contratos laborales
  • 🆔 Documentos — Documentos generales del empleado
  • 📋 Nóminas — Recibos y comprobantes de nómina
  • 📅 Ausencias — Justificantes de ausencias y permisos

Click en carpeta:

  • Navega a /employee/my-portal/documents/{folderId}
  • Carga componente DocumentFolder.tsx
  • Muestra archivos de esa categoría

Rutas de carpetas:

type FolderType = "contracts" | "documents" | "payrolls" | "absences";

Cada carpeta incluye:

  • Icono representativo con fondo verde claro
  • Nombre de la carpeta
  • Descripción breve
  • Efecto hover con borde verde
  • Transición suave

Configuración de carpetas
interface FolderConfig {
id: FolderType;
label: string;
icon: React.ComponentType<{ className?: string }>;
description: string;
}
const folders: FolderConfig[] = [
{
id: "contracts",
label: "Contratos",
icon: FileCheck,
description: "Contratos laborales"
},
{
id: "documents",
label: "Documentos",
icon: IdCard,
description: "Documentos generales"
},
{
id: "payrolls",
label: "Nóminas",
icon: FileText,
description: "Recibos de nómina"
},
{
id: "absences",
label: "Ausencias",
icon: Calendar,
description: "Justificantes de ausencias"
}
];
Navegación programática
const navigate = useNavigate();
const handleFolderClick = (folderId: FolderType) => {
// Navegación relativa para compatibilidad con cualquier basePath
navigate(folderId);
};

Resultado: Navega a /employee/my-portal/documents/{folderId}

Componente de carpeta
<Card
key={folder.id}
className="cursor-pointer hover:shadow-md hover:border-primary/30 transition-all duration-200 group"
onClick={() => handleFolderClick(folder.id)}
>
<CardContent className="p-6 flex flex-col items-center justify-center text-center min-h-[140px]">
<div className="w-14 h-14 rounded-xl bg-primary/10 flex items-center justify-center mb-3 group-hover:bg-primary/20 transition-colors">
<IconComponent className="h-7 w-7 text-primary" />
</div>
<span className="font-medium text-foreground">{folder.label}</span>
</CardContent>
</Card>

Transiciones:

  • hover:shadow-md — Sombra elevada
  • hover:border-primary/30 — Borde verde sutil
  • group-hover:bg-primary/20 — Fondo icono más intenso
  • transition-all duration-200 — Animación suave
Componente DocumentFolder

El componente DocumentFolder.tsx se encarga de:

Funcionalidades:

  1. Detectar folderId desde URL params
  2. Consultar documentos de esa categoría
  3. Mostrar lista con previews
  4. Permitir descarga individual
  5. Botón de volver a carpetas

Query de documentos:

const { folderId } = useParams<{ folderId: FolderType }>();
const { data: documents } = useQuery({
queryKey: ['employee-documents', employeeId, folderId],
queryFn: async () => {
const { data, error } = await supabase
.from('employee_documents')
.select('*')
.eq('employee_id', employeeId)
.eq('folder_type', folderId)
.order('created_at', { ascending: false });
if (error) throw error;
return data;
},
enabled: !!employeeId && !!folderId,
});

Tabla:

ColumnaTipoDescripción
iduuidID del documento
employee_iduuidEmpleado propietario
folder_typetext’contracts’, ‘documents’, ‘payrolls’, ‘absences’
file_nametextNombre del archivo
file_urltextURL en Supabase Storage
file_sizeintegerTamaño en bytes
uploaded_attimestamptzFecha de carga

<div className="grid grid-cols-2 gap-4 max-w-2xl mx-auto">
{folders.map((folder) => (
<Card key={folder.id}>
{/* Contenido de la carpeta */}
</Card>
))}
</div>

Responsive:

  • Desktop: 2 columnas (2x2)
  • Mobile: 2 columnas compactas
  • Max-width: 2xl para centrado

Fondo del icono:

className="bg-primary/10 group-hover:bg-primary/20"

Icono:

className="text-primary"

Borde hover:

className="hover:border-primary/30"

Si una carpeta está vacía (en DocumentFolder):

<Card>
<CardContent className="py-12 text-center">
<FolderOpen className="h-16 w-16 mx-auto mb-4 text-muted-foreground/50" />
<h3 className="text-lg font-semibold mb-2">Sin documentos</h3>
<p className="text-muted-foreground max-w-md mx-auto">
Aquí aparecerán tus documentos de {folderLabel} cuando estén disponibles
</p>
</CardContent>
</Card>

Desde Nóminas:

  • Al descargar PDF → Guarda copia en carpeta payrolls
  • Link directo a /employee/my-portal/documents/payrolls

Desde Contratos:

  • Al descargar contrato → Disponible en contracts
  • Link en footer del sheet

Desde Ausencias:

  • Al subir justificante → Se almacena en absences
  • Acceso directo desde solicitud

Los documentos se sincronizan automáticamente:

  • Nóminas firmadas → Carpeta Nóminas
  • Contratos firmados → Carpeta Contratos
  • Justificantes aprobados → Carpeta Ausencias
  • Documentos RRHH → Carpeta Documentos

RLS Policy:

  • Empleados solo ven sus propios documentos
  • Filtrado por employee_id = auth.uid()
  • No pueden eliminar documentos subidos por RRHH
  • Pueden descargar todos los documentos de sus carpetas

Storage Bucket:

  • Bucket: employee-documents
  • Ruta: {business_id}/{employee_id}/{folder_type}/{filename}
  • Signed URLs con expiración de 1 hora