diff --git a/client/app/auth/change-password/page.tsx b/client/app/auth/change-password/page.tsx index d42fea2..4138d97 100644 --- a/client/app/auth/change-password/page.tsx +++ b/client/app/auth/change-password/page.tsx @@ -16,14 +16,14 @@ import { setCookie } from "@/lib/cookieFunctions"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; +import ReCAPTCHA from "react-google-recaptcha"; export default function ChangePassword() { const router = useRouter(); const { changePass, setPassChangeData } = useChangePass(); - const [correctCaptcha, setCorrectCaptcha] = useState(""); - const [userCaptcha, setUserCaptcha] = useState(""); + const [isVerified, setIsCaptchaVerified] = useState(); const [oldPassword, setOldPassword] = useState(""); const [newPassword, setNewPassword] = useState(""); @@ -33,8 +33,8 @@ export default function ChangePassword() { let timer = setInterval(() => { setTime((time) => { if (time === 0) { - clearInterval(timer); - alert("Time Expired!"); + clearInterval(timer); + alert("Time Expired!"); router.push("/dashboard"); return 0; } else return time - 0.5; @@ -44,15 +44,15 @@ export default function ChangePassword() { async function submitForm() { setPassChangeData({ oldPassword, newPassword }); - correctCaptcha === userCaptcha && - (await changePass()) && - router.push("/dashboard"); - correctCaptcha !== userCaptcha && alert("Invalid Captcha!"); + isVerified && (await changePass()) && router.push("/dashboard"); + !isVerified && alert("Invalid Captcha!"); } return (
- + Change Password @@ -62,7 +62,7 @@ export default function ChangePassword() { {`${time % 60}`.padStart(2, "0")} mins. - +
@@ -84,10 +84,12 @@ export default function ChangePassword() { required />
- +
+ +
diff --git a/client/app/auth/reset-password/initiate/forgetPasswordInitiate.tsx b/client/app/auth/reset-password/initiate/forgetPasswordInitiate.tsx index a9266a7..bcf9fff 100644 --- a/client/app/auth/reset-password/initiate/forgetPasswordInitiate.tsx +++ b/client/app/auth/reset-password/initiate/forgetPasswordInitiate.tsx @@ -7,14 +7,20 @@ import { Label } from "@/components/ui/label"; import { useRouter } from "next/navigation"; import useForgetPassInitiate from "@/hooks/auth/useForgetPassInitiate"; -import { useContext } from "react"; +import { useContext, useState } from "react"; +import ReCAPTCHA from "react-google-recaptcha"; function ForgetPassInitiateForm() { const { userEmail, setUserEmail, initiate } = useForgetPassInitiate(); + const [isCaptchaVerified, setIsCaptchaVerified] = useState(); const router = useRouter(); async function handleFormSubmit(e: React.FormEvent) { e.preventDefault(); + if (!isCaptchaVerified) { + alert("Please verify that you are not a robot."); + return; + } const success = await initiate(); success && router.push("/auth/reset-password/confirm" + "?email=" + userEmail); } @@ -34,7 +40,8 @@ function ForgetPassInitiateForm() { } required /> -
+ + diff --git a/client/app/billPrint/page.tsx b/client/app/billPrint/page.tsx new file mode 100644 index 0000000..70ceeea --- /dev/null +++ b/client/app/billPrint/page.tsx @@ -0,0 +1,138 @@ +"use client"; +import { useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import Head from "next/head"; +import { Bill } from "@/components/dataTables/CompletedBillList"; +import { Button } from "@/components/ui/button"; + +const BillPrintComponent = () => { + const searchParams = useSearchParams(); + const [queryParams, setQueryParams] = useState({ + id: "", + billNo: "", + stsName: "", + landFillName: "", + vehicleNumber: "", + vehicleType: "", + weightOfWaste: 0, + shortage: 0, + loadedFuelCostPerKm: 0, + unloadedFuelCostPerKm: 0, + capacity: 0, + estimatedFuelCost: 0, + distance: 0, + estimatedDuration: 0, + actualDuration: 0, + allocatedFuelCost: 0, + tripId: "", + }); + + useEffect(() => { + setQueryParams({ + id: searchParams.get("id") || "", + billNo: searchParams.get("billNo") || "", + stsName: searchParams.get("stsName") || "", + landFillName: searchParams.get("landFillName") || "", + vehicleNumber: searchParams.get("vehicleNumber") || "", + vehicleType: searchParams.get("vehicleType") || "", + weightOfWaste: Number(searchParams.get("weightOfWaste")) || 0, + shortage: Number(searchParams.get("shortage")) || 0, + loadedFuelCostPerKm: Number(searchParams.get("loadedFuelCostPerKm")) || 0, + unloadedFuelCostPerKm: + Number(searchParams.get("unloadedFuelCostPerKm")) || 0, + capacity: Number(searchParams.get("capacity")) || 0, + estimatedFuelCost: Number(searchParams.get("estimatedFuelCost")) || 0, + distance: Number(searchParams.get("distance")) || 0, + estimatedDuration: Number(searchParams.get("estimatedDuration")) || 0, + actualDuration: Number(searchParams.get("actualDuration")) || 0, + allocatedFuelCost: Number(searchParams.get("allocatedFuelCost")) || 0, + tripId: searchParams.get("tripId") || "", + }); + }, [searchParams]); + +return ( + <> +
+
+
+ + Bill number:  {queryParams.billNo} + + logo +
+
+
+ STS:
{queryParams.stsName}{" "} +
+
+ Landfill:
{queryParams.landFillName} +
+
+
+
+ Vehicle Number:
{queryParams.vehicleNumber}{" "} +
+
+ Vehicle Type:
{queryParams.vehicleType} +
+
+
+
+ Capacity:
{queryParams.capacity} Ton{" "} +
+
+ + Weight Trasfered:
{queryParams.weightOfWaste} Ton{" "} +
+
+
+ Shortage:
{queryParams.shortage} Ton +
+
+
+
+ Loaded Fuel Cost:
{queryParams.loadedFuelCostPerKm}{" "} + BDT +
+
+ Unloaded Fuel Cost:
{" "} + {queryParams.unloadedFuelCostPerKm} BDT +
+
+
+
+ Actual Duration:
{queryParams.estimatedDuration} min +
+
+ Estimated Duration:
{queryParams.actualDuration} min +
+
+
+
+ Total Distance:
{queryParams.distance} km +
+
+ Estimated Fuel Cost:
{queryParams.estimatedFuelCost}{" "} + BDT +
+
+
+ + ALLOCATED BILL:  {queryParams.allocatedFuelCost} BDT + +
+
+
+
+ +
+ +); +}; + +export default BillPrintComponent; diff --git a/client/app/dashboard/page.tsx b/client/app/dashboard/page.tsx index 4fe6014..570e738 100644 --- a/client/app/dashboard/page.tsx +++ b/client/app/dashboard/page.tsx @@ -21,7 +21,7 @@ export default function Dashboard() { setCurrentActive(getCookie(curActive)); }, []); - console.log('Role: ' + curRole + ' Current Active: ' + currentActive); + //console.log('Role: ' + curRole + ' Current Active: ' + currentActive); return (
diff --git a/client/app/globals.css b/client/app/globals.css index a30a014..b4d0a3e 100644 --- a/client/app/globals.css +++ b/client/app/globals.css @@ -98,3 +98,21 @@ background: #0F172A; /* Color of the scrollbar handle on hover */ border: 2px solid white; /* Color of the scrollbar handle on hover */ } + +nextjs-portal { + display: none; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.loader { + border-top-color: #3498db; + animation: spin 1s linear infinite; +} \ No newline at end of file diff --git a/client/app/layout.tsx b/client/app/layout.tsx index 6232bd7..b28bdce 100644 --- a/client/app/layout.tsx +++ b/client/app/layout.tsx @@ -1,6 +1,10 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import '@mantine/core/styles.css'; +import '@mantine/charts/styles.css'; + +import { ColorSchemeScript, MantineProvider } from '@mantine/core'; const inter = Inter({ subsets: ["latin"] }); @@ -19,7 +23,10 @@ export default function RootLayout({ }>) { return ( - {children} + + + + {children} ); } diff --git a/client/app/loading.tsx b/client/app/loading.tsx new file mode 100644 index 0000000..337a603 --- /dev/null +++ b/client/app/loading.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +const LoadingPage = () => { + return ( +
+
+ logo +
+
+

EcoSync is loading...

+
+
+ ); +}; + +export default LoadingPage; \ No newline at end of file diff --git a/client/app/maps/page.tsx b/client/app/maps/page.tsx index 7ee0998..a6217d3 100644 --- a/client/app/maps/page.tsx +++ b/client/app/maps/page.tsx @@ -1,11 +1,20 @@ "use client"; +import { AllStsMapShow } from '@/components/maps/AllStsShow'; import GoogleMapComponent from '@/components/maps/GoogleMap' import RouteMap from '@/components/maps/RouteMap'; import SetZone from '@/components/maps/SetZone'; import { ChakraProvider, theme } from '@chakra-ui/react' +type StsShow = { + + lat: number, + lng: number, + // storagePercentage: number + +}; + const Map = () => { - const coordinates = [ + const coordinates : StsShow[] = [ { lat: 23.7031879, lng: 90.35564201 }, { lat: 23.6856870, lng: 90.44630134 }, { lat: 23.6843407, lng: 90.56538359 }, @@ -20,9 +29,10 @@ const Map = () => {
{/* */} {/* */} - + {/* - + */} +
) diff --git a/client/app/profile/[userId]/page.tsx b/client/app/profile/[userId]/page.tsx index b4dd4f2..40ba8dd 100644 --- a/client/app/profile/[userId]/page.tsx +++ b/client/app/profile/[userId]/page.tsx @@ -1,26 +1,62 @@ "use client"; +import BackgroundComponent from "@/components/profile/backgroundComp"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import useGetUserProfile from "@/hooks/user_data/useGetUserProfile"; import { PersonIcon } from "@radix-ui/react-icons"; import { useParams } from "next/navigation"; import { useRouter } from "next/navigation"; +import { EditIcon, Factory, CircleUser } from "lucide-react"; +import {useEffect, useState} from "react"; +import { ProfileEditModal } from "@/components/modals/ProfileEditModal"; +import useGetAllRole from "@/hooks/user_data/useGetAllRole"; + +type RolesWithPermisson = { + id: string; + name: string; + permissions: [{ + name: string; + description: string; +}] +}; + export default function ProfilePage() { - const userId = useParams().userId.toString(); + const router = useRouter(); - const { userData } = useGetUserProfile(userId); + const { user, stsDetails, landfillDetails, getUserDetails} = useGetUserProfile(); // Destructure user and getUserDetails + const [role, setRole] = useState("Role Name"); + // const [permissions, setPermissions] = useState([]); + const RolePlace = 'Station'; + const { fetchAllRoles, roles, rolesWithPermissions } = useGetAllRole(); + useEffect(() => { + const fetchData = async () => { + await getUserDetails(); + await fetchAllRoles(); + + setRole(user.roleName); + }; + + fetchData(); + + }, []); + return ( -
+
+ -
+
+ {user.roleName} +
+
+
@@ -28,12 +64,113 @@ export default function ProfilePage() {

Profile Page

-

ID: {userId}

-

Email: {userData.email}

-

Role: {userData.role}

-

Name: {userData.name}

-

Assigned Area: {userData.assignedArea}

+

ID:

+

Email: {user.email}

+

Role: {user.roleName}

+

Profile Name: {user.profileName}

+

Username: {user.username}

+

Role Description: {user.roleDescription}

+ + +
+ + +
+ + + + {user?.roleName === 'STS_MANAGER' && stsDetails?.stsId?.toString().length > 1 &&
+
STS Details
+

Id: {stsDetails.stsId}

+

STS Name: {stsDetails.stsName}

+

Ward Number: {stsDetails.stsWardNumber}

+

Capacity: {stsDetails.stsCapacity}

+

Current Total Waste: {stsDetails.stsCurrentTotalWaste}

+

Coordinate: {stsDetails.stsLatitude}, {stsDetails.stsLongitude}

+ + +
+ + } + +{user?.roleName === 'STS_MANAGER' && stsDetails?.stsId?.toString().length < 1 &&
+
STS Not Assigned
+ Call your admin to assign your STS. +
+ } +{user?.roleName === 'STS_MANAGER' && rolesWithPermissions.some(role => role.name === 'STS_MANAGER') && ( +
+

STS Manager Permissions:

+
    + {rolesWithPermissions + .find(role => role.name === 'STS_MANAGER') + ?.permissions.map(permission => ( +
  • + {permission.name}: {permission.description} +
  • + ))} +
+
+)} + + + {user?.roleName === 'LAND_MANAGER' && landfillDetails?.landfillId?.toString().length > 1 &&
+
Landfill Details
+

ID: {landfillDetails.landfillId}

+

Landfill Name: {landfillDetails.landFillName}

+

Capacity: {landfillDetails.landFillCapacity}

+

Current Total Waste: {landfillDetails.landFillCurrentWaste}

+

Coordinate: {landfillDetails.landfillLatitude}, {landfillDetails.landFillLongitude}

+
} + + {user?.roleName === 'LAND_MANAGER' && landfillDetails?.landfillId?.toString().length < 1 && ( +
+
LandFill Not Assigned
+ Call your admin to assign your Landfill. +
+ )} + +{user?.roleName === 'LAND_MANAGER' && rolesWithPermissions.some(role => role.name === 'LAND_MANAGER') && ( +
+

Land Manager Permissions:

+
    + {rolesWithPermissions + .find(role => role.name === 'LAND_MANAGER') + ?.permissions.map(permission => ( +
  • + {permission.name}: {permission.description} +
  • + ))} +
+
+)} + + {user?.roleName === 'SYSTEM_ADMIN' &&
+
Admin
+
You are admin
+
} + {user?.roleName !== 'STS_MANAGER' && user?.roleName !== 'LAND_MANAGER' && user?.roleName !== 'SYSTEM_ADMIN' && ( +
Oops! Your role has not assigned yet
+ )} + +{user?.roleName === 'SYSTEM_ADMIN' && rolesWithPermissions.some(role => role.name === 'SYSTEM_ADMIN') && ( +
+

System Admin Permissions:

+
    + {rolesWithPermissions + .find(role => role.name === 'SYSTEM_ADMIN') + ?.permissions.map(permission => ( +
  • + {permission.name}: {permission.description} +
  • + ))} +
+
+)} + + +
); diff --git a/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx b/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx index bab6f88..87137ee 100644 --- a/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx +++ b/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx @@ -1,6 +1,6 @@ -export default function EmptyFillContainer({children}: {children: React.ReactNode}) { +export default function EmptyFillContainer({children, className = ""}: {children: React.ReactNode, className?: string}) { return ( -
{children}
+
{children}
); } \ No newline at end of file diff --git a/client/components/dashboard-componenets/mainContent/landFillManagerContents/Dashboard.tsx b/client/components/dashboard-componenets/mainContent/landFillManagerContents/Dashboard.tsx index 8da5c9b..1c9ba51 100644 --- a/client/components/dashboard-componenets/mainContent/landFillManagerContents/Dashboard.tsx +++ b/client/components/dashboard-componenets/mainContent/landFillManagerContents/Dashboard.tsx @@ -1,46 +1,62 @@ +"use client" import { Button } from "@/components/ui/button"; import EmptyFillContainer from "../../cards/EmptyFillContainer"; -import { Cog, UserRoundCog } from "lucide-react"; +import { Cog, Download, Send, Upload, UserRoundCog } from "lucide-react"; +import LanfFillUpcomingVehiclesInDashboard from "../../../dataTables/LandFillUpcomingVehicleInDashboard"; +import PendingBillList from "@/components/dataTables/PendingBillList"; +import { landfillId } from "@/data/cookieNames"; +import useGetLandfillDatabyID from "@/hooks/landFillDashboard/getLandFillDataById"; +import { getCookie } from "@/lib/cookieFunctions"; +import { useEffect } from "react"; +import { AreaChart, PieChart } from "@mantine/charts"; export default function LandfillManagerDashboard() { + + const { getLandfillDatabyID, landfillData } = useGetLandfillDatabyID(); + + + useEffect(() => { + getLandfillDatabyID(getCookie(landfillId)) + }, []); + + useEffect(() => { + // alert(JSON.stringify(landfillData)) + }, [landfillData]); + return ( -
-
-

- Landfill Dashboard -

-
-
- - -
-
+
-
-
- Storage Status -
-
- Upcoming Gari -
-
- Dumping History -
-
- Dump graph -
-
- MAP +
+
+ +
+ {" "} +
STORAGE STATUS OF
OF {(landfillData?.name)?.toUpperCase()}
+
+ {" "} + {landfillData?.graphData?.emptyPercentage}% EMPTY +
+ {landfillData?.graphData?.fullPercentage}% FULL +
+
+ {(landfillData?.graphData?.empty !== undefined && landfillData?.graphData?.full !== undefined) ? : "loading..."} +
+
+
+
-
- Storage Graph +
+
+
diff --git a/client/components/dashboard-componenets/mainContent/landFillManagerContents/DumpEntry.tsx b/client/components/dashboard-componenets/mainContent/landFillManagerContents/DumpEntry.tsx index 0fb2970..4129fb1 100644 --- a/client/components/dashboard-componenets/mainContent/landFillManagerContents/DumpEntry.tsx +++ b/client/components/dashboard-componenets/mainContent/landFillManagerContents/DumpEntry.tsx @@ -1,111 +1,42 @@ +"use client"; +import GoogleMapComponent from "@/components/maps/GoogleMap"; +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import LanfFillUpcomingVehicles from "../../../dataTables/LandFillUpcomingVehicle"; +import LandFillDeliveredVehicles from "../../../dataTables/LandFillDeliveredVehicles"; +import { AllStsMapShow } from "@/components/maps/AllStsShow"; import { Button } from "@/components/ui/button"; -import { - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, - CardFooter, -} from "@/components/ui/card"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; +import { Cog, LogOut } from "lucide-react"; + export default function LandfillManagerDumpEntries() { + return ( +
-
-

SETTINGS

+
+

DUMP ENTRIES

+
+
+ +
-
-
- {/*

ADD NEW DUMP ENTRY

*/} -
-

ADD STORAGE ENTRIES

- - - Create project - - Deploy your new project in one-click. - - - -
-
-
- - -
-
- - -
-
-
-
- - - - -
-
-
-

ADD DUMP ENTRIES

- - - Create project - - Deploy your new project in one-click. - - - -
-
-
- - -
-
- - -
-
-
-
- - - - -
+
+
+
+ + +
+
+ + + +
- ); + ); } diff --git a/client/components/dashboard-componenets/mainContent/landFillManagerContents/LandfillManagerBills.tsx b/client/components/dashboard-componenets/mainContent/landFillManagerContents/LandfillManagerBills.tsx new file mode 100644 index 0000000..79c255b --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/landFillManagerContents/LandfillManagerBills.tsx @@ -0,0 +1,32 @@ +import { Button } from "@/components/ui/button"; +import { UserRoundCog, Cog } from "lucide-react"; +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import PendingBillList from "@/components/dataTables/PendingBillList"; +import CompletedBillList from "@/components/dataTables/CompletedBillList"; + +export default function LandfillManagerBillsManagementPanel() { + return ( +
+
+

Bills

+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+
+ ); + } \ No newline at end of file diff --git a/client/components/dashboard-componenets/mainContent/landFillManagerContents/Schedules.tsx b/client/components/dashboard-componenets/mainContent/landFillManagerContents/Schedules.tsx index a37a202..429ae81 100644 --- a/client/components/dashboard-componenets/mainContent/landFillManagerContents/Schedules.tsx +++ b/client/components/dashboard-componenets/mainContent/landFillManagerContents/Schedules.tsx @@ -1,18 +1,35 @@ +"use client"; +import GoogleMapComponent from "@/components/maps/GoogleMap"; import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import LanfFillUpcomingVehicles from "../../../dataTables/LandFillUpcomingVehicle"; +import LandFillDeliveredVehicles from "../../../dataTables/LandFillDeliveredVehicles"; +import { AllStsMapShow } from "@/components/maps/AllStsShow"; export default function LandfillManagerSchedules() { + // const coordinates = [ + // { lat: 23.7031879, lng: 90.35564201 }, + // { lat: 23.6856870, lng: 90.44630134 }, + // { lat: 23.6843407, lng: 90.56538359 }, + // { lat: 23.7588160, lng: 90.52911986 }, + // { lat: 23.7592645, lng: 90.42032866 }, + // { lat: 23.7615071, lng: 90.38945549 }, + // { lat: 23.7888633, lng: 90.36152261 } + // ]; + + // const dumpFills = [30, 25, 81, 45, 70, 50, 90, 60]; return (
-
- UPCOMING - CRITICAL FIRST SCHEDULES -
-
- DISPATCH HISTORY +
+ + +
-
- MAP TRACKING +
+ + +
diff --git a/client/components/dashboard-componenets/mainContent/landFillManagerContents/Storage.tsx b/client/components/dashboard-componenets/mainContent/landFillManagerContents/Storage.tsx index 3cd08cc..71ff7e3 100644 --- a/client/components/dashboard-componenets/mainContent/landFillManagerContents/Storage.tsx +++ b/client/components/dashboard-componenets/mainContent/landFillManagerContents/Storage.tsx @@ -1,40 +1,154 @@ +"use client"; import { Button } from "@/components/ui/button"; -import { Cog, UserRoundCog } from "lucide-react"; +import "@mantine/charts/styles.css"; import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import { AllStsMapShow } from "@/components/maps/AllStsShow"; +import useGetAllSTS from "@/hooks/stsdata/useGetAllSTS"; +import { use, useEffect, useState } from "react"; +import GoogleMapComponent from "@/components/maps/GoogleMap"; +// import PieChart from "@/components/graphs/PieChart"; +import BarChart from "@/components/graphs/BarChart"; +import { AreaChart, PieChart } from "@mantine/charts"; +import { landfillId } from "@/data/cookieNames"; +import useGetLandfillDatabyID from "@/hooks/landFillDashboard/getLandFillDataById"; +import { getCookie } from "@/lib/cookieFunctions"; export default function LandfillManagerStorageData() { + const { getAllSTS, stsCoordinate, storagePercentage } = useGetAllSTS(); + const { getLandfillDatabyID, landfillData } = useGetLandfillDatabyID(); + + + useEffect(() => { + getAllSTS(); + getLandfillDatabyID(getCookie(landfillId)) + }, []); + + useEffect(() => { + // alert(JSON.stringify(landfillData)) + }, [landfillData]); + return (
-
-

Storage

-
-
- - -
-
-
-
- Dumping history -
-
- Storage history +
+
+ + +
-
- Dumping graph +
+ +
+ {" "} +
STORAGE STATUS OF
+
OF {(landfillData?.name)?.toUpperCase()} {" "}
+
+ {" "} + {landfillData?.graphData?.emptyPercentage}% EMPTY +
+
+ {" "} + {landfillData?.graphData?.fullPercentage}% FULL{" "} +
+
+ {(landfillData?.graphData?.empty !== undefined && landfillData?.graphData?.full !== undefined) ? : "loading..."} +
-
- storage status pie +
+ + TOTAL WEIGHT HISTOTY + +
-
- Storage graph +
+ + WEEKLY INCOMING WASTE HISTORY + +
diff --git a/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx b/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx index 0acc05a..bfabbba 100644 --- a/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx +++ b/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx @@ -1,4 +1,5 @@ -import Link from "next/link"; +"use client"; + import { CircleUser, CircleUserRound, @@ -36,8 +37,18 @@ import { useRouter } from "next/navigation"; import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; import { useContext } from "react"; import { NavContext } from "@/hooks/contexts/useNavCtx"; -import { eraseCookie } from "@/lib/cookieFunctions"; -import { jwtToken, role } from "@/data/cookieNames"; +import { eraseCookie, getCookie } from "@/lib/cookieFunctions"; +import { + curActive, + jwtToken, + landfillName, + role, + stsId, + stsName, + username, +} from "@/data/cookieNames"; +import { get } from "http"; +import { landfillManager, stsManager } from "@/data/roles"; function logout(router: AppRouterInstance) { eraseCookie(role); @@ -55,7 +66,7 @@ export default function MainSectionHeader({ const router = useRouter(); const { currentActive, setCurrentActive } = useContext(NavContext); return ( -
+
{/* Sheet for displaying menu in small screens */} @@ -72,20 +83,38 @@ export default function MainSectionHeader({ {/* Main content of header */} -
-
+
+
+
+ {getCookie(curActive)?.startsWith(stsManager) && + (getCookie(stsName) ? ( + YOUR STS : {getCookie(stsName).toUpperCase()} + ) : ( + <>{"NO STS ASSIGNED"} + ))} + + {getCookie(curActive)?.startsWith(landfillManager) && + (getCookie(landfillName) ? ( + YOUR LANDFILL : {getCookie(landfillName).toUpperCase()} + ) : ( + <>{"NO LANDFILL ASSIGNED"} + ))} +
+ {/* Profile Icon and dropdown menu */} +
+ {getCookie(username)}
); } diff --git a/client/components/dashboard-componenets/mainContent/stsManagerContents/Schedules.tsx b/client/components/dashboard-componenets/mainContent/stsManagerContents/Schedules.tsx index 0c02f1f..c35b12a 100644 --- a/client/components/dashboard-componenets/mainContent/stsManagerContents/Schedules.tsx +++ b/client/components/dashboard-componenets/mainContent/stsManagerContents/Schedules.tsx @@ -3,6 +3,7 @@ import { ChakraProvider, theme } from "@chakra-ui/react"; import OptimizedRouteMap from "@/components/maps/OptimizedRoute"; import RouteMap from "@/components/maps/RouteMap"; import STSVehicleList from "@/components/dataTables/StsVehicleList"; +import GetStsCoordinateForRoute from "@/components/maps/getStsCoordinateForRoute"; export default function STSManagerSchedules() { return ( @@ -20,9 +21,7 @@ export default function STSManagerSchedules() {
*/}
- - - +
diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx index 8471bc4..7190efb 100644 --- a/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx +++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx @@ -1,7 +1,9 @@ import { Button } from "@/components/ui/button"; import { Cog, UserRoundCog } from "lucide-react"; import EmptyFillContainer from "../../cards/EmptyFillContainer"; -import { RoleCreateModal } from "@/components/modals/RoleModal"; +import { RoleCreateModal } from "@/components/modals/userControls/RoleModal"; +import PermissonList from "@/components/lists/PermissonList"; +import { RoleDeleteModal } from "@/components/modals/userControls/RoleDeleteModal"; export default function AdminRolesManagementPanel() { return ( @@ -11,32 +13,30 @@ export default function AdminRolesManagementPanel() {
- + + + +
-
-
-
- Roles List -
-
- Permisson List -
-
- Role Distribution chart -
-
- Role Requests -
-
+
+ +

+ PERMISSON LIST BY ROLE +

+ +
); diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/System.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/System.tsx index a2888b0..2ef7807 100644 --- a/client/components/dashboard-componenets/mainContent/systemAdminContents/System.tsx +++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/System.tsx @@ -1,11 +1,13 @@ import { Button } from "@/components/ui/button"; import EmptyFillContainer from "../../cards/EmptyFillContainer"; import { Plus, Truck } from "lucide-react"; -import { StsCreateModal } from "../../../modals/StsModal"; +import { StsCreateModal } from "../../../modals/stsControl/StsModal"; import { VehicleCreateModal } from "@/components/modals/VehicleModal"; -import { LandfillCreateModal } from "@/components/modals/LandfillModal"; +import { LandfillCreateModal } from "@/components/modals/landfillControl/LandfillModal"; import UserListTable from "@/components/dataTables/UserList"; import STSListTable from "@/components/dataTables/STSList"; +import LandFillListTable from "@/components/dataTables/LandFillList"; +import AllVehicleList from "@/components/dataTables/AllVehicleList"; export default function AdminSystemDataPanel() { return ( @@ -47,16 +49,16 @@ export default function AdminSystemDataPanel() {
-
-
+
+
-
- VEICLE LIST -
-
- LANDFILL LIST -
+
+ +
+
+ +
diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/Users.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/Users.tsx index 3e68718..ea3bf20 100644 --- a/client/components/dashboard-componenets/mainContent/systemAdminContents/Users.tsx +++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/Users.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import useAddNewUser from "@/hooks/user_data/useAddNewUser"; -import { AddNewUserModal } from "@/components/modals/AddNewUserModal"; +import { AddNewUserModal } from "@/components/modals/userControls/AddNewUserModal"; import UserListTable from "@/components/dataTables/UserList"; export default function AdminUserManagementPanel() { diff --git a/client/components/dataTables/AllVehicleList.tsx b/client/components/dataTables/AllVehicleList.tsx new file mode 100644 index 0000000..9b75234 --- /dev/null +++ b/client/components/dataTables/AllVehicleList.tsx @@ -0,0 +1,361 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllUser from "@/hooks/user_data/useGetAllUser"; +import { DeleteUserModal } from "../modals/userControls/DeleteUserModal"; +import { Copy, EditIcon } from "lucide-react"; +import { EditUserModal } from "../modals/userControls/EditUserInfoModal"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { roleList } from "@/data/roles"; +import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; +import { EditSTSInfoModal } from "../modals/stsControl/EditSTSInfoModal"; +import { DeleteSTSModal } from "../modals/stsControl/DeleteSTSModal"; +import useVehicleList from "@/hooks/vehicles/useVehiclesData"; +import useVehicleListForSTS from "@/hooks/vehicles/useGetVeicleForSTS"; +import { DeleteVehicleModalForSTS } from "../modals/DeleteVehicleModalForSTS"; +import useGetAllVehicleList from "@/hooks/vehicles/useGetAllVehicleList"; +import { DeleteVehicleModal } from "../modals/vehicleControl/DeleteVehicleModal"; +import { EditVehicleInfoModal } from "../modals/vehicleControl/EditVehicleInfoModal"; + +type Vehicle = { + id: string, + vehicleNumber: string, + vehicleType: string, + capacity: string, + loadedFuelCostPerKm: string, + unloadedFuelCostPerKm: string, + landFillId: string, + landFillName: string, +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "vehicleNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("vehicleNumber")}
+ ), + }, + { + accessorKey: "vehicleType", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("vehicleType")} +
+ ), + }, + { + accessorKey: "capacity", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("capacity") + " Ton"}
+ ), + }, + { + accessorKey: "landFillName", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("landFillName")}
+ ), + }, + { + accessorKey: "loadedFuelCostPerKm", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("loadedFuelCostPerKm")}
+ ), + }, + { + accessorKey: "unloadedFuelCostPerKm", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("unloadedFuelCostPerKm")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const vehicle: Vehicle = row.original; + + return ( +
+ + +
+ ); + }, + }, +]; + +export default function AllVehicleList() { + const [data, setData] = React.useState([]); + const { getVehicleList, vehicleList } = useGetAllVehicleList(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + getVehicleList(); + }, []); + + React.useEffect(() => { + setData(vehicleList); + }, [vehicleList]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); + return ( + <> +
MANAGE ALL VEHICLES
+
+ + table.getColumn("vehicleNumber")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/CompletedBillList.tsx b/client/components/dataTables/CompletedBillList.tsx new file mode 100644 index 0000000..b191589 --- /dev/null +++ b/client/components/dataTables/CompletedBillList.tsx @@ -0,0 +1,387 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllVehicleList from "@/hooks/vehicles/useGetAllVehicleList"; +import { DeleteVehicleModal } from "../modals/vehicleControl/DeleteVehicleModal"; +import { EditVehicleInfoModal } from "../modals/vehicleControl/EditVehicleInfoModal"; +import useGetAllPendingBillList from "@/hooks/bills/useGetAllPendingBillList"; +import { Package, PackageCheck, PackageX } from "lucide-react"; +import { getCookie } from "@/lib/cookieFunctions"; +import { landfillId, landfillName } from "@/data/cookieNames"; +import { BillCreationModal } from "../modals/billControl/BillCreationModal"; +import useGetAllCompletedBillList from "@/hooks/bills/useGetAllCompletedBillList"; +import { BillViewModal } from "../modals/billControl/BillViewModal"; + +export type Bill = { + id: string; + billNo: string; + stsName: string; + landFillName: string; + vehicleNumber: string; + vehicleType: string; + weightOfWaste: number; + shortage: number; + loadedFuelCostPerKm: number; + unloadedFuelCostPerKm: number; + capacity: number; + estimatedFuelCost: number; + distance: number; + estimatedDuration: number; + actualDuration: number; + allocatedFuelCost: number; + tripId: string; +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "billNo", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("billNo")}
+ ), + }, + { + accessorKey: "vehicleNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("vehicleNumber")} +
+ ), + }, + { + accessorKey: "stsName", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("stsName")}
+ ), + }, + { + accessorKey: "weightOfWaste", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("weightOfWaste") + " Ton"} +
+ ), + }, + { + accessorKey: "allocatedFuelCost", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("allocatedFuelCost")} +
+ ), + }, + { + accessorKey: "actualDuration", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("actualDuration")} +
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const bill: Bill = row.original; + + return ( +
+ {/* + */} + +
+ ); + }, + }, +]; + +export default function PendingBillList() { + const [data, setData] = React.useState([]); + const { billList, getbillList } = useGetAllCompletedBillList(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + getbillList( + getCookie(landfillId) || "", + ); + }, []); + + React.useEffect(() => { + setData(billList); + }, [billList]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); + return ( + <> +
+ COMPLETED BILL LIST +
+
+ + table.getColumn("vehicleNumber")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + {billList.length > 0 ? ( +
+ + No bills include this vehicle number. +
+ ) : ( +
+ + No pending bills. +
+ )} +
+
+ )} +
+
+
+
+
+ Total {table.getFilteredRowModel().rows.length} row(s) rendered. +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/LandFillDeliveredVehicles.tsx b/client/components/dataTables/LandFillDeliveredVehicles.tsx new file mode 100644 index 0000000..5226258 --- /dev/null +++ b/client/components/dataTables/LandFillDeliveredVehicles.tsx @@ -0,0 +1,375 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllUser from "@/hooks/user_data/useGetAllUser"; +import { Copy, EditIcon } from "lucide-react"; + +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { roleList } from "@/data/roles"; +import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; + +import useVehicleList from "@/hooks/vehicles/useVehiclesData"; +import useVehicleListForSTS from "@/hooks/vehicles/useGetVeicleForSTS"; +import { DeleteVehicleModalForSTS } from "../modals/DeleteVehicleModalForSTS"; +import { STSVehicleRelease } from "../modals/STSVehicleReleaseModal"; +import useVehicleTripCompleteList from "@/hooks/landFillDashboard/useVehicleTripCompleteList"; +import { LandfillVehicleEntryModal } from "../modals/LandFillVehicleEntryModal"; +import { getCookie } from "@/lib/cookieFunctions"; +import { landfillId } from "@/data/cookieNames"; + +type Vehicle = { + tripId: string, + weightOfWaste: string, + shortage: string, + vehicleNumber: string, + stsName: string, + vehicleType: string, + distance: string, + actualDuration: string, + estimatedFuelCost: string, + tripStartTime: string, + tripEndTime: string, + estimatedDuration: string + tripStatus: string + capacity: string, + + + }; + + +export const columns: ColumnDef[] = [ + { + accessorKey: "vehicleNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("vehicleNumber")}
+ ), + }, + { + accessorKey: "vehicleType", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("vehicleType")} +
+ ), + }, + { + accessorKey: "distance", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("distance".toLocaleString())}
+ ), + }, + { + accessorKey: "actualDuration", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("actualDuration")}
+ ), + }, + { + accessorKey: "estimatedFuelCost", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("estimatedFuelCost")}
+ ), + }, + { + accessorKey: "weightOfWaste", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("weightOfWaste")}
+ ), + }, + + + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const sts: Vehicle = row.original; + + return ( +
+ {/* + */} + {/* */} +
+ ); + }, + }, +]; + +export default function LanfFillDeliveredVehicles() { + const [data, setData] = React.useState([]); + const { vehicleList, VehcileTripCompleteList } = useVehicleTripCompleteList(getCookie(landfillId)); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + VehcileTripCompleteList(); + }, []); + + React.useEffect(() => { + setData(vehicleList); + }, [vehicleList]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); + return ( + <> +
+ RECIEVED VEHICLES +
+
+ + table.getColumn("vehicleNumber")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/LandFillList.tsx b/client/components/dataTables/LandFillList.tsx new file mode 100644 index 0000000..72ce361 --- /dev/null +++ b/client/components/dataTables/LandFillList.tsx @@ -0,0 +1,277 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllUser from "@/hooks/user_data/useGetAllUser"; +import { DeleteUserModal } from "../modals/userControls/DeleteUserModal"; +import { Copy, EditIcon, Plus } from "lucide-react"; +import { EditUserModal } from "../modals/userControls/EditUserInfoModal"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { roleList } from "@/data/roles"; +import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; +import { EditSTSInfoModal } from "../modals/stsControl/EditSTSInfoModal"; +import { DeleteSTSModal } from "../modals/stsControl/DeleteSTSModal"; +import { ViewSTSInfoModal } from "../modals/stsControl/ViewSTSInfoModal"; +import { StsCreateModal } from "../modals/stsControl/StsModal"; +import useGetAllLandfill from "@/hooks/dataQuery/useGetAllLandfill"; +import { ViewLandFIllInfoModal } from "../modals/landfillControl/ViewLandfillInfoModal"; +import { EditLandfillInfoModal } from "../modals/landfillControl/EditLandfillInfoModal"; +import { DeleteLandfillModal } from "../modals/landfillControl/DeleteLandfillModal"; + +export type LandFill = { + id: string; + name: string; + capacity: string; + latitude: string; + longitude: string; + manager: string[]; +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("name")}
+ ), + }, + { + accessorKey: "capacity", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("capacity")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const landfill: LandFill = row.original; + + return ( +
+ + + +
+ ); + }, + }, +]; + +export default function LandFillListTable() { + const [data, setData] = React.useState([]); + const { fetchAllLandfills, landFillData } = useGetAllLandfill(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllLandfills(); + }, []); + + React.useEffect(() => { + setData(landFillData); + }, [landFillData]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); + return ( + <> +
MANAGE ALL LANDFILLS
+
+ + table.getColumn("name")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ Total {table.getFilteredRowModel().rows.length} row(s) loaded. +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/LandFillUpcomingVehicle.tsx b/client/components/dataTables/LandFillUpcomingVehicle.tsx new file mode 100644 index 0000000..625f856 --- /dev/null +++ b/client/components/dataTables/LandFillUpcomingVehicle.tsx @@ -0,0 +1,396 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllUser from "@/hooks/user_data/useGetAllUser"; +import { Copy, EditIcon } from "lucide-react"; + +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { roleList } from "@/data/roles"; +import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; + +import useVehicleList from "@/hooks/vehicles/useVehiclesData"; +import useVehicleListForSTS from "@/hooks/vehicles/useGetVeicleForSTS"; +import { DeleteVehicleModalForSTS } from "../modals/DeleteVehicleModalForSTS"; +import { STSVehicleRelease } from "../modals/STSVehicleReleaseModal"; +import useUpcomingVehicle from "@/hooks/landFillDashboard/useUpcomingVehiclesList"; +import { LandfillVehicleEntryModal } from "../modals/LandFillVehicleEntryModal"; +import { getCookie } from "@/lib/cookieFunctions"; +import { landfillId } from "@/data/cookieNames"; +import formatTimestamp from "@/lib/formatTimestamp"; + +type Vehicle = { + tripId: string, + weightOfWaste: string, + vehicleNumber: string, + stsId: string, + vehicleType: string, + distance: string, + tripStartTime: string, + estimatedDuration: string + tripStatus: string + capacity: string, + + }; + + +export const columns: ColumnDef[] = [ + { + accessorKey: "vehicleNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("vehicleNumber")}
+ ), + }, + { + accessorKey: "vehicleType", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("vehicleType")} +
+ ), + }, + { + accessorKey: "tripStartTime", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{formatTimestamp(row.getValue("tripStartTime".toLocaleString()))}
+ ), + }, + { + accessorKey: "estimatedDuration", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("estimatedDuration")}
+ ), + }, + { + accessorKey: "distance", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("distance")}
+ ), + }, + { + accessorKey: "weightOfWaste", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("weightOfWaste")}
+ ), + }, + { + accessorKey: "stsId", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("stsId")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const sts: Vehicle = row.original; + + return ( +
+ {/* + */} + +
+ ); + }, + }, +]; + +export default function LanfFillUpcomingVehicles() { + const [data, setData] = React.useState([]); + const { vehicleList, UpcomingVehicle } = useUpcomingVehicle(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + async function getAllUpcomingVehicles() { + await UpcomingVehicle(await getCookie(landfillId)); + } + + React.useEffect(() => { + getAllUpcomingVehicles(); + }, []); + + React.useEffect(() => { + setData(vehicleList); + }, [vehicleList]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + initialState: { + pagination: { + pageIndex: 0, + pageSize: 4, + }, + }, + }); + return ( + <> +
UPCOMING VEHICLES
+
+ + table.getColumn("vehicleNumber")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ Showing {data.length} entries +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/LandFillUpcomingVehicleInDashboard.tsx b/client/components/dataTables/LandFillUpcomingVehicleInDashboard.tsx new file mode 100644 index 0000000..c344050 --- /dev/null +++ b/client/components/dataTables/LandFillUpcomingVehicleInDashboard.tsx @@ -0,0 +1,334 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllUser from "@/hooks/user_data/useGetAllUser"; +import { Copy, EditIcon } from "lucide-react"; + +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { roleList } from "@/data/roles"; +import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; + +import useVehicleList from "@/hooks/vehicles/useVehiclesData"; +import useVehicleListForSTS from "@/hooks/vehicles/useGetVeicleForSTS"; +import { DeleteVehicleModalForSTS } from "../modals/DeleteVehicleModalForSTS"; +import { STSVehicleRelease } from "../modals/STSVehicleReleaseModal"; +import useUpcomingVehicle from "@/hooks/landFillDashboard/useUpcomingVehiclesList"; +import { LandfillVehicleEntryModal } from "../modals/LandFillVehicleEntryModal"; +import { getCookie } from "@/lib/cookieFunctions"; +import { landfillId, landfillName } from "@/data/cookieNames"; +import formatTimestamp from "@/lib/formatTimestamp"; + +type Vehicle = { + tripId: string, + weightOfWaste: string, + vehicleNumber: string, + stsId: string, + vehicleType: string, + distance: string, + tripStartTime: string, + estimatedDuration: string + tripStatus: string + capacity: string, + + }; + +export const columns: ColumnDef[] = [ + { + accessorKey: "vehicleNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("vehicleNumber")}
+ ), + }, + { + accessorKey: "stsId", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("stsId")}
+ ), + }, + { + accessorKey: "tripStartTime", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{formatTimestamp(row.getValue("tripStartTime".toLocaleString()))}
+ ), + }, + { + accessorKey: "weightOfWaste", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("weightOfWaste")}
+ ), + }, + + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const sts: Vehicle = row.original; + + return ( +
+ {/* + */} + +
+ ); + }, + }, +]; + +export default function LanfFillUpcomingVehiclesInDashboard() { + const [data, setData] = React.useState([]); + const { vehicleList, UpcomingVehicle } = useUpcomingVehicle(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + async function callUpcomingVehicle() { + await UpcomingVehicle(await getCookie(landfillId)); + } + + React.useEffect(() => { + callUpcomingVehicle(); + }, []); + + React.useEffect(() => { + setData(vehicleList); + }, [vehicleList]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + initialState: { + pagination: { + pageIndex: 0, + pageSize: 4, + }, + }, + }); + return ( + <> +
Upcoming trips to your {getCookie(landfillName)} landfill
+
+ + table.getColumn("vehicleNumber")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ Showing {data.length} entries +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/PendingBillList.tsx b/client/components/dataTables/PendingBillList.tsx new file mode 100644 index 0000000..6985a26 --- /dev/null +++ b/client/components/dataTables/PendingBillList.tsx @@ -0,0 +1,364 @@ +"use client"; + +import * as React from "react"; +import { + CaretSortIcon, + ChevronDownIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons"; + +import { Button } from "@/components/ui/button"; + +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import useGetAllVehicleList from "@/hooks/vehicles/useGetAllVehicleList"; +import { DeleteVehicleModal } from "../modals/vehicleControl/DeleteVehicleModal"; +import { EditVehicleInfoModal } from "../modals/vehicleControl/EditVehicleInfoModal"; +import useGetAllPendingBillList from "@/hooks/bills/useGetAllPendingBillList"; +import { Package, PackageCheck, PackageX } from "lucide-react"; +import { getCookie } from "@/lib/cookieFunctions"; +import { landfillId, landfillName } from "@/data/cookieNames"; +import { BillCreationModal } from "../modals/billControl/BillCreationModal"; + +export type Trip = { + id: string, + stsName: string, + landFillName: string, + vehicleNumber: string, + vehicleType: string, + weightOfWaste: number, + shortage: number, + loadedFuelCostPerKm: number, + unloadedFuelCostPerKm: number, + capacity: number, + estimatedFuelCost: number, + distance: number, + estimatedDuration: number, + actualDuration: number, +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "vehicleNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("vehicleNumber")}
+ ), + }, + { + accessorKey: "stsName", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("stsName")} +
+ ), + }, + { + accessorKey: "weightOfWaste", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("weightOfWaste") + " Ton"}
+ ), + }, + { + accessorKey: "estimatedFuelCost", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("estimatedFuelCost")}
+ ), + }, + { + accessorKey: "estimatedDuration", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("estimatedDuration")}
+ ), + }, + { + accessorKey: "actualDuration", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("actualDuration")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const trip: Trip = row.original; + + return ( +
+ {/* + */} + +
+ ); + }, + }, +]; + +export default function PendingBillList() { + const [data, setData] = React.useState([]); + const { getTripList, tripList } = useGetAllPendingBillList(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + getTripList(getCookie(landfillId) || ""); + }, []); + + React.useEffect(() => { + setData(tripList); + }, [tripList]); + + const table = useReactTable({ + data, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + }, + }); + return ( + <> +
PENDING BILL LIST
+
+ + table.getColumn("vehicleNumber")?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + {tripList.length > 0 ?
+ No bills include this vehicle number. +
:
+ No pending bills. +
} +
+
+ )} +
+
+
+
+
+ Total {table.getFilteredRowModel().rows.length} row(s) rendered. +
+
+ + +
+
+ + ); +} diff --git a/client/components/dataTables/STSList.tsx b/client/components/dataTables/STSList.tsx index e97343d..ad53c01 100644 --- a/client/components/dataTables/STSList.tsx +++ b/client/components/dataTables/STSList.tsx @@ -41,14 +41,16 @@ import { TableRow, } from "@/components/ui/table"; import useGetAllUser from "@/hooks/user_data/useGetAllUser"; -import { DeleteUserModal } from "../modals/DeleteUserModal"; -import { Copy, EditIcon } from "lucide-react"; -import { EditUserModal } from "../modals/EditUserInfoModal"; +import { DeleteUserModal } from "../modals/userControls/DeleteUserModal"; +import { Copy, EditIcon, Plus } from "lucide-react"; +import { EditUserModal } from "../modals/userControls/EditUserInfoModal"; import gettAllRoles from "@/hooks/user_data/useGetAllRole"; import { roleList } from "@/data/roles"; import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; -import { EditSTSInfoModal } from "../modals/EditSTSInfoModal"; -import { DeleteSTSModal } from "../modals/DeleteSTSModal"; +import { EditSTSInfoModal } from "../modals/stsControl/EditSTSInfoModal"; +import { DeleteSTSModal } from "../modals/stsControl/DeleteSTSModal"; +import { ViewSTSInfoModal } from "../modals/stsControl/ViewSTSInfoModal"; +import { StsCreateModal } from "../modals/stsControl/StsModal"; export type STS = { id: string; @@ -57,6 +59,7 @@ export type STS = { capacity: string; latitude: string; longitude: string; + manager: string[]; }; export const columns: ColumnDef[] = [ @@ -129,9 +132,10 @@ export const columns: ColumnDef[] = [ const sts: STS = row.original; return ( -
- +
+ +
); }, @@ -177,7 +181,8 @@ export default function STSListTable() { }); return ( <> -
+
MANAGE ALL STS
+
- + @@ -265,8 +270,7 @@ export default function STSListTable() {
- {table.getFilteredSelectedRowModel().rows.length} of{" "} - {table.getFilteredRowModel().rows.length} row(s) selected. + Total {table.getFilteredRowModel().rows.length} row(s) loaded.
+
+ + + + {coordinates.map((coordinate, index) => { + // let circleColor = '#00FF00'; // Default to green - // if (dumpFills[index] >= 40 && dumpFills[index] <= 80) { - // circleColor = '#FFFF00'; // Yellow - // } else if (dumpFills[index] > 80) { - // circleColor = '#FF0000'; // Red - // } - let circleColor = threshold(dumpFills[index]); + // if (dumpFills[index] >= 40 && dumpFills[index] <= 80) { + // circleColor = '#FFFF00'; // Yellow + // } else if (dumpFills[index] > 80) { + // circleColor = '#FF0000'; // Red + // } + let circleColor = threshold(dumpFills[index]); - - return ( - handleCircleHover(index)} - onMouseOut={handleCircleMouseOut} - options={{ - fillColor: circleColor, - fillOpacity: 0.35, - strokeColor: circleColor, - strokeOpacity: 0.8, - strokeWeight: 2, - }} - /> - ); - })} - {hoveredCircle !== null && ( - -
- Trash: {dumpFills[hoveredCircle]}% -
-
- )} -
-
+ return ( + handleCircleHover(index)} + onMouseOut={handleCircleMouseOut} + options={{ + fillColor: circleColor, + fillOpacity: 0.35, + strokeColor: circleColor, + strokeOpacity: 0.8, + strokeWeight: 2, + }} + /> + ); + })} + {hoveredCircle !== null && ( + +
Trash: {dumpFills[hoveredCircle]}%
+
+ )} + + + ); }; diff --git a/client/components/maps/OptimizedRoute.tsx b/client/components/maps/OptimizedRoute.tsx index b99dec4..6c25786 100644 --- a/client/components/maps/OptimizedRoute.tsx +++ b/client/components/maps/OptimizedRoute.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useRef, useState } from "react"; +import React, { useRef, useState, useEffect } from "react"; import { Box, Button, @@ -28,7 +28,16 @@ import { ChevronDownIcon } from "@chakra-ui/icons"; const center = { lat: 23.7244018, lng: 90.3887196 }; -function OptimizedRouteMap() { +type StsRouteType = { + coordinate: string; + name: string; +}; + +type MapProps = { + coordinates: StsRouteType[]; +}; + +const OptimizedRouteMap: React.FC = ({ coordinates }) => { const [routeType, setRouteType] = useState( "Location Based Optimal Route" ); @@ -40,6 +49,13 @@ function OptimizedRouteMap() { const [landTerm, setLandterm] = useState(""); const [showLandSuggestion, setShowLandsuggestion] = useState(false); + //coordinate data stuffs + const [allCoordinates, setAllCoordinates] = useState([]); + const [landFilCoord, setLandFillCoord] = useState({ + coordinate: "23.7244018, 90.3887196", + name: "Amin Bazar", + }); + const suggestionsList: string[] = [ "23.7751927, 90.3810282", "Dhanmondi STS", @@ -50,7 +66,14 @@ function OptimizedRouteMap() { "Rampura STS", ]; - const landfillList: string[] = ["23.7618195, 90.3833253","Amin Bazar", "Chashara"]; + useEffect(() => { + const coordinateArray: string[] = coordinates.map((route) => route.name); + setAllCoordinates(coordinateArray); + }, [coordinates]); + + const landfillList: string[] = ["Amin Bazar"]; + + //input studds const handleChangeInputType = () => { setUseDropdown((prev) => !prev); @@ -90,7 +113,7 @@ function OptimizedRouteMap() { setShowLandsuggestion(false); }; - const filteredSuggestions = suggestionsList.filter((suggestion) => + const filteredSuggestions = allCoordinates.filter((suggestion) => suggestion.toLowerCase().includes(searchTerm.toLowerCase()) ); @@ -119,17 +142,27 @@ function OptimizedRouteMap() { return ; } + const getSTSCoodrdinateByName = (stsName: string): string => { + const vehicle = coordinates.find((sts) => sts.name === stsName); + if (vehicle) { + return vehicle.coordinate.toString(); + } + + // If vehicle is not found, return undefined + return "no vehicle"; + }; + async function calculateRoute() { - if( routeType === "Location Based Optimal Route"){ - if ( + if (routeType === "Location Based Optimal Route") { + if ( !originRef.current || !destinationRef.current || originRef.current.value === "" || - destinationRef.current.value === "") { + destinationRef.current.value === "" + ) { return; } - const directionsService = new google.maps.DirectionsService(); const results = await directionsService.route({ origin: originRef.current.value, @@ -142,14 +175,14 @@ function OptimizedRouteMap() { setDistance(results.routes[0].legs[0].distance?.text || ""); setDuration(results.routes[0].legs[0].duration?.text || ""); } - }else{ - - - + } else { + const stsCoord = getSTSCoodrdinateByName(searchTerm); + const newString = stsCoord.substring(0, 11); + console.log(stsCoord); const directionsService = new google.maps.DirectionsService(); const results = await directionsService.route({ - origin: searchTerm, - destination: landTerm, + origin: stsCoord, + destination: landFilCoord.coordinate, travelMode: google.maps.TravelMode.DRIVING, }); @@ -158,13 +191,7 @@ function OptimizedRouteMap() { setDistance(results.routes[0].legs[0].distance?.text || ""); setDuration(results.routes[0].legs[0].duration?.text || ""); } - } - - - - - } function clearRoute() { @@ -320,6 +347,6 @@ function OptimizedRouteMap() { ); -} +}; export default OptimizedRouteMap; diff --git a/client/components/maps/getStsCoordinateForRoute.tsx b/client/components/maps/getStsCoordinateForRoute.tsx new file mode 100644 index 0000000..c6bc4c9 --- /dev/null +++ b/client/components/maps/getStsCoordinateForRoute.tsx @@ -0,0 +1,60 @@ + +import { ChakraProvider, theme } from "@chakra-ui/react"; +import OptimizedRouteMap from "@/components/maps/OptimizedRoute"; +import RouteMap from "@/components/maps/RouteMap"; +import STSVehicleList from "@/components/dataTables/StsVehicleList"; + +import useGetAllSTS from "@/hooks/stsdata/useGetAllSTS"; +import * as React from "react"; +import {useState, useEffect} from "react"; + + + +type StsRouteType = { + coordinate: string, + name: string, +} + +export default function GetStsCoordinateForRoute() { + const {stsList, getAllSTS, stsRoute } = useGetAllSTS(); + const [coordinates, setCoordinates] = useState([]); + const [stsName, setStsName] = useState([]); + const [stsRouting , setStsRouting] = useState([]); + + + + const suggestionsList: string[] = [ + "23.7751927, 90.3810282", + "Bolbacchan STS", + "Gulshan STS", + "Baridhara STS", + "Mohammadpur STS", + "Gulistan STS", + "Rampura STS", + ]; + + const landfillList: string[] = ["23.7618195, 90.3833253","Amin Bazar", "Chashara"]; + + + + useEffect(() => { + getAllSTS(); +console.log(stsRoute); + + + }, []); + useEffect(() => { + const coordinateArray: string[] = stsRoute.map(route => route.coordinate); + setCoordinates(coordinateArray); + setStsRouting(stsRoute); + }, [stsRoute]); + + + + console.log(coordinates); + return ( + + + + ); +} diff --git a/client/components/modals/LandFillVehicleEntryModal.tsx b/client/components/modals/LandFillVehicleEntryModal.tsx new file mode 100644 index 0000000..efc7778 --- /dev/null +++ b/client/components/modals/LandFillVehicleEntryModal.tsx @@ -0,0 +1,184 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogClose, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { Download, LucideArrowDownWideNarrow, Send, Trash, Truck } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; +import VehicleRelaseRoute from "../maps/VehicleReleaseRoute"; +import useVehicleReleaseFromSTS from "@/hooks/StsDashboard/useVehicleReleaseFromSTS"; +import useUpcomingVehicle from "@/hooks/landFillDashboard/useUpcomingVehiclesList"; +import useTripComplete from "@/hooks/landFillDashboard/useTripComplete"; + +import DatePicker from "react-datepicker"; + +import "react-datepicker/dist/react-datepicker.css"; + + +type Vehicle = { + tripId: string; + weightOfWaste: string; + vehicleNumber: string; + stsId: string; + vehicleType: string; + distance: string; + tripStartTime: string; + estimatedDuration: string; + tripStatus: string; + capacity: string; +}; + +export const LandfillVehicleEntryModal = ({ + vehicleInfo, +}: { + vehicleInfo: Vehicle; +}) => { + const [vehicleData, setVehicleData] = useState(vehicleInfo); + const [weightOfWaste, setWeightOfWaste] = useState(""); + const [entryTime, setEntryTime] = useState(new Date().toLocaleString()); + const [stsCoordinate, setStsCoordinate] = useState(""); + const [landFillCoordinate, setLandFillCoordinate] = useState(""); + const [distance, setDistance] = useState(""); + const [duration, setDuration] = useState(""); + const { TripComplete } = useTripComplete(); + + + + const [selectedDateTime, setSelectedDateTime] = useState(new Date()); + +const handleDateChange = (date: Date) => { + setSelectedDateTime(date); +}; + + + + const handleSaveChanges = async () => { + try { + const postEntry = await TripComplete({ + tripId: vehicleInfo.tripId, + weightOfWaste: weightOfWaste, + entryTime: selectedDateTime.toISOString()//entryTime, + }); + + window.location.reload(); + } catch (error) { + console.error("Error:", error); + } + }; + + return ( + + + + + + + + Vehicle Entry Details + + +
+

+ Vehicle Number: + {vehicleInfo.vehicleNumber} +

+

+ Vehicle Type: + {vehicleInfo.vehicleType} +

+

+ STS Name: + {vehicleInfo.stsId} +

+

+ Capacity: + {vehicleInfo.capacity} +

+

+ Distance: + {vehicleInfo.distance} +

+

+ Trip Start Time: + {vehicleInfo.tripStartTime} +

+
+
+
+
+
+ + setWeightOfWaste(e.target.value)} + /> +
+ +
+ + {/* setEntryTime(e.target.value)} + /> */} +
+ {/* Other component content */} + +
+
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/ProfileEditModal.tsx b/client/components/modals/ProfileEditModal.tsx new file mode 100644 index 0000000..f98e2e4 --- /dev/null +++ b/client/components/modals/ProfileEditModal.tsx @@ -0,0 +1,158 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogClose +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { Send, Trash,EditIcon } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; +import VehicleRelaseRoute from "../maps/VehicleReleaseRoute"; +import useVehicleReleaseFromSTS from "@/hooks/StsDashboard/useVehicleReleaseFromSTS"; +import useUpcomingVehicle from "@/hooks/landFillDashboard/useUpcomingVehiclesList"; +import useTripComplete from "@/hooks/landFillDashboard/useTripComplete"; +import { profile } from "console"; +import useEditProfileInfo from "@/hooks/user_data/useEditProfileInfo"; +import useGetUserProfile from "@/hooks/user_data/useGetUserProfile"; + + +type User = { + id: string; + username: string; + email: string; + profileName: string; + roleName: string; + roleDescription: string; + +}; + + +export const ProfileEditModal = ({ profileInfo }: { profileInfo: User }) => { + const [profileData, setProfileData] = useState(profileInfo); + + const { user, stsDetails, landfillDetails, getUserDetails} = useGetUserProfile(); + + + const [username , setUsername] = useState(user.username); + const [profilename , setProfilename] = useState(user.profileName); + + const { EditProfileInfo } = useEditProfileInfo(); + + + + + const handleSaveChanges = async () => { + try { + + + const postEntry = await EditProfileInfo({ + username : username, + profileName: profilename + }); + + } catch (error) { + console.error("Error:", error); + } + + + }; + useEffect(() => { + getUserDetails(); + }, []); + + + return ( + + + + + + + + Edit Profile + + +
+

+ Role Name: + {user.roleName} +

+

+ Email: + {user.email} +

+

+ Username: + {user.username} +

+

+ Profile Name: + {user.profileName} +

+ + + +
+
+
+
+
+ + setUsername(e.target.value)} + /> +
+ +
+ + setProfilename(e.target.value)} + /> +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/STSVehicleReleaseModal.tsx b/client/components/modals/STSVehicleReleaseModal.tsx index aacad80..d30a650 100644 --- a/client/components/modals/STSVehicleReleaseModal.tsx +++ b/client/components/modals/STSVehicleReleaseModal.tsx @@ -32,7 +32,9 @@ import editSTS from "@/hooks/entityCreation/editSTS"; import getUserByRole from "@/hooks/user_data/getUserByRole"; import VehicleRelaseRoute from "../maps/VehicleReleaseRoute"; import useVehicleReleaseFromSTS from "@/hooks/StsDashboard/useVehicleReleaseFromSTS"; +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; type Vehicle = { entryId: string, @@ -60,25 +62,12 @@ export const STSVehicleRelease = ({ vehicleInfo }: { vehicleInfo: Vehicle }) => const [distance, setDistance] = useState(""); const [duration, setDuration] = useState("");const { VehicleReleaseFromSTS } = useVehicleReleaseFromSTS(); - - -// const dummyCoordinates[ -// 23.76652752, 90.4258899 -// 23.76449486, 90.3879528 -// 23.73897468, 90.3750954 -// 23.76431111, 90.3651622 -// 23.77393625, 90.3814204 -// 23.76461481, 90.3915441 -// 23.77089053, 90.4042765 -// 23.72965447, 90.3873709 -// ] -// { -// "stsVehicleId": "sv1", -// "weightOfWaste" : 2, -// "exitTime" : "2024-03-27T08:00:00Z", -// "distance": "156.3", -// "estimatedDuration": "23" -// } + const [selectedDateTime, setSelectedDateTime] = useState(new Date()); + + const handleDateChange = (date: Date) => { + setSelectedDateTime(date); + }; + const handleSaveChanges = async () => { @@ -87,7 +76,7 @@ export const STSVehicleRelease = ({ vehicleInfo }: { vehicleInfo: Vehicle }) => const postEntry = await VehicleReleaseFromSTS({ stsVehicleId: vehicleInfo.entryId, weightOfWaste: weightOfWaste, - exitTime: exitTime, + exitTime: selectedDateTime.toISOString(), distance: distance, estimatedDuration: duration }); @@ -116,11 +105,11 @@ export const STSVehicleRelease = ({ vehicleInfo }: { vehicleInfo: Vehicle }) =>

Vehicle Number: - {vehicleInfo.stsLattitude} + {vehicleInfo.vehicleNumber}

Entry Time: - {vehicleInfo.stsLongitude} + {vehicleInfo.entryTime}

Capacity: @@ -165,13 +154,24 @@ export const STSVehicleRelease = ({ vehicleInfo }: { vehicleInfo: Vehicle }) => - setExitTime(e.target.value)} - /> + /> */} +

+ {/* Other component content */} + +
diff --git a/client/components/modals/StsVehicleEntryModal.tsx b/client/components/modals/StsVehicleEntryModal.tsx index 8b4767d..f121840 100644 --- a/client/components/modals/StsVehicleEntryModal.tsx +++ b/client/components/modals/StsVehicleEntryModal.tsx @@ -19,6 +19,10 @@ import { getCookie } from '@/lib/cookieFunctions'; import useVehicleEntry from "@/hooks/StsDashboard/useVehicleEntry"; import useVehicleList from "@/hooks/vehicles/useVehiclesData"; +import DatePicker from "react-datepicker"; + +import "react-datepicker/dist/react-datepicker.css"; + interface DialogWrapperProps { children: React.ReactNode; @@ -42,6 +46,12 @@ export const StsVehicleEntryModal: React.FC = ({ callVehcilse(); }, []); + const [selectedDateTime, setSelectedDateTime] = useState(new Date()); + +const handleDateChange = (date: Date) => { + setSelectedDateTime(date); +}; + const handleInputChange = (event: React.ChangeEvent) => { setSearchTerm(event.target.value); @@ -81,7 +91,7 @@ export const StsVehicleEntryModal: React.FC = ({ console.log(vehicleList); const postEntry = await VehicleEntry({ vehicleIds: vehicleId, - entryTimes: entryTime, + entryTimes: selectedDateTime.toISOString(), }); } catch (error) { @@ -115,6 +125,7 @@ export const StsVehicleEntryModal: React.FC = ({ value={searchTerm} onChange={handleInputChange} className="border border-gray-300 mx-4 px-1 py-2 rounded-md focus:outline-none focus:border-blue-500" + /> {showSuggestions && (
    @@ -133,13 +144,24 @@ export const StsVehicleEntryModal: React.FC = ({ - setEntryTime(e.target.value)} - /> + /> */} +
    + {/* Other component content */} + +
diff --git a/client/components/modals/VehicleModal.tsx b/client/components/modals/VehicleModal.tsx index 9ec547c..87e9ba7 100644 --- a/client/components/modals/VehicleModal.tsx +++ b/client/components/modals/VehicleModal.tsx @@ -7,7 +7,7 @@ import { DialogHeader, DialogTitle, DialogTrigger, - DialogClose + DialogClose, } from "@/components/ui/dialog"; import { DropdownMenu, @@ -20,7 +20,18 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import React , {useState} from "react"; +import React, { useEffect, useState } from "react"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "../ui/select"; +import useGetAllLandfill from "@/hooks/dataQuery/useGetAllLandfill"; +import useCreateVehicle from "@/hooks/entityCreation/useCreateVehicle"; interface DialogWrapperProps { children: React.ReactNode; @@ -29,24 +40,39 @@ interface DialogWrapperProps { export const VehicleCreateModal: React.FC = ({ children, }) => { - const [vehicleNumber, setVehicleNumber] = useState(""); - const [vehicleType, setVehicleType] = useState("Select Type"); - const [capacity, setCapacity] = useState(""); - - const handleItemClick = (text: string) => { - setVehicleType(text); - }; + const [vehicleNumber, setVehicleNumber] = useState(""); + const [vehicleType, setVehicleType] = useState("Dump Truck"); + const [capacity, setCapacity] = useState(); + const [loadedFuelCostPerKm, setLoadedFuelCostPerKm] = useState(); + const [unloadedFuelCostPerKm, setUnloadedFuelCostPerKm] = useState(); + const [assignedLandfill, setAssignedLandfill] = useState(""); + const {createVehicle} = useCreateVehicle(); + const vehicleTypeList = [ + "Dump Truck", + "Compactor Truck", + "Open Truck", + "Container Carrier", + ]; + const { landFillData, fetchAllLandfills } = useGetAllLandfill(); - + useEffect(() => {}, [landFillData]); - - const handleSaveChanges = () => { + const handleSaveChanges = async () => { console.log("Vehicle Number:", vehicleNumber); console.log("Vehicle Type:", vehicleType); console.log("Capacity:", capacity); + //alert(assignedLandfill); + const res = await createVehicle({ + vehicleNumber, + vehicleType, + capacity: capacity || 0, + loadedFuelCostPerKm: loadedFuelCostPerKm || 0, + unloadedFuelCostPerKm: unloadedFuelCostPerKm || 0, + landFillId: assignedLandfill, + }) + if(res) return alert(res); }; - return ( @@ -61,53 +87,127 @@ export const VehicleCreateModal: React.FC = ({
-
-
); diff --git a/client/components/modals/billControl/BillCreationModal.tsx b/client/components/modals/billControl/BillCreationModal.tsx new file mode 100644 index 0000000..ece447f --- /dev/null +++ b/client/components/modals/billControl/BillCreationModal.tsx @@ -0,0 +1,144 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { EditIcon, Ghost, Receipt, Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; +import useGetAllLandfill from "@/hooks/dataQuery/useGetAllLandfill"; +import editVehicle from "@/hooks/vehicles/editVehicle"; +import { Trip } from "@/components/dataTables/PendingBillList"; +import makeBill from "@/hooks/bills/makeBill"; + +export const BillCreationModal = ({ tripInfo }: { tripInfo: Trip }) => { + const [allocatedCost, setAllocatedCost] = useState(); + + return ( + + + + + + + + Generate Bill + + + Give allocated cost for the trip for Trip ID : {tripInfo.id} + + +
+
+
+
+ STS:
{tripInfo.stsName}{" "} +
+
+ Landfill:
{tripInfo.landFillName} +
+
+
+
+ Vehicle Number:
{tripInfo.vehicleNumber}{" "} +
+
+ Vehicle Type:
{tripInfo.vehicleType} +
+
+
+
+ Capacity:
{tripInfo.capacity} Ton{" "} +
+
+ + Weight Trasfered:
{tripInfo.weightOfWaste} Ton{" "} +
+
+
+ Shortage:
{tripInfo.shortage} Ton +
+
+
+
+ Loaded Fuel Cost:
{tripInfo.loadedFuelCostPerKm} BDT +
+
+ Unloaded Fuel Cost:
{tripInfo.unloadedFuelCostPerKm} BDT +
+
+
+
+ Actual Duration:
{tripInfo.estimatedDuration} min + +
+
+ Estimated Duration:
{tripInfo.actualDuration} min +
+
+
+
+ Total Distance:
{tripInfo.distance} km + +
+
+ Estimated Fuel Cost:
{tripInfo.estimatedFuelCost} BDT +
+
+
+
+ + setAllocatedCost(parseFloat(e.target.value))} + className="col-span-3" + required + /> +
+ + + +
+
+
+ ); +}; diff --git a/client/components/modals/billControl/BillViewModal.tsx b/client/components/modals/billControl/BillViewModal.tsx new file mode 100644 index 0000000..5e7806b --- /dev/null +++ b/client/components/modals/billControl/BillViewModal.tsx @@ -0,0 +1,154 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import ReactToPrint from 'react-to-print'; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { EditIcon, PrinterIcon, Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; +import useGetAllLandfill from "@/hooks/dataQuery/useGetAllLandfill"; +import editVehicle from "@/hooks/vehicles/editVehicle"; +import makeBill from "@/hooks/bills/makeBill"; +import { Bill } from "@/components/dataTables/CompletedBillList"; +import Link from "next/link"; + +export const BillViewModal = ({ billInfo }: { billInfo: Bill }) => { + const componentRef = React.useRef(); + return ( + + + + + + + + Print Bill + + + This is the bill for the trip {billInfo.tripId} + + +
+ +
+
+ + Bill number:  {billInfo.billNo} + logo +
+
+
+ STS:
{billInfo.stsName}{" "} +
+
+ Landfill:
{billInfo.landFillName} +
+
+
+
+ Vehicle Number:
{billInfo.vehicleNumber}{" "} +
+
+ Vehicle Type:
{billInfo.vehicleType} +
+
+
+
+ Capacity:
{billInfo.capacity} Ton{" "} +
+
+ + Weight Trasfered:
{billInfo.weightOfWaste} Ton{" "} +
+
+
+ Shortage:
{billInfo.shortage} Ton +
+
+
+
+ Loaded Fuel Cost:
{billInfo.loadedFuelCostPerKm}{" "} + BDT +
+
+ Unloaded Fuel Cost:
{" "} + {billInfo.unloadedFuelCostPerKm} BDT +
+
+
+
+ Actual Duration:
{billInfo.estimatedDuration} min +
+
+ Estimated Duration:
{billInfo.actualDuration} min +
+
+
+
+ Total Distance:
{billInfo.distance} km +
+
+ Estimated Fuel Cost:
{billInfo.estimatedFuelCost}{" "} + BDT +
+
+
+ ALLOCATED BILL:  {billInfo.allocatedFuelCost} BDT +
+
+ + + +
+ + + + +
+
+
+
+
+ ); +}; diff --git a/client/components/modals/billControl/lol.tsx b/client/components/modals/billControl/lol.tsx new file mode 100644 index 0000000..9476b5f --- /dev/null +++ b/client/components/modals/billControl/lol.tsx @@ -0,0 +1,10 @@ +import React from "react"; + +export class LOL extends React.PureComponent<{}, {}> { + + render() { + return ( +
lol
+ ); + } +} \ No newline at end of file diff --git a/client/components/modals/landfillControl/DeleteLandfillModal.tsx b/client/components/modals/landfillControl/DeleteLandfillModal.tsx new file mode 100644 index 0000000..754553c --- /dev/null +++ b/client/components/modals/landfillControl/DeleteLandfillModal.tsx @@ -0,0 +1,92 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { useState } from "react"; +import { Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import deleteSTS from "@/hooks/entityCreation/deleteSTS"; +import { LandFill } from "@/components/dataTables/LandFillList"; +import deleteLandFill from "@/hooks/entityCreation/deleteLandfill"; + +export const DeleteLandfillModal = ({ landfillInfo }: { landfillInfo: LandFill }) => { + const [confirmText, setConfirmText] = useState(""); + return ( + + + + + + + + Confirm Delete STS? + + +
+

+ ID: + {landfillInfo.id} +

+

+ Name: + {landfillInfo.name} +

+

+ Capacity: + {landfillInfo.capacity} +

+

Managers:

+ {landfillInfo.manager.length === 0 ?

    NO MANAGER ASSGINED FOR THIS LandFill

: landfillInfo.manager.map((manager, index) => ( +

+     {index + 1 + "."} {manager} +

+ ))} +
+
+
+
+
+
+ + setConfirmText(e.target.value)} + required + /> +
+
+ + + +
+
+
+ ); +}; diff --git a/client/components/modals/landfillControl/EditLandfillInfoModal.tsx b/client/components/modals/landfillControl/EditLandfillInfoModal.tsx new file mode 100644 index 0000000..04eb53c --- /dev/null +++ b/client/components/modals/landfillControl/EditLandfillInfoModal.tsx @@ -0,0 +1,149 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { EditIcon, Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; +import editLandfill from "@/hooks/entityCreation/editLandfill"; + +type LandFill = { + id: string; + name: string; + capacity: string; + latitude: string; + longitude: string; + manager: string[]; +}; + +type LandFillManager = { + id: string; + username: string; +}; + +export const EditLandfillInfoModal = ({ landfillInfo }: { landfillInfo: LandFill }) => { + const [landfillData, setLandfillData] = useState(landfillInfo); + const [landfillManagerData, setLandfillManagerData] = useState(); + const [landfillManagerList, setLandfillManagerList] = useState([]); + + const getManagerList = async () => { + const result = await getUserByRole(landfillManager); + if (result) await setLandfillManagerList(result); + }; + + useEffect(() => { + getManagerList(); + }, []); + + return ( + + + + + + + + Edit Landfill Details + + + Update Landfill information by filling out the form below. + + +
+
+
+ + + setLandfillData({ ...landfillData, name: e.target.value }) + } + className="col-span-3" + required + /> +
+
+ + + setLandfillData({ ...landfillData, capacity: e.target.value }) + } + className="col-span-3" + required + /> +
+
+ + +
+
+ + + +
+
+
+ ); +}; diff --git a/client/components/modals/LandfillModal.tsx b/client/components/modals/landfillControl/LandfillModal.tsx similarity index 98% rename from client/components/modals/LandfillModal.tsx rename to client/components/modals/landfillControl/LandfillModal.tsx index aae67c1..f625693 100644 --- a/client/components/modals/LandfillModal.tsx +++ b/client/components/modals/landfillControl/LandfillModal.tsx @@ -13,7 +13,7 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import React, { useState } from "react"; -import SetZone from "../maps/SetZone"; +import SetZone from "../../maps/SetZone"; import useCreateLandFill, { LandFill, } from "@/hooks/entityCreation/useCreateLandfill"; diff --git a/client/components/modals/landfillControl/ViewLandfillInfoModal.tsx b/client/components/modals/landfillControl/ViewLandfillInfoModal.tsx new file mode 100644 index 0000000..15d7462 --- /dev/null +++ b/client/components/modals/landfillControl/ViewLandfillInfoModal.tsx @@ -0,0 +1,108 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { EditIcon, Eye, Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; + +type LandFill = { + id: string; + name: string; + capacity: string; + latitude: string; + longitude: string; + manager: string[]; +}; + +type LandFillManager = { + id: string; + username: string; +}; + +export const ViewLandFIllInfoModal = ({ landfillInfo }: { landfillInfo: LandFill }) => { + const [landFillData, setLanfillData] = useState(landfillInfo); + const [landfillManagerData, setLandfillManagerData] = useState(); + const [landfillManagerList, setLandfillManagerList] = useState([]); + + const getManagerList = async () => { + const result = await getUserByRole(landfillManager); + if (result) await setLandfillManagerList(result); + }; + + useEffect(() => { + getManagerList(); + }, []); + + return ( + + + + + + + + View STS Details + + +

Here are the description of the STS

+
+

+ ID: + {landFillData.id} +

+

+ Name: + {landFillData.name} +

+

+ Capacity: + {landFillData.capacity} +

+

+ Latitude: + {landFillData.latitude} +

+

+ Longitude: + {landFillData.longitude} +

+

Managers:

+ {landFillData.manager.length === 0 ?

    NO MANAGER ASSGINED FOR THIS LandFill

: landFillData.manager.map((manager, index) => ( +

+     {index + 1 + "."} {manager} +

+ ))} +
+
+
+
+
+ ); +}; diff --git a/client/components/modals/DeleteSTSModal.tsx b/client/components/modals/stsControl/DeleteSTSModal.tsx similarity index 87% rename from client/components/modals/DeleteSTSModal.tsx rename to client/components/modals/stsControl/DeleteSTSModal.tsx index d7c6420..52373e1 100644 --- a/client/components/modals/DeleteSTSModal.tsx +++ b/client/components/modals/stsControl/DeleteSTSModal.tsx @@ -23,6 +23,7 @@ export type STS = { capacity: string; latitude: string; longitude: string; + manager: string[]; }; export const DeleteSTSModal = ({ stsInfo }: { stsInfo: STS }) => { @@ -58,6 +59,12 @@ export const DeleteSTSModal = ({ stsInfo }: { stsInfo: STS }) => { Capacity: {stsInfo.capacity}

+

Managers:

+ {stsInfo.manager.length === 0 ?

    NO MANAGER ASSGINED FOR THIS LandFill

: stsInfo.manager.map((manager, index) => ( +

+     {index + 1 + "."} {manager} +

+ ))}
diff --git a/client/components/modals/EditSTSInfoModal.tsx b/client/components/modals/stsControl/EditSTSInfoModal.tsx similarity index 86% rename from client/components/modals/EditSTSInfoModal.tsx rename to client/components/modals/stsControl/EditSTSInfoModal.tsx index f6a9733..5f4d425 100644 --- a/client/components/modals/EditSTSInfoModal.tsx +++ b/client/components/modals/stsControl/EditSTSInfoModal.tsx @@ -22,7 +22,7 @@ import { SelectGroup, SelectLabel, SelectItem, -} from "../ui/select"; +} from "../../ui/select"; import editUser from "@/hooks/user_data/editUser"; import gettAllRoles from "@/hooks/user_data/useGetAllRole"; import { number } from "prop-types"; @@ -37,6 +37,7 @@ export type STS = { capacity: string; latitude: string; longitude: string; + manager: string[]; }; type STSManager = { @@ -71,24 +72,7 @@ export const EditSTSInfoModal = ({ stsInfo }: { stsInfo: STS }) => { Edit STS Details -
-

- ID: - {stsData.id} -

-

- Name: - {stsData.name} -

-

- Capacity: - {stsData.capacity} -

-

- Ward: - {stsData.wardNumber} -

-
+ Update STS information by filling out the form below.
@@ -140,14 +124,14 @@ export const EditSTSInfoModal = ({ stsInfo }: { stsInfo: STS }) => {
setRoleName(e.target.value)} + /> +
+
+ + +
+ +
+
+
+ + + + ); +}; diff --git a/client/components/modals/RoleModal.tsx b/client/components/modals/userControls/RoleModal.tsx similarity index 72% rename from client/components/modals/RoleModal.tsx rename to client/components/modals/userControls/RoleModal.tsx index fc9f7d0..cad0dd3 100644 --- a/client/components/modals/RoleModal.tsx +++ b/client/components/modals/userControls/RoleModal.tsx @@ -12,6 +12,7 @@ import { import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import addNewRole from "@/hooks/user_data/addNewRole"; import React , {useState} from "react"; interface DialogWrapperProps { @@ -21,13 +22,11 @@ interface DialogWrapperProps { export const RoleCreateModal: React.FC = ({ children, }) => { - const [description, setDescription] = useState(""); const [roleName, setRoleName] = useState(""); - const handleSaveChanges = () => { - console.log("Vehicle Number:", description); - console.log("Vehicle Number:", roleName); - + const handleSaveChanges = async () => { + await addNewRole(roleName); + window.location.reload(); }; @@ -56,19 +55,7 @@ export const RoleCreateModal: React.FC = ({ value={roleName} onChange={(e) => setRoleName(e.target.value)} /> -
-
- - setDescription(e.target.value)} - /> -
+ diff --git a/client/components/modals/vehicleControl/DeleteVehicleModal.tsx b/client/components/modals/vehicleControl/DeleteVehicleModal.tsx new file mode 100644 index 0000000..10102c1 --- /dev/null +++ b/client/components/modals/vehicleControl/DeleteVehicleModal.tsx @@ -0,0 +1,101 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { useState } from "react"; +import { Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import deleteVehicleEntryFromSTS from "@/hooks/StsDashboard/deleteVehicleEntryFromSTS"; +import deleteVehiclebyId from "@/hooks/vehicles/deleteVehiclebyId"; + +type Vehicle = { + id: string, + vehicleNumber: string, + vehicleType: string, + capacity: string, + loadedFuelCostPerKm: string, + unloadedFuelCostPerKm: string, + landFillId: string, + landFillName: string, + }; + +export const DeleteVehicleModal = ({ vehicleInfo }: { vehicleInfo: Vehicle }) => { + const [confirmText, setConfirmText] = useState(""); + console.log(vehicleInfo); + return ( + + + + + + + + Confirm Delete Vehicle {vehicleInfo.vehicleNumber}? + + +
+

+ ID: + {vehicleInfo.id} +

+

+ Vehicle Number: + {vehicleInfo.vehicleNumber} +

+

+ Vehicle Type: + {vehicleInfo.vehicleType} +

+

+ Assigned Landfll: + {vehicleInfo.landFillName} +

+
+
+
+ +
+
+ + setConfirmText(e.target.value)} + required + /> +
+
+ + + + +
+
+ ); +}; diff --git a/client/components/modals/vehicleControl/EditVehicleInfoModal.tsx b/client/components/modals/vehicleControl/EditVehicleInfoModal.tsx new file mode 100644 index 0000000..84d13ff --- /dev/null +++ b/client/components/modals/vehicleControl/EditVehicleInfoModal.tsx @@ -0,0 +1,202 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import React, { use, useEffect, useState } from "react"; +import { EditIcon, Trash } from "lucide-react"; +import deleteUser from "@/hooks/user_data/deleteUser"; +import { + Select, + SelectTrigger, + SelectValue, + SelectContent, + SelectGroup, + SelectLabel, + SelectItem, +} from "../../ui/select"; +import editUser from "@/hooks/user_data/editUser"; +import gettAllRoles from "@/hooks/user_data/useGetAllRole"; +import { number } from "prop-types"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import editSTS from "@/hooks/entityCreation/editSTS"; +import getUserByRole from "@/hooks/user_data/getUserByRole"; +import useGetAllLandfill from "@/hooks/dataQuery/useGetAllLandfill"; +import editVehicle from "@/hooks/vehicles/editVehicle"; + +type Vehicle = { + id: string, + vehicleNumber: string, + vehicleType: string, + capacity: string, + loadedFuelCostPerKm: string, + unloadedFuelCostPerKm: string, + landFillId: string, + landFillName: string, +}; + +export const EditVehicleInfoModal = ({ vehicleInfo }: { vehicleInfo: Vehicle }) => { + const [vehicleData, setvehicleData] = useState(vehicleInfo); + const [selectedLandfill, setSelectedLandfill] = useState(vehicleInfo.landFillId); + const vehicleTypeList = [ + "Dump Truck", + "Compactor Truck", + "Open Truck", + "Container Carrier", + ]; + const {landFillData} = useGetAllLandfill(); + + useEffect(() => {}, [landFillData]); + + return ( + + + + + + + + Edit Vehicle Details + + + Update Vehicle information by filling out the form below. + + +
+
+
+ + + setvehicleData({ ...vehicleData, vehicleNumber: e.target.value }) + } + className="col-span-4" + required + /> +
+
+ + +
+
+ + + setvehicleData({ ...vehicleData, capacity: e.target.value }) + } + className="col-span-4" + required + /> +
+
+ + + setvehicleData({ ...vehicleData, loadedFuelCostPerKm: e.target.value }) + } + className="col-span-4" + required + /> +
+
+ + + setvehicleData({ ...vehicleData, unloadedFuelCostPerKm: e.target.value }) + } + className="col-span-4" + required + /> +
+
+ + +
+
+ + + +
+
+
+ ); +}; diff --git a/client/components/profile/backgroundComp.tsx b/client/components/profile/backgroundComp.tsx new file mode 100644 index 0000000..47d8518 --- /dev/null +++ b/client/components/profile/backgroundComp.tsx @@ -0,0 +1,17 @@ +// components/BackgroundComponent.tsx +"use client"; + +import React from 'react'; + +const BackgroundComponent: React.FC = () => { + return ( +
+ +
+ ); +}; + +export default BackgroundComponent; diff --git a/client/components/ui/radio-group.tsx b/client/components/ui/radio-group.tsx new file mode 100644 index 0000000..cf01673 --- /dev/null +++ b/client/components/ui/radio-group.tsx @@ -0,0 +1,44 @@ +"use client" + +import * as React from "react" +import { CheckIcon } from "@radix-ui/react-icons" +import * as RadioGroupPrimitive from "@radix-ui/react-radio-group" + +import { cn } from "@/lib/utils" + +const RadioGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + ) +}) +RadioGroup.displayName = RadioGroupPrimitive.Root.displayName + +const RadioGroupItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + return ( + + + + + + ) +}) +RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName + +export { RadioGroup, RadioGroupItem } diff --git a/client/data/apiRoutes.ts b/client/data/apiRoutes.ts index ee26e02..7a766df 100644 --- a/client/data/apiRoutes.ts +++ b/client/data/apiRoutes.ts @@ -21,15 +21,42 @@ export const apiRoutes = { }, }, landfill: { + getbyId: `${baseUrl}/landfills/`, create: `${baseUrl}/landfills/create`, getAll: `${baseUrl}/landfills`, delete: `${baseUrl}/landfills/`, edit: `${baseUrl}/landfills/`, + getAllIncomingVehicles: `${baseUrl}/trips/search?tripStatus=PENDING&landfillId=`, + getAllRecievedVechicleHistory: `${baseUrl}/trips/search?tripStatus=DELIVERED&landfillId=`, }, rbac: { - create: `${baseUrl}/rbac/create`, - getByRole: `${baseUrl}/rbac/roles/`, - delete: `${baseUrl}/rbac/`, - edit: `${baseUrl}/rbac/`, + + create: `${baseUrl}/rbac/roles`, + getByRole: `${baseUrl}/rbac/roles/get/`, + delete: `${baseUrl}/rbac/roles/delete/`, + edit: `${baseUrl}/rbac/roles/`, + getAllRolesWithPermisson: `${baseUrl}/rbac/all`, + fetchAllPermissons: `${baseUrl}/rbac/permissions`, + }, + vehicle: { + create: `${baseUrl}/vehicles/create`, + getAll: `${baseUrl}/vehicles`, + delete: `${baseUrl}/vehicles/`, + edit: `${baseUrl}/vehicles/`, + }, + trip: { + create: `${baseUrl}/trips/create`, + getAll: `${baseUrl}/trips`, + delete: `${baseUrl}/trips/`, + edit: `${baseUrl}/trips/`, + search: `${baseUrl}/trips/search`, + }, + bills: { + makeBill: `${baseUrl}/bills/create-from-trip/`, + search: `${baseUrl}/bills/search`, }, + profile: { + getProfile: `${baseUrl}/profile`, + edit: `${baseUrl}/profile`, + } } \ No newline at end of file diff --git a/client/data/cookieNames.tsx b/client/data/cookieNames.tsx index 4278abc..e56a591 100644 --- a/client/data/cookieNames.tsx +++ b/client/data/cookieNames.tsx @@ -4,3 +4,6 @@ export const uid='userid'; export const username='username'; export const stsId='stsId'; export const curActive = "curActivee"; +export const landfillId = "landfillId"; +export const landfillName = "landfillName"; +export const stsName = "stsName"; \ No newline at end of file diff --git a/client/data/mainContentElements/getDashboardFor.tsx b/client/data/mainContentElements/getDashboardFor.tsx index eec462c..f101218 100644 --- a/client/data/mainContentElements/getDashboardFor.tsx +++ b/client/data/mainContentElements/getDashboardFor.tsx @@ -5,11 +5,12 @@ import {getContentsOfLandfillManager} from "./getLandfillContents"; import { getContentsOfSTSManager } from "./getSTSContents"; import { getContentsOfUnassigned } from "./getUnassignedContents"; -export default function getDashboardFor(state: string) { - if (state.startsWith(admin)) return getContentsOfAdmin(state); - else if (state.startsWith(stsManager)) return getContentsOfSTSManager(state); - else if (state.startsWith(landfillManager)) +export default function getDashboardFor(state: string | null) { + + if (state?.startsWith(admin)) return getContentsOfAdmin(state); + else if (state?.startsWith(stsManager)) return getContentsOfSTSManager(state); + else if (state?.startsWith(landfillManager)) return getContentsOfLandfillManager(state); - else if (state.startsWith(unassigned)) return getContentsOfUnassigned(state); + else if (state?.startsWith(unassigned)) return getContentsOfUnassigned(state); else return InvalidSate(); } \ No newline at end of file diff --git a/client/data/mainContentElements/getLandfillContents.tsx b/client/data/mainContentElements/getLandfillContents.tsx index ab8cdb4..1f2ae31 100644 --- a/client/data/mainContentElements/getLandfillContents.tsx +++ b/client/data/mainContentElements/getLandfillContents.tsx @@ -5,6 +5,7 @@ import LandfillManagerSchedules from "../../components/dashboard-componenets/mai import LandfillManagerStorageData from "../../components/dashboard-componenets/mainContent/landFillManagerContents/Storage"; import LandfillManagerDumpEntries from "../../components/dashboard-componenets/mainContent/landFillManagerContents/DumpEntry"; import LandfillManagerSettings from "../../components/dashboard-componenets/mainContent/landFillManagerContents/Settings"; +import LandfillManagerBillsManagementPanel from "@/components/dashboard-componenets/mainContent/landFillManagerContents/LandfillManagerBills"; export function getContentsOfLandfillManager(state: string) { switch (state) { @@ -16,6 +17,8 @@ export function getContentsOfLandfillManager(state: string) { return ; case landfillManager + "-Dump Entry": return ; + case landfillManager + "-Bills": + return ; case landfillManager + "-Settings": return ; default: diff --git a/client/data/sidebarElements/landfillManagerSidebar.ts b/client/data/sidebarElements/landfillManagerSidebar.ts index 4ddb517..3c55308 100644 --- a/client/data/sidebarElements/landfillManagerSidebar.ts +++ b/client/data/sidebarElements/landfillManagerSidebar.ts @@ -33,7 +33,11 @@ export const LandfillManagerSidebarItemList = [ { Icon: Truck, title: "Dump Entry", - }, + }, + { + Icon: Receipt, + title: "Bills", + }, { Icon: Settings, title: "Settings", diff --git a/client/hooks/auth/useLogin.tsx b/client/hooks/auth/useLogin.tsx index 458486d..ba51c8a 100644 --- a/client/hooks/auth/useLogin.tsx +++ b/client/hooks/auth/useLogin.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import {admin, landfillManager, stsManager, unassigned} from '@/data/roles'; import { setCookie } from '@/lib/cookieFunctions'; import axios from 'axios'; -import { jwtToken, role , uid , stsId, username, curActive} from '@/data/cookieNames'; +import { jwtToken, role , uid , stsId, username, curActive, landfillId, landfillName, stsName} from '@/data/cookieNames'; import { apiRoutes } from "@/data/apiRoutes"; @@ -34,8 +34,12 @@ export default function useLogin() { setCookie(curActive, res.data.user.roleName === unassigned ? unassigned + "-My Profile" : res.data.user.roleName + "-Dashboard", 1); setCookie(jwtToken, res.data.token, 1); - setCookie(stsId, res.data.stsId, 1); - setCookie(username, res.data.username, 1); + setCookie(stsId, res.data.user.stsId, 1); + setCookie(landfillId, res.data.user.landfillId, 1); + setCookie(username, res.data.user.username, 1); + + setCookie(landfillName, res.data?.user?.landfill?.name, 1); + setCookie(stsName, res.data?.user?.sts?.name, 1); return true; } catch (error: any) { diff --git a/client/hooks/bills/makeBill.ts b/client/hooks/bills/makeBill.ts new file mode 100644 index 0000000..f443b2e --- /dev/null +++ b/client/hooks/bills/makeBill.ts @@ -0,0 +1,38 @@ +import { User } from "@/components/dataTables/UserList"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { STS } from "@/components/modals/stsControl/EditSTSInfoModal"; +import { Trip } from "@/components/dataTables/PendingBillList"; + + +export default async function makeBill(tripData: Trip, allocatedCost: number) { + if (tripData && allocatedCost) { + try { + const payload = { + tripId: tripData.id, + allocatedFuelCost: allocatedCost, + }; + const res2 = await axios.post( + apiRoutes.bills.makeBill, + payload, + { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + } + ); + console.log(res2.data); + return "bill created successfully"; + } catch (error: any) { + return ( + error.message?.toString() || + "error making bill. You may not have the required permissions." + ); + } + } + + return null; +} diff --git a/client/hooks/bills/useGetAllCompletedBillList.tsx b/client/hooks/bills/useGetAllCompletedBillList.tsx new file mode 100644 index 0000000..6b50877 --- /dev/null +++ b/client/hooks/bills/useGetAllCompletedBillList.tsx @@ -0,0 +1,53 @@ +import { useState } from "react"; +import axios from "axios"; +import { uri } from "@/data/constant"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; +import { Trip } from "@/components/dataTables/PendingBillList"; +import { Bill } from "@/components/dataTables/CompletedBillList"; + +export default function useGetAllCompletedBillList() { + const [billList, setbillList] = useState([]); // Initialize with an empty array of Vehicle objects + + async function getbillList(landfillId: string) { + try { + const res = await axios.get(apiRoutes.bills.search + "?landFillId=" + landfillId, { + headers: { Authorization: `Bearer ${getCookie(jwtToken)}` }, + }); + + //alert(JSON.stringify(res.data)); + // Assuming the response data is an array of vehicles + const allBills: Bill[] = res.data.map((bill: any) => ({ + id: bill.id, + billNo: bill.billNo, + stsName: bill.sts.name, + landFillName: bill.landfill.name, + vehicleNumber: bill.vehicle.vehicleNumber, + vehicleType: bill.vehicle.vehicleType, + weightOfWaste: bill.weightOfWaste, + shortage: bill.trip.shortage, + loadedFuelCostPerKm: bill.vehicle.loadedFuelCostPerKm, + unloadedFuelCostPerKm: bill.vehicle.unloadedFuelCostPerKm, + capacity: bill.vehicle.capacity, + estimatedFuelCost: bill.trip.estimatedFuelCost, + distance: bill.trip.distance, + estimatedDuration: bill.trip.estimatedDuration, + actualDuration: bill.trip.actualDuration, + allocatedFuelCost: bill.allocatedFuelCost, + tripId: bill.tripId, + })); + + console.log(allBills); + + setbillList(allBills); + + return true; + } catch (error: any) { + alert(error.message?.toString() || "Error fetching pending bills list"); + return false; + } + } + + return { billList, getbillList }; +} diff --git a/client/hooks/bills/useGetAllPendingBillList.tsx b/client/hooks/bills/useGetAllPendingBillList.tsx new file mode 100644 index 0000000..63fe70f --- /dev/null +++ b/client/hooks/bills/useGetAllPendingBillList.tsx @@ -0,0 +1,47 @@ +import { useState } from "react"; +import axios from "axios"; +import { uri } from "@/data/constant"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; +import { Trip } from "@/components/dataTables/PendingBillList"; + +export default function useGetAllPendingBillList() { + const [tripList, setTripList] = useState([]); // Initialize with an empty array of Vehicle objects + + async function getTripList(landfillId: string) { + try { + const res = await axios.get(apiRoutes.trip.search + "?tripStatus=DELIVERED&landFillId=" + landfillId, { + headers: { Authorization: `Bearer ${getCookie(jwtToken)}` }, + }); + // Assuming the response data is an array of vehicles + const allTrips: Trip[] = res.data.map((trip: any) => ({ + id: trip.id, + stsName: trip.sts.name, + landFillName: trip.landfill.name, + vehicleNumber: trip.vehicle.vehicleNumber, + vehicleType: trip.vehicle.vehicleType, + weightOfWaste: trip.weightOfWaste, + shortage: trip.shortage, + loadedFuelCostPerKm: trip.vehicle.loadedFuelCostPerKm, + unloadedFuelCostPerKm: trip.vehicle.unloadedFuelCostPerKm, + capacity: trip.vehicle.capacity, + estimatedFuelCost: trip.estimatedFuelCost, + distance: trip.distance, + estimatedDuration: trip.estimatedDuration, + actualDuration: trip.actualDuration + })); + + console.log(allTrips); + + setTripList(allTrips); + + return true; + } catch (error: any) { + alert(error.message?.toString() || "Error fetching pending bills list"); + return false; + } + } + + return { tripList, getTripList }; +} diff --git a/client/hooks/dataQuery/useGetAllLandfill.tsx b/client/hooks/dataQuery/useGetAllLandfill.tsx new file mode 100644 index 0000000..b813f17 --- /dev/null +++ b/client/hooks/dataQuery/useGetAllLandfill.tsx @@ -0,0 +1,51 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + +type LandFill = { + id: string; + name: string; + capacity: string; + latitude: string; + longitude: string; + manager: string[]; + }; + +export default function useGetAllLandfill() { + const [landFillData, setLandfillData] = useState([]); + + async function fetchAllLandfills() { + try { + const res = await axios.get(apiRoutes.landfill.getAll, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + const landfillList = res.data.map((sts: any) => { + return { + id: sts.id, + name: sts.name, + capacity: sts.capacity, + latitude: sts.latitude, + longitude: sts.longitude, + manager: sts.manager.map((manager: any) => manager.username), + }; + }); + await setLandfillData(landfillList); + console.log(landfillList); + } catch (error: any) { + alert("Error fetching sts data... Are you authorized?"); + console.log(error.message); + } + } + + useEffect(() => { + fetchAllLandfills(); + }, []); + + return {fetchAllLandfills, landFillData}; +} diff --git a/client/hooks/dataQuery/useGetAllSTS.tsx b/client/hooks/dataQuery/useGetAllSTS.tsx index d1ffcb6..ff3f3b0 100644 --- a/client/hooks/dataQuery/useGetAllSTS.tsx +++ b/client/hooks/dataQuery/useGetAllSTS.tsx @@ -13,6 +13,7 @@ export type STS = { capacity: string; latitude: string; longitude: string; + manager: string[]; }; export default function useGetAllSTS() { @@ -33,6 +34,7 @@ export default function useGetAllSTS() { capacity: sts.capacity, latitude: sts.latitude, longitude: sts.longitude, + manager: sts.manager.map((manager: any) => manager.username), }; }); await setSTSData(stsList); diff --git a/client/hooks/entityCreation/deleteLandfill.ts b/client/hooks/entityCreation/deleteLandfill.ts new file mode 100644 index 0000000..67ab9db --- /dev/null +++ b/client/hooks/entityCreation/deleteLandfill.ts @@ -0,0 +1,22 @@ +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; + +export default async function deleteLandFill(landFillId: string) { + if (landFillId) { + try { + const res = await axios.delete(apiRoutes.landfill.delete + landFillId, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + return "landfills deleted successfully"; + } catch (error: any) { + return error.message?.toString() || "error deleteing landfill"; + } + } + + return null; +} diff --git a/client/hooks/entityCreation/editLandfill.ts b/client/hooks/entityCreation/editLandfill.ts new file mode 100644 index 0000000..2ce7f02 --- /dev/null +++ b/client/hooks/entityCreation/editLandfill.ts @@ -0,0 +1,46 @@ +import { User } from "@/components/dataTables/UserList"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { STS } from "@/components/modals/stsControl/EditSTSInfoModal"; +import { LandFill } from "@/components/dataTables/LandFillList"; + +export default async function editLandfill(landfillData: LandFill, managerId: string) { + if (landfillData && managerId) { + try { + const res1 = await axios.put( + apiRoutes.user.edit + managerId, + { + landfillId: landfillData.id, + }, + { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + } + ); + const {manager, ...payload} = {...landfillData}; + const res2 = await axios.put( + apiRoutes.landfill.edit + landfillData.id, + { + ...payload, + }, + { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + } + ); + return "landfill updated successfully"; + } catch (error: any) { + return ( + error.message?.toString() || + "error updating landfill. You may not have the required permissions." + ); + } + } + + return null; +} diff --git a/client/hooks/entityCreation/editSTS.ts b/client/hooks/entityCreation/editSTS.ts index e9e4b73..de8df83 100644 --- a/client/hooks/entityCreation/editSTS.ts +++ b/client/hooks/entityCreation/editSTS.ts @@ -4,13 +4,13 @@ import { jwtToken } from "@/data/cookieNames"; import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; import { getCookie } from "@/lib/cookieFunctions"; import axios from "axios"; -import { STS } from "@/components/modals/EditSTSInfoModal"; +import { STS } from "@/components/modals/stsControl/EditSTSInfoModal"; -export default async function editSTS(stsData: STS, manager: string) { - if (stsData && manager) { +export default async function editSTS(stsData: STS, managerId: string) { + if (stsData && managerId) { try { const res1 = await axios.put( - apiRoutes.user.edit + manager, + apiRoutes.user.edit + managerId, { stsId: stsData.id, }, @@ -20,10 +20,11 @@ export default async function editSTS(stsData: STS, manager: string) { }, } ); + const {manager, ...payload} = {...stsData}; const res2 = await axios.put( apiRoutes.sts.edit + stsData.id, { - ...stsData, + ...payload, }, { headers: { diff --git a/client/hooks/entityCreation/useCreateLandfill.tsx b/client/hooks/entityCreation/useCreateLandfill.tsx index 79c2cbb..ae042a9 100644 --- a/client/hooks/entityCreation/useCreateLandfill.tsx +++ b/client/hooks/entityCreation/useCreateLandfill.tsx @@ -32,6 +32,7 @@ export default function useCreateLandFill() { Authorization: `Bearer ${await getCookie(jwtToken)}`, }, }); + window.location.reload(); return "Landfill Aadded successfully"; } catch (error: any) { return error.message?.toString() || "Error creating Landfill"; diff --git a/client/hooks/entityCreation/useCreateSTS.tsx b/client/hooks/entityCreation/useCreateSTS.tsx index f57dcb3..d61af0a 100644 --- a/client/hooks/entityCreation/useCreateSTS.tsx +++ b/client/hooks/entityCreation/useCreateSTS.tsx @@ -34,6 +34,7 @@ export default function useCreateSTS() { Authorization: `Bearer ${await getCookie(jwtToken)}`, }, }); + window.location.reload(); return "STS Aadded successfully"; } catch (error: any) { return error.message?.toString() || "Error creating STS"; diff --git a/client/hooks/entityCreation/useCreateVehicle.ts b/client/hooks/entityCreation/useCreateVehicle.ts new file mode 100644 index 0000000..b19be2d --- /dev/null +++ b/client/hooks/entityCreation/useCreateVehicle.ts @@ -0,0 +1,50 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +export type Vehicle = { + vehicleNumber: string; + vehicleType: string; + capacity: number; + loadedFuelCostPerKm: number; + unloadedFuelCostPerKm: number; + landFillId: string; +}; + +export default function useCreateVehicle() { +// const [stsData, setStsData] = useState(); + + function isValid(vehicleData: Vehicle) { + + return ( + vehicleData.vehicleNumber.length > 0 && + vehicleData.vehicleType.length > 0 && + vehicleData.capacity > 0 && + vehicleData.loadedFuelCostPerKm !== null && + vehicleData.unloadedFuelCostPerKm !== null && + vehicleData.landFillId.length > 0 + ); + } + + async function createVehicle(vehicleData: Vehicle) { + if (vehicleData && isValid(vehicleData)) { + try { + const res = await axios.post(apiRoutes.vehicle.create, vehicleData, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + window.location.reload(); + return "Vehicle Aadded successfully"; + } catch (error: any) { + return error.message?.toString() || "Error creating Vehicle. Do you have permisson?."; + } + } + + return null; + } + + return { createVehicle }; +} diff --git a/client/hooks/landFillDashboard/getLandFillDataById.ts b/client/hooks/landFillDashboard/getLandFillDataById.ts new file mode 100644 index 0000000..9f3e48c --- /dev/null +++ b/client/hooks/landFillDashboard/getLandFillDataById.ts @@ -0,0 +1,58 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; +import { set } from "react-hook-form"; + +type LandFill = { + id: string; + name: string; + capacity: number; + currentTotalWaste: number; + latitude: string; + longitude: string; + graphData: { + empty: number; + full: number; + emptyPercentage: number; + fullPercentage: number; + }; +}; + +export default function useGetLandfillDatabyID() { + const [landfillData, setLandfillData] = useState(null); + + async function getLandfillDatabyID(landfillId: string) { + try { + const res = await axios.get(apiRoutes.landfill.getbyId + landfillId, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + setLandfillData({ + id: res.data.landfill.id, + name: res.data.landfill.name, + capacity: parseFloat(res.data.landfill.capacity), + currentTotalWaste: parseFloat(res.data.landfill.currentTotalWaste), + latitude: res.data.landfill.latitude, + longitude: res.data.landfill.longitude, + graphData: { + empty: res.data.graphData.empty, + full: res.data.graphData.full, + emptyPercentage: res.data.graphData.emptyPercentage, + fullPercentage: res.data.graphData.fullPercentage, + }, + }); + console.log(landfillData); + return landfillData; + } catch (error: any) { + alert("Error fetching landfill data... Are you authorized?"); + console.log(error.message); + } + } + + return { landfillData, getLandfillDatabyID }; +} diff --git a/client/hooks/landFillDashboard/useTripComplete.tsx b/client/hooks/landFillDashboard/useTripComplete.tsx new file mode 100644 index 0000000..08d73f8 --- /dev/null +++ b/client/hooks/landFillDashboard/useTripComplete.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react'; +import {admin, landfillManager, stsManager, unassigned} from '@/data/roles'; +import { setCookie, getCookie } from '@/lib/cookieFunctions'; +import axios from 'axios'; +import { jwtToken, role , uid , username, stsId} from '@/data/cookieNames'; +import { uri } from '@/data/constant'; + +export default function useTripComplete() { + + + + async function TripComplete(data: { + tripId: string, + weightOfWaste: string, + entryTime: string, + + }) { + const userStsId = getCookie(stsId); + // console.log(stsId); + + + try { + const isoString = new Date(data.entryTime).toISOString(); + + const weightWithoutUnit = parseFloat(data.weightOfWaste.replace(" tons", "")); +// console.log(data.stsVehicleId); +// console.log(data.weightOfWaste); +// console.log(data.exitTime); +// console.log(distanceWithoutUnit); +// console.log(durationWithoutUnit); + const res = await axios.post('http://localhost:8585/trips/complete', { + tripId: data.tripId, + weightOfWaste: weightWithoutUnit, + entryTime: isoString, + }); + //use the response from here + console.log(res.data); + + return true; + } catch (error: any) { + alert(error.message?.toString() || "error logging in"); + return false; + } + + } + + return {TripComplete}; +} \ No newline at end of file diff --git a/client/hooks/landFillDashboard/useUpcomingVehiclesList.tsx b/client/hooks/landFillDashboard/useUpcomingVehiclesList.tsx new file mode 100644 index 0000000..60bb228 --- /dev/null +++ b/client/hooks/landFillDashboard/useUpcomingVehiclesList.tsx @@ -0,0 +1,53 @@ +import { useState } from 'react'; +import axios from 'axios'; +import { uri } from '@/data/constant'; +import { apiRoutes } from '@/data/apiRoutes'; + +type Vehicle = { + tripId: string, + weightOfWaste: string, + vehicleNumber: string, + stsId: string, + vehicleType: string, + distance: string, + tripStartTime: string, + estimatedDuration: string + tripStatus: string + capacity: string, + +}; + +export default function useUpcomingVehicle() { + const [vehicleList, setVehicleList] = useState([]); // Initialize with an empty array of Vehicle objects + const [vehicleNumberList, setVehicleNumberList] = useState([]); + + async function UpcomingVehicle(landFillId: string) { + try { + const res = await axios.get(apiRoutes.landfill.getAllIncomingVehicles + landFillId); + // Assuming the response data is an array of vehicles + const AllVehicle: Vehicle[] = res.data.map((vehicle: any) => ({ + tripId: vehicle.id, + weightOfWaste: `${vehicle.weightOfWaste} Tons`, + vehicleNumber: vehicle.vehicle.vehicleNumber, + stsId: vehicle.sts.name, + vehicleType: vehicle.vehicle.vehicleType, + distance: `${vehicle.distance} km`, + tripStartTime: vehicle.tripStartTime, + estimatedDuration: `${vehicle.estimatedDuration} mins`, + tripStatus: vehicle.tripStatus, + capacity: vehicle.vehicle.capacity + })); + + + setVehicleList(AllVehicle); + + + return true; + } catch (error: any) { + alert(error.message?.toString() || "Error fetching vehicle list"); + return false; + } + } + + return { vehicleList, UpcomingVehicle }; +} diff --git a/client/hooks/landFillDashboard/useVehicleTripCompleteList.tsx b/client/hooks/landFillDashboard/useVehicleTripCompleteList.tsx new file mode 100644 index 0000000..6df38aa --- /dev/null +++ b/client/hooks/landFillDashboard/useVehicleTripCompleteList.tsx @@ -0,0 +1,59 @@ +import { useState } from "react"; +import axios from "axios"; +import { uri } from "@/data/constant"; +import { apiRoutes } from "@/data/apiRoutes"; + +type Vehicle = { + tripId: string; + weightOfWaste: string; + shortage: string; + vehicleNumber: string; + stsName: string; + vehicleType: string; + distance: string; + actualDuration: string; + estimatedFuelCost: string; + tripStartTime: string; + tripEndTime: string; + estimatedDuration: string; + tripStatus: string; + capacity: string; +}; + +export default function useVehicleTripCompleteList(landfillId: string) { + const [vehicleList, setVehicleList] = useState([]); // Initialize with an empty array of Vehicle objects + const [vehicleNumberList, setVehicleNumberList] = useState([]); + + async function VehcileTripCompleteList() { + try { + const res = await axios.get( + apiRoutes.landfill.getAllRecievedVechicleHistory + landfillId + ); + // Assuming the response data is an array of vehicles + const AllVehicle: Vehicle[] = res.data.map((vehicle: any) => ({ + tripId: vehicle.id, + weightOfWaste: `${vehicle.weightOfWaste} Tons`, + shortage: vehicle.shortage, + vehicleNumber: vehicle.vehicle.vehicleNumber, + stsName: vehicle.sts.name, + vehicleType: vehicle.vehicle.vehicleType, + distance: `${vehicle.distance} km`, + tripStartTime: vehicle.tripStartTime, + estimatedDuration: `${vehicle.estimatedDuration} mins`, + estimatedFuelCost: `${parseFloat(vehicle.estimatedFuelCost).toFixed(2)} tk`, + actualDuration: `${parseFloat(vehicle.actualDuration).toFixed(2)} mins`, + tripStatus: vehicle.tripStatus, + capacity: vehicle.vehicle.capacity, + })); + + setVehicleList(AllVehicle); + + return true; + } catch (error: any) { + alert(error.message?.toString() || "Error fetching vehicle list"); + return false; + } + } + + return { vehicleList, VehcileTripCompleteList }; +} diff --git a/client/hooks/stsdata/useGetAllSTS.tsx b/client/hooks/stsdata/useGetAllSTS.tsx new file mode 100644 index 0000000..198b02d --- /dev/null +++ b/client/hooks/stsdata/useGetAllSTS.tsx @@ -0,0 +1,85 @@ +import { useState, useEffect } from 'react'; +import axios from 'axios'; +import { uri } from '@/data/constant'; +type STS = { + id: string, + name: string, + wardNumber: string, + capacity: string, + currentTotalWaste: string, + latitude: string, + longitude: string, + createdAt: string, + updatedAt: string +}; + +type StsShow = { + + lat: number, + lng: number, +}; + +type StsRouteType = { + coordinate: string, + name: string, +} + +export default function useGetAllSTS() { + const [stsList, setSTSList] = useState([]); // Initialize with an empty array of STS objects + const [storagePercentage, setStoragePercentage] = useState([]); + const [stsCoordinate, setStsCoordinate] = useState([]); + const [stsRoute, setStsRoute] = useState([]); + + async function getAllSTS() { + try { + const res = await axios.get('http://localhost:8585/sts'); + // Assuming the response data is an array of STS + const AllSTS: STS[] = res.data.map((data: any) => ({ + id: data.id, + name: data.name, + wardNumber: data.wardNumber, + capacity: data.capacity, + currentTotalWaste: data.currentTotalWaste, + latitude: data.latitude, + longitude: data.longitude, + createdAt: data.createdAt, + updatedAt: data.updatedAt, + })); + + const stsCoordinates: StsShow[] = AllSTS.map((data: STS) => ({ + lat: Number(data.latitude), + lng: Number(data.longitude), + })); + + // Calculate storage percentage for each STS + const percentages: number[] = AllSTS.map((data: STS) => { + const capacity = parseFloat(data.capacity); + const currentTotalWaste = parseFloat(data.currentTotalWaste); + const percentage = (currentTotalWaste / capacity) * 100; + return parseFloat(percentage.toFixed(2)); + }); + + const stsRouteCalc: StsRouteType[] = AllSTS.map((data: STS) => ({ + coordinate: `${data.latitude}, ${data.longitude}`, + name: data.name + })); + + setStsRoute(stsRouteCalc); + + setStsCoordinate(stsCoordinates); + setSTSList(AllSTS); + setStoragePercentage(percentages); + + return true; + } catch (error: any) { + alert(error.message?.toString() || "Error fetching STS list"); + return false; + } + } + useEffect(() => { + getAllSTS(); + }, []); + + + return { stsList, stsRoute, storagePercentage, stsCoordinate, getAllSTS }; +} \ No newline at end of file diff --git a/client/hooks/user_data/addNewRole.ts b/client/hooks/user_data/addNewRole.ts new file mode 100644 index 0000000..bcab66b --- /dev/null +++ b/client/hooks/user_data/addNewRole.ts @@ -0,0 +1,27 @@ +"use client"; +import { UserData } from "@/components/graphs/Data"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + + +export default async function addNewRole(roleName: string) { + + if(!roleName) return "Enter a valid role name."; + try { + const res = await axios.post(apiRoutes.rbac.create, { roleName },{ + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + + if(res) return "Role added successfully"; + } catch (error: any) { + alert("Error creating role... Are you authorized?"); + console.log(error.message); + } + +} diff --git a/client/hooks/user_data/deleteRole.ts b/client/hooks/user_data/deleteRole.ts new file mode 100644 index 0000000..4619a8c --- /dev/null +++ b/client/hooks/user_data/deleteRole.ts @@ -0,0 +1,27 @@ +"use client"; +import { UserData } from "@/components/graphs/Data"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + + +export default async function deleteRole(roleName: string) { + + if(!roleName) return "Enter a valid role name."; + try { + const res = await axios.delete(apiRoutes.rbac.delete + roleName ,{ + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + + if(res) return "Role added successfully"; + } catch (error: any) { + alert("Error creating role... Are you authorized?"); + console.log(error.message); + } + +} diff --git a/client/hooks/user_data/editUser.tsx b/client/hooks/user_data/editUser.tsx index a7aabe4..6265a02 100644 --- a/client/hooks/user_data/editUser.tsx +++ b/client/hooks/user_data/editUser.tsx @@ -17,6 +17,13 @@ export default async function editUser(user: User) { Authorization: `Bearer ${await getCookie(jwtToken)}`, }, }); + const res2 = res.data.roleName !== user.role ? await axios.put(apiRoutes.user.edit + user.id + "/roles", { + roleName: user.role, + }, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }) : null; return "user updated successfully"; } catch (error: any) { return error.message?.toString() || "error updating user. You may not have the required permissions."; diff --git a/client/hooks/user_data/getAllPermissonList.ts b/client/hooks/user_data/getAllPermissonList.ts new file mode 100644 index 0000000..9bf5a50 --- /dev/null +++ b/client/hooks/user_data/getAllPermissonList.ts @@ -0,0 +1,26 @@ +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; + +export async function fetchAllPermissons() { + try { + const token = getCookie(jwtToken); + const response = await axios.get(apiRoutes.rbac.fetchAllPermissons, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + //console.log(response.data); + return response.data.map((permisson: any) => { + return { + name: permisson.name, + description: permisson.description, + }; + }); + } catch (error: any) { + alert("Error fetching permissons... Are you authorized?"); + console.log(error.message); + } +} diff --git a/client/hooks/user_data/updatePermisson.tsx b/client/hooks/user_data/updatePermisson.tsx new file mode 100644 index 0000000..d155dcf --- /dev/null +++ b/client/hooks/user_data/updatePermisson.tsx @@ -0,0 +1,46 @@ +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; + +type EditPermisson = { + role: string; + permission: string; + action: string; +}; + +export async function updatePermisson(payload: EditPermisson) { + try { + const token = getCookie(jwtToken); + let response; + payload.action === "ADD" + ? (response = await axios.post( + apiRoutes.rbac.edit + + payload.role + + "/permissions/" + + payload.permission, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + )) + : (response = await axios.delete( + apiRoutes.rbac.delete + + payload.role + + "/permissions/" + + payload.permission, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + )); + + //console.log(response.data); + return "Permisson updated successfully!"; + } catch (error: any) { + alert("Error updating permissons... Are you authorized?"); + console.log(error.message); + } +} diff --git a/client/hooks/user_data/useEditProfileInfo.tsx b/client/hooks/user_data/useEditProfileInfo.tsx new file mode 100644 index 0000000..df963f4 --- /dev/null +++ b/client/hooks/user_data/useEditProfileInfo.tsx @@ -0,0 +1,39 @@ +import { useState } from 'react'; +import { setCookie, getCookie } from '@/lib/cookieFunctions'; +import axios from 'axios'; +import { jwtToken, stsId } from '@/data/cookieNames'; // Ensure these variables are properly defined +import { apiRoutes } from '@/data/apiRoutes'; + +export default function useEditProfileInfo() { + async function EditProfileInfo(data: { + username: string; + profileName: string; + }) { + + + try { + const editedProfile = { + username: data.username, + profileName: data.profileName, + }; + + + const res = await axios.put( + apiRoutes.profile.edit, + editedProfile, + { + headers: { Authorization: `Bearer ${getCookie(jwtToken)}` }, + } + ); + + + + return true; + } catch (error: any) { + alert(error.message?.toString() || 'Error Editing'); + return false; + } + } + + return { EditProfileInfo }; +} diff --git a/client/hooks/user_data/useGetAllRole.tsx b/client/hooks/user_data/useGetAllRole.tsx index 933d946..4e8f8da 100644 --- a/client/hooks/user_data/useGetAllRole.tsx +++ b/client/hooks/user_data/useGetAllRole.tsx @@ -5,21 +5,43 @@ import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; import { getCookie } from "@/lib/cookieFunctions"; import axios from "axios"; import { useState, useEffect, use } from "react"; +import { set } from "react-hook-form"; -type User = { +type RolesWithPermisson = { id: string; - username: string; - email: string; - role: string; + name: string; + permissions: [{ + name: string; + description: string; +}] }; export default function useGetAllRole() { const [roles, setRoles] = useState([]); + const [rolesWithPermissions, setRolesWithPermissions] = useState([]); async function fetchAllRoles() { try { - await setRoles([unassigned, admin, landfillManager, stsManager]); - console.log(roles); + const token = getCookie(jwtToken); + const response = await axios.get(apiRoutes.rbac.getAllRolesWithPermisson, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + setRolesWithPermissions(response.data.map((role: RolesWithPermisson) => { + return { + id: role.id, + name: role.name, + permissions: role.permissions.map((permission: any) => { + return { + name: permission.name, + description: permission.description, + }; + }, + )}; + })); + await setRoles([unassigned, admin, landfillManager, stsManager]); + // console.log(roles); } catch (error: any) { alert("Error fetching roles... Are you authorized?"); console.log(error.message); @@ -27,8 +49,9 @@ export default function useGetAllRole() { } useEffect(() => { + fetchAllRoles(); }, []); - return {fetchAllRoles, roles}; + return {fetchAllRoles, roles, rolesWithPermissions}; } diff --git a/client/hooks/user_data/useGetUserProfile.tsx b/client/hooks/user_data/useGetUserProfile.tsx index 5227fbe..a0dd207 100644 --- a/client/hooks/user_data/useGetUserProfile.tsx +++ b/client/hooks/user_data/useGetUserProfile.tsx @@ -1,35 +1,205 @@ + + + import { useState, useEffect } from 'react'; +import axios from 'axios'; +import { apiRoutes } from '@/data/apiRoutes'; // Adjust the import path +import { jwtToken } from '@/data/cookieNames'; // Adjust the import path +import { getCookie } from '@/lib/cookieFunctions'; // Adjust the import path + +type User = { + id: string; + username: string; + email: string; + profileName: string; + roleName: string; + roleDescription: string; + + userCreated: string; + userUpdated: string; + + +}; + +type STSType = { + stsId: string ; + stsName: string; + stsWardNumber: string; + stsCapacity: string; + stsCurrentTotalWaste: string; + stsLatitude: string; + stsLongitude: string; + + roleCreated: string; + roleUpdated: string; +} + +type LandfillType = { + landfillId: string ; + landFillName: string; + landFillCapacity: string; + landFillCurrentWaste: string; + landfillLatitude: string; + landFillLongitude: string; -export default function useGetUserProfile(userId: string) { + roleCreated: string; + roleUpdated: string; +} - const [userData, setUserData] = useState({ - email: "", - role: "", - name: "", - assignedArea: "", +export default function useGetUserProfile() { + const [user, setUser] = useState({ + id: '', + username: '', + email: '', + profileName: '', + roleName: '', + roleDescription: '', + userCreated: '', + userUpdated: '' + }); // Initialize with undefined + const [stsDetails, setStsDetails] = useState({ + stsId: '', + stsName: '', + stsWardNumber: '', + stsCapacity: '', + stsCurrentTotalWaste: '', + stsLatitude: '', + stsLongitude: '', + roleCreated: '', + roleUpdated: '' }); - useEffect(() => { - fetchUser(); - }, []); - - async function fetchUser() { - - setUserData({ - email: userId + "@gmail.com", - role: "STS Manager", - name: "Mehrajul Islam", - assignedArea: "Gulshan-1, Dhaka", - }); - - if (userData) { - // Call the API + const [landfillDetails, setLandfillDetails] = useState({ + landfillId: '', + landFillName: '', + landFillCapacity: '', + landFillCurrentWaste: '', + landfillLatitude: '', + landFillLongitude: '', + roleCreated: '', + roleUpdated: '' + }); + + async function getUserDetails() { + try { + const res = await axios.get(apiRoutes.profile.getProfile, { + headers: { Authorization: `Bearer ${getCookie(jwtToken)}` }, + + }); + // console.log(res.data); + if (res.data.roleName === "STS_MANAGER" ) { + const userDetails: User = { + id: res.data.id, + username: res.data.username, + email: res.data.email, + profileName: res.data.profileName, + roleName: res.data.roleName, + roleDescription: res.data.role.description, + + userCreated: res.data.createdAt, + userUpdated: res.data.updatedAt + + + + }; + + if(res.data.stsId){ + const ResStsDetails: STSType = { + stsId: res.data.sts.id, + stsName: res.data.sts.name, + stsWardNumber: res.data.sts.wardNumber, + stsCapacity: res.data.sts.capacity, + stsCurrentTotalWaste: res.data.sts.currentTotalWaste, + stsLatitude: res.data.sts.latitude, + stsLongitude: res.data.sts.longitude, + + roleCreated: res.data.role.createdAt, + roleUpdated: res.data.role.updatedAt + } + setStsDetails(ResStsDetails); + } + - return userData; - } - - return null; + + setUser(userDetails); + + } else if(res.data.roleName === "LAND_MANAGER" ) { + + const userDetails: User = { + id: res.data.id, + username: res.data.username, + email: res.data.email, + profileName: res.data.profileName, + roleName: res.data.roleName, + roleDescription: res.data.role.description, + + userCreated: res.data.createdAt, + userUpdated: res.data.updatedAt + }; + + if(res.data.landfillId){ + const ResLandDetails: LandfillType = { + + landfillId: res.data.landfill.id, + landFillName: res.data.landfill.name, + landFillCapacity: res.data.landfill.capacity, + landFillCurrentWaste: res.data.landfill.currentTotalWaste, + landfillLatitude: res.data.landfill.latitude, + landFillLongitude: res.data.landfill.longitude, + + roleCreated: res.data.role.createdAt, + roleUpdated: res.data.role.updatedAt + }; + setLandfillDetails(ResLandDetails); + + } + setUser(userDetails); + + }else if(res.data.roleName === "SYSTEM_ADMIN" ) { + + const userDetails: User = { + id: res.data.id, + username: res.data.username, + email: res.data.email, + profileName: res.data.profileName, + roleName: res.data.roleName, + roleDescription: res.data.role.description, + + userCreated: res.data.createdAt, + userUpdated: res.data.updatedAt + }; + + + setUser(userDetails); + + }else{ + const userDetails: User = { + id: res.data.id, + username: res.data.username, + email: res.data.email, + profileName: res.data.profileName, + roleName: res.data.roleName, + roleDescription: res.data.role.description, + + userCreated: res.data.createdAt, + userUpdated: res.data.updatedAt + }; + setUser(userDetails); + + } + + + + } catch (error: any) { + alert(error.message?.toString() || 'Error fetching user profile'); // Updated error message + } } - return {userData}; -} \ No newline at end of file + // useEffect(() => { + // console.log(user); + // console.log(stsDetails); + // console.log(landfillDetails) + // }, [user, stsDetails, landfillDetails]); // Call getUserDetails when the component mounts + + return { user, stsDetails, landfillDetails, getUserDetails }; +} diff --git a/client/hooks/vehicles/deleteVehiclebyId.ts b/client/hooks/vehicles/deleteVehiclebyId.ts new file mode 100644 index 0000000..1e82181 --- /dev/null +++ b/client/hooks/vehicles/deleteVehiclebyId.ts @@ -0,0 +1,23 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; + +export default async function deleteVehiclebyId(id: string) { + if (id) { + try { + const res = await axios.delete(apiRoutes.vehicle.delete + id, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + return "vehicle removed successfully"; + } catch (error: any) { + return error.message?.toString() || "error removing vehicle"; + } + } + + return null; +} diff --git a/client/hooks/vehicles/editVehicle.ts b/client/hooks/vehicles/editVehicle.ts new file mode 100644 index 0000000..5ea0613 --- /dev/null +++ b/client/hooks/vehicles/editVehicle.ts @@ -0,0 +1,50 @@ +import { User } from "@/components/dataTables/UserList"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import axios from "axios"; +import { STS } from "@/components/modals/stsControl/EditSTSInfoModal"; + +type Vehicle = { + id: string; + vehicleNumber: string; + vehicleType: string; + capacity: string; + loadedFuelCostPerKm: string; + unloadedFuelCostPerKm: string; + landFillId: string; + landFillName: string; +}; + +export default async function editVehicle(vehicleData: Vehicle) { + if (vehicleData) { + try { + const payload = { + vehicleNumber: vehicleData.vehicleNumber, + vehicleType: vehicleData.vehicleType, + capacity: vehicleData.capacity, + loadedFuelCostPerKm: vehicleData.loadedFuelCostPerKm, + unloadedFuelCostPerKm: vehicleData.unloadedFuelCostPerKm, + landFillId: vehicleData.landFillId, + }; + const res2 = await axios.put( + apiRoutes.vehicle.edit + vehicleData.id, + payload, + { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + } + ); + return "vehicle updated successfully"; + } catch (error: any) { + return ( + error.message?.toString() || + "error updating vehicle. You may not have the required permissions." + ); + } + } + + return null; +} diff --git a/client/hooks/vehicles/useGetAllVehicleList.tsx b/client/hooks/vehicles/useGetAllVehicleList.tsx new file mode 100644 index 0000000..9f4c8c9 --- /dev/null +++ b/client/hooks/vehicles/useGetAllVehicleList.tsx @@ -0,0 +1,60 @@ +import { useState } from "react"; +import axios from "axios"; +import { uri } from "@/data/constant"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; + +type Vehicle = { + entryId: string; + id: string; + vehicleNumber: string; + vehicleType: string; + capacity: string; + loadedFuelCostPerKm: string; + unloadedFuelCostPerKm: string; + landFillId: string; + entryTime: string; + landFillName: string; + stsLattitude: string; + stsLongitude: string; + landfillLattitude: string; + landfillLongitude: string; +}; + +export default function useGetAllVehicleList() { + const [vehicleList, setVehicleList] = useState([]); // Initialize with an empty array of Vehicle objects + const [vehicleNumberList, setVehicleNumberList] = useState([]); + + async function getVehicleList() { + try { + const res = await axios.get(apiRoutes.vehicle.getAll, { + headers: { Authorization: `Bearer ${getCookie(jwtToken)}` }, + }); + // Assuming the response data is an array of vehicles + const AllVehicle: Vehicle[] = res.data.map((vehicle: any) => ({ + id: vehicle.id, + vehicleNumber: vehicle.vehicleNumber, + vehicleType: vehicle.vehicleType, + capacity: vehicle.capacity, + loadedFuelCostPerKm: vehicle.loadedFuelCostPerKm, + unloadedFuelCostPerKm: vehicle.unloadedFuelCostPerKm, + landFillId: vehicle.landFillId, + landFillName: vehicle.landFill.name, + })); + const vehicleNumbers = res.data.map( + (vehicle: Vehicle) => vehicle.vehicleNumber + ); + + setVehicleList(AllVehicle); + setVehicleNumberList(vehicleNumbers); + + return true; + } catch (error: any) { + alert(error.message?.toString() || "Error fetching vehicle list"); + return false; + } + } + + return { vehicleList, vehicleNumberList, getVehicleList }; +} diff --git a/client/lib/formatTimestamp.ts b/client/lib/formatTimestamp.ts new file mode 100644 index 0000000..5cc0892 --- /dev/null +++ b/client/lib/formatTimestamp.ts @@ -0,0 +1,14 @@ +export default function formatTimestamp(timestamp : string) { + const date = new Date(timestamp); + let hours = date.getHours(); + let minutes:(string | number) = date.getMinutes(); + const ampm = hours >= 12 ? 'PM' : 'AM'; + + hours = hours % 12; + hours = hours ? hours : 12; // the hour '0' should be '12' + minutes = minutes < 10 ? '0'+ minutes : minutes; + + const strTime = hours + '.' + minutes + ' ' + ampm; + return strTime; + } + \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 003a621..02ed085 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -12,6 +12,9 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", + "@mantine/charts": "^7.7.1", + "@mantine/core": "^7.7.1", + "@mantine/hooks": "^7.7.1", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", @@ -19,6 +22,7 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", @@ -36,10 +40,14 @@ "next-themes": "^0.3.0", "react": "^18", "react-chartjs-2": "^5.2.0", + "react-datepicker": "^6.6.0", "react-dom": "^18", + "react-google-recaptcha": "^3.1.0", "react-hook-form": "^7.51.1", "react-icons": "^5.0.1", "react-simple-captcha": "^9.3.1", + "react-to-print": "^2.15.1", + "recharts": "^2.12.3", "sonner": "^1.4.41", "tailwind-merge": "^2.2.2", "tailwindcss-animate": "^1.0.7", @@ -49,11 +57,15 @@ "@hookform/resolvers": "^3.3.4", "@types/node": "^20", "@types/react": "^18", + "@types/react-datepicker": "^6.2.0", "@types/react-dom": "^18", + "@types/react-google-recaptcha": "^2.1.9", "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.1.4", - "postcss": "^8", + "postcss": "^8.4.38", + "postcss-preset-mantine": "^1.13.0", + "postcss-simple-vars": "^7.0.1", "tailwindcss": "^3.3.0", "typescript": "^5" } @@ -1621,6 +1633,20 @@ "@floating-ui/utils": "^0.2.0" } }, + "node_modules/@floating-ui/react": { + "version": "0.26.10", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.10.tgz", + "integrity": "sha512-sh6f9gVvWQdEzLObrWbJ97c0clJObiALsFe0LiR/kb3tDRKwEhObASEH2QyfdoO/ZBPzwxa9j+nYFo+sqgbioA==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@floating-ui/utils": "^0.2.0", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@floating-ui/react-dom": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", @@ -1786,6 +1812,79 @@ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" }, + "node_modules/@mantine/charts": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@mantine/charts/-/charts-7.7.1.tgz", + "integrity": "sha512-7FMsTQcEQZrX5WAxzhvH/yWBwAJ2oVaZga9f5D80dyCtVgSQt0aRYSQejiMpgYCcaohCVSdQ+TL1YS5nZLvjog==", + "peerDependencies": { + "@mantine/core": "7.7.1", + "@mantine/hooks": "7.7.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "recharts": "^2.10.3" + } + }, + "node_modules/@mantine/core": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.7.1.tgz", + "integrity": "sha512-SdPzjvqvEK7uHFuVD3a8w3OZyQVoCwIXLSUfOtRNouDMQgsq6Ac7QjKXBBOk3wNweOWFVOU1vATLHobSmow0lQ==", + "dependencies": { + "@floating-ui/react": "^0.26.9", + "clsx": "2.1.0", + "react-number-format": "^5.3.1", + "react-remove-scroll": "^2.5.7", + "react-textarea-autosize": "8.5.3", + "type-fest": "^4.12.0" + }, + "peerDependencies": { + "@mantine/hooks": "7.7.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/core/node_modules/react-remove-scroll": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.9.tgz", + "integrity": "sha512-bvHCLBrFfM2OgcrpPY2YW84sPdS2o2HKWJUf1xGyGLnSoEnOTOBpahIarjRuYtN0ryahCeP242yf+5TrBX/pZA==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mantine/core/node_modules/type-fest": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.14.0.tgz", + "integrity": "sha512-on5/Cw89wwqGZQu+yWO0gGMGu8VNxsaW9SB2HE8yJjllEk7IDTwnSN1dUVldYILhYPN5HzD7WAaw2cc/jBfn0Q==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mantine/hooks": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.7.1.tgz", + "integrity": "sha512-3YH2FzKMlg840tb04PBDcDXyBCi9puFOxEBVgc6Y/pN6KFqfOoAnQE/YvgOtwSNXZlbTWyDlQoYj+3je7pA7og==", + "peerDependencies": { + "react": "^18.2.0" + } + }, "node_modules/@next/env": { "version": "14.1.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.4.tgz", @@ -2510,6 +2609,38 @@ } } }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.1.3.tgz", + "integrity": "sha512-x+yELayyefNeKeTx4fjK6j99Fs6c4qKm3aY38G3swQVTN6xMpsrbigC0uHs2L//g8q4qR7qOcww8430jJmi2ag==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-roving-focus": "1.0.4", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", @@ -2871,6 +3002,60 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", + "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, "node_modules/@types/google.maps": { "version": "3.55.2", "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.55.2.tgz", @@ -2926,6 +3111,17 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-datepicker": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-6.2.0.tgz", + "integrity": "sha512-+JtO4Fm97WLkJTH8j8/v3Ldh7JCNRwjMYjRaKh4KHH0M3jJoXtwiD3JBCsdlg3tsFIw9eQSqyAPeVDN2H2oM9Q==", + "dev": true, + "dependencies": { + "@floating-ui/react": "^0.26.2", + "@types/react": "*", + "date-fns": "^3.3.1" + } + }, "node_modules/@types/react-dom": { "version": "18.2.22", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz", @@ -2935,6 +3131,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-google-recaptcha": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@types/react-google-recaptcha/-/react-google-recaptcha-2.1.9.tgz", + "integrity": "sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", @@ -3823,6 +4028,116 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -3880,6 +4195,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.10", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", @@ -3902,6 +4226,11 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3998,6 +4327,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -4680,11 +5018,24 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -5308,6 +5659,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -5871,6 +6230,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6537,6 +6901,28 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/postcss-mixins": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-9.0.4.tgz", + "integrity": "sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "postcss-js": "^4.0.0", + "postcss-simple-vars": "^7.0.0", + "sugarss": "^4.0.1" + }, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, "node_modules/postcss-nested": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", @@ -6555,6 +6941,19 @@ "postcss": "^8.2.14" } }, + "node_modules/postcss-preset-mantine": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.13.0.tgz", + "integrity": "sha512-1bv/mQz2K+/FixIMxYd83BYH7PusDZaI7LpUtKbb1l/5N5w6t1p/V9ONHfRJeeAZyfa6Xc+AtR+95VKdFXRH1g==", + "dev": true, + "dependencies": { + "postcss-mixins": "^9.0.4", + "postcss-nested": "^6.0.1" + }, + "peerDependencies": { + "postcss": ">=8.0.0" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.16", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", @@ -6567,6 +6966,22 @@ "node": ">=4" } }, + "node_modules/postcss-simple-vars": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", + "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", + "dev": true, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.1" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -6654,6 +7069,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-chartjs-2": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", @@ -6674,6 +7101,22 @@ "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-datepicker": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-6.6.0.tgz", + "integrity": "sha512-ERC0/Q4pPC9bNIcGUpdCbHc+oCxhkU3WI3UOGHkyJ3A9fqALCYpEmLc5S5xvAd7DuCDdbsyW97oRPM6pWWwjww==", + "dependencies": { + "@floating-ui/react": "^0.26.2", + "clsx": "^2.1.0", + "date-fns": "^3.3.1", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -6713,6 +7156,18 @@ } } }, + "node_modules/react-google-recaptcha": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz", + "integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, "node_modules/react-hook-form": { "version": "7.51.1", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.1.tgz", @@ -6741,6 +7196,31 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-number-format": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.4.tgz", + "integrity": "sha512-2hHN5mbLuCDUx19bv0Q8wet67QqYK6xmtLQeY5xx+h7UXiMmRtaCwqko4mMPoKXLc6xAzwRrutg8XbTRlsfjRg==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-onclickoutside": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", + "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } + }, "node_modules/react-remove-scroll": { "version": "2.5.5", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", @@ -6808,6 +7288,20 @@ "react": "^0.14.0 || ^15.0.0 || ^16.0.0-0" } }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -6830,6 +7324,46 @@ } } }, + "node_modules/react-textarea-autosize": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", + "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-to-print": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.15.1.tgz", + "integrity": "sha512-1foogIFbCpzAVxydkhBiDfMiFYhIMphiagDOfcG4X/EcQ+fBPqJ0rby9Wv/emzY1YLkIQy/rEgOrWQT+rBKhjw==", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -6862,6 +7396,36 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.3.tgz", + "integrity": "sha512-vE/F7wTlokf5mtCqVDJlVKelCjliLSJ+DJxj79XlMREm7gpV7ljwbrwE3CfeaoDlOaLX+6iwHaVRn9587YkwIg==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", @@ -7451,6 +8015,22 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sugarss": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-4.0.1.tgz", + "integrity": "sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, "node_modules/supercluster": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", @@ -7482,6 +8062,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tailwind-merge": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", @@ -7825,6 +8410,43 @@ } } }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sidecar": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", @@ -7851,6 +8473,27 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/client/package.json b/client/package.json index 43872d3..e9b8b98 100644 --- a/client/package.json +++ b/client/package.json @@ -13,6 +13,9 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", + "@mantine/charts": "^7.7.1", + "@mantine/core": "^7.7.1", + "@mantine/hooks": "^7.7.1", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", @@ -20,6 +23,7 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", @@ -37,10 +41,14 @@ "next-themes": "^0.3.0", "react": "^18", "react-chartjs-2": "^5.2.0", + "react-datepicker": "^6.6.0", "react-dom": "^18", + "react-google-recaptcha": "^3.1.0", "react-hook-form": "^7.51.1", "react-icons": "^5.0.1", "react-simple-captcha": "^9.3.1", + "react-to-print": "^2.15.1", + "recharts": "^2.12.3", "sonner": "^1.4.41", "tailwind-merge": "^2.2.2", "tailwindcss-animate": "^1.0.7", @@ -50,11 +58,15 @@ "@hookform/resolvers": "^3.3.4", "@types/node": "^20", "@types/react": "^18", + "@types/react-datepicker": "^6.2.0", "@types/react-dom": "^18", + "@types/react-google-recaptcha": "^2.1.9", "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.1.4", - "postcss": "^8", + "postcss": "^8.4.38", + "postcss-preset-mantine": "^1.13.0", + "postcss-simple-vars": "^7.0.1", "tailwindcss": "^3.3.0", "typescript": "^5" } diff --git a/client/postcss.config.cjs b/client/postcss.config.cjs new file mode 100644 index 0000000..069b052 --- /dev/null +++ b/client/postcss.config.cjs @@ -0,0 +1,14 @@ +module.exports = { + plugins: { + 'postcss-preset-mantine': {}, + 'postcss-simple-vars': { + variables: { + 'mantine-breakpoint-xs': '36em', + 'mantine-breakpoint-sm': '48em', + 'mantine-breakpoint-md': '62em', + 'mantine-breakpoint-lg': '75em', + 'mantine-breakpoint-xl': '88em', + }, + }, + }, + }; \ No newline at end of file diff --git a/client/public/profileBack.png b/client/public/profileBack.png new file mode 100644 index 0000000..2889ec5 Binary files /dev/null and b/client/public/profileBack.png differ diff --git a/client/tsconfig.json b/client/tsconfig.json index e7ff90f..7c7c258 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, + "allowJs": true, "skipLibCheck": true, "strict": true, "noEmit": true, @@ -12,6 +12,8 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, + "downlevelIteration": true, + "target": "es2015", "plugins": [ { "name": "next" diff --git a/server/src/controllers/auth.ts b/server/src/controllers/auth.ts index 3487057..e17f06b 100644 --- a/server/src/controllers/auth.ts +++ b/server/src/controllers/auth.ts @@ -54,7 +54,11 @@ const login = errorWrapper( const user = await prisma.user.findUnique({ where: { - email, + email, + }, + include: { + landfill: true, + sts: true, }, }); diff --git a/server/src/controllers/bills.ts b/server/src/controllers/bills.ts index cf8489b..3dafedb 100644 --- a/server/src/controllers/bills.ts +++ b/server/src/controllers/bills.ts @@ -1,4 +1,4 @@ -import { Bill, PrismaClient } from "@prisma/client"; +import { Bill, Prisma, PrismaClient } from "@prisma/client"; import { Request, Response } from "express"; import errorWrapper from "../middlewares/errorWrapper"; import { @@ -8,6 +8,9 @@ import { getBills, updateBill, } from "../services/billServices"; +import { getTripById } from "../services/tripServices"; +import CustomError from "../services/CustomError"; +import { TripStatus } from "../types/tripStatus"; const prisma = new PrismaClient(); @@ -61,4 +64,95 @@ const removeBill = errorWrapper( { statusCode: 500, message: "Couldn't delete bill" } ); -export { createBill, fetchBills, fetchBill, editBill, removeBill }; +const createBillFromTrip = errorWrapper( + async (req: Request, res: Response) => { + const { tripId, allocatedFuelCost } = req.body; + + const trip = await prisma.trip + .update({ + where: { + id: tripId, + }, + data: { + tripStatus: TripStatus.BILLED, + }, + }) + .catch((err) => { + throw new CustomError("Couldn't update trip", 500); + }); + + if (!trip) { + throw new CustomError("No such trip found", 404); + } + + const bill = await prisma.bill.create({ + data: { + vehicleId: trip.vehicleId, + stsId: trip.stsId, + landfillId: trip.landfillId, + tripId: trip.id, + weightOfWaste: trip.weightOfWaste, + allocatedFuelCost, + }, + include: { + trip: true, + vehicle: true, + sts: true, + landfill: true, + }, + }); + + res.status(201).json(bill); + }, + { statusCode: 500, message: "Couldn't create bill" } +); + +const getListOfBills = errorWrapper(async (req: Request, res: Response) => { + const { landfillId, day, billNo } = req.query; + + const billNumber = parseInt(billNo as string, 10); + + let where: Prisma.BillWhereInput | undefined = undefined; + + if (landfillId || day || billNo) { + where = {}; + if (landfillId) { + where.landfillId = landfillId as string; + } + if (day) { + const days = parseInt(day as string, 10); + + const thresholdDate = new Date(); + thresholdDate.setDate(thresholdDate.getDate() - days); + + where.createdAt = { + gte: thresholdDate.toISOString(), + }; + } + + if (billNo) { + where.billNo = billNumber as number; + } + } + + const bills = await prisma.bill.findMany({ + where, + include: { + trip: true, + vehicle: true, + sts: true, + landfill: true, + }, + }); + res.json(bills); +}); + +export { + createBill, + fetchBills, + fetchBill, + editBill, + removeBill, + createBillFromTrip, + getListOfBills, +}; diff --git a/server/src/controllers/createTrip.ts b/server/src/controllers/createTrip.ts deleted file mode 100644 index 84bbac8..0000000 --- a/server/src/controllers/createTrip.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Request, Response } from "express"; -import errorWrapper from "../middlewares/errorWrapper"; -import { addTrip } from "../services/tripServices"; -import CustomError from "../services/CustomError"; -import { getStsVehicleEntryById } from "../services/stsVehicle"; - -const createTrip = errorWrapper(async (req: Request, res: Response) => { - const { stsVehicleId, weightOfWaste, exitTime, distance, estimatedDuration } = - req.body; - - const stsVehicleInfo = await getStsVehicleEntryById(stsVehicleId); - - if (!stsVehicleInfo) { - throw new CustomError("No such Vehicle entry found", 404); - } - - const vehicle = stsVehicleInfo.vehicle; - const sts = stsVehicleInfo.sts; - - const unloadedFuelCostPerKm = Number(vehicle.unloadedFuelCostPerKm); - const loadedFuelCostPerKm = Number(vehicle.loadedFuelCostPerKm); - const capacity = Number(vehicle.capacity); - const estimatedCost = - unloadedFuelCostPerKm + - (weightOfWaste / capacity) * (loadedFuelCostPerKm - unloadedFuelCostPerKm); - - // const trip = { - // weightOfWaste, - // distance, - // estimatedDuration, - // estimatedFuelCost: new Prisma.Decimal(estimatedCost), - // tripStatus: "PENDING", - // }; - const newTrip = await addTrip(trip); - // res.status(201).json(newTrip); -}); diff --git a/server/src/controllers/landfills.ts b/server/src/controllers/landfills.ts index 2da1cc8..713d20b 100644 --- a/server/src/controllers/landfills.ts +++ b/server/src/controllers/landfills.ts @@ -19,7 +19,11 @@ const addlandfill = errorWrapper( const getAllLandfills = errorWrapper( async (req: Request, res: Response) => { - const landfills = await prisma.landfill.findMany({}); + const landfills = await prisma.landfill.findMany({ + include: { + manager: true, + }, + }); res.status(200).json(landfills); }, { statusCode: 500, message: "Couldn't fetch landfills" } @@ -32,13 +36,35 @@ const getLandfillById = errorWrapper( where: { id: landfillId, }, - }); + }); + + if (!landfill) { + throw new CustomError("Landfill not found", 404); + } - res.status(200).json(landfill); + const percentage = await calculatePercentage(landfill); + + res.status(200).json({ landfill, graphData: percentage }); }, { statusCode: 404, message: "Landfill not found" } ); +async function calculatePercentage(landfill: Landfill) { + // Perform calculation to get the percentage + // Return the calculated percentage + var mot: number = parseInt(landfill?.capacity?.toString() || "") || 0; + var ase: number = parseInt(landfill?.currentTotalWaste?.toString() || "") || 0; + + const graphData = { + empty: mot - ase, + full: ase, + emptyPercentage: ((mot - ase) / mot) * 100, + fullPercentage: (ase / mot) * 100, + }; + + return graphData; // Replace with the actual calculation +} + const updateLandfill = errorWrapper( async (req: Request, res: Response) => { const { landfillId } = req.params; diff --git a/server/src/controllers/profile.ts b/server/src/controllers/profile.ts index 38661a9..5ccc7de 100644 --- a/server/src/controllers/profile.ts +++ b/server/src/controllers/profile.ts @@ -15,6 +15,8 @@ const getUserById = errorWrapper( }, include: { role: true, + sts: true, + landfill: true }, }); diff --git a/server/src/controllers/rbac.ts b/server/src/controllers/rbac.ts index 0a7451e..a5d3612 100644 --- a/server/src/controllers/rbac.ts +++ b/server/src/controllers/rbac.ts @@ -1,12 +1,20 @@ import errorWrapper from "../middlewares/errorWrapper"; import { Request, Response } from "express"; import { PrismaClient } from "@prisma/client"; +import CustomError from "../services/CustomError"; const prisma = new PrismaClient(); const getAllRoles = errorWrapper( async (req: Request, res: Response) => { - const roles = await prisma.role.findMany({}); + const roles = await prisma.role.findMany({ + include: { + User: true, + }, + }); + + // const roleNames = roles.map((role) => role.name); + res.json(roles); }, { statusCode: 500, message: "Couldn't fetch roles" } @@ -25,4 +33,198 @@ const getUsersByRole = errorWrapper( { statusCode: 500, message: "Couldn't fetch users" } ); -export { getAllRoles, getUsersByRole }; +const addRole = errorWrapper( + async (req: Request, res: Response) => { + const { roleName } = req.body; + const role = await prisma.role.create({ + data: { + name: roleName, + }, + }); + res.status(201).json(role); + }, + { statusCode: 500, message: "Couldn't add role" } +); + +const deleteRole = errorWrapper( + async (req: Request, res: Response) => { + const { roleName } = req.params; + const role = await prisma.role.delete({ + where: { + name: roleName, + }, + }); + res.json(role); + }, + { statusCode: 500, message: "Couldn't delete role" } +); + +const getPermissions = errorWrapper( + async (req: Request, res: Response) => { + const permissions = await prisma.permission.findMany({}); + + const permissionNames = permissions; + + res.json(permissionNames); + }, + { statusCode: 500, message: "Couldn't fetch permissions" } +); + +const addPermission = errorWrapper( + async (req: Request, res: Response) => { + const { permissionName } = req.body; + const permission = await prisma.permission.create({ + data: { + name: permissionName, + }, + }); + res.status(201).json(permission); + }, + { statusCode: 500, message: "Couldn't add permission" } +); + +const addPermissionToRole = errorWrapper( + async (req: Request, res: Response) => { + const { roleName, permissionName } = req.params; + + const role = await prisma.role.findUnique({ + where: { + name: roleName, + }, + }); + + if (!role) { + throw new CustomError("Role not found", 404); + } + + const permission = await prisma.permission.findUnique({ + where: { + name: permissionName, + }, + }); + + if (!permission) { + throw new CustomError("Permission not found", 404); + } + + const newRole = await prisma.role.update({ + where: { + name: roleName, + }, + data: { + permissions: { + connect: { + id: permission.id, + }, + }, + }, + include: { + permissions: true, + }, + }); + + res.json(newRole); + }, + { statusCode: 500, message: "Couldn't add permission to role" } +); + +const removePermissionFromRole = errorWrapper( + async (req: Request, res: Response) => { + const { roleName, permissionName } = req.params; + const role = await prisma.role.findUnique({ + where: { + name: roleName, + }, + }); + + if (!role) { + throw new CustomError("Role not found", 404); + } + + const permission = await prisma.permission.findUnique({ + where: { + name: permissionName, + }, + }); + + if (!permission) { + throw new CustomError("Permission not found", 404); + } + + const rolePermission = await prisma.role.update({ + where: { + name: roleName, + }, + data: { + permissions: { + disconnect: { + id: permission.id, + }, + }, + }, + include: { + permissions: true, + }, + }); + + res.json(rolePermission); + }, + { statusCode: 500, message: "Couldn't remove permission from role" } +); + +const getAllRolePermissions = errorWrapper( + async (req: Request, res: Response) => { + const rolePermissions = await prisma.role.findMany({ + include: { + permissions: true, + }, + }); + + res.json(rolePermissions); + }, + { statusCode: 500, message: "Couldn't fetch role permissions" } +); + +const getRolesFromPermission = errorWrapper( + async (req: Request, res: Response) => { + const { permissionName } = req.params; + + const permission = await prisma.permission.findUnique({ + where: { + name: permissionName, + }, + }); + + if (!permission) { + throw new CustomError("Permission not found", 404); + } + + const roles = await prisma.role.findMany({ + where: { + permissions: { + some: { + id: permission.id, + }, + }, + }, + }); + + let roleNames = roles.map((role) => role.name); + + res.json(roleNames); + }, + { statusCode: 500, message: "Couldn't fetch roles" } +); + +export { + getAllRoles, + getUsersByRole, + addRole, + deleteRole, + getPermissions, + addPermission, + addPermissionToRole, + removePermissionFromRole, + getRolesFromPermission, + getAllRolePermissions, +}; diff --git a/server/src/controllers/roles.ts b/server/src/controllers/roles.ts deleted file mode 100644 index e69de29..0000000 diff --git a/server/src/controllers/sts.ts b/server/src/controllers/sts.ts index ea9c116..c49973c 100644 --- a/server/src/controllers/sts.ts +++ b/server/src/controllers/sts.ts @@ -20,7 +20,11 @@ const addSTS = errorWrapper( const getAllSTS = errorWrapper( async (req: Request, res: Response) => { - const stss = await prisma.sTS.findMany({}); + const stss = await prisma.sTS.findMany({ + include: { + manager: true, + }, + }); res.json(stss); }, { statusCode: 500, message: "Couldn't fetch stss" } diff --git a/server/src/controllers/trip.ts b/server/src/controllers/trip.ts index ab24ec9..6093a4d 100644 --- a/server/src/controllers/trip.ts +++ b/server/src/controllers/trip.ts @@ -10,6 +10,7 @@ import { getStsVehicleEntryById, updateStsVehicleEntry, } from "../services/stsVehicle"; +import { TripStatus } from "../types/tripStatus"; const prisma = new PrismaClient(); @@ -50,8 +51,9 @@ const createTrip = errorWrapper(async (req: Request, res: Response) => { weightOfWaste, distance, estimatedDuration, + tripStartTime: exitTime, estimatedFuelCost: new Prisma.Decimal(estimatedCost), - tripStatus: "PENDING", + tripStatus: TripStatus.PENDING, }; const newTrip = await addTrip(trip as Trip); @@ -59,24 +61,44 @@ const createTrip = errorWrapper(async (req: Request, res: Response) => { }); const getListOfTrips = errorWrapper(async (req: Request, res: Response) => { - const { tripStatus } = req.query; + + const { tripStatus, landfillId } = req.query; let where: Prisma.TripWhereInput | undefined = undefined; - if (tripStatus) { - where = { - tripStatus: tripStatus as string, - }; + + if (tripStatus || landfillId) { + where = {}; + if (tripStatus) { + where.tripStatus = tripStatus as string; + } + if (landfillId) { + where.landfillId = landfillId as string; + } } const trips = await prisma.trip.findMany({ where, + include: { + sts: true, + landfill: true, + vehicle: true, + }, }); res.json(trips); }); const completeTrip = errorWrapper(async (req: Request, res: Response) => { - const { tripId, landfillId, vehicleId, weightOfWaste, entryTime } = req.body; + const { tripId, weightOfWaste, entryTime } = req.body; + + const trip = await getTripById(tripId); + + if (!trip) { + throw new CustomError("No such trip found", 404); + } + + const landfillId = trip.landfillId; + const vehicleId = trip.vehicleId; prisma.landfillVehicleEntry.create({ data: { @@ -87,24 +109,25 @@ const completeTrip = errorWrapper(async (req: Request, res: Response) => { }, }); - const trip = await getTripById(tripId); + const shortage = Number(trip.weightOfWaste) - weightOfWaste; - if (!trip) { - throw new CustomError("No such trip found", 404); - } + const tripStartTime = new Date(trip.tripStartTime as Date); - const shortage = Number(trip.weightOfWaste) - weightOfWaste; + const entryTimeConverted = new Date(entryTime); - // calculate actual duration + let duration = + (entryTimeConverted.getTime() - tripStartTime.getTime()) / (1000 * 60); const completedTrip = await prisma.trip.update({ where: { id: tripId, }, data: { - tripStatus: "COMPLETED", + tripStatus: TripStatus.DELIVERED, weightOfWaste, shortage, + tripEndTime: entryTime, + actualDuration: duration, }, }); diff --git a/server/src/controllers/users.ts b/server/src/controllers/users.ts index 564b6d5..7ed16bd 100644 --- a/server/src/controllers/users.ts +++ b/server/src/controllers/users.ts @@ -28,6 +28,8 @@ const getUserById = errorWrapper( }, include: { role: true, + sts: true, + landfill: true, }, }); diff --git a/server/src/middlewares/authorizer.ts b/server/src/middlewares/authorizer.ts new file mode 100644 index 0000000..85ca73a --- /dev/null +++ b/server/src/middlewares/authorizer.ts @@ -0,0 +1,31 @@ +import { Request, Response, NextFunction } from "express"; +import errorWrapper from "./errorWrapper"; +import CustomError from "../services/CustomError"; +import { getPermittedRoleNames } from "../permissions/permissions"; + +const authRole = (roles: string[]) => { + return errorWrapper( + async (req: Request, res: Response, next: NextFunction) => { + const userRole = ((req as any).user as any).role; + if (!roles.includes(userRole)) { + throw new CustomError("Unauthorized", 401); + } + next(); + } + ); +}; + +const authorizer = (permission: string) => { + return errorWrapper( + async (req: Request, res: Response, next: NextFunction) => { + const role = (req as any).user.role; + + const permittedRoles = await getPermittedRoleNames(permission); + + if (role && permittedRoles.includes(role)) { + next(); + } else throw new CustomError("Unauthorized", 401); + } + ); +}; +export { authRole, authorizer }; diff --git a/server/src/middlewares/roleGuards.ts b/server/src/middlewares/roleGuards.ts deleted file mode 100644 index 67d912a..0000000 --- a/server/src/middlewares/roleGuards.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import errorWrapper from "./errorWrapper"; -import CustomError from "../services/CustomError"; - -const authRole = (roles: string[]) => { - return errorWrapper( - async (req: Request, res: Response, next: NextFunction) => { - const userRole = ((req as any).user as any).role; - if (!roles.includes(userRole)) { - throw new CustomError("Unauthorized", 401); - } - next(); - } - ); -}; - -export { authRole }; diff --git a/server/src/permissions/permissions.ts b/server/src/permissions/permissions.ts index 0de61e7..96fd6b3 100644 --- a/server/src/permissions/permissions.ts +++ b/server/src/permissions/permissions.ts @@ -1,20 +1,72 @@ import { User, PrismaClient, Permission } from "@prisma/client"; +import CustomError from "../services/CustomError"; const prisma = new PrismaClient(); const PERMISSIONS = { CREATE_USER: "CREATE_USER", - GET_ALL_USERS: "GET_ALL_USERS", + LOGIN: "LOGIN", + CHANGE_PASSWORD: "CHANGE_PASSWORD", DELETE_USER: "DELETE_USER", - UPDATE_USER_ROLE: "UPDATE_USER_ROLE", -}; -const canCreateUser = (user: User) => { - const permittedRoles = getPermittedRoles(PERMISSIONS.CREATE_USER); - return permittedRoles.includes(user.roleName); + CREATE_BILL: "CREATE_BILL", + GET_BILLS: "GET_BILLS", + DELETE_BILL: "DELETE_BILL", + + UPDATING_USER_ROLE: "UPDATING_USER_ROLE", + + CREATE_LANDFILL: "CREATE_LANDFILL", + DELETE_LANDFILL: "DELETE_LANDFILL", + UPDATE_LANDFILL: "UPDATE_LANDFILL", + + CREATE_STS: "CREATE_STS", + DELETE_STS: "DELETE_STS", + UPDATE_STS: "UPDATE_STS", + + CREATE_STS_VEHICLE_ENTRY: "CREATE_STS_VEHICLE_ENTRY", + GET_CURRENT_STS_VEHICLE: "GET_CURRENT_STS_VEHICLE", + GET_LEFT_STS_VEHICLE: "GET_LEFT_STS_VEHICLE", + DELETE_STS_VEHICLE_ENTRY: "DELETE_STS_VEHICLE_ENTRY", + UPDATE_STS_VEHICLE_ENTRY: "UPDATE_STS_VEHICLE_ENTRY", + + CREATE_LANDFILL_VEHICLE_ENTRY: "CREATE_LANDFILL_VEHICLE_ENTRY", + DELETE_LANDFILL_VEHICLE_ENTRY: "DELETE_LANDFILL_VEHICLE_ENTRY", + UPDATE_LANDFILL_VEHICLE_ENTRY: "UPDATE_LANDFILL_VEHICLE_ENTRY", + + RBAC: "RBAC", + + CREATE_TRIP: "CREATE_TRIP", + COMPLETE_TRIP: "COMPLETE_TRIP", + + CREATE_VEHICLE: "CREATE_VEHICLE", + DELETE_VEHICLE: "DELETE_VEHICLE", + EDIT_VEHICLE: "EDIT_VEHICLE", }; -const getPermittedRoles = (permission: string) => { - const permittedRoles: string[] = []; - return permittedRoles; +const getPermittedRoleNames = async (permissionName: string) => { + const permission = await prisma.permission.findUnique({ + where: { + name: permissionName, + }, + }); + + if (!permission) { + throw new CustomError("Permission not found", 404); + } + + const roles = await prisma.role.findMany({ + where: { + permissions: { + some: { + id: permission.id, + }, + }, + }, + }); + + let roleNames = roles.map((role) => role.name); + + return roleNames; }; + +export { PERMISSIONS, getPermittedRoleNames }; diff --git a/server/src/prisma/.DS_Store b/server/src/prisma/.DS_Store index cc77c9a..5008ddf 100644 Binary files a/server/src/prisma/.DS_Store and b/server/src/prisma/.DS_Store differ diff --git a/server/src/prisma/migrations/migration_lock.toml b/server/src/prisma/migrations/migration_lock.toml deleted file mode 100644 index fbffa92..0000000 --- a/server/src/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file diff --git a/server/src/prisma/schema.prisma b/server/src/prisma/schema.prisma index d0bacc3..06fd7e7 100644 --- a/server/src/prisma/schema.prisma +++ b/server/src/prisma/schema.prisma @@ -14,12 +14,12 @@ model User { hashedPassword String profileName String? profileImage String? - role Role @relation(fields: [roleName], references: [name]) + role Role @relation(fields: [roleName], references: [name], onDelete: Cascade) roleName String @default("UNASSIGNED") - sts STS? @relation(fields: [stsId], references: [id]) + sts STS? @relation(fields: [stsId], references: [id], onDelete: Cascade) stsId String? - landfill Landfill? @relation(fields: [landfillId], references: [id]) + landfill Landfill? @relation(fields: [landfillId], references: [id], onDelete: Cascade) landfillId String? createdAt DateTime @default(now()) @@ -30,39 +30,27 @@ model User { } model Role { - id String @id @default(uuid()) - name String @unique + id String @id @default(uuid()) + name String @unique description String? User User[] - permission RolePermission[] + + permissions Permission[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Permission { - id String @id @default(uuid()) - name String @unique + id String @id @default(uuid()) + name String @unique description String? - role RolePermission[] + roles Role[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } -model RolePermission { - id String @id @default(uuid()) - role Role @relation(fields: [roleId], references: [id]) - roleId String - permission Permission @relation(fields: [permissionId], references: [id]) - permissionId String - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@unique([roleId, permissionId], name: "role_permission_unique") -} - model Vehicle { id String @id @default(uuid()) vehicleNumber String @unique @@ -73,7 +61,7 @@ model Vehicle { currentLatitude Decimal? currentLongitude Decimal? - landFill Landfill @relation(fields: [landFillId], references: [id]) + landFill Landfill @relation(fields: [landFillId], references: [id], onDelete: Cascade) landFillId String STSVehicleEntry STSVehicleEntry[] LandfillVehicleEntry LandfillVehicleEntry[] @@ -123,9 +111,9 @@ model Landfill { model STSVehicleEntry { id String @id @default(uuid()) - sts STS @relation(fields: [stsId], references: [id]) + sts STS @relation(fields: [stsId], references: [id], onDelete: Cascade) stsId String - vehicle Vehicle @relation(fields: [vehicleId], references: [id]) + vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade) vehicleId String weightOfWaste Decimal? entryTime DateTime? @@ -137,9 +125,9 @@ model STSVehicleEntry { model LandfillVehicleEntry { id String @id @default(uuid()) - landfill Landfill @relation(fields: [landfillId], references: [id]) + landfill Landfill @relation(fields: [landfillId], references: [id], onDelete: Cascade) landfillId String - vehicle Vehicle @relation(fields: [vehicleId], references: [id]) + vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade) vehicleId String weightOfWaste Decimal? entryTime DateTime? @@ -151,16 +139,17 @@ model LandfillVehicleEntry { model Bill { id String @id @default(uuid()) - vehicle Vehicle @relation(fields: [vehicleId], references: [id]) + billNo Int @unique @default(autoincrement()) + vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade) vehicleId String - sts STS @relation(fields: [stsId], references: [id]) + sts STS @relation(fields: [stsId], references: [id], onDelete: Cascade) stsId String - landfill Landfill @relation(fields: [landfillId], references: [id]) + landfill Landfill @relation(fields: [landfillId], references: [id], onDelete: Cascade) landfillId String - Trip Trip? @relation(fields: [TripId], references: [id]) - TripId String? + trip Trip? @relation(fields: [tripId], references: [id], onDelete: Cascade) + tripId String? weightOfWaste Decimal? allocatedFuelCost Decimal? @@ -171,13 +160,15 @@ model Bill { model Trip { id String @id @default(uuid()) - sts STS @relation(fields: [stsId], references: [id]) + sts STS @relation(fields: [stsId], references: [id], onDelete: Cascade) stsId String - landfill Landfill @relation(fields: [landfillId], references: [id]) + landfill Landfill @relation(fields: [landfillId], references: [id], onDelete: Cascade) landfillId String - vehicle Vehicle @relation(fields: [vehicleId], references: [id]) + vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade) vehicleId String + tripStartTime DateTime? + tripEndTime DateTime? weightOfWaste Decimal? shortage Decimal? estimatedFuelCost Decimal? diff --git a/server/src/prisma/seed.ts b/server/src/prisma/seed.ts index 0587268..3c51b28 100644 --- a/server/src/prisma/seed.ts +++ b/server/src/prisma/seed.ts @@ -2,6 +2,7 @@ import { PrismaClient, Prisma } from "@prisma/client"; import { RoleName } from "../types/rolesTypes"; import { TripStatus } from "../types/tripStatus"; +import { PERMISSIONS } from "../permissions/permissions"; const prisma = new PrismaClient(); @@ -26,6 +27,130 @@ const roleData: Prisma.RoleCreateInput[] = [ }, ]; +const permissionData: Prisma.PermissionCreateInput[] = [ + { name: PERMISSIONS.CREATE_USER, description: "Can Create User" }, + { name: PERMISSIONS.LOGIN, description: "Can Login" }, + { name: PERMISSIONS.CHANGE_PASSWORD, description: "Can Change Password" }, + { name: PERMISSIONS.DELETE_USER, description: "Can Delete User" }, + { name: PERMISSIONS.CREATE_BILL, description: "Can Create Bill" }, + { name: PERMISSIONS.GET_BILLS, description: "Can Get Bills" }, + { name: PERMISSIONS.UPDATING_USER_ROLE, description: "Can Update User Role" }, + { name: PERMISSIONS.DELETE_BILL, description: "Can Delete Bill" }, + { name: PERMISSIONS.CREATE_LANDFILL, description: "Can Create Landfill" }, + { name: PERMISSIONS.DELETE_LANDFILL, description: "Can Delete Landfill" }, + { name: PERMISSIONS.UPDATE_LANDFILL, description: "Can Update Landfill" }, + { name: PERMISSIONS.CREATE_STS, description: "Can Create Sts" }, + { name: PERMISSIONS.DELETE_STS, description: "Can Delete Sts" }, + { name: PERMISSIONS.UPDATE_STS, description: "Can Update Sts" }, + { + name: PERMISSIONS.CREATE_STS_VEHICLE_ENTRY, + description: "Can Create Sts Vehicle Entry", + }, + { + name: PERMISSIONS.GET_CURRENT_STS_VEHICLE, + description: "Can Get Current Sts Vehicle", + }, + { + name: PERMISSIONS.GET_LEFT_STS_VEHICLE, + description: "Can Get Left Sts Vehicle", + }, + { + name: PERMISSIONS.DELETE_STS_VEHICLE_ENTRY, + description: "Can Delete Sts Vehicle Entry", + }, + { + name: PERMISSIONS.UPDATE_STS_VEHICLE_ENTRY, + description: "Can Update Sts Vehicle Entry", + }, + { + name: PERMISSIONS.CREATE_LANDFILL_VEHICLE_ENTRY, + description: "Can Create Landfill Vehicle Entry", + }, + { + name: PERMISSIONS.DELETE_LANDFILL_VEHICLE_ENTRY, + description: "Can Delete Landfill Vehicle Entry", + }, + { + name: PERMISSIONS.UPDATE_LANDFILL_VEHICLE_ENTRY, + description: "Can Update Landfill Vehicle Entry", + }, + { name: PERMISSIONS.RBAC, description: "Can RBAC" }, + { name: PERMISSIONS.CREATE_TRIP, description: "Can Create Trip" }, + { name: PERMISSIONS.COMPLETE_TRIP, description: "Can Complete Trip" }, + { name: PERMISSIONS.CREATE_VEHICLE, description: "Can Create Vehicle" }, + { name: PERMISSIONS.DELETE_VEHICLE, description: "Can Delete Vehicle" }, + { name: PERMISSIONS.EDIT_VEHICLE, description: "Can Edit Vehicle" }, +]; + +const roleAssignments = [ + { + roleName: RoleName.SYSTEM_ADMIN, + permissions: [ + PERMISSIONS.CREATE_USER, + PERMISSIONS.LOGIN, + PERMISSIONS.CHANGE_PASSWORD, + PERMISSIONS.DELETE_USER, + PERMISSIONS.CREATE_BILL, + PERMISSIONS.GET_BILLS, + PERMISSIONS.UPDATING_USER_ROLE, + PERMISSIONS.DELETE_BILL, + PERMISSIONS.CREATE_LANDFILL, + PERMISSIONS.DELETE_LANDFILL, + PERMISSIONS.UPDATE_LANDFILL, + PERMISSIONS.CREATE_STS, + PERMISSIONS.DELETE_STS, + PERMISSIONS.UPDATE_STS, + PERMISSIONS.CREATE_STS_VEHICLE_ENTRY, + PERMISSIONS.GET_CURRENT_STS_VEHICLE, + PERMISSIONS.GET_LEFT_STS_VEHICLE, + PERMISSIONS.DELETE_STS_VEHICLE_ENTRY, + PERMISSIONS.UPDATE_STS_VEHICLE_ENTRY, + PERMISSIONS.CREATE_LANDFILL_VEHICLE_ENTRY, + PERMISSIONS.DELETE_LANDFILL_VEHICLE_ENTRY, + PERMISSIONS.UPDATE_LANDFILL_VEHICLE_ENTRY, + PERMISSIONS.RBAC, + PERMISSIONS.CREATE_TRIP, + PERMISSIONS.COMPLETE_TRIP, + PERMISSIONS.CREATE_VEHICLE, + PERMISSIONS.DELETE_VEHICLE, + PERMISSIONS.EDIT_VEHICLE, + ], + }, + + { + roleName: RoleName.LAND_MANAGER, + permissions: [ + PERMISSIONS.LOGIN, + PERMISSIONS.CHANGE_PASSWORD, + PERMISSIONS.CREATE_BILL, + PERMISSIONS.GET_BILLS, + PERMISSIONS.UPDATE_LANDFILL, + PERMISSIONS.CREATE_LANDFILL_VEHICLE_ENTRY, + PERMISSIONS.DELETE_LANDFILL_VEHICLE_ENTRY, + PERMISSIONS.UPDATE_LANDFILL_VEHICLE_ENTRY, + PERMISSIONS.COMPLETE_TRIP, + ], + }, + { + roleName: RoleName.STS_MANAGER, + permissions: [ + PERMISSIONS.LOGIN, + PERMISSIONS.CHANGE_PASSWORD, + PERMISSIONS.UPDATE_STS, + PERMISSIONS.CREATE_STS_VEHICLE_ENTRY, + PERMISSIONS.GET_CURRENT_STS_VEHICLE, + PERMISSIONS.GET_LEFT_STS_VEHICLE, + PERMISSIONS.DELETE_STS_VEHICLE_ENTRY, + PERMISSIONS.UPDATE_STS_VEHICLE_ENTRY, + PERMISSIONS.CREATE_TRIP, + ], + }, + { + roleName: RoleName.UNASSIGNED, + permissions: [PERMISSIONS.CHANGE_PASSWORD], + }, +]; + const userData: Prisma.UserCreateInput[] = [ { username: "Shawon Majid", @@ -204,28 +329,58 @@ const vehicleData: Prisma.VehicleCreateInput[] = [ const stsData: Prisma.STSCreateInput[] = [ { id: "sts1", - name: "Moheshkhali", + name: "Mohakhali STS", wardNumber: "13", capacity: 1000, - latitude: 23.76652752, - longitude: 90.4258899 + currentTotalWaste: 900, + latitude: 23.777742178642388, + longitude: 90.40575221162331, }, { id: "sts2", - name: "Gulshan", + name: "Gulshan STS", wardNumber: "2", capacity: 2000, - latitude: 23.76449486, - longitude: 90.3879528, + currentTotalWaste: 1200, + latitude: 23.792464932754005, + longitude: 90.40782465254337, }, { id: "sts3", - name: "Bonani", + name: "Bonani STS", + wardNumber: "4", + capacity: 1500, + currentTotalWaste: 200, + latitude: 23.793630794902622, + longitude: 90.40660514416635, + }, + { + id: "sts4", + name: "Badda STS", + wardNumber: "4", + capacity: 1500, + currentTotalWaste: 900, + latitude: 23.78042151306244, + longitude: 90.42669427037866, + }, + { + id: "sts5", + name: "Jatrabari STS", + wardNumber: "4", + capacity: 1500, + currentTotalWaste: 200, + latitude: 23.710484797357275, + longitude: 90.43479693063576, + }, + { + id: "sts6", + name: "Motijheel STS", wardNumber: "4", capacity: 1500, - latitude: 23.73897468, - longitude: 90.3750954, + currentTotalWaste: 1400, + latitude: 23.72800766871942, + longitude: 90.41902325467944, }, ]; @@ -293,6 +448,38 @@ async function main() { console.log(newRole); } + console.log("Seeding permissions..."); + + for (const permission of permissionData) { + const newPermission = await prisma.permission.create({ + data: permission, + }); + console.log(newPermission); + } + + console.log("Assigning permissions to roles..."); + + for (const roleAssignment of roleAssignments) { + for (const permission of roleAssignment.permissions) { + const role = await prisma.role.update({ + where: { + name: roleAssignment.roleName, + }, + data: { + permissions: { + connect: { + name: permission, + }, + }, + }, + include: { + permissions: true, + }, + }); + console.log(role); + } + } + console.log("Seeding users..."); for (const user of userData) { const newUser = await prisma.user.create({ diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 04b9302..067ec8a 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -8,18 +8,19 @@ import { updatePassword, } from "../controllers/auth"; import authChecker from "../middlewares/auth"; -import { authRole } from "../middlewares/roleGuards"; +import { authRole, authorizer } from "../middlewares/authorizer"; import { RoleName } from "../types/rolesTypes"; +import { PERMISSIONS } from "../permissions/permissions"; const router = express.Router(); router .route("/create") - .post(authChecker, authRole([RoleName.SYSTEM_ADMIN]), createUser); -router.route("/login").post(login); + .post(authChecker, authorizer(PERMISSIONS.CREATE_USER), createUser); +router.route("/login").post(login); // add permission router.route("/logout").get(authChecker, logout); router.route("/reset-password/initiate").post(resetPasswordInit); router.route("/reset-password/confirm").post(resetPasswordConfirm); -router.route("/change-password").post(authChecker, updatePassword); +router.route("/change-password").post(authChecker, updatePassword); // add permission export default router; diff --git a/server/src/routes/bills.ts b/server/src/routes/bills.ts index 4b632c8..8eb2bb0 100644 --- a/server/src/routes/bills.ts +++ b/server/src/routes/bills.ts @@ -3,16 +3,20 @@ const router = express.Router(); import { createBill, + createBillFromTrip, editBill, fetchBill, fetchBills, + getListOfBills, removeBill, } from "../controllers/bills"; router.route("/").get(fetchBills); +router.route("/search").get(getListOfBills); // add permission router.route("/create").post(createBill); router.route("/:billId").get(fetchBill); router.route("/:billId").put(editBill); -router.route("/:billId").delete(removeBill); +router.route("/:billId").delete(removeBill); // add permission +router.route("/create-from-trip/").post(createBillFromTrip); // add permission export default router; diff --git a/server/src/routes/landfillVehicle.ts b/server/src/routes/landfillVehicle.ts index 7e3cdd8..bfd8c20 100644 --- a/server/src/routes/landfillVehicle.ts +++ b/server/src/routes/landfillVehicle.ts @@ -8,10 +8,10 @@ import { } from "../controllers/landfillVehicle"; const router = express.Router(); -router.route("/create").post(addVehicleEntry); +router.route("/create").post(addVehicleEntry); // add permission router.route("/").get(getAllVehicleEntries); router.route("/:vehicleEntryId").get(getVehicleEntryById); -router.route("/:vehicleEntryId").put(updateVehicleEntry); -router.route("/:vehicleEntryId").delete(deleteVehicleEntry); +router.route("/:vehicleEntryId").put(updateVehicleEntry); // add permission +router.route("/:vehicleEntryId").delete(deleteVehicleEntry); // add permission export default router; diff --git a/server/src/routes/landfills.ts b/server/src/routes/landfills.ts index 27e06a7..8cd451f 100644 --- a/server/src/routes/landfills.ts +++ b/server/src/routes/landfills.ts @@ -9,10 +9,10 @@ import { const router = express.Router(); -router.route("/create").post(addlandfill); +router.route("/create").post(addlandfill); // add permission router.route("/").get(getAllLandfills); router.route("/:landfillId").get(getLandfillById); -router.route("/:landfillId").put(updateLandfill); -router.route("/:landfillId").delete(deleteLandfill); +router.route("/:landfillId").put(updateLandfill); // add permission +router.route("/:landfillId").delete(deleteLandfill); // add permision export default router; diff --git a/server/src/routes/rbac.ts b/server/src/routes/rbac.ts index 39452be..399f0f7 100644 --- a/server/src/routes/rbac.ts +++ b/server/src/routes/rbac.ts @@ -1,8 +1,33 @@ import express from "express"; -import { getUsersByRole } from "../controllers/rbac"; +import { + addPermission, + addPermissionToRole, + addRole, + deleteRole, + getAllRolePermissions, + getAllRoles, + getPermissions, + getRolesFromPermission, + getUsersByRole, + removePermissionFromRole, +} from "../controllers/rbac"; const router = express.Router(); -router.route("/roles/:roleName").get(getUsersByRole); +// everything for this file will be under rbac Permission only for system admins + +router.route("/roles").post(addRole).get(getAllRoles); +router + .route("/roles/:roleName/permissions/:permissionName") + .post(addPermissionToRole) + .delete(removePermissionFromRole); + +router.route("/roles/get/:roleName").get(getUsersByRole); +router.route("/roles/delete/:roleName").delete(deleteRole); + +router.route("/roles/:permissionName").get(getRolesFromPermission); + +router.route("/permissions").get(getPermissions).post(addPermission); +router.route("/all").get(getAllRolePermissions); export default router; diff --git a/server/src/routes/sts.ts b/server/src/routes/sts.ts index 5ec031d..f4f2f5e 100644 --- a/server/src/routes/sts.ts +++ b/server/src/routes/sts.ts @@ -9,10 +9,10 @@ import { const router = express.Router(); -router.route("/create").post(addSTS); +router.route("/create").post(addSTS); // add permission router.route("/").get(getAllSTS); router.route("/:stsId").get(getSTSById); -router.route("/:stsId").put(updateSTS); -router.route("/:stsId").delete(deleteSTS); +router.route("/:stsId").put(updateSTS); // add permission +router.route("/:stsId").delete(deleteSTS); // add permision export default router; diff --git a/server/src/routes/stsVehicle.ts b/server/src/routes/stsVehicle.ts index fb98d72..8b5a889 100644 --- a/server/src/routes/stsVehicle.ts +++ b/server/src/routes/stsVehicle.ts @@ -10,12 +10,12 @@ import { } from "../controllers/stsVehicle"; const router = express.Router(); -router.route("/create").post(addVehicleEntry); +router.route("/create").post(addVehicleEntry); // add permission router.route("/").get(getAllVehicleEntries); router.route("/:vehicleEntryId").get(getVehicleEntryById); -router.route("/:vehicleEntryId").put(updateVehicleEntry); -router.route("/:vehicleEntryId").delete(deleteVehicleEntry); -router.route("/:stsId/get-current-vehicles").get(getCurrentVehiclesInSTS); -router.route("/:stsId/get-left-vehicles").get(getLeftVehiclesInSTS); +router.route("/:vehicleEntryId").put(updateVehicleEntry); // add permission +router.route("/:vehicleEntryId").delete(deleteVehicleEntry); // add permission +router.route("/:stsId/get-current-vehicles").get(getCurrentVehiclesInSTS); // add permission +router.route("/:stsId/get-left-vehicles").get(getLeftVehiclesInSTS); // add permission export default router; diff --git a/server/src/routes/trip.ts b/server/src/routes/trip.ts index d296fc5..6a08c33 100644 --- a/server/src/routes/trip.ts +++ b/server/src/routes/trip.ts @@ -2,8 +2,8 @@ import express from "express"; import { completeTrip, createTrip, getListOfTrips } from "../controllers/trip"; const router = express.Router(); -router.route("/create").post(createTrip); +router.route("/create").post(createTrip); // add permission router.route("/search").get(getListOfTrips); -router.route("/complete").post(completeTrip); +router.route("/complete").post(completeTrip); // add permission export default router; diff --git a/server/src/routes/users.ts b/server/src/routes/users.ts index c0bb6dd..7b45ea6 100644 --- a/server/src/routes/users.ts +++ b/server/src/routes/users.ts @@ -16,8 +16,8 @@ router.route("/").get(getAllUsers); router.route("/roles").get(getAllRoles); router.route("/:userId").get(getUserById); router.route("/:userId").put(updateUser); -router.route("/:userId").delete(deleteUser); -router.route("/:userId/roles").put(updateUsersRole); +router.route("/:userId").delete(deleteUser); // add permission +router.route("/:userId/roles").put(updateUsersRole); // add permission export default router; diff --git a/server/src/routes/vehicles.ts b/server/src/routes/vehicles.ts index f9839af..16a2390 100644 --- a/server/src/routes/vehicles.ts +++ b/server/src/routes/vehicles.ts @@ -10,11 +10,11 @@ import { const router = express.Router(); -router.route("/create").post(createVehicle); +router.route("/create").post(createVehicle); // add permission router.route("/search").get(getVehiclesOnQuery); router.route("/").get(fetchAllVehicles); router.route("/:vehicleId").get(fetchVehicleById); -router.route("/:vehicleId").put(editVehicle); -router.route("/:vehicleId").delete(removeVehicle); +router.route("/:vehicleId").put(editVehicle); // add permission +router.route("/:vehicleId").delete(removeVehicle); // add permission export default router; diff --git a/server/src/services/vehicleServices.ts b/server/src/services/vehicleServices.ts index 6fbd363..faaa4d3 100644 --- a/server/src/services/vehicleServices.ts +++ b/server/src/services/vehicleServices.ts @@ -13,7 +13,11 @@ const addVehicle = async (vehicleInfo: Vehicle) => { }; const getAllVehicles = async () => { - const vehicles = await prisma.vehicle.findMany({}); + const vehicles = await prisma.vehicle.findMany({ + include: { + landFill: true, + }, + }); return vehicles; }; diff --git a/server/src/types/express/index.d.ts b/server/src/types/express/index.d.ts index e9b9617..d7b0b5e 100644 --- a/server/src/types/express/index.d.ts +++ b/server/src/types/express/index.d.ts @@ -1,4 +1,5 @@ import { JwtPayload } from "jsonwebtoken"; +import { User } from "../custom"; // to make the file a module and avoid the TypeScript error export {}; @@ -6,7 +7,7 @@ export {}; declare global { namespace Express { export interface Request { - user?: any; + user?: User; } } }