diff --git a/backend/src/modules/users/users.service.js b/backend/src/modules/users/users.service.js
index 8cbf5a1..cddeee7 100644
--- a/backend/src/modules/users/users.service.js
+++ b/backend/src/modules/users/users.service.js
@@ -1,6 +1,6 @@
const usersRepository = require('./users.repository');
-const ALLOWED_SIGNATURE_STYLES = ['classic', 'qr', 'professional', 'image'];
+const ALLOWED_SIGNATURE_STYLES = ['classic', 'qr', 'professional'];
const ALLOWED_QR_POSITIONS = ['left', 'right'];
const ALLOWED_QR_BACKGROUNDS = ['transparent', 'soft'];
const ALLOWED_QR_BORDERS = ['none', 'line'];
diff --git a/frontend/src/components/ui/SignatureStylePicker.jsx b/frontend/src/components/ui/SignatureStylePicker.jsx
index 4489baf..8bc0a23 100644
--- a/frontend/src/components/ui/SignatureStylePicker.jsx
+++ b/frontend/src/components/ui/SignatureStylePicker.jsx
@@ -1,5 +1,4 @@
-import { useRef } from 'react';
-import { QrCode, Briefcase, ImageIcon, Stamp, PanelsLeftBottom, PanelsRightBottom, CircleOff, Square } from 'lucide-react';
+import { QrCode, Briefcase, Stamp, PanelsLeftBottom, PanelsRightBottom, CircleOff, Square } from 'lucide-react';
const getQrPreviewWidth = (name = '') => {
const extraWidth = Math.max(0, name.trim().length - 18) * 4;
@@ -102,36 +101,11 @@ const STYLES = [
),
},
- {
- id: 'image',
- label: 'Imagen',
- icon: ImageIcon,
- desc: 'Sube tu firma manuscrita o imagen personalizada',
- preview: ({ name, date, image }) => (
-
-
-
- {image
- ?

- :
Tu firma aquí
- }
-
-
-
- {name}
- Firmado digitalmente
- {date}
-
-
- ),
- },
];
export default function SignatureStylePicker({
value,
onChange,
- signatureImage,
- onImageChange,
signerName,
qrPosition = 'left',
onQrPositionChange,
@@ -140,19 +114,8 @@ export default function SignatureStylePicker({
qrBorder = 'none',
onQrBorderChange,
}) {
- const fileRef = useRef();
const today = new Date().toLocaleDateString('es-EC');
- const handleImageFile = (e) => {
- const file = e.target.files[0];
- if (!file) return;
- if (!file.type.startsWith('image/')) return;
- const reader = new FileReader();
- reader.onload = (ev) => onImageChange(ev.target.result);
- reader.readAsDataURL(file);
- e.target.value = '';
- };
-
return (
@@ -176,7 +139,6 @@ export default function SignatureStylePicker({
{s.preview({
name: signerName || 'Milton González H.',
date: today,
- image: signatureImage,
qrPosition,
qrBackground,
qrBorder,
@@ -267,21 +229,6 @@ export default function SignatureStylePicker({
);
})}
-
- {value === 'image' && (
-
-
- {signatureImage && (
-
- )}
- Fondo transparente (PNG) recomendado para mejor resultado
-
-
- )}
);
}
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 3b75def..3cf6876 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -633,6 +633,21 @@ select {
padding: 6px 10px;
}
+.documents-page-size {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--gray-700);
+ font-size: 12px;
+}
+
+.documents-page-size select {
+ border: 1px solid var(--gray-300);
+ border-radius: 8px;
+ background: #fff;
+ padding: 6px 10px;
+}
+
.audit-table-header,
.audit-table-row {
grid-template-columns: minmax(220px, 1.2fr) 180px 120px minmax(260px, 1.5fr);
diff --git a/frontend/src/pages/auth/LoginPage.jsx b/frontend/src/pages/auth/LoginPage.jsx
index d0c2b23..cb38d66 100644
--- a/frontend/src/pages/auth/LoginPage.jsx
+++ b/frontend/src/pages/auth/LoginPage.jsx
@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useMemo, useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext';
import { Download, PenTool } from 'lucide-react';
@@ -11,6 +11,10 @@ export default function LoginPage() {
const [loading, setLoading] = useState(false);
const apiBaseUrl = (import.meta.env.VITE_API_URL || 'http://localhost:4000').replace(/\/$/, '');
const androidApkUrl = `${apiBaseUrl}/downloads/android-apk`;
+ const showAndroidDownload = useMemo(() => {
+ if (typeof navigator === 'undefined') return false;
+ return /Android/i.test(navigator.userAgent || '');
+ }, []);
const handleChange = (e) => setForm((f) => ({ ...f, [e.target.name]: e.target.value }));
@@ -60,10 +64,12 @@ export default function LoginPage() {
-
-
- Descargar app Android
-
+ {showAndroidDownload && (
+
+
+ Descargar app Android
+
+ )}
¿No tienes cuenta? Registrarse
diff --git a/frontend/src/pages/documents/DocumentsPage.jsx b/frontend/src/pages/documents/DocumentsPage.jsx
index 01c4ed6..299fed4 100644
--- a/frontend/src/pages/documents/DocumentsPage.jsx
+++ b/frontend/src/pages/documents/DocumentsPage.jsx
@@ -22,9 +22,13 @@ const STATUS_LABELS = {
export default function DocumentsPage() {
const navigate = useNavigate();
+ const pageSizeOptions = [5, 10, 15, 20];
const [docs, setDocs] = useState([]);
const [signedDocs, setSignedDocs] = useState([]);
const [total, setTotal] = useState(0);
+ const [pageSize, setPageSize] = useState(5);
+ const [docsPage, setDocsPage] = useState(1);
+ const [signedPage, setSignedPage] = useState(1);
const [loading, setLoading] = useState(true);
const [uploading, setUploading] = useState(false);
const fileInputRef = useRef();
@@ -53,6 +57,14 @@ export default function DocumentsPage() {
useEffect(() => { loadDocs(); }, []);
+ useEffect(() => { setDocsPage(1); }, [pageSize, docs.length]);
+ useEffect(() => { setSignedPage(1); }, [pageSize, signedDocs.length]);
+
+ const docsTotalPages = Math.max(1, Math.ceil(docs.length / pageSize));
+ const signedTotalPages = Math.max(1, Math.ceil(signedDocs.length / pageSize));
+ const pagedDocs = docs.slice((docsPage - 1) * pageSize, docsPage * pageSize);
+ const pagedSignedDocs = signedDocs.slice((signedPage - 1) * pageSize, signedPage * pageSize);
+
const handleUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
@@ -160,6 +172,18 @@ export default function DocumentsPage() {
{total} documento{total !== 1 ? 's' : ''} original{total !== 1 ? 'es' : ''}
+
+
+
+