Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a5686c7
Initial conversion to cards
emsilkina Mar 27, 2025
ca0a745
Made them horizontal
emsilkina Mar 27, 2025
674067a
Fixed wrap around - now need to center
emsilkina Mar 27, 2025
643e783
Added classroomManagement page + management button
emsilkina Mar 31, 2025
4cc634b
Added archive button - functionality works only after reloading page
emsilkina Mar 31, 2025
0981853
Added back button
emsilkina Mar 31, 2025
f96ce82
Fixed typo
emsilkina Mar 31, 2025
ae0be28
Added name change button to management page
emsilkina Mar 31, 2025
0ae68fa
Moved invite member to class management page
emsilkina Mar 31, 2025
4df2cad
Added Delete Button
emsilkina Mar 31, 2025
a0f69b3
Added Upload Materials Button
emsilkina Mar 31, 2025
c96fbcc
Member Deletion
emsilkina Apr 1, 2025
62c4d95
Merge branch 'main' into DEVT-32-Classroom-Cards
safipatel Apr 2, 2025
83df24f
toolkit
emsilkina Apr 3, 2025
6e58c1a
change delay duration
safipatel Apr 3, 2025
badbf65
added toast with search params
safipatel Apr 3, 2025
3e06fe6
addedIcons
emsilkina Apr 3, 2025
7260634
resolvingErrors
emsilkina Apr 3, 2025
e0e73d3
addedMembersListManagementPage
emsilkina Apr 3, 2025
e4da343
fixed erorr
safipatel Apr 3, 2025
363593f
Merge branch 'DEVT-32-Classroom-Cards' of https://github.com/TechAtNY…
safipatel Apr 3, 2025
55a67b4
updated buttons
safipatel Apr 3, 2025
bd73d4e
added animated option to card
safipatel Apr 3, 2025
41604dc
editedMembersButton
emsilkina Apr 4, 2025
81eef9e
addedCreateClassButton
emsilkina Apr 4, 2025
1dcc6f3
Fixed create classroom
emsilkina Apr 4, 2025
67cc9ba
added dialog
safipatel Apr 4, 2025
86ebff1
Added toast for creating a new classroom
emsilkina Apr 4, 2025
7e4ba17
routes classroom after deletion
emsilkina Apr 4, 2025
d86f33f
changes
emsilkina Apr 5, 2025
af5e243
added delete success and refresh
safipatel Apr 5, 2025
0cf7d3b
added delete row button
safipatel Apr 5, 2025
974c7de
Added Alert Dialog
emsilkina Apr 5, 2025
6088f80
Created Change Classroom Name Alert
emsilkina Apr 5, 2025
d2037fc
added dialog for new classroom
emsilkina Apr 6, 2025
5615b29
Implemented Member Removal
emsilkina Apr 6, 2025
ffbf44f
Added archive toast
emsilkina Apr 6, 2025
1b881d0
edited success variant color
emsilkina Apr 6, 2025
1c7e269
Member deletion shows on screen
emsilkina Apr 6, 2025
252101c
made some dialog changes
safipatel Apr 7, 2025
e964818
continued dialog changes
safipatel Apr 8, 2025
93b6496
added label from shad
safipatel Apr 8, 2025
9caa02d
removed and moved
safipatel Apr 8, 2025
d1610ca
refactored classroom page code, manage code, dialog code, etc.
safipatel Apr 8, 2025
8dad5f7
fixed stale data for deletion
safipatel Apr 8, 2025
8f44476
formatting
safipatel Apr 8, 2025
c680dc4
Merge branch 'main' into DEVT-32-Classroom-Cards
safipatel Apr 8, 2025
621030a
moved router
safipatel Apr 8, 2025
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
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
"use client";
import { useState } from "react";
import { inviteMemberToClassroom } from "./actions";
import { inviteMemberToClassroom } from "../../../actions";
import { toast } from "@shared/hooks/use-toast";

export default function InviteMember({
classroomId,
onInviteSuccess,
}: {
classroomId: number;
onInviteSuccess: () => void;
}) {
export default function InviteMember({ classroomId }: { classroomId: number }) {
const [email, setEmail] = useState("");
const handleInvite = async () => {
try {
await inviteMemberToClassroom(email, classroomId);
setEmail("");
onInviteSuccess();
toast({
title: "Added Member Successfully",
description: `${email} was added to the class.`,
});
} catch (error: unknown) {
//type unknown for typescript lint
if (error instanceof Error) {
console.error(error.message);
toast({
variant: "destructive",
title: "The user is already part of the classroom.",
// description: { email } + "was added to the class.",
});
// console.error(error.message);
} else {
console.error("Error Occured");
}
Expand Down
189 changes: 189 additions & 0 deletions app/classroom/[classroomId]/manage/buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// import { createClient } from "@/utils/supabase/server"; // notice how it uses the server one since we don't have "useclient" so the default is server side component
// "use client";

// import { getCurrentUserId, retrieveClassroomData } from "../../classroom/actions";
"use client";

import Link from "next/link";
// import MemberList from "../../classroom/memberList";
import {
changeClassroomName,
deleteClassroom,
setArchiveStatusClassroom,
} from "../../actions";
import { optimisticUpdateAndFetchClassroomData } from "../../clientUtils";
import {
ClassroomWithMembers,
getUserAndClassroomData,
UserWithClassroomsData,
} from "@shared/lib/userContext/contextFetcher";
import { useRouter } from "next/navigation";
import { toast } from "@shared/hooks/use-toast";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@shared/components/ui/alert-dialog";
import { Dispatch, SetStateAction, useTransition } from "react";
import { User } from "@supabase/supabase-js";
import SaveClassroomDialog from "../../_components/saveClassroomDialog";
import { Skeleton } from "@shared/components/ui/skeleton";
import MemberList from "../../memberList";
import InviteMember from "./_components/inviteMember";
import { Loader2 } from "lucide-react";

export default function ClassroomManagementButtons({
userData,
classroomData,
setUserAndClassCallback,
}: {
userData: User;
classroomData: ClassroomWithMembers;
setUserAndClassCallback: Dispatch<SetStateAction<UserWithClassroomsData>>;
}) {
const router = useRouter();
const [isPending, startTransition] = useTransition();

// optimisticUpdateAndFetchClassroomData(
// classroomId,
// async () => changeClassroomName(classroomId, newName),
// { name: newName },
// setUserAndClassData,
// refreshClassrooms
// );
// };
const refreshClassrooms = async () => {
const refreshedData = await getUserAndClassroomData();
if (refreshedData) {
setUserAndClassCallback(refreshedData);
}
};

const handleChangeClassroomName = async (newName: string) => {
return await optimisticUpdateAndFetchClassroomData(
async () => changeClassroomName(classroomData.id, newName),
{ name: newName },
setUserAndClassCallback,
classroomData.id,
refreshClassrooms
);
};

const deleteClassroomFunction = async () => {
router.push(`/classroom`);
startTransition(async () => {
await deleteClassroom(classroomData.id);
});
toast({
title: "Successfully deleted classroom",
});
// const confirmation = window.confirm(
// "Are you sure? This action can't be undone."
// );
// if (confirmation) {

// const delete_success = new URL("/classroom");
// delete_success.searchParams.append(
// "delete_success",
// classroomId.toString()
// );
// redirect(delete_success)

refreshClassrooms();
};

const archiveClassroomCallback = async () => {
// optimisticUpdateAndFetchClassroomData(
// classroomId,
// async () => setArchiveStatusClassroom(classroomId, true),
// { archived: true },
// setUserAndClassData,
// refreshClassrooms
// );
setArchiveStatusClassroom(classroomData.id, true);
toast({
title: "Successfully archived classroom",
});
router.push(`/classroom`);
// router.push(`/classroom?archive_success=${classroomData.id.toString()}`);
refreshClassrooms();
};

return (
<div>
{"Look at the class info: " + classroomData.name}
<Link href={`upload`} passHref>
<button
type="button"
className="me-2 rounded-lg border border-green-700 px-5 py-2.5 text-center text-sm font-medium text-green-700 hover:bg-green-800 hover:text-white focus:outline-none focus:ring-4 focus:ring-green-300 dark:border-green-500 dark:text-green-500 dark:hover:bg-green-600 dark:hover:text-white dark:focus:ring-green-900"
>
Upload Materials
</button>
</Link>
{/* ARCHIVE BUTTON */}
<button
type="button"
className="me-2 rounded-lg border border-red-700 px-5 py-2.5 text-center text-sm font-medium text-red-700 hover:bg-red-800 hover:text-white focus:outline-none focus:ring-4 focus:ring-red-300 dark:border-red-500 dark:text-red-500 dark:hover:bg-red-600 dark:hover:text-white dark:focus:ring-red-900"
onClick={() => archiveClassroomCallback()}
>
Archive
</button>

{/* <button
type="button"
className="me-2 rounded-lg border border-red-700 px-5 py-2.5 text-center text-sm font-medium text-red-700 hover:bg-red-800 hover:text-white focus:outline-none focus:ring-4 focus:ring-red-300 dark:border-red-500 dark:text-red-500 dark:hover:bg-red-600 dark:hover:text-white dark:focus:ring-red-900"
onClick={() => deleteClassroomFunction(classroomIdNumber)}
>
Delete
</button> */}

<AlertDialog>
<AlertDialogTrigger>Delete</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This will permanently delete this
classroom.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
disabled={isPending}
onClick={() => deleteClassroomFunction()}
>
{isPending && <Loader2 className="animate-spin" />} Continue
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>

<SaveClassroomDialog
// isDialogOpen={isDialogOpen}
// setIsDialogOpen={setIsDialogOpen}
optimisticUpdateCallback={handleChangeClassroomName}
actionText="update"
/>

{classroomData.Classroom_Members &&
classroomData.Classroom_Members.length > 0 ? (
<MemberList
classroom={classroomData}
enableDeletion={true}
userId={userData.id}
/>
) : (
<Skeleton></Skeleton>
)}
<p>Invite Member:</p>
<InviteMember classroomId={classroomData.id} />
</div>
);
}
83 changes: 83 additions & 0 deletions app/classroom/[classroomId]/manage/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// import { createClient } from "@/utils/supabase/server"; // notice how it uses the server one since we don't have "useclient" so the default is server side component
"use client";

// import { getCurrentUserId, retrieveClassroomData } from "../../classroom/actions";

import { useContext, useEffect, useState } from "react";
import ClassroomManagementButtons from "./buttons";
import Link from "next/link";
import { UserContext } from "@shared/lib/userContext/userContext";
import { useParams } from "next/navigation";
import { Skeleton } from "@shared/components/ui/skeleton";
import { ClassroomWithMembers } from "@shared/lib/userContext/contextFetcher";

export default function ClassroomManagementPage() {
const { classroomId } = useParams<{ classroomId: string }>();
const [staleDataIfDeleted, setStaleDataIfDeleted] = useState<
ClassroomWithMembers | undefined
>();

const userContext = useContext(UserContext);
if (!userContext) {
return (
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
);
}
const classroomIdNumber = Number(classroomId);
const { setUserAndClassData, userAndClassData } = userContext;
const classroomInfo = userAndClassData.classroomsData.find(
(x) => x.id === classroomIdNumber
);

// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
setStaleDataIfDeleted(structuredClone(classroomInfo));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// If the userContext is undefined still, give loading visual
let classToRender = undefined;

if (!classroomInfo) {
if (staleDataIfDeleted) {
classToRender = staleDataIfDeleted;
} else {
return (
<div className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
);
}
} else {
classToRender = classroomInfo;
}

return (
<div>
<h1>Hello this is classroom {classroomId}</h1>
<ClassroomManagementButtons
classroomData={classToRender}
setUserAndClassCallback={setUserAndClassData}
userData={userAndClassData.userData}
/>
<Link href={`/classroom`} passHref>
<button
type="button"
className="me-2 rounded-lg border border-green-700 px-5 py-2.5 text-center text-sm font-medium text-green-700 hover:bg-green-800 hover:text-white focus:outline-none focus:ring-4 focus:ring-green-300 dark:border-green-500 dark:text-green-500 dark:hover:bg-green-600 dark:hover:text-white dark:focus:ring-green-900"
>
Classroom Page
</button>
</Link>
</div>
);
}
File renamed without changes.
Loading