|
|
|
|
@@ -13,7 +13,6 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
const [formData, setFormData] = useState<CreateExperimentRequest & { schedule_status: ScheduleStatus; results_status: ResultsStatus }>({
|
|
|
|
|
experiment_number: initialData?.experiment_number || 0,
|
|
|
|
|
reps_required: initialData?.reps_required || 1,
|
|
|
|
|
rep_number: initialData?.rep_number || 1,
|
|
|
|
|
soaking_duration_hr: initialData?.soaking_duration_hr || 0,
|
|
|
|
|
air_drying_time_min: initialData?.air_drying_time_min || 0,
|
|
|
|
|
plate_contact_frequency_hz: initialData?.plate_contact_frequency_hz || 1,
|
|
|
|
|
@@ -38,13 +37,9 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
newErrors.reps_required = 'Repetitions required must be a positive integer'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!formData.rep_number || formData.rep_number <= 0) {
|
|
|
|
|
newErrors.rep_number = 'Repetition number must be a positive integer'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (formData.rep_number > formData.reps_required) {
|
|
|
|
|
newErrors.rep_number = 'Repetition number cannot exceed repetitions required'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (formData.soaking_duration_hr < 0) {
|
|
|
|
|
newErrors.soaking_duration_hr = 'Soaking duration cannot be negative'
|
|
|
|
|
@@ -78,7 +73,21 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await onSubmit(formData)
|
|
|
|
|
// Prepare data for submission
|
|
|
|
|
const submitData = isEditing ? formData : {
|
|
|
|
|
experiment_number: formData.experiment_number,
|
|
|
|
|
reps_required: formData.reps_required,
|
|
|
|
|
soaking_duration_hr: formData.soaking_duration_hr,
|
|
|
|
|
air_drying_time_min: formData.air_drying_time_min,
|
|
|
|
|
plate_contact_frequency_hz: formData.plate_contact_frequency_hz,
|
|
|
|
|
throughput_rate_pecans_sec: formData.throughput_rate_pecans_sec,
|
|
|
|
|
crush_amount_in: formData.crush_amount_in,
|
|
|
|
|
entry_exit_height_diff_in: formData.entry_exit_height_diff_in,
|
|
|
|
|
schedule_status: formData.schedule_status,
|
|
|
|
|
results_status: formData.results_status
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await onSubmit(submitData)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Form submission error:', error)
|
|
|
|
|
}
|
|
|
|
|
@@ -112,7 +121,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="experiment_number"
|
|
|
|
|
value={formData.experiment_number}
|
|
|
|
|
onChange={(e) => handleInputChange('experiment_number', parseInt(e.target.value) || 0)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.experiment_number ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.experiment_number ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="Enter unique experiment number"
|
|
|
|
|
min="1"
|
|
|
|
|
@@ -133,7 +142,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="reps_required"
|
|
|
|
|
value={formData.reps_required}
|
|
|
|
|
onChange={(e) => handleInputChange('reps_required', parseInt(e.target.value) || 1)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.reps_required ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.reps_required ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="Total repetitions needed"
|
|
|
|
|
min="1"
|
|
|
|
|
@@ -145,53 +154,34 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="rep_number" className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
Current Repetition Number *
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
id="rep_number"
|
|
|
|
|
value={formData.rep_number}
|
|
|
|
|
onChange={(e) => handleInputChange('rep_number', parseInt(e.target.value) || 1)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.rep_number ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="Current repetition"
|
|
|
|
|
min="1"
|
|
|
|
|
step="1"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
{errors.rep_number && (
|
|
|
|
|
<p className="mt-1 text-sm text-red-600">{errors.rep_number}</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="soaking_duration_hr" className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
Soaking Duration (hours) *
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
id="soaking_duration_hr"
|
|
|
|
|
value={formData.soaking_duration_hr}
|
|
|
|
|
onChange={(e) => handleInputChange('soaking_duration_hr', parseFloat(e.target.value) || 0)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.soaking_duration_hr ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="0.0"
|
|
|
|
|
min="0"
|
|
|
|
|
step="0.1"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
{errors.soaking_duration_hr && (
|
|
|
|
|
<p className="mt-1 text-sm text-red-600">{errors.soaking_duration_hr}</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Process Parameters */}
|
|
|
|
|
{/* Experiment Parameters */}
|
|
|
|
|
<div className="border-t pt-6">
|
|
|
|
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Process Parameters</h3>
|
|
|
|
|
<h3 className="text-lg font-medium text-gray-900 mb-4">Experiment Parameters</h3>
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="soaking_duration_hr" className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
Soaking Duration (hours) *
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
id="soaking_duration_hr"
|
|
|
|
|
value={formData.soaking_duration_hr}
|
|
|
|
|
onChange={(e) => handleInputChange('soaking_duration_hr', parseFloat(e.target.value) || 0)}
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.soaking_duration_hr ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="0.0"
|
|
|
|
|
min="0"
|
|
|
|
|
step="0.1"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
{errors.soaking_duration_hr && (
|
|
|
|
|
<p className="mt-1 text-sm text-red-600">{errors.soaking_duration_hr}</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<label htmlFor="air_drying_time_min" className="block text-sm font-medium text-gray-700 mb-2">
|
|
|
|
|
Air Drying Time (minutes) *
|
|
|
|
|
@@ -201,7 +191,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="air_drying_time_min"
|
|
|
|
|
value={formData.air_drying_time_min}
|
|
|
|
|
onChange={(e) => handleInputChange('air_drying_time_min', parseInt(e.target.value) || 0)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.air_drying_time_min ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.air_drying_time_min ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="0"
|
|
|
|
|
min="0"
|
|
|
|
|
@@ -222,7 +212,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="plate_contact_frequency_hz"
|
|
|
|
|
value={formData.plate_contact_frequency_hz}
|
|
|
|
|
onChange={(e) => handleInputChange('plate_contact_frequency_hz', parseFloat(e.target.value) || 1)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.plate_contact_frequency_hz ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.plate_contact_frequency_hz ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="1.0"
|
|
|
|
|
min="0.1"
|
|
|
|
|
@@ -243,7 +233,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="throughput_rate_pecans_sec"
|
|
|
|
|
value={formData.throughput_rate_pecans_sec}
|
|
|
|
|
onChange={(e) => handleInputChange('throughput_rate_pecans_sec', parseFloat(e.target.value) || 1)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.throughput_rate_pecans_sec ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.throughput_rate_pecans_sec ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="1.0"
|
|
|
|
|
min="0.1"
|
|
|
|
|
@@ -264,7 +254,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="crush_amount_in"
|
|
|
|
|
value={formData.crush_amount_in}
|
|
|
|
|
onChange={(e) => handleInputChange('crush_amount_in', parseFloat(e.target.value) || 0)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.crush_amount_in ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-xs px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.crush_amount_in ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="0.0"
|
|
|
|
|
min="0"
|
|
|
|
|
@@ -285,7 +275,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="entry_exit_height_diff_in"
|
|
|
|
|
value={formData.entry_exit_height_diff_in}
|
|
|
|
|
onChange={(e) => handleInputChange('entry_exit_height_diff_in', parseFloat(e.target.value) || 0)}
|
|
|
|
|
className={`w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.entry_exit_height_diff_in ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
className={`max-w-sm px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm ${errors.entry_exit_height_diff_in ? 'border-red-300' : 'border-gray-300'
|
|
|
|
|
}`}
|
|
|
|
|
placeholder="0.0 (can be negative)"
|
|
|
|
|
step="0.1"
|
|
|
|
|
@@ -312,7 +302,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="schedule_status"
|
|
|
|
|
value={formData.schedule_status}
|
|
|
|
|
onChange={(e) => handleInputChange('schedule_status', e.target.value as ScheduleStatus)}
|
|
|
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm"
|
|
|
|
|
className="max-w-xs px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm"
|
|
|
|
|
>
|
|
|
|
|
<option value="pending schedule">Pending Schedule</option>
|
|
|
|
|
<option value="scheduled">Scheduled</option>
|
|
|
|
|
@@ -329,7 +319,7 @@ export function ExperimentForm({ initialData, onSubmit, onCancel, isEditing = fa
|
|
|
|
|
id="results_status"
|
|
|
|
|
value={formData.results_status}
|
|
|
|
|
onChange={(e) => handleInputChange('results_status', e.target.value as ResultsStatus)}
|
|
|
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm"
|
|
|
|
|
className="max-w-xs px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors text-sm"
|
|
|
|
|
>
|
|
|
|
|
<option value="valid">Valid</option>
|
|
|
|
|
<option value="invalid">Invalid</option>
|
|
|
|
|
|