diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
index b8735d8..65522eb 100755
--- a/src/app/dashboard/page.tsx
+++ b/src/app/dashboard/page.tsx
@@ -15,20 +15,13 @@ import {
ChevronRight,
Bot,
Radio,
- BarChart3,
- Settings,
+ Building,
} from "lucide-react";
import { Button } from "~/components/ui/button";
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "~/components/ui/card";
import { Badge } from "~/components/ui/badge";
import { ScrollArea } from "~/components/ui/scroll-area";
+import { PageHeader } from "~/components/ui/page-layout";
import { api } from "~/trpc/react";
export default function DashboardPage() {
@@ -42,7 +35,6 @@ export default function DashboardPage() {
const { data: recentTrials } = api.trials.list.useQuery({
limit: 5,
- status: undefined,
});
const { data: liveTrials } = api.dashboard.getLiveTrials.useQuery(
@@ -59,62 +51,57 @@ export default function DashboardPage() {
return "Good evening";
})();
- return (
-
- {/* Header */}
-
-
-
- {greeting}, {userName.split(" ")[0]}
-
-
- Ready to run your next session?
-
-
+ const firstStudy = userStudies?.studies?.[0];
-
-
-
-
-
+ return (
+
+ {/* Header */}
+
+
+
+
+ }
+ />
{/* Live Trials Banner */}
{liveTrials && liveTrials.length > 0 && (
-
-
-
-
-
-
-
-
- {liveTrials.length} Active Session{liveTrials.length > 1 ? "s" : ""}
-
-
- {liveTrials.map((t) => t.participantCode).join(", ")}
-
-
-
-
-
+
+
+
+
+
+
+ {liveTrials.length} Active Session{liveTrials.length > 1 ? "s" : ""}
+
+
+ {liveTrials.map((t) => t.participantCode).join(", ")}
+
+
+
+
)}
{/* Stats Row */}
-
+
@@ -144,22 +131,19 @@ export default function DashboardPage() {
{/* Main Grid */}
{/* Studies List */}
-
-
-
-
-
- Your Studies
-
-
Recent studies and quick access
+
+
+
+
+
Your Studies
-
-
+
+
{userStudies?.studies?.slice(0, 5).map((study) => (
)}
-
-
+
+
{/* Quick Links & Recent */}
{/* Quick Actions */}
-
-
- Quick Actions
-
-
+
+
+
Quick Actions
+
+
-
-
+
+
{/* Recent Trials */}
-
-
-
-
- Recent Trials
-
-
-
+
+
+
{recentTrials?.slice(0, 5).map((trial) => (
@@ -292,8 +276,8 @@ export default function DashboardPage() {
)}
-
-
+
+
@@ -312,15 +296,15 @@ function StatCard({
color: "teal" | "emerald" | "blue" | "violet";
}) {
const colorClasses = {
- teal: "bg-teal-500/10 text-teal-600 dark:text-teal-400",
+ teal: "bg-primary/10 text-primary",
emerald: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400",
blue: "bg-blue-500/10 text-blue-600 dark:text-blue-400",
violet: "bg-violet-500/10 text-violet-600 dark:text-violet-400",
};
return (
-
-
+
+
@@ -328,7 +312,7 @@ function StatCard({
{value}
{label}
-
-
+
+
);
}
diff --git a/src/components/studies/add-member-dialog.tsx b/src/components/studies/add-member-dialog.tsx
new file mode 100644
index 0000000..4fc17d5
--- /dev/null
+++ b/src/components/studies/add-member-dialog.tsx
@@ -0,0 +1,195 @@
+"use client";
+
+import * as React from "react";
+import { useSession } from "~/lib/auth-client";
+import { api } from "~/trpc/react";
+import { toast } from "sonner";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "~/components/ui/dialog";
+import { Button } from "~/components/ui/button";
+import { Input } from "~/components/ui/input";
+import { Label } from "~/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "~/components/ui/select";
+import { Plus, Loader2, Trash2, Shield, UserMinus } from "lucide-react";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "~/components/ui/dropdown-menu";
+
+interface Member {
+ id: string;
+ userId: string;
+ role: string;
+ user: {
+ name: string | null;
+ email: string;
+ };
+}
+
+interface AddMemberDialogProps {
+ studyId: string;
+ children?: React.ReactNode;
+}
+
+export function AddMemberDialog({ studyId, children }: AddMemberDialogProps) {
+ const utils = api.useUtils();
+ const [open, setOpen] = React.useState(false);
+ const [email, setEmail] = React.useState("");
+ const [role, setRole] = React.useState
("researcher");
+
+ const { data: membersData } = api.studies.getMembers.useQuery({ studyId });
+
+ const addMember = api.studies.addMember.useMutation({
+ onSuccess: () => {
+ toast.success("Member added successfully");
+ void utils.studies.getMembers.invalidate();
+ setEmail("");
+ setRole("researcher");
+ setOpen(false);
+ },
+ onError: (error) => {
+ toast.error(error.message || "Failed to add member");
+ },
+ });
+
+ const removeMember = api.studies.removeMember.useMutation({
+ onSuccess: () => {
+ toast.success("Member removed");
+ void utils.studies.getMembers.invalidate();
+ },
+ onError: (error) => {
+ toast.error(error.message || "Failed to remove member");
+ },
+ });
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (!email || !role) return;
+ addMember.mutate({ studyId, email, role: role as "researcher" | "wizard" | "observer" });
+ };
+
+ const handleRemove = (memberId: string, memberName: string | null) => {
+ if (confirm(`Remove ${memberName ?? memberId} from this study?`)) {
+ removeMember.mutate({ studyId, memberId });
+ }
+ };
+
+ const members = membersData ?? [];
+ const currentUser = members.find((m) => m.userId);
+ const isOwner = currentUser?.role === "owner";
+
+ return (
+
+ );
+}