feat: Implement dynamic plugin definition loading from remote/local sources and standardize action IDs using plugin metadata.

This commit is contained in:
2026-02-02 12:05:52 -05:00
parent 54c34b6f7d
commit 7fd0d97a67
23 changed files with 270 additions and 243 deletions

View File

@@ -161,7 +161,7 @@ export default function NaoTestPage() {
data.topic?.includes("touch") ||
data.topic?.includes("sonar")
) {
setSensorData((prev) => ({
setSensorData((prev: any) => ({
...prev,
[data.topic]: data.msg,
}));
@@ -196,14 +196,14 @@ export default function NaoTestPage() {
const walkForward = () => {
publishMessage("/cmd_vel", "geometry_msgs/Twist", {
linear: { x: walkSpeed[0], y: 0, z: 0 },
linear: { x: walkSpeed[0] ?? 0, y: 0, z: 0 },
angular: { x: 0, y: 0, z: 0 },
});
};
const walkBackward = () => {
publishMessage("/cmd_vel", "geometry_msgs/Twist", {
linear: { x: -walkSpeed[0], y: 0, z: 0 },
linear: { x: -(walkSpeed[0] ?? 0), y: 0, z: 0 },
angular: { x: 0, y: 0, z: 0 },
});
};
@@ -211,14 +211,14 @@ export default function NaoTestPage() {
const turnLeft = () => {
publishMessage("/cmd_vel", "geometry_msgs/Twist", {
linear: { x: 0, y: 0, z: 0 },
angular: { x: 0, y: 0, z: turnSpeed[0] },
angular: { x: 0, y: 0, z: turnSpeed[0] ?? 0 },
});
};
const turnRight = () => {
publishMessage("/cmd_vel", "geometry_msgs/Twist", {
linear: { x: 0, y: 0, z: 0 },
angular: { x: 0, y: 0, z: -turnSpeed[0] },
angular: { x: 0, y: 0, z: -(turnSpeed[0] ?? 0) },
});
};
@@ -232,7 +232,7 @@ export default function NaoTestPage() {
const moveHead = () => {
publishMessage("/joint_angles", "naoqi_bridge_msgs/JointAnglesWithSpeed", {
joint_names: ["HeadYaw", "HeadPitch"],
joint_angles: [headYaw[0], headPitch[0]],
joint_angles: [headYaw[0] ?? 0, headPitch[0] ?? 0],
speed: 0.3,
});
};
@@ -365,7 +365,7 @@ export default function NaoTestPage() {
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>Walk Speed: {walkSpeed[0].toFixed(2)} m/s</Label>
<Label>Walk Speed: {(walkSpeed[0] ?? 0).toFixed(2)} m/s</Label>
<Slider
value={walkSpeed}
onValueChange={setWalkSpeed}
@@ -375,7 +375,7 @@ export default function NaoTestPage() {
/>
</div>
<div className="space-y-2">
<Label>Turn Speed: {turnSpeed[0].toFixed(2)} rad/s</Label>
<Label>Turn Speed: {(turnSpeed[0] ?? 0).toFixed(2)} rad/s</Label>
<Slider
value={turnSpeed}
onValueChange={setTurnSpeed}
@@ -415,7 +415,7 @@ export default function NaoTestPage() {
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label>Head Yaw: {headYaw[0].toFixed(2)} rad</Label>
<Label>Head Yaw: {(headYaw[0] ?? 0).toFixed(2)} rad</Label>
<Slider
value={headYaw}
onValueChange={setHeadYaw}
@@ -425,7 +425,7 @@ export default function NaoTestPage() {
/>
</div>
<div className="space-y-2">
<Label>Head Pitch: {headPitch[0].toFixed(2)} rad</Label>
<Label>Head Pitch: {(headPitch[0] ?? 0).toFixed(2)} rad</Label>
<Slider
value={headPitch}
onValueChange={setHeadPitch}

View File

@@ -260,7 +260,6 @@ export default function StudyAnalyticsPage() {
setSelectedTrialId={setSelectedTrialId}
trialsList={trialsList ?? []}
isLoadingList={isLoadingList}
studyId={studyId}
/>
</Suspense>
</div>

View File

@@ -26,21 +26,17 @@ import {
import { toast } from "sonner";
import { api } from "~/trpc/react";
import { useRouter } from "next/navigation";
import { type Experiment } from "~/lib/experiments/types";
import { type experiments, experimentStatusEnum } from "~/server/db/schema";
import { type InferSelectModel } from "drizzle-orm";
type Experiment = InferSelectModel<typeof experiments>;
const formSchema = z.object({
name: z.string().min(2, {
message: "Name must be at least 2 characters.",
}),
description: z.string().optional(),
status: z.enum([
"draft",
"ready",
"data_collection",
"analysis",
"completed",
"archived",
]),
status: z.enum(experimentStatusEnum.enumValues),
});
interface ExperimentFormProps {
@@ -133,11 +129,9 @@ export function ExperimentForm({ experiment }: ExperimentFormProps) {
</FormControl>
<SelectContent>
<SelectItem value="draft">Draft</SelectItem>
<SelectItem value="testing">Testing</SelectItem>
<SelectItem value="ready">Ready</SelectItem>
<SelectItem value="data_collection">Data Collection</SelectItem>
<SelectItem value="analysis">Analysis</SelectItem>
<SelectItem value="completed">Completed</SelectItem>
<SelectItem value="archived">Archived</SelectItem>
<SelectItem value="deprecated">Deprecated</SelectItem>
</SelectContent>
</Select>
<FormDescription>

View File

@@ -1,5 +1,8 @@
import { notFound } from "next/navigation";
import { type Experiment } from "~/lib/experiments/types";
import { type experiments } from "~/server/db/schema";
import { type InferSelectModel } from "drizzle-orm";
type Experiment = InferSelectModel<typeof experiments>;
import { api } from "~/trpc/server";
import { ExperimentForm } from "./experiment-form";
import {
@@ -43,14 +46,6 @@ export default async function ExperimentEditPage({
title="Edit Experiment"
subtitle={`Update settings for ${experiment.name}`}
icon="Edit"
backButton={
<Button variant="ghost" size="sm" asChild className="-ml-2 mb-2">
<Link href={`/studies/${studyId}/experiments/${experimentId}`}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Experiment
</Link>
</Button>
}
/>
<div className="max-w-2xl">

View File

@@ -39,11 +39,10 @@ export default async function ParticipantDetailPage({
title={participant.participantCode}
subtitle={participant.name ?? "Unnamed Participant"}
icon="Users"
badge={
<Badge variant={participant.consentGiven ? "default" : "secondary"}>
{participant.consentGiven ? "Consent Given" : "No Consent"}
</Badge>
}
status={{
label: participant.consentGiven ? "Consent Given" : "No Consent",
variant: participant.consentGiven ? "default" : "secondary"
}}
actions={
<Button asChild variant="outline" size="sm">
<Link href={`/studies/${studyId}/participants/${participantId}/edit`}>