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.
This commit is contained in:
156
management-dashboard-web-app/src/components/ExperimentPhases.tsx
Normal file
156
management-dashboard-web-app/src/components/ExperimentPhases.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user