Add History Drawer & Clean up APIs. Will clean up more when I am home and have updated the iOS app.

This commit is contained in:
Gabriel Brown 2024-07-23 15:18:27 -05:00
parent ef0a79de21
commit b4ff2da7f5
7 changed files with 160 additions and 106 deletions

View File

@ -1,6 +1,7 @@
"use server"; "use server";
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { getEmployees } from '~/server/functions'; import { getEmployees } from '~/server/functions';
import { auth } from '~/auth';
type Technician = { type Technician = {
name: string; name: string;
@ -10,27 +11,30 @@ type Technician = {
export const GET = async (request: Request) => { export const GET = async (request: Request) => {
try { try {
const url = new URL(request.url); const session = await auth();
const apiKey = url.searchParams.get('apikey'); if (!session) {
if (apiKey !== process.env.API_KEY) const url = new URL(request.url);
return NextResponse.json( const apiKey = url.searchParams.get('apikey');
{ message: 'Unauthorized' }, if (apiKey !== process.env.API_KEY)
{ status: 401 } return NextResponse.json(
); { message: 'Unauthorized' },
const employees = await getEmployees(); { status: 401 }
// Necessary because I haven't updated the iOS app );
// yet to expect updatedAt rather than time else {
const formattedEmployees = employees.map((employee: Technician) => ({ const employees = await getEmployees();
name: employee.name, const formattedEmployees = employees.map((employee: Technician) => ({
status: employee.status, name: employee.name,
time: employee.updatedAt status: employee.status,
})); time: employee.updatedAt
return NextResponse.json(formattedEmployees, { status: 200 }); }));
return NextResponse.json(formattedEmployees, { status: 200 });
}
} else {
const employees = await getEmployees();
return NextResponse.json(employees, { status: 200 });
}
} catch (error) { } catch (error) {
console.error('Error fetching employees:', error); console.error('Error fetching employees:', error);
return NextResponse.json( return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
{ message: 'Internal server error' },
{ status: 500 }
);
} }
}; };

View File

@ -0,0 +1,44 @@
// Update Employee Status by IDs
"use server";
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { updateEmployeeStatus } from '~/server/functions';
import { auth } from '~/auth';
type UpdateStatusBody = {
employeeIds: string[];
newStatus: string;
};
export const POST = async (req: NextRequest) => {
const session = await auth();
if (!session) {
const url = new URL(req.url);
const apiKey = url.searchParams.get('apikey');
if (apiKey !== process.env.API_KEY)
return NextResponse.json(
{ message: 'Unauthorized' },
{ status: 401 }
);
} else {
const { employeeIds, newStatus } = await req.json() as UpdateStatusBody;
if (!Array.isArray(employeeIds) || typeof newStatus !== 'string')
return NextResponse.json(
{ message: 'Invalid input' },
{ status: 400 }
);
try {
await updateEmployeeStatus(employeeIds, newStatus);
return NextResponse.json(
{ message: 'Status updated successfully' },
{ status: 200 }
);
} catch (error) {
console.error('Error updating status:', error);
return NextResponse.json(
{ message: 'Internal server error' },
{ status: 500 }
);
}
}
};

View File

@ -1,8 +1,9 @@
// Update Employee Status by Names
"use server"; "use server";
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { updateEmployeeStatusByName } from '~/server/functions'; import { updateEmployeeStatusByName } from '~/server/functions';
interface Technician { type Technician = {
name: string; name: string;
status: string; status: string;
} }

View File

@ -1,17 +0,0 @@
"use server";
import { NextResponse } from 'next/server';
import { getEmployees } from '~/server/functions';
import { auth } from '~/auth';
export const GET = async () => {
try {
const session = await auth();
if (!session)
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
const employees = await getEmployees();
return NextResponse.json(employees, { status: 200 });
} catch (error) {
console.error('Error fetching employees:', error);
return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
}
};

View File

@ -1,38 +0,0 @@
"use server";
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { updateEmployeeStatus } from '~/server/functions';
import { auth } from '~/auth';
type UpdateStatusBody = {
employeeIds: string[];
newStatus: string;
};
export const POST = async (req: NextRequest) => {
const session = await auth();
if (!session)
return NextResponse.json(
{ message: 'Unauthorized' },
{ status: 401 }
);
const { employeeIds, newStatus } = await req.json() as UpdateStatusBody;
if (!Array.isArray(employeeIds) || typeof newStatus !== 'string')
return NextResponse.json(
{ message: 'Invalid input' },
{ status: 400 }
);
try {
await updateEmployeeStatus(employeeIds, newStatus);
return NextResponse.json(
{ message: 'Status updated successfully' },
{ status: 200 }
);
} catch (error) {
console.error('Error updating status:', error);
return NextResponse.json(
{ message: 'Internal server error' },
{ status: 500 }
);
}
};

View File

@ -1,27 +1,87 @@
import { Button } from "~/components/ui/shadcn/button";
import { import {
Drawer,
DrawerClose, DrawerClose,
DrawerContent, DrawerContent,
DrawerDescription,
DrawerFooter, DrawerFooter,
DrawerHeader, DrawerHeader,
DrawerTitle, DrawerTitle,
DrawerTrigger,
} from "~/components/ui/shadcn/drawer"; } from "~/components/ui/shadcn/drawer";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "~/components/ui/shadcn/table";
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "~/components/ui/shadcn/pagination";
//import { Button } from "~/components/ui/shadcn/button";
import Image from "next/image";
<Drawer> export default function History_Drawer() {
<DrawerTrigger>Open</DrawerTrigger> //const
<DrawerContent> return (
<DrawerHeader> <DrawerContent>
<DrawerTitle>Are you absolutely sure?</DrawerTitle> <DrawerHeader>
<DrawerDescription>This action cannot be undone.</DrawerDescription> <DrawerTitle>
</DrawerHeader> <div className="flex flex-row items-center text-center
<DrawerFooter> sm:justify-center sm:ml-0 py-4">
<Button>Submit</Button> <Image src="/images/tech_tracker_logo.png"
<DrawerClose> alt="Tech Tracker Logo" width={60} height={60}
<Button variant="outline">Cancel</Button> className="max-w-[40px] md:max-w-[120px]"
</DrawerClose> />
</DrawerFooter> <h1 className="title-text text-sm md:text-2xl lg:text-6xl
</DrawerContent> bg-gradient-to-r from-[#bec8e6] via-[#F0EEE4] to-[#FFF8E7]
</Drawer> font-bold pl-2 md:pl-4 text-transparent bg-clip-text">
History
</h1>
</div>
</DrawerTitle>
</DrawerHeader>
<Table className="w-5/6 lg:w-1/2 m-auto"
>
<TableHeader>
<TableRow>
<TableHead className="">Name</TableHead>
<TableHead>Status</TableHead>
<TableHead>Updated At</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell className="font-medium">INV001</TableCell>
<TableCell>Paid</TableCell>
<TableCell>Credit Card</TableCell>
</TableRow>
</TableBody>
</Table>
<DrawerFooter>
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationPrevious href="#" />
</PaginationItem>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
<PaginationItem>
<PaginationEllipsis />
</PaginationItem>
<PaginationItem>
<PaginationNext href="#" />
</PaginationItem>
</PaginationContent>
</Pagination>
<DrawerClose>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
);
};

View File

@ -3,9 +3,10 @@ import { useState, useEffect, useCallback } from 'react';
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import Loading from "~/components/ui/Loading"; import Loading from "~/components/ui/Loading";
import { useTVMode } from "~/components/context/TVModeContext"; import { useTVMode } from "~/components/context/TVModeContext";
import { Drawer, DrawerTrigger } from "~/components/ui/shadcn/drawer";
import History_Drawer from "~/components/ui/History_Drawer";
// Define the Employee interface to match data fetched on the server type Employee = {
interface Employee {
id: number; id: number;
name: string; name: string;
status: string; status: string;
@ -22,7 +23,7 @@ export default function Tech_Table({ employees }: { employees: Employee[] }) {
const [employeeData, setEmployeeData] = useState(employees); const [employeeData, setEmployeeData] = useState(employees);
const fetch_employees = useCallback(async (): Promise<Employee[]> => { const fetch_employees = useCallback(async (): Promise<Employee[]> => {
const res = await fetch('/api/v2/get_employees', { const res = await fetch('/api/technicians', {
method: 'GET', method: 'GET',
headers: { headers: {
'Authorization': `Bearer ${process.env.API_KEY}` 'Authorization': `Bearer ${process.env.API_KEY}`
@ -35,12 +36,10 @@ export default function Tech_Table({ employees }: { employees: Employee[] }) {
if (!session) { if (!session) {
alert("You must be signed in to update status."); alert("You must be signed in to update status.");
return; return;
} } else if (selectedIds.length === 0 && employeeStatus.trim() !== '') {
if (selectedIds.length === 0 && employeeStatus.trim() !== '') {
const cur_user = employees.find(employee => employee.name === session.user?.name); const cur_user = employees.find(employee => employee.name === session.user?.name);
if (cur_user) { if (cur_user) {
await fetch('/api/v2/update_status', { await fetch('/api/update_status_by_id', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -50,7 +49,7 @@ export default function Tech_Table({ employees }: { employees: Employee[] }) {
}); });
} }
} else if (employeeStatus.trim() !== '') { } else if (employeeStatus.trim() !== '') {
await fetch('/api/v2/update_status', { await fetch('/api/update_status_by_id', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -165,9 +164,10 @@ export default function Tech_Table({ employees }: { employees: Employee[] }) {
)} )}
<th className="border border-[#3e4446] py-3">Name</th> <th className="border border-[#3e4446] py-3">Name</th>
<th className="border border-[#3e4446] py-3"> <th className="border border-[#3e4446] py-3">
<button> <Drawer>
Status <DrawerTrigger>Status</DrawerTrigger>
</button> <History_Drawer />
</Drawer>
</th> </th>
<th className="border border-[#3e4446] py-3">Updated At</th> <th className="border border-[#3e4446] py-3">Updated At</th>
</tr> </tr>