Update frontend/components/JobForm.tsx
Browse files- frontend/components/JobForm.tsx +47 -14
frontend/components/JobForm.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import EvolutionaryParams from './EvolutionaryParams';
|
|
| 6 |
export default function JobForm() {
|
| 7 |
const { t } = useTranslation('common');
|
| 8 |
const [method, setMethod] = useState('linear');
|
| 9 |
-
const [mergeType, setMergeType] = useState('linear');
|
| 10 |
const [modelASource, setModelASource] = useState('hf');
|
| 11 |
const [modelBSource, setModelBSource] = useState('hf');
|
| 12 |
const [modelAId, setModelAId] = useState('');
|
|
@@ -15,20 +15,34 @@ export default function JobForm() {
|
|
| 15 |
const [outputRepo, setOutputRepo] = useState('');
|
| 16 |
const [datasetFile, setDatasetFile] = useState<File | null>(null);
|
| 17 |
const [evoParams, setEvoParams] = useState({});
|
| 18 |
-
const [frankenLayers, setFrankenLayers] = useState('');
|
| 19 |
const [submitting, setSubmitting] = useState(false);
|
|
|
|
| 20 |
|
| 21 |
const handleSubmit = async (e: React.FormEvent) => {
|
| 22 |
e.preventDefault();
|
| 23 |
setSubmitting(true);
|
|
|
|
|
|
|
| 24 |
let datasetPath = '';
|
| 25 |
if (datasetFile) {
|
| 26 |
const formData = new FormData();
|
| 27 |
formData.append('file', datasetFile);
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
|
|
|
| 32 |
const payload = {
|
| 33 |
model_a_source: modelASource,
|
| 34 |
model_a_id: modelAId,
|
|
@@ -43,22 +57,41 @@ export default function JobForm() {
|
|
| 43 |
civitai_key: sessionStorage.getItem('civitai_key') || '',
|
| 44 |
franken_layers: mergeType === 'franken' ? frankenLayers.split(',').map(s => s.trim()) : undefined,
|
| 45 |
};
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
alert(t('job_submitted'));
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
}
|
| 56 |
-
setSubmitting(false);
|
| 57 |
};
|
| 58 |
|
| 59 |
return (
|
| 60 |
<form onSubmit={handleSubmit} className="space-y-4 bg-white p-6 shadow rounded-lg">
|
| 61 |
<ApiKeyInput />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 63 |
<div>
|
| 64 |
<label className="block text-sm font-medium">{t('model_a')}</label>
|
|
|
|
| 6 |
export default function JobForm() {
|
| 7 |
const { t } = useTranslation('common');
|
| 8 |
const [method, setMethod] = useState('linear');
|
| 9 |
+
const [mergeType, setMergeType] = useState('linear');
|
| 10 |
const [modelASource, setModelASource] = useState('hf');
|
| 11 |
const [modelBSource, setModelBSource] = useState('hf');
|
| 12 |
const [modelAId, setModelAId] = useState('');
|
|
|
|
| 15 |
const [outputRepo, setOutputRepo] = useState('');
|
| 16 |
const [datasetFile, setDatasetFile] = useState<File | null>(null);
|
| 17 |
const [evoParams, setEvoParams] = useState({});
|
| 18 |
+
const [frankenLayers, setFrankenLayers] = useState('');
|
| 19 |
const [submitting, setSubmitting] = useState(false);
|
| 20 |
+
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
| 21 |
|
| 22 |
const handleSubmit = async (e: React.FormEvent) => {
|
| 23 |
e.preventDefault();
|
| 24 |
setSubmitting(true);
|
| 25 |
+
setErrorMessage(null);
|
| 26 |
+
|
| 27 |
let datasetPath = '';
|
| 28 |
if (datasetFile) {
|
| 29 |
const formData = new FormData();
|
| 30 |
formData.append('file', datasetFile);
|
| 31 |
+
try {
|
| 32 |
+
const res = await fetch('/api/backend/upload-dataset', { method: 'POST', body: formData });
|
| 33 |
+
if (!res.ok) {
|
| 34 |
+
const err = await res.json();
|
| 35 |
+
throw new Error(err.detail || err.error || 'Dataset upload failed');
|
| 36 |
+
}
|
| 37 |
+
const json = await res.json();
|
| 38 |
+
datasetPath = json.path;
|
| 39 |
+
} catch (err: any) {
|
| 40 |
+
setErrorMessage(err.message);
|
| 41 |
+
setSubmitting(false);
|
| 42 |
+
return;
|
| 43 |
+
}
|
| 44 |
}
|
| 45 |
+
|
| 46 |
const payload = {
|
| 47 |
model_a_source: modelASource,
|
| 48 |
model_a_id: modelAId,
|
|
|
|
| 57 |
civitai_key: sessionStorage.getItem('civitai_key') || '',
|
| 58 |
franken_layers: mergeType === 'franken' ? frankenLayers.split(',').map(s => s.trim()) : undefined,
|
| 59 |
};
|
| 60 |
+
|
| 61 |
+
try {
|
| 62 |
+
const res = await fetch('/api/backend/submit-job', {
|
| 63 |
+
method: 'POST',
|
| 64 |
+
headers: { 'Content-Type': 'application/json' },
|
| 65 |
+
body: JSON.stringify(payload),
|
| 66 |
+
});
|
| 67 |
+
|
| 68 |
+
if (!res.ok) {
|
| 69 |
+
const data = await res.json();
|
| 70 |
+
throw new Error(data.error || data.detail || `Request failed with status ${res.status}`);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
alert(t('job_submitted'));
|
| 74 |
+
// Clear form
|
| 75 |
+
setModelAId('');
|
| 76 |
+
setModelBId('');
|
| 77 |
+
setOutputRepo('');
|
| 78 |
+
setDatasetFile(null);
|
| 79 |
+
setFrankenLayers('');
|
| 80 |
+
} catch (err: any) {
|
| 81 |
+
setErrorMessage(err.message);
|
| 82 |
+
} finally {
|
| 83 |
+
setSubmitting(false);
|
| 84 |
}
|
|
|
|
| 85 |
};
|
| 86 |
|
| 87 |
return (
|
| 88 |
<form onSubmit={handleSubmit} className="space-y-4 bg-white p-6 shadow rounded-lg">
|
| 89 |
<ApiKeyInput />
|
| 90 |
+
{errorMessage && (
|
| 91 |
+
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
|
| 92 |
+
{errorMessage}
|
| 93 |
+
</div>
|
| 94 |
+
)}
|
| 95 |
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 96 |
<div>
|
| 97 |
<label className="block text-sm font-medium">{t('model_a')}</label>
|