feat(frontend): React UI — pages, forms, audit log section, Tailwind, Vite build

This commit is contained in:
Experiments DB Dev
2026-04-15 13:18:31 -04:00
parent f1d2449808
commit c32a5d200d
22 changed files with 10356 additions and 0 deletions
@@ -0,0 +1,74 @@
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>
);
}