feat: implementar Fase 2 del roadmap (Modo Replica Exacta / Mirroring)

This commit is contained in:
Carlos Tello
2026-08-14 21:50:00 -03:00
parent a72cf1480e
commit a1cafff90e
14 changed files with 421 additions and 14 deletions
+73 -2
View File
@@ -31,6 +31,11 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
keep_weekly: 4,
keep_monthly: 12,
min_stable_time_seconds: 60,
exclude_patterns: '',
skip_hidden: false,
skip_system: false,
skip_readonly: false,
sync_deletions: false,
});
const handleCreateJob = async (e: React.FormEvent) => {
@@ -103,6 +108,11 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
keep_weekly: settings?.default_keep_weekly ?? 4,
keep_monthly: settings?.default_keep_monthly ?? 12,
min_stable_time_seconds: 60,
exclude_patterns: '',
skip_hidden: false,
skip_system: false,
skip_readonly: false,
sync_deletions: false,
});
setShowModal(true);
}}
@@ -136,7 +146,14 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
{job.job_code}
</span>
</td>
<td style={{ fontWeight: 600 }}>{job.name}</td>
<td style={{ fontWeight: 600 }}>
{job.name}
{job.sync_deletions && (
<span className="badge" style={{ backgroundColor: 'rgba(6, 182, 212, 0.15)', color: 'var(--accent-cyan)', marginLeft: '8px', fontSize: '0.7rem', padding: '2px 6px', border: '1px solid rgba(6, 182, 212, 0.3)' }}>
Réplica
</span>
)}
</td>
<td style={{ fontSize: '0.84rem', color: 'var(--text-muted)' }}>
{getClientName(job.client_id)}
</td>
@@ -257,7 +274,7 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
/>
</div>
<div className="form-group">
<label>Filtros de Archivo:</label>
<label>Filtros de Archivo (Inclusión):</label>
<input
type="text"
required
@@ -294,6 +311,60 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
</div>
</div>
<div style={{ borderTop: '1px solid rgba(255,255,255,0.08)', paddingTop: '16px', marginTop: '12px' }}>
<h4 style={{ fontSize: '0.9rem', fontWeight: 600, color: 'var(--accent-cyan)', marginBottom: '12px' }}>
Filtros Avanzados (Estilo Karen's Replicator)
</h4>
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: '12px', marginBottom: '12px' }}>
<div className="form-group">
<label>Filtros de Exclusión (Separados por coma):</label>
<input
type="text"
className="form-input"
placeholder="Ej: *.tmp, *.log, node_modules, obj"
value={formData.exclude_patterns}
onChange={(e) => setFormData({ ...formData, exclude_patterns: e.target.value })}
/>
</div>
</div>
<div style={{ display: 'flex', gap: '18px', marginBottom: '16px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.84rem', cursor: 'pointer' }}>
<input
type="checkbox"
checked={formData.skip_hidden}
onChange={(e) => setFormData({ ...formData, skip_hidden: e.target.checked })}
/>
Omitir Archivos Ocultos
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.84rem', cursor: 'pointer' }}>
<input
type="checkbox"
checked={formData.skip_system}
onChange={(e) => setFormData({ ...formData, skip_system: e.target.checked })}
/>
Omitir Archivos de Sistema
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.84rem', cursor: 'pointer' }}>
<input
type="checkbox"
checked={formData.skip_readonly}
onChange={(e) => setFormData({ ...formData, skip_readonly: e.target.checked })}
/>
Omitir Solo Lectura
</label>
</div>
<div style={{ display: 'flex', gap: '18px', marginBottom: '16px', borderTop: '1px dashed rgba(255,255,255,0.08)', paddingTop: '10px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '6px', fontSize: '0.84rem', cursor: 'pointer', color: 'var(--accent-cyan)' }}>
<input
type="checkbox"
checked={formData.sync_deletions}
onChange={(e) => setFormData({ ...formData, sync_deletions: e.target.checked })}
/>
Modo Réplica Exacta (Eliminar en destino si se elimina en origen)
</label>
</div>
</div>
<div className="form-group">
<label>Políticas de Retención (Conservar copias):</label>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '12px' }}>