mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 14:44:44 -05:00
- Remove outdated root-level documentation files - Delete IMPLEMENTATION_STATUS.md, WORK_IN_PROGRESS.md, UI_IMPROVEMENTS_SUMMARY.md, CLAUDE.md - Reorganize documentation into docs/ folder - Move UNIFIED_EDITOR_EXPERIENCES.md → docs/unified-editor-experiences.md - Move DATATABLE_MIGRATION_PROGRESS.md → docs/datatable-migration-progress.md - Move SEED_SCRIPT_README.md → docs/seed-script-readme.md - Create comprehensive new documentation - Add docs/implementation-status.md with production readiness assessment - Add docs/work-in-progress.md with active development tracking - Add docs/development-achievements.md consolidating all major accomplishments - Update documentation hub - Enhance docs/README.md with complete 13-document structure - Organize into logical categories: Core, Status, Achievements - Provide clear navigation and purpose for each document Features: - 73% code reduction achievement through unified editor experiences - Complete DataTable migration with enterprise features - Comprehensive seed database with realistic research scenarios - Production-ready status with 100% backend, 95% frontend completion - Clean documentation architecture supporting future development Breaking Changes: None - documentation restructuring only Migration: Documentation moved to docs/ folder, no code changes required
332 lines
12 KiB
TypeScript
332 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Activity, Bot, CheckCircle,
|
|
Circle, Clock, GitBranch, Play, Target, Users
|
|
} from "lucide-react";
|
|
import { Badge } from "~/components/ui/badge";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
|
import { Progress } from "~/components/ui/progress";
|
|
import { Separator } from "~/components/ui/separator";
|
|
|
|
interface TrialProgressProps {
|
|
steps: Array<{
|
|
id: string;
|
|
name: string;
|
|
type: "wizard_action" | "robot_action" | "parallel_steps" | "conditional_branch";
|
|
description?: string;
|
|
duration?: number;
|
|
parameters?: any;
|
|
}>;
|
|
currentStepIndex: number;
|
|
trialStatus: "scheduled" | "in_progress" | "completed" | "aborted" | "failed";
|
|
}
|
|
|
|
const stepTypeConfig = {
|
|
wizard_action: {
|
|
label: "Wizard",
|
|
icon: Play,
|
|
color: "blue",
|
|
bgColor: "bg-blue-100",
|
|
textColor: "text-blue-600",
|
|
borderColor: "border-blue-300"
|
|
},
|
|
robot_action: {
|
|
label: "Robot",
|
|
icon: Bot,
|
|
color: "green",
|
|
bgColor: "bg-green-100",
|
|
textColor: "text-green-600",
|
|
borderColor: "border-green-300"
|
|
},
|
|
parallel_steps: {
|
|
label: "Parallel",
|
|
icon: Users,
|
|
color: "purple",
|
|
bgColor: "bg-purple-100",
|
|
textColor: "text-purple-600",
|
|
borderColor: "border-purple-300"
|
|
},
|
|
conditional_branch: {
|
|
label: "Branch",
|
|
icon: GitBranch,
|
|
color: "orange",
|
|
bgColor: "bg-orange-100",
|
|
textColor: "text-orange-600",
|
|
borderColor: "border-orange-300"
|
|
}
|
|
};
|
|
|
|
export function TrialProgress({ steps, currentStepIndex, trialStatus }: TrialProgressProps) {
|
|
if (!steps || steps.length === 0) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="p-6 text-center">
|
|
<div className="text-slate-500">
|
|
<Target className="h-8 w-8 mx-auto mb-2 opacity-50" />
|
|
<p className="text-sm">No experiment steps defined</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
const progress = trialStatus === "completed" ? 100 :
|
|
trialStatus === "aborted" ? 0 :
|
|
((currentStepIndex + 1) / steps.length) * 100;
|
|
|
|
const completedSteps = trialStatus === "completed" ? steps.length :
|
|
trialStatus === "aborted" || trialStatus === "failed" ? 0 :
|
|
currentStepIndex;
|
|
|
|
const getStepStatus = (index: number) => {
|
|
if (trialStatus === "aborted" || trialStatus === "failed") return "aborted";
|
|
if (trialStatus === "completed" || index < currentStepIndex) return "completed";
|
|
if (index === currentStepIndex && trialStatus === "in_progress") return "active";
|
|
if (index === currentStepIndex && trialStatus === "scheduled") return "pending";
|
|
return "upcoming";
|
|
};
|
|
|
|
const getStepStatusConfig = (status: string) => {
|
|
switch (status) {
|
|
case "completed":
|
|
return {
|
|
icon: CheckCircle,
|
|
iconColor: "text-green-600",
|
|
bgColor: "bg-green-100",
|
|
borderColor: "border-green-300",
|
|
textColor: "text-green-800"
|
|
};
|
|
case "active":
|
|
return {
|
|
icon: Activity,
|
|
iconColor: "text-blue-600",
|
|
bgColor: "bg-blue-100",
|
|
borderColor: "border-blue-300",
|
|
textColor: "text-blue-800"
|
|
};
|
|
case "pending":
|
|
return {
|
|
icon: Clock,
|
|
iconColor: "text-amber-600",
|
|
bgColor: "bg-amber-100",
|
|
borderColor: "border-amber-300",
|
|
textColor: "text-amber-800"
|
|
};
|
|
case "aborted":
|
|
return {
|
|
icon: Circle,
|
|
iconColor: "text-red-600",
|
|
bgColor: "bg-red-100",
|
|
borderColor: "border-red-300",
|
|
textColor: "text-red-800"
|
|
};
|
|
default: // upcoming
|
|
return {
|
|
icon: Circle,
|
|
iconColor: "text-slate-400",
|
|
bgColor: "bg-slate-100",
|
|
borderColor: "border-slate-300",
|
|
textColor: "text-slate-600"
|
|
};
|
|
}
|
|
};
|
|
|
|
const totalDuration = steps.reduce((sum, step) => sum + (step.duration || 0), 0);
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<div className="flex items-center justify-between">
|
|
<CardTitle className="flex items-center space-x-2">
|
|
<Target className="h-5 w-5" />
|
|
<span>Trial Progress</span>
|
|
</CardTitle>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge variant="outline" className="text-xs">
|
|
{completedSteps}/{steps.length} steps
|
|
</Badge>
|
|
{totalDuration > 0 && (
|
|
<Badge variant="outline" className="text-xs">
|
|
~{Math.round(totalDuration / 60)}min
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CardHeader>
|
|
|
|
<CardContent className="space-y-6">
|
|
{/* Overall Progress Bar */}
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between text-sm">
|
|
<span className="text-slate-600">Overall Progress</span>
|
|
<span className="font-medium">{Math.round(progress)}%</span>
|
|
</div>
|
|
<Progress
|
|
value={progress}
|
|
className={`h-2 ${
|
|
trialStatus === "completed" ? "bg-green-100" :
|
|
trialStatus === "aborted" || trialStatus === "failed" ? "bg-red-100" :
|
|
"bg-blue-100"
|
|
}`}
|
|
/>
|
|
<div className="flex justify-between text-xs text-slate-500">
|
|
<span>Start</span>
|
|
<span>
|
|
{trialStatus === "completed" ? "Completed" :
|
|
trialStatus === "aborted" ? "Aborted" :
|
|
trialStatus === "failed" ? "Failed" :
|
|
trialStatus === "in_progress" ? "In Progress" :
|
|
"Not Started"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Steps Timeline */}
|
|
<div className="space-y-4">
|
|
<h4 className="font-medium text-slate-900 text-sm">Experiment Steps</h4>
|
|
|
|
<div className="space-y-3">
|
|
{steps.map((step, index) => {
|
|
const stepConfig = stepTypeConfig[step.type];
|
|
const StepIcon = stepConfig.icon;
|
|
const status = getStepStatus(index);
|
|
const statusConfig = getStepStatusConfig(status);
|
|
const StatusIcon = statusConfig.icon;
|
|
|
|
return (
|
|
<div key={step.id} className="relative">
|
|
{/* Connection Line */}
|
|
{index < steps.length - 1 && (
|
|
<div
|
|
className={`absolute left-6 top-12 w-0.5 h-6 ${
|
|
getStepStatus(index + 1) === "completed" ||
|
|
(getStepStatus(index + 1) === "active" && status === "completed")
|
|
? "bg-green-300"
|
|
: "bg-slate-300"
|
|
}`}
|
|
/>
|
|
)}
|
|
|
|
{/* Step Card */}
|
|
<div className={`flex items-start space-x-3 p-3 rounded-lg border transition-all ${
|
|
status === "active"
|
|
? `${statusConfig.bgColor} ${statusConfig.borderColor} shadow-md ring-2 ring-blue-200`
|
|
: status === "completed"
|
|
? `${statusConfig.bgColor} ${statusConfig.borderColor}`
|
|
: status === "aborted"
|
|
? `${statusConfig.bgColor} ${statusConfig.borderColor}`
|
|
: "bg-slate-50 border-slate-200"
|
|
}`}>
|
|
{/* Step Number & Status */}
|
|
<div className="flex-shrink-0 space-y-1">
|
|
<div className={`w-12 h-8 rounded-lg flex items-center justify-center ${
|
|
status === "active" ? statusConfig.bgColor :
|
|
status === "completed" ? "bg-green-100" :
|
|
status === "aborted" ? "bg-red-100" :
|
|
"bg-slate-100"
|
|
}`}>
|
|
<span className={`text-sm font-medium ${
|
|
status === "active" ? statusConfig.textColor :
|
|
status === "completed" ? "text-green-700" :
|
|
status === "aborted" ? "text-red-700" :
|
|
"text-slate-600"
|
|
}`}>
|
|
{index + 1}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-center">
|
|
<StatusIcon className={`h-4 w-4 ${statusConfig.iconColor}`} />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Step Content */}
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-start justify-between">
|
|
<div className="min-w-0 flex-1">
|
|
<h5 className={`font-medium truncate ${
|
|
status === "active" ? "text-slate-900" :
|
|
status === "completed" ? "text-green-900" :
|
|
status === "aborted" ? "text-red-900" :
|
|
"text-slate-700"
|
|
}`}>
|
|
{step.name}
|
|
</h5>
|
|
{step.description && (
|
|
<p className="text-sm text-slate-600 mt-1 line-clamp-2">
|
|
{step.description}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex-shrink-0 ml-3 space-y-1">
|
|
<Badge
|
|
variant="outline"
|
|
className={`text-xs ${stepConfig.textColor} ${stepConfig.borderColor}`}
|
|
>
|
|
<StepIcon className="mr-1 h-3 w-3" />
|
|
{stepConfig.label}
|
|
</Badge>
|
|
{step.duration && (
|
|
<div className="flex items-center space-x-1 text-xs text-slate-500">
|
|
<Clock className="h-3 w-3" />
|
|
<span>{step.duration}s</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Step Status Message */}
|
|
{status === "active" && trialStatus === "in_progress" && (
|
|
<div className="flex items-center space-x-1 mt-2 text-sm text-blue-600">
|
|
<Activity className="h-3 w-3 animate-pulse" />
|
|
<span>Currently executing...</span>
|
|
</div>
|
|
)}
|
|
{status === "active" && trialStatus === "scheduled" && (
|
|
<div className="flex items-center space-x-1 mt-2 text-sm text-amber-600">
|
|
<Clock className="h-3 w-3" />
|
|
<span>Ready to start</span>
|
|
</div>
|
|
)}
|
|
{status === "completed" && (
|
|
<div className="flex items-center space-x-1 mt-2 text-sm text-green-600">
|
|
<CheckCircle className="h-3 w-3" />
|
|
<span>Completed</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Summary Stats */}
|
|
<Separator />
|
|
<div className="grid grid-cols-3 gap-4 text-center">
|
|
<div>
|
|
<div className="text-2xl font-bold text-green-600">{completedSteps}</div>
|
|
<div className="text-xs text-slate-600">Completed</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-2xl font-bold text-blue-600">
|
|
{trialStatus === "in_progress" ? 1 : 0}
|
|
</div>
|
|
<div className="text-xs text-slate-600">Active</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-2xl font-bold text-slate-600">
|
|
{steps.length - completedSteps - (trialStatus === "in_progress" ? 1 : 0)}
|
|
</div>
|
|
<div className="text-xs text-slate-600">Remaining</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|