Hide advanced signing options by default
This commit is contained in:
+5
-1
@@ -2,10 +2,14 @@
|
|||||||
|
|
||||||
## Versión actual
|
## Versión actual
|
||||||
|
|
||||||
- Versión base: `1.2.12`
|
- Versión base: `1.2.13`
|
||||||
|
|
||||||
## Historial
|
## Historial
|
||||||
|
|
||||||
|
### 1.2.13 - 2026-04-27
|
||||||
|
|
||||||
|
- Web y Android ocultan por defecto los detalles avanzados de `Firmar ahora` y los muestran solo bajo demanda con botón de ojo.
|
||||||
|
|
||||||
### 1.2.12 - 2026-04-23
|
### 1.2.12 - 2026-04-23
|
||||||
|
|
||||||
- Web reordena el menú principal para mostrar `Firmar ahora` en segunda posición y `Mis documentos` justo debajo.
|
- Web reordena el menú principal para mostrar `Firmar ahora` en segunda posición y `Mis documentos` justo debajo.
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ android {
|
|||||||
applicationId "net.linkedcorp.linkedsign"
|
applicationId "net.linkedcorp.linkedsign"
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionCode 10212
|
versionCode 10213
|
||||||
versionName "1.2.12"
|
versionName "1.2.13"
|
||||||
|
|
||||||
buildConfigField "String", "API_BASE_URL", "\"${apiBaseUrl}\""
|
buildConfigField "String", "API_BASE_URL", "\"${apiBaseUrl}\""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1330,6 +1330,7 @@ private fun SignScreen(
|
|||||||
var certPassword by rememberSaveable { mutableStateOf("") }
|
var certPassword by rememberSaveable { mutableStateOf("") }
|
||||||
var reason by rememberSaveable { mutableStateOf("Firmado digitalmente") }
|
var reason by rememberSaveable { mutableStateOf("Firmado digitalmente") }
|
||||||
var location by rememberSaveable { mutableStateOf("") }
|
var location by rememberSaveable { mutableStateOf("") }
|
||||||
|
var showAdvancedOptions by remember { mutableStateOf(false) }
|
||||||
var currentPageIndex by rememberSaveable(selectedDocumentId) { mutableStateOf(0) }
|
var currentPageIndex by rememberSaveable(selectedDocumentId) { mutableStateOf(0) }
|
||||||
var preview by remember(selectedDocumentId) { mutableStateOf<PdfPreviewState?>(null) }
|
var preview by remember(selectedDocumentId) { mutableStateOf<PdfPreviewState?>(null) }
|
||||||
var placement by remember(selectedDocumentId) { mutableStateOf<SignaturePlacement?>(null) }
|
var placement by remember(selectedDocumentId) { mutableStateOf<SignaturePlacement?>(null) }
|
||||||
@@ -1339,6 +1340,9 @@ private fun SignScreen(
|
|||||||
val selectedDocument = state.documents.firstOrNull { it.id.toString() == selectedDocumentId }
|
val selectedDocument = state.documents.firstOrNull { it.id.toString() == selectedDocumentId }
|
||||||
val signerLabel = state.userName ?: state.userEmail ?: "Firmante"
|
val signerLabel = state.userName ?: state.userEmail ?: "Firmante"
|
||||||
val signatureStyle = state.signaturePreferences?.signatureStyle ?: "qr"
|
val signatureStyle = state.signaturePreferences?.signatureStyle ?: "qr"
|
||||||
|
val qrPosition = state.signaturePreferences?.qrPosition ?: "left"
|
||||||
|
val qrBackground = state.signaturePreferences?.qrBackground ?: "transparent"
|
||||||
|
val qrBorder = state.signaturePreferences?.qrBorder ?: "none"
|
||||||
|
|
||||||
LaunchedEffect(initialDocumentId, state.documents) {
|
LaunchedEffect(initialDocumentId, state.documents) {
|
||||||
val documentId = initialDocumentId ?: return@LaunchedEffect
|
val documentId = initialDocumentId ?: return@LaunchedEffect
|
||||||
@@ -1360,7 +1364,6 @@ private fun SignScreen(
|
|||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
SectionCard(title = "Vista previa y firma") {
|
SectionCard(title = "Vista previa y firma") {
|
||||||
Text("Esta versión usa la configuración visual de firma ya guardada en tu cuenta.", color = TextMuted, fontSize = AppFontSizes.sm)
|
|
||||||
DropdownSelector(
|
DropdownSelector(
|
||||||
label = "Documento",
|
label = "Documento",
|
||||||
options = state.documents.associate { it.id.toString() to (it.title ?: it.filename ?: "Documento ${it.id}") },
|
options = state.documents.associate { it.id.toString() to (it.title ?: it.filename ?: "Documento ${it.id}") },
|
||||||
@@ -1374,8 +1377,37 @@ private fun SignScreen(
|
|||||||
onSelect = { selectedCertificateId = it }
|
onSelect = { selectedCertificateId = it }
|
||||||
)
|
)
|
||||||
AppTextField(value = certPassword, onValueChange = { certPassword = it }, label = "Contraseña del certificado", isPassword = true)
|
AppTextField(value = certPassword, onValueChange = { certPassword = it }, label = "Contraseña del certificado", isPassword = true)
|
||||||
AppTextField(value = reason, onValueChange = { reason = it }, label = "Razón")
|
OutlinedActionButton(
|
||||||
AppTextField(value = location, onValueChange = { location = it }, label = "Ubicación")
|
label = if (showAdvancedOptions) "Ocultar detalles" else "Mostrar detalles",
|
||||||
|
icon = if (showAdvancedOptions) Icons.Outlined.VisibilityOff else Icons.Outlined.Visibility
|
||||||
|
) {
|
||||||
|
showAdvancedOptions = !showAdvancedOptions
|
||||||
|
}
|
||||||
|
if (showAdvancedOptions) {
|
||||||
|
Text("Esta versión usa la configuración visual de firma ya guardada en tu cuenta.", color = TextMuted, fontSize = AppFontSizes.sm)
|
||||||
|
AppTextField(value = reason, onValueChange = { reason = it }, label = "Razón")
|
||||||
|
AppTextField(value = location, onValueChange = { location = it }, label = "Ubicación")
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(18.dp),
|
||||||
|
color = SurfaceSoft,
|
||||||
|
border = BorderStroke(1.dp, StrokeSoft)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
|
) {
|
||||||
|
Text("Estilo aplicado", color = BrandDark, fontSize = AppFontSizes.base, fontWeight = FontWeight.SemiBold)
|
||||||
|
Text("La firma usará tu configuración guardada en el menú Estilo Firma.", color = TextMuted, fontSize = AppFontSizes.sm)
|
||||||
|
Text("Tipo: $signatureStyle", color = BrandDark, fontSize = AppFontSizes.sm)
|
||||||
|
if (signatureStyle == "qr") {
|
||||||
|
Text("Posición QR: ${if (qrPosition == "left") "Izquierda" else "Derecha"}", color = BrandDark, fontSize = AppFontSizes.sm)
|
||||||
|
Text("Fondo: ${if (qrBackground == "transparent") "Transparente" else "Suave"}", color = BrandDark, fontSize = AppFontSizes.sm)
|
||||||
|
Text("Borde: ${if (qrBorder == "none") "Sin borde" else "Con borde"}", color = BrandDark, fontSize = AppFontSizes.sm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
OutlinedActionButton("Cargar PDF para ubicar firma", Icons.Outlined.Description) {
|
OutlinedActionButton("Cargar PDF para ubicar firma", Icons.Outlined.Description) {
|
||||||
val documentId = selectedDocumentId.toIntOrNull() ?: return@OutlinedActionButton
|
val documentId = selectedDocumentId.toIntOrNull() ?: return@OutlinedActionButton
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "linkedsign-backend",
|
"name": "linkedsign-backend",
|
||||||
"version": "1.2.12",
|
"version": "1.2.13",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Initial backend for LinkedSign",
|
"description": "Initial backend for LinkedSign",
|
||||||
"main": "src/server.js",
|
"main": "src/server.js",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.2.12",
|
"version": "1.2.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1059,6 +1059,13 @@ select {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-inline-toggle {
|
||||||
|
width: fit-content;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.sign-form-card form { display: flex; flex-direction: column; gap: 14px; }
|
.sign-form-card form { display: flex; flex-direction: column; gap: 14px; }
|
||||||
|
|
||||||
.sign-info-panel { display: flex; flex-direction: column; gap: 14px; }
|
.sign-info-panel { display: flex; flex-direction: column; gap: 14px; }
|
||||||
@@ -2314,6 +2321,11 @@ select {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-inline-toggle {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.steps-bar {
|
.steps-bar {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useSearchParams } from 'react-router-dom';
|
|||||||
import { listDocuments, downloadDocument, uploadDocument } from '../../api/documents';
|
import { listDocuments, downloadDocument, uploadDocument } from '../../api/documents';
|
||||||
import { listCertificates } from '../../api/certificates';
|
import { listCertificates } from '../../api/certificates';
|
||||||
import { signDocument, getSignatures, downloadSigned } from '../../api/signatures';
|
import { signDocument, getSignatures, downloadSigned } from '../../api/signatures';
|
||||||
import { PenTool, FileText, Shield, CheckCircle, MapPin, Download, Check, Plus } from 'lucide-react';
|
import { PenTool, FileText, Shield, CheckCircle, MapPin, Download, Check, Plus, Eye, EyeOff } from 'lucide-react';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import PasswordModal from '../../components/ui/PasswordModal';
|
import PasswordModal from '../../components/ui/PasswordModal';
|
||||||
import PdfSignaturePlacer from '../../components/ui/PdfSignaturePlacer';
|
import PdfSignaturePlacer from '../../components/ui/PdfSignaturePlacer';
|
||||||
@@ -40,6 +40,7 @@ export default function SignaturesPage() {
|
|||||||
const [pendingSign, setPendingSign] = useState(null);
|
const [pendingSign, setPendingSign] = useState(null);
|
||||||
const [uploadingDocument, setUploadingDocument] = useState(false);
|
const [uploadingDocument, setUploadingDocument] = useState(false);
|
||||||
const [pendingLocalDocument, setPendingLocalDocument] = useState(null);
|
const [pendingLocalDocument, setPendingLocalDocument] = useState(null);
|
||||||
|
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false);
|
||||||
const documentInputRef = useRef(null);
|
const documentInputRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -293,22 +294,34 @@ export default function SignaturesPage() {
|
|||||||
</select>
|
</select>
|
||||||
{certs.length === 0 && <small className="field-hint">No tienes certificados activos.</small>}
|
{certs.length === 0 && <small className="field-hint">No tienes certificados activos.</small>}
|
||||||
</div>
|
</div>
|
||||||
<div className="field">
|
<button
|
||||||
<label>Razón de firma</label>
|
type="button"
|
||||||
<input type="text" name="reason" value={form.reason} onChange={handleChange} />
|
className="btn btn-ghost btn-inline-toggle"
|
||||||
</div>
|
onClick={() => setShowAdvancedOptions((current) => !current)}
|
||||||
<div className="field">
|
>
|
||||||
<label>Ubicación</label>
|
{showAdvancedOptions ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
<input type="text" name="location" value={form.location} onChange={handleChange} placeholder="Ciudad, País..." />
|
{showAdvancedOptions ? 'Ocultar detalles' : 'Mostrar detalles'}
|
||||||
</div>
|
</button>
|
||||||
<div className="info-box">
|
{showAdvancedOptions && (
|
||||||
<h4>Estilo aplicado</h4>
|
<>
|
||||||
<p>La firma usará tu configuración guardada en el menú <strong>Estilo Firma</strong>.</p>
|
<div className="field">
|
||||||
<p><strong>Tipo:</strong> {form.signatureStyle}</p>
|
<label>Razón de firma</label>
|
||||||
{form.signatureStyle === 'qr' && <p><strong>Posición QR:</strong> {form.qrPosition === 'left' ? 'Izquierda' : 'Derecha'}</p>}
|
<input type="text" name="reason" value={form.reason} onChange={handleChange} />
|
||||||
{form.signatureStyle === 'qr' && <p><strong>Fondo:</strong> {form.qrBackground === 'transparent' ? 'Transparente' : 'Suave'}</p>}
|
</div>
|
||||||
{form.signatureStyle === 'qr' && <p><strong>Borde:</strong> {form.qrBorder === 'none' ? 'Sin borde' : 'Con borde'}</p>}
|
<div className="field">
|
||||||
</div>
|
<label>Ubicación</label>
|
||||||
|
<input type="text" name="location" value={form.location} onChange={handleChange} placeholder="Ciudad, País..." />
|
||||||
|
</div>
|
||||||
|
<div className="info-box">
|
||||||
|
<h4>Estilo aplicado</h4>
|
||||||
|
<p>La firma usará tu configuración guardada en el menú <strong>Estilo Firma</strong>.</p>
|
||||||
|
<p><strong>Tipo:</strong> {form.signatureStyle}</p>
|
||||||
|
{form.signatureStyle === 'qr' && <p><strong>Posición QR:</strong> {form.qrPosition === 'left' ? 'Izquierda' : 'Derecha'}</p>}
|
||||||
|
{form.signatureStyle === 'qr' && <p><strong>Fondo:</strong> {form.qrBackground === 'transparent' ? 'Transparente' : 'Suave'}</p>}
|
||||||
|
{form.signatureStyle === 'qr' && <p><strong>Borde:</strong> {form.qrBorder === 'none' ? 'Sin borde' : 'Con borde'}</p>}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<button className="btn btn-primary btn-full" onClick={handleGoToPlace}>
|
<button className="btn btn-primary btn-full" onClick={handleGoToPlace}>
|
||||||
<MapPin size={16} /> Elegir posición de firma →
|
<MapPin size={16} /> Elegir posición de firma →
|
||||||
</button>
|
</button>
|
||||||
@@ -372,7 +385,7 @@ export default function SignaturesPage() {
|
|||||||
<div className="step-nav">
|
<div className="step-nav">
|
||||||
<button className="btn btn-ghost" onClick={() => setStep(0)}>← Volver</button>
|
<button className="btn btn-ghost" onClick={() => setStep(0)}>← Volver</button>
|
||||||
<p className="step-desc">
|
<p className="step-desc">
|
||||||
Haz clic donde quieres que aparezca la firma en <strong>{selectedDoc?.title}</strong>
|
Haz clic donde quieres que aparezca la firma en <strong>{selectedDocumentTitle}</strong>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<PdfSignaturePlacer
|
<PdfSignaturePlacer
|
||||||
@@ -396,15 +409,27 @@ export default function SignaturesPage() {
|
|||||||
<h3>Posición confirmada — listo para firmar</h3>
|
<h3>Posición confirmada — listo para firmar</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="sign-confirm-details">
|
<div className="sign-confirm-details">
|
||||||
<p><strong>Documento:</strong> {selectedDoc?.title}</p>
|
<p><strong>Documento:</strong> {selectedDocumentTitle}</p>
|
||||||
<p><strong>Certificado:</strong> {selectedCert?.alias}</p>
|
<p><strong>Certificado:</strong> {selectedCert?.alias}</p>
|
||||||
<p><strong>Razón:</strong> {form.reason}</p>
|
|
||||||
{form.location && <p><strong>Ubicación:</strong> {form.location}</p>}
|
|
||||||
<p><strong>Estilo visual:</strong> {form.signatureStyle}</p>
|
|
||||||
{form.signatureStyle === 'qr' && <p><strong>Posición QR:</strong> {form.qrPosition === 'left' ? 'Izquierda' : 'Derecha'}</p>}
|
|
||||||
{form.signatureStyle === 'qr' && <p><strong>Fondo:</strong> {form.qrBackground === 'transparent' ? 'Transparente' : 'Suave'}</p>}
|
|
||||||
{form.signatureStyle === 'qr' && <p><strong>Borde:</strong> {form.qrBorder === 'none' ? 'Sin borde' : 'Con borde'}</p>}
|
|
||||||
<p><strong>Página:</strong> {(sigPlacement?.page ?? 0) + 1}</p>
|
<p><strong>Página:</strong> {(sigPlacement?.page ?? 0) + 1}</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-ghost btn-inline-toggle"
|
||||||
|
onClick={() => setShowAdvancedOptions((current) => !current)}
|
||||||
|
>
|
||||||
|
{showAdvancedOptions ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
|
{showAdvancedOptions ? 'Ocultar detalles' : 'Mostrar detalles'}
|
||||||
|
</button>
|
||||||
|
{showAdvancedOptions && (
|
||||||
|
<>
|
||||||
|
<p><strong>Razón:</strong> {form.reason}</p>
|
||||||
|
{form.location && <p><strong>Ubicación:</strong> {form.location}</p>}
|
||||||
|
<p><strong>Estilo visual:</strong> {form.signatureStyle}</p>
|
||||||
|
{form.signatureStyle === 'qr' && <p><strong>Posición QR:</strong> {form.qrPosition === 'left' ? 'Izquierda' : 'Derecha'}</p>}
|
||||||
|
{form.signatureStyle === 'qr' && <p><strong>Fondo:</strong> {form.qrBackground === 'transparent' ? 'Transparente' : 'Suave'}</p>}
|
||||||
|
{form.signatureStyle === 'qr' && <p><strong>Borde:</strong> {form.qrBorder === 'none' ? 'Sin borde' : 'Con borde'}</p>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="sign-confirm-actions">
|
<div className="sign-confirm-actions">
|
||||||
<button className="btn btn-ghost" onClick={() => setStep(1)}>← Cambiar posición</button>
|
<button className="btn btn-ghost" onClick={() => setStep(1)}>← Cambiar posición</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user