Initial LinkedSign platform
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { listDocuments } from '../../api/documents';
|
||||
import { FileText, PenTool, CheckCircle2, CircleDot, Search } from 'lucide-react';
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { user } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const [stats, setStats] = useState({ docs: 0, certs: 0, signed: 0, pending: 0, review: 0 });
|
||||
const [recentDocs, setRecentDocs] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
listDocuments({ limit: 100 })
|
||||
.then((docsRes) => {
|
||||
const docs = docsRes.data.data.rows || [];
|
||||
const pending = docs.filter((d) => d.status === 'pending').length;
|
||||
const review = docs.filter((d) => d.status === 'partially_signed').length;
|
||||
setStats({
|
||||
docs: docsRes.data.data.total || 0,
|
||||
signed: docs.filter((d) => d.status === 'signed').length,
|
||||
pending,
|
||||
review,
|
||||
});
|
||||
setRecentDocs(docs.slice(0, 5));
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="step-bar">
|
||||
<div className="step">
|
||||
<div className="step-num done"><CheckCircle2 size={10} /></div>
|
||||
<span className="step-label done-label">Subir documento</span>
|
||||
</div>
|
||||
<div className="step-line done-line" />
|
||||
<div className="step">
|
||||
<div className="step-num done"><CheckCircle2 size={10} /></div>
|
||||
<span className="step-label done-label">Revisar contenido</span>
|
||||
</div>
|
||||
<div className="step-line done-line" />
|
||||
<div className="step">
|
||||
<div className="step-num active">3</div>
|
||||
<span className="step-label active-label">Firmar</span>
|
||||
</div>
|
||||
<div className="step-line" />
|
||||
<div className="step">
|
||||
<div className="step-num todo">4</div>
|
||||
<span className="step-label todo-label">Verificar identidad</span>
|
||||
</div>
|
||||
<div className="step-line" />
|
||||
<div className="step">
|
||||
<div className="step-num todo">5</div>
|
||||
<span className="step-label todo-label">Completado</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stats-row">
|
||||
<div className="stat-card" onClick={() => navigate('/documents')}>
|
||||
<div className="stat-label">Total documentos</div>
|
||||
<div className="stat-val">{stats.docs}</div>
|
||||
<span className="stat-badge badge-green">+{Math.max(stats.docs - stats.signed, 0)} este mes</span>
|
||||
</div>
|
||||
<div className="stat-card" onClick={() => navigate('/documents')}>
|
||||
<div className="stat-label">Pendientes de firma</div>
|
||||
<div className="stat-val">{stats.pending}</div>
|
||||
<span className="stat-badge badge-orange">Requieren acción</span>
|
||||
</div>
|
||||
<div className="stat-card" onClick={() => navigate('/signatures')}>
|
||||
<div className="stat-label">Firmados</div>
|
||||
<div className="stat-val">{stats.signed}</div>
|
||||
<span className="stat-badge badge-green">{stats.docs ? Math.round((stats.signed / stats.docs) * 100) : 0}% tasa de éxito</span>
|
||||
</div>
|
||||
<div className="stat-card" onClick={() => navigate('/documents')}>
|
||||
<div className="stat-label">En revisión</div>
|
||||
<div className="stat-val">{stats.review}</div>
|
||||
<span className="stat-badge badge-blue">En proceso</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="panel">
|
||||
<div className="panel-header">
|
||||
<span className="panel-title">Documentos recientes</span>
|
||||
<span className="panel-action" onClick={() => navigate('/documents')}>Ver todos</span>
|
||||
</div>
|
||||
<div className="table-header">
|
||||
<span className="th">Documento</span>
|
||||
<span className="th">Firmante</span>
|
||||
<span className="th">Fecha</span>
|
||||
<span className="th">Estado</span>
|
||||
<span className="th"></span>
|
||||
</div>
|
||||
|
||||
{recentDocs.map((doc) => {
|
||||
const status = doc.status === 'signed'
|
||||
? { cls: 'pill-signed', label: 'Firmado', icon: <CheckCircle2 size={12} /> }
|
||||
: doc.status === 'partially_signed'
|
||||
? { cls: 'pill-review', label: 'Revisión', icon: <Search size={12} /> }
|
||||
: { cls: 'pill-pending', label: 'Pendiente', icon: <CircleDot size={12} /> };
|
||||
return (
|
||||
<div className="table-row" key={doc.id}>
|
||||
<div className="doc-cell">
|
||||
<div className="doc-thumb" style={{ background: doc.status === 'signed' ? 'var(--c-success-bg)' : 'var(--c-info-bg)' }}>
|
||||
<FileText size={13} color={doc.status === 'signed' ? 'var(--c-success)' : 'var(--c-primary)'} />
|
||||
</div>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div className="doc-n">{doc.title}</div>
|
||||
<div className="doc-m">{((doc.file_size || 0) / 1024).toFixed(1)} KB</div>
|
||||
</div>
|
||||
</div>
|
||||
<span className="td">{user?.name || '—'}</span>
|
||||
<span className="td">{doc.created_at ? new Date(doc.created_at).toLocaleDateString('es-EC') : '—'}</span>
|
||||
<span className={`status-pill ${status.cls}`}><span className="dot" />{status.label}</span>
|
||||
<button className="action-ico" type="button" title={doc.status === 'signed' ? 'Ver documento' : 'Firmar'} onClick={() => navigate(doc.status === 'signed' ? '/documents' : `/signatures?docId=${doc.id}`)}>
|
||||
{doc.status === 'signed' ? <Search size={13} /> : <PenTool size={13} />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user