Vector Motion
Medical

Clinical Trials Card - Research Management

Monitor clinical trial enrollment and participant progress. Track study milestones and trial outcomes.

Clinical Trials Card

The Clinical Trials Card Track clinical trial enrollment, progress, and participant status.

Preview

Installation

ash npx shadcn@latest add https://vectormotion.vercel.app/registry/clinical-trials-card.json

Clinical Trials Card
'use client'import React from 'react';import { Microscope, ArrowUpRight } from 'lucide-react';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx";import { twMerge } from "tailwind-merge";function cn(...inputs: ClassValue[]) {   return twMerge(clsx(inputs));}interface TrialData {   name: string;   progress: number;   status: 'active' | 'recruiting' | 'closing' | 'completed';   [key: string]: any;}interface ClinicalTrialsCardProps {   className?: string;   title?: string;   subtitle?: string;   activeCount?: number;   closingCount?: number;   trials?: TrialData[];}const DEFAULT_TRIALS: TrialData[] = [   { name: 'Oncology Ph. III', progress: 85, status: 'active' },   { name: 'Cardio Ph. II', progress: 15, status: 'recruiting' },];const DEFAULT_TITLE = "Research";const DEFAULT_SUBTITLE = "Clinical Trials";const DEFAULT_ACTIVE_COUNT = 4;const DEFAULT_CLOSING_COUNT = 2;export const ClinicalTrialsCard: React.FC<ClinicalTrialsCardProps> = ({   className = "",   title = DEFAULT_TITLE,   subtitle = DEFAULT_SUBTITLE,   activeCount = DEFAULT_ACTIVE_COUNT,   closingCount = DEFAULT_CLOSING_COUNT,   trials = DEFAULT_TRIALS,}) => {   const isInteractive = true;   const index = 50;   return (      <motion.div         layoutId={isInteractive ? `card-${index}-${title}` : undefined}         transition={{ duration: 0.4, ease: "easeOut" }}         className={cn(            "relative overflow-hidden rounded-2xl border border-border bg-card text-card-foreground shadow-sm transition-all flex flex-col h-full",            isInteractive ? "hover:border-primary/50 hover:shadow-md" : "",            className         )}      >         <div className="p-5 flex flex-col h-full relative z-10">            <div className="mb-4 flex items-start justify-between">               <div>                  <h3 className="font-bold text-lg text-foreground">                     {title}                  </h3>                  <div className="flex items-center gap-1.5 mt-1">                     <span className="relative flex h-2 w-2">                        <span className="relative inline-flex rounded-full h-2 w-2 bg-blue-500"></span>                     </span>                     <p className="text-sm text-muted-foreground font-medium">                        {subtitle}                     </p>                  </div>               </div>               <div className="rounded-xl bg-blue-500/10 p-2.5 text-blue-500 dark:text-blue-400 flex items-center justify-center ring-1 ring-blue-100 dark:ring-blue-800">                  <Microscope className="h-5 w-5" />               </div>            </div>            <div className="flex-1 space-y-4">               {trials.map((trial, i) => (                  <div key={i}>                     <div className="flex justify-between text-xs mb-1.5">                        <span className="font-bold text-zinc-800 dark:text-zinc-200">{trial.name}</span>                        {trial.status === 'recruiting' ? (                           <span className="text-amber-500 font-bold bg-amber-500/10 px-1.5 py-0.5 rounded flex items-center gap-0.5">                              Recruiting <ArrowUpRight className="h-2.5 w-2.5" />                           </span>                        ) : (                           <span className="text-emerald-500 font-bold bg-emerald-500/10 px-1.5 py-0.5 rounded">{trial.progress}%</span>                        )}                     </div>                     <div className="w-full bg-zinc-100 dark:bg-zinc-800 h-2 rounded-full overflow-hidden">                        <motion.div                           initial={{ width: 0 }}                           animate={{ width: `${trial.progress}%` }}                           transition={{ delay: i * 0.2 }}                           className={cn(                              "h-full rounded-full",                              trial.status === 'recruiting' ? "bg-amber-500" : "bg-blue-500"                           )}                        />                     </div>                  </div>               ))}               <div className="pt-2 flex gap-2">                  <div className="px-2 py-1 bg-muted rounded-lg text-[10px] font-bold text-muted-foreground uppercase">                     {activeCount} Active                  </div>                  <div className="px-2 py-1 bg-muted rounded-lg text-[10px] font-bold text-muted-foreground uppercase">                     {closingCount} Closing                  </div>               </div>            </div>         </div>      </motion.div>   );};

Usage

This component is a demo card displaying medical metrics with animated visualizations and dark mode support.

Prop

Type