75 lines
2.2 KiB
React
75 lines
2.2 KiB
React
import React, { useState, useEffect } from 'react';
|
||
import Button from './ui/Button';
|
||
import FormField, { Input } from './ui/FormField';
|
||
import Alert from './ui/Alert';
|
||
import { experimentsApi } from '../api/client';
|
||
|
||
export default function ExperimentForm({ existing, onSuccess, onCancel }) {
|
||
const [title, setTitle] = useState(existing?.title ?? '');
|
||
const [errors, setErrors] = useState({});
|
||
const [apiError, setApiError] = useState(null);
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
useEffect(() => {
|
||
setTitle(existing?.title ?? '');
|
||
setErrors({});
|
||
setApiError(null);
|
||
}, [existing]);
|
||
|
||
function validate() {
|
||
const e = {};
|
||
if (!title.trim()) e.title = 'Title is required';
|
||
else if (title.trim().length > 255) e.title = 'Title must be 255 characters or fewer';
|
||
setErrors(e);
|
||
return Object.keys(e).length === 0;
|
||
}
|
||
|
||
async function handleSubmit(e) {
|
||
e.preventDefault();
|
||
if (!validate()) return;
|
||
setLoading(true);
|
||
setApiError(null);
|
||
try {
|
||
const result = existing
|
||
? await experimentsApi.update(existing.id, { title: title.trim() })
|
||
: await experimentsApi.create({ title: title.trim() });
|
||
onSuccess(result);
|
||
} catch (err) {
|
||
setApiError(err);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<form onSubmit={handleSubmit} noValidate className="space-y-4">
|
||
{apiError && (
|
||
<Alert
|
||
type="error"
|
||
message={apiError.message}
|
||
details={apiError.details}
|
||
onDismiss={() => setApiError(null)}
|
||
/>
|
||
)}
|
||
<FormField label="Experiment Title" name="title" error={errors.title} required>
|
||
<Input
|
||
name="title"
|
||
value={title}
|
||
onChange={(e) => setTitle(e.target.value)}
|
||
placeholder="e.g. Drug Efficacy Study – Q3"
|
||
required
|
||
disabled={loading}
|
||
/>
|
||
</FormField>
|
||
<div className="flex justify-end gap-3 pt-2">
|
||
<Button variant="secondary" type="button" onClick={onCancel} disabled={loading}>
|
||
Cancel
|
||
</Button>
|
||
<Button type="submit" loading={loading}>
|
||
{existing ? 'Save Changes' : 'Create Experiment'}
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
);
|
||
}
|