chore(deps): Update dependencies and refactor API routes for improved error handling

- Updated various dependencies in package.json and pnpm-lock.yaml, including '@clerk/nextjs' to version 6.7.1 and several others for better performance and security.
- Refactored API routes to use Promise.resolve for context parameters, enhancing reliability in asynchronous contexts.
- Improved error handling in the dashboard and studies components, ensuring better user experience during data fetching.
- Removed unused favicon.ico file to clean up the project structure.
- Enhanced the dashboard components to utilize a new utility function for API URL fetching, promoting code reusability.
This commit is contained in:
2024-12-05 11:52:22 -05:00
parent 29ce631901
commit 207f4d7fb8
25 changed files with 719 additions and 682 deletions

View File

@@ -187,15 +187,19 @@ export function Sidebar() {
<div className="border-t border-[hsl(var(--sidebar-separator))]">
<div className="flex items-center justify-between pt-4">
<div className="flex items-center space-x-4">
<UserButton />
<div>
<p className="text-sm font-medium text-[hsl(var(--sidebar-foreground))]">
{user?.fullName ?? user?.username ?? 'User'}
</p>
<p className="text-xs text-[hsl(var(--sidebar-muted))]">
{user?.primaryEmailAddress?.emailAddress ?? 'user@example.com'}
</p>
<div className="w-8 h-8">
<UserButton afterSignOutUrl="/" />
</div>
{user && (
<div className="min-w-0">
<p className="text-sm font-medium text-[hsl(var(--sidebar-foreground))] truncate">
{user.fullName ?? user.username ?? 'User'}
</p>
<p className="text-xs text-[hsl(var(--sidebar-muted))] truncate">
{user.primaryEmailAddress?.emailAddress ?? 'user@example.com'}
</p>
</div>
)}
</div>
</div>
</div>

View File

@@ -1,6 +1,6 @@
'use client';
import { useState, useEffect } from "react";
import { useCallback, useEffect, useState } from "react";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
import { useToast } from "~/hooks/use-toast";
@@ -28,11 +28,7 @@ export function InvitationsTab({ studyId, permissions }: InvitationsTabProps) {
const hasPermission = (permission: string) => permissions.includes(permission);
const canManageRoles = hasPermission(PERMISSIONS.MANAGE_ROLES);
useEffect(() => {
fetchInvitations();
}, [studyId]);
const fetchInvitations = async () => {
const fetchInvitations = useCallback(async () => {
try {
const response = await fetch(`/api/invitations?studyId=${studyId}`);
if (!response.ok) throw new Error("Failed to fetch invitations");
@@ -48,7 +44,12 @@ export function InvitationsTab({ studyId, permissions }: InvitationsTabProps) {
} finally {
setIsLoading(false);
}
};
}, [studyId, toast]);
useEffect(() => {
fetchInvitations();
}, [fetchInvitations]);
const handleDeleteInvitation = async (invitationId: string) => {
try {

View File

@@ -1,6 +1,6 @@
'use client';
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { Button } from "~/components/ui/button";
import {
Dialog,
@@ -41,14 +41,7 @@ export function InviteUserDialog({ studyId, onInviteSent }: InviteUserDialogProp
const [roles, setRoles] = useState<Role[]>([]);
const { toast } = useToast();
// Fetch available roles when dialog opens
useEffect(() => {
if (isOpen) {
fetchRoles();
}
}, [isOpen]);
const fetchRoles = async () => {
const fetchRoles = useCallback(async () => {
try {
const response = await fetch("/api/roles");
if (!response.ok) {
@@ -67,7 +60,12 @@ export function InviteUserDialog({ studyId, onInviteSent }: InviteUserDialogProp
variant: "destructive",
});
}
};
}, [toast]);
// Fetch available roles when dialog opens
useEffect(() => {
fetchRoles();
}, [fetchRoles]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();

View File

@@ -1,6 +1,6 @@
'use client';
import { useState, useEffect } from "react";
import { useCallback, useEffect, useState } from "react";
import { PlusIcon, Trash2Icon } from "lucide-react";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
@@ -43,11 +43,7 @@ export function ParticipantsTab({ studyId, permissions }: ParticipantsTabProps)
const canDeleteParticipant = hasPermission(PERMISSIONS.DELETE_PARTICIPANT);
const canViewNames = hasPermission(PERMISSIONS.VIEW_PARTICIPANT_NAMES);
useEffect(() => {
fetchParticipants();
}, [studyId]);
const fetchParticipants = async () => {
const fetchParticipants = useCallback(async () => {
try {
const response = await fetch(`/api/studies/${studyId}/participants`);
if (!response.ok) throw new Error("Failed to fetch participants");
@@ -63,7 +59,11 @@ export function ParticipantsTab({ studyId, permissions }: ParticipantsTabProps)
} finally {
setIsLoading(false);
}
};
}, [toast, studyId]);
useEffect(() => {
fetchParticipants();
}, [fetchParticipants]);
const createParticipant = async (e: React.FormEvent) => {
e.preventDefault();

View File

@@ -72,7 +72,7 @@ export function SettingsTab({ study }: SettingsTabProps) {
<Card>
<CardContent className="py-8">
<p className="text-center text-muted-foreground">
You don't have permission to edit this study.
You don&apos;t have permission to edit this study.
</p>
</CardContent>
</Card>

View File

@@ -1,6 +1,6 @@
'use client';
import { useState, useEffect } from "react";
import { useState, useEffect, useCallback } from "react";
import { UserAvatar } from "~/components/user-avatar";
import { Button } from "~/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
@@ -38,8 +38,7 @@ import {
interface User {
id: string;
email: string;
firstName: string | null;
lastName: string | null;
name: string | null;
roles: Array<{ id: number; name: string }>;
}
@@ -72,23 +71,7 @@ export function UsersTab({ studyId, permissions }: UsersTabProps) {
const hasPermission = (permission: string) => permissions.includes(permission);
const canManageRoles = hasPermission(PERMISSIONS.MANAGE_ROLES);
useEffect(() => {
fetchData();
}, [studyId]);
const fetchData = async () => {
try {
await Promise.all([
fetchUsers(),
fetchInvitations(),
fetchRoles(),
]);
} finally {
setIsLoading(false);
}
};
const fetchUsers = async () => {
const fetchUsers = useCallback(async () => {
try {
const response = await fetch(`/api/studies/${studyId}/users`);
if (!response.ok) throw new Error("Failed to fetch users");
@@ -102,9 +85,9 @@ export function UsersTab({ studyId, permissions }: UsersTabProps) {
variant: "destructive",
});
}
};
}, [studyId, toast]);
const fetchInvitations = async () => {
const fetchInvitations = useCallback(async () => {
try {
const response = await fetch(`/api/invitations?studyId=${studyId}`);
if (!response.ok) throw new Error("Failed to fetch invitations");
@@ -118,9 +101,9 @@ export function UsersTab({ studyId, permissions }: UsersTabProps) {
variant: "destructive",
});
}
};
}, [studyId, toast]);
const fetchRoles = async () => {
const fetchRoles = useCallback(async () => {
try {
const response = await fetch("/api/roles");
if (!response.ok) throw new Error("Failed to fetch roles");
@@ -136,7 +119,24 @@ export function UsersTab({ studyId, permissions }: UsersTabProps) {
variant: "destructive",
});
}
};
}, [toast]);
const fetchData = useCallback(async () => {
setIsLoading(true);
try {
await Promise.all([
fetchUsers(),
fetchInvitations(),
fetchRoles(),
]);
} finally {
setIsLoading(false);
}
}, [fetchUsers, fetchInvitations, fetchRoles]);
useEffect(() => {
fetchData();
}, [fetchData]);
const handleRoleChange = async (userId: string, newRoleId: string) => {
try {