diff --git a/src/app/(login)/component/LoginForm.tsx b/src/app/(login)/component/LoginForm.tsx index 3e6a078..d22ad53 100644 --- a/src/app/(login)/component/LoginForm.tsx +++ b/src/app/(login)/component/LoginForm.tsx @@ -29,16 +29,16 @@ export default function LoginForm() { }); if (status?.ok) { - toast.success("Logged in successfully"); + toast.success("Berhasil Login"); router.push("/assignment"); } }; return ( -
-
+
+
-
+
OSKM Logo

LOGIN

-
+

NIM *

@@ -66,13 +60,13 @@ export default function LoginForm() { -
+
setNim(v.target.value)} - className="font-REM h-full w-full bg-white pl-3 pr-3 text-sm text-black placeholder-gray-500 focus:outline-none" + className="h-full w-full rounded-lg bg-white py-2 pl-3 pr-3 text-sm text-black placeholder-gray-500 focus:outline-none" placeholder="Masukkan NIM Anda" />
@@ -85,13 +79,13 @@ export default function LoginForm() { -
+
setPassword(v.target.value)} - className="font-REM h-full w-full bg-white pl-3 pr-10 text-sm text-black placeholder-gray-500 focus:outline-none" + className="font-REM h-full w-full rounded-lg bg-white py-2 pl-3 pr-10 text-sm text-black placeholder-gray-500 focus:outline-none" placeholder="Masukkan Sandi Anda" />
{ - if (downloadUrl) { - const fileBlob = await downloadFile(downloadUrl); - saveAs(fileBlob, judulTugas); + const handleDownloadCsv = async (assignmentId: string) => { + try { + const response = await assignmentCsvMutation.mutateAsync({ + assignmentId, + }); + + const { fileName, mimeType, content } = response; + + const blob = new Blob([content], { type: mimeType }); + saveAs(blob, fileName); + } catch (error) { + console.error("Error downloading CSV:", error); + toast.error("Failed to download CSV."); } }; @@ -59,10 +69,10 @@ export default function MametListAssignment({ await assignmentDeleteMutation.mutateAsync({ assignmentId, }); - alert("Assignment deleted successfully"); + toast.success("Berhasil Menghapus Tugas"); router.refresh(); } catch (err) { - alert("Error deleting assignment"); + toast.error("Gagal Menghapus Tugas"); console.error("Error deleting assignment : ", err); } }; @@ -158,10 +168,7 @@ export default function MametListAssignment({ diff --git a/src/app/(sidebar)/assignment/components/MentorSubmisiPeserta.tsx b/src/app/(sidebar)/assignment/components/MentorSubmisiPeserta.tsx index 50f77b2..fc43e51 100644 --- a/src/app/(sidebar)/assignment/components/MentorSubmisiPeserta.tsx +++ b/src/app/(sidebar)/assignment/components/MentorSubmisiPeserta.tsx @@ -40,14 +40,26 @@ const MentorSubmisiPeserta = ({ const [editingIndex, setEditingIndex] = useState(null); const [editedValue, setEditedValue] = useState(""); const penilaianSubmisi = assignmentSubmissions.map((submission, index) => { - const { nama, nim, keterlambatan, assignmentSubmissions: submissionDetail, linkFile, nilai } = submission; + const { + nama, + nim, + keterlambatan, + assignmentSubmissions: submissionDetail, + linkFile, + nilai, + } = submission; return { no: index + 1, name: nama || "Unknown", nim: nim || "N/A", interval: formatKeterlambatan(keterlambatan), - status: Number(keterlambatan) > 0 ? "Late" : (submissionDetail ? "Submitted" : "Not Submitted"), + status: + Number(keterlambatan) > 0 + ? "Late" + : submissionDetail + ? "Submitted" + : "Not Submitted", nilai: nilai || "0", linksubmisi: linkFile, }; @@ -59,7 +71,7 @@ const MentorSubmisiPeserta = ({ currentValue: number, ) => { if (status.toLowerCase() === "not submitted") { - toast.error("Mentee haven't submitted the assignment yet"); + toast.error("Mentee belum mengumpulkan tugas"); return; } @@ -81,6 +93,8 @@ const MentorSubmisiPeserta = ({ }); toast.success("Nilai berhasil disimpan"); + + router.refresh(); } catch (error) { console.error("Error saving points:", error); toast.error("Gagal menyimpan nilai :" + String(error)); @@ -178,12 +192,13 @@ const MentorSubmisiPeserta = ({
{item.status}
@@ -208,7 +223,11 @@ const MentorSubmisiPeserta = ({

{item.nilai}

- handleEditClick(index, item.status, Number(item.nilai)) + handleEditClick( + index, + item.status, + Number(item.nilai), + ) } className="cursor-pointer" /> @@ -254,7 +273,7 @@ const MentorSubmisiPeserta = ({
  • Previous @@ -280,7 +299,7 @@ const MentorSubmisiPeserta = ({ {index + 1} @@ -289,7 +308,7 @@ const MentorSubmisiPeserta = ({
  • Next diff --git a/src/app/(sidebar)/assignment/page.tsx b/src/app/(sidebar)/assignment/page.tsx index 22f0857..78b6ff5 100644 --- a/src/app/(sidebar)/assignment/page.tsx +++ b/src/app/(sidebar)/assignment/page.tsx @@ -36,12 +36,22 @@ export default async function Page({ }; try { - const response = await api.assignment.getAllMainAssignment({ - page: currentPage, - pageSize: 5, - searchString: query, - sortOrder: "asc", - }); + let response; + if (session?.user.role === "Mamet") { + response = await api.assignment.getAllAssignment({ + page: currentPage, + pageSize: 5, + searchString: query, + sortOrder: "asc", + }); + } else { + response = await api.assignment.getAllMainAssignment({ + page: currentPage, + pageSize: 5, + searchString: query, + sortOrder: "asc", + }); + } if (response?.data) { assignments = response.data; diff --git a/src/app/(sidebar)/assignment/tambah/page.tsx b/src/app/(sidebar)/assignment/tambah/page.tsx index 274e81a..2d6b438 100644 --- a/src/app/(sidebar)/assignment/tambah/page.tsx +++ b/src/app/(sidebar)/assignment/tambah/page.tsx @@ -2,8 +2,21 @@ import React from "react"; import MametNavigationAssignment from "../components/MametNavigationAssignment"; import DashboardHeader from "~/app/components/DashboardHeader"; import MametAddAssignment from "../components/MametAddAssignment"; +import { getServerAuthSession } from "~/server/auth"; +import { redirect } from "next/navigation"; + +export default async function page() { + const session = await getServerAuthSession(); + const user = session?.user; + + if (!user) { + redirect("/"); + } + + if (user.role.toLowerCase() != "mamet") { + redirect("/assignment"); + } -export default function page() { return (
    diff --git a/src/app/(sidebar)/attendance/edit/[id]/page.tsx b/src/app/(sidebar)/attendance/edit/[id]/page.tsx index b488de5..4457d93 100644 --- a/src/app/(sidebar)/attendance/edit/[id]/page.tsx +++ b/src/app/(sidebar)/attendance/edit/[id]/page.tsx @@ -86,8 +86,7 @@ export default async function Page({ eventId: params.id, groupName: kelompok!, presenceEvent: eventType as "Opening" | "Closing", - page: currentPage, - search: search, + limit: 100, }); } catch (error) {} diff --git a/src/app/(sidebar)/gather-points/components/PointList.tsx b/src/app/(sidebar)/gather-points/components/PointList.tsx index d9a06c8..e5152e1 100644 --- a/src/app/(sidebar)/gather-points/components/PointList.tsx +++ b/src/app/(sidebar)/gather-points/components/PointList.tsx @@ -88,7 +88,7 @@ export default function PointList({ const points = parseInt(newPoints); if (points < 0) { - toast.error("Poin harus lebih"); + toast.error("Poin harus lebih besar dari 0"); return; } diff --git a/src/app/(sidebar)/group-information/page.tsx b/src/app/(sidebar)/group-information/page.tsx index 86a713a..66e5b14 100644 --- a/src/app/(sidebar)/group-information/page.tsx +++ b/src/app/(sidebar)/group-information/page.tsx @@ -90,39 +90,39 @@ export default async function Page({ sortBy: sortBy, }); - const kelompokMentorQuery = api.user.detailKelompokMentor({ - userNim: session.user.nim, - page: currentPage, - search: query, - }); - const tugasQuery = api.assignment.getAllMainAssignment({}); - const [kelompokMamet, kelompokMentor, allTugas] = await Promise.all([ + const [kelompokMamet, allTugas] = await Promise.all([ kelompokMametQuery, - kelompokMentorQuery, tugasQuery, ]); - group = await api.user.getMentorGroupName({ - userNim: session.user.nim, - }); - groupInformationsMamet = kelompokMamet; - groupInformationsMentor = kelompokMentor; meta = allTugas.meta; - metaMentor = { - page: kelompokMentor.page, - totalPages: 1, - pageSize: 5, - totalCount: kelompokMentor.mentees.length, - }; + metaMamet = { page: kelompokMamet.page, totalPages: 1, pageSize: 5, totalCount: kelompokMamet.mentees.length, }; + + groupInformationsMentor = await api.user.detailKelompokMentor({ + userNim: session.user.nim, + page: currentPage, + search: query, + }); + + metaMentor = { + page: groupInformationsMentor.page, + totalPages: 1, + pageSize: 5, + totalCount: groupInformationsMentor.mentees.length, + }; + + group = await api.user.getMentorGroupName({ + userNim: session.user.nim, + }); } catch (error) { console.error("Failed to fetch assignments:", error); } diff --git a/src/app/(sidebar)/merchandise/components/MerchandiseList.tsx b/src/app/(sidebar)/merchandise/components/MerchandiseList.tsx index 349ac70..3e905e7 100644 --- a/src/app/(sidebar)/merchandise/components/MerchandiseList.tsx +++ b/src/app/(sidebar)/merchandise/components/MerchandiseList.tsx @@ -62,7 +62,7 @@ export default function MerchandiseList() { ), ); - toast.success("Quantity updated successfully"); + toast.success("Berhasil mengubah quantity"); setEditingId(null); setQuantity(null); }; diff --git a/src/app/components/Sidebar.tsx b/src/app/components/Sidebar.tsx index 2c15bc7..27de245 100644 --- a/src/app/components/Sidebar.tsx +++ b/src/app/components/Sidebar.tsx @@ -31,11 +31,6 @@ const sidebarItems = [ href: "/gather-points", icon: GiAlliedStar, }, - { - name: "Merchandise", - href: "/merchandise", - icon: FaShoppingCart, - }, ]; interface SidebarProps { diff --git a/src/server/api/routers/assignment.ts b/src/server/api/routers/assignment.ts index 26f7db9..dfdc6bb 100644 --- a/src/server/api/routers/assignment.ts +++ b/src/server/api/routers/assignment.ts @@ -12,7 +12,17 @@ import { } from "@katitb2024/database"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; -import { eq, and, inArray, asc, or, ilike, count, desc } from "drizzle-orm"; +import { + eq, + and, + inArray, + asc, + or, + ilike, + count, + desc, + sum, +} from "drizzle-orm"; import { calculateOverDueTime } from "~/utils/dateUtils"; import { createTRPCRouter, @@ -242,34 +252,6 @@ export const assignmentRouter = createTRPCRouter({ try { const { assignmentId, menteeNim, point } = input; - // Fetch group and submission data based on assignmentId and menteeNim - const group = ( - await ctx.db - .select({ - groupName: profiles.group, - groupPoint: groups.point, - assignmentPoint: assignmentSubmissions.point, - }) - .from(assignmentSubmissions) - .innerJoin(users, eq(assignmentSubmissions.userNim, users.nim)) - .innerJoin(profiles, eq(users.id, profiles.userId)) - .innerJoin(groups, eq(profiles.group, groups.name)) - .where( - and( - eq(assignmentSubmissions.assignmentId, assignmentId), - eq(users.nim, menteeNim), - ), - ) - )[0]; - - if (!group) { - throw new TRPCError({ - code: "NOT_FOUND", - message: - "Group data or assignment submission not found for the given assignment and mentee", - }); - } - if (point < 0 || point > 100) { throw new TRPCError({ code: "BAD_REQUEST", @@ -277,10 +259,28 @@ export const assignmentRouter = createTRPCRouter({ }); } - const { groupName, groupPoint, assignmentPoint } = group; + // Fetch submission data to find the previous point + const submission = await ctx.db + .select({ + point: assignmentSubmissions.point, + }) + .from(assignmentSubmissions) + .where( + and( + eq(assignmentSubmissions.assignmentId, assignmentId), + eq(assignmentSubmissions.userNim, menteeNim), + ), + ); + + if (!submission || submission.length === 0) { + throw new TRPCError({ + code: "NOT_FOUND", + message: + "Assignment submission not found for the given mentee and assignment", + }); + } - // Adjust the group's point value by subtracting the previous point and adding the new point - const updatedGroupPoint = groupPoint - (assignmentPoint ?? 0) + point; + const previousPoint = submission[0]?.point ?? 0; // Update the assignment submission's point await ctx.db @@ -293,10 +293,63 @@ export const assignmentRouter = createTRPCRouter({ ), ); + // Recalculate the total points for the mentee's profile + const totalMenteePoints = await ctx.db + .select({ + total: sum(assignmentSubmissions.point), + }) + .from(assignmentSubmissions) + .where(eq(assignmentSubmissions.userNim, menteeNim)) + .then((rows) => rows[0]?.total ?? 0); + + // Update the mentee's profile point in the profiles table + const user = await ctx.db + .select({ + userId: users.id, + }) + .from(users) + .where(eq(users.nim, menteeNim)); + + if (!user || user.length === 0) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } + + await ctx.db + .update(profiles) + .set({ point: Number(totalMenteePoints) }) + .where(eq(profiles.userId, user[0]?.userId ?? "")); + + // Recalculate the total group points for the assignment + const groupInfo = await ctx.db + .select({ + groupName: profiles.group, + groupPoint: groups.point, + }) + .from(profiles) + .innerJoin(users, eq(profiles.userId, users.id)) + .innerJoin(groups, eq(profiles.group, groups.name)) + .where(eq(users.nim, menteeNim)) + .then((rows) => rows[0]); + + if (!groupInfo) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Group information not found for the mentee", + }); + } + + const { groupName, groupPoint } = groupInfo; + + // Recalculate total group points + const newGroupPoints = groupPoint - previousPoint + point; + // Update the group's point in the groups table await ctx.db .update(groups) - .set({ point: updatedGroupPoint }) + .set({ point: newGroupPoints }) .where(eq(groups.name, groupName)); return { @@ -304,7 +357,7 @@ export const assignmentRouter = createTRPCRouter({ message: "Mentee assignment point updated successfully", updatedGroup: { groupName, - updatedGroupPoint, + updatedGroupPoint: newGroupPoints, }, }; } catch (error) { @@ -316,7 +369,7 @@ export const assignmentRouter = createTRPCRouter({ } }), - getAllMainAssignment: mentorMametProcedure + getAllAssignment: mentorMametProcedure .input( z.object({ searchString: z.string().optional().default(""), @@ -384,7 +437,83 @@ export const assignmentRouter = createTRPCRouter({ } }), - getMainQuestAssignmentCsv: mentorMametProcedure + getAllMainAssignment: mentorMametProcedure + .input( + z.object({ + searchString: z.string().optional().default(""), + sortOrder: z.enum(["asc", "desc"]).optional().default("asc"), + page: z.number().optional().default(1), + pageSize: z.number().optional().default(10), + }), + ) + .query(async ({ ctx, input }) => { + try { + const { searchString, sortOrder, page, pageSize } = input; + + const offset = (page - 1) * pageSize; + + const res = await ctx.db + .select({ + judulTugas: assignments.title, + waktuMulai: assignments.startTime, + waktuSelesai: assignments.deadline, + assignmentId: assignments.id, + downloadUrl: assignments.downloadUrl, + }) + .from(assignments) + .where( + and( + eq(assignments.assignmentType, assignmentTypeEnum.enumValues[0]), + or( + ilike(assignments.title, `%${searchString}%`), + ilike(assignments.description, `%${searchString}%`), + ), + ), + ) + .limit(pageSize) + .offset(offset) + .orderBy( + sortOrder === "asc" + ? asc(assignments.startTime) + : desc(assignments.startTime), + ); + + // Count the total number of "Main" assignments + const countRows = ( + await ctx.db + .select({ + count: count(), + }) + .from(assignments) + .where( + eq(assignments.assignmentType, assignmentTypeEnum.enumValues[0]), + ) + )[0] ?? { count: 0 }; // Ensure count matches "Main" assignments + + return { + data: res, + meta: { + totalCount: countRows.count, + page, + pageSize, + totalPages: Math.ceil(countRows.count / pageSize), + }, + }; + } catch (error) { + if (error instanceof TRPCError) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: `An error occurred: ${String(error)}`, + }); + } + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "An error occurred while getting all main assignments", + }); + } + }), + + getSpecificAssignmentCsv: mentorMametProcedure .input( z.object({ assignmentId: z.string(), @@ -511,7 +640,7 @@ export const assignmentRouter = createTRPCRouter({ console.log(error); throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", - message: "An error occurred while generating the CSV", + message: "An error occurred while creating a new assignment", }); } }),