diff --git a/README.md b/README.md index 448b510..bb58415 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # **EcoSync** by DEFINE CODERS -EcoSync is a full-stack application built with Next.js, Express.js, Prisma, and PostgreSQL. +EcoSync is a full-stack application built with Next.js, Express.js, Prisma, and PostgreSQL. This was developed by the team to define codes for CODE SAMURAI 2024. This project handles waste collection and management for Dhaka North City Corporation. ## Project Structure @@ -10,25 +10,27 @@ The project is divided into two main directories: - `/client` - Contains the Next.js frontend application. - `/server` - Contains the Express.js backend server, which uses Prisma for ORM and PostgreSQL as the database. +- `/waste_management` - Contains the mobile app for the customers and employers -## RUNNING THE PROJECT USING DOCKER COMPOSE (RECCOMANDED) +## RUNNING THE PROJECT LOCALLY 1. Clone the repository: ```bash git clone https://github.com/definecoder/CS24-p2-define-coders.git ``` -2. Open the source directory in a terminal and give the following command +2. Run frontend using: ```bash -docker compose up --build +cd .\client\ +npm i +npm run dev ``` -wait for the docker compose to complete and then frontend application will be available running at `http://localhost:3000`, and the backend server will be running at `http://localhost:8585` (or whatever port you specified). - -3. After backend and frontend services are running again open the source directory in `another terminal` and give the following command +3. Open a new terminal in the project root directory and Run the backend using: ```bash -docker exec -it backend /bin/bash -c "cd ./src && npx prisma migrate dev --name init && npx prisma db seed 2> /dev/null || echo \'Database is already seeded\'" +cd .\server\ +npm i +npm run dev ``` -This will do the prisma migration and run the seed file to push initial data. ## Credentials set by the initial db migration @@ -49,31 +51,6 @@ This will do the prisma migration and run the seed file to push initial data. } ``` -## RUNNING THE BACKEND USING AWS (IF DOCKER COMPOSE FAILS!) -Go to `client\data\apiRoutes` and comment the `first line` and uncomment the second -```js -export const baseUrl = "http://localhost:8585"; // Uncomment to run Locally -// export const baseUrl = "http://13.250.36.61"; // Uncomment to run in AWS -``` -## CONFIGURATION -In the `/server` & `/client` directory, rename `.env.example` to `.env` and fill in your PostgreSQL database details `with you postgres password e.g: "postgresql://postgres:YOURPASSWORDHERE@127.0.0.1:5432/ecosync"` and other environment variables from [this google doc link](https://docs.google.com/document/d/1j1UFD3U4ejqeDRb26N9WffqvaLzwYYAxGY2OaWlZTO4/edit?usp=sharing). - -## RUNNING FRONTEND -Run the frontend using docker: -```bash -docker compose up --build -``` -or locally by running following command: -```bash -cd .\client\ -npm i -npm run dev -``` - - -## NOTE ON USING AWS BACKEND -When running on AWS the frontend might face some slow network problems which can be fixed by switching speed from no thorttling to fast 3G. [You can check this video to see the demonstration of the problem mentioned here](https://drive.google.com/drive/folders/1B5N5o0ms7mizSYm5HNiAjJj0O82Zb1tq?usp=sharing). - ## License [MIT](https://choosealicense.com/licenses/mit/) diff --git a/client/components/dashboard-componenets/cards/IssueCard.tsx b/client/components/dashboard-componenets/cards/IssueCard.tsx new file mode 100644 index 0000000..5e4c44a --- /dev/null +++ b/client/components/dashboard-componenets/cards/IssueCard.tsx @@ -0,0 +1,42 @@ +import React from 'react'; + +// Define the Issue type +type Issue = { + issueType: string; + description: string; + latitude: string; + longitude: string; + isAnonymous: string; +}; + + + +const IssueCard: React.FC = ({ issueType, description, latitude, longitude }) => { + return ( + + ); +}; + +export default IssueCard; diff --git a/client/components/dashboard-componenets/mainContent/contractManagerContents/Bills.tsx b/client/components/dashboard-componenets/mainContent/contractManagerContents/Bills.tsx new file mode 100644 index 0000000..79a0a50 --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/contractManagerContents/Bills.tsx @@ -0,0 +1,31 @@ +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"; +import { UpdateStsStorage } from "@/components/modals/stsControl/updateSTSStorage"; +import { GenerateBill } from "@/components/modals/stsControl/generateBill"; +import CompletedBillListOfSTS from "@/components/dataTables/completedBillsOfSTS"; + +export default function ContractManagerBillManagementPanel() { + return ( +
+
+

Bills

+
+
+ +
+
+
+
+
+ + + +
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/contractManagerContents/CollectionPlan.tsx b/client/components/dashboard-componenets/mainContent/contractManagerContents/CollectionPlan.tsx new file mode 100644 index 0000000..7c85a28 --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/contractManagerContents/CollectionPlan.tsx @@ -0,0 +1,386 @@ +"use client"; +import { Button } from "@/components/ui/button"; +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"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import useGetAllArea from "@/hooks/dataQuery/useGetAllArea"; +import { Input } from "@/components/ui/input"; +import { DatePicker, message } from "antd"; +import useGetAllRoutes from "@/hooks/dataQuery/useGetAllRoutes"; +import useGetAllEmployees from "@/hooks/dataQuery/useGetAllEmployee"; +import axios from "axios"; +import { apiRoutes } from "@/data/apiRoutes"; + +export default function CollectionPlanPanel() { + const [refreshKey, setRefreshKey] = useState(0); + const [finalState, setFinalState] = useState(false); + const { areaData, fetchAllArea } = useGetAllArea(); + const { fetchAllRoutes, routesData } = useGetAllRoutes(); + const { fetchAllEmployees, employeeData } = useGetAllEmployees(); + const [planData, setPlanData] = useState<{ + areaId?: string; + collectionStartTime?: string; + durationForCollection?: number; + numberOfLaborers?: number; + numberOfVans?: number; + expectedWaste?: number; + assignments?: { routeId: string; employeeId: string }[]; + }>(); + const [selectedEmployees, setSelectedEmployees] = useState([]); + const [selectedRoutes, setSelectedRoutes] = useState([]); + + useEffect(() => { + fetchAllArea(); + fetchAllRoutes(); + fetchAllEmployees(); + }, []); + useEffect(() => { + console.log(areaData); + }, [areaData]); + useEffect(() => { + console.log(routesData); + }, [routesData]); + useEffect(() => { + console.log(employeeData); + }, [employeeData]); + useEffect(() => { + console.log(selectedEmployees); + }, [selectedEmployees]); + useEffect(() => { + console.log(selectedRoutes); + }, [selectedRoutes]); + + function formatDate(dateString: string) { + const date = new Date(dateString); + + const year = date.getUTCFullYear(); + const month = String(date.getUTCMonth() + 1).padStart(2, "0"); // Months are 0-indexed in JavaScript + const day = String(date.getUTCDate()).padStart(2, "0"); + const hours = String(date.getUTCHours()).padStart(2, "0"); + const minutes = String(date.getUTCMinutes()).padStart(2, "0"); + const seconds = String(date.getUTCSeconds()).padStart(2, "0"); + + return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}Z`; + } + + return ( +
+
+
+
+ {!finalState && ( + +

CREATE PLAN

+
+ Select area + +
+
+ Starting Time:{" "} + { + console.log("string: ", dateString); + setPlanData({ + ...planData, + collectionStartTime: formatDate(dateString.toString()), + }); + }} + /> +
+
+ Duration (in hour):{" "} + { + setPlanData({ + ...planData, + durationForCollection: Number(e.target.value), + }); + }} + /> +
+
+ Expected Waste:{" "} + { + setPlanData({ + ...planData, + expectedWaste: Number(e.target.value), + }); + }} + /> +
+
+ Number of labors:{" "} + { + setPlanData({ + ...planData, + numberOfLaborers: Number(e.target.value), + }); + }} + /> +
+
+ Number of Vans:{" "} + { + setPlanData({ + ...planData, + numberOfVans: Number(e.target.value), + }); + }} + /> +
+ +
+ )} + {finalState && ( + +

CREATE PLAN

+
+ {selectedEmployees.map((employee, index) => ( +
+
+
Employee {index + 1}
+ +
+
+ Route {index + 1} + +
+
+ ))} +
+ +
+ )} +
+
+ +
+ {" "} +
+ YOUR TARGET +
+
+ PROGRESS {" "} +
+
+ {" "} + 65% DONE +
+
+ {" "} + 35% PENDING{" "} +
+
+ +
+
+
+ + COLLECTION HISTORY (LAST 7 DAY) + + +
+
+ +

EMPLOYEE STATUS

+

+ + 12{" "} + /25 + {" "} + AVAILABLE +

+
+
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/contractManagerContents/CompanyDetails.tsx b/client/components/dashboard-componenets/mainContent/contractManagerContents/CompanyDetails.tsx new file mode 100644 index 0000000..91a0f42 --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/contractManagerContents/CompanyDetails.tsx @@ -0,0 +1,31 @@ +"use client"; + +export default function CompanyDetails() { + + return ( +
+

CONTRACTOR DETAILS

+
+

Company Name

+

ABC Company

+
+
+

Number of Employees

+

100

+
+
+

Area

+

Software Development

+
+
+

Phone

+

123-456-7890

+
+
+

Address

+

123 Main Street, City, State, ZIP

+
+
+ ); + +} diff --git a/client/components/dashboard-componenets/mainContent/contractManagerContents/Dashboard.tsx b/client/components/dashboard-componenets/mainContent/contractManagerContents/Dashboard.tsx new file mode 100644 index 0000000..836b0ae --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/contractManagerContents/Dashboard.tsx @@ -0,0 +1,188 @@ +"use client"; +import { Button } from "@/components/ui/button"; +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"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import useGetAllArea from "@/hooks/dataQuery/useGetAllArea"; +import { Input } from "@/components/ui/input"; +import { DatePicker, message } from "antd"; +import useGetAllRoutes from "@/hooks/dataQuery/useGetAllRoutes"; +import useGetAllEmployees from "@/hooks/dataQuery/useGetAllEmployee"; +import axios from "axios"; +import { apiRoutes } from "@/data/apiRoutes"; +import EmployeeLogs from "@/components/dataTables/EmployeeLogs"; + +export default function ContractManagerDashboard() { + const [refreshKey, setRefreshKey] = useState(0); + const [finalState, setFinalState] = useState(false); + const { areaData, fetchAllArea } = useGetAllArea(); + const { fetchAllRoutes, routesData } = useGetAllRoutes(); + const { fetchAllEmployees, employeeData } = useGetAllEmployees(); + const [planData, setPlanData] = useState<{ + areaId?: string; + collectionStartTime?: string; + durationForCollection?: number; + numberOfLaborers?: number; + numberOfVans?: number; + expectedWaste?: number; + assignments?: { routeId: string; employeeId: string }[]; + }>(); + const [selectedEmployees, setSelectedEmployees] = useState([]); + const [selectedRoutes, setSelectedRoutes] = useState([]); + + useEffect(() => { + fetchAllArea(); + fetchAllRoutes(); + fetchAllEmployees(); + }, []); + useEffect(() => { + console.log(areaData); + }, [areaData]); + useEffect(() => { + console.log(routesData); + }, [routesData]); + useEffect(() => { + console.log(employeeData); + }, [employeeData]); + useEffect(() => { + console.log(selectedEmployees); + }, [selectedEmployees]); + useEffect(() => { + console.log(selectedRoutes); + }, [selectedRoutes]); + + function formatDate(dateString: string) { + const date = new Date(dateString); + + const year = date.getUTCFullYear(); + const month = String(date.getUTCMonth() + 1).padStart(2, "0"); // Months are 0-indexed in JavaScript + const day = String(date.getUTCDate()).padStart(2, "0"); + const hours = String(date.getUTCHours()).padStart(2, "0"); + const minutes = String(date.getUTCMinutes()).padStart(2, "0"); + const seconds = String(date.getUTCSeconds()).padStart(2, "0"); + + return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}Z`; + } + + return ( +
+
+
+
+

+ +
+
+ +
+ {" "} +
+ YOUR TARGET +
+
+ PROGRESS {" "} +
+
+ {" "} + 65% DONE +
+
+ {" "} + 35% PENDING{" "} +
+
+ +
+
+
+ + COLLECTION HISTORY (LAST 7 DAY) + + +
+
+ +

EMPLOYEE STATUS

+

+ + 12{" "} + /25 + {" "} + AVAILABLE +

+
+
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/contractManagerContents/Employees.tsx b/client/components/dashboard-componenets/mainContent/contractManagerContents/Employees.tsx new file mode 100644 index 0000000..b7e85ce --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/contractManagerContents/Employees.tsx @@ -0,0 +1,52 @@ +import { Button } from "@/components/ui/button"; +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import { Plus, Trash, Truck, Warehouse } from "lucide-react"; +import ContractLists from "@/components/dataTables/ContractLists"; +import ContractorLogTable from "@/components/dataTables/ContractorLogs"; +import { AddNewContractor } from "@/components/modals/ContractorControl/AddNewContractor"; +import { AddNewContractorManager } from "@/components/modals/ContractorControl/AddNewContractorManager"; +import CleanerLists from "@/components/dataTables/CleanerList"; +import { AddNewCleaner } from "@/components/modals/cleanerControl/AddNewCleaner"; +import CleanerLog from "@/components/dataTables/CleanerLog"; +import EmployeeList from "@/components/dataTables/EmployeeList"; +import EmployeeLogs from "@/components/dataTables/EmployeeLogs"; +import { AddNewEmployee } from "@/components/modals/cleanerControl/AddNewEmployee"; + +export default function EmployeePanel() { + return ( +
+
+

+ EMPLOYEE DATA +

+
+
+ + + +
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/contractManagerContents/Schedule.tsx b/client/components/dashboard-componenets/mainContent/contractManagerContents/Schedule.tsx new file mode 100644 index 0000000..d0e2f5f --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/contractManagerContents/Schedule.tsx @@ -0,0 +1,72 @@ +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +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"; +import STSVehicleHistoryList from "@/components/dataTables/STSVehicleHistoryList"; +import { BillBySTSId } from "@/components/dataTables/BillBySTSId"; +import { getCookie } from "@/lib/cookieFunctions"; +import { stsId } from "@/data/cookieNames"; +import { useState } from "react"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Button } from "@/components/ui/button"; +import { CalendarSearchIcon } from "lucide-react"; +import useGetAllSTS from "@/hooks/dataQuery/useGetAllSTS"; +import { Calendar } from "@/components/ui/calendar"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { ScheduleByContractor } from "@/components/dataTables/ScheduleByContractor"; + +export default function ContractorManagerSchedule() { + const [date, setDate] = useState(new Date()); + + function formatDate(dateString: string | undefined) { + if (!dateString) return undefined; + const date = new Date(dateString); + const year = date.getFullYear(); + const month = ("0" + (date.getMonth() + 1)).slice(-2); // Months are 0 based, so +1 and pad with 0 + const day = ("0" + date.getDate()).slice(-2); + + return `${year}-${month}-${day}`; + } + return ( +
+ +
+ SELECT DATE OF SCHEDULE : + + + + + + + + +
+ + + +
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx b/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx index 2857af5..c3d8a7f 100644 --- a/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx +++ b/client/components/dashboard-componenets/mainContent/mainSectionHeader.tsx @@ -39,6 +39,7 @@ import { useContext } from "react"; import { NavContext } from "@/hooks/contexts/useNavCtx"; import { eraseCookie, getCookie } from "@/lib/cookieFunctions"; import { + companyName, curActive, jwtToken, landfillName, @@ -48,7 +49,7 @@ import { username, } from "@/data/cookieNames"; import { get } from "http"; -import { admin, landfillManager, stsManager } from "@/data/roles"; +import { admin, contractorManager, landfillManager, stsManager } from "@/data/roles"; import axios from "axios"; import { message } from "antd"; @@ -128,11 +129,13 @@ export default function MainSectionHeader({ <>{"NO LANDFILL ASSIGNED"} ))} - {getCookie(curActive)?.startsWith(admin) && - - SYSTEM ADMIN OF ECOSYNC - } + {getCookie(curActive)?.startsWith(admin) && ( + SYSTEM ADMIN OF ECOSYNC + )} + {getCookie(curActive)?.startsWith(contractorManager) && ( + CONTRACTOR MANAGER + )} {/* Profile Icon and dropdown menu */} diff --git a/client/components/dashboard-componenets/mainContent/stsManagerContents/RoutesPanel.tsx b/client/components/dashboard-componenets/mainContent/stsManagerContents/RoutesPanel.tsx new file mode 100644 index 0000000..bc3de5b --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/stsManagerContents/RoutesPanel.tsx @@ -0,0 +1,152 @@ +import { Button } from "@/components/ui/button"; +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import { ArrowDown, Cog, Map, Send, Truck, UserRoundCog } from "lucide-react"; +import { stsId } from "@/data/cookieNames"; +import useGetstsDatabyID from "@/hooks/StsDashboard/getStsDataById"; +import { useEffect } from "react"; +import { getCookie } from "@/lib/cookieFunctions"; + +import { AddNewAreaModal } from "@/components/modals/stsControl/addNewArea"; +import { AddNewRouteModal } from "@/components/modals/stsControl/AddNewRoute"; +import useGetAllArea from "@/hooks/dataQuery/useGetAllArea"; +import useGetAllRoutes from "@/hooks/dataQuery/useGetAllRoutes"; + +export default function RoutesPanel() { + const { areaData, fetchAllArea } = useGetAllArea(); + const { routesData, fetchAllRoutes } = useGetAllRoutes(); + const areas = [ + "akhalia", + "akhalia", + "akhalia", + "akhalia", + "akhalia", + "akhalia", + "akhalia", + ]; + const routes = [ + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + },{ + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + }, + { + area: "akhalia", + description: "akhalia to akhalia to akhalia", + name: "Route 1", + } + ]; + + useEffect(() => { + fetchAllArea(); + fetchAllRoutes(); + }, []); + + useEffect(() => {}, [areaData]); + useEffect(() => {}, [routesData]); + + return ( +
+
+

+ AREA AND ROUTES +

+
+
+ + + + + + +
+
+
+
+
+ +

AREA LISTS

+
+
+ {areaData.map((area, index) => ( +
+

{area.name}

+
+ ))} +
+
+
+
+ +

ROUTES LIST

+
+
+ {routesData.map((route, index) => ( +
+

{route.name}

+

{route.areaName}

+

{route.description}

+
+ ))} +
+
+
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/stsManagerContents/WasteCollection.tsx b/client/components/dashboard-componenets/mainContent/stsManagerContents/WasteCollection.tsx new file mode 100644 index 0000000..7bb82c5 --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/stsManagerContents/WasteCollection.tsx @@ -0,0 +1,29 @@ +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"; +import { UpdateStsStorage } from "@/components/modals/stsControl/updateSTSStorage"; +import { GenerateBill } from "@/components/modals/stsControl/generateBill"; +import CompletedBillListOfSTS from "@/components/dataTables/completedBillsOfSTS"; + +export default function STSWasteCollection() { + return ( +
+
+

Bills

+
+
+ +
+
+
+
+
+ +
+
+
+
+ ); + } \ No newline at end of file diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/Contracts.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/Contracts.tsx new file mode 100644 index 0000000..9697046 --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/Contracts.tsx @@ -0,0 +1,58 @@ +import { Button } from "@/components/ui/button"; +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import { Plus, Trash, Truck, UserCog, Warehouse } from "lucide-react"; +import { StsCreateModal } from "../../../modals/stsControl/StsModal"; +import { VehicleCreateModal } from "@/components/modals/VehicleModal"; +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"; +import ContractLists from "@/components/dataTables/ContractLists"; +import ContractorLogTable from "@/components/dataTables/ContractorLogs"; +import { AddNewContractor } from "@/components/modals/ContractorControl/AddNewContractor"; +import { AddNewContractorManager } from "@/components/modals/ContractorControl/AddNewContractorManager"; + + +export default function AdminContractsPanel() { + return ( +
+
+

CONTRUCTOR DATA

+
+
+ + + + + + +
+
+
+
+
+ +
+
+ +
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/Schedule.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/Schedule.tsx index caf28a5..252f009 100644 --- a/client/components/dashboard-componenets/mainContent/systemAdminContents/Schedule.tsx +++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/Schedule.tsx @@ -58,12 +58,13 @@ export default function AdminSchedulePanel() { ))} - + AND DATE : - + @@ -74,7 +75,7 @@ export default function AdminSchedulePanel() { className="rounded-md border" /> - +
diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/Workforce.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/Workforce.tsx new file mode 100644 index 0000000..f650962 --- /dev/null +++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/Workforce.tsx @@ -0,0 +1,71 @@ +import { Button } from "@/components/ui/button"; +import * as React from "react"; +import EmptyFillContainer from "../../cards/EmptyFillContainer"; +import { Plus, Trash, Truck, Warehouse } from "lucide-react"; +import ContractLists from "@/components/dataTables/ContractLists"; +import ContractorLogTable from "@/components/dataTables/ContractorLogs"; +import { AddNewContractor } from "@/components/modals/ContractorControl/AddNewContractor"; +import { AddNewContractorManager } from "@/components/modals/ContractorControl/AddNewContractorManager"; +import CleanerLists from "@/components/dataTables/CleanerList"; +import { AddNewCleaner } from "@/components/modals/cleanerControl/AddNewCleaner"; +import CleanerLog from "@/components/dataTables/CleanerLog"; +import useGetIssue from "@/hooks/issues/useGetIssue"; +import IssueCard from "../../cards/IssueCard"; + + +export default function AdminWorkforcePanel() { + const { issueData, getAllIssue } = useGetIssue(); + + React.useEffect(() => { + getAllIssue(); + }, []); + + return ( +
+
+

+ WORKFORCE DATA +

+
+
+ + + +
+
+
+
+
+ + + +
+
+ {/* + + */} +
+ {issueData.map((issue, index) => ( + + ))} +
+
+
+
+
+ ); +} diff --git a/client/components/dashboard-componenets/sidebar/SidebarItem.tsx b/client/components/dashboard-componenets/sidebar/SidebarItem.tsx index 8d6c97c..42ab3ef 100644 --- a/client/components/dashboard-componenets/sidebar/SidebarItem.tsx +++ b/client/components/dashboard-componenets/sidebar/SidebarItem.tsx @@ -16,7 +16,7 @@ export const SidebarItem = ({
{Icon && } diff --git a/client/components/dataTables/CleanerList.tsx b/client/components/dataTables/CleanerList.tsx new file mode 100644 index 0000000..09a34c7 --- /dev/null +++ b/client/components/dataTables/CleanerList.tsx @@ -0,0 +1,308 @@ +"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 { Contractor, 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 useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; + + +export const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("name")}
+ ), + }, + { + accessorKey: "contactNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("contactNumber")} +
+ ), + }, + { + accessorKey: "workforceSize", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("workforceSize")}
+ ), + }, + { + accessorKey: "assignedSTS", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("assignedSTS")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const contractor: Contractor = row.original; + + return ( +
+ {/* + + */} +
+ ); + }, + }, +]; + +export default function CleanerLists() { + const [data, setData] = React.useState([]); + const { fetchAllContractors, contractorData } = useGetAllContractor(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllContractors(); + }, []); + + React.useEffect(() => { + setData(contractorData); + }, [contractorData]); + + 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 ( + <> +
LIST OF ALL CLEANERS
+
+ + 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/CleanerLog.tsx b/client/components/dataTables/CleanerLog.tsx new file mode 100644 index 0000000..aea0234 --- /dev/null +++ b/client/components/dataTables/CleanerLog.tsx @@ -0,0 +1,259 @@ +"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 useGetAllLogs from "@/hooks/dataQuery/useGetAllLogs"; + +export type ContractorLog = { + type: string; + description: string; +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "type", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("type")}
+ ), + }, + { + accessorKey: "description", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("description")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const landfill: ContractorLog = row.original; + + return ( +
+ {/* + + */} +
+ ); + }, + }, +]; + +export default function CleanerLog() { + const [data, setData] = React.useState([]); + const { contractorLog, fetchAllContractorLog } = useGetAllLogs(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllContractorLog(); + }, []); + + React.useEffect(() => { + setData(contractorLog); + }, [contractorLog]); + + 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 ( + <> +
Cleaner Logs
+
+ + 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/ContractLists.tsx b/client/components/dataTables/ContractLists.tsx new file mode 100644 index 0000000..ff6ed8c --- /dev/null +++ b/client/components/dataTables/ContractLists.tsx @@ -0,0 +1,308 @@ +"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 { Contractor, 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 useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; + + +export const columns: ColumnDef[] = [ + { + accessorKey: "name", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("name")}
+ ), + }, + { + accessorKey: "contactNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("contactNumber")} +
+ ), + }, + { + accessorKey: "workforceSize", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("workforceSize")}
+ ), + }, + { + accessorKey: "assignedSTS", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("assignedSTS")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const contractor: Contractor = row.original; + + return ( +
+ {/* + + */} +
+ ); + }, + }, +]; + +export default function ContractLists() { + const [data, setData] = React.useState([]); + const { fetchAllContractors, contractorData } = useGetAllContractor(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllContractors(); + }, []); + + React.useEffect(() => { + setData(contractorData); + }, [contractorData]); + + 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 ( + <> +
LIST OF ALL CONTRACTORS
+
+ + 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/ContractorLogs.tsx b/client/components/dataTables/ContractorLogs.tsx new file mode 100644 index 0000000..5b697c6 --- /dev/null +++ b/client/components/dataTables/ContractorLogs.tsx @@ -0,0 +1,279 @@ +"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 useGetAllLogs from "@/hooks/dataQuery/useGetAllLogs"; + +export type ContractorLog = { + type: string; + description: string; +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "type", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("type")}
+ ), + }, + { + accessorKey: "description", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("description")}
+ ), + }, + { + accessorKey: "time", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("time")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const landfill: ContractorLog = row.original; + + return ( +
+ {/* + + */} +
+ ); + }, + }, +]; + +export default function ContractorLogTable() { + const [data, setData] = React.useState([]); + const { contractorLog, fetchAllContractorLog } = useGetAllLogs(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllContractorLog(); + }, []); + + React.useEffect(() => { + setData(contractorLog); + }, [contractorLog]); + + 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 ( + <> +
Contractor Logs
+
+ + table.getColumn("description")?.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/EmployeeList.tsx b/client/components/dataTables/EmployeeList.tsx new file mode 100644 index 0000000..cbf16af --- /dev/null +++ b/client/components/dataTables/EmployeeList.tsx @@ -0,0 +1,309 @@ +"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 { Contractor, Employee, 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 useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; +import useGetAllEmployees from "@/hooks/dataQuery/useGetAllEmployee"; + + +export const columns: ColumnDef[] = [ + { + accessorKey: "username", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("username")}
+ ), + }, + { + accessorKey: "contactNumber", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("contactNumber")} +
+ ), + }, + { + accessorKey: "paymentRatePerHour", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("paymentRatePerHour")}
+ ), + }, + { + accessorKey: "jobTitle", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("jobTitle")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const employee: Employee = row.original; + + return ( +
+ {/* + + */} +
+ ); + }, + }, +]; + +export default function EmployeeList() { + const [data, setData] = React.useState([]); + const { employeeData, fetchAllEmployees } = useGetAllEmployees(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllEmployees(); + }, []); + + React.useEffect(() => { + setData(employeeData); + }, [employeeData]); + + 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 ( + <> +
LIST OF ALL EMPLOYEES
+
+ + table.getColumn("username")?.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/EmployeeLogs.tsx b/client/components/dataTables/EmployeeLogs.tsx new file mode 100644 index 0000000..ee9bebc --- /dev/null +++ b/client/components/dataTables/EmployeeLogs.tsx @@ -0,0 +1,259 @@ +"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 useGetAllLogs from "@/hooks/dataQuery/useGetAllLogs"; + +export type ContractorLog = { + type: string; + description: string; +}; + +export const columns: ColumnDef[] = [ + { + accessorKey: "type", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("type")}
+ ), + }, + { + accessorKey: "description", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("description")}
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const landfill: ContractorLog = row.original; + + return ( +
+ {/* + + */} +
+ ); + }, + }, +]; + +export default function EmployeeLogs() { + const [data, setData] = React.useState([]); + const { contractorLog, fetchAllContractorLog } = useGetAllLogs(); + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + React.useEffect(() => { + fetchAllContractorLog(); + }, []); + + React.useEffect(() => { + setData(contractorLog); + }, [contractorLog]); + + 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 ( + <> +
Employee Logs
+
+ + 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/ScheduleByContractor.tsx b/client/components/dataTables/ScheduleByContractor.tsx new file mode 100644 index 0000000..40e3957 --- /dev/null +++ b/client/components/dataTables/ScheduleByContractor.tsx @@ -0,0 +1,125 @@ +"use client"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableFooter, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { jwtToken, role } from "@/data/cookieNames"; +import { message } from "antd"; +import axios from "axios"; +import { useEffect, useState } from "react"; +import EmptyFillContainer from "../dashboard-componenets/cards/EmptyFillContainer"; +import { getCookie } from "@/lib/cookieFunctions"; +import { admin } from "@/data/roles"; +import { Button } from "../ui/button"; +import { apiRoutes, baseUrl } from "@/data/apiRoutes"; + +type ScheduleEntry = { + time: string; + vehicleNumber: string; + vehicleType: string; + landFillName: string; + amount: string; + duration: string; +}; + +export function ScheduleByContractor({ date }: { date: string}) { + + const [companyName, setCompanyName] = useState(""); + const [scheduleData, setScheduleData] = useState([]); + + async function fetchSchedule() { + + try { + // fetch data from api + const res: any = await axios.get( + apiRoutes.plans.getSchedules, + { + headers: { + Authorization: `Bearer ${localStorage.getItem(jwtToken)}`, + }, + } + ); + const data = res.data.map((d: any) => ({ + collectionStartTime: d.collectionStartTime, + durationForCollection: d.durationForCollection, + numberOfLaborers: d.numberOfLaborers, + numberOfVans: d.numberOfVans, + expectedWaste: d.expectedWaste, + })); + setCompanyName(res?.data[0]?.Contractor?.companyName); + setScheduleData(data); + console.log(data, companyName); + } catch (error: any) { + message.error(error?.response?.data?.message || error); + } + } + + useEffect(() => { + fetchSchedule(); + }, []); + + + + + return ( + <> + {scheduleData[0] ? ( +
+

EcoSync

+
Date: {date}
+

SCHEDULE FOR WASTE TRANSFER

+

{companyName}

+ + + Schedule of {companyName} for {date}. + + + + Start Time + Collection Duration + Employee Count + Van Count + Expected Waste + + + + {scheduleData.map((data, i) => ( + + {new Date(data.collectionStartTime).toLocaleString()} + {data.durationForCollection} + {data.numberOfLaborers} + {data.numberOfVans} + {data.expectedWaste} + + ))} + + + + + Total Employee :{" "} + + + {scheduleData.reduce( + (total, data) => total + parseInt(data.numberOfLaborers), + 0 + )}{" "} + Persons + + + +
+
+ ) : ( + + No schedule generated by Contractor Manager for {companyName} on {date}. + + )} + + ); +} diff --git a/client/components/dataTables/completedBillsOfSTS.tsx b/client/components/dataTables/completedBillsOfSTS.tsx new file mode 100644 index 0000000..e03da40 --- /dev/null +++ b/client/components/dataTables/completedBillsOfSTS.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, stsName } from "@/data/cookieNames"; +import { BillCreationModal } from "../modals/billControl/BillCreationModal"; +import useGetAllCompletedBillList from "@/hooks/bills/useGetAllCompletedBillList"; +import { BillViewModal } from "../modals/billControl/BillViewModal"; +import useGetAllSTSBills from "@/hooks/bills/useGetAllSTSBills"; + +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: "contractorName", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("contractorName")}
+ ), + }, + { + accessorKey: "weightRequired", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("weightRequired")} +
+ ), + }, + { + accessorKey: "weightCollected", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
{row.getValue("weightCollected")}
+ ), + }, + { + accessorKey: "paymentAmount", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("paymentAmount")} +
+ ), + }, + { + accessorKey: "fine", + header: ({ column }) => { + return ( +
+ +
+ ); + }, + cell: ({ row }) => ( +
+ {row.getValue("fine")} +
+ ), + }, + { + id: "actions", + enableHiding: false, + cell: ({ row }) => { + const bill: any = row.original; + + return ( +
+ {/* + */} + + +
+ ); + }, + }, +]; + +export default function CompletedBillListOfSTS() { + const [data, setData] = React.useState([]); + const { billList, getbillList } = useGetAllSTSBills(); + 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/modals/ContractorControl/AddNewContractor.tsx b/client/components/modals/ContractorControl/AddNewContractor.tsx new file mode 100644 index 0000000..bdb6360 --- /dev/null +++ b/client/components/modals/ContractorControl/AddNewContractor.tsx @@ -0,0 +1,234 @@ +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, { useEffect, useState } from "react"; +import SetZone from "../../maps/SetZone"; +import useCreateSTS, { STS } from "@/hooks/entityCreation/useCreateSTS"; +import { message } from "antd"; +import { Contractor } from "@/data/roles"; +import useCreateContractor from "@/hooks/entityCreation/useCreateContractor"; +import useGetAllSTS from "@/hooks/stsdata/useGetAllSTS"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +interface DialogWrapperProps { + children: React.ReactNode; +} + +export const AddNewContractor: React.FC = ({ + children, +}) => { + const [name, setName] = useState(""); + const [registrationId, setRegId] = useState(""); + const [tinNumber, setTinNumber] = useState(""); + const [contactNumber, setContactNumber] = useState(""); + const [workforceSize, setWorkForceSize] = useState(0); + const [paymentPerTon, setPaymentPerTon] = useState(0); + const [requiredWastePerDay, setRequiredWastePerDay] = useState(0); + const [contractDuration, setContractDuration] = useState(""); + const [area, setArea] = useState(""); + const [stsId, setStsId] = useState(""); + const { createContractor } = useCreateContractor(); + + const { stsList } = useGetAllSTS(); + useEffect(() => {}, [stsList]); + + const handleSaveChanges = async () => { + const data: Contractor = { + name, + registrationId, + tinNumber, + contactNumber, + workforceSize, + paymentPerTon, + requiredWastePerDay, + contractDuration, + area, + stsId, + }; + + console.log(data); + (await createContractor(data)) + ? message.success("Contractor added successfully") + : message.error("Contractor data invalid"); + }; + + return ( + + {children} + + + ADD NEW CONTRACTOR + + Add new Contractor here. Click save when you're done. + + +
+
+ + setName(e.target.value)} + /> +
+
+ + setRegId(e.target.value)} + /> +
+ +
+ + setTinNumber(e.target.value)} + /> +
+
+ + setContactNumber(e.target.value)} + /> +
+
+ + setWorkForceSize(parseInt(e.target.value))} + /> +
+
+ + setPaymentPerTon(parseInt(e.target.value))} + /> +
+
+ + setRequiredWastePerDay(parseInt(e.target.value))} + /> +
+
+ + setContractDuration(e.target.value)} + /> +
+
+ + setArea(e.target.value)} + /> +
+
+ + +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/ContractorControl/AddNewContractorManager.tsx b/client/components/modals/ContractorControl/AddNewContractorManager.tsx new file mode 100644 index 0000000..4447f36 --- /dev/null +++ b/client/components/modals/ContractorControl/AddNewContractorManager.tsx @@ -0,0 +1,157 @@ +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, { useEffect, useState } from "react"; +import SetZone from "../../maps/SetZone"; +import useCreateSTS, { STS } from "@/hooks/entityCreation/useCreateSTS"; +import { message } from "antd"; +import { Contractor, ContractorManager } from "@/data/roles"; +import useCreateContractor from "@/hooks/entityCreation/useCreateContractor"; +import useCreateContractorManager from "@/hooks/entityCreation/useCreateContractorManager"; +import useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; + +interface DialogWrapperProps { + children: React.ReactNode; +} + +export const AddNewContractorManager: React.FC = ({ + children, +}) => { + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); + const [contactNumber, setContactNumber] = useState(""); + const [password, setPassword] = useState(""); + const [contractorId, setContractorId] = useState(""); + const {createContractorManager} = useCreateContractorManager(); + const { contractorData } = useGetAllContractor(); + + useEffect(() => { + console.log(contractorData); + } , [contractorData]); + + const handleSaveChanges = async () => { + const data: ContractorManager = { + username, + email, + contactNumber, + password, + contractorId + }; + + console.log(data); + (await createContractorManager(data)) + ? message.success("Contractor Manager added successfully") + : message.error("Contractor Manager data invalid"); + }; + + return ( + + {children} + + + ADD NEW CONTRACTOR MANAGER + + Add new Contractor Manager here. Click save when you're done. + + +
+
+ + setUsername(e.target.value)} + /> +
+
+ + setEmail(e.target.value)} + /> +
+ +
+ + setPassword(e.target.value)} + /> +
+
+ + setContactNumber(e.target.value)} + /> +
+
+ + +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/cleanerControl/AddNewCleaner.tsx b/client/components/modals/cleanerControl/AddNewCleaner.tsx new file mode 100644 index 0000000..b08e1eb --- /dev/null +++ b/client/components/modals/cleanerControl/AddNewCleaner.tsx @@ -0,0 +1,131 @@ +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, { useEffect, useState } from "react"; +import SetZone from "../../maps/SetZone"; +import useCreateSTS, { STS } from "@/hooks/entityCreation/useCreateSTS"; +import { message } from "antd"; +import { Contractor, ContractorManager } from "@/data/roles"; +import useCreateContractor from "@/hooks/entityCreation/useCreateContractor"; +import useCreateContractorManager from "@/hooks/entityCreation/useCreateContractorManager"; +import useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; + +interface DialogWrapperProps { + children: React.ReactNode; +} + +export const AddNewCleaner: React.FC = ({ + children, +}) => { + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); + const [contactNumber, setContactNumber] = useState(""); + const [password, setPassword] = useState(""); + const [contractorId, setContractorId] = useState(""); + const {createContractorManager} = useCreateContractorManager(); + const { contractorData } = useGetAllContractor(); + + useEffect(() => { + console.log(contractorData); + } , [contractorData]); + + const handleSaveChanges = async () => { + const data: ContractorManager = { + username, + email, + contactNumber, + password, + contractorId + }; + + console.log(data); + // (await createContractorManager(data)) + // ? message.success("Contractor Manager added successfully") + // : message.error("Contractor Manager data invalid"); + }; + + return ( + + {children} + + + ADD NEW CLEANER + + Add new Cleaner here. Click save when you're done. + + +
+
+ + setUsername(e.target.value)} + /> +
+
+ + setEmail(e.target.value)} + /> +
+ +
+ + setPassword(e.target.value)} + /> +
+
+ + setContactNumber(e.target.value)} + /> +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/cleanerControl/AddNewEmployee.tsx b/client/components/modals/cleanerControl/AddNewEmployee.tsx new file mode 100644 index 0000000..ac7e2f0 --- /dev/null +++ b/client/components/modals/cleanerControl/AddNewEmployee.tsx @@ -0,0 +1,165 @@ +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, { useEffect, useState } from "react"; +import SetZone from "../../maps/SetZone"; +import useCreateSTS, { STS } from "@/hooks/entityCreation/useCreateSTS"; +import { message } from "antd"; +import { Contractor, ContractorManager, contractorEmployee } from "@/data/roles"; +import useCreateContractor from "@/hooks/entityCreation/useCreateContractor"; +import useCreateContractorManager from "@/hooks/entityCreation/useCreateContractorManager"; +import useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { getCookie } from "@/lib/cookieFunctions"; +import { contractorId } from "@/data/cookieNames"; +import useAddNewEmployee from "@/hooks/entityCreation/useAddNewEmployee"; + +interface DialogWrapperProps { + children: React.ReactNode; +} + +export const AddNewEmployee: React.FC = ({ children }) => { + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); + const [contactNumber, setContactNumber] = useState(""); + const [password, setPassword] = useState(""); + const [jobTitle, setJobTitle] = useState(""); + const [paymentRate, setPaymentRate] = useState(""); + const {createEmployee} = useAddNewEmployee(); +// const [routeId, setRouteId] = useState(""); + + + + const handleSaveChanges = async () => { + const data: any = { + username: username, + email: email, + contactNumber: contactNumber, + password: password, + jobTitle: jobTitle, + roleName: contractorEmployee, + paymentRatePerHour: paymentRate, + contractorId: getCookie(contractorId), + }; + + console.log(data); + (await createEmployee(data)) + ? message.success("Employee added successfully") + : message.error("Contractor Manager data invalid"); + }; + + return ( + + {children} + + + ADD NEW EMPLOYEE + + Add new Employee here. Click save when you're done. + + +
+
+ + setUsername(e.target.value)} + /> +
+
+ + setEmail(e.target.value)} + /> +
+
+ + setPassword(e.target.value)} + /> +
+
+ + setJobTitle(e.target.value)} + /> +
+
+ + setContactNumber(e.target.value)} + /> +
+
+ + setPaymentRate(e.target.value)} + /> +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/stsControl/AddNewRoute.tsx b/client/components/modals/stsControl/AddNewRoute.tsx new file mode 100644 index 0000000..26d430e --- /dev/null +++ b/client/components/modals/stsControl/AddNewRoute.tsx @@ -0,0 +1,138 @@ +"use client"; +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, { useState, useEffect } from "react"; + +import "react-datepicker/dist/react-datepicker.css"; + +import useGetSTSAvailableVehicles from "@/hooks/vehicles/useSTSAvailableVehicles"; +import { message } from "antd"; +import useGetAllArea from "@/hooks/dataQuery/useGetAllArea"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; +import axios from "axios"; +import { apiRoutes } from "@/data/apiRoutes"; +import { stsId } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; + +interface DialogWrapperProps { + children: React.ReactNode; +} + +export const AddNewRouteModal: React.FC = ({ children }) => { + const [areaId, setAreaId] = useState(""); + const [RouteName, setRouteName] = useState(""); + const [RouteDetails, setRouteDetails] = useState(""); + const { areaData, fetchAllArea } = useGetAllArea(); + const handleSaveChanges = async () => { + axios.post(apiRoutes.route.create, { name:RouteName, stsId:getCookie(stsId), description: RouteDetails, areaId: areaId }, { + headers: { + Authorization: `Bearer ${getCookie("token")}`, + }, + }).then((res) => { + console.log(res.data); + message.success("Area added successfully!"); + window.location.reload(); + }).catch((err) => { + console.log(err); + message.error("Failed to add area!"); + }); + }; + + useEffect(() => { + fetchAllArea(); + }, []); + + useEffect(() => { + console.log(areaData); + }, [areaData]); + + return ( + + + {/* */} + {children} + + + + Add New Route + + Add new route here. Click save when you're done. + + +
+
+ + setRouteName(e.target.value)} + className="col-span-3" + /> +
+
+
+ + +
+
+
+ + setRouteDetails(e.target.value)} + className="col-span-3" + /> +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/stsControl/addNewArea.tsx b/client/components/modals/stsControl/addNewArea.tsx new file mode 100644 index 0000000..4215815 --- /dev/null +++ b/client/components/modals/stsControl/addNewArea.tsx @@ -0,0 +1,85 @@ +"use client"; +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, { useState, useEffect } from "react"; + +import "react-datepicker/dist/react-datepicker.css"; + +import useGetSTSAvailableVehicles from "@/hooks/vehicles/useSTSAvailableVehicles"; +import { message } from "antd"; +import axios from "axios"; +import { apiRoutes } from "@/data/apiRoutes"; +import { getCookie } from "@/lib/cookieFunctions"; +import { stsId } from "@/data/cookieNames"; + +interface DialogWrapperProps { + children: React.ReactNode; +} + +export const AddNewAreaModal: React.FC = ({ children }) => { + const [areaName, setAreaName] = useState(""); + const handleSaveChanges = async () => { + axios.post(apiRoutes.area.create, { name:areaName, stsId:getCookie(stsId) }, { + headers: { + Authorization: `Bearer ${getCookie("token")}`, + }, + }).then((res) => { + console.log(res.data); + message.success("Area added successfully!"); + window.location.reload(); + }).catch((err) => { + console.log(err); + message.error("Failed to add area!"); + }); + }; + + return ( + + + {/* */} + {children} + + + + Add New Area + + Add new area here. Click save when you're done. + + +
+
+ + setAreaName(e.target.value)} + className="col-span-3" + /> +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/stsControl/generateBill.tsx b/client/components/modals/stsControl/generateBill.tsx new file mode 100644 index 0000000..22e9d39 --- /dev/null +++ b/client/components/modals/stsControl/generateBill.tsx @@ -0,0 +1,144 @@ +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, ArrowDown } from "lucide-react"; + +import useEditProfileInfo from "@/hooks/user_data/useEditProfileInfo"; +import useGetUserProfile from "@/hooks/user_data/useGetUserProfile"; +import useUpdateSts from "@/hooks/StsDashboard/useUpdateSts"; +import { message } from "antd"; +import useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { apiRoutes } from "@/data/apiRoutes"; +import axios from "axios"; +import { getCookie } from "@/lib/cookieFunctions"; +import { jwtToken } from "@/data/cookieNames"; + +type DumpEntry = { + stsId: string; + entryTime: string; + contractorId: string; + wasteType: string; + wasteWeight: number; + vehicleNumber: string; +}; + +export const GenerateBill = () => { + const { user, stsDetails, landfillDetails, getUserDetails } = + useGetUserProfile(); + + const [dumpEntry, setDumpEntry] = useState(); + + const {contractorData, fetchAllContractors} = useGetAllContractor(); + + useEffect(() => { + fetchAllContractors(); + }, []); + + useEffect(() => { + if (contractorData) { + console.log(contractorData); + } + }, [contractorData]); + + const [wastageEntry, setWastageEntry] = useState( + stsDetails.stsCurrentTotalWaste + ); + + const { UpdateSts } = useUpdateSts(); + + const handleSaveChanges = async () => { + if (dumpEntry) { + const data = { + stsId: stsDetails?.stsId, + contractorId: dumpEntry?.contractorId, + }; + console.log(data); + const response = await axios.post(apiRoutes.sts.makeBill, data, + { + headers: { + "Content-Type": "application/json", + "Bearer": getCookie(jwtToken), + }, + } + ); + console.log(response); + if (response.status === 200 || response.status === 201) { + message.success("Bill creation Successful"); + window.location.reload(); + } else { + message.error("Bill creation Failed"); + } + } + }; + + useEffect(() => { + getUserDetails(); + }, []); + + return ( + + + + + + + + Make bills for this week + + + + +
+ +
+ + +
+
+ + + + + +
+
+ ); +}; diff --git a/client/components/modals/stsControl/updateSTSStorage.tsx b/client/components/modals/stsControl/updateSTSStorage.tsx index bcabd6d..c026f19 100644 --- a/client/components/modals/stsControl/updateSTSStorage.tsx +++ b/client/components/modals/stsControl/updateSTSStorage.tsx @@ -19,22 +19,40 @@ import useEditProfileInfo from "@/hooks/user_data/useEditProfileInfo"; import useGetUserProfile from "@/hooks/user_data/useGetUserProfile"; import useUpdateSts from "@/hooks/StsDashboard/useUpdateSts"; import { message } from "antd"; +import useGetAllContractor from "@/hooks/dataQuery/useGetAllContractor"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { apiRoutes } from "@/data/apiRoutes"; +import axios from "axios"; +import { getCookie } from "@/lib/cookieFunctions"; +import { jwtToken } from "@/data/cookieNames"; -type User = { - id: string; - username: string; - email: string; - profileName: string; - roleName: string; - roleDescription: string; +type DumpEntry = { + stsId: string; + entryTime: string; + contractorId: string; + wasteType: string; + wasteWeight: number; + vehicleNumber: string; }; export const UpdateStsStorage = () => { const { user, stsDetails, landfillDetails, getUserDetails } = useGetUserProfile(); - const [username, setUsername] = useState(user.username); - const [profilename, setProfilename] = useState(user.profileName); + const [dumpEntry, setDumpEntry] = useState(); + + const {contractorData, fetchAllContractors} = useGetAllContractor(); + + useEffect(() => { + fetchAllContractors(); + }, []); + + useEffect(() => { + if (contractorData) { + console.log(contractorData); + } + }, [contractorData]); + const [wastageEntry, setWastageEntry] = useState( stsDetails.stsCurrentTotalWaste ); @@ -42,19 +60,33 @@ export const UpdateStsStorage = () => { const { UpdateSts } = useUpdateSts(); const handleSaveChanges = async () => { - try { - const remainingCapacity = - parseInt(stsDetails.stsCurrentTotalWaste) + parseInt(wastageEntry); - const postEntry = await UpdateSts({ - storedData: remainingCapacity, - stsId: stsDetails.stsId, - }); - - if (postEntry) return message.success(postEntry); - } catch (error) { - console.error("Error:", error); - } + if (dumpEntry) { + const data = { + stsId: stsDetails?.stsId, + contractorId: dumpEntry?.contractorId, + wasteType: dumpEntry?.wasteType, + wasteWeight: dumpEntry?.wasteWeight, + vehicleNumber: dumpEntry?.vehicleNumber, + }; + console.log(data); + const response = await axios.post(apiRoutes.sts.addIncomingDump, data, + { + headers: { + "Content-Type": "application/json", + "Bearer": getCookie(jwtToken), + }, + } + ); + console.log(response); + if (response.status === 200 || response.status === 201) { + message.success("Dump Entry Successful"); + window.location.reload(); + } else { + message.error("Dump Entry Failed"); + } + } }; + useEffect(() => { getUserDetails(); }, []); @@ -100,20 +132,69 @@ export const UpdateStsStorage = () => {
setWastageEntry(e.target.value)} + type="number" + value={dumpEntry?.wasteWeight} + onChange={(e) => setDumpEntry({ ...dumpEntry, wasteWeight: (e.target.value || "") })} + /> +
+
+ + +
+
+ + setDumpEntry({ ...dumpEntry, wasteType: (e.target.value || "") })} + /> +
+
+ + setDumpEntry({ ...dumpEntry, vehicleNumber: (e.target.value || "") })} />
- diff --git a/client/data/apiRoutes.ts b/client/data/apiRoutes.ts index 10610f5..d3322aa 100644 --- a/client/data/apiRoutes.ts +++ b/client/data/apiRoutes.ts @@ -15,12 +15,27 @@ export const apiRoutes = { delete: `${baseUrl}/users/`, edit: `${baseUrl}/users/`, }, + area: { + create: `${baseUrl}/route-areas/add-area`, + getAll: `${baseUrl}/route-areas/area`, + delete: `${baseUrl}/areas/`, + edit: `${baseUrl}/areas/`, + }, + route: { + create: `${baseUrl}/route-areas/add-route`, + getAll: `${baseUrl}/route-areas/routes`, + delete: `${baseUrl}/areas/`, + edit: `${baseUrl}/areas/`, + }, sts: { create: `${baseUrl}/sts/create`, getAll: `${baseUrl}/sts`, delete: `${baseUrl}/sts/`, edit: `${baseUrl}/sts/`, - getById: `${baseUrl}/sts/`, + getById: `${baseUrl}/sts/`, + makeBill: `${baseUrl}/contractor-bills/generate`, + getAllBills: `${baseUrl}/contractor-bills`, + addIncomingDump: `${baseUrl}/sts-contractor-entry/create`, vehicle: { create: `${baseUrl}/sts-entry/create`, delete: `${baseUrl}/sts-entry/`, @@ -29,6 +44,24 @@ export const apiRoutes = { current: `${baseUrl}/sts-entry/`, }, }, + contractor: { + create: `${baseUrl}/contractors/create`, + getAll: `${baseUrl}/contractors`, + delete: `${baseUrl}/contractors/`, + edit: `${baseUrl}/contractors/`, + manager: { + create: `${baseUrl}/auth/createmanager`, + getAll: `${baseUrl}/contractor-managers`, + delete: `${baseUrl}/contractor-managers/`, + edit: `${baseUrl}/contractor-managers/`, + }, + }, + employee: { + create: `${baseUrl}/auth/createempolyee`, + getAll: `${baseUrl}/employees`, + delete: `${baseUrl}/employees/`, + edit: `${baseUrl}/employees/`, + }, landfill: { getbyId: `${baseUrl}/landfills/`, create: `${baseUrl}/landfills/create`, @@ -70,4 +103,18 @@ export const apiRoutes = { getProfile: `${baseUrl}/profile`, edit: `${baseUrl}/profile`, }, + logs: { + getAll: `${baseUrl}/logs/admin`, + }, + plans: { + create: `${baseUrl}/collection-plans/create`, + getSchedules: `${baseUrl}/collection-plans/all`, + getAll: `${baseUrl}/plans`, + delete: `${baseUrl}/plans/`, + edit: `${baseUrl}/plans/`, + }, + issue: { + create: `${baseUrl}/issues/all`, + + }, }; diff --git a/client/data/cookieNames.tsx b/client/data/cookieNames.tsx index 979e7eb..6c1a58e 100644 --- a/client/data/cookieNames.tsx +++ b/client/data/cookieNames.tsx @@ -5,6 +5,8 @@ export const username='username'; export const stsId='stsId'; export const curActive = "curActivee"; export const landfillId = "landfillId"; +export const contractorId = "contractorId"; export const landfillName = "landfillName"; +export const companyName = "companyName"; export const stsName = "stsName"; export const otpToken = "otpToken"; \ No newline at end of file diff --git a/client/data/mainContentElements/getAdminContents.tsx b/client/data/mainContentElements/getAdminContents.tsx index 4838c0c..6ef4340 100644 --- a/client/data/mainContentElements/getAdminContents.tsx +++ b/client/data/mainContentElements/getAdminContents.tsx @@ -7,6 +7,8 @@ import AdminRolesManagementPanel from "../../components/dashboard-componenets/ma import AdminUserManagementPanel from "../../components/dashboard-componenets/mainContent/systemAdminContents/Users"; import AdminBillsManagementPanel from "../../components/dashboard-componenets/mainContent/systemAdminContents/Bills"; import AdminSettingsPanel from "../../components/dashboard-componenets/mainContent/systemAdminContents/Settings"; +import AdminContractsPanel from "@/components/dashboard-componenets/mainContent/systemAdminContents/Contracts"; +import AdminWorkforcePanel from "@/components/dashboard-componenets/mainContent/systemAdminContents/Workforce"; export function getContentsOfAdmin(state: string) { switch (state) { @@ -22,8 +24,12 @@ export function getContentsOfAdmin(state: string) { return ; case admin + "-Bills": return ; + case admin + "-Contracts": + return ; + case admin + "-Workforce": + return ; case admin + "-Settings": - return ; + return ; default: return InvalidSate(); } diff --git a/client/data/mainContentElements/getContractorManagerContents.tsx b/client/data/mainContentElements/getContractorManagerContents.tsx new file mode 100644 index 0000000..337130c --- /dev/null +++ b/client/data/mainContentElements/getContractorManagerContents.tsx @@ -0,0 +1,30 @@ +import InvalidSate from "./InvalidState"; +import { contractorManager } from "@/data/roles"; +import UnassignedMyProfilePanel from "../../components/dashboard-componenets/mainContent/unassignedContents/MyProfile"; +import UnassignedContactAdmiPanel from "../../components/dashboard-componenets/mainContent/unassignedContents/ContactAdmin"; +import UnassignedSettingsPanel from "../../components/dashboard-componenets/mainContent/unassignedContents/Settings"; +import EmployeePanel from "@/components/dashboard-componenets/mainContent/contractManagerContents/Employees"; +import CollectionPlanPanel from "@/components/dashboard-componenets/mainContent/contractManagerContents/CollectionPlan"; +import ContractorManagerSchedule from "@/components/dashboard-componenets/mainContent/contractManagerContents/Schedule"; +import ContractManagerDashboard from "@/components/dashboard-componenets/mainContent/contractManagerContents/Dashboard"; +import ContractManagerBillManagementPanel from "@/components/dashboard-componenets/mainContent/contractManagerContents/Bills"; +import CompanyDetails from "@/components/dashboard-componenets/mainContent/contractManagerContents/CompanyDetails"; + +export function getContentsOfContractorManager(state: string) { + switch (state) { + case contractorManager + "-Dashboard": + return ; + case contractorManager + "-Employees": + return ; + case contractorManager + "-Bills": + return ; + case contractorManager + "-Collection Plan": + return ; + case contractorManager + "-Schedule": + return ; + case contractorManager + "-Company Details": + return ; + default: + return InvalidSate(); + } + } \ No newline at end of file diff --git a/client/data/mainContentElements/getDashboardFor.tsx b/client/data/mainContentElements/getDashboardFor.tsx index f101218..83b6a3b 100644 --- a/client/data/mainContentElements/getDashboardFor.tsx +++ b/client/data/mainContentElements/getDashboardFor.tsx @@ -1,9 +1,10 @@ import InvalidSate from "./InvalidState"; -import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { admin, landfillManager, stsManager, unassigned, contractorManager } from "@/data/roles"; import { getContentsOfAdmin } from "./getAdminContents"; import {getContentsOfLandfillManager} from "./getLandfillContents"; import { getContentsOfSTSManager } from "./getSTSContents"; import { getContentsOfUnassigned } from "./getUnassignedContents"; +import { getContentsOfContractorManager } from "./getContractorManagerContents"; export default function getDashboardFor(state: string | null) { @@ -12,5 +13,6 @@ export default function getDashboardFor(state: string | null) { else if (state?.startsWith(landfillManager)) return getContentsOfLandfillManager(state); else if (state?.startsWith(unassigned)) return getContentsOfUnassigned(state); + else if(state?.startsWith(contractorManager)) return getContentsOfContractorManager(state); else return InvalidSate(); } \ No newline at end of file diff --git a/client/data/mainContentElements/getSTSContents.tsx b/client/data/mainContentElements/getSTSContents.tsx index d171e2f..e6d1911 100644 --- a/client/data/mainContentElements/getSTSContents.tsx +++ b/client/data/mainContentElements/getSTSContents.tsx @@ -5,6 +5,8 @@ import STSManagerSchedules from "../../components/dashboard-componenets/mainCont import STSManagerStorageData from "../../components/dashboard-componenets/mainContent/stsManagerContents/Storage"; import STSManagerDumpEntries from "../../components/dashboard-componenets/mainContent/stsManagerContents/DumpEntry"; import STSManagerSettings from "../../components/dashboard-componenets/mainContent/stsManagerContents/Settings"; +import RoutesPanel from "@/components/dashboard-componenets/mainContent/stsManagerContents/RoutesPanel"; +import STSWasteCollection from "@/components/dashboard-componenets/mainContent/stsManagerContents/WasteCollection"; export function getContentsOfSTSManager(state: string) { switch (state) { @@ -16,6 +18,10 @@ export function getContentsOfSTSManager(state: string) { return ; case stsManager + "-Dump Entry": return ; + case stsManager + "-Routes": + return ; + case stsManager + "-Bills": + return ; case stsManager + "-Settings": return ; default: diff --git a/client/data/roles.tsx b/client/data/roles.tsx index dd6f73b..0713b38 100644 --- a/client/data/roles.tsx +++ b/client/data/roles.tsx @@ -2,5 +2,49 @@ export const admin = "SYSTEM_ADMIN"; export const stsManager = "STS_MANAGER"; export const landfillManager = "LAND_MANAGER"; export const unassigned = "UNASSIGNED"; +export const contractorManager = "CONTRACTOR_MANAGER"; +export const contractorEmployee = "CONTRACTOR_EMPLOYEE"; + +export const roleList = [unassigned, admin, landfillManager, stsManager]; + +export type ContractorManager = { + id?: string; + username: string; + email: string; + password?: string; + contactNumber: string; + createdAt?: string; + contractorId: string; +}; + +export type Contractor = { + id?: string; + name: string; + registrationId: string; + tinNumber: string; + contactNumber: string; + workforceSize: number; + paymentPerTon: number; + requiredWastePerDay: number; + contractDuration: string; + area: string; + stsId: string; + stsName?: string; + manager?: ContractorManager; +}; + +export type Employee = { + id?: string; + username: string; + email: string; + password: string; + roleName: string; + dateOfBirth: string; + dateOfHire: string; + jobTitle: string; + paymentRatePerHour: number; + routeId: string; + contactNumber: string; + contractorId: string; +}; -export const roleList = [unassigned, admin, landfillManager, stsManager]; \ No newline at end of file diff --git a/client/data/sidebarElements/adminSidebar.ts b/client/data/sidebarElements/adminSidebar.ts index 77e3d9f..00c244d 100644 --- a/client/data/sidebarElements/adminSidebar.ts +++ b/client/data/sidebarElements/adminSidebar.ts @@ -7,11 +7,13 @@ import { LucideIcon, Package, Package2, + Pickaxe, Receipt, ScanFace, Settings, ShoppingCart, Truck, + UserCog, Users, } from "lucide-react"; import { admin } from "../roles"; @@ -37,6 +39,14 @@ export const AdminSidebarItemList = [ Icon: Receipt, title: "Bills", }, + { + Icon: UserCog, + title: "Contracts", + }, + { + Icon: Pickaxe, + title: "Workforce", + }, { Icon: Settings, title: "Settings", diff --git a/client/data/sidebarElements/contractorManagerSidebar.tsx b/client/data/sidebarElements/contractorManagerSidebar.tsx new file mode 100644 index 0000000..2b552ee --- /dev/null +++ b/client/data/sidebarElements/contractorManagerSidebar.tsx @@ -0,0 +1,57 @@ +import { + Bell, + BookOpen, + Building2, + CalendarCheck, + CircleUserRound, + Database, + History, + Home, + LayoutDashboard, + LineChart, + LucideIcon, + MessageCircle, + Package, + Package2, + Receipt, + ScanFace, + Send, + Settings, + ShoppingCart, + Truck, + User2, + Users, + } from "lucide-react"; +import { unassigned } from "../roles"; + +export const ContractorManagerSidebarItemList = [ + { + Icon: Home, + title: "Dashboard", + }, + { + Icon: User2, + title: "Employees", + },{ + Icon: Receipt, + title: "Bills", + }, + { + Icon: BookOpen, + title: "Collection Plan", + }, + { + Icon: History, + title: "Schedule", + }, + { + Icon: Building2, + title: "Company Details", + } + ]; + + export const unassignedStateList = () => { + ContractorManagerSidebarItemList.map((item) => { + return unassigned + "-" + item.title; + }); + }; \ No newline at end of file diff --git a/client/data/sidebarElements/getSidebarByRole.ts b/client/data/sidebarElements/getSidebarByRole.ts index 9a3e825..a69d1cc 100644 --- a/client/data/sidebarElements/getSidebarByRole.ts +++ b/client/data/sidebarElements/getSidebarByRole.ts @@ -2,8 +2,9 @@ import { LucideIcon } from "lucide-react"; import { AdminSidebarItemList } from "./adminSidebar"; import { LandfillManagerSidebarItemList } from "./landfillManagerSidebar"; import { STSManagerSidebarItemList } from "./stsManagerSidebar"; -import { admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { admin, contractorManager, landfillManager, stsManager, unassigned } from "@/data/roles"; import { UnassignedSidebarItemList } from "./unassignedSidebar"; +import { ContractorManagerSidebarItemList } from "./contractorManagerSidebar"; export interface SidebarElement {Icon: LucideIcon, title: string} @@ -12,5 +13,6 @@ export const getSidebarElements = (role: string):SidebarElement[] => { else if (role === landfillManager) return LandfillManagerSidebarItemList; else if (role === stsManager) return STSManagerSidebarItemList; else if(role === unassigned) return UnassignedSidebarItemList; + else if(role === contractorManager) return ContractorManagerSidebarItemList; return []; } \ No newline at end of file diff --git a/client/data/sidebarElements/stsManagerSidebar.ts b/client/data/sidebarElements/stsManagerSidebar.ts index 5114f27..cf9e9d6 100644 --- a/client/data/sidebarElements/stsManagerSidebar.ts +++ b/client/data/sidebarElements/stsManagerSidebar.ts @@ -1,8 +1,10 @@ import { Bell, CalendarCheck, + Car, Database, Home, + HomeIcon, LayoutDashboard, LineChart, LucideIcon, @@ -34,6 +36,14 @@ export const STSManagerSidebarItemList = [ Icon: Truck, title: "Dump Entry", }, + { + Icon: Car, + title: "Routes" + }, + { + Icon: Receipt, + title: "Bills" + }, { Icon: Settings, title: "Settings", diff --git a/client/hooks/auth/useLogin.tsx b/client/hooks/auth/useLogin.tsx index ced3f08..b6bb632 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, { Axios, AxiosError } from 'axios'; -import { jwtToken, role , uid , stsId, username, curActive, landfillId, landfillName, stsName} from '@/data/cookieNames'; +import { jwtToken, role , uid , stsId, username, curActive, landfillId, landfillName, stsName, contractorId} from '@/data/cookieNames'; import { apiRoutes } from "@/data/apiRoutes"; import { message } from 'antd'; @@ -41,6 +41,8 @@ export default function useLogin() { setCookie(landfillName, res.data?.user?.landfill?.name, 1); setCookie(stsName, res.data?.user?.sts?.name, 1); + setCookie(contractorId, res.data?.user?.contractorId, 1); + //setCookie(companyName, res.data?.user?.company?.name, 1); message.success("Login successful! Welcome to the EcoSync, " + res.data.user.username + "!"); return true; diff --git a/client/hooks/bills/useGetAllSTSBills.tsx b/client/hooks/bills/useGetAllSTSBills.tsx new file mode 100644 index 0000000..a766407 --- /dev/null +++ b/client/hooks/bills/useGetAllSTSBills.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, role } from "@/data/cookieNames"; +import { getCookie } from "@/lib/cookieFunctions"; +import { Trip } from "@/components/dataTables/PendingBillList"; +import { Bill } from "@/components/dataTables/CompletedBillList"; +import { message } from "antd"; +import { admin } from "@/data/roles"; + +export default function useGetAllSTSBills() { + const [billList, setbillList] = useState([]); // Initialize with an empty array of Vehicle objects + + async function getbillList(landfillId: string) { + try { + + let url = apiRoutes.sts.getAllBills; + + const res = await axios.get(url, { + headers: { Authorization: `Bearer ${getCookie(jwtToken)}` }, + }); + + //message.success("Bills list fetched successfully"); + + // Assuming the response data is an array of vehicles + const allBills: any = res.data.map((bill: any) => ({ + contractorName: bill.contractor.name, + weightRequired: bill.weightRequired, + weightCollected: bill.weightCollected, + paymentAmount: bill.paymentAmount, + fine: bill.fine, + })); + + console.log(allBills); + + setbillList(allBills); + + return true; + } catch (error: any) { + message.error(error?.response?.data.message?.toString() || "Error fetching pending bills list"); + return false; + } + } + + return { billList, getbillList }; +} diff --git a/client/hooks/dataQuery/useGetAllArea.tsx b/client/hooks/dataQuery/useGetAllArea.tsx new file mode 100644 index 0000000..d918d80 --- /dev/null +++ b/client/hooks/dataQuery/useGetAllArea.tsx @@ -0,0 +1,35 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + +export default function useGetAllArea() { + const [areaData, setAreaData] = useState([]); + + async function fetchAllArea() { + try { + const res = await axios.get(apiRoutes.area.getAll, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + const areaList = res.data.map((area: any) => { + return area; + }); + await setAreaData(areaList); + console.log(areaList); + } catch (error: any) { + message.error(error?.response?.data?.message + "Error fetching area data... Are you authorized?"); + } + } + + useEffect(() => { + fetchAllArea(); + }, []); + + return {fetchAllArea, areaData}; +} diff --git a/client/hooks/dataQuery/useGetAllContractor.tsx b/client/hooks/dataQuery/useGetAllContractor.tsx new file mode 100644 index 0000000..5de066f --- /dev/null +++ b/client/hooks/dataQuery/useGetAllContractor.tsx @@ -0,0 +1,35 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + +export default function useGetAllContractor() { + const [contractorData, setContractorData] = useState([]); + + async function fetchAllContractors() { + try { + const res = await axios.get(apiRoutes.contractor.getAll, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + const contractorList = res.data.map((contractor: any) => { + return {...contractor, assignedSTS: contractor.assignedSTS.name || "Not Assigned"}; + }); + await setContractorData(contractorList); + console.log(contractorList); + } catch (error: any) { + message.error(error?.response?.data?.message + "Error fetching contractor data... Are you authorized?"); + } + } + + useEffect(() => { + fetchAllContractors(); + }, []); + + return {fetchAllContractors, contractorData}; +} diff --git a/client/hooks/dataQuery/useGetAllEmployee.tsx b/client/hooks/dataQuery/useGetAllEmployee.tsx new file mode 100644 index 0000000..502901f --- /dev/null +++ b/client/hooks/dataQuery/useGetAllEmployee.tsx @@ -0,0 +1,35 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, Employee, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + +export default function useGetAllEmployees() { + const [employeeData, setEmployeeData] = useState([]); + + async function fetchAllEmployees() { + try { + const res = await axios.get(apiRoutes.employee.getAll, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + const employeeList = res.data.map((employee: any) => { + return employee; + }); + await setEmployeeData(employeeList); + console.log(employeeList); + } catch (error: any) { + message.error(error?.response?.data?.message + "Error fetching employee data... Are you authorized?"); + } + } + + useEffect(() => { + fetchAllEmployees(); + }, []); + + return {fetchAllEmployees, employeeData}; +} diff --git a/client/hooks/dataQuery/useGetAllLogs.tsx b/client/hooks/dataQuery/useGetAllLogs.tsx new file mode 100644 index 0000000..24cc401 --- /dev/null +++ b/client/hooks/dataQuery/useGetAllLogs.tsx @@ -0,0 +1,46 @@ +"use client"; +import { ContractorLog } from "@/components/dataTables/ContractorLogs"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + +export default function useGetAllLogs() { + const [contractorLog, setContractorLog] = useState([]); + + async function fetchAllContractorLog() { + try { + const res = await axios.get(apiRoutes.logs.getAll, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + const logList = res.data.map((log: any) => { + return { + type: log.type, + description: log.description, + time: new Date(log.createdAt).toLocaleString( + "en-US", + { + hour: "2-digit", + minute: "2-digit", + } + ), + }; + }); + await setContractorLog(logList); + console.log(logList); + } catch (error: any) { + message.error(error?.response?.data?.message + "Error fetching contractor log... Are you authorized?"); + } + } + + useEffect(() => { + fetchAllContractorLog(); + }, []); + + return {fetchAllContractorLog, contractorLog}; +} diff --git a/client/hooks/dataQuery/useGetAllRoutes.tsx b/client/hooks/dataQuery/useGetAllRoutes.tsx new file mode 100644 index 0000000..8970df1 --- /dev/null +++ b/client/hooks/dataQuery/useGetAllRoutes.tsx @@ -0,0 +1,58 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { + Contractor, + admin, + landfillManager, + stsManager, + unassigned, +} from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; +import { useState, useEffect, use } from "react"; + +export default function useGetAllRoutes() { + const [routesData, setRoutesData] = useState< + { + id: string; + name: string; + description: string; + areaId: string; + stsId: string; + areaName: string; + }[] + >([]); + + async function fetchAllRoutes() { + try { + const res = await axios.get(apiRoutes.route.getAll, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + const routesList = res.data.map((route: any) => { + return { + id: route?.id, + name: route?.name || "No Name", + areaName: route?.area?.name || "No Area", + description: route?.description || "No Description", + }; + }); + await setRoutesData(routesList); + console.log(routesList); + } catch (error: any) { + message.error( + error?.response?.data?.message + + "Error fetching routes data... Are you authorized?" + ); + } + } + + useEffect(() => { + fetchAllRoutes(); + }, []); + + return { fetchAllRoutes, routesData }; +} diff --git a/client/hooks/entityCreation/useAddNewEmployee.tsx b/client/hooks/entityCreation/useAddNewEmployee.tsx new file mode 100644 index 0000000..3fbc43c --- /dev/null +++ b/client/hooks/entityCreation/useAddNewEmployee.tsx @@ -0,0 +1,41 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, ContractorManager, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; + +export default function useAddNewEmployee() { +// const [stsData, setStsData] = useState(); + + function isValid(employeeData: any) { + + return ( + employeeData.username.length > 0 && + employeeData.contactNumber.length > 0 && + employeeData.contractorId.length !== null + ); + } + + async function createEmployee(employeeData: ContractorManager) { + if (employeeData && isValid(employeeData)) { + try { + const res = await axios.post(apiRoutes.employee.create, employeeData, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + window.location.reload(); + return "Contructor Manager Aadded successfully"; + } catch (error: any) { + message.error(error.message?.toString() || "Error creating Contructor Manager"); + return null; + } + } + + return null; + } + + return { createEmployee }; +} diff --git a/client/hooks/entityCreation/useCreateContractor.tsx b/client/hooks/entityCreation/useCreateContractor.tsx new file mode 100644 index 0000000..0f3b9a1 --- /dev/null +++ b/client/hooks/entityCreation/useCreateContractor.tsx @@ -0,0 +1,41 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; + +export default function useCreateContractor() { +// const [stsData, setStsData] = useState(); + + function isValid(contractorData: Contractor) { + + return ( + contractorData.name.length > 0 && + contractorData.contactNumber.length > 0 && + contractorData.tinNumber.length !== null + ); + } + + async function createContractor(contractorData: Contractor) { + if (contractorData && isValid(contractorData)) { + try { + const res = await axios.post(apiRoutes.contractor.create, contractorData, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + window.location.reload(); + return "Contructor Aadded successfully"; + } catch (error: any) { + message.error(error.message?.toString() || "Error creating Contructor"); + return null; + } + } + + return null; + } + + return { createContractor }; +} diff --git a/client/hooks/entityCreation/useCreateContractorManager.tsx b/client/hooks/entityCreation/useCreateContractorManager.tsx new file mode 100644 index 0000000..6c00ba7 --- /dev/null +++ b/client/hooks/entityCreation/useCreateContractorManager.tsx @@ -0,0 +1,41 @@ +"use client"; +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { Contractor, ContractorManager, admin, landfillManager, stsManager, unassigned } from "@/data/roles"; +import { getCookie } from "@/lib/cookieFunctions"; +import { message } from "antd"; +import axios from "axios"; + +export default function useCreateContractorManager() { +// const [stsData, setStsData] = useState(); + + function isValid(contractorData: ContractorManager) { + + return ( + contractorData.username.length > 0 && + contractorData.contactNumber.length > 0 && + contractorData.contractorId.length !== null + ); + } + + async function createContractorManager(contractorManagerData: ContractorManager) { + if (contractorManagerData && isValid(contractorManagerData)) { + try { + const res = await axios.post(apiRoutes.contractor.manager.create, contractorManagerData, { + headers: { + Authorization: `Bearer ${await getCookie(jwtToken)}`, + }, + }); + window.location.reload(); + return "Contructor Manager Aadded successfully"; + } catch (error: any) { + message.error(error.message?.toString() || "Error creating Contructor Manager"); + return null; + } + } + + return null; + } + + return { createContractorManager }; +} diff --git a/client/hooks/issues/useGetIssue.tsx b/client/hooks/issues/useGetIssue.tsx new file mode 100644 index 0000000..a6dadef --- /dev/null +++ b/client/hooks/issues/useGetIssue.tsx @@ -0,0 +1,47 @@ +import { apiRoutes } from "@/data/apiRoutes"; +import { jwtToken } from "@/data/cookieNames"; +import { message } from "antd"; +import axios from "axios"; +import { useState } from "react"; +import { getCookie } from "@/lib/cookieFunctions"; + +type Issue = { + issueType: string; + description: string; + latitude: string; + longitude: string; + isAnonymous: string; +}; + +export default function useGetIssue() { + const [issueData, setIssueData] = useState([]); + + async function getAllIssue() { + try { + const token = await getCookie(jwtToken); + const res = await axios.get(apiRoutes.issue.create, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + const allIssues: Issue[] = res.data.map((issue: any) => ({ + issueType: issue.issueType, + description: issue.description, + latitude: issue.latitude, + longitude: issue.longitude, + isAnonymous: issue.isAnonymous, + })); + + setIssueData(allIssues); + + return allIssues; + } catch (error: any) { + message.error( + (error?.response?.data?.message || "Error fetching issue data.") + + " Are you authorized?" + ); + } + } + + return { issueData, getAllIssue }; +} diff --git a/server/.DS_Store b/server/.DS_Store index d0668d7..f49d1f9 100644 Binary files a/server/.DS_Store and b/server/.DS_Store differ diff --git a/server/.env.example b/server/.env.example deleted file mode 100644 index 1f71591..0000000 --- a/server/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -PORT=8585 -DATABASE_URL="postgresql://postgres:YOURPASSWORDHERE@127.0.0.1:5432/ecosync" -JWT_SECRET=CHECK_DOC -MAIL_EMAIL=CHECK_DOC -MAIL_PASSWORD=CHECK_DOC -NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=CHECK_DOC \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 0e79a8c..19f93e2 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -27,7 +27,8 @@ "jsonwebtoken": "^9.0.2", "mailgen": "^2.0.28", "nodemailer": "^6.9.13", - "prisma": "^5.11.0" + "prisma": "^5.11.0", + "socket.io": "^4.7.5" }, "devDependencies": { "@types/express": "^4.17.21", @@ -168,6 +169,11 @@ "@prisma/debug": "5.11.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.10.tgz", @@ -219,6 +225,11 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "node_modules/@types/cors": { "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", @@ -494,6 +505,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -948,6 +967,63 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", @@ -2109,6 +2185,107 @@ "node": "*" } }, + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -2408,6 +2585,26 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/server/package.json b/server/package.json index 2f63944..8220b3f 100644 --- a/server/package.json +++ b/server/package.json @@ -6,11 +6,10 @@ "scripts": { "install": "cd ./src && npx prisma migrate dev --name init && createdb ecosync 2> /dev/null || echo 'database already exists'", "build": "tsc", - "install": "cd ./src && npx prisma migrate dev --name init && createdb ecosync 2> /dev/null || echo 'database already exists'", "start": "node build/index.js", "dev": "ts-node src/index.ts", "test": "echo \"Error: no test specified\" && exit 1", - "prisma": "npx prisma generate && npx prisma migrate dev" + "prisma": "npx prisma generate && npx prisma migrate dev" }, "keywords": [], "author": "", @@ -33,7 +32,8 @@ "jsonwebtoken": "^9.0.2", "mailgen": "^2.0.28", "nodemailer": "^6.9.13", - "prisma": "^5.11.0" + "prisma": "^5.11.0", + "socket.io": "^4.7.5" }, "devDependencies": { "@types/express": "^4.17.21", diff --git a/server/src/.DS_Store b/server/src/.DS_Store index 3c11c80..6f9f212 100644 Binary files a/server/src/.DS_Store and b/server/src/.DS_Store differ diff --git a/server/src/controllers/auth.ts b/server/src/controllers/auth.ts index 9db061e..5f02f70 100644 --- a/server/src/controllers/auth.ts +++ b/server/src/controllers/auth.ts @@ -1,6 +1,6 @@ import { Request, Response } from "express"; import errorWrapper from "../middlewares/errorWrapper"; -import { PrismaClient, User } from "@prisma/client"; +import { PrismaClient } from "@prisma/client"; import bcrypt from "bcrypt"; import { generateToken, @@ -13,6 +13,10 @@ import { randomOTPGenerator, randomPasswordGenerator } from "../services/utils"; import { sendMail, sendOTPMail } from "../services/mailService"; import { PERMISSIONS, getPermittedRoleNames } from "../permissions/permissions"; +import { adminLog, contractorLog } from "../services/logdata"; + +import { RoleName } from "../types/rolesTypes"; + const prisma = new PrismaClient(); const createUser = errorWrapper( @@ -49,6 +53,101 @@ const createUser = errorWrapper( { statusCode: 500, message: `Couldn't create user` } ); +const createManager = errorWrapper( + async (req: Request, res: Response) => { + const { username, password, email, roleName, contactNumber, contractorId } = + req.body; + const hashedPassword = await bcrypt.hash(password, 10); + + const user = await prisma.user.create({ + data: { + username, + email, + hashedPassword, + roleName: RoleName.CONTRACTOR_MANAGER, + contactNumber, + contractorId, + }, + }); + + const token = generateToken( + { + id: user.id, + role: user.roleName, + }, + "10h" + ); + + sendMail( + user, + `Welcome To EcoSync!`, + `Your account has been created by Admin! Here are the Credentials:`, + `username: ${username}
email: ${email}
password: ${password}

Regards,
EcoSync Team` + ); + + await adminLog(`Manager Entry`, `Manager ${user.username} added`); + + res.status(201).json({ user, token }); + }, + { statusCode: 500, message: `Couldn't create user` } +); + +const createEmployee = errorWrapper( + async (req: Request, res: Response) => { + const { + username, + password, + email, + roleName, + contactNumber, + contractorId, + dateOfBirth, + dateOfHire, + jobTitle, + paymentRatePerHour, + routeId, + } = req.body; + const hashedPassword = await bcrypt.hash(password, 10); + + const user = await prisma.user.create({ + data: { + username, + email, + hashedPassword, + roleName, + contactNumber, + dateOfBirth, + dateOfHire, + jobTitle, + paymentRatePerHour, + routeId, + contractorId, + }, + }); + + const token = generateToken( + { + id: user.id, + role: user.roleName, + }, + "10h" + ); + + sendMail( + user, + `Welcome To EcoSync!`, + `Your account has been created by Contractor Manager! Here are the Credentials:`, + `username: ${username}
email: ${email}
password: ${password}

Regards,
EcoSync Team` + ); + + await adminLog(`Employee Entry`, `Employee ${user.username} added`); + await contractorLog(`Employee Entry`, `Employee ${user.username} added`); + + res.status(201).json({ user, token }); + }, + { statusCode: 500, message: `Couldn't create user` } +); + const login = errorWrapper( async (req: Request, res: Response) => { const { email, password } = req.body; @@ -60,6 +159,7 @@ const login = errorWrapper( include: { landfill: true, sts: true, + Contractor: true, }, }); @@ -78,6 +178,18 @@ const login = errorWrapper( // console.log(roles); // console.log(user.roleName); + adminLog( + `Login`, + `User ${user.username}, Role: ${user.roleName} logged in` + ); + + if (user.roleName === RoleName.CONTRACTOR_EMPLOYEE) { + contractorLog( + `Login`, + `User ${user.username}, Role: ${user.roleName} logged in` + ); + } + if (!roles.includes(user.roleName)) { throw new CustomError("You are not allowed to login", 403); } @@ -100,6 +212,19 @@ const logout = errorWrapper( const token = getToken(req) || "no token"; invalidateToken(token); console.log("Logged Out Successfully"); + + if (req.user?.roleName === RoleName.CONTRACTOR_EMPLOYEE) { + await contractorLog( + `Logout`, + `User ${req.user.username}, Role: ${req.user.roleName} logged out` + ); + } + + await adminLog( + `Logout`, + `User ${req.user?.username}, Role: ${req.user?.roleName} logged out` + ); + res.json({ msg: "Logged Out Successfully" }); }, { statusCode: 500, message: `Logout Failed` } @@ -215,6 +340,8 @@ const resetPasswordConfirm = errorWrapper( export { createUser, + createManager, + createEmployee, login, logout, resetPasswordInit, diff --git a/server/src/controllers/collectionPlans.ts b/server/src/controllers/collectionPlans.ts new file mode 100644 index 0000000..5c4a14a --- /dev/null +++ b/server/src/controllers/collectionPlans.ts @@ -0,0 +1,84 @@ +import { PrismaClient } from "@prisma/client"; +import { Request, Response } from "express"; +import errorWrapper from "../middlewares/errorWrapper"; + +const prisma = new PrismaClient(); + +const addCollectionPlan = errorWrapper( + async (req: Request, res: Response) => { + const { + areaId, + collectionStartTime, + durationForCollection, + numberOfLaborers, + numberOfVans, + expectedWaste, + routes, + employees, + } = req.body; + const newCollectionPlan = await prisma.collectionPlan.create({ + data: { + areaId, + collectionStartTime, + durationForCollection, + numberOfLaborers, + numberOfVans, + expectedWaste, + }, + }); + + const assignments: {}[] = routes.map((route: any, index: number) => { + return { + routeId: route, + employeeId: employees[index], + }; + }); + + assignments.forEach(async (assignment: any) => { + await prisma.user.update({ + where: { + id: assignment.employeeId, + }, + data: { + routeId: assignment.routeId, + collectionPlanId: newCollectionPlan.id, + }, + }); + }); + + res.status(201).json(newCollectionPlan); + }, + { statusCode: 400, message: "Couldn't add collection plan" } +); + +const getAllCollectionPlans = errorWrapper( + async (req: Request, res: Response) => { + const collectionPlans = await prisma.collectionPlan.findMany({ + include: { + User: { + include: { + assignedRoute: true, + Contractor: true, + }, + }, + }, + }); + res.status(200).json(collectionPlans); + } +); + +const getCollectionPlansBySTS = errorWrapper( + async (req: Request, res: Response) => { + const stsId = req.params.stsId; + const collectionPlans = await prisma.collectionPlan.findMany({ + where: { + stsId: stsId, + }, + }); + + res.json(collectionPlans); + }, + { statusCode: 404, message: "Collection Plans not found" } +); + +export { addCollectionPlan, getAllCollectionPlans, getCollectionPlansBySTS }; diff --git a/server/src/controllers/contractor.ts b/server/src/controllers/contractor.ts new file mode 100644 index 0000000..c2acb0c --- /dev/null +++ b/server/src/controllers/contractor.ts @@ -0,0 +1,98 @@ +import { PrismaClient } from "@prisma/client"; +import { Request, Response } from "express"; +import errorWrapper from "../middlewares/errorWrapper"; +import CustomError from "../services/CustomError"; +import { adminLog } from "../services/logdata"; + +const prisma = new PrismaClient(); + +const addContractor = errorWrapper( + async (req: Request, res: Response) => { + const contractorInfo = req.body; + const contractor = await prisma.contractor.create({ + data: contractorInfo, + }); + + console.info("Contractor added", { contractor: contractor }); + + await adminLog("Contractor Entry", `Contractor ${contractor.name} added`); + + res.status(201).json(contractor); + }, + { statusCode: 400, message: "Couldn't add contractor" } +); + +const getAllContractors = errorWrapper( + async (req: Request, res: Response) => { + const contractors = await prisma.contractor.findMany({ + include: { + assignedSTS: true, + }, + }); + res.status(200).json(contractors); + }, + { statusCode: 500, message: "Couldn't fetch contractors" } +); + +const getContractorById = errorWrapper( + async (req: Request, res: Response) => { + const { contractorId } = req.params; + const contractor = await prisma.contractor.findUnique({ + where: { + id: contractorId, + }, + }); + + if (!contractor) { + throw new CustomError("Contractor not found", 404); + } + + res.status(200).json(contractor); + }, + { statusCode: 404, message: "Contractor not found" } +); + +const updateContractor = errorWrapper( + async (req: Request, res: Response) => { + const { contractorId } = req.params; + const contractorInfo = req.body; + const contractor = await prisma.contractor.update({ + where: { + id: contractorId, + }, + data: contractorInfo, + }); + + await adminLog( + "Contractor Entry", + `Contractor ${contractor.name} information updated` + ); + + res.status(200).json(contractor); + }, + { statusCode: 400, message: "Couldn't update contractor" } +); + +const deleteContractor = errorWrapper( + async (req: Request, res: Response) => { + const { contractorId } = req.params; + const contractor = await prisma.contractor.delete({ + where: { + id: contractorId, + }, + }); + + adminLog("Contractor Entry", `Contractor ${contractor.name} deleted`); + + res.status(200).json(contractor); + }, + { statusCode: 400, message: "Couldn't delete contractor" } +); + +export { + addContractor, + getAllContractors, + getContractorById, + updateContractor, + deleteContractor, +}; diff --git a/server/src/controllers/contractorBills.ts b/server/src/controllers/contractorBills.ts new file mode 100644 index 0000000..d706430 --- /dev/null +++ b/server/src/controllers/contractorBills.ts @@ -0,0 +1,105 @@ +import { Request, Response } from "express"; +import errorWrapper from "../middlewares/errorWrapper"; +import { PrismaClient } from "@prisma/client"; +import { adminLog } from "../services/logdata"; + +const prisma = new PrismaClient(); + +const generateBillFortheWeek = errorWrapper( + async (req: Request, res: Response) => { + const { stsId, contractorId } = req.body; + + const sts = await prisma.sTS.findUnique({ + where: { + id: stsId, + }, + }); + + if (!sts) { + res.status(404).json({ message: "STS not found" }); + } + + // get the entries for the week + + const contractorEntries = await prisma.sTSContractorEntry.findMany({ + where: { + stsId, + contractorId, + entryTime: { + gte: new Date(new Date().setDate(new Date().getDate() - 7)), + }, + }, + }); + + const totalWasteOfWeek = contractorEntries.reduce( + (acc, entry) => acc + Number(entry.wasteWeight), + 0 + ); + + const requiredWaste = Number(sts?.requiredWastePerWeek); + const PaymentPerTon = Number(sts?.paymentPerTon); + + const basicPay = totalWasteOfWeek * PaymentPerTon; + + const deficit = Math.max(0, requiredWaste - totalWasteOfWeek); + + const fine = deficit * PaymentPerTon; + + const paymentAmount = basicPay - fine; + + const contractorBill = await prisma.contractorBill.create({ + data: { + stsId, + contractorId, + billNo: Math.floor(Math.random() * 1000000), + weightCollected: totalWasteOfWeek, + weightRequired: requiredWaste, + fine: fine, + paymentPerTon: PaymentPerTon, + deficit: deficit, + paymentAmount: paymentAmount, + }, + }); + + adminLog( + "Bill Generated", + `Bill Generated for Contractor ${contractorId} for STS ${sts?.name}` + ); + + res.status(201).json(contractorBill); + }, + { statusCode: 400, message: "Couldn't generate bill" } +); + +const getAllContractorBills = errorWrapper( + async (req: Request, res: Response) => { + const contractorBills = await prisma.contractorBill.findMany({ + include:{ + sts:true, + contractor:true + } + }); + res.status(200).json(contractorBills); + }, + { statusCode: 404, message: "Contractor Bills not found" } +); + +const getContractorBillById = errorWrapper( + async (req: Request, res: Response) => { + const { contractorBillId } = req.params; + const contractorBill = await prisma.contractorBill.findUnique({ + where: { + id: contractorBillId, + }, + }); + + if (!contractorBill) { + res.status(404).json({ message: "Contractor Bill not found" }); + } + + res.status(200).json(contractorBill); + }, + { statusCode: 404, message: "Contractor Bill not found" } +); + +export { generateBillFortheWeek, getAllContractorBills, getContractorBillById }; diff --git a/server/src/controllers/employee.ts b/server/src/controllers/employee.ts new file mode 100644 index 0000000..9d97808 --- /dev/null +++ b/server/src/controllers/employee.ts @@ -0,0 +1,87 @@ +import { PrismaClient } from "@prisma/client"; +import { Request, Response } from "express"; +import { RoleName } from "../types/rolesTypes"; +import { contractorLog } from "../services/logdata"; +import errorWrapper from "../middlewares/errorWrapper"; + +const prisma = new PrismaClient(); + +const getAllEmployees = errorWrapper( + async (req: Request, res: Response) => { + const employees = await prisma.user.findMany({ + where: { + roleName: RoleName.CONTRACTOR_EMPLOYEE, + }, + }); + res.status(200).json(employees); + }, + { statusCode: 500, message: "Couldn't fetch employees" } +); + +const getEmployeeById = errorWrapper(async (req: Request, res: Response) => { + const { employeeId } = req.params; + const employee = await prisma.user.findUnique({ + where: { + id: employeeId, + }, + }); + + if (!employee) { + res.status(404).json({ message: "Employee not found" }); + } + + res.status(200).json(employee); +}); + +const updateEmployee = errorWrapper( + async (req: Request, res: Response) => { + const { employeeId } = req.params; + const employeeInfo = req.body; + const employee = await prisma.user.update({ + where: { + id: employeeId, + }, + data: employeeInfo, + }); + + await contractorLog( + "Employee Entry", + `Employee named ${employee.username} updated` + ); + + res.status(200).json(employee); + }, + { statusCode: 400, message: "Couldn't update employee" } +); + +const deleteEmployee = errorWrapper( + async (req: Request, res: Response) => { + const { employeeId } = req.params; + + const employee = await prisma.user.findUnique({ + where: { + id: employeeId, + }, + }); + + if (!employee) { + res.status(404).json({ message: "Employee not found" }); + } + + await prisma.user.delete({ + where: { + id: employeeId, + }, + }); + + await contractorLog( + "Employee Entry", + `Employee ${employee?.username} deleted` + ); + + res.status(200).json({ message: "Employee deleted successfully" }); + }, + { statusCode: 400, message: "Couldn't delete employee" } +); + +export { getAllEmployees, getEmployeeById, updateEmployee, deleteEmployee }; diff --git a/server/src/controllers/issue.ts b/server/src/controllers/issue.ts new file mode 100644 index 0000000..c2d5c1c --- /dev/null +++ b/server/src/controllers/issue.ts @@ -0,0 +1,30 @@ +import { PrismaClient } from "@prisma/client"; +import { Request, Response } from "express"; +import errorWrapper from "../middlewares/errorWrapper"; +import { adminLog } from "../services/logdata"; + +const prisma = new PrismaClient(); + +const addIssue = errorWrapper( + async (req: Request, res: Response) => { + const payload = req.body; + const newIssue = await prisma.issue.create({ + data: payload, + }); + + await adminLog("Issue Entry", `Issue ${newIssue.issueType} created`); + + res.status(201).json(newIssue); + }, + { statusCode: 400, message: "Issue not created" } +); + +const getAllIssues = errorWrapper( + async (req: Request, res: Response) => { + const issues = await prisma.issue.findMany(); + res.status(200).json(issues); + }, + { statusCode: 404, message: "Issues not found" } +); + +export { addIssue, getAllIssues }; diff --git a/server/src/controllers/logs.ts b/server/src/controllers/logs.ts new file mode 100644 index 0000000..4d44b16 --- /dev/null +++ b/server/src/controllers/logs.ts @@ -0,0 +1,33 @@ +// get all admin logs + +import { PrismaClient } from "@prisma/client"; +import errorWrapper from "../middlewares/errorWrapper"; +import { Request, Response } from "express"; + +const prisma = new PrismaClient(); + +const getAdminLogs = errorWrapper( + async (req: Request, res: Response) => { + const logs = await prisma.adminLogs.findMany({ + orderBy:{ + createdAt : 'desc' + } + }); + res.status(200).json(logs); + }, + { statusCode: 500, message: "Couldn't get admin logs" } +); + +// get all sts manager logs + +// get all contractor manager logs + +const getContractorManagerLogs = errorWrapper( + async (req: Request, res: Response) => { + const logs = await prisma.contractorLogs.findMany(); + res.status(200).json(logs); + }, + { statusCode: 500, message: "Couldn't get contractor manager logs" } +); + +export { getAdminLogs, getContractorManagerLogs }; diff --git a/server/src/controllers/routeArea.ts b/server/src/controllers/routeArea.ts new file mode 100644 index 0000000..1872f28 --- /dev/null +++ b/server/src/controllers/routeArea.ts @@ -0,0 +1,123 @@ +import { PrismaClient } from "@prisma/client"; +import { Request, Response } from "express"; +import errorWrapper from "../middlewares/errorWrapper"; + +const prisma = new PrismaClient(); + + +const addRoute = errorWrapper( + async (req: Request, res: Response) => { + const { name, description, stsId } = req.body; + const newRoute = await prisma.route.create({ + data: { + name, + description, + stsId, + }, + }); + + res.status(201).json(newRoute); + }, + { statusCode: 400, message: "Route not created" } +); + +const getAllRoutes = errorWrapper( + async (req: Request, res: Response) => { + const routes = await prisma.route.findMany(); + res.status(200).json(routes); + }, + { statusCode: 404, message: "Routes not found" } +); + +const getRouteById = errorWrapper( + async (req: Request, res: Response) => { + const { routeId } = req.params; + const route = await prisma.route.findUnique({ + where: { + id: routeId, + }, + }); + + if (!route) { + res.status(404).json({ message: "Route not found" }); + } + + res.status(200).json(route); + }, + { statusCode: 404, message: "Route not found" } +); + +const addArea = errorWrapper( + async (req: Request, res: Response) => { + const { name, stsId } = req.body; + const newArea = await prisma.area.create({ + data: { + name, + stsId, + }, + }); + + res.status(201).json(newArea); + }, + { statusCode: 400, message: "Area not created" } +); + + +const addRouteToArea = errorWrapper( + async (req: Request, res: Response) => { + const { areaId, routeId } = req.params; + + const area = await prisma.area.findUnique({ + where: { + id: areaId, + }, + }); + + if (!area) { + res.status(404).json({ message: "Area not found" }); + } + + const route = await prisma.route.findUnique({ + where: { + id: routeId, + }, + }); + + if (!route) { + res.status(404).json({ message: "Route not found" }); + } + + const updatedArea = await prisma.area.update({ + where: { + id: areaId, + }, + data: { + routes: { + connect: { + id: routeId, + }, + }, + }, + }); + + res.status(200).json(updatedArea); + }, + { statusCode: 404, message: "Route or Area not found" } +); + +const getAllAreas = errorWrapper( + async (req: Request, res: Response) => { + const areas = await prisma.area.findMany(); + res.status(200).json(areas); + }, + { statusCode: 404, message: "Areas not found" } +); + +export { + addRoute, + getAllRoutes, + getRouteById, + addArea, + addRouteToArea, + getAllAreas, +}; diff --git a/server/src/controllers/stsContractorEntry.ts b/server/src/controllers/stsContractorEntry.ts new file mode 100644 index 0000000..33d82ae --- /dev/null +++ b/server/src/controllers/stsContractorEntry.ts @@ -0,0 +1,51 @@ +import { PrismaClient } from "@prisma/client"; +import { Request, Response } from "express"; +import { adminLog } from "../services/logdata"; +import errorWrapper from "../middlewares/errorWrapper"; + +const prisma = new PrismaClient(); + +const addContractorEntry = errorWrapper( + async (req: Request, res: Response) => { + const payload = req.body; + const newContractorEntry = await prisma.sTSContractorEntry.create({ + data: payload, + }); + + await adminLog( + "Contractor Entry", + `${newContractorEntry.vehicleNumber} has entered STS ${newContractorEntry.stsId}` + ); + + res.status(201).json(newContractorEntry); + }, + { statusCode: 400, message: "Contractor Entry not created" } +); + +const getAllContractorEntries = errorWrapper( + async (req: Request, res: Response) => { + const contractorEntries = await prisma.sTSContractorEntry.findMany(); + res.status(200).json(contractorEntries); + }, + { statusCode: 404, message: "Contractor Entries not found" } +); + +const getContractorEntryById = errorWrapper( + async (req: Request, res: Response) => { + const { contractorEntryId } = req.params; + const contractorEntry = await prisma.sTSContractorEntry.findUnique({ + where: { + id: contractorEntryId, + }, + }); + + if (!contractorEntry) { + res.status(404).json({ message: "Contractor Entry not found" }); + } + + res.status(200).json(contractorEntry); + }, + { statusCode: 404, message: "Contractor Entry not found" } +); + +export { addContractorEntry, getAllContractorEntries, getContractorEntryById }; diff --git a/server/src/controllers/tracking.ts b/server/src/controllers/tracking.ts new file mode 100644 index 0000000..202ce85 --- /dev/null +++ b/server/src/controllers/tracking.ts @@ -0,0 +1,125 @@ +import { Server, Socket } from "socket.io"; +import { PrismaClient } from "@prisma/client"; + +// + +const prisma = new PrismaClient(); + +const track = (io: Server) => { + io.on("connection", (socket) => { + const connectionType = socket.handshake.query.type; + + if (connectionType == "vehicle") { + handleVehicle(socket); + } else if (connectionType == "admin") { + handleAdmin(socket); + } else if (connectionType == "sts") { + handleSts(socket); + } else if (connectionType == "landfill") { + handleLandfill(socket); + } else if (connectionType == "citizen") { + // handleCitizen(socket); + } + }); +}; + +// const handleCitizen = (socket: Socket) => { + +// socket.on("join_rooms", async (data) => { +// socket.join("notification"); +// } + +// } + +const handleSts = (socket: Socket) => { + const id = socket.handshake.query.id; + + socket.on("join_rooms", async (data) => { + if (id == undefined) { + return; + } + console.log("sts", id, "has joined"); + + socket.join(id); + console.log("joined sts room"); + }); +}; + +const handleLandfill = (socket: Socket) => { + const id = socket.handshake.query.id; + + socket.on("join_rooms", async (data) => { + if (id == undefined) { + return; + } + console.log("landfill", id, "has joined"); + + socket.join(id); + console.log("joined landfill room"); + }); +}; + +const handleVehicle = (socket: Socket) => { + socket.on("join_rooms", async (data) => { + // get vehicle id + const id = data.vehicleId; + + console.log("vehicle", id, "has joined"); + + if (!id) return; + + const vehicle = await prisma.vehicle.findUnique({ + where: { + id: id, + }, + select: { + stsId: true, + landFillId: true, + }, + }); + + if (!vehicle) { + return; + } + + socket.join(vehicle.stsId); + console.log("joined sts room"); + socket.join(vehicle.landFillId); + console.log("joined landfill room"); + socket.join("adminsVehicles"); + console.log("joined adminsVehicles room"); + }); + + socket.on("send_location", async (data) => { + const { latitude, longitude } = data; + + socket.to(data.stsId).emit("location", { + vehicleId: data.vehicleId, + latitude, + longitude, + }); + + socket.to(data.landFillId).emit("location", { + vehicleId: data.vehicleId, + latitude, + longitude, + }); + + socket.to("adminsVehicles").emit("location", { + latitude, + longitude, + vehicleId: data.vehicleId, + }); + }); +}; + +const handleAdmin = (socket: Socket) => { + socket.on("join_rooms", async (data) => { + console.log("admin has joined"); + + socket.join("adminsVehicles"); + console.log("joined adminsVehicles room"); + }); +}; + +export { track }; diff --git a/server/src/index.ts b/server/src/index.ts index d553bbe..039e5e7 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,24 +1,16 @@ -import express, { urlencoded, Request, Response } from "express"; +import express, { urlencoded } from "express"; import dotenv from "dotenv"; dotenv.config(); import checkDatabaseConnection from "./db/connection"; -import { PrismaClient } from "@prisma/client"; -import authRoute from "./routes/auth"; -import userRoute from "./routes/users"; -import profileRoute from "./routes/profile"; -import rbacRoute from "./routes/rbac"; -import vehicleRoute from "./routes/vehicles"; -import stsRoute from "./routes/sts"; -import landfillRoute from "./routes/landfills"; -import landfillEntryRoute from "./routes/landfillVehicle"; -import stsEntryRoute from "./routes/stsVehicle"; -import billRoute from "./routes/bills"; -import tripRoute from "./routes/trip"; -import scheduleRoute from "./routes/schedule"; +import routes from "./routes"; import cors from "cors"; -import authChecker from "./middlewares/auth"; +import { createServer } from "http"; +import { Server } from "socket.io"; +import { track } from "./controllers/tracking"; +import { getIPAddress } from "./services/utils"; -const prisma = new PrismaClient(); + +getIPAddress(); const PORT = process.env.PORT || 3000; const app = express(); @@ -33,24 +25,19 @@ app.use( app.use(express.json()); app.use(urlencoded({ extended: true })); -app.use("/auth", authRoute); -app.use("/users", userRoute); -app.use("/profile", profileRoute); -app.use("/rbac", rbacRoute); // authentication and authorization both will be added here -app.use("/vehicles", authChecker, vehicleRoute); -app.use("/sts", authChecker, stsRoute); -app.use("/landfills", authChecker, landfillRoute); -app.use("/landfill-entry", authChecker, landfillEntryRoute); -app.use("/sts-entry", authChecker, stsEntryRoute); -app.use("/bills", authChecker, billRoute); -app.use("/trips", authChecker, tripRoute); -app.use("/schedules", scheduleRoute); +app.use("/", routes); app.get("/", (req, res) => { res.send("EcoSync Server is Up..."); }); -app.listen(PORT, async () => { +const httpServer = createServer(app); + +const io = new Server(httpServer, {}); + +track(io); + +httpServer.listen(PORT, async () => { await checkDatabaseConnection(); console.log(`EcoSync Server is running on PORT ${PORT}`); }); diff --git a/server/src/middlewares/auth.ts b/server/src/middlewares/auth.ts index 10aeb4e..910b97d 100644 --- a/server/src/middlewares/auth.ts +++ b/server/src/middlewares/auth.ts @@ -12,7 +12,6 @@ const authChecker = errorWrapper( } const decoded = verifyToken(token); req.user = decoded as JwtPayload; - console.log(req.user); next(); } ); diff --git a/server/src/prisma/schema.prisma b/server/src/prisma/schema.prisma index 426b587..ed64252 100644 --- a/server/src/prisma/schema.prisma +++ b/server/src/prisma/schema.prisma @@ -8,22 +8,36 @@ generator client { } model User { - id String @id @default(uuid()) - username String @unique - email String @unique - hashedPassword String - profileName String? - profileImage String? - role Role @relation(fields: [roleName], references: [name], onDelete: Cascade) - roleName String @default("UNASSIGNED") - sts STS? @relation(fields: [stsId], references: [id], onDelete: Cascade) - stsId String? + id String @id @default(uuid()) + username String + email String @unique + hashedPassword String + profileName String? // full name for contractor manager, and employeee + profileImage String? + accessLevel String? + dateOfBirth DateTime? + dateOfHire DateTime? + jobTitle String? + paymentRatePerHour Decimal? + assignedRoute Route? @relation(fields: [routeId], references: [id]) + routeId String? + collectionPlan CollectionPlan? @relation(fields: [collectionPlanId], references: [id]) + collectionPlanId String? + + contactNumber String? + + role Role @relation(fields: [roleName], references: [name], onDelete: Cascade) + roleName String @default("UNASSIGNED") + sts STS? @relation(fields: [stsId], references: [id], onDelete: Cascade) + stsId String? landfill Landfill? @relation(fields: [landfillId], references: [id], onDelete: Cascade) landfillId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + Contractor Contractor? @relation(fields: [contractorId], references: [id]) + contractorId String? @@index([username], name: "username_index") @@index([email], name: "email_index") @@ -51,6 +65,102 @@ model Permission { updatedAt DateTime @updatedAt } +model ContractorLogs { + id String @id @default(uuid()) + type String + description String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Issue { + id String @id @default(uuid()) + issueType String + description String + latitude Decimal + longitude Decimal + isAnonymous Boolean + issuePic String @default("https://thumbs.dreamstime.com/b/pile-garbage-home-junk-left-front-house-street-plastic-bags-dumper-truck-to-collect-pile-115303584.jpg") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model AdminLogs { + id String @id @default(uuid()) + type String + description String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Route { + id String @id @default(uuid()) + name String + description String? + area Area? @relation(fields: [areaId], references: [id]) + areaId String? + + sts STS? @relation(fields: [stsId], references: [id]) + stsId String? + emoloyee User[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Area { + id String @id @default(uuid()) + name String + routes Route[] + CollectionPlan CollectionPlan[] + sts STS? @relation(fields: [stsId], references: [id]) + stsId String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model CollectionPlan { + id String @id @default(uuid()) + area Area @relation(fields: [areaId], references: [id]) + areaId String + collectionStartTime DateTime? + durationForCollection Decimal? + numberOfLaborers Decimal? // + numberOfVans Decimal? + expectedWaste Decimal? + stsId String? + sts STS? @relation(fields: [stsId], references: [id]) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + User User[] +} + +model Contractor { + id String @id @default(uuid()) // contractor id + name String + registrationId String + registrationDate DateTime? + tinNumber String? + contactNumber String? + workforceSize Decimal? + paymentPerTon Decimal @db.Decimal(10, 2) + requiredWastePerDay Decimal @db.Decimal(10, 2) + contractDuration String? + area String? + + assignedSTS STS? @relation(fields: [stsId], references: [id], onDelete: Cascade) + stsId String? + + manager User[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + STSContractorEntry STSContractorEntry[] + ContractorBill ContractorBill[] +} + model Vehicle { id String @id @default(uuid()) vehicleNumber String @unique @@ -80,23 +190,34 @@ model Vehicle { } model STS { - id String @id @default(uuid()) + id String @id @default(uuid()) name String? wardNumber String? - capacity Decimal? @db.Decimal(10, 2) - currentTotalWaste Decimal? @db.Decimal(10, 2) - latitude Decimal - longitude Decimal - Vehicle Vehicle[] + capacity Decimal? @db.Decimal(10, 2) + currentTotalWaste Decimal? @db.Decimal(10, 2) + + requiredWastePerWeek Decimal? @db.Decimal(10, 2) + paymentPerTon Decimal? @db.Decimal(10, 2) + fine Decimal? @db.Decimal(10, 2) + + latitude Decimal + longitude Decimal + Vehicle Vehicle[] STSVehicleEntry STSVehicleEntry[] manager User[] Trip Trip[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - Bill Bill[] - Schedule Schedule[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + Bill Bill[] + Schedule Schedule[] + Contractor Contractor[] + Route Route[] + Area Area[] + CollectionPlan CollectionPlan[] + STSContractorEntry STSContractorEntry[] + ContractorBill ContractorBill[] } model Landfill { @@ -118,6 +239,10 @@ model Landfill { Vehicle Vehicle[] } +// model STSContractorEntry{ + +// } + model STSVehicleEntry { id String @id @default(uuid()) sts STS @relation(fields: [stsId], references: [id], onDelete: Cascade) @@ -132,6 +257,44 @@ model STSVehicleEntry { updatedAt DateTime @updatedAt } +model STSContractorEntry { + id String @id @default(uuid()) + sts STS @relation(fields: [stsId], references: [id], onDelete: Cascade) + stsId String + contractor Contractor @relation(fields: [contractorId], references: [id], onDelete: Cascade) + contractorId String + + wasteType String? + + wasteWeight Decimal? @db.Decimal(10, 2) + entryTime DateTime? @default(now()) + exitTime DateTime? + + vehicleNumber String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model ContractorBill { + id String @id @default(uuid()) + contractor Contractor @relation(fields: [contractorId], references: [id], onDelete: Cascade) + contractorId String + sts STS @relation(fields: [stsId], references: [id], onDelete: Cascade) + stsId String + billNo Int @unique @default(autoincrement()) + paymentPerTon Decimal? @db.Decimal(10, 2) + deficit Decimal? @db.Decimal(10, 2) + + weightRequired Decimal? @db.Decimal(10, 2) + weightCollected Decimal? @db.Decimal(10, 2) + paymentAmount Decimal? @db.Decimal(10, 2) + fine Decimal? @db.Decimal(10, 2) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model LandfillVehicleEntry { id String @id @default(uuid()) landfill Landfill @relation(fields: [landfillId], references: [id], onDelete: Cascade) diff --git a/server/src/prisma/seed.ts b/server/src/prisma/seed.ts index 05baa1d..935b715 100644 --- a/server/src/prisma/seed.ts +++ b/server/src/prisma/seed.ts @@ -25,6 +25,14 @@ const roleData: Prisma.RoleCreateInput[] = [ name: RoleName.UNASSIGNED, description: "Unassigned Role", }, + { + name: RoleName.CONTRACTOR_MANAGER, + description: "Contractor Manager Role", + }, + { + name: RoleName.CONTRACTOR_EMPLOYEE, + description: "Contractor Employee Role", + }, ]; const permissionData: Prisma.PermissionCreateInput[] = [ @@ -153,6 +161,93 @@ const roleAssignments = [ }, ]; +const areaData: Prisma.AreaCreateInput[] = [ + { + id: "area1", + name: "Mohakhali", + }, + { + id: "area2", + name: "Gulshan", + }, + { + id: "area3", + name: "Bonani", + }, + { + id: "area4", + name: "Badda", + }, + { + id: "area5", + name: "Jatrabari", + }, +]; + +const routeData: Prisma.RouteCreateInput[] = [ + { + id: "route1", + name: "Mohakhali to Amin Bazar", + description: "Route from Mohakhali to Amin Bazar", + area: { + connect: { + id: "area1", + }, + }, + }, + // add 5 more routes + { + id: "route2", + name: "Gulshan to Amin Bazar", + description: "Route from Gulshan to Amin Bazar", + area: { + connect: { + id: "area1", + }, + }, + }, + { + id: "route3", + name: "Bonani to Amin Bazar", + description: "Route from Bonani to Amin Bazar", + area: { + connect: { + id: "area1", + }, + }, + }, + { + id: "route4", + name: "Badda to Amin Bazar", + description: "Route from Badda to Amin Bazar", + area: { + connect: { + id: "area2", + }, + }, + }, + { + id: "route5", + name: "Jatrabari to Amin Bazar", + description: "Route from Jatrabari to Amin Bazar", + area: { + connect: { + id: "area2", + }, + }, + }, + { + id: "route6", + name: "Mohakhali to Gulshan", + description: "Route from Mohakhali to Gulshan", + area: { + connect: { + id: "area2", + }, + }, + }, +]; + const userData: Prisma.UserCreateInput[] = [ { username: "Admin", @@ -390,7 +485,10 @@ const stsData: Prisma.STSCreateInput[] = [ name: "Mohakhali STS", wardNumber: "13", capacity: 1000, + fine: 10.5, + paymentPerTon: 10.5, currentTotalWaste: 220, + requiredWastePerWeek: 500, latitude: 23.777742178642388, longitude: 90.40575221162331, }, @@ -399,6 +497,9 @@ const stsData: Prisma.STSCreateInput[] = [ name: "Gulshan STS", wardNumber: "2", capacity: 2000, + paymentPerTon: 10.5, + fine: 10.5, + requiredWastePerWeek: 500, currentTotalWaste: 225, latitude: 23.792464932754005, longitude: 90.40782465254337, @@ -409,15 +510,21 @@ const stsData: Prisma.STSCreateInput[] = [ name: "Bonani STS", wardNumber: "4", capacity: 1500, + fine: 10.5, + requiredWastePerWeek: 500, currentTotalWaste: 240, + paymentPerTon: 10.5, latitude: 23.793630794902622, longitude: 90.40660514416635, }, { id: "sts4", name: "Badda STS", + paymentPerTon: 10.5, wardNumber: "4", + fine: 10.5, capacity: 1500, + requiredWastePerWeek: 500, currentTotalWaste: 275, latitude: 23.78042151306244, longitude: 90.42669427037866, @@ -426,8 +533,11 @@ const stsData: Prisma.STSCreateInput[] = [ id: "sts5", name: "Jatrabari STS", wardNumber: "4", + fine: 10.5, + paymentPerTon: 10.5, capacity: 1500, currentTotalWaste: 290, + requiredWastePerWeek: 500, latitude: 23.710484797357275, longitude: 90.43479693063576, }, @@ -488,6 +598,71 @@ const tripData: Prisma.TripCreateInput[] = [ }, ]; +const contractorData: Prisma.ContractorCreateInput[] = [ + { + id: "con1", + name: "Contractor 2 Name", + registrationId: "ABSDC123456", + registrationDate: "2024-05-10T00:00:00Z", + tinNumber: "34334", + contactNumber: "+929323", + workforceSize: 100, + paymentPerTon: 10.5, + requiredWastePerDay: 500.0, + contractDuration: "1 year", + area: "City XYZ", + assignedSTS: { + connect: { + id: "sts1", + }, + }, + }, +]; + +const employeeData: Prisma.UserCreateInput[] = [ + { + username: "employee4", + email: "employee4@example.com", + hashedPassword: hashedP, + role: { + connect: { + name: RoleName.CONTRACTOR_EMPLOYEE, + }, + }, + dateOfBirth: "1990-01-01T00:00:00Z", + contactNumber: "012321312", + dateOfHire: "2024-05-10T00:00:00Z", + jobTitle: "E Job Title", + paymentRatePerHour: 20.0, + Contractor: { + connect: { + id: "con1", + }, + }, + }, + // add 5 more employees + ...Array.from({ length: 5 }, (_, i) => ({ + username: `employee${i + 5}`, + email: `employee${i + 5}@example.com`, + hashedPassword: hashedP, + role: { + connect: { + name: RoleName.CONTRACTOR_EMPLOYEE, + }, + }, + dateOfBirth: "1990-01-01T00:00:00Z", + contactNumber: "012321312", + dateOfHire: "2024-05-10T00:00:00Z", + jobTitle: "E Job Title", + paymentRatePerHour: 20.0, + Contractor: { + connect: { + id: "con1", + }, + }, + })), +]; + async function main() { console.log("Seeding roles..."); for (const role of roleData) { @@ -529,6 +704,22 @@ async function main() { } } + console.log("Seeding areas..."); + for (const area of areaData) { + const newArea = await prisma.area.create({ + data: area, + }); + console.log(newArea); + } + + console.log("Seeding routes..."); + for (const route of routeData) { + const newRoute = await prisma.route.create({ + data: route, + }); + console.log(newRoute); + } + console.log("Seeding users..."); for (const user of userData) { const newUser = await prisma.user.create({ @@ -577,6 +768,22 @@ async function main() { console.log(newTrip); } + console.log("Seeding contractors..."); + for (const contractor of contractorData) { + const newContractor = await prisma.contractor.create({ + data: contractor, + }); + console.log(newContractor); + } + + console.log("Seeding employees..."); + for (const employee of employeeData) { + const newEmployee = await prisma.user.create({ + data: employee, + }); + console.log(newEmployee); + } + console.log("Seeding completed!"); } diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 986e080..b9ea325 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -1,5 +1,7 @@ import express from "express"; import { + createEmployee, + createManager, createUser, login, logout, @@ -14,6 +16,8 @@ import { PERMISSIONS } from "../permissions/permissions"; const router = express.Router(); +router.route("/createempolyee").post(createEmployee); +router.route("/createmanager").post(authChecker, createManager); router .route("/create") .post(authChecker, authorizer(PERMISSIONS.CREATE_USER), createUser); diff --git a/server/src/routes/collectionPlans.ts b/server/src/routes/collectionPlans.ts new file mode 100644 index 0000000..3e97063 --- /dev/null +++ b/server/src/routes/collectionPlans.ts @@ -0,0 +1,14 @@ +import express from "express"; +const router = express.Router(); + +import { + addCollectionPlan, + getAllCollectionPlans, + getCollectionPlansBySTS, +} from "../controllers/collectionPlans"; + +router.route("/create").post(addCollectionPlan); +router.route("/all").get(getAllCollectionPlans); +router.route("/").get(getCollectionPlansBySTS); + +export default router; diff --git a/server/src/routes/contractor-bills.ts b/server/src/routes/contractor-bills.ts new file mode 100644 index 0000000..a4d43af --- /dev/null +++ b/server/src/routes/contractor-bills.ts @@ -0,0 +1,14 @@ +import express from "express"; +const router = express.Router(); + +import { + generateBillFortheWeek, + getAllContractorBills, + getContractorBillById, +} from "../controllers/contractorBills"; + +router.route("/generate").post(generateBillFortheWeek); +router.route("/").get(getAllContractorBills); +router.route("/:contractorBillId").get(getContractorBillById); + +export default router; diff --git a/server/src/routes/contractor.ts b/server/src/routes/contractor.ts new file mode 100644 index 0000000..f346c9f --- /dev/null +++ b/server/src/routes/contractor.ts @@ -0,0 +1,20 @@ +import { get } from "http"; +import express from "express"; +import { + addContractor, + deleteContractor, + getAllContractors, + getContractorById, + updateContractor, +} from "../controllers/contractor"; +const router = express.Router(); + +router.route("/create").post(addContractor); +router.route("/").get(getAllContractors); +router + .route("/:contractorId") + .get(getContractorById) + .put(updateContractor) + .delete(deleteContractor); + +export default router; diff --git a/server/src/routes/employee.ts b/server/src/routes/employee.ts new file mode 100644 index 0000000..7ba0130 --- /dev/null +++ b/server/src/routes/employee.ts @@ -0,0 +1,18 @@ +import express from "express"; +const router = express.Router(); + +import { + getAllEmployees, + getEmployeeById, + updateEmployee, + deleteEmployee, +} from "../controllers/employee"; + +router.route("/").get(getAllEmployees); +router + .route("/:employeeId") + .get(getEmployeeById) + .put(updateEmployee) + .delete(deleteEmployee); + +export default router; diff --git a/server/src/routes/index.ts b/server/src/routes/index.ts new file mode 100644 index 0000000..76c8520 --- /dev/null +++ b/server/src/routes/index.ts @@ -0,0 +1,49 @@ +import express from "express"; +const router = express.Router(); + +import authRoute from "./auth"; +import userRoute from "./users"; +import profileRoute from "./profile"; +import rbacRoute from "./rbac"; +import vehicleRoute from "./vehicles"; +import stsRoute from "./sts"; +import landfillRoute from "./landfills"; +import landfillEntryRoute from "./landfillVehicle"; +import stsEntryRoute from "./stsVehicle"; +import billRoute from "./bills"; +import tripRoute from "./trip"; +import scheduleRoute from "./schedule"; +import authChecker from "../middlewares/auth"; +import contractorRoute from "./contractor"; +import logRouter from "./logs"; +import routeAreaRouter from "./routeArea"; +import employeeRoute from "./employee"; +import collectionPlanRoute from "./collectionPlans"; +import issueRoute from "./issue"; +import stsContractorRoute from "./stsContractor"; +import contractorBillRoute from "./contractor-bills"; + +router.use("/auth", authRoute); +router.use("/users", userRoute); +router.use("/profile", profileRoute); +router.use("/rbac", rbacRoute); // authentication and authorization both will be added here +router.use("/vehicles", authChecker, vehicleRoute); +router.use("/sts", authChecker, stsRoute); +router.use("/landfills", authChecker, landfillRoute); +router.use("/landfill-entry", authChecker, landfillEntryRoute); +router.use("/sts-entry", authChecker, stsEntryRoute); +router.use("/bills", authChecker, billRoute); +router.use("/trips", authChecker, tripRoute); +router.use("/schedules", scheduleRoute); + +// auth checker needed +router.use("/contractors", contractorRoute); +router.use("/logs", logRouter); +router.use("/route-areas", routeAreaRouter); +router.use("/employees", employeeRoute); +router.use("/collection-plans", collectionPlanRoute); +router.use("/issues", issueRoute); +router.use("/sts-contractor-entry", stsContractorRoute); +router.use("/contractor-bills", contractorBillRoute); + +export default router; diff --git a/server/src/routes/issue.ts b/server/src/routes/issue.ts new file mode 100644 index 0000000..2aebbaf --- /dev/null +++ b/server/src/routes/issue.ts @@ -0,0 +1,9 @@ +import express from "express"; +const router = express.Router(); + +import { addIssue, getAllIssues } from "../controllers/issue"; + +router.route("/create").post(addIssue); +router.route("/all").get(getAllIssues); + +export default router; diff --git a/server/src/routes/logs.ts b/server/src/routes/logs.ts new file mode 100644 index 0000000..567c413 --- /dev/null +++ b/server/src/routes/logs.ts @@ -0,0 +1,22 @@ +import express from "express"; +import { getAdminLogs, getContractorManagerLogs } from "../controllers/logs"; +import { adminLog, contractorLog } from "../services/logdata"; +const router = express.Router(); + +router.route("/checkin").post((req, res) => { + const { flag } = req.body; + + if (flag) { + adminLog("Checkin", "Shawon Majid has checked in"); + contractorLog("Checkin", "Shawon Majid has checked in"); + } else { + contractorLog("Checkin", "Shawon Majid has checked in"); + adminLog("Checkout", "Shawon Majid has checked out"); + } + + res.status(200).json({ message: "Checkin status updated" }); +}); +router.route("/admin").get(getAdminLogs); +router.route("/contractor").get(getContractorManagerLogs); + +export default router; diff --git a/server/src/routes/routeArea.ts b/server/src/routes/routeArea.ts new file mode 100644 index 0000000..b28bba7 --- /dev/null +++ b/server/src/routes/routeArea.ts @@ -0,0 +1,20 @@ +import express from "express"; +const router = express.Router(); + +import { + addArea, + getAllAreas, + addRoute, + getAllRoutes, + getRouteById, + addRouteToArea, +} from "../controllers/routeArea"; + +router.route("/add-route").post(addRoute); +router.route("/routes").get(getAllRoutes); +router.route("/add-area").post(addArea); +router.route("/area").get(getAllAreas); +router.route("/routes/:routeId").get(getRouteById); +router.route("/area/:areaId/route/:routeId").post(addRouteToArea); + +export default router; diff --git a/server/src/routes/stsContractor.ts b/server/src/routes/stsContractor.ts new file mode 100644 index 0000000..cd2cb29 --- /dev/null +++ b/server/src/routes/stsContractor.ts @@ -0,0 +1,14 @@ +import express from "express"; +const router = express.Router(); + +import { + addContractorEntry, + getAllContractorEntries, + getContractorEntryById, +} from "../controllers/stsContractorEntry"; + +router.route("/create").post(addContractorEntry); +router.route("/all").get(getAllContractorEntries); +router.route("/:id").get(getContractorEntryById); + +export default router; diff --git a/server/src/services/logdata.ts b/server/src/services/logdata.ts new file mode 100644 index 0000000..30e2743 --- /dev/null +++ b/server/src/services/logdata.ts @@ -0,0 +1,21 @@ +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +export const adminLog = async (logType: string, description: string) => { + await prisma.adminLogs.create({ + data: { + type: logType, + description: description, + }, + }); +}; + +export const contractorLog = async (logType: string, description: string) => { + await prisma.contractorLogs.create({ + data: { + type: logType, + description: description, + }, + }); +}; diff --git a/server/src/services/utils.ts b/server/src/services/utils.ts index 5cb55da..271bbfb 100644 --- a/server/src/services/utils.ts +++ b/server/src/services/utils.ts @@ -1,3 +1,5 @@ +import * as os from 'os'; + const randomOTPGenerator = () => { const otp = Math.floor(1000 + Math.random() * 9000); return otp; @@ -8,4 +10,27 @@ const randomPasswordGenerator = () => { return password; }; -export { randomOTPGenerator, randomPasswordGenerator }; + +const getIPAddress = (): string => { + // Get the network interfaces + const networkInterfaces = os.networkInterfaces(); + + // Extract the IPv4 address from the network interfaces + const ipv4Addresses: string[] = []; + for (const interfaceName in networkInterfaces) { + if (Object.prototype.hasOwnProperty.call(networkInterfaces, interfaceName)) { + const interfaces = networkInterfaces[interfaceName]; + for (const iface of interfaces?.filter(Boolean) || []){ + if (iface.family === 'IPv4' && !iface.internal) { + ipv4Addresses.push(iface.address); + } + } + } + } + + console.log('IPv4 Addresses:', ipv4Addresses[0]); + return ipv4Addresses[0] || ''; +}; + + +export { randomOTPGenerator, randomPasswordGenerator, getIPAddress }; diff --git a/server/src/types/express/index.d.ts b/server/src/types/express/index.d.ts index fc30a24..8a85ac6 100644 --- a/server/src/types/express/index.d.ts +++ b/server/src/types/express/index.d.ts @@ -1,3 +1,4 @@ +import { Server } from "socket.io"; import { JwtPayload } from "jsonwebtoken"; import { User } from "../custom"; @@ -6,5 +7,6 @@ import { User } from "../custom"; declare module "express-serve-static-core" { interface Request { user?: JwtPayload; + io?: Server; } } diff --git a/server/src/types/rolesTypes.ts b/server/src/types/rolesTypes.ts index e13e9b8..9832bcb 100644 --- a/server/src/types/rolesTypes.ts +++ b/server/src/types/rolesTypes.ts @@ -3,6 +3,8 @@ const RoleName = { LAND_MANAGER: "LAND_MANAGER", STS_MANAGER: "STS_MANAGER", UNASSIGNED: "UNASSIGNED", + CONTRACTOR_MANAGER: "CONTRACTOR_MANAGER", + CONTRACTOR_EMPLOYEE: "CONTRACTOR_EMPLOYEE", }; export { RoleName }; diff --git a/waste_management/.vscode/settings.json b/waste_management/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/waste_management/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/waste_management/lib/constants/theming.dart b/waste_management/lib/constants/theming.dart index f210341..2802c3b 100644 --- a/waste_management/lib/constants/theming.dart +++ b/waste_management/lib/constants/theming.dart @@ -4,7 +4,7 @@ const kBackgroundColor = Color.fromRGBO(232, 223, 202, 1); const kPrimaryColor = Color.fromRGBO(26, 77, 46, 1); const ksecondaryHeaderColor = Color.fromRGBO(245, 239, 230, 1); const kPrimaryLightColor = Color.fromRGBO(79, 111, 82, 1); -String uri= 'http://10.42.0.238:8585'; //10.42.0.107 +String uri= 'http://192.168.1.109:8585'; //10.42.0.107 const appBarGradient = LinearGradient( colors: [ diff --git a/waste_management/lib/main.dart b/waste_management/lib/main.dart index 1dd66d8..1b13b58 100644 --- a/waste_management/lib/main.dart +++ b/waste_management/lib/main.dart @@ -11,13 +11,10 @@ import 'package:waste_management/widgets/employeeFeatures/checkInout/checkedOut. import 'package:waste_management/widgets/employeeFeatures/checkInout/checkin.dart'; import 'package:waste_management/widgets/employeeFeatures/taskList/taskListShow.dart'; - void main() { WidgetsFlutterBinding.ensureInitialized(); - runApp(MultiProvider(providers: [ - ChangeNotifierProvider(create: (context)=> UserProvider()) - ], - + runApp(MultiProvider( + providers: [ChangeNotifierProvider(create: (context) => UserProvider())], child: const MyApp())); } @@ -35,7 +32,7 @@ class _MyAppState extends State { @override void initState() { super.initState(); - // authService.getUserData(context); + // authService.getUserData(context); } @override @@ -58,5 +55,6 @@ class _MyAppState extends State { ), home: const SplashScreen() //ForumDashboard()//TaskListView()//CheckOut()//IssuePage()// ); + } -} \ No newline at end of file +} diff --git a/waste_management/lib/models/issueModel.dart b/waste_management/lib/models/issueModel.dart new file mode 100644 index 0000000..e96fd23 --- /dev/null +++ b/waste_management/lib/models/issueModel.dart @@ -0,0 +1,24 @@ +class Issue { + final String id; + final String type; + final String issuePic; + final String description; + final String latitude; + final String longitude; + final bool isAnonymous; + + Issue({ + required this.id, + required this.type, + required this.issuePic, + required this.description, + required this.latitude, + required this.longitude, + required this.isAnonymous + }); +} + + + + + // Printing the dummy issues for verification diff --git a/waste_management/lib/notification/notificationScreen.dart b/waste_management/lib/notification/notificationScreen.dart new file mode 100644 index 0000000..7e2fcba --- /dev/null +++ b/waste_management/lib/notification/notificationScreen.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class NotificationScreen extends StatefulWidget { + const NotificationScreen({super.key}); + + @override + State createState() => _NotificationScreenState(); +} + +class _NotificationScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center(child: Text("You have no notification right now")), + ); + } +} diff --git a/waste_management/lib/notification/notificiationIcon.dart b/waste_management/lib/notification/notificiationIcon.dart new file mode 100644 index 0000000..6b17851 --- /dev/null +++ b/waste_management/lib/notification/notificiationIcon.dart @@ -0,0 +1,58 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:waste_management/constants/theming.dart'; +import 'package:waste_management/notification/notificationScreen.dart'; + +class NotificationWidget extends StatefulWidget { + @override + State createState() => _NotificationWidgetState(); +} + +class _NotificationWidgetState extends State { + int notificationCount = 0; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + GestureDetector( + onTap: (){ + Navigator.push( + context, + // generateRoute( + // RouteSettings(name: HomeScreen.routeName) + // ), + MaterialPageRoute(builder: (context) => NotificationScreen()), // same as above + //(route) => false + ); + }, + child: Icon( + Icons.notification_important, + size: 32, + color: ksecondaryHeaderColor,// Adjust icon size as needed + ), + ), + if (notificationCount == 0) + Positioned( + right: 0, + top: 0, + child: Container( + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.red, + ), + child: Text( + notificationCount.toString(), + style: TextStyle( + color: Colors.white, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ); + } +} diff --git a/waste_management/lib/screens/homescreen/adminHomeScreen.dart b/waste_management/lib/screens/homescreen/adminHomeScreen.dart index 0e9ba30..314fef5 100644 --- a/waste_management/lib/screens/homescreen/adminHomeScreen.dart +++ b/waste_management/lib/screens/homescreen/adminHomeScreen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:curved_navigation_bar/curved_navigation_bar.dart'; import 'package:waste_management/constants/theming.dart'; +import 'package:waste_management/notification/notificiationIcon.dart'; import 'package:waste_management/screens/mapscreen/livelocation.dart'; import 'package:waste_management/screens/welcome/loginscreen.dart'; @@ -10,6 +11,7 @@ import 'package:waste_management/widgets/adminFeatures/Schedules.dart'; import 'package:waste_management/widgets/adminFeatures/Users.dart'; import 'package:waste_management/widgets/citizenFeatures/Forum/forumDashboard.dart'; import 'package:waste_management/widgets/citizenFeatures/educationalBlog/blogDashboard.dart'; +import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueFeed.dart'; import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueScreen.dart'; import 'package:waste_management/widgets/citizenFeatures/volunteer/volunteer.dart'; import 'package:waste_management/widgets/common/Profile.dart'; @@ -33,7 +35,7 @@ class _HomeScreenState extends State { // const Bills(), // Profile(), EventCalendar(), - IssuePage(), + IssueFeed(), ForumDashboard(), const GoogleMapLive(), const BlogDashboard() @@ -81,6 +83,7 @@ class _HomeScreenState extends State { alignment: Alignment.topLeft, child: Image.asset("assets/images/img.png") ), + NotificationWidget() ], ), diff --git a/waste_management/lib/screens/homescreen/employeeDashboard.dart b/waste_management/lib/screens/homescreen/employeeDashboard.dart new file mode 100644 index 0000000..7ded943 --- /dev/null +++ b/waste_management/lib/screens/homescreen/employeeDashboard.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; +import 'package:curved_navigation_bar/curved_navigation_bar.dart'; +import 'package:waste_management/constants/theming.dart'; +import 'package:waste_management/notification/notificiationIcon.dart'; + +import 'package:waste_management/screens/mapscreen/livelocation.dart'; +import 'package:waste_management/screens/welcome/loginscreen.dart'; +import 'package:waste_management/widgets/adminFeatures/Bills.dart'; +import 'package:waste_management/widgets/adminFeatures/Dashboard.dart'; +import 'package:waste_management/widgets/adminFeatures/Schedules.dart'; +import 'package:waste_management/widgets/adminFeatures/Users.dart'; +import 'package:waste_management/widgets/citizenFeatures/Forum/forumDashboard.dart'; +import 'package:waste_management/widgets/citizenFeatures/educationalBlog/blogDashboard.dart'; +import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueFeed.dart'; +import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueScreen.dart'; +import 'package:waste_management/widgets/citizenFeatures/volunteer/volunteer.dart'; +import 'package:waste_management/widgets/common/Profile.dart'; +import 'package:waste_management/widgets/employeeFeatures/checkInout/checkin.dart'; + +class EmployeeDashboard extends StatefulWidget { + static const String routeName = '/admin-home-screen'; + const EmployeeDashboard({super.key}); + + @override + State createState() => _EmployeeDashboardState(); +} + +class _EmployeeDashboardState extends State { + int _page = 1; + List pages = [ + + // const UserManage(), + // const Schedules(), + // const AdminDashboard(), + // const Bills(), + // Profile(), + EventCalendar(), + + CheckIn(), + const GoogleMapLive(), + + + + + ]; + + + // Initial Selected Value + String dropdownvalue = 'off'; + String mealCvalue= '1'; + final _msgController = TextEditingController(); + + + + + @override + void dispose() { + _msgController.dispose(); + //_msgController.clear(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + + + return Scaffold( + appBar: PreferredSize( + preferredSize: const Size.fromHeight(50), + child: AppBar( + flexibleSpace: Container( + decoration: const BoxDecoration( + color: Colors.black, + ), + ), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + height: 50, + alignment: Alignment.topLeft, + child: Image.asset("assets/images/img.png") + ), + NotificationWidget() + + ], + ), + actions: [ + PopupMenuButton( + onSelected: (value) { + if (value == 'logout') { + // Implement your logout logic here + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute(builder: (context) => LoginScreen()), + (route) => false, // Set to false to remove all previous pages + ); + + } + }, + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + value: 'logout', + child: Text('Logout'), + ), + ], + ), + ], + ), + ), + bottomNavigationBar: CurvedNavigationBar( + backgroundColor: kBackgroundColor, + color: kPrimaryLightColor, + index: 2, + items: [ + Icon(Icons.people_outlined, size: 30, color: ksecondaryHeaderColor,), + + Icon(Icons.home, size: 30, color: ksecondaryHeaderColor,), + Icon(Icons.place_rounded, size: 30, color: ksecondaryHeaderColor,), + + ], + onTap: (index) { + setState(() { + _page = index; + }); + }, + ), + resizeToAvoidBottomInset: false, + body: pages[_page], + ); + } +} diff --git a/waste_management/lib/screens/welcome/loginscreen.dart b/waste_management/lib/screens/welcome/loginscreen.dart index 44388d9..aa21a88 100644 --- a/waste_management/lib/screens/welcome/loginscreen.dart +++ b/waste_management/lib/screens/welcome/loginscreen.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:waste_management/constants/theming.dart'; import 'package:waste_management/router.dart'; import 'package:waste_management/screens/homescreen/adminHomeScreen.dart'; +import 'package:waste_management/screens/homescreen/employeeDashboard.dart'; import 'package:waste_management/screens/welcome/emailForVerify.dart'; import 'package:waste_management/services/auth_service.dart'; import 'package:waste_management/widgets/authWidgets/button_widget.dart'; @@ -49,6 +50,23 @@ class _LoginScreenState extends State { ); } + void loginEmployee() { + // authService.signInUser( + // context: context, + // email: emailController.text, + // password: passwordController.text + // ); + + Navigator.push( + context, + // generateRoute( + // RouteSettings(name: HomeScreen.routeName) + // ), + MaterialPageRoute(builder: (context) => EmployeeDashboard()), // same as above + //(route) => false + ); + } + @override Widget build(BuildContext context) { @@ -131,10 +149,19 @@ class _LoginScreenState extends State { width: MediaQuery.of(context).size.width, child: ButtonWidget( textSize: 20, - btnText: "Login", + btnText: "Login as Citizen", onPress: login, ), ), + SizedBox( + height: 60, + width: MediaQuery.of(context).size.width, + child: ButtonWidget( + textSize: 20, + btnText: "Login as Employee", + onPress: loginEmployee, + ), + ), const SizedBox( height: 20, ) diff --git a/waste_management/lib/screens/welcome/otpVerify.dart b/waste_management/lib/screens/welcome/otpVerify.dart index 7142b06..2196261 100644 --- a/waste_management/lib/screens/welcome/otpVerify.dart +++ b/waste_management/lib/screens/welcome/otpVerify.dart @@ -5,10 +5,7 @@ import 'package:waste_management/screens/welcome/emailForVerify.dart'; import 'package:waste_management/services/auth_service.dart'; class MyVerify extends StatefulWidget { - MyVerify({super.key, - required this.email, - required this.otpToken - }); + MyVerify({super.key, required this.email, required this.otpToken}); String email; String otpToken; @@ -24,12 +21,7 @@ class _MyVerifyState extends State { final fillColor = Color.fromRGBO(243, 246, 249, 0); final borderColor = Color.fromRGBO(23, 171, 144, 0.4); - void ForgetPassInitiate(BuildContext context, String otp){ - - - } - - + void ForgetPassInitiate(BuildContext context, String otp) {} @override void dispose() { @@ -37,7 +29,6 @@ class _MyVerifyState extends State { pinController.dispose(); } - @override Widget build(BuildContext context) { final defaultPinTheme = PinTheme( @@ -113,10 +104,9 @@ class _MyVerifyState extends State { ), Pinput( length: 4, - defaultPinTheme: defaultPinTheme, - focusedPinTheme: focusedPinTheme, - submittedPinTheme: submittedPinTheme, - + defaultPinTheme: defaultPinTheme, + focusedPinTheme: focusedPinTheme, + submittedPinTheme: submittedPinTheme, showCursor: true, onCompleted: (pin) => print(pin), ), @@ -131,24 +121,26 @@ class _MyVerifyState extends State { backgroundColor: Colors.green.shade600, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10))), - onPressed: () {}, - child: const Text("Verify Email Address", style: TextStyle( - color: ksecondaryHeaderColor))), + onPressed: () { + authService.checkMailOTP( + context: context, + otp: pinController.text, + forgetPassToken: widget.otpToken); + }, + child: const Text("Verify Email Address", + style: TextStyle(color: ksecondaryHeaderColor))), ), Row( children: [ TextButton( onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => MyPhone() - ) - ); + Navigator.push(context, + MaterialPageRoute(builder: (context) => MyPhone())); }, child: Text( "Edit Email Address ?", - style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold), + style: TextStyle( + color: Colors.black, fontWeight: FontWeight.bold), )) ], ) @@ -158,4 +150,4 @@ class _MyVerifyState extends State { ), ); } -} \ No newline at end of file +} diff --git a/waste_management/lib/services/auth_service.dart b/waste_management/lib/services/auth_service.dart index 41f175a..409316d 100644 --- a/waste_management/lib/services/auth_service.dart +++ b/waste_management/lib/services/auth_service.dart @@ -13,128 +13,181 @@ import 'package:waste_management/router.dart'; import 'package:waste_management/screens/homescreen/adminHomeScreen.dart'; import 'package:waste_management/screens/welcome/loginscreen.dart'; import 'package:waste_management/screens/welcome/otpVerify.dart'; +import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueFeed.dart'; -class AuthServices{ - - // void signUpUser({ - // required BuildContext context, - // required String email, - // required String password, - // required String name, - // required String messid, - // }) async { - // try{ - // User user = User( - // id: '', - // name: name, - // email: email, - // password: password, - // messid: messid, - // messname: '', - // token: '' - // ); - // - // http.Response res= await http.post(Uri.parse('$uri/api/signup'), - // body: user.toJson(), - // headers: { - // 'Content-Type' : 'application/json; charset=UTF-8', - // - // } - // ); - // print("Sign up info"); - // print(res.body); - // - // - // - // httpErrorHandle( - // response: res, - // context: context, - // onSuccess: (){ - // //print("Account opened"); - // showSnackBar(context, 'Account created! Log in with same email and password'); - // } - // ); - // - // - // }catch(e){ - // print(e.toString()); - // // ScaffoldMessenger.of(context).showSnackBar( - // // SnackBar( - // // content: Text("Try again with right information" ))); - // - // - // } - // } +class AuthServices { - void mailVerify({ + void checkIn({ required BuildContext context, - required String email, + required bool flag, + }) async { - try{ + try { - final res= await http.post(Uri.parse('$uri/auth/reset-password/initiate'), + final res = await http.post(Uri.parse('$uri/logs/checkin'), body: jsonEncode({ - 'email': email, + "flag": flag }), headers: { // "Access-Control-Allow-Origin": "*", - 'Content-Type' : 'application/json; charset=UTF-8', + 'Content-Type': 'application/json; charset=UTF-8', // 'Accept': '*/*' - } - ); - print(email); + }); + - Map response = jsonDecode(res.body as String); - String otpToken = response['otptoken']; - print(otpToken); // print(res.body); httpErrorHandle( response: res, context: context, onSuccess: () async { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => CheckI() + // ) + // ); + }); + } catch (e) { + print(e.toString()); + showSnackBar(context, e.toString()); + } + } + void issuePost({ + required BuildContext context, + required String type, + required String description, + required String latitude, + required String longitude, + required bool isAnonymous, + }) async { + try { + print(type); + print(description); + final res = await http.post(Uri.parse('$uri/issues/create'), + body: jsonEncode({ + "issueType": type, + "description": description, + "latitude": latitude, + "longitude": longitude, + "isAnonymous": isAnonymous + }), + headers: { + // "Access-Control-Allow-Origin": "*", + 'Content-Type': 'application/json; charset=UTF-8', + // 'Accept': '*/*' + }); + + + +// print(res.body); + httpErrorHandle( + response: res, + context: context, + onSuccess: () async { Navigator.push( context, MaterialPageRoute( - builder: (context) => MyVerify(email: email, otpToken: otpToken,) + builder: (context) => IssueFeed() ) ); - - } - ); - }catch(e){ + }); + } catch (e) { print(e.toString()); showSnackBar(context, e.toString()); } } - void signInUser({ + void mailVerify({ required BuildContext context, required String email, - required String password, }) async { - try{ + try { + final res = + await http.post(Uri.parse('$uri/auth/reset-password/initiate'), + body: jsonEncode({ + 'email': email, + }), + headers: { + // "Access-Control-Allow-Origin": "*", + 'Content-Type': 'application/json; charset=UTF-8', + // 'Accept': '*/*' + }); + print(email); + + Map response = jsonDecode(res.body as String); + String otpToken = response['otptoken']; + print(otpToken); + +// print(res.body); + httpErrorHandle( + response: res, + context: context, + onSuccess: () async { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MyVerify( + email: email, + otpToken: otpToken, + ))); + }); + } catch (e) { + print(e.toString()); + showSnackBar(context, e.toString()); + } + } - final res= await http.post(Uri.parse('$uri/auth/login'), + void checkMailOTP({ + required BuildContext context, + required String otp, + required String forgetPassToken, + }) async { + try { + final res = await http.post(Uri.parse('$uri/auth/reset-password/confirm'), body: jsonEncode({ - 'email': email, - 'password': password + 'otp': otp, }), headers: { // "Access-Control-Allow-Origin": "*", - 'Content-Type' : 'application/json; charset=UTF-8', + 'Content-Type': 'application/json; charset=UTF-8', + 'Authorization': 'Bearer $forgetPassToken', // 'Accept': '*/*' - } - ); + }); + print(otp); + + if (res.statusCode == 200) { + Navigator.push( + context, MaterialPageRoute(builder: (context) => LoginScreen())); + } else { + showSnackBar(context, 'Invalid OTP'); + } + } catch (e) { + print(e.toString()); + showSnackBar(context, e.toString()); + } + } + void signInUser({ + required BuildContext context, + required String email, + required String password, + }) async { + try { + final res = await http.post(Uri.parse('$uri/auth/login'), + body: jsonEncode({'email': email, 'password': password}), + headers: { + // "Access-Control-Allow-Origin": "*", + 'Content-Type': 'application/json; charset=UTF-8', + // 'Accept': '*/*' + }); // print(res.body); httpErrorHandle( response: res, context: context, onSuccess: () async { - // log in er por token store kore rakhbo jeno barbar log in krte na hoy SharedPreferences prefs = await SharedPreferences.getInstance(); @@ -147,10 +200,10 @@ class AuthServices{ print(loggeduser.roleName); print(loggeduser.token); - Provider.of(context, listen: false).setUser(loggeduser); - await prefs.setString('Authentication', jsonDecode(res.body)['token']); - - + Provider.of(context, listen: false) + .setUser(loggeduser); + await prefs.setString( + 'Authentication', jsonDecode(res.body)['token']); final user = Provider.of(context, listen: false).user; @@ -159,20 +212,15 @@ class AuthServices{ //shared preference a jst token ta thakbe Navigator.pushAndRemoveUntil( context, - generateRoute( - RouteSettings(name: HomeScreen.routeName) - ), + generateRoute(RouteSettings(name: HomeScreen.routeName)), //MaterialPageRoute(builder: (context) => HomeScreen()), same as above - (route) => false); - } - ); - }catch(e){ + (route) => false); + }); + } catch (e) { print(e.toString()); showSnackBar(context, e.toString()); } } - - -} \ No newline at end of file +} diff --git a/waste_management/lib/widgets/citizenFeatures/educationalBlog/videoPlayer.dart b/waste_management/lib/widgets/citizenFeatures/educationalBlog/videoPlayer.dart index cfc41fd..e4122ce 100644 --- a/waste_management/lib/widgets/citizenFeatures/educationalBlog/videoPlayer.dart +++ b/waste_management/lib/widgets/citizenFeatures/educationalBlog/videoPlayer.dart @@ -16,7 +16,7 @@ class _MyVideoPlayerState extends State { void initState() { // TODO: implement initState super.initState(); - flickManager = FlickManager(videoPlayerController: VideoPlayerController.networkUrl(Uri.parse("https://cdn.discordapp.com/attachments/1190253751903207538/1238581046828732529/IMPROPER_WASTE_DISPOSAL_EFFECTS_AND_SOLUTIONS.mp4?ex=663fcdfd&is=663e7c7d&hm=a9b710a3edfe79ccdd6c6a06e6e2d57bc910963ecc04e70065e750a2d0bb361a&"))); + flickManager = FlickManager(videoPlayerController: VideoPlayerController.networkUrl(Uri.parse("https://cdn.discordapp.com/attachments/1190253751903207538/1238652969969389651/Untitled_design.mp4?ex=664010f9&is=663ebf79&hm=6fa59fd4dfb83d1d1da912e73488eae5a004eb75bf45cc5c6ac377c7f95fb4ff&"))); } diff --git a/waste_management/lib/widgets/citizenFeatures/issueScreen/issueCard.dart b/waste_management/lib/widgets/citizenFeatures/issueScreen/issueCard.dart new file mode 100644 index 0000000..db801d8 --- /dev/null +++ b/waste_management/lib/widgets/citizenFeatures/issueScreen/issueCard.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; + +class IssueCard extends StatelessWidget { + final String id; + final String type; + final String issuePic; + final String description; + final String latitude; + final String longitude; + + + IssueCard({ + required this.id, + required this.type, + required this.issuePic, + required this.description, + required this.latitude, + required this.longitude, + + }); + + @override + Widget build(BuildContext context) { + return Card( + elevation: 3, + margin: EdgeInsets.all(10), + child: Padding( + padding: EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.blue, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + type, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox(height: 10), + Text( + 'Latitude: $latitude, Longitude: $longitude', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), + Text(description), + SizedBox(height: 10), + Image.asset( + issuePic, + height: 150, + width: double.infinity, + fit: BoxFit.cover, + ), + ], + ), + ), + ); + } +} diff --git a/waste_management/lib/widgets/citizenFeatures/issueScreen/issueFeed.dart b/waste_management/lib/widgets/citizenFeatures/issueScreen/issueFeed.dart new file mode 100644 index 0000000..44f6c42 --- /dev/null +++ b/waste_management/lib/widgets/citizenFeatures/issueScreen/issueFeed.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:waste_management/models/issueModel.dart'; +import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueCard.dart'; +import 'package:waste_management/widgets/citizenFeatures/issueScreen/issueScreen.dart'; +// Import the IssueCard widget +// Import the Forum class + +class IssueFeed extends StatefulWidget { + + + @override + State createState() => _IssueFeedState(); +} + +class _IssueFeedState extends State { + List dummyIssues = [ + Issue( + id: '1', + type: 'Overflowing bins', + issuePic: "assets/images/garbage7.png", + description: 'Overflowing bins on the street corner', + latitude: '23.789', + longitude: '90.412', + isAnonymous: false, + ), + Issue( + id: '2', + type: 'Littering', + issuePic: "assets/images/garbage8.png", + description: 'Public littering near the park', + latitude: '23.791', + longitude: '90.415', + isAnonymous: false, + ), + ]; + + void AddIssue(Issue newIssue){ + setState(() { + dummyIssues.add(newIssue); + }); + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ListView.builder( + itemCount: dummyIssues.length, + itemBuilder: (context, index) { + final issue = dummyIssues[index]; + return IssueCard( + id: issue.id, + type: issue.type, + issuePic: issue.issuePic, + description: issue.description, + latitude: issue.latitude, + longitude: issue.longitude, + ); + }, + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + // Add your action here + Navigator.push( + context, + + MaterialPageRoute(builder: (context) => IssuePage(addIssue: AddIssue),)); + }, + child: Row( + children: [ + Icon(Icons.add), + Text("New") + ], + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + ); + } +} diff --git a/waste_management/lib/widgets/citizenFeatures/issueScreen/issueScreen.dart b/waste_management/lib/widgets/citizenFeatures/issueScreen/issueScreen.dart index 75f6f2e..d81eb9e 100644 --- a/waste_management/lib/widgets/citizenFeatures/issueScreen/issueScreen.dart +++ b/waste_management/lib/widgets/citizenFeatures/issueScreen/issueScreen.dart @@ -1,6 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:waste_management/models/issueModel.dart'; +import 'package:waste_management/services/auth_service.dart'; class IssuePage extends StatefulWidget { + final Function(Issue) addIssue; // Function to add issue + + IssuePage({required this.addIssue}); + @override _IssuePageState createState() => _IssuePageState(); } @@ -12,6 +18,40 @@ class _IssuePageState extends State { String? _selectedIssue; bool _isAnonymous = false; + final AuthServices authService = AuthServices(); + + void _submitIssue(BuildContext context) { + authService.issuePost( + context: context, + type: _selectedIssue!, + description: _descriptionController.text, + latitude: _latitudeController.text, + longitude: _longitudeController.text, + isAnonymous: _isAnonymous + ); + // Create a new Issue object with the provided data + Issue newIssue = Issue( + id: DateTime.now().toString(), // Unique ID based on timestamp + type: _selectedIssue!, + issuePic: "assets/images/garbage9.png", // Default image path, you can change it as needed + description: _descriptionController.text, + latitude: _latitudeController.text, + longitude: _longitudeController.text, + isAnonymous: _isAnonymous, + ); + + // Call the addIssue function passed from IssueFeed + widget.addIssue(newIssue); + + // Reset fields after submission + _latitudeController.clear(); + _longitudeController.clear(); + _descriptionController.clear(); + _selectedIssue = null; + _isAnonymous = false; + + Navigator.pop(context); + } @override Widget build(BuildContext context) { @@ -35,7 +75,9 @@ class _IssuePageState extends State { _buildAnonymousOption(), SizedBox(height: 20), ElevatedButton( - onPressed: _submitIssue, + onPressed: (){ + _submitIssue(context); + }, child: Text('Submit'), ), ], @@ -244,19 +286,7 @@ class _IssuePageState extends State { ); } - void _submitIssue() { - // Implement submission logic here - print('Issue: $_selectedIssue'); - print('Location: ${_latitudeController.text} longL: ${_longitudeController}'); - print('Description: ${_descriptionController.text}'); - print('Anonymous: $_isAnonymous'); - // Reset fields after submission if needed - _latitudeController.clear(); - _longitudeController.clear(); - _descriptionController.clear(); - _selectedIssue = null; - _isAnonymous = false; - } + } diff --git a/waste_management/lib/widgets/citizenFeatures/volunteer/volunteer.dart b/waste_management/lib/widgets/citizenFeatures/volunteer/volunteer.dart index ce6669c..9f82889 100644 --- a/waste_management/lib/widgets/citizenFeatures/volunteer/volunteer.dart +++ b/waste_management/lib/widgets/citizenFeatures/volunteer/volunteer.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:table_calendar/table_calendar.dart'; +import 'package:waste_management/widgets/citizenFeatures/volunteer/volunteerForm.dart'; class EventCalendar extends StatefulWidget { const EventCalendar({Key? key}) : super(key: key); @@ -34,62 +35,81 @@ class _EventCalendarState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - TableCalendar( - firstDay: DateTime.utc(2010, 10, 16), - lastDay: DateTime.utc(2030, 3, 14), - focusedDay: _focusedDay, - selectedDayPredicate: (day) { - return isSameDay(_selectedDay, day); - }, - onDaySelected: _onDaySelected, - calendarFormat: _calendarFormat, - onFormatChanged: (format) { - setState(() { - _calendarFormat = format; - }); - }, - onPageChanged: (focusedDay) { - setState(() { - _focusedDay = focusedDay; - }); - }, - eventLoader: _getEventsForDay, - calendarBuilders: CalendarBuilders( - dowBuilder: (context, day) { - if (day.weekday == DateTime.sunday) { - final text = DateFormat.E().format(day); - - return Center( - child: Text( - text, - style: TextStyle(color: Colors.red), - ), - ); - } + return Scaffold( + body: Column( + children: [ + TableCalendar( + firstDay: DateTime.utc(2010, 10, 16), + lastDay: DateTime.utc(2030, 3, 14), + focusedDay: _focusedDay, + selectedDayPredicate: (day) { + return isSameDay(_selectedDay, day); }, - ), - ), - if (_selectedEvents.isNotEmpty) - Column( - children: [ - SizedBox(height: 10), - Text('Events for ${DateFormat.yMMMMd().format(_selectedDay)}'), - SizedBox(height: 10), - ListView.builder( - shrinkWrap: true, - itemCount: _selectedEvents.length, - itemBuilder: (context, index) { - final event = _selectedEvents[index]; - return ListTile( - title: Text(event.title), + onDaySelected: _onDaySelected, + calendarFormat: _calendarFormat, + onFormatChanged: (format) { + setState(() { + _calendarFormat = format; + }); + }, + onPageChanged: (focusedDay) { + setState(() { + _focusedDay = focusedDay; + }); + }, + eventLoader: _getEventsForDay, + calendarBuilders: CalendarBuilders( + dowBuilder: (context, day) { + if (day.weekday == DateTime.sunday) { + final text = DateFormat.E().format(day); + + return Center( + child: Text( + text, + style: TextStyle(color: Colors.red), + ), ); - }, - ), - ], + } + }, + ), ), - ], + if (_selectedEvents.isNotEmpty) + Column( + children: [ + SizedBox(height: 10), + Text('Events for ${DateFormat.yMMMMd().format(_selectedDay)}'), + SizedBox(height: 10), + ListView.builder( + shrinkWrap: true, + itemCount: _selectedEvents.length, + itemBuilder: (context, index) { + final event = _selectedEvents[index]; + return ListTile( + title: Text(event.title), + ); + }, + ), + ], + ), + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + // Add your action here + Navigator.push( + context, + + MaterialPageRoute(builder: (context) => VolunteerForm())); + }, + child: Row( + children: [ + Icon(Icons.add), + Text("Join") + ], + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, + ); } diff --git a/waste_management/lib/widgets/citizenFeatures/volunteer/volunteerForm.dart b/waste_management/lib/widgets/citizenFeatures/volunteer/volunteerForm.dart index e69de29..31d0bf2 100644 --- a/waste_management/lib/widgets/citizenFeatures/volunteer/volunteerForm.dart +++ b/waste_management/lib/widgets/citizenFeatures/volunteer/volunteerForm.dart @@ -0,0 +1,200 @@ +import 'package:flutter/material.dart'; + +class VolunteerForm extends StatefulWidget { + @override + _VolunteerFormState createState() => _VolunteerFormState(); +} + +class _VolunteerFormState extends State { + final TextEditingController _latitudeController = TextEditingController(); + final TextEditingController _longitudeController = TextEditingController(); + final TextEditingController _descriptionController = TextEditingController(); + + String? _selectedIssue; + bool _isAnonymous = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Volunteer Request'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildIssueDropdown(), + SizedBox(height: 20), + _buildLocationField(), + SizedBox(height: 20), + _buildDescriptionField(), + SizedBox(height: 20), + _buildPhotoAttachmentField(), + + SizedBox(height: 20), + ElevatedButton( + onPressed: _submitIssue, + child: Text('Submit'), + ), + ], + ), + ), + ); + } + + Widget _buildIssueDropdown() { + return Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + + child: DropdownButtonFormField( + value: _selectedIssue, + onChanged: (value) { + setState(() { + _selectedIssue = value; + }); + }, + decoration: InputDecoration( + labelText: 'Select Volunteer Type', + border: OutlineInputBorder(), + ), + items: [ + 'Regular', + 'Monthly Volunteer', + 'Weekly Volunteer', + + ].map((issue) { + return DropdownMenuItem( + value: issue, + child: Text(issue), + ); + }).toList(), + ), + ); + } + + Widget _buildLocationField() { + return Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + child: TextField( + controller: _longitudeController, + decoration: InputDecoration( + labelText: 'Location Area', + border: OutlineInputBorder(), + ), + ), + ), + ); + } + + + Widget _buildDescriptionField() { + return Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + child: TextField( + controller: _descriptionController, + maxLines: 3, + decoration: InputDecoration( + labelText: 'Describe Yourself', + border: OutlineInputBorder(), + ), + ), + ); + } + + Widget _buildPhotoAttachmentField() { + return Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text('Photo Attachment'), + SizedBox(height: 10), + ElevatedButton( + onPressed: () { + // Add functionality to attach photo + }, + child: Text('Attach Photo'), + ), + ], + ), + ); + } + + + + void _submitIssue() { + // Implement submission logic here + print('Issue: $_selectedIssue'); + print('Location: ${_latitudeController.text} longL: ${_longitudeController}'); + print('Description: ${_descriptionController.text}'); + print('Anonymous: $_isAnonymous'); + // Reset fields after submission if needed + _latitudeController.clear(); + _longitudeController.clear(); + _descriptionController.clear(); + _selectedIssue = null; + _isAnonymous = false; + } +} + + diff --git a/waste_management/lib/widgets/employeeFeatures/checkInout/checkOutConfirm.dart b/waste_management/lib/widgets/employeeFeatures/checkInout/checkOutConfirm.dart index 88a2627..58802de 100644 --- a/waste_management/lib/widgets/employeeFeatures/checkInout/checkOutConfirm.dart +++ b/waste_management/lib/widgets/employeeFeatures/checkInout/checkOutConfirm.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:waste_management/screens/homescreen/adminHomeScreen.dart'; import 'package:waste_management/screens/welcome/loginscreen.dart'; +import 'package:waste_management/services/auth_service.dart'; import 'package:waste_management/widgets/employeeFeatures/checkInout/checkedOut.dart'; import 'package:waste_management/widgets/employeeFeatures/checkInout/checkin.dart'; @@ -15,12 +16,14 @@ class CheckoutConfirm extends StatefulWidget { } class _CheckoutConfirmState extends State { + final AuthServices authServices = AuthServices(); @override void initState() { // TODO: implement initState super.initState(); + authServices.checkIn(context: context, flag: false); Timer(Duration(seconds: 6), () { Navigator.pushReplacement( context, diff --git a/waste_management/lib/widgets/employeeFeatures/checkInout/checkin.dart b/waste_management/lib/widgets/employeeFeatures/checkInout/checkin.dart index 9205d52..89f5649 100644 --- a/waste_management/lib/widgets/employeeFeatures/checkInout/checkin.dart +++ b/waste_management/lib/widgets/employeeFeatures/checkInout/checkin.dart @@ -3,6 +3,7 @@ import 'package:page_transition/page_transition.dart'; import 'package:swipeable_button_view/swipeable_button_view.dart'; import 'package:waste_management/constants/theming.dart'; +import 'package:waste_management/services/auth_service.dart'; import 'package:waste_management/widgets/employeeFeatures/checkInout/checkOutConfirm.dart'; import 'package:waste_management/widgets/employeeFeatures/checkInout/confirmPage.dart'; @@ -16,6 +17,7 @@ class CheckIn extends StatefulWidget { class _CheckInState extends State { bool isFinished = false; + @override Widget build(BuildContext context) { return Scaffold( diff --git a/waste_management/lib/widgets/employeeFeatures/checkInout/confirmPage.dart b/waste_management/lib/widgets/employeeFeatures/checkInout/confirmPage.dart index 3b8e8e3..e4f99d2 100644 --- a/waste_management/lib/widgets/employeeFeatures/checkInout/confirmPage.dart +++ b/waste_management/lib/widgets/employeeFeatures/checkInout/confirmPage.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lottie/lottie.dart'; import 'package:waste_management/constants/theming.dart'; +import 'package:waste_management/services/auth_service.dart'; import 'dart:async'; import 'package:waste_management/widgets/employeeFeatures/checkInout/checkin.dart'; @@ -16,10 +17,12 @@ class ConfirmationPage extends StatefulWidget { class _ConfirmationPageState extends State with TickerProviderStateMixin { late final AnimationController _controller; + final AuthServices authServices = AuthServices(); @override void initState() { super.initState(); + authServices.checkIn(context: context, flag: true); _controller = AnimationController(vsync: this); Timer(Duration(seconds: 4), () { Navigator.pushReplacement(