Refactor experiment management and update data structures
- Renamed columns in the experimental run sheet CSV for clarity. - Updated the ExperimentForm component to include new fields for weight per repetition and additional parameters specific to Meyer Cracker experiments. - Enhanced the data entry logic to handle new experiment phases and machine types. - Refactored repetition scheduling logic to use scheduled_date instead of schedule_status for better clarity in status representation. - Improved the user interface for displaying experiment phases and their associated statuses. - Removed outdated seed data and updated database migration scripts to reflect the new schema changes.
This commit is contained in:
@@ -38,6 +38,11 @@ export interface ExperimentPhase {
|
||||
id: string
|
||||
name: string
|
||||
description?: string | null
|
||||
has_soaking: boolean
|
||||
has_airdrying: boolean
|
||||
has_cracking: boolean
|
||||
has_shelling: boolean
|
||||
cracking_machine_type_id?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
@@ -47,15 +52,14 @@ export interface Experiment {
|
||||
id: string
|
||||
experiment_number: number
|
||||
reps_required: number
|
||||
soaking_duration_hr: number
|
||||
air_drying_time_min: number
|
||||
plate_contact_frequency_hz: number
|
||||
throughput_rate_pecans_sec: number
|
||||
crush_amount_in: number
|
||||
entry_exit_height_diff_in: number
|
||||
weight_per_repetition_lbs: number
|
||||
results_status: ResultsStatus
|
||||
completion_status: boolean
|
||||
phase_id?: string | null
|
||||
soaking_id?: string | null
|
||||
airdrying_id?: string | null
|
||||
cracking_id?: string | null
|
||||
shelling_id?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
@@ -63,25 +67,115 @@ export interface Experiment {
|
||||
|
||||
|
||||
|
||||
// Machine Types
|
||||
export interface MachineType {
|
||||
id: string
|
||||
name: string
|
||||
description?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
}
|
||||
|
||||
// Phase-specific interfaces
|
||||
export interface Soaking {
|
||||
id: string
|
||||
experiment_id: string
|
||||
repetition_id?: string | null
|
||||
scheduled_start_time: string
|
||||
actual_start_time?: string | null
|
||||
soaking_duration_minutes: number
|
||||
scheduled_end_time: string
|
||||
actual_end_time?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
}
|
||||
|
||||
export interface Airdrying {
|
||||
id: string
|
||||
experiment_id: string
|
||||
repetition_id?: string | null
|
||||
scheduled_start_time: string
|
||||
actual_start_time?: string | null
|
||||
duration_minutes: number
|
||||
scheduled_end_time: string
|
||||
actual_end_time?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
}
|
||||
|
||||
export interface Cracking {
|
||||
id: string
|
||||
experiment_id: string
|
||||
repetition_id?: string | null
|
||||
machine_type_id: string
|
||||
scheduled_start_time: string
|
||||
actual_start_time?: string | null
|
||||
actual_end_time?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
}
|
||||
|
||||
export interface Shelling {
|
||||
id: string
|
||||
experiment_id: string
|
||||
repetition_id?: string | null
|
||||
scheduled_start_time: string
|
||||
actual_start_time?: string | null
|
||||
actual_end_time?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
created_by: string
|
||||
}
|
||||
|
||||
// Machine-specific parameter interfaces
|
||||
export interface JCCrackerParameters {
|
||||
id: string
|
||||
cracking_id: string
|
||||
plate_contact_frequency_hz: number
|
||||
throughput_rate_pecans_sec: number
|
||||
crush_amount_in: number
|
||||
entry_exit_height_diff_in: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface MeyerCrackerParameters {
|
||||
id: string
|
||||
cracking_id: string
|
||||
motor_speed_hz: number
|
||||
jig_displacement_inches: number
|
||||
spring_stiffness_nm: number
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface CreateExperimentPhaseRequest {
|
||||
name: string
|
||||
description?: string
|
||||
has_soaking: boolean
|
||||
has_airdrying: boolean
|
||||
has_cracking: boolean
|
||||
has_shelling: boolean
|
||||
cracking_machine_type_id?: string
|
||||
}
|
||||
|
||||
export interface UpdateExperimentPhaseRequest {
|
||||
name?: string
|
||||
description?: string
|
||||
has_soaking?: boolean
|
||||
has_airdrying?: boolean
|
||||
has_cracking?: boolean
|
||||
has_shelling?: boolean
|
||||
}
|
||||
|
||||
export interface CreateExperimentRequest {
|
||||
experiment_number: number
|
||||
reps_required: number
|
||||
soaking_duration_hr: number
|
||||
air_drying_time_min: number
|
||||
plate_contact_frequency_hz: number
|
||||
throughput_rate_pecans_sec: number
|
||||
crush_amount_in: number
|
||||
entry_exit_height_diff_in: number
|
||||
weight_per_repetition_lbs: number
|
||||
results_status?: ResultsStatus
|
||||
completion_status?: boolean
|
||||
phase_id?: string
|
||||
@@ -90,12 +184,7 @@ export interface CreateExperimentRequest {
|
||||
export interface UpdateExperimentRequest {
|
||||
experiment_number?: number
|
||||
reps_required?: number
|
||||
soaking_duration_hr?: number
|
||||
air_drying_time_min?: number
|
||||
plate_contact_frequency_hz?: number
|
||||
throughput_rate_pecans_sec?: number
|
||||
crush_amount_in?: number
|
||||
entry_exit_height_diff_in?: number
|
||||
weight_per_repetition_lbs?: number
|
||||
results_status?: ResultsStatus
|
||||
completion_status?: boolean
|
||||
phase_id?: string
|
||||
@@ -105,12 +194,10 @@ export interface CreateRepetitionRequest {
|
||||
experiment_id: string
|
||||
repetition_number: number
|
||||
scheduled_date?: string | null
|
||||
schedule_status?: ScheduleStatus
|
||||
}
|
||||
|
||||
export interface UpdateRepetitionRequest {
|
||||
scheduled_date?: string | null
|
||||
schedule_status?: ScheduleStatus
|
||||
completion_status?: boolean
|
||||
}
|
||||
|
||||
@@ -137,7 +224,6 @@ export interface ExperimentRepetition {
|
||||
experiment_id: string
|
||||
repetition_number: number
|
||||
scheduled_date?: string | null
|
||||
schedule_status: ScheduleStatus
|
||||
completion_status: boolean
|
||||
is_locked: boolean
|
||||
locked_at?: string | null
|
||||
@@ -219,6 +305,58 @@ export interface UpdatePhaseDataRequest {
|
||||
[key: string]: any // For phase-specific data fields
|
||||
}
|
||||
|
||||
// Phase creation request interfaces
|
||||
export interface CreateSoakingRequest {
|
||||
experiment_id: string
|
||||
repetition_id?: string
|
||||
scheduled_start_time: string
|
||||
soaking_duration_minutes: number
|
||||
actual_start_time?: string
|
||||
actual_end_time?: string
|
||||
}
|
||||
|
||||
export interface CreateAirdryingRequest {
|
||||
experiment_id: string
|
||||
repetition_id?: string
|
||||
scheduled_start_time?: string // Will be auto-calculated from soaking if not provided
|
||||
duration_minutes: number
|
||||
actual_start_time?: string
|
||||
actual_end_time?: string
|
||||
}
|
||||
|
||||
export interface CreateCrackingRequest {
|
||||
experiment_id: string
|
||||
repetition_id?: string
|
||||
machine_type_id: string
|
||||
scheduled_start_time?: string // Will be auto-calculated from airdrying if not provided
|
||||
actual_start_time?: string
|
||||
actual_end_time?: string
|
||||
}
|
||||
|
||||
export interface CreateShellingRequest {
|
||||
experiment_id: string
|
||||
repetition_id?: string
|
||||
scheduled_start_time: string
|
||||
actual_start_time?: string
|
||||
actual_end_time?: string
|
||||
}
|
||||
|
||||
// Machine parameter creation request interfaces
|
||||
export interface CreateJCCrackerParametersRequest {
|
||||
cracking_id: string
|
||||
plate_contact_frequency_hz: number
|
||||
throughput_rate_pecans_sec: number
|
||||
crush_amount_in: number
|
||||
entry_exit_height_diff_in: number
|
||||
}
|
||||
|
||||
export interface CreateMeyerCrackerParametersRequest {
|
||||
cracking_id: string
|
||||
motor_speed_hz: number
|
||||
jig_displacement_inches: number
|
||||
spring_stiffness_nm: number
|
||||
}
|
||||
|
||||
export interface UserRole {
|
||||
id: string
|
||||
user_id: string
|
||||
@@ -624,6 +762,349 @@ export const experimentManagement = {
|
||||
}
|
||||
}
|
||||
|
||||
// Machine Type Management
|
||||
export const machineTypeManagement = {
|
||||
// Get all machine types
|
||||
async getAllMachineTypes(): Promise<MachineType[]> {
|
||||
const { data, error } = await supabase
|
||||
.from('machine_types')
|
||||
.select('*')
|
||||
.order('name')
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
// Get machine type by ID
|
||||
async getMachineTypeById(id: string): Promise<MachineType | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('machine_types')
|
||||
.select('*')
|
||||
.eq('id', id)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
// Phase Management
|
||||
export const phaseManagement = {
|
||||
// Soaking management
|
||||
async createSoaking(request: CreateSoakingRequest): Promise<Soaking> {
|
||||
const { data: { user }, error: authError } = await supabase.auth.getUser()
|
||||
if (authError || !user) throw new Error('User not authenticated')
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('soaking')
|
||||
.insert({
|
||||
...request,
|
||||
created_by: user.id
|
||||
})
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async updateSoaking(id: string, updates: Partial<Soaking>): Promise<Soaking> {
|
||||
const { data, error } = await supabase
|
||||
.from('soaking')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async getSoakingByExperimentId(experimentId: string): Promise<Soaking | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('soaking')
|
||||
.select('*')
|
||||
.eq('experiment_id', experimentId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
async getSoakingByRepetitionId(repetitionId: string): Promise<Soaking | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('soaking')
|
||||
.select('*')
|
||||
.eq('repetition_id', repetitionId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
// Airdrying management
|
||||
async createAirdrying(request: CreateAirdryingRequest): Promise<Airdrying> {
|
||||
const { data: { user }, error: authError } = await supabase.auth.getUser()
|
||||
if (authError || !user) throw new Error('User not authenticated')
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('airdrying')
|
||||
.insert({
|
||||
...request,
|
||||
created_by: user.id
|
||||
})
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async updateAirdrying(id: string, updates: Partial<Airdrying>): Promise<Airdrying> {
|
||||
const { data, error } = await supabase
|
||||
.from('airdrying')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async getAirdryingByExperimentId(experimentId: string): Promise<Airdrying | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('airdrying')
|
||||
.select('*')
|
||||
.eq('experiment_id', experimentId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
async getAirdryingByRepetitionId(repetitionId: string): Promise<Airdrying | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('airdrying')
|
||||
.select('*')
|
||||
.eq('repetition_id', repetitionId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
// Cracking management
|
||||
async createCracking(request: CreateCrackingRequest): Promise<Cracking> {
|
||||
const { data: { user }, error: authError } = await supabase.auth.getUser()
|
||||
if (authError || !user) throw new Error('User not authenticated')
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('cracking')
|
||||
.insert({
|
||||
...request,
|
||||
created_by: user.id
|
||||
})
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async updateCracking(id: string, updates: Partial<Cracking>): Promise<Cracking> {
|
||||
const { data, error } = await supabase
|
||||
.from('cracking')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async getCrackingByExperimentId(experimentId: string): Promise<Cracking | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('cracking')
|
||||
.select('*')
|
||||
.eq('experiment_id', experimentId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
async getCrackingByRepetitionId(repetitionId: string): Promise<Cracking | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('cracking')
|
||||
.select('*')
|
||||
.eq('repetition_id', repetitionId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
// Shelling management
|
||||
async createShelling(request: CreateShellingRequest): Promise<Shelling> {
|
||||
const { data: { user }, error: authError } = await supabase.auth.getUser()
|
||||
if (authError || !user) throw new Error('User not authenticated')
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('shelling')
|
||||
.insert({
|
||||
...request,
|
||||
created_by: user.id
|
||||
})
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async updateShelling(id: string, updates: Partial<Shelling>): Promise<Shelling> {
|
||||
const { data, error } = await supabase
|
||||
.from('shelling')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async getShellingByExperimentId(experimentId: string): Promise<Shelling | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('shelling')
|
||||
.select('*')
|
||||
.eq('experiment_id', experimentId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
async getShellingByRepetitionId(repetitionId: string): Promise<Shelling | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('shelling')
|
||||
.select('*')
|
||||
.eq('repetition_id', repetitionId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
// Machine Parameter Management
|
||||
export const machineParameterManagement = {
|
||||
// JC Cracker parameters
|
||||
async createJCCrackerParameters(request: CreateJCCrackerParametersRequest): Promise<JCCrackerParameters> {
|
||||
const { data, error } = await supabase
|
||||
.from('jc_cracker_parameters')
|
||||
.insert(request)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async updateJCCrackerParameters(id: string, updates: Partial<JCCrackerParameters>): Promise<JCCrackerParameters> {
|
||||
const { data, error } = await supabase
|
||||
.from('jc_cracker_parameters')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async getJCCrackerParametersByCrackingId(crackingId: string): Promise<JCCrackerParameters | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('jc_cracker_parameters')
|
||||
.select('*')
|
||||
.eq('cracking_id', crackingId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
},
|
||||
|
||||
// Meyer Cracker parameters
|
||||
async createMeyerCrackerParameters(request: CreateMeyerCrackerParametersRequest): Promise<MeyerCrackerParameters> {
|
||||
const { data, error } = await supabase
|
||||
.from('meyer_cracker_parameters')
|
||||
.insert(request)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async updateMeyerCrackerParameters(id: string, updates: Partial<MeyerCrackerParameters>): Promise<MeyerCrackerParameters> {
|
||||
const { data, error } = await supabase
|
||||
.from('meyer_cracker_parameters')
|
||||
.update(updates)
|
||||
.eq('id', id)
|
||||
.select()
|
||||
.single()
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
|
||||
async getMeyerCrackerParametersByCrackingId(crackingId: string): Promise<MeyerCrackerParameters | null> {
|
||||
const { data, error } = await supabase
|
||||
.from('meyer_cracker_parameters')
|
||||
.select('*')
|
||||
.eq('cracking_id', crackingId)
|
||||
.single()
|
||||
|
||||
if (error) {
|
||||
if (error.code === 'PGRST116') return null // Not found
|
||||
throw error
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
// Experiment Repetitions Management
|
||||
export const repetitionManagement = {
|
||||
// Get all repetitions for an experiment
|
||||
@@ -673,7 +1154,6 @@ export const repetitionManagement = {
|
||||
async scheduleRepetition(id: string, scheduledDate: string): Promise<ExperimentRepetition> {
|
||||
const updates: UpdateRepetitionRequest = {
|
||||
scheduled_date: scheduledDate,
|
||||
schedule_status: 'scheduled'
|
||||
}
|
||||
|
||||
return this.updateRepetition(id, updates)
|
||||
@@ -683,7 +1163,6 @@ export const repetitionManagement = {
|
||||
async removeRepetitionSchedule(id: string): Promise<ExperimentRepetition> {
|
||||
const updates: UpdateRepetitionRequest = {
|
||||
scheduled_date: null,
|
||||
schedule_status: 'pending schedule'
|
||||
}
|
||||
|
||||
return this.updateRepetition(id, updates)
|
||||
@@ -700,15 +1179,14 @@ export const repetitionManagement = {
|
||||
},
|
||||
|
||||
// Get repetitions by status
|
||||
async getRepetitionsByStatus(scheduleStatus?: ScheduleStatus): Promise<ExperimentRepetition[]> {
|
||||
async getRepetitionsByStatus(isScheduled?: boolean): Promise<ExperimentRepetition[]> {
|
||||
let query = supabase.from('experiment_repetitions').select('*')
|
||||
|
||||
if (scheduleStatus) {
|
||||
query = query.eq('schedule_status', scheduleStatus)
|
||||
if (isScheduled === true) {
|
||||
query = query.not('scheduled_date', 'is', null)
|
||||
} else if (isScheduled === false) {
|
||||
query = query.is('scheduled_date', null)
|
||||
}
|
||||
|
||||
const { data, error } = await query.order('created_at', { ascending: false })
|
||||
|
||||
if (error) throw error
|
||||
return data
|
||||
},
|
||||
@@ -743,8 +1221,7 @@ export const repetitionManagement = {
|
||||
for (let i = 1; i <= experiment.reps_required; i++) {
|
||||
repetitions.push({
|
||||
experiment_id: experimentId,
|
||||
repetition_number: i,
|
||||
schedule_status: 'pending schedule'
|
||||
repetition_number: i
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user