feat: agregar edicion de Trabajos de Backup desde la UI web
This commit is contained in:
@@ -7,7 +7,8 @@ import {
|
||||
Folder,
|
||||
Filter,
|
||||
Calendar,
|
||||
X
|
||||
X,
|
||||
Edit2
|
||||
} from 'lucide-react';
|
||||
import { BackupJobItem, ClientItem, api, SystemSettingsResponse } from '../services/api';
|
||||
|
||||
@@ -37,8 +38,9 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
|
||||
skip_readonly: false,
|
||||
sync_deletions: false,
|
||||
});
|
||||
const [editingJobId, setEditingJobId] = useState<number | null>(null);
|
||||
|
||||
const handleCreateJob = async (e: React.FormEvent) => {
|
||||
const handleSubmitJob = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!formData.client_id) {
|
||||
alert('Por favor selecciona un cliente');
|
||||
@@ -46,16 +48,41 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
await api.createJob(formData);
|
||||
if (editingJobId !== null) {
|
||||
await api.updateJob(editingJobId, formData);
|
||||
} else {
|
||||
await api.createJob(formData);
|
||||
}
|
||||
setShowModal(false);
|
||||
onRefresh();
|
||||
} catch (err: any) {
|
||||
alert(`Error creando trabajo: ${err.message}`);
|
||||
alert(`Error guardando trabajo: ${err.message}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditClick = (job: BackupJobItem) => {
|
||||
setEditingJobId(job.id);
|
||||
setFormData({
|
||||
client_id: job.client_id,
|
||||
name: job.name,
|
||||
source_path: job.source_path,
|
||||
file_patterns: job.file_patterns,
|
||||
schedule_cron: job.schedule_cron,
|
||||
keep_daily: job.keep_daily,
|
||||
keep_weekly: job.keep_weekly,
|
||||
keep_monthly: job.keep_monthly,
|
||||
min_stable_time_seconds: job.min_stable_time_seconds,
|
||||
exclude_patterns: job.exclude_patterns || '',
|
||||
skip_hidden: job.skip_hidden,
|
||||
skip_system: job.skip_system,
|
||||
skip_readonly: job.skip_readonly,
|
||||
sync_deletions: job.sync_deletions,
|
||||
});
|
||||
setShowModal(true);
|
||||
};
|
||||
|
||||
const handleTrigger = async (job: BackupJobItem) => {
|
||||
try {
|
||||
await api.triggerJob(job.id);
|
||||
@@ -98,6 +125,7 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
|
||||
alert('Primero debes registrar al menos un cliente Windows.');
|
||||
return;
|
||||
}
|
||||
setEditingJobId(null);
|
||||
setFormData({
|
||||
client_id: clients[0].id,
|
||||
name: '',
|
||||
@@ -195,6 +223,14 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
|
||||
<Play size={13} />
|
||||
Ejecutar
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-secondary"
|
||||
style={{ padding: '6px 10px', fontSize: '0.78rem', color: 'var(--text-muted)' }}
|
||||
onClick={() => handleEditClick(job)}
|
||||
title="Editar parámetros del trabajo"
|
||||
>
|
||||
<Edit2 size={13} />
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
style={{ padding: '6px 10px', fontSize: '0.78rem' }}
|
||||
@@ -222,18 +258,16 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
|
||||
{/* Create Job Modal */}
|
||||
{showModal && (
|
||||
<div className="modal-backdrop">
|
||||
<div className="modal-card">
|
||||
<div className="modal-content" style={{ maxWidth: '640px' }}>
|
||||
<div className="modal-header">
|
||||
<h3 style={{ fontSize: '1.1rem', fontWeight: 700 }}>Nuevo Trabajo de Backup</h3>
|
||||
<button
|
||||
style={{ background: 'transparent', border: 'none', color: 'var(--text-muted)', cursor: 'pointer' }}
|
||||
onClick={() => setShowModal(false)}
|
||||
>
|
||||
<X size={20} />
|
||||
<h4 style={{ fontSize: '1.1rem', fontWeight: 700 }}>
|
||||
{editingJobId ? 'Editar Trabajo de Backup' : 'Crear Trabajo de Backup'}
|
||||
</h4>
|
||||
<button className="modal-close" onClick={() => setShowModal(false)}>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleCreateJob}>
|
||||
<form onSubmit={handleSubmitJob}>
|
||||
<div className="form-group">
|
||||
<label>Cliente Windows Destino:</label>
|
||||
<select
|
||||
@@ -401,12 +435,12 @@ export const JobsView: React.FC<JobsViewProps> = ({ jobs, clients, onRefresh, se
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '12px', marginTop: '24px' }}>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={() => setShowModal(false)}>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" className="btn btn-primary" disabled={loading}>
|
||||
{loading ? 'Guardando...' : 'Crear Trabajo'}
|
||||
{loading ? 'Guardando...' : editingJobId ? 'Actualizar Trabajo' : 'Crear Trabajo'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -210,6 +210,27 @@ export const api = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(jobData),
|
||||
}),
|
||||
updateJob: (jobId: number, jobData: Partial<{
|
||||
client_id: number;
|
||||
name: string;
|
||||
source_path: string;
|
||||
file_patterns: string;
|
||||
exclude_patterns: string;
|
||||
skip_hidden: boolean;
|
||||
skip_system: boolean;
|
||||
skip_readonly: boolean;
|
||||
sync_deletions: boolean;
|
||||
schedule_cron: string;
|
||||
keep_daily: number;
|
||||
keep_weekly: number;
|
||||
keep_monthly: number;
|
||||
min_stable_time_seconds: number;
|
||||
is_active: boolean;
|
||||
}>) =>
|
||||
request<BackupJobItem>(`/jobs/${jobId}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(jobData),
|
||||
}),
|
||||
triggerJob: (jobId: number) =>
|
||||
request<{ message: string }>(`/jobs/${jobId}/trigger`, { method: 'POST' }),
|
||||
deleteJob: (jobId: number) =>
|
||||
|
||||
Reference in New Issue
Block a user