Skip to content

Commit

Permalink
Feat/staff view evaluations (#13)
Browse files Browse the repository at this point in the history
* feat: Add staff evaluations page and component

* Refactor staff evaluations page and component

* add: simple evaluation slots page

* Refactor staff evaluations page and component

* Refactor evaluation slots page link URL

* chore: bump version

* Add fish shell feature to dev container

* Refactor link URL for staff evaluations page

* Refactor link URL for staff evaluations page

* Refactor link URL for staff evaluations page

* Refactor link URL for staff evaluations page
  • Loading branch information
Lodimup authored Oct 12, 2024
1 parent ef6a03d commit 14c97ef
Show file tree
Hide file tree
Showing 46 changed files with 5,190 additions and 259 deletions.
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
}
},
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
"features": {
"ghcr.io/meaningful-ooo/devcontainer-features/fish": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or with the host.
// "forwardPorts": [3000, 5432],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export const ProjectSelectForm = async (p: IProjectSelectForm) => {
label: p.project.name,
value: p.project.name,
}));
console.log(p);

return (
<SelectForm
Expand Down
2 changes: 1 addition & 1 deletion app/app/(landing)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default async function Page() {
<LandingBody />
{isStaff && (
<Button asChild>
<Link href="/staff/iam">Staff IAM</Link>
<Link href="/staff">Staff Dashboard</Link>
</Button>
)}
{isTutor && (
Expand Down
57 changes: 57 additions & 0 deletions app/app/(staff)/staff/evaluation-slots/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Link from "next/link";
import { BackBtn } from "@/components/back-btn";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { getEvaluatedSlots, transformEvaluationSlots } from "./utils";

export default async function Page() {
const dbEvaluationSlots = await getEvaluatedSlots();
const evaluationSlots = transformEvaluationSlots(dbEvaluationSlots);

return (
<div className="flex flex-col gap-4">
<BackBtn />
<Card>
<CardHeader>
<CardTitle>Evaluated Slots</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Action</TableHead>
<TableHead>Project</TableHead>
<TableHead>Date</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{evaluationSlots.map((slot) => (
<TableRow key={slot.id}>
<TableCell>
<Link
href={`/staff/evaluations?evaluationSlotId=${slot.id}`}
className="text-blue-600 hover:underline"
>
View
</Link>
</TableCell>
<TableCell>{slot.project}</TableCell>
<TableCell>{new Date(slot.date).toLocaleString()}</TableCell>
<TableCell>{slot.status}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</div>
);
}
44 changes: 44 additions & 0 deletions app/app/(staff)/staff/evaluation-slots/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { db } from "@/lib/db/clients";
import { evaluationSlots } from "@/drizzle/schemas";
import { and, desc, isNotNull } from "drizzle-orm";

type DbEvaluationSlot = {
id: string;
startDateTime: Date;
project: string | null;
isEvaluated: boolean;
};

type EvaluationSlot = {
id: string;
project: string;
date: string;
status: string;
};

export async function getEvaluatedSlots(): Promise<DbEvaluationSlot[]> {
return await db.query.evaluationSlots.findMany({
where: and(
isNotNull(evaluationSlots.isEvaluated),
evaluationSlots.isEvaluated
),
columns: {
id: true,
startDateTime: true,
project: true,
isEvaluated: true,
},
orderBy: desc(evaluationSlots.startDateTime),
});
}

export function transformEvaluationSlots(
dbSlots: DbEvaluationSlot[]
): EvaluationSlot[] {
return dbSlots.map((slot) => ({
id: slot.id,
project: slot.project || "Unknown",
date: slot.startDateTime.toISOString(),
status: slot.isEvaluated ? "Completed" : "Pending",
}));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { BackBtn } from "@/components/back-btn";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { transformEvaluations } from "../../utils";

export function StaffEvaluations({
evaluations,
}: {
evaluations: Awaited<ReturnType<typeof transformEvaluations>>;
}) {
return (
<main className="flex flex-col gap-4">
<BackBtn />
<Card>
<CardHeader>
<CardTitle>Evaluations</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Project</TableHead>
<TableHead>Student</TableHead>
<TableHead>Comment</TableHead>
<TableHead>Evaluator</TableHead>
<TableHead>Date</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{evaluations.map((evaluation) => (
<TableRow key={evaluation.id}>
<TableCell>{evaluation.project}</TableCell>
<TableCell>{evaluation.student}</TableCell>
<TableHead>{evaluation.comment}</TableHead>
<TableCell>{evaluation.evaluator}</TableCell>
<TableCell>{evaluation.date}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</main>
);
}
17 changes: 17 additions & 0 deletions app/app/(staff)/staff/evaluations/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { StaffEvaluations } from "./_components/staff-evaluations";
import { getAllEvaluations, transformEvaluations } from "./utils";

export default async function Page({
searchParams,
}: {
searchParams?: {
evaluationSlotId?: string;
};
}) {
const dbEvaluations = await getAllEvaluations({
evaluationSlotId: searchParams?.evaluationSlotId || "",
});
const evaluationsData = transformEvaluations(dbEvaluations);

return <StaffEvaluations evaluations={evaluationsData} />;
}
47 changes: 47 additions & 0 deletions app/app/(staff)/staff/evaluations/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { db } from "@/lib/db/clients";
import { evaluationSlots, accounts } from "@/drizzle/schemas";
import { eq } from "drizzle-orm";

export function transformEvaluations(
dbEvaluations: Awaited<ReturnType<typeof getAllEvaluations>>
) {
return dbEvaluations.flatMap((slot) =>
slot.evaluatees.map((evaluatee) => ({
id: evaluatee.id,
student: evaluatee.user.name || "Unknown",
evaluator: slot.evaluator ? slot.evaluator.name : "Unknown",
project: slot.project || "Unknown",
date: slot.startDateTime.toISOString(),
comment: evaluatee.comment,
}))
);
}

export async function getAllEvaluations({
evaluationSlotId,
}: {
evaluationSlotId: string;
}) {
return await db.query.evaluationSlots.findMany({
where: eq(evaluationSlots.id, evaluationSlotId),
with: {
evaluator: true,
evaluatees: {
columns: {
id: true,
comment: true,
isTeamLeader: true,
},
with: {
user: {
with: {
accounts: {
where: eq(accounts.provider, "42-school"),
},
},
},
},
},
},
});
}
7 changes: 4 additions & 3 deletions app/app/(staff)/staff/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { BackBtn } from "@/components/back-btn";
import { TypographyH1 } from "@/components/typographies";
import { Button } from "@/components/ui/button";
import Link from "next/link";

export default function Page() {
return (
<main>
<main className="flex flex-col gap-4">
<BackBtn />
<p>Staff page</p>
<TypographyH1>Staff Dashboard</TypographyH1>
<div className="flex flex-col gap-2">
<Button asChild>
<Link href="/staff/iam">IAM</Link>
</Button>
<Button asChild>
<Link href="/staff/">Booking management</Link>
<Link href="/staff/evaluation-slots">Evaluations</Link>
</Button>
</div>
</main>
Expand Down
57 changes: 57 additions & 0 deletions app/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client"

import * as React from "react"
import * as AccordionPrimitive from "@radix-ui/react-accordion"
import { ChevronDownIcon } from "@radix-ui/react-icons"

import { cn } from "@/lib/utils"

const Accordion = AccordionPrimitive.Root

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
))
AccordionItem.displayName = "AccordionItem"

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
))
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
))
AccordionContent.displayName = AccordionPrimitive.Content.displayName

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
Loading

0 comments on commit 14c97ef

Please sign in to comment.