Consolidate all study-dependent routes and UI

- Remove global experiments and plugins routes; redirect to study-scoped
  pages
- Update sidebar navigation to separate platform-level and study-level
  items
- Add study filter to dashboard and stats queries
- Refactor participants, trials, analytics pages to use new header and
  breadcrumbs
- Update documentation for new route architecture and migration guide
- Remove duplicate experiment creation route
- Upgrade Next.js to 15.5.4 in package.json and bun.lock
This commit is contained in:
2025-09-24 13:41:29 -04:00
parent e0679f726e
commit cd7c657d5f
18 changed files with 961 additions and 775 deletions

View File

@@ -24,11 +24,20 @@ import {
} from "~/components/ui/card";
import { Badge } from "~/components/ui/badge";
import { Progress } from "~/components/ui/progress";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
import { api } from "~/trpc/react";
// Dashboard Overview Cards
function OverviewCards() {
const { data: stats, isLoading } = api.dashboard.getStats.useQuery();
function OverviewCards({ studyFilter }: { studyFilter: string | null }) {
const { data: stats, isLoading } = api.dashboard.getStats.useQuery({
studyId: studyFilter ?? undefined,
});
const cards = [
{
@@ -105,10 +114,11 @@ function OverviewCards() {
}
// Recent Activity Component
function RecentActivity() {
function RecentActivity({ studyFilter }: { studyFilter: string | null }) {
const { data: activities = [], isLoading } =
api.dashboard.getRecentActivity.useQuery({
limit: 8,
studyId: studyFilter ?? undefined,
});
const getStatusIcon = (status: string) => {
@@ -236,10 +246,11 @@ function QuickActions() {
}
// Study Progress Component
function StudyProgress() {
function StudyProgress({ studyFilter }: { studyFilter: string | null }) {
const { data: studies = [], isLoading } =
api.dashboard.getStudyProgress.useQuery({
limit: 5,
studyId: studyFilter ?? undefined,
});
return (
@@ -313,17 +324,59 @@ function StudyProgress() {
}
export default function DashboardPage() {
const [studyFilter, setStudyFilter] = React.useState<string | null>(null);
// Get user studies for filter dropdown
const { data: userStudiesData } = api.studies.list.useQuery({
memberOnly: true,
limit: 100,
});
const userStudies = userStudiesData?.studies ?? [];
return (
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold tracking-tight">Dashboard</h1>
<h1 className="text-3xl font-bold tracking-tight">
Dashboard
{studyFilter && (
<Badge variant="secondary" className="ml-2">
{userStudies.find((s) => s.id === studyFilter)?.name}
</Badge>
)}
</h1>
<p className="text-muted-foreground">
Welcome to your HRI Studio research platform
{studyFilter
? "Study-specific dashboard view"
: "Welcome to your HRI Studio research platform"}
</p>
</div>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<span className="text-muted-foreground text-sm">
Filter by study:
</span>
<Select
value={studyFilter ?? "all"}
onValueChange={(value) =>
setStudyFilter(value === "all" ? null : value)
}
>
<SelectTrigger className="w-[200px]">
<SelectValue placeholder="All Studies" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Studies</SelectItem>
{userStudies.map((study) => (
<SelectItem key={study.id} value={study.id}>
{study.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Badge variant="outline" className="text-xs">
<Calendar className="mr-1 h-3 w-3" />
{new Date().toLocaleDateString()}
@@ -332,13 +385,13 @@ export default function DashboardPage() {
</div>
{/* Overview Cards */}
<OverviewCards />
<OverviewCards studyFilter={studyFilter} />
{/* Main Content Grid */}
<div className="grid gap-4 lg:grid-cols-7">
<StudyProgress />
<StudyProgress studyFilter={studyFilter} />
<div className="col-span-4 space-y-4">
<RecentActivity />
<RecentActivity studyFilter={studyFilter} />
</div>
</div>