Files
usda-vision/management-dashboard-web-app/src/components/RepetitionPhaseSelector.tsx

224 lines
7.5 KiB
TypeScript
Executable File

import { useState, useEffect } from 'react'
import { phaseDraftManagement, type ExperimentRepetition, type ExperimentPhase, type ExperimentPhaseDraft, type User } from '../lib/supabase'
interface RepetitionPhaseSelectorProps {
repetition: ExperimentRepetition
currentUser: User
onPhaseSelect: (phase: ExperimentPhase) => void
}
interface PhaseInfo {
name: ExperimentPhase
title: string
description: string
icon: string
color: string
}
const phases: PhaseInfo[] = [
{
name: 'pre-soaking',
title: 'Pre-Soaking',
description: 'Initial measurements before soaking process',
icon: '🌰',
color: 'bg-blue-500'
},
{
name: 'air-drying',
title: 'Air-Drying',
description: 'Post-soak measurements and air-drying data',
icon: '💨',
color: 'bg-green-500'
},
{
name: 'cracking',
title: 'Cracking',
description: 'Cracking process timing and parameters',
icon: '🔨',
color: 'bg-yellow-500'
},
{
name: 'shelling',
title: 'Shelling',
description: 'Final measurements and yield data',
icon: '📊',
color: 'bg-purple-500'
}
]
export function RepetitionPhaseSelector({ repetition, currentUser: _currentUser, onPhaseSelect }: RepetitionPhaseSelectorProps) {
const [phaseDrafts, setPhaseDrafts] = useState<Record<ExperimentPhase, ExperimentPhaseDraft[]>>({
'pre-soaking': [],
'air-drying': [],
'cracking': [],
'shelling': []
})
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
loadPhaseDrafts()
}, [repetition.id])
const loadPhaseDrafts = async () => {
try {
setLoading(true)
setError(null)
const allDrafts = await phaseDraftManagement.getUserPhaseDraftsForRepetition(repetition.id)
// Group drafts by phase
const groupedDrafts: Record<ExperimentPhase, ExperimentPhaseDraft[]> = {
'pre-soaking': [],
'air-drying': [],
'cracking': [],
'shelling': []
}
allDrafts.forEach(draft => {
groupedDrafts[draft.phase_name].push(draft)
})
setPhaseDrafts(groupedDrafts)
} catch (err: any) {
setError(err.message || 'Failed to load phase drafts')
console.error('Load phase drafts error:', err)
} finally {
setLoading(false)
}
}
const getPhaseStatus = (phase: ExperimentPhase) => {
const drafts = phaseDrafts[phase]
if (drafts.length === 0) return 'empty'
const hasSubmitted = drafts.some(d => d.status === 'submitted')
const hasDraft = drafts.some(d => d.status === 'draft')
const hasWithdrawn = drafts.some(d => d.status === 'withdrawn')
if (hasSubmitted) return 'submitted'
if (hasDraft) return 'draft'
if (hasWithdrawn) return 'withdrawn'
return 'empty'
}
const getStatusBadge = (status: string) => {
switch (status) {
case 'submitted':
return <span className="px-2 py-1 text-xs font-medium bg-green-100 text-green-800 rounded-full">Submitted</span>
case 'draft':
return <span className="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded-full">Draft</span>
case 'withdrawn':
return <span className="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded-full">Withdrawn</span>
case 'empty':
return <span className="px-2 py-1 text-xs font-medium bg-gray-100 text-gray-800 rounded-full">No Data</span>
default:
return null
}
}
const getDraftCount = (phase: ExperimentPhase) => {
return phaseDrafts[phase].length
}
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-4 text-gray-600">Loading phases...</p>
</div>
</div>
)
}
if (error) {
return (
<div className="bg-red-50 border border-red-200 rounded-md p-4">
<div className="text-sm text-red-700">{error}</div>
</div>
)
}
return (
<div>
<div className="mb-6">
<h2 className="text-2xl font-bold text-gray-900 mb-2">Select Phase</h2>
<p className="text-gray-600">
Choose a phase to enter or view data. Each phase can have multiple drafts.
</p>
{repetition.is_locked && (
<div className="mt-2 p-3 bg-red-50 border border-red-200 rounded-md">
<div className="flex items-center">
<span className="text-red-800 text-sm font-medium">🔒 This repetition is locked by an admin</span>
</div>
<p className="text-red-700 text-xs mt-1">
You can view existing data but cannot create new drafts or modify existing ones.
</p>
</div>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{phases.map((phase) => {
const status = getPhaseStatus(phase.name)
const draftCount = getDraftCount(phase.name)
return (
<div
key={phase.name}
onClick={() => onPhaseSelect(phase.name)}
className="bg-white rounded-lg shadow-md border border-gray-200 p-6 cursor-pointer hover:shadow-lg hover:border-blue-300 transition-all duration-200"
>
<div className="flex items-start justify-between mb-4">
<div className="flex items-center">
<div className={`w-12 h-12 ${phase.color} rounded-lg flex items-center justify-center text-white text-xl mr-4`}>
{phase.icon}
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900">{phase.title}</h3>
<p className="text-sm text-gray-600">{phase.description}</p>
</div>
</div>
{getStatusBadge(status)}
</div>
<div className="flex items-center justify-between text-sm text-gray-500">
<span>
{draftCount === 0 ? 'No drafts' : `${draftCount} draft${draftCount === 1 ? '' : 's'}`}
</span>
<svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
{draftCount > 0 && (
<div className="mt-3 pt-3 border-t border-gray-100">
<div className="flex flex-wrap gap-1">
{phaseDrafts[phase.name].slice(0, 3).map((draft, index) => (
<span
key={draft.id}
className={`px-2 py-1 text-xs rounded ${draft.status === 'submitted' ? 'bg-green-100 text-green-700' :
draft.status === 'draft' ? 'bg-yellow-100 text-yellow-700' :
'bg-red-100 text-red-700'
}`}
>
{draft.draft_name || `Draft ${index + 1}`}
</span>
))}
{draftCount > 3 && (
<span className="px-2 py-1 text-xs bg-gray-100 text-gray-600 rounded">
+{draftCount - 3} more
</span>
)}
</div>
</div>
)}
</div>
)
})}
</div>
</div>
)
}