Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/routes/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,4 +228,6 @@ router.delete("/delete", async (req: Request, res: Response) => {
}
});



export default router;
34 changes: 34 additions & 0 deletions backend/src/routes/violation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,38 @@ router.delete("/delete", async (req: Request, res: Response) => {
}
});

router.patch("/status-change", async (req: Request, res: Response) => {
try {
const { id } = req.body;
const { rows: violations } = await pool.query(
`SELECT * FROM violations WHERE id = $1`,
[id],
);

const foundViolation = await violations[0];

if (!foundViolation) {
res.status(404).json({ message: "Violation cannot be found." });
return;
}

const updated = await pool.query(
`UPDATE violations SET paid_status = $1 WHERE id = $2 RETURNING *`,
[!foundViolation.paid_status, id],
);

if (updated.rowCount === 0) {
res.status(400).json({ message: "Update is not successful." });
return;
}

res
.status(200)
.json({ message: "Violation paid status has been updated." });
} catch (error) {
const errorMessage = (error as Error).message;
res.status(500).json({ title: "Unknown Error", message: errorMessage });
}
});

export default router;
63 changes: 36 additions & 27 deletions frontend/src/components/ViolationCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { useDeleteViolation } from "../hooks/violation-hooks/useDeleteViolation"
import { useState, useRef, useEffect } from "react";
import { Violation } from "../types/datatypes";
import { useEditViolation } from "../hooks/violation-hooks/useEditViolation";
import useUpdateStatus from "../hooks/useUpdateStatus";

const ViolationCard = ({ violation }: { violation: Violation }) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [editedViolation, setEditedViolation] = useState<Violation>(violation);
const { deleteViolation } = useDeleteViolation();
const { updateViolation } = useEditViolation()
const { updateViolation } = useEditViolation();
const { handleUpdateStatus } = useUpdateStatus();
const dropdownRef = useRef<HTMLDivElement>(null);
const date = new Date(violation.violation_date!);
const year = date.getFullYear();
Expand All @@ -33,22 +35,28 @@ const ViolationCard = ({ violation }: { violation: Violation }) => {
}
};

const handleStatusClick = async () => {
await handleUpdateStatus(violation.id!);
};

const handleEditViolation = () => {
updateViolation(editedViolation);
setIsEditing(false);
setIsMenuOpen(false);
};

const handleCancelEdit = () => {
if (isEditing){
const handleCancelEdit = () => {
if (isEditing) {
setIsEditing(false);
setIsMenuOpen(false)
setIsMenuOpen(false);
}
}
};

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsMenuOpen(false);
}
Expand All @@ -68,68 +76,69 @@ const ViolationCard = ({ violation }: { violation: Violation }) => {
<h1 className="text-white font-syke-light lg:text-lg md:text-md text-sm">
Violation
</h1>
{isEditing ? (
<input
{isEditing ? (
<input
type="text"
name="violation_type"
value={editedViolation.violation_type}
onChange={handleInputChange}
className="text-input"
/>
) : (
<h1 className="text-textgreen font-syke-medium lg:text-xl md:text-lg text-md">
{violation.violation_type}
</h1>
)}
<h1 className="text-textgreen font-syke-medium lg:text-xl md:text-lg text-md">
{violation.violation_type}
</h1>
)}
</div>

<div className="flex-1">
<h1 className="text-white font-syke-light lg:text-lg md:text-md text-sm">
Date of Violation
</h1>
{isEditing ? (
<input
<input
type="date"
name="violation_date"
value={editedViolation.violation_date}
onChange={handleInputChange}
className="date-input"
/>
) : (
<h1 className="text-textgreen font-syke-medium lg:text-xl md:text-lg text-md">
{year}/{month}/{day}
</h1>
)}
<h1 className="text-textgreen font-syke-medium lg:text-xl md:text-lg text-md">
{year}/{month}/{day}
</h1>
)}
</div>
</div>

<div className="flex space-x-4">
<div className="flex-1">
<h1 className="text-white font-syke">
Description
</h1>
<h1 className="text-white font-syke">Description</h1>
{isEditing ? (
<input
<input
type="text"
name="description"
value={editedViolation.description}
onChange={handleInputChange}
className="text-input"
/>
) : (
<h1 className="text-textgreen font-syke lg:text-lg md:text-md text-sm lg:w-lg md:w-md w-sm break-normal">
{violation.description}
</h1>
)}
<h1 className="text-textgreen font-syke lg:text-lg md:text-md text-sm lg:w-lg md:w-md w-sm break-normal">
{violation.description}
</h1>
)}
</div>

<div className="flex-1">
<h1 className="text-white font-syke-light lg:text-lg md:text-md text-sm">
Status
</h1>
<h1 className="text-textgreen font-syke-medium lg:text-xl md:text-lg text-md">
<span
onClick={handleStatusClick}
className="text-textgreen hover:text-blue-200 font-syke-medium lg:text-xl md:text-lg text-md"
>
{violation.paid_status ? "Paid" : "Unpaid"}
</h1>
</span>
</div>
</div>
</div>
Expand Down
42 changes: 42 additions & 0 deletions frontend/src/hooks/useUpdateStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { fetchWithAuth } from "../utils/fetch";
import useFetchWithAuthExports from "./context-hooks/useFetchWithAuthExports";
import { BackendMessage } from "../types/response.types";
import { toast } from "react-toastify";
import { LoadingContextType } from "../types/loading.types";
import useLoading from "./context-hooks/useLoading";

const useUpdateStatus = () => {
const { navigate, refresh, auth } = useFetchWithAuthExports();
const { setAppLoading }: LoadingContextType = useLoading();

const handleUpdateStatus = async (id: string) => {
setAppLoading!(true);
try {
const response = await fetchWithAuth(
navigate,
refresh,
auth,
"/violation/status-change",
"patch",
{ id },
);

if (!response.ok) {
const backendError: BackendMessage = await response.json();
toast.error(backendError.message);
return;
}

const backendNotification: BackendMessage = await response.json();
toast.success(backendNotification.message);
} catch (error) {
const errorMessage = (error as Error).message;
toast.error(errorMessage);
} finally {
setAppLoading!(false);
}
};
return { handleUpdateStatus };
};

export default useUpdateStatus;
4 changes: 2 additions & 2 deletions frontend/src/pages/AboutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Header from "../components/Header";

const AboutPage = () => {
return (
<div className="flex flex-col items-center bg-policies-bg bg-cover bg-no-repeat sm:bg-top md:bg-right lg:bg-left min-h-screen px-4">
<div className="flex flex-col items-center bg-policies-bg bg-cover bg-no-repeat min-h-screen w-screen h-screen">
<div className="w-full">
<Header />
</div>
<div className="w-full max-w-4xl h-auto max-h-[70vh] bg-gray-400 overflow-y-auto rounded-md mt-6 p-4 scrollbar bg-clip-padding backdrop-filter backdrop-blur-sm bg-opacity-10">
<div className="w-7/8 h-auto max-h-[70vh] bg-gray-400 overflow-y-auto rounded-md mt-6 p-4 scrollbar bg-clip-padding backdrop-filter backdrop-blur-sm bg-opacity-10">
<div className="text-left py-2 px-6 rounded-xl bg-clip-padding ">
<h1 className="text-textgreen font-syke-medium text-xl sm:text-2xl mt-4">About Us</h1>

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/AdminHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const AdminLandingPage = () => {
Encode
</button>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 font-syke-medium lg:text-3xl md:text-2xl text-xl gap-5 w-full max-w-4xl">
<div className="grid grid-cols-1 lg:grid-cols-3 font-syke-medium lg:text-3xl md:text-2xl text-xl gap-5 w-full max-w-4xl">
<button
className="transition-transform duration-300 hover:scale-105 text-white px-5 py-4 rounded-md bg-buttongreen active:bg-colorhover font-syke-medium"
onClick={handleAddDriverButton}
Expand Down
Loading