Add Ratings Functionality

This commit is contained in:
Emma Ruby 2024-11-29 01:01:42 -06:00
parent 3f354d2271
commit ab1aa64695
8 changed files with 114 additions and 41 deletions

View File

@ -11,7 +11,7 @@ export async function GET() {
},
distinct: ['cid'],
})
console.log(controllers)
// console.log(controllers)
return NextResponse.json(controllers)
} catch (error) {
console.error('Failed to fetch controller data:', error)

View File

@ -11,6 +11,22 @@ const facilityTypes = {
6: "CTR",
} as const;
const ratings = {
"-1": "INA",
"0": "SUS",
"1": "OBS",
"2": "S1",
"3": "S2",
"4": "S3",
"5": "C1",
"6": "C2",
"7": "C3",
"8": "I1",
"9": "I2",
"10": "I3",
"11": "SUP",
"12": "ADM",
}
const CZQM_AIRPORTS = ["CYHZ", "CYFC", "CYQM", "CYSJ", "CYZX", "CYYG", "CYYT", "CYQX", "CYYR", "LFVP", "CYQI", "CYAY", "CYDF", "CYJT"];
export const dynamic = 'force-dynamic'
export async function GET() {
@ -22,7 +38,7 @@ export async function GET() {
// Filter controllers in CZQM airspace
const czqmControllers = data.controllers.filter((controller: any) => {
const callsign = controller.callsign;
return callsign.startsWith('CZQM_') ||
return callsign.startsWith('CZQM_') || callsign.startsWith('CZQX_') ||
CZQM_AIRPORTS.some(airport => callsign.startsWith(airport));
});
@ -41,6 +57,7 @@ export async function GET() {
callsign: controller.callsign,
facilityType: facilityType,
frequency: controller.frequency,
rating: ratings[controller.rating],
airport: airport,
lastSeen: new Date(),
logonTime: new Date(controller.logon_time),

View File

@ -9,8 +9,23 @@ import {
import { Skeleton } from "@/components/ui/skeleton";
import { Card } from "@/components/ui/card";
import { formatDistanceToNow, format } from "date-fns";
import { Clock, Calendar, Radio } from "lucide-react";
import { Clock, Calendar, Radio, Star } from "lucide-react";
const ratings = {
"-1": "INA",
"0": "SUS",
"1": "OBS",
"2": "S1",
"3": "S2",
"4": "S3",
"5": "C1",
"6": "C2",
"7": "C3",
"8": "I1",
"9": "I2",
"10": "I3",
"11": "SUP",
"12": "ADM",
}
interface ControllerProfileProps {
sessions: any[];
loading: boolean;
@ -34,41 +49,48 @@ export function ControllerProfile({ sessions, loading }: ControllerProfileProps)
return (
<div className="space-y-6 p-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Clock className="h-5 w-5 text-blue-500" />
<h3 className="text-lg font-semibold">Total Time</h3>
</div>
<p className="text-3xl font-bold">
{totalHours}h {totalMinutes}m
</p>
</Card>
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Calendar className="h-5 w-5 text-blue-500" />
<h3 className="text-lg font-semibold">Total Sessions</h3>
</div>
<p className="text-3xl font-bold">{sessions.length}</p>
</Card>
<Card className="p-6">
<div className="flex items-center gap-3 mb-4">
<Radio className="h-5 w-5 text-blue-500" />
<h3 className="text-lg font-semibold">Positions</h3>
</div>
<div className="flex flex-wrap gap-2">
{positions.map(position => (
<span
key={position}
className="inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900 dark:text-blue-200"
>
{position}
</span>
))}
</div>
</Card>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
{[
{
icon: <Clock className="h-5 w-5 text-blue-500" />,
title: "Total Time",
value: `${totalHours}h ${totalMinutes}m`,
},
{
icon: <Calendar className="h-5 w-5 text-blue-500" />,
title: "Total Sessions",
value: sessions.length,
},
{
icon: <Radio className="h-5 w-5 text-blue-500" />,
title: "Positions",
value: (
<div className="flex flex-wrap gap-2">
{positions.map(position => (
<span
key={position}
className="inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900 dark:text-blue-200"
>
{position}
</span>
))}
</div>
),
},
{
icon: <Star className="h-5 w-5 text-blue-500" aria-hidden="true" />,
title: "Highest Rating",
value: sessions[0].rating || "N/A",
},
].map(({ icon, title, value }, i) => (
<Card key={i} className="p-6 h-full flex flex-col justify-between">
<div className="flex items-center gap-3 mb-4">
{icon}
<h3 className="text-lg font-semibold">{title}</h3>
</div>
<p className="text-3xl font-bold">{value}</p>
</Card>
))}
</div>
<div className="mt-8">
@ -82,6 +104,7 @@ export function ControllerProfile({ sessions, loading }: ControllerProfileProps)
<TableHead>Airport</TableHead>
<TableHead>Frequency</TableHead>
<TableHead>Duration</TableHead>
<TableHead>Rating</TableHead>
</TableRow>
</TableHeader>
<TableBody>
@ -97,6 +120,7 @@ export function ControllerProfile({ sessions, loading }: ControllerProfileProps)
<TableCell>
{formatSessionDuration(session.logonTime, session.lastSeen)}
</TableCell>
<TableCell>{session.rating}</TableCell>
</TableRow>
))}
</TableBody>
@ -116,7 +140,8 @@ function formatSessionDuration(start: string, end: string) {
function LoadingSkeleton() {
return (
<div className="space-y-6 p-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<Skeleton className="h-[140px] rounded-lg" />
<Skeleton className="h-[140px] rounded-lg" />
<Skeleton className="h-[140px] rounded-lg" />
<Skeleton className="h-[140px] rounded-lg" />

View File

@ -36,6 +36,7 @@ export function ControllerTable({ data, loading }: ControllerTableProps) {
<TableHead>Current Position</TableHead>
<TableHead>Airport</TableHead>
<TableHead>Frequency</TableHead>
<TableHead>Rating</TableHead>
<TableHead>Last Seen</TableHead>
<TableHead>Session Duration</TableHead>
</TableRow>
@ -52,6 +53,7 @@ export function ControllerTable({ data, loading }: ControllerTableProps) {
<TableCell>{controller.callsign}</TableCell>
<TableCell>{controller.airport}</TableCell>
<TableCell>{controller.frequency}</TableCell>
<TableCell>{controller.rating}</TableCell>
<TableCell>
{formatDistanceToNow(new Date(controller.lastSeen), { addSuffix: true })}
</TableCell>
@ -82,6 +84,7 @@ function LoadingSkeleton() {
<Skeleton className="h-4 w-[120px]" />
<Skeleton className="h-4 w-[80px]" />
<Skeleton className="h-4 w-[100px]" />
<Skeleton className="h-4 w-[80px]" />
<Skeleton className="h-4 w-[120px]" />
<Skeleton className="h-4 w-[100px]" />
</div>

View File

@ -0,0 +1,22 @@
-- CreateTable
CREATE TABLE "controller_sessions" (
"id" SERIAL NOT NULL,
"cid" TEXT NOT NULL,
"name" TEXT NOT NULL,
"callsign" TEXT NOT NULL,
"facilityType" TEXT NOT NULL,
"frequency" TEXT NOT NULL,
"airport" TEXT NOT NULL,
"rating" TEXT NOT NULL,
"last_seen" TIMESTAMP(3) NOT NULL,
"logon_time" TIMESTAMP(3) NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "controller_sessions_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "controller_sessions_cid_idx" ON "controller_sessions"("cid");
-- CreateIndex
CREATE INDEX "controller_sessions_last_seen_idx" ON "controller_sessions"("last_seen");

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "controller_sessions" ALTER COLUMN "rating" DROP NOT NULL;

View File

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View File

@ -15,6 +15,7 @@ model ControllerSession {
facilityType String
frequency String
airport String
rating String?
lastSeen DateTime @map("last_seen")
logonTime DateTime @map("logon_time")
createdAt DateTime @default(now()) @map("created_at")