Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions actions/(announcements)/announcements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export async function sendAnnouncement(
const announcement = await prisma.announcements.findUnique({
where: { id: announcementId },
include: {
team: {
teams: {
include: {
teamMembers: {
team_members: {
include: {
member: true,
members: true,
},
},
},
Expand All @@ -51,7 +51,7 @@ export async function sendAnnouncement(
}

// Verify user is team leader
if (announcement.team.leader_id !== leaderId) {
if (announcement.teams.leader_id !== leaderId) {
return { success: false, error: "Unauthorized: Not team leader" };
}

Expand Down Expand Up @@ -85,9 +85,9 @@ export async function sendAnnouncement(
}

// Fetch team members
const recipients = announcement.team.teamMembers.map((tm) => ({
email: tm.member.email,
name: tm.member.full_name || undefined,
const recipients = announcement.teams.team_members.map((tm) => ({
email: tm.members.email,
name: tm.members.full_name || undefined,
}));

if (recipients.length === 0) {
Expand All @@ -109,7 +109,7 @@ export async function sendAnnouncement(
recipients,
title: announcement.title,
content: announcement.content,
teamName: announcement.team.name,
teamName: announcement.teams.name,
});

// Store batch IDs (as JSON array if multiple batches)
Expand Down Expand Up @@ -177,15 +177,15 @@ export async function getAnnouncementStatus(announcementId: bigint) {
const announcement = await prisma.announcements.findUnique({
where: { id: announcementId },
include: {
team: true,
teams: true,
},
});

if (!announcement) {
return { success: false, error: "Announcement not found" };
}

if (announcement.team.leader_id !== leaderId) {
if (announcement.teams.leader_id !== leaderId) {
return { success: false, error: "Unauthorized" };
}

Expand Down
4 changes: 2 additions & 2 deletions actions/(announcements)/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ export async function deleteAnnouncement(announcementId: bigint) {
// Fetch announcement with team
const announcement = await prisma.announcements.findUnique({
where: { id: announcementId },
include: { team: true },
include: { teams: true },
});

if (!announcement) {
return { success: false, error: "Announcement not found" };
}

// Verify team ownership
if (announcement.team.leader_id !== leaderId) {
if (announcement.teams.leader_id !== leaderId) {
return { success: false, error: "Unauthorized: Not team leader" };
}

Expand Down
10 changes: 5 additions & 5 deletions actions/members.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,17 @@ export async function getMembersForTeam(teamId: string) {
const teamMembers = await prisma.team_members.findMany({
where: { team_id: teamId },
include: {
member: true,
members: true,
},
orderBy: {
added_at: "desc",
},
});

const members = teamMembers.map((tm) => ({
id: tm.member.id,
email: tm.member.email,
full_name: tm.member.full_name,
id: tm.members.id,
email: tm.members.email,
full_name: tm.members.full_name,
added_at: tm.added_at,
}));

Expand Down Expand Up @@ -273,7 +273,7 @@ export async function updateMember(
const teamMembership = await prisma.team_members.findFirst({
where: {
member_id: memberId,
team: {
teams: {
leader_id: leaderId,
},
},
Expand Down
21 changes: 14 additions & 7 deletions actions/teams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function getTeams() {
id: true,
name: true,
_count: {
select: { teamMembers: true },
select: { team_members: true },
},
},
});
Expand All @@ -38,7 +38,7 @@ export async function getTeams() {
return teams.map((team) => ({
id: team.id,
name: team.name,
memberCount: team._count.teamMembers,
memberCount: team._count.team_members,
}));
}

Expand Down Expand Up @@ -74,7 +74,7 @@ export async function createTeam(teamName: string) {
// Validate team inputs
const result = Team.safeParse({ name: teamName, leader_id: leaderId });
if (!result.success) {
return { success: false, error: result.error.issues[0].message };
throw new Error(result.error.issues[0].message);
}

try {
Expand Down Expand Up @@ -106,7 +106,7 @@ export async function createTeam(teamName: string) {
};
} catch (error) {
console.error("Database Error:", error);
return { success: false, error: "Failed to create team" };
throw new Error(error instanceof Error ? error.message : "Failed to create team");
}
}

Expand All @@ -120,6 +120,13 @@ export async function deleteTeam(teamId: string) {
}

try {
// Delete announcements first
await prisma.announcements.deleteMany({
where: {
team_id: teamId,
},
});

// Delete calendar
const calendarResult = await deleteTeamCalendar(teamId);
if (!calendarResult.success) {
Expand All @@ -136,7 +143,7 @@ export async function deleteTeam(teamId: string) {
return { success: true };
} catch (error) {
console.error("Database Error:", error);
return { success: false, error: "Failed to delete team" };
throw new Error(error instanceof Error ? error.message : "Failed to delete team");
}
}
// Update teams
Expand All @@ -150,7 +157,7 @@ export async function updateTeam(teamId: string, teamName: string) {
// Validate team inputs
const result = Team.safeParse({ name: teamName, leader_id: leaderId });
if (!result.success) {
return { success: false, error: result.error.issues[0].message };
throw new Error(result.error.issues[0].message);
}

try {
Expand All @@ -172,6 +179,6 @@ export async function updateTeam(teamId: string, teamName: string) {
return { success: true };
} catch (error) {
console.error("Database Error:", error);
return { success: false, error: "Failed to update team" };
throw new Error(error instanceof Error ? error.message : "Failed to update team");
}
}
3 changes: 2 additions & 1 deletion app/(auth)/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ export default function Page() {
// Underline effect
useEffect(() => {
if (emphasisRef.current) {
const isMobile = window.innerWidth < 640;
const annotation = annotate(emphasisRef.current, {
type: "underline",
padding: -3,
strokeWidth: 3,
strokeWidth: isMobile ? 2 : 3,
color: "#2563eb",
});
annotation.show();
Expand Down
16 changes: 9 additions & 7 deletions app/dashboard/components/NewTeamBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ function NewTeamBtn() {
if (!teamName.trim()) return;
setIsLoading(true);

const loadingToast = toast.loading("Creating team...");
try {
toast.loading("Creating team...");
await createTeam(teamName);
toast.dismiss(loadingToast);
toast.success(`Team "${teamName}" created successfully!`);
setTeamName("");
setOpen(false);
} catch (error) {
toast.dismiss(loadingToast);
toast.error(
error instanceof Error ? error.message : "Failed to create team",
);
Expand All @@ -43,18 +45,18 @@ function NewTeamBtn() {
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button className="flex items-center justify-end gap-2">
<Plus className="w-4 h-4" />
<Button className="flex items-center justify-end gap-1.5 sm:gap-2 text-xs sm:text-base" size="sm">
<Plus className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
New Team
</Button>
</DialogTrigger>
<DialogOverlay className="bg-black/30">
<DialogContent className="p-8 rounded-xl outline-4 outline-black overflow-visible">
<DialogHeader className="p-1">
<DialogContent className="p-4.5 sm:p-8 rounded-lg sm:rounded-xl outline-2 sm:outline-4 outline-black overflow-visible">
<DialogHeader className="p-0.5 sm:p-1">
<DialogTitle
className={`${instrumentSerif.className} text-md font-base font-bold text-4xl flex items-center gap-3`}
className={`${instrumentSerif.className} text-md font-base font-bold text-2xl sm:text-4xl flex items-center gap-2 sm:gap-3`}
>
<Users className="w-10 h-10 text-blue-600" />
<Users className="w-7 h-7 sm:w-10 sm:h-10 text-blue-600" />
Create New Team
</DialogTitle>
</DialogHeader>
Expand Down
23 changes: 12 additions & 11 deletions app/dashboard/components/WelcomeMsg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ function WelcomeMsg({ name, avatarUrl, email }: WelcomeMsgProps) {

useEffect(() => {
if (nameRef.current) {
const isMobile = window.innerWidth < 640;
const annotation = annotate(nameRef.current, {
type: "underline",
padding: -3,
strokeWidth: 3,
strokeWidth: isMobile ? 2 : 3,
color: "#2563eb",
});
annotation.show();
Expand All @@ -33,31 +34,31 @@ function WelcomeMsg({ name, avatarUrl, email }: WelcomeMsgProps) {
};

return (
<div className="flex flex-col gap-4">
<h1 className={`${instrumentSerif.className} text-4xl sm:text-5xl`}>
<div className="flex flex-col gap-2 sm:gap-4">
<h1 className={`${instrumentSerif.className} text-2xl sm:text-5xl`}>
Welcome back,{" "}
<span ref={nameRef} className="text-blue-600 italic">
{name}
</span>
</h1>
<div className="flex flex-row justify-between items-center gap-3">
<div className="flex items-center w-fit justify-center gap-2 px-3 py-1.5 bg-white rounded-full border border-gray-200 shadow-sm">
<div className="flex flex-row justify-between items-center gap-2 sm:gap-3">
<div className="flex items-center w-fit justify-center gap-1.5 sm:gap-2 px-2 sm:px-3 py-1 sm:py-1.5 bg-white rounded-full border border-gray-200 shadow-sm">
<Image
src={avatarUrl}
alt={`Profile of ${name}`}
width={24}
height={24}
className="rounded-full"
width={23}
height={23}
className="rounded-full sm:w-6 sm:h-6"
/>
<span className={`${inter.className} text-sm text-gray-600`}>
<span className={`${inter.className} text-xs sm:text-sm text-gray-600`}>
{email}
</span>
</div>
<button
onClick={handleSignOut}
className="flex items-center gap-2 px-3 sm:px-4 py-2 text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors font-medium"
className="flex items-center gap-1.5 sm:gap-2 px-2 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-base text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors font-medium"
>
<LogOut className="w-4 h-4" />
<LogOut className="w-3.5 h-3.5 sm:w-4 sm:h-4" />
<span className="hidden sm:inline">Log Out</span>
</button>
</div>
Expand Down
6 changes: 3 additions & 3 deletions app/dashboard/components/team-table/EditTeamModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ function EditTeamModal({ team, open, onOpenChange }: EditTeamModalProps) {
if (!teamName.trim() || !team) return;
setIsLoading(true);

const loadingToast = toast.loading("Updating team...");
try {
toast.loading("Updating team...");
await updateTeam(team.id, teamName);
toast.dismiss();
toast.dismiss(loadingToast);
toast.success(`Team "${teamName}" updated successfully!`);
onOpenChange(false);
} catch (error) {
toast.dismiss();
toast.dismiss(loadingToast);
toast.error(
error instanceof Error ? error.message : "Failed to update team",
);
Expand Down
55 changes: 32 additions & 23 deletions app/dashboard/components/team-table/TeamTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ export default function TeamTable({ data = sampleData }: TeamTableProps) {

const handleConfirmDelete = async () => {
if (teamToDelete) {
toast.loading(`Deleting team ${teamToDelete.name} ...`);
await deleteTeam(teamToDelete.id);
setDeleteModalOpen(false);
setTeamToDelete(null);
toast.dismiss();
toast.success(`Team ${teamToDelete.name} deleted successfully!`);
const loadingToast = toast.loading(`Deleting team ${teamToDelete.name}...`);
try {
await deleteTeam(teamToDelete.id);
setDeleteModalOpen(false);
setTeamToDelete(null);
toast.dismiss(loadingToast);
toast.success(`Team ${teamToDelete.name} deleted successfully!`);
} catch (error) {
toast.dismiss(loadingToast);
toast.error(
error instanceof Error ? error.message : "Failed to delete team",
);
}
}
};

Expand All @@ -62,23 +69,25 @@ export default function TeamTable({ data = sampleData }: TeamTableProps) {

return (
<>
<DataTable<Team>
data={data}
columns={columns}
searchKey="name"
searchPlaceholder="Search teams..."
emptyMessage="Try adding a new team!"
onRowClick={(team) => router.push(`/dashboard/teams/${team.id}`)}
renderActions={(team) => (
<ActionsMenu
team={team}
onEdit={() => handleEditClick(team)}
onDelete={() => handleDeleteClick(team)}
/>
)}
SearchComponent={SearchInput}
PaginationComponent={Pagination}
/>
<div className="flex-1 flex flex-col min-h-0">
<DataTable<Team>
data={data}
columns={columns}
searchKey="name"
searchPlaceholder="Search teams..."
emptyMessage="Try adding a new team!"
onRowClick={(team) => router.push(`/dashboard/teams/${team.id}`)}
renderActions={(team) => (
<ActionsMenu
team={team}
onEdit={() => handleEditClick(team)}
onDelete={() => handleDeleteClick(team)}
/>
)}
SearchComponent={SearchInput}
PaginationComponent={Pagination}
/>
</div>

<ConfirmationModal
open={deleteModalOpen}
Expand Down
Loading
Loading