Clean up all code
This commit is contained in:
parent
017c07a1cf
commit
88f36531b1
@ -1,23 +1,25 @@
|
|||||||
"use server";
|
"use server";
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { legacyGetHistory } from '~/server/functions';
|
import { getHistory } from '~/server/functions';
|
||||||
|
|
||||||
export const GET = async (request: Request) => {
|
export const GET = async (request: Request) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const apiKey = url.searchParams.get('apikey');
|
const apiKey = url.searchParams.get('apikey');
|
||||||
const page = Number(url.searchParams.get('page')) || 1;
|
const page = Number(url.searchParams.get('page')) || 1;
|
||||||
|
if (apiKey !== process.env.API_KEY)
|
||||||
if (apiKey !== 'zAf4vYVN2pszrK') {
|
return NextResponse.json(
|
||||||
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
|
{ message: 'Unauthorized' },
|
||||||
}
|
{ status: 401 }
|
||||||
|
);
|
||||||
const perPage = 50; // You can adjust the perPage value as needed
|
const perPage = 50;
|
||||||
const historyData = await legacyGetHistory(page, perPage);
|
const historyData = await getHistory(page, perPage);
|
||||||
|
|
||||||
return NextResponse.json(historyData, { status: 200 });
|
return NextResponse.json(historyData, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching history data:', error);
|
console.error('Error fetching history data:', error);
|
||||||
return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
|
return NextResponse.json(
|
||||||
|
{ message: 'Internal server error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use server";
|
"use server";
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { legacyGetEmployees } from '~/server/functions';
|
import { getEmployees } from '~/server/functions';
|
||||||
|
|
||||||
type Technician = {
|
type Technician = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -12,22 +12,25 @@ export const GET = async (request: Request) => {
|
|||||||
try {
|
try {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const apiKey = url.searchParams.get('apikey');
|
const apiKey = url.searchParams.get('apikey');
|
||||||
|
if (apiKey !== process.env.API_KEY)
|
||||||
if (apiKey !== 'zAf4vYVN2pszrK') {
|
return NextResponse.json(
|
||||||
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
|
{ message: 'Unauthorized' },
|
||||||
}
|
{ status: 401 }
|
||||||
|
);
|
||||||
const employees = await legacyGetEmployees();
|
const employees = await getEmployees();
|
||||||
|
// Necessary because I haven't updated the iOS app
|
||||||
|
// yet to expect updatedAt rather than time
|
||||||
const formattedEmployees = employees.map((employee: Technician) => ({
|
const formattedEmployees = employees.map((employee: Technician) => ({
|
||||||
name: employee.name,
|
name: employee.name,
|
||||||
status: employee.status,
|
status: employee.status,
|
||||||
time: employee.updatedAt
|
time: employee.updatedAt
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return NextResponse.json(formattedEmployees, { status: 200 });
|
return NextResponse.json(formattedEmployees, { status: 200 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching employees:', error);
|
console.error('Error fetching employees:', error);
|
||||||
return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
|
return NextResponse.json(
|
||||||
|
{ message: 'Internal server error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
"use server";
|
"use server";
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { legacyUpdateEmployeeStatusByName } from '~/server/functions';
|
import { updateEmployeeStatusByName } from '~/server/functions';
|
||||||
|
|
||||||
// Define the Technician type directly in the file
|
|
||||||
interface Technician {
|
interface Technician {
|
||||||
name: string;
|
name: string;
|
||||||
status: string;
|
status: string;
|
||||||
@ -11,37 +10,42 @@ interface Technician {
|
|||||||
// Type guard to check if an object is a Technician
|
// Type guard to check if an object is a Technician
|
||||||
const isTechnician = (technician: unknown): technician is Technician => {
|
const isTechnician = (technician: unknown): technician is Technician => {
|
||||||
if (typeof technician !== 'object' || technician === null) return false;
|
if (typeof technician !== 'object' || technician === null) return false;
|
||||||
return 'name' in technician && typeof (technician as Technician).name === 'string' &&
|
return 'name' in technician &&
|
||||||
'status' in technician && typeof (technician as Technician).status === 'string';
|
typeof (technician as Technician).name === 'string' &&
|
||||||
|
'status' in technician &&
|
||||||
|
typeof (technician as Technician).status === 'string';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const POST = async (request: Request) => {
|
export const POST = async (request: Request) => {
|
||||||
try {
|
try {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const apiKey = url.searchParams.get('apikey');
|
const apiKey = url.searchParams.get('apikey');
|
||||||
|
if (apiKey !== process.env.API_KEY)
|
||||||
if (apiKey !== 'zAf4vYVN2pszrK') {
|
|
||||||
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
|
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
|
||||||
}
|
|
||||||
|
|
||||||
const body: unknown = await request.json();
|
const body: unknown = await request.json();
|
||||||
|
|
||||||
// Validate the body and its technicians property
|
// Validate the body and its technicians property
|
||||||
if (typeof body !== 'object' || body === null || !Array.isArray((body as { technicians?: unknown[] }).technicians)) {
|
if (typeof body !== 'object' || body === null ||
|
||||||
return NextResponse.json({ message: 'Invalid input: expecting an array of technicians.' }, { status: 400 });
|
!Array.isArray((body as { technicians?: unknown[] }).technicians))
|
||||||
}
|
return NextResponse.json(
|
||||||
|
{ message: 'Invalid input: expecting an array of technicians.' },
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
const technicians = (body as { technicians: unknown[] }).technicians;
|
const technicians = (body as { technicians: unknown[] }).technicians;
|
||||||
|
if (!technicians.every(isTechnician))
|
||||||
if (!technicians.every(isTechnician)) {
|
return NextResponse.json(
|
||||||
return NextResponse.json({ message: 'Invalid input: missing name or status for a technician.' }, { status: 400 });
|
{ message: 'Invalid input: missing name or status for a technician.' },
|
||||||
}
|
{ status: 400 }
|
||||||
|
);
|
||||||
await legacyUpdateEmployeeStatusByName(technicians);
|
await updateEmployeeStatusByName(technicians);
|
||||||
|
return NextResponse.json(
|
||||||
return NextResponse.json({ message: 'Technicians updated successfully.' }, { status: 200 });
|
{ message: 'Technicians updated successfully.' },
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating technicians:', error);
|
console.error('Error updating technicians:', error);
|
||||||
return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
|
return NextResponse.json(
|
||||||
|
{ message: 'Internal server error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"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';
|
import { auth } from '~/auth';
|
||||||
|
@ -12,19 +12,27 @@ type UpdateStatusBody = {
|
|||||||
export const POST = async (req: NextRequest) => {
|
export const POST = async (req: NextRequest) => {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
if (!session)
|
if (!session)
|
||||||
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
|
return NextResponse.json(
|
||||||
|
{ message: 'Unauthorized' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
const { employeeIds, newStatus } = await req.json() as UpdateStatusBody;
|
const { employeeIds, newStatus } = await req.json() as UpdateStatusBody;
|
||||||
|
if (!Array.isArray(employeeIds) || typeof newStatus !== 'string')
|
||||||
if (!Array.isArray(employeeIds) || typeof newStatus !== 'string') {
|
return NextResponse.json(
|
||||||
return NextResponse.json({ message: 'Invalid input' }, { status: 400 });
|
{ message: 'Invalid input' },
|
||||||
}
|
{ status: 400 }
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
await updateEmployeeStatus(employeeIds, newStatus);
|
await updateEmployeeStatus(employeeIds, newStatus);
|
||||||
return NextResponse.json({ message: 'Status updated successfully' }, { status: 200 });
|
return NextResponse.json(
|
||||||
|
{ message: 'Status updated successfully' },
|
||||||
|
{ status: 200 }
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating status:', error);
|
console.error('Error updating status:', error);
|
||||||
return NextResponse.json({ message: 'Internal server error' }, { status: 500 });
|
return NextResponse.json(
|
||||||
|
{ message: 'Internal server error' },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,7 +7,8 @@ import Sign_Out from "~/components/auth/Sign_Out";
|
|||||||
import { type Metadata } from "next";
|
import { type Metadata } from "next";
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Tech Tracker",
|
title: "Tech Tracker",
|
||||||
description: "App used by COG IT employees to update their status throughout the day.",
|
description: "App used by COG IT employees to \
|
||||||
|
update their status throughout the day.",
|
||||||
icons: [
|
icons: [
|
||||||
{
|
{
|
||||||
rel: 'icon',
|
rel: 'icon',
|
||||||
|
@ -9,8 +9,8 @@ export default async function HomePage() {
|
|||||||
return <No_Session />
|
return <No_Session />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen bg-gradient-to-b
|
<main className="min-h-screen
|
||||||
from-[#111111] to-[#212325]">
|
bg-gradient-to-b from-[#111111] to-[#212325]">
|
||||||
<TT_Header />
|
<TT_Header />
|
||||||
<Techs />
|
<Techs />
|
||||||
</main>
|
</main>
|
||||||
|
@ -3,15 +3,15 @@ import Image from "next/image";
|
|||||||
export default function TT_Header() {
|
export default function TT_Header() {
|
||||||
return (
|
return (
|
||||||
<header className="w-full py-2 pt-6 md:py-5">
|
<header className="w-full py-2 pt-6 md:py-5">
|
||||||
<div className="flex flex-row items-center text-center sm:justify-center
|
<div className="flex flex-row items-center text-center
|
||||||
ml-4 sm:ml-0 p-4">
|
sm:justify-center ml-4 sm:ml-0 p-4">
|
||||||
<Image src="/images/tech_tracker_logo.png"
|
<Image src="/images/tech_tracker_logo.png"
|
||||||
alt="Tech Tracker Logo" width={100} height={100}
|
alt="Tech Tracker Logo" width={100} height={100}
|
||||||
className="max-w-[40px] md:max-w-[120px]"
|
className="max-w-[40px] md:max-w-[120px]"
|
||||||
/>
|
/>
|
||||||
<h1 className="title-text text-sm md:text-4xl lg:text-8xl font-bold pl-2 md:pl-12
|
<h1 className="title-text text-sm md:text-4xl lg:text-8xl
|
||||||
bg-gradient-to-r from-[#bec8e6] via-[#F0EEE4] to-[#FFF8E7]
|
bg-gradient-to-r from-[#bec8e6] via-[#F0EEE4] to-[#FFF8E7]
|
||||||
text-transparent bg-clip-text">
|
font-bold pl-2 md:pl-12 text-transparent bg-clip-text">
|
||||||
Tech Tracker
|
Tech Tracker
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,19 +19,7 @@ export default function Table({ employees }: { employees: Employee[] }) {
|
|||||||
const [employeeStatus, setStatus] = useState('');
|
const [employeeStatus, setStatus] = useState('');
|
||||||
const [employeeData, setEmployeeData] = useState(employees);
|
const [employeeData, setEmployeeData] = useState(employees);
|
||||||
|
|
||||||
useEffect(() => {
|
const fetch_employees = useCallback(async (): Promise<Employee[]> => {
|
||||||
if (status !== "loading") {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}, [status]);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Refresh employee data if needed after state updates
|
|
||||||
setEmployeeData(employees);
|
|
||||||
}, [employees]);
|
|
||||||
|
|
||||||
const fetchEmployees = useCallback(async (): Promise<Employee[]> => {
|
|
||||||
const res = await fetch('/api/v2/get_employees', {
|
const res = await fetch('/api/v2/get_employees', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
@ -41,28 +29,39 @@ export default function Table({ employees }: { employees: Employee[] }) {
|
|||||||
return res.json() as Promise<Employee[]>;
|
return res.json() as Promise<Employee[]>;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
const update_status = async () => {
|
||||||
const fetchAndUpdateEmployees = async () => {
|
if (!session) {
|
||||||
const updatedEmployees = await fetchEmployees();
|
alert("You must be signed in to update status.");
|
||||||
setEmployeeData(updatedEmployees);
|
return;
|
||||||
};
|
}
|
||||||
|
// If no employee is selected and status is not empty
|
||||||
fetchAndUpdateEmployees()
|
if (selectedIds.length === 0 && employeeStatus.trim() !== '') {
|
||||||
.catch((error) => {
|
const cur_user = employees.find(employee => employee.name === session.user?.name);
|
||||||
console.error('Error fetching employees:', error);
|
if (cur_user) {
|
||||||
});
|
await fetch('/api/v2/update_status', {
|
||||||
|
method: 'POST',
|
||||||
const intervalId = setInterval(() => {
|
headers: {
|
||||||
(async () => {
|
'Content-Type': 'application/json',
|
||||||
await fetchAndUpdateEmployees();
|
'Authorization': `Bearer ${process.env.API_KEY}`
|
||||||
})()
|
},
|
||||||
.catch((error) => {
|
body: JSON.stringify({ employeeIds: [cur_user.id], newStatus: employeeStatus }),
|
||||||
console.error('Error fetching employees:', error);
|
});
|
||||||
|
}
|
||||||
|
} else if (employeeStatus.trim() !== '') {
|
||||||
|
await fetch('/api/v2/update_status', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${process.env.API_KEY}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ employeeIds: selectedIds, newStatus: employeeStatus }),
|
||||||
});
|
});
|
||||||
}, 10000); // Poll every 10 seconds
|
}
|
||||||
|
const updatedEmployees = await fetch_employees();
|
||||||
return () => clearInterval(intervalId); // Clear interval on component unmount
|
setEmployeeData(updatedEmployees);
|
||||||
}, [fetchEmployees]);
|
setSelectedIds([]);
|
||||||
|
setStatus('');
|
||||||
|
};
|
||||||
|
|
||||||
const handleCheckboxChange = (id: number) => {
|
const handleCheckboxChange = (id: number) => {
|
||||||
setSelectedIds((prevSelected) =>
|
setSelectedIds((prevSelected) =>
|
||||||
@ -82,59 +81,18 @@ export default function Table({ employees }: { employees: Employee[] }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedIds.length === employeeData.length && employeeData.length > 0) {
|
|
||||||
setSelectAll(true);
|
|
||||||
} else {
|
|
||||||
setSelectAll(false);
|
|
||||||
}
|
|
||||||
}, [selectedIds, employeeData]);
|
|
||||||
|
|
||||||
const handleStatusChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleStatusChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setStatus(e.target.value);
|
setStatus(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (!session) {
|
|
||||||
alert("You must be signed in to update status.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If no employee is selected and status is not empty
|
|
||||||
if (selectedIds.length === 0 && employeeStatus.trim() !== '') {
|
|
||||||
const cur_user = employees.find(employee => employee.name === session.user?.name);
|
|
||||||
if (cur_user) {
|
|
||||||
await fetch('/api/v2/update_status', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${process.env.API_KEY}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ employeeIds: [cur_user.id], newStatus: employeeStatus }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (employeeStatus.trim() !== '') {
|
|
||||||
await fetch('/api/v2/update_status', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${process.env.API_KEY}`
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ employeeIds: selectedIds, newStatus: employeeStatus }),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Optionally refresh data on the client-side after update
|
|
||||||
const updatedEmployees = await fetchEmployees();
|
|
||||||
setEmployeeData(updatedEmployees);
|
|
||||||
setSelectedIds([]);
|
|
||||||
setStatus('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyPress = async (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
await handleSubmit();
|
await update_status();
|
||||||
|
// if key is i then focus text input
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Format time for display
|
||||||
const formatTime = (timestamp: Date) => {
|
const formatTime = (timestamp: Date) => {
|
||||||
const date = new Date(timestamp);
|
const date = new Date(timestamp);
|
||||||
const time = date.toLocaleTimeString('en-US', {
|
const time = date.toLocaleTimeString('en-US', {
|
||||||
@ -145,64 +103,121 @@ const handleSubmit = async () => {
|
|||||||
const month = date.toLocaleString('default', { month: 'long' });
|
const month = date.toLocaleString('default', { month: 'long' });
|
||||||
return `${time} - ${month} ${day}`;
|
return `${time} - ${month} ${day}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Loading bar while we wait for auth
|
||||||
|
useEffect(() => {
|
||||||
|
if (status !== "loading") {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
|
// Refresh employee data if needed after state updates
|
||||||
|
useEffect(() => {
|
||||||
|
setEmployeeData(employees);
|
||||||
|
}, [employees]);
|
||||||
|
|
||||||
|
// Fetch employees from the server every 10 seconds
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAndUpdateEmployees = async () => {
|
||||||
|
const updatedEmployees = await fetch_employees();
|
||||||
|
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
|
||||||
|
}, [fetch_employees]);
|
||||||
|
|
||||||
|
// Handle checkbox changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedIds.length === employeeData.length && employeeData.length > 0) {
|
||||||
|
setSelectAll(true);
|
||||||
|
} else {
|
||||||
|
setSelectAll(false);
|
||||||
|
}
|
||||||
|
}, [selectedIds, employeeData]);
|
||||||
|
|
||||||
if (loading) return <Loading interval_amount={3} />;
|
if (loading) return <Loading interval_amount={3} />;
|
||||||
return (
|
else {
|
||||||
<div>
|
return (
|
||||||
<table className="techtable rounded-2xl w-5/6 m-auto text-center text-[42px]">
|
<div>
|
||||||
<thead className="bg-gradient-to-b from-[#282828] to-[#383838]">
|
<table className="techtable rounded-2xl w-5/6 m-auto text-center text-[42px]">
|
||||||
<tr>
|
<thead className="tabletitles border border-[#3e4446]
|
||||||
<th className="tabletitles p-4 border border-[#3e4446] text-[48px]">
|
bg-gradient-to-b from-[#282828] to-[#383838] text-[48px]">
|
||||||
<input
|
<tr>
|
||||||
type="checkbox"
|
<th className="py-3 px-4 border border-[#3e4446]">
|
||||||
className="m-0 cursor-pointer transform scale-150"
|
|
||||||
checked={selectAll}
|
|
||||||
onChange={handleSelectAllChange}
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th className="tabletitles p-2 border border-[#3e4446] text-[48px]">Name</th>
|
|
||||||
<th className="tabletitles p-2 border border-[#3e4446] text-[48px]">Status</th>
|
|
||||||
<th className="tabletitles p-2 border border-[#3e4446] text-[48px]">Updated At</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{employeeData.map((employee) => (
|
|
||||||
<tr className="even:bg-gradient-to-br from-[#272727] to-[#313131]
|
|
||||||
odd:bg-gradient-to-bl odd:from-[#252525] odd:to-[#212125]" key={employee.id}>
|
|
||||||
<td className="p-1 border border-[#3e4446]">
|
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="m-0 cursor-pointer transform scale-150"
|
className="m-auto cursor-pointer transform scale-150"
|
||||||
checked={selectedIds.includes(employee.id)}
|
checked={selectAll}
|
||||||
onChange={() => handleCheckboxChange(employee.id)}
|
onChange={handleSelectAllChange}
|
||||||
/>
|
/>
|
||||||
</td>
|
</th>
|
||||||
<td className="n-column px-1 md:py-5 border border-[#3e4446]">{employee.name}</td>
|
<th className="border border-[#3e4446]">Name</th>
|
||||||
<td className="s-column px-1 md:py-5 border border-[#3e4446]">{employee.status}</td>
|
<th className="border border-[#3e4446]">Status</th>
|
||||||
<td className="ua-column px-1 md:py-5 border border-[#3e4446]">{formatTime(employee.updatedAt)}</td>
|
<th className="border border-[#3e4446]">Updated At</th>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{employeeData.map((employee) => (
|
||||||
<div className="m-auto flex flex-row items-center justify-center py-5">
|
<tr className="even:bg-gradient-to-br from-[#272727] to-[#313131]
|
||||||
<input
|
odd:bg-gradient-to-bl odd:from-[#252525] odd:to-[#212125]"
|
||||||
type="text"
|
key={employee.id}
|
||||||
placeholder="New Status"
|
>
|
||||||
className="min-w-[120px] lg:min-w-[400px] bg-[#F9F6EE] py-2 px-3 border-none rounded-xl text-[#111111] lg:text-2xl"
|
<td className="p-1 border border-[#3e4446]">
|
||||||
value={employeeStatus}
|
<input
|
||||||
onChange={handleStatusChange}
|
type="checkbox"
|
||||||
onKeyDown={handleKeyPress}
|
className="m-0 cursor-pointer transform scale-150"
|
||||||
/>
|
checked={selectedIds.includes(employee.id)}
|
||||||
<button
|
onChange={() => handleCheckboxChange(employee.id)}
|
||||||
type="submit"
|
/>
|
||||||
className="min-w-[100px] lg:min-w-[160px] m-2 p-2 border-none rounded-xl text-center
|
</td>
|
||||||
font-semibold lg:text-2xl hover:text-slate-300
|
<td className="n-column px-1 md:py-5 border border-[#3e4446]">
|
||||||
hover:bg-gradient-to-bl hover:from-[#484848] hover:to-[#333333]
|
{employee.name}
|
||||||
bg-gradient-to-br from-[#595959] to-[#444444]"
|
</td>
|
||||||
onClick={handleSubmit}
|
<td className="s-column px-1 md:py-5 border border-[#3e4446]">
|
||||||
>
|
{employee.status}
|
||||||
Update
|
</td>
|
||||||
</button>
|
<td className="ua-column px-1 md:py-5 border border-[#3e4446]">
|
||||||
|
{formatTime(employee.updatedAt)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="m-auto flex flex-row items-center justify-center py-5">
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
type="text"
|
||||||
|
placeholder="New Status"
|
||||||
|
className="min-w-[120px] lg:min-w-[400px] bg-[#F9F6EE]
|
||||||
|
py-2 px-3 border-none rounded-xl text-[#111111] lg:text-2xl"
|
||||||
|
value={employeeStatus}
|
||||||
|
onChange={handleStatusChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="min-w-[100px] lg:min-w-[160px] m-2 p-2 border-none
|
||||||
|
rounded-xl text-center font-semibold lg:text-2xl hover:text-slate-300
|
||||||
|
hover:bg-gradient-to-bl hover:from-[#484848] hover:to-[#333333]
|
||||||
|
bg-gradient-to-br from-[#595959] to-[#444444]"
|
||||||
|
onClick={update_status}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,37 +2,29 @@ import "server-only";
|
|||||||
import { db } from "~/server/db";
|
import { db } from "~/server/db";
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
|
|
||||||
// Function to Get Employees
|
|
||||||
export const getEmployees = async () => {
|
export const getEmployees = async () => {
|
||||||
return await db.query.users.findMany({
|
return await db.query.users.findMany({
|
||||||
orderBy: (model, { asc }) => asc(model.id),
|
orderBy: (model, { asc }) => asc(model.id),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Uncomment this and change updatedAt below if using localhost and you want correct time.
|
// Update Employee Status uses Raw SQL because Drizzle ORM doesn't support
|
||||||
// I dont know why it is like this.
|
// update with MySQL
|
||||||
//const convertToUTC = (date: Date) => {
|
export const updateEmployeeStatus =
|
||||||
//return new Date(date.setHours(date.getUTCHours())+ 5);
|
async (employeeIds: string[], newStatus: string) => {
|
||||||
//};
|
|
||||||
|
|
||||||
// Function to Update Employee Status using Raw SQL
|
|
||||||
export const updateEmployeeStatus = async (employeeIds: string[], newStatus: string) => {
|
|
||||||
try {
|
try {
|
||||||
// Convert array of ids to a format suitable for SQL query (comma-separated string)
|
// Convert array of ids to a format suitable for SQL query (comma-separated string)
|
||||||
const idList = employeeIds.map(id => parseInt(id, 10));
|
const idList = employeeIds.map(id => parseInt(id, 10));
|
||||||
//const updatedAt = convertToUTC(new Date());
|
let updatedAt = new Date();
|
||||||
const updatedAt = new Date(); // Do not change for PROD! It acts different on PROD
|
// Not sure why but localhost is off by 5 hours
|
||||||
|
if (process.env.NODE_ENV === 'development')
|
||||||
// Prepare the query using drizzle-orm's template-like syntax for escaping variables
|
updatedAt = new Date(updatedAt.setHours(updatedAt.getUTCHours())+ 5);
|
||||||
const query = sql`
|
const query = sql`
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET status = ${newStatus}, updatedAt = ${updatedAt}
|
SET status = ${newStatus}, updatedAt = ${updatedAt}
|
||||||
WHERE id IN ${idList}
|
WHERE id IN ${idList}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Execute the query
|
|
||||||
await db.execute(query);
|
await db.execute(query);
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating employee status:", error);
|
console.error("Error updating employee status:", error);
|
||||||
@ -40,15 +32,32 @@ export const updateEmployeeStatus = async (employeeIds: string[], newStatus: str
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Legacy Functions for Legacy API for iOS App
|
// Function to Update Employee Status by Name using Raw SQL
|
||||||
|
export const updateEmployeeStatusByName =
|
||||||
|
async (technicians:{ name: string, status: string }[]) => {
|
||||||
|
try {
|
||||||
|
for (const technician of technicians) {
|
||||||
|
const { name, status } = technician;
|
||||||
|
const query = sql`
|
||||||
|
UPDATE users
|
||||||
|
SET status = ${status}, updatedAt = ${new Date()}
|
||||||
|
WHERE name = ${name}
|
||||||
|
`;
|
||||||
|
await db.execute(query);
|
||||||
|
}
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating employee status by name:", error);
|
||||||
|
throw new Error("Failed to update status by name");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Type definitions
|
// Type definitions for Paginated History API
|
||||||
interface HistoryEntry {
|
interface HistoryEntry {
|
||||||
name: string;
|
name: string;
|
||||||
status: string;
|
status: string;
|
||||||
time: Date;
|
time: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PaginatedHistory {
|
interface PaginatedHistory {
|
||||||
data: HistoryEntry[];
|
data: HistoryEntry[];
|
||||||
meta: {
|
meta: {
|
||||||
@ -59,25 +68,9 @@ interface PaginatedHistory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const legacyGetEmployees = async () => {
|
export const getHistory =
|
||||||
const employees = await db.query.users.findMany({
|
async (page: number, perPage: number): Promise<PaginatedHistory> => {
|
||||||
orderBy: (model, { asc }) => asc(model.id),
|
|
||||||
});
|
|
||||||
if (employees.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
for (const employee of employees) {
|
|
||||||
const date = new Date(employee.updatedAt);
|
|
||||||
employee.updatedAt = date;
|
|
||||||
}
|
|
||||||
return employees;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to Get History Data with Pagination using Raw SQL
|
|
||||||
export const legacyGetHistory = async (page: number, perPage: number): Promise<PaginatedHistory> => {
|
|
||||||
const offset = (page - 1) * perPage;
|
const offset = (page - 1) * perPage;
|
||||||
|
|
||||||
// Raw SQL queries
|
|
||||||
const historyQuery = sql`
|
const historyQuery = sql`
|
||||||
SELECT u.name, h.status, h.updatedAt
|
SELECT u.name, h.status, h.updatedAt
|
||||||
FROM history h
|
FROM history h
|
||||||
@ -85,31 +78,26 @@ export const legacyGetHistory = async (page: number, perPage: number): Promise<P
|
|||||||
ORDER BY h.id DESC
|
ORDER BY h.id DESC
|
||||||
LIMIT ${perPage} OFFSET ${offset}
|
LIMIT ${perPage} OFFSET ${offset}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const countQuery = sql`
|
const countQuery = sql`
|
||||||
SELECT COUNT(*) AS total_count
|
SELECT COUNT(*) AS total_count
|
||||||
FROM history
|
FROM history
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const [historyResults, countResults] = await Promise.all([
|
const [historyResults, countResults] = await Promise.all([
|
||||||
db.execute(historyQuery),
|
db.execute(historyQuery),
|
||||||
db.execute(countQuery),
|
db.execute(countQuery),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Safely cast results
|
// Safely cast results
|
||||||
const historyRows = historyResults[0] as unknown as { name: string, status: string, updatedAt: Date }[];
|
const historyRows = historyResults[0] as unknown as
|
||||||
|
{ name: string, status: string, updatedAt: Date }[];
|
||||||
const countRow = countResults[0] as unknown as { total_count: number }[];
|
const countRow = countResults[0] as unknown as { total_count: number }[];
|
||||||
|
|
||||||
const totalCount = countRow[0]?.total_count ?? 0;
|
const totalCount = countRow[0]?.total_count ?? 0;
|
||||||
const totalPages = Math.ceil(totalCount / perPage);
|
const totalPages = Math.ceil(totalCount / perPage);
|
||||||
|
|
||||||
// Format and map results
|
// Format and map results
|
||||||
const formattedResults: HistoryEntry[] = historyRows.map(row => ({
|
const formattedResults: HistoryEntry[] = historyRows.map(row => ({
|
||||||
name: row.name,
|
name: row.name,
|
||||||
status: row.status,
|
status: row.status,
|
||||||
time: new Date(row.updatedAt),
|
time: new Date(row.updatedAt),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: formattedResults,
|
data: formattedResults,
|
||||||
meta: {
|
meta: {
|
||||||
@ -121,25 +109,3 @@ export const legacyGetHistory = async (page: number, perPage: number): Promise<P
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to Update Employee Status by Name using Raw SQL
|
|
||||||
export const legacyUpdateEmployeeStatusByName = async (technicians: { name: string, status: string }[]) => {
|
|
||||||
try {
|
|
||||||
// Prepare and execute the queries for each technician
|
|
||||||
for (const technician of technicians) {
|
|
||||||
const { name, status } = technician;
|
|
||||||
const utcdate: Date = new Date();
|
|
||||||
const query = sql`
|
|
||||||
UPDATE users
|
|
||||||
SET status = ${status}, updatedAt = ${utcdate}
|
|
||||||
WHERE name = ${name}
|
|
||||||
`;
|
|
||||||
|
|
||||||
await db.execute(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true };
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating employee status by name:", error);
|
|
||||||
throw new Error("Failed to update status by name");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user