Files
usda-vision/management-dashboard-web-app/src/components/ExperimentPhases.tsx
salirezav d1fe478478 Refactor: enhance dashboard layout and experiment management features
- Added functionality to save and retrieve the current dashboard view in localStorage for improved user experience.
- Updated DashboardLayout component to handle view changes with access control based on user roles.
- Renamed Experiments component to ExperimentManagement for clarity.
- Introduced new ExperimentPhase interface and related utility functions for managing experiment phases.
- Updated seed data to include initial roles and experiment phases for testing.
- Cleaned up unnecessary blank lines in various files for better code readability.
2025-09-19 12:03:46 -04:00

157 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from 'react'
import { experimentPhaseManagement, userManagement } from '../lib/supabase'
import type { ExperimentPhase, User } from '../lib/supabase'
interface ExperimentPhasesProps {
onPhaseSelect: (phase: ExperimentPhase) => void
}
export function ExperimentPhases({ onPhaseSelect }: ExperimentPhasesProps) {
const [phases, setPhases] = useState<ExperimentPhase[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [currentUser, setCurrentUser] = useState<User | null>(null)
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
try {
setLoading(true)
setError(null)
const [phasesData, userData] = await Promise.all([
experimentPhaseManagement.getAllExperimentPhases(),
userManagement.getCurrentUser()
])
setPhases(phasesData)
setCurrentUser(userData)
} catch (err: any) {
setError(err.message || 'Failed to load experiment phases')
console.error('Load experiment phases error:', err)
} finally {
setLoading(false)
}
}
const canManagePhases = currentUser?.roles.includes('admin') || currentUser?.roles.includes('conductor')
if (loading) {
return (
<div className="p-6">
<div className="flex items-center justify-center h-64">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
</div>
)
}
return (
<div className="p-6">
{/* Header */}
<div className="mb-8">
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold text-gray-900">Experiment Phases</h1>
<p className="mt-2 text-gray-600">Select an experiment phase to view and manage its experiments</p>
<p className="mt-2 text-gray-600">Experiment phases help organize experiments into logical groups for easier navigation and management.</p>
</div>
{canManagePhases && (
<button
onClick={() => {
// TODO: Implement create phase modal
alert('Create phase functionality will be implemented')
}}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
New Phase
</button>
)}
</div>
</div>
{/* Error Message */}
{error && (
<div className="mb-6 rounded-md bg-red-50 p-4">
<div className="text-sm text-red-700">{error}</div>
</div>
)}
{/* Phases Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{phases.map((phase) => (
<div
key={phase.id}
onClick={() => onPhaseSelect(phase)}
className="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow duration-200 cursor-pointer border border-gray-200 hover:border-blue-300"
>
<div className="p-6">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center">
<div className="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<svg className="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
</svg>
</div>
</div>
<div className="text-right">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Active
</span>
</div>
</div>
<h3 className="text-lg font-semibold text-gray-900 mb-2">
{phase.name}
</h3>
{phase.description && (
<p className="text-sm text-gray-600 mb-4 line-clamp-2">
{phase.description}
</p>
)}
<div className="flex items-center justify-between text-sm text-gray-500">
<span>Created {new Date(phase.created_at).toLocaleDateString()}</span>
<div className="flex items-center text-blue-600 hover:text-blue-800">
<span className="mr-1">View Experiments</span>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
</div>
</div>
))}
</div>
{phases.length === 0 && (
<div className="text-center py-12">
<svg className="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" />
</svg>
<h3 className="mt-2 text-sm font-medium text-gray-900">No experiment phases found</h3>
<p className="mt-1 text-sm text-gray-500">
Get started by creating your first experiment phase.
</p>
{canManagePhases && (
<div className="mt-6">
<button
onClick={() => {
// TODO: Implement create phase modal
alert('Create phase functionality will be implemented')
}}
className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Create First Phase
</button>
</div>
)}
</div>
)}
</div>
)
}