diff --git a/src/app/api/get_employees.ts b/src/app/api/get_employees.ts deleted file mode 100644 index 882ff44..0000000 --- a/src/app/api/get_employees.ts +++ /dev/null @@ -1,18 +0,0 @@ -"use server"; -import type { NextApiRequest, NextApiResponse } from 'next'; -import { getEmployees } from '~/server/functions'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method === 'GET') { - try { - const employees = await getEmployees(); - res.status(200).json(employees); - } catch (error) { - console.error('Error fetching employees:', error); - res.status(500).json({ message: 'Internal server error' }); - } - } else { - res.setHeader('Allow', ['GET']); - res.status(405).json({ message: `Method ${req.method} Not Allowed` }); - } -} diff --git a/src/app/api/get_employees/route.ts b/src/app/api/get_employees/route.ts new file mode 100644 index 0000000..eb8f238 --- /dev/null +++ b/src/app/api/get_employees/route.ts @@ -0,0 +1,18 @@ +"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 }); + } +}; diff --git a/src/app/api/update_status.ts b/src/app/api/update_status.ts deleted file mode 100644 index f8654ac..0000000 --- a/src/app/api/update_status.ts +++ /dev/null @@ -1,29 +0,0 @@ -"use server"; -import type { NextApiRequest, NextApiResponse } from 'next'; -import { updateEmployeeStatus } from '~/server/functions'; - -type UpdateStatusBody = { - employeeIds: number[]; - newStatus: string; -}; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method === 'POST') { - const { employeeIds, newStatus } = req.body as UpdateStatusBody; - - if (!Array.isArray(employeeIds) || typeof newStatus !== 'string') { - return res.status(400).json({ message: 'Invalid input' }); - } - - try { - await updateEmployeeStatus(employeeIds, newStatus); - return res.status(200).json({ message: 'Status updated successfully' }); - } catch (error) { - console.error('Error updating status:', error); - return res.status(500).json({ message: 'Internal server error' }); - } - } else { - res.setHeader('Allow', ['POST']); - return res.status(405).json({ message: `Method ${req.method} Not Allowed` }); - } -} diff --git a/src/app/api/update_status/route.ts b/src/app/api/update_status/route.ts new file mode 100644 index 0000000..4fc8920 --- /dev/null +++ b/src/app/api/update_status/route.ts @@ -0,0 +1,30 @@ +"use server"; + +import { NextRequest, NextResponse } 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 }); + } +}; diff --git a/src/app/page.tsx b/src/app/page.tsx index 151f500..4e16e61 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,7 @@ import { auth } from "~/auth"; import No_Session from "~/components/auth/No_Session"; import TT_Header from "~/components/ui/TT_Header"; -import Techs_Table from "~/components/ui/Techs_Table"; +import Techs from "~/components/ui/Techs"; export default async function HomePage() { const session = await auth(); @@ -12,7 +12,7 @@ export default async function HomePage() {
- +
); } diff --git a/src/components/ui/TT_Header.tsx b/src/components/ui/TT_Header.tsx index 61a9a10..b75ad81 100644 --- a/src/components/ui/TT_Header.tsx +++ b/src/components/ui/TT_Header.tsx @@ -2,12 +2,12 @@ import Image from "next/image"; export default function TT_Header() { return ( -
+
Tech Tracker Logo -

+

Tech Tracker

diff --git a/src/components/ui/Table.tsx b/src/components/ui/Table.tsx index 76f3808..db35241 100644 --- a/src/components/ui/Table.tsx +++ b/src/components/ui/Table.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; // Define the Employee interface to match data fetched on the server interface Employee { @@ -20,6 +20,39 @@ export default function Table({ employees }: { employees: Employee[] }) { setEmployeeData(employees); }, [employees]); + const fetchEmployees = useCallback(async (): Promise => { + const res = await fetch('/api/get_employees', { + method: 'GET', + headers: { + 'Authorization': `Bearer ${process.env.API_KEY}` + } + }); + return res.json() as Promise; + }, []); + + useEffect(() => { + const fetchAndUpdateEmployees = async () => { + const updatedEmployees = await fetchEmployees(); + setEmployeeData(updatedEmployees); + }; + + fetchAndUpdateEmployees() + .catch((error) => { + console.error('Error fetching employees:', error); + }); + + const intervalId = setInterval(() => { + (async () => { + await fetchAndUpdateEmployees(); + })() + .catch((error) => { + console.error('Error fetching employees:', error); + }); + }, 10000); // Poll every 10 seconds + + return () => clearInterval(intervalId); // Clear interval on component unmount + }, [fetchEmployees]); + const handleCheckboxChange = (id: number) => { setSelectedIds((prevSelected) => prevSelected.includes(id) @@ -50,14 +83,10 @@ export default function Table({ employees }: { employees: Employee[] }) { } }; - const fetchEmployees = async (): Promise => { - const res = await fetch('/api/get_employees', { - method: 'GET', - headers: { - 'Authorization': `Bearer ${process.env.API_KEY}` - } - }); - return res.json() as Promise; + const handleKeyPress = async (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + await handleSubmit(); + } }; const formatTime = (timestamp: Date) => { @@ -73,13 +102,13 @@ export default function Table({ employees }: { employees: Employee[] }) { return (
- +
- - - + + + @@ -94,23 +123,27 @@ export default function Table({ employees }: { employees: Employee[] }) { /> - - + + ))}
- NameStatusUpdated At + NameStatusUpdated At
{employee.name}{employee.status}{formatTime(employee.updatedAt)}{employee.status}{formatTime(employee.updatedAt)}
-
+
- //
- //
- //); -//}; diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index f289d63..7fcad48 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -15,7 +15,7 @@ export const users = createTable( id: bigint("id", {mode: "number"}).primaryKey().autoincrement(), name: varchar("name", { length: 256 }).notNull(), status: varchar("status", { length: 256 }).notNull(), - updatedAt: timestamp("updated_at") + updatedAt: timestamp("updatedAt") .default(sql`CURRENT_TIMESTAMP`) .notNull(), }, @@ -27,6 +27,6 @@ export const history = createTable( id: bigint("id", {mode: "number"}).primaryKey().autoincrement(), user_id: bigint("user_id", {mode: "number"}).references(() => users.id), status: varchar("status", { length: 256 }).notNull(), - updatedAt: timestamp("updated_at").notNull(), + updatedAt: timestamp("updatedAt").notNull(), }, ); diff --git a/src/server/functions.ts b/src/server/functions.ts index 89333d1..9ecb4e7 100644 --- a/src/server/functions.ts +++ b/src/server/functions.ts @@ -10,20 +10,21 @@ export const getEmployees = async () => { }; // Function to Update Employee Status using Raw SQL -export const updateEmployeeStatus = async (employeeIds: number[], newStatus: string) => { +export const updateEmployeeStatus = async (employeeIds: string[], newStatus: string) => { try { // Convert array of ids to a format suitable for SQL query (comma-separated string) - const idString = employeeIds.join(","); + const idList = employeeIds.map(id => parseInt(id, 10)); + const updatedAt = new Date(); - // Prepare the raw SQL query with embedded variables - const query = ` + // Prepare the query using drizzle-orm's template-like syntax for escaping variables + const query = sql` UPDATE users - SET status = '${newStatus}', updatedAt = '${new Date().toISOString()}' - WHERE id IN (${idString}) + SET status = ${newStatus}, updatedAt = ${updatedAt} + WHERE id IN ${idList} `; - // Execute the raw SQL query using the execute method - await db.execute(sql`${query}`); + // Execute the query + await db.execute(query); return { success: true }; } catch (error) { diff --git a/src/styles/globals.css b/src/styles/globals.css index d8fb6d9..89dd5a6 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -67,3 +67,55 @@ @apply bg-background text-foreground; } } + +@media (min-width: 2000px) { + .ua-column { + min-width: 500px; + } + .s-column { + max-width: 800px + } +} + +@media (max-width: 2000px) { + .techtable { + width: 90%; + font-size: 36; + } + .tabletitles { + font-size: 40; + } +} + +@media (min-width: 1500px) { + .ua-column { + min-width: 400px; + } +} + +@media (max-width: 1500px) { + .techtable { + font-size: 32px; + } + .tabletitles { + font-size:36px; + } +} + +@media (min-width: 1000px) { + .ua-column { + min-width: 300px; + } +} + +@media (max-width: 1000px) { + .title-text { + font-size: 32px; + } + .techtable { + font-size: 20px; + } + .tabletitles { + font-size:24px; + } +}