diff --git a/Backend/EgressAPI/handlers/SensorGatewayData.go b/Backend/EgressAPI/handlers/SensorGatewayData.go index 17b23decc79178127e1e2e5ed0e2c62568699ed8..87caf51cae511f0560e6cf3d619ba6cff1bb58ed 100644 --- a/Backend/EgressAPI/handlers/SensorGatewayData.go +++ b/Backend/EgressAPI/handlers/SensorGatewayData.go @@ -23,18 +23,19 @@ func SensorGatewayData(w http.ResponseWriter, r *http.Request) { Sensors []other.SensorInfo `json:"sensor"` Gateways []other.Gateway `json:"gateway"` }{} - + //gets the data for the sensors sensors, err := other.GetSensors(token) if err != nil { log.Println(err.Error()) return } - + //get the data for the gateways gateways, err := other.GetGateway(token) if err != nil { log.Println(err.Error()) return } + //encode both data and send it to frontend data.Sensors = sensors data.Gateways = gateways encoder := json.NewEncoder(w) diff --git a/Backend/EgressAPI/handlers/SystemData.go b/Backend/EgressAPI/handlers/SystemData.go index ccbc8f04f4f4578dbf96d3cb8097905860b3c007..dae28a53b4a5e95fa9ce92426261db8f33c1a1bb 100644 --- a/Backend/EgressAPI/handlers/SystemData.go +++ b/Backend/EgressAPI/handlers/SystemData.go @@ -11,6 +11,7 @@ func BuildDep(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Access-Control-Allow-Origin", ""+other.WebsiteURL) w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") + //POST or get building and department data if r.Method == http.MethodPost { token, err := other.ValidateRequest(r.Body) if err != nil { @@ -18,7 +19,7 @@ func BuildDep(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } - + //gets the building and its connected departments data connected to the company of the user of the token buildingDep, err := other.GetBuildingsAndDepartments(token) if err != nil { log.Println(err.Error()) @@ -30,7 +31,7 @@ func BuildDep(w http.ResponseWriter, r *http.Request) { BuildingDep map[string][]other.Department `json:"buildingDep"` Process []other.Processes `json:"process"` } - + //sort the data for building and its departments buildingDepJSON := make(map[string][]other.Department) for key, value := range buildingDep { departments := make([]other.Department, len(value)) @@ -44,7 +45,7 @@ func BuildDep(w http.ResponseWriter, r *http.Request) { response := responseData{ BuildingDep: buildingDepJSON, } - + //sends the data to frontend encoder := json.NewEncoder(w) err = encoder.Encode(response) if err != nil { @@ -62,6 +63,7 @@ func Process(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Access-Control-Allow-Origin", ""+other.WebsiteURL) w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") + //POST or get process data if r.Method == http.MethodPost { token, err := other.ValidateRequest(r.Body) if err != nil { @@ -69,7 +71,7 @@ func Process(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } - + //gets the process data connected to the company of the user of the token process, err := other.GetProcess(token) if err != nil { log.Println(err.Error()) @@ -85,7 +87,7 @@ func Process(w http.ResponseWriter, r *http.Request) { response := responseData{ Process: process, } - + //sends the data to frontend encoder := json.NewEncoder(w) err = encoder.Encode(response) if err != nil { @@ -104,6 +106,7 @@ func ProcessMachine(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Access-Control-Allow-Origin", ""+other.WebsiteURL) w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") + //POST or get process and machine/sensor data if r.Method == http.MethodPost { decoder := json.NewDecoder(r.Body) //creates an anonymous struct to reduce clutter in structs file @@ -116,6 +119,7 @@ func ProcessMachine(w http.ResponseWriter, r *http.Request) { println(err.Error()) return } + //Checks if sessiontoken is active or not valid, err := other.IsTokenStillActive(data.SessionToken) if !valid { log.Println("Session token expired or not valid!") @@ -125,6 +129,7 @@ func ProcessMachine(w http.ResponseWriter, r *http.Request) { } return } + //gets the connected process and connected machines/sensors and all machines and their name additionally processMachines, machines, err := other.GetMachinesForProcess(data.SessionToken, data.Process) if err != nil { log.Println(err.Error()) @@ -145,6 +150,7 @@ func ProcessMachine(w http.ResponseWriter, r *http.Request) { var added = false // Check if the machine is in processMachines for _, processMachine := range processMachines { + //If machine is already connected to the process it returns true if not connected it returns false if processMachine.MachineID == machine.EUI { added = true break @@ -153,6 +159,7 @@ func ProcessMachine(w http.ResponseWriter, r *http.Request) { // Append the result to the responseData slice response.AddedOrNot = append(response.AddedOrNot, added) } + //sends the data to front end response.Machines = machines encoder := json.NewEncoder(w) err = encoder.Encode(response) diff --git a/Backend/EgressAPI/handlers/enokData.go b/Backend/EgressAPI/handlers/enokData.go index 8b173847b1901feed1ad184e5efa913e049769af..6d7c7e9c11bdf5d70f448935c35c1f82e6beb334 100644 --- a/Backend/EgressAPI/handlers/enokData.go +++ b/Backend/EgressAPI/handlers/enokData.go @@ -11,6 +11,7 @@ func EnoekData(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Access-Control-Allow-Origin", ""+other.WebsiteURL) w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") + //POST or get enoeks posts if r.Method == http.MethodPost { token, err := other.ValidateRequest(r.Body) if err != nil { @@ -59,7 +60,7 @@ func EnoekData(w http.ResponseWriter, r *http.Request) { Approved: enoeks[i].Approved, }) } - + //send data back to front end encoder := json.NewEncoder(w) err = encoder.Encode(data) if err != nil { diff --git a/Backend/EgressAPI/handlers/userData.go b/Backend/EgressAPI/handlers/userData.go index 19a5447c600c842a78d7114fa15fd57b0e6a7720..835116c48bcdc51382fd94dbd7fb87e9d347ec7c 100644 --- a/Backend/EgressAPI/handlers/userData.go +++ b/Backend/EgressAPI/handlers/userData.go @@ -11,7 +11,9 @@ func UserData(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Headers", "Content-Type") w.Header().Set("Access-Control-Allow-Origin", ""+other.WebsiteURL) w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") + //POST or get user data if r.Method == http.MethodPost { + //gets token from the user token, err := other.ValidateRequest(r.Body) if err != nil { log.Println(err.Error()) @@ -23,11 +25,24 @@ func UserData(w http.ResponseWriter, r *http.Request) { Users []other.Users `json:"Users"` }{} + //verify that the user making the request is allowed + permissionLevel, err := other.GetPermissionFromToken(token) + if err != nil { + log.Println(err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + if permissionLevel != 0 { + w.WriteHeader(http.StatusUnauthorized) + return + } + //get the user connected to the company the token is connected to users, err := other.SeeUsers(token) if err != nil { log.Println(err.Error()) return } + //sends the data to front end data.Users = users encoder := json.NewEncoder(w) err = encoder.Encode(data) diff --git a/Backend/commands.sql b/Backend/commands.sql index 563d057b119b5684a78d9bd9af604165605f79a8..1bc79ac91b34ad89f6b86faa29c5d88739cc1316 100644 --- a/Backend/commands.sql +++ b/Backend/commands.sql @@ -114,11 +114,12 @@ ALTER TABLE `sensorData` ADD FOREIGN KEY (`eui`) REFERENCES `machine` (`eui`); ALTER TABLE `processes` ADD FOREIGN KEY (`company_id`) REFERENCES `company` (`id`); +INSERT INTO company (`name`) VALUES ('Test'); INSERT INTO company (`name`) VALUES ('Vyrk'); INSERT INTO company (`name`) VALUES ('Innoveria'); INSERT INTO company (`name`) VALUES ('Monitor'); INSERT INTO users (`email`, `first_name`, `last_name`, `password`, `permission`, `company_id`)VALUES ('admin@admin.no', 'admin', 'admin', 'admin', 0, 1); -INSERT INTO session VALUES ("0321412", 1, '2024-02-01 15:30:45', '2024-07-09 15:30:45'); +INSERT INTO session VALUES ('0321412', 1, '2024-02-01 15:30:45', '2024-07-09 15:30:45'); INSERT INTO building (`company_id`, `name`) VALUES (1, 'A'); INSERT INTO building (`company_id`, `name`) VALUES (1, 'B'); INSERT INTO building (`company_id`, `name`) VALUES (1, 'C'); @@ -131,9 +132,8 @@ INSERT INTO department (`building_id`, `name`) VALUES (2, 'BC'); INSERT INTO department (`building_id`, `name`) VALUES (3, 'CA'); INSERT INTO department (`building_id`, `name`) VALUES (3, 'CB'); INSERT INTO department (`building_id`, `name`) VALUES (3, 'CC'); -INSERT INTO machine (`eui`, `name`, `expected_use`, `machineNr`, `building_id`, `department_id`) VALUES ("E890E1CC8CF44B49", "press", 5, "4727AD", 1, 1); -INSERT INTO processes (`name`, `description`, `company_id`) VALUES ("WINDOW", "Make window for houses", 1); -INSERT INTO enoek_suggestion_measures (`header`, `description`, `author`, `start_date`, `stop_date`, `active`, `approved`, `process_id`) VALUES ("Oven", "Oven burn more stuff", 1, '2024-02-01 15:30:45', '2024-07-09 15:30:45', 0, NULL, 1); -INSERT INTO machine_processes (`machine_id`, `processes_id`) VALUES ("E890E1CC8CF44B49", 1); +INSERT INTO processes (`name`, `description`, `company_id`) VALUES ('WINDOW', 'Make window for houses', 1); +INSERT INTO enoek_suggestion_measures (`header`, `description`, `author`, `start_date`, `stop_date`, `active`, `approved`, `process_id`) VALUES ('Oven', 'Oven burn more stuff', 1, '2024-02-01 15:30:45', '2024-07-09 15:30:45', 0, NULL, 1); +INSERT INTO machine_processes (`machine_id`, `processes_id`) VALUES ('E890E1CC8CF44B49', 1); INSERT INTO sensorData (`eui`, `Acumulation`, `AVG_current`, `Offset_max`, `Offset_min`, `Voltage`, `Temprature`, `date_time`) VALUES - ("E890E1CC8CF44B49", 0, 0, 0, 0, 0, 0, '2024-02-11 15:30:45'); \ No newline at end of file + ('E890E1CC8CF44B49', 0, 0, 0, 0, 0, 0, '2024-02-11 15:30:45'); \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekAdd.tsx b/Frontend/power-tracker/src/components/enoekAdd.tsx index c294cb23fc4637232fa3f420eadc6731846236a4..55e773473e3585895efad7202900242451233758 100644 --- a/Frontend/power-tracker/src/components/enoekAdd.tsx +++ b/Frontend/power-tracker/src/components/enoekAdd.tsx @@ -1,7 +1,4 @@ - -import React, { useEffect, useState, useRef } from "react"; -import TopBar from '@/components/topbar'; -//import QRScan from '@/components/qr'; +import React, { useEffect, useState } from "react"; import { redirect } from 'react-router-dom'; import { Button } from "@/components/ui/button"; @@ -13,56 +10,32 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" -import { - Table, - TableBody, - TableCaption, - TableCell, - TableHead, - TableHeader, - TableRow, - SortableColumnHeader, -} from "@/components/ui/table" import { AlertDialog, - AlertDialogAction, AlertDialogCancel, AlertDialogContent, - AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, - AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, - FormMessage, } from "@/components/ui/form" -import { ScrollArea } from "@/components/ui/scroll-area" import { z } from "zod" import { useForm } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import { PlusIcon } from 'lucide-react'; import { Input } from "@/components/ui/input" -import { machine } from 'os'; import { DatePicker } from '@/components/ui/datepicker' + +// making the EgressAPI and IngressAPI addresses to consts const EgressAPI = import.meta.env.VITE_EAPI_URL const IngressAPI = import.meta.env.VITE_IAPI_URL +//an type for the diffrent values for enoek data type EnokDataItem = { id: number; header: string; @@ -73,67 +46,64 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { +//interface value for the maincomponent +interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } +//class for the data getting fetched from processes class Processes { name: string = ""; - constructor(name: string) { - this.name = name; - } - } - - - - - + constructor(name: string) { + this.name = name; + } +} -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) =>{ - const [title, setTitle] = useState(""); +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { + //start and enddate storage for the calendar pickers const [startDate, setStarDate] = React.useState<Date>() const [endDate, setEndDate] = React.useState<Date>() - const [description, setDescription] = useState(""); - const [process, setProcess] = useState(""); + //usestate for the process data const [processData, setProcessData] = useState({ process: [{ - name: "" + name: "" }] }); + //function to fetch processes from EgressAPI async function fetchDataProcess(): Promise<Processes[]> { var processArrray: Processes[] = [] await axios.post(EgressAPI + '/process', - { - sessionToken: sessionStorage.getItem('TOKEN') - }).then((res) => { - res.data.process.forEach((element: any) => { - processArrray.push({ - name: element.name - }) - }); - }).catch((error) => { - console.log(error) - setOpen(true) - }) - return processArrray - } - + { + sessionToken: sessionStorage.getItem('TOKEN') + }).then((res) => { + res.data.process.forEach((element: any) => { + processArrray.push({ + name: element.name + }) + }); + }).catch((error) => { + console.log(error) + setErrorOpen(true) + }) + return processArrray + } + // Assuming fetchDataProcess is an async function that fetches data useEffect(() => { fetchDataProcess().then((data) => { - setProcessData({ - ...processData, - process: data.map((prcoesses) => ({ name: prcoesses.name })) - }); + setProcessData({ + ...processData, + process: data.map((prcoesses) => ({ name: prcoesses.name })) + }); }); }, []); + //Zod schema to decide the types of the values in the form const m_schema = z.object({ id: z.number().int(), header: z.string(), @@ -142,10 +112,10 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) end_date: z.date().optional(), process: z.string(), }); - + const form = useForm<EnokDataItem>(); - - + + //function to call the IngressAPI to add enoek function addEnoek(values: z.infer<typeof m_schema>) { console.log(values, startDate, endDate) var token: string = "" @@ -158,178 +128,139 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) console.log('URL To call: ' + IngressAPI + 'token used' + token) axios.post( IngressAPI + '/new-enoek', - { + { enoek: { header: values.header, description: values.description, start_date: startDate, - stop_date: endDate, - }, + stop_date: endDate, + }, sessionToken: token, process: values.process }) - .then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) + .then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setErrorOpen(true) //if error the usestate is true to show a error message + }) } - const [open, setOpen] = useState(false); + //usestate to decide if to show error popup message or not + const [errorOpen, setErrorOpen] = useState(false); + //handeling of closing the error popup const handleClose = () => { - setOpen(false); + setErrorOpen(false); }; - + return ( <> - <AlertDialog open={open}> - <AlertDialogContent> - <AlertDialogHeader> - <AlertDialogTitle>Something went wrong</AlertDialogTitle> - </AlertDialogHeader> - <div> - Please try again or check your connection - </div> - <AlertDialogFooter> - <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> - </AlertDialogFooter> - </AlertDialogContent> - </AlertDialog> - <div> - <Form {...form}> - <form onSubmit={form.handleSubmit(addEnoek)}> - <FormField - control={form.control} - name='header' - render={({ field }) => ( - <FormItem> - <FormLabel>Header</FormLabel> - <FormControl> - <Input placeholder="Header" {...field} /> - </FormControl> - </FormItem> - )} - /> - <br></br> - <FormField - control={form.control} - name='description' - render={({ field }) => ( - <FormItem> - <FormLabel>Description</FormLabel> - <FormControl> - <textarea className={"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"} placeholder="Description" {...field} /> - </FormControl> - </FormItem> - )} - /> - <br></br> - <FormField - control={form.control} - name='start_date' - render={({ field }) => ( - <FormItem> - <FormLabel>Start Date</FormLabel> - <br></br> - <FormControl> - <DatePicker dates={startDate} setDate={setStarDate} allowedDate={new Date()} {...field}/> - </FormControl> - </FormItem> - )} - /> - <br></br> - <FormField - control={form.control} - name='end_date' - render={({ field }) => ( - <FormItem> - <FormLabel>Start Date</FormLabel> - <br></br> - <FormControl> - <DatePicker dates={endDate} setDate={setEndDate} allowedDate={new Date("3000 01 01")} {...field}/> - </FormControl> - </FormItem> - )} - /> - <FormField - control={form.control} - name='process' - render={({ field }) => ( - <FormItem> - <FormLabel>Process</FormLabel> - <Select onValueChange={field.onChange} > - <FormControl> - <SelectTrigger> - <SelectValue placeholder="Process" /> - </SelectTrigger> - </FormControl> - <SelectContent> - { processData.process.map((name, index) => { - if (index == -1) return (<></>) - return ( - name.name !== "" ? ( - <SelectItem key={index} value={name.name}>{name.name}</SelectItem> - ):null - ) - })} - </SelectContent> - </Select> - </FormItem> - )} - /> - <br></br> - <Button type='submit'>Done</Button> - </form> - </Form> - {/* - <form onSubmit={(e) => { addEnoek(e,title,description,startDate,endDate,process); setTitle(""); setStarDate(undefined); setEndDate(undefined); setDescription(""); setProcess("");}}> - - <input type="text" id="uname" value={title} onChange={(event) => setTitle(event.target.value)} placeholder="Title" required/> - <select name="m_buldings" id="m_building" onChange={(e) => setProcess(e.target.value)} value={process}> - {processData.process.map((name, index) => ( - name.name !== "" ? ( - <option key={index} value={name.name}> - {name.name} - </option> - ) : null - ))} - </select> - <label> - Date From: - <DatePicker dates={startDate} setDate={setStarDate} allowedDate={new Date()}/> - </label> <br/> <br/> - <label> - Date To: - <DatePicker dates={endDate} setDate={setEndDate} allowedDate={new Date("3000 01 01")} /> - </label> <br/> <br/> - <textarea id="description" value={description} onChange={(event) => setDescription(event.target.value)} placeholder="Description" required/> - <br/><br/> - <Button type='submit' onClick={() => { - setData(prevState => ({ - ...prevState, - enoek: [ - ...prevState.enoek, - { - id: prevState.enoek.length > 0 ? prevState.enoek[prevState.enoek.length - 1].id + 1 : 1, - header : title, - description : description, - author : "YOU", - start_date : startDate, - end_date : endDate, - active : false, - process : process, - approved : null, - } - ] - })); - - }}>Submit Enoek!</Button> - </form> - */} - </div></> + {/*Error message popup */} + {/*--------------------------*/} + <AlertDialog open={errorOpen}> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle>Something went wrong</AlertDialogTitle> + </AlertDialogHeader> + <div> + Please try again or check your connection + </div> + <AlertDialogFooter> + <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> + {/*--------------------------*/} + <div> + {/*Form get the data that should be added */} + <Form {...form}> + <form onSubmit={form.handleSubmit(addEnoek)}> + <FormField + control={form.control} + name='header' + render={({ field }) => ( + <FormItem> + <FormLabel>Header</FormLabel> + <FormControl> + <Input placeholder="Header" {...field} /> + </FormControl> + </FormItem> + )} + /> + <br></br> + <FormField + control={form.control} + name='description' + render={({ field }) => ( + <FormItem> + <FormLabel>Description</FormLabel> + <FormControl> + <textarea className={"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"} placeholder="Description" {...field} /> + </FormControl> + </FormItem> + )} + /> + <br></br> + <FormField + control={form.control} + name='start_date' + render={({ field }) => ( + <FormItem> + <FormLabel>Start Date</FormLabel> + <br></br> + <FormControl> + <DatePicker dates={startDate} setDate={setStarDate} allowedDate={new Date()} {...field} /> + </FormControl> + </FormItem> + )} + /> + <br></br> + <FormField + control={form.control} + name='end_date' + render={({ field }) => ( + <FormItem> + <FormLabel>Start Date</FormLabel> + <br></br> + <FormControl> + <DatePicker dates={endDate} setDate={setEndDate} allowedDate={new Date("3000 01 01")} {...field} /> + </FormControl> + </FormItem> + )} + /> + <FormField + control={form.control} + name='process' + render={({ field }) => ( + <FormItem> + <FormLabel>Process</FormLabel> + <Select onValueChange={field.onChange} > + <FormControl> + <SelectTrigger> + <SelectValue placeholder="Process" /> + </SelectTrigger> + </FormControl> + <SelectContent> + {processData.process.map((name, index) => { + if (index == -1) return (<></>) + return ( + name.name !== "" ? ( + <SelectItem key={index} value={name.name}>{name.name}</SelectItem> + ) : null + ) + })} + </SelectContent> + </Select> + </FormItem> + )} + /> + <br></br> + <Button type='submit'>Done</Button> + </form> + </Form> + </div></> ); }; - - export default MainComponent \ No newline at end of file + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekAktiv.tsx b/Frontend/power-tracker/src/components/enoekAktiv.tsx index 97669b1c7c286774ad2e8c5dfcfd2b378d967a65..745183eeb6675cd2c77e8220dbcd4bf0d38e82f1 100644 --- a/Frontend/power-tracker/src/components/enoekAktiv.tsx +++ b/Frontend/power-tracker/src/components/enoekAktiv.tsx @@ -1,28 +1,7 @@ -import { Button } from "@/components/ui/button"; -import { DatePicker } from '@/components/ui/datepicker' import React from "react"; import { useState } from "react"; -import axios from 'axios'; -import { redirect } from 'react-router-dom'; -import { date } from "zod"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Table, - TableBody, TableCaption, TableCell, TableHead, @@ -30,28 +9,9 @@ import { TableRow, SortableColumnHeader, } from "@/components/ui/table" -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" import { ScrollArea } from "@/components/ui/scroll-area" +//Type for the enoek data type EnokDataItem = { id: number; header: string; @@ -62,35 +22,36 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { +//the type of data the Maincomponent resives +interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) =>{ - +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { + //use state to keep track of which table column is sorted const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); + //use state to keep track of which sort direction is used const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); const handleClick = (column: string) => { if (currentSortedColumn === column) { - // Toggle sort direction if clicking on the same column - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + // Toggle sort direction if clicking on the same column + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { - // Set new sorted column and reset sort direction - setCurrentSortedColumn(column); - setSortDirection('asc'); + // Set new sorted column and reset sort direction + setCurrentSortedColumn(column); + setSortDirection('asc'); } }; return ( <> - <ScrollArea> + <ScrollArea> <Table> <TableCaption> (づ ◕‿◕ )づ @@ -98,106 +59,108 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = <br /> </TableCaption> <TableHeader className=""> - <TableRow> - <SortableColumnHeader - column="description" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Description - </SortableColumnHeader> - <SortableColumnHeader - column="author" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Author - </SortableColumnHeader> - <SortableColumnHeader - column="startDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Start Date - </SortableColumnHeader> - <SortableColumnHeader - column="endDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - End Date - </SortableColumnHeader> - <SortableColumnHeader - column="process" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Process - </SortableColumnHeader> - <TableHead></TableHead> - </TableRow> - </TableHeader> - {enoek.sort((a,b)=>{ - // Ensure currentSortedColumn is defined and not null - if (!currentSortedColumn) { - return 0; - } - var valueA; - var valueB; - - switch (currentSortedColumn) { - case "description": - valueA = a.header.toLowerCase(); - valueB = b.header.toLowerCase(); - break; - case "author": - valueA = a.author.toLowerCase(); - valueB = b.author.toLowerCase(); - break; - case "startDate": - valueA = a.start_date; - valueB = b.start_date; - break; - case "endDate": - valueA = a.end_date; - valueB = b.end_date; - break; - case "process": - valueA = a.process; - valueB = b.process; - break; - default: - return 0; // No sorting if currentSortedColumn is not recognized - } - // Compare the values - if (valueA === undefined || valueB === undefined) { - return 0; // No sorting if valueA or valueB is undefined - } else { - // Use ternary operator to decide the comparison direction based on sortDirection - const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - return sortDirection === "asc" ? comparison : -comparison; - } - }).map((data, index) => ( - data.start_date != undefined && data.end_date != undefined&& new Date()>=new Date(data.start_date) && new Date()<=new Date(data.end_date)&& data.approved && (data.header.toLowerCase().includes(search.toLowerCase())||data.author.toLowerCase().includes(search.toLowerCase()))? - <TableRow key={index}> - <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> - <TableCell>{data.author}</TableCell> - <TableCell>{new Date(data.start_date).toDateString()}</TableCell> - <TableCell>{new Date(data.end_date).toDateString()}</TableCell> - <TableCell>{data.process}</TableCell> - </TableRow> - : null - ))} - </Table> - </ScrollArea> - </> + {/*Table column names */} + <TableRow> + <SortableColumnHeader + column="description" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Description + </SortableColumnHeader> + <SortableColumnHeader + column="author" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Author + </SortableColumnHeader> + <SortableColumnHeader + column="startDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Start Date + </SortableColumnHeader> + <SortableColumnHeader + column="endDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + End Date + </SortableColumnHeader> + <SortableColumnHeader + column="process" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Process + </SortableColumnHeader> + <TableHead></TableHead> + </TableRow> + </TableHeader> + {/*Sort the data being show based upon the sorting direction and the column that is suppose to be sorted */} + {enoek.sort((a, b) => { + if (!currentSortedColumn) { + return 0; + } + var valueA; + var valueB; + //desides the values based on which column being sorted + switch (currentSortedColumn) { + case "description": + valueA = a.header.toLowerCase(); + valueB = b.header.toLowerCase(); + break; + case "author": + valueA = a.author.toLowerCase(); + valueB = b.author.toLowerCase(); + break; + case "startDate": + valueA = a.start_date; + valueB = b.start_date; + break; + case "endDate": + valueA = a.end_date; + valueB = b.end_date; + break; + case "process": + valueA = a.process; + valueB = b.process; + break; + default: + return 0; // No sorting if currentSortedColumn is not recognized + } + // Compare the values + if (valueA === undefined || valueB === undefined) { + return 0; // No sorting if valueA or valueB is undefined + } else { + //decide the comparison direction based on sortDirection + const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + return sortDirection === "asc" ? comparison : -comparison; + } + }).map((data, index) => ( + //if values isnt undefined/null, if the current date is between start and end date of the enoek suggestion, if its approved and it includes what the user search for, display that part of data + data.start_date != undefined && data.end_date != undefined && new Date() >= new Date(data.start_date) && new Date() <= new Date(data.end_date) && data.approved && (data.header.toLowerCase().includes(search.toLowerCase()) || data.author.toLowerCase().includes(search.toLowerCase())) ? + <TableRow key={index}> + <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> + <TableCell>{data.author}</TableCell> + <TableCell>{new Date(data.start_date).toDateString()}</TableCell> + <TableCell>{new Date(data.end_date).toDateString()}</TableCell> + <TableCell>{data.process}</TableCell> + </TableRow> + : null + ))} + </Table> + </ScrollArea> + </> ); }; - - - export default MainComponent \ No newline at end of file + + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekAllMeasures.tsx b/Frontend/power-tracker/src/components/enoekAllMeasures.tsx index f46eebaa5ad8f0d83d86f5a600de7d01b609ebce..fe90c5e5198a4fc75353e3e316d16345bcc02905 100644 --- a/Frontend/power-tracker/src/components/enoekAllMeasures.tsx +++ b/Frontend/power-tracker/src/components/enoekAllMeasures.tsx @@ -1,29 +1,10 @@ - import { Button } from "@/components/ui/button"; -import { DatePicker } from '@/components/ui/datepicker' import React from "react"; import { useState } from "react"; import axios from 'axios'; import { redirect } from 'react-router-dom'; -import { date } from "zod"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Table, - TableBody, TableCaption, TableCell, TableHead, @@ -42,20 +23,13 @@ import { AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" import { ScrollArea } from "@/components/ui/scroll-area" +//make a const that has the IngressAPI ip const IngressAPI = import.meta.env.VITE_IAPI_URL +//Type for the enoek data type EnokDataItem = { id: number; header: string; @@ -66,32 +40,34 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { +//the type of data the Maincomponent resives +interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) =>{ +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { - const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); - const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); + //use state to keep track of which table column is sorted + const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); + //use state to keep track of which sort direction is used + const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); + + const handleClick = (column: string) => { + if (currentSortedColumn === column) { + // Toggle sort direction if clicking on the same column + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + } else { + // Set new sorted column and reset sort direction + setCurrentSortedColumn(column); + setSortDirection('asc'); + } + }; + const deleteEnoek = (id: number) => { - const handleClick = (column: string) => { - if (currentSortedColumn === column) { - // Toggle sort direction if clicking on the same column - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); - } else { - // Set new sorted column and reset sort direction - setCurrentSortedColumn(column); - setSortDirection('asc'); - } - }; - const deleteEnoek = ( id: number) => { - var token: string = "" var tokenBool = sessionStorage.getItem("TOKEN") if (tokenBool == null) { @@ -102,44 +78,49 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = console.log('URL To call: ' + IngressAPI + 'token used' + token) axios.delete( IngressAPI + '/new-enoek', - { - data:{ - id: id, - sessionToken: token + { + data: { + id: id, + sessionToken: token } }) - .then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) - } - const permission = sessionStorage.getItem('PERMISSION'); - - const [open, setOpen] = useState(false); + .then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setErrorOpen(true) + }) + } + //gets the permission level of the user + const permission = sessionStorage.getItem('PERMISSION'); + //Usestate to store if the error popup should be shown or not + const [errorOpen, setErrorOpen] = useState(false); + //handles the closing of the error popup const handleClose = () => { - setOpen(false); + setErrorOpen(false); }; return ( <> - <ScrollArea> - <AlertDialog open={open}> - <AlertDialogContent> - <AlertDialogHeader> - <AlertDialogTitle>Something went wrong</AlertDialogTitle> - </AlertDialogHeader> - <div> - Please try again or check your connection - </div> - <AlertDialogFooter> - <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> - </AlertDialogFooter> - </AlertDialogContent> - </AlertDialog> + <ScrollArea> + {/*Error message popup */} + {/*--------------------------*/} + <AlertDialog open={errorOpen}> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle>Something went wrong</AlertDialogTitle> + </AlertDialogHeader> + <div> + Please try again or check your connection + </div> + <AlertDialogFooter> + <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> + {/*--------------------------*/} <Table> <TableCaption> (づ ◕‿◕ )づ @@ -147,121 +128,124 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = <br /> </TableCaption> <TableHeader className=""> - <TableRow> - <SortableColumnHeader - column="description" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Description - </SortableColumnHeader> - <SortableColumnHeader - column="author" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Author - </SortableColumnHeader> - <SortableColumnHeader - column="startDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Start Date - </SortableColumnHeader> - <SortableColumnHeader - column="endDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - End Date - </SortableColumnHeader> - <SortableColumnHeader - column="process" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Process - </SortableColumnHeader> - <TableHead></TableHead> - </TableRow> - </TableHeader> - {enoek.sort((a,b)=>{ - // Ensure currentSortedColumn is defined and not null - if (!currentSortedColumn) { - return 0; - } - var valueA; - var valueB; + {/*Table column names */} + <TableRow> + <SortableColumnHeader + column="description" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Description + </SortableColumnHeader> + <SortableColumnHeader + column="author" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Author + </SortableColumnHeader> + <SortableColumnHeader + column="startDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Start Date + </SortableColumnHeader> + <SortableColumnHeader + column="endDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + End Date + </SortableColumnHeader> + <SortableColumnHeader + column="process" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Process + </SortableColumnHeader> + <TableHead></TableHead> + </TableRow> + </TableHeader> + {/*Sort the data being show based upon the sorting direction and the column that is suppose to be sorted */} + {enoek.sort((a, b) => { + if (!currentSortedColumn) { + return 0; + } + var valueA; + var valueB; + //desides the values based on which column being sorted + switch (currentSortedColumn) { + case "description": + valueA = a.header.toLowerCase(); + valueB = b.header.toLowerCase(); + break; + case "author": + valueA = a.author.toLowerCase(); + valueB = b.author.toLowerCase(); + break; + case "startDate": + valueA = a.start_date; + valueB = b.start_date; + break; + case "endDate": + valueA = a.end_date; + valueB = b.end_date; + break; + case "process": + valueA = a.process; + valueB = b.process; + break; + default: + return 0; // No sorting if currentSortedColumn is not recognized + } + // Compare the values + if (valueA === undefined || valueB === undefined) { + return 0; // No sorting if valueA or valueB is undefined + } else { + //decide the comparison direction based on sortDirection + const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + return sortDirection === "asc" ? comparison : -comparison; + } + }).map((data, index) => ( + //if values isnt undefined/null and it includes what the user search for, display that part of data + data.start_date != undefined && data.end_date != undefined && (data.header.toLowerCase().includes(search.toLowerCase()) || data.author.toLowerCase().includes(search.toLowerCase())) ? + <TableRow key={index}> + <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> + <TableCell>{data.author}</TableCell> + <TableCell>{new Date(data.start_date).toDateString()}</TableCell> + <TableCell>{new Date(data.end_date).toDateString()}</TableCell> + <TableCell>{data.process}</TableCell> - switch (currentSortedColumn) { - case "description": - valueA = a.header.toLowerCase(); - valueB = b.header.toLowerCase(); - break; - case "author": - valueA = a.author.toLowerCase(); - valueB = b.author.toLowerCase(); - break; - case "startDate": - valueA = a.start_date; - valueB = b.start_date; - break; - case "endDate": - valueA = a.end_date; - valueB = b.end_date; - break; - case "process": - valueA = a.process; - valueB = b.process; - break; - default: - return 0; // No sorting if currentSortedColumn is not recognized - } - // Compare the values - if (valueA === undefined || valueB === undefined) { - return 0; // No sorting if valueA or valueB is undefined - } else { - // Use ternary operator to decide the comparison direction based on sortDirection - const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - return sortDirection === "asc" ? comparison : -comparison; - } - }).map((data, index) => ( - data.start_date != undefined && data.end_date != undefined && (data.header.toLowerCase().includes(search.toLowerCase())||data.author.toLowerCase().includes(search.toLowerCase()))? - <TableRow key={index}> - <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> - <TableCell>{data.author}</TableCell> - <TableCell>{new Date(data.start_date).toDateString()}</TableCell> - <TableCell>{new Date(data.end_date).toDateString()}</TableCell> - <TableCell>{data.process}</TableCell> - - {(permission && parseInt(permission, 10) <= 1) && ( - <> - <br></br> - <AlertDialog> - <AlertDialogTrigger asChild> - <Button size="sm" variant="outline">Delete</Button> - </AlertDialogTrigger> - <AlertDialogContent> - <AlertDialogTitle>Delete enoek suggestion?</AlertDialogTitle> - <AlertDialogDescription>Do you want to delete {data.author}'s post about {data.header}?</AlertDialogDescription> - <AlertDialogAction onClick={() => {deleteEnoek(data.id); }}>DELETE</AlertDialogAction> - <AlertDialogCancel >Cancel</AlertDialogCancel> - </AlertDialogContent> - </AlertDialog></> - )} - </TableRow> - : null - ))} - </Table> - </ScrollArea> - </> + {(permission && parseInt(permission, 10) <= 1) && ( + <> + <br></br> + {/*Delete button to delete that enoek suggestion */} + <AlertDialog> + <AlertDialogTrigger asChild> + <Button size="sm" variant="outline">Delete</Button> + </AlertDialogTrigger> + <AlertDialogContent> + <AlertDialogTitle>Delete enoek suggestion?</AlertDialogTitle> + <AlertDialogDescription>Do you want to delete {data.author}'s post about {data.header}?</AlertDialogDescription> + <AlertDialogAction onClick={() => { deleteEnoek(data.id); }}>DELETE</AlertDialogAction> + <AlertDialogCancel >Cancel</AlertDialogCancel> + </AlertDialogContent> + </AlertDialog></> + )} + </TableRow> + : null + ))} + </Table> + </ScrollArea> + </> ); }; - - export default MainComponent \ No newline at end of file + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekApprovedMeasures.tsx b/Frontend/power-tracker/src/components/enoekApprovedMeasures.tsx index 70a6db4f3b315411f9dccfa04aea2af441baf097..0de914f946a47e2b07843cc6b9567ef9b1554aab 100644 --- a/Frontend/power-tracker/src/components/enoekApprovedMeasures.tsx +++ b/Frontend/power-tracker/src/components/enoekApprovedMeasures.tsx @@ -1,29 +1,8 @@ -import { Button } from "@/components/ui/button"; -import { DatePicker } from '@/components/ui/datepicker' import React from "react"; import { useState } from "react"; -import axios from 'axios'; -import { redirect } from 'react-router-dom'; -import { date } from "zod"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Table, - TableBody, TableCaption, TableCell, TableHead, @@ -31,28 +10,9 @@ import { TableRow, SortableColumnHeader, } from "@/components/ui/table" -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" import { ScrollArea } from "@/components/ui/scroll-area" +//Type for the enoek data type EnokDataItem = { id: number; header: string; @@ -63,35 +23,36 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { +//the type of data the Maincomponent resives +interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) =>{ - +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { + //use state to keep track of which table column is sorted const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); + //use state to keep track of which sort direction is used const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); const handleClick = (column: string) => { if (currentSortedColumn === column) { - // Toggle sort direction if clicking on the same column - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + // Toggle sort direction if clicking on the same column + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { - // Set new sorted column and reset sort direction - setCurrentSortedColumn(column); - setSortDirection('asc'); + // Set new sorted column and reset sort direction + setCurrentSortedColumn(column); + setSortDirection('asc'); } }; return ( <> - <ScrollArea> + <ScrollArea> <Table> <TableCaption> (づ ◕‿◕ )づ @@ -99,106 +60,108 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = <br /> </TableCaption> <TableHeader className=""> - <TableRow> - <SortableColumnHeader - column="description" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Description - </SortableColumnHeader> - <SortableColumnHeader - column="author" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Author - </SortableColumnHeader> - <SortableColumnHeader - column="startDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Start Date - </SortableColumnHeader> - <SortableColumnHeader - column="endDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - End Date - </SortableColumnHeader> - <SortableColumnHeader - column="process" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Process - </SortableColumnHeader> - <TableHead></TableHead> - </TableRow> - </TableHeader> - {enoek.sort((a,b)=>{ - // Ensure currentSortedColumn is defined and not null - if (!currentSortedColumn) { - return 0; - } - var valueA; - var valueB; - - switch (currentSortedColumn) { - case "description": - valueA = a.header.toLowerCase(); - valueB = b.header.toLowerCase(); - break; - case "author": - valueA = a.author.toLowerCase(); - valueB = b.author.toLowerCase(); - break; - case "startDate": - valueA = a.start_date; - valueB = b.start_date; - break; - case "endDate": - valueA = a.end_date; - valueB = b.end_date; - break; - case "process": - valueA = a.process; - valueB = b.process; - break; - default: - return 0; // No sorting if currentSortedColumn is not recognized - } - // Compare the values - if (valueA === undefined || valueB === undefined) { - return 0; // No sorting if valueA or valueB is undefined - } else { - // Use ternary operator to decide the comparison direction based on sortDirection - const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - return sortDirection === "asc" ? comparison : -comparison; - } - }).map((data, index) => ( - data.start_date != undefined && data.end_date != undefined && data.approved&& (data.header.toLowerCase().includes(search.toLowerCase())||data.author.toLowerCase().includes(search.toLowerCase()))? - <TableRow key={index}> - <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> - <TableCell>{data.author}</TableCell> - <TableCell>{new Date(data.start_date).toDateString()}</TableCell> - <TableCell>{new Date(data.end_date).toDateString()}</TableCell> - <TableCell>{data.process}</TableCell> - </TableRow> - : null - ))} - </Table> - </ScrollArea> - </> + {/*Table column names */} + <TableRow> + <SortableColumnHeader + column="description" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Description + </SortableColumnHeader> + <SortableColumnHeader + column="author" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Author + </SortableColumnHeader> + <SortableColumnHeader + column="startDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Start Date + </SortableColumnHeader> + <SortableColumnHeader + column="endDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + End Date + </SortableColumnHeader> + <SortableColumnHeader + column="process" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Process + </SortableColumnHeader> + <TableHead></TableHead> + </TableRow> + </TableHeader> + {/*Sort the data being show based upon the sorting direction and the column that is suppose to be sorted */} + {enoek.sort((a, b) => { + if (!currentSortedColumn) { + return 0; + } + var valueA; + var valueB; + //desides the values based on which column being sorted + switch (currentSortedColumn) { + case "description": + valueA = a.header.toLowerCase(); + valueB = b.header.toLowerCase(); + break; + case "author": + valueA = a.author.toLowerCase(); + valueB = b.author.toLowerCase(); + break; + case "startDate": + valueA = a.start_date; + valueB = b.start_date; + break; + case "endDate": + valueA = a.end_date; + valueB = b.end_date; + break; + case "process": + valueA = a.process; + valueB = b.process; + break; + default: + return 0; // No sorting if currentSortedColumn is not recognized + } + // Compare the values + if (valueA === undefined || valueB === undefined) { + return 0; // No sorting if valueA or valueB is undefined + } else { + //decide the comparison direction based on sortDirection + const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + return sortDirection === "asc" ? comparison : -comparison; + } + }).map((data, index) => ( + //if values isnt undefined/null, the enoek suggetion is approved or true and it includes what the user search for, display that part of data + data.start_date != undefined && data.end_date != undefined && data.approved && (data.header.toLowerCase().includes(search.toLowerCase()) || data.author.toLowerCase().includes(search.toLowerCase())) ? + <TableRow key={index}> + <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> + <TableCell>{data.author}</TableCell> + <TableCell>{new Date(data.start_date).toDateString()}</TableCell> + <TableCell>{new Date(data.end_date).toDateString()}</TableCell> + <TableCell>{data.process}</TableCell> + </TableRow> + : null + ))} + </Table> + </ScrollArea> + </> ); }; - - export default MainComponent \ No newline at end of file + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekDecision.tsx b/Frontend/power-tracker/src/components/enoekDecision.tsx index fcabd504d1e1def1de17970118bd762a9afc4d42..0630c0d9fcb70b646c5260d16b7bb87249437d8d 100644 --- a/Frontend/power-tracker/src/components/enoekDecision.tsx +++ b/Frontend/power-tracker/src/components/enoekDecision.tsx @@ -1,28 +1,10 @@ import { Button } from "@/components/ui/button"; -import { DatePicker } from '@/components/ui/datepicker' import React from "react"; import { useState } from "react"; import axios from 'axios'; import { redirect } from 'react-router-dom'; -import { date } from "zod"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Table, - TableBody, TableCaption, TableCell, TableHead, @@ -32,27 +14,18 @@ import { } from "@/components/ui/table" import { AlertDialog, - AlertDialogAction, AlertDialogCancel, AlertDialogContent, - AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, - AlertDialogTrigger, } from "@/components/ui/alert-dialog" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" import { ScrollArea } from "@/components/ui/scroll-area" +//make a const that has the IngressAPI ip const IngressAPI = import.meta.env.VITE_IAPI_URL + +//Type for the enoek data type EnokDataItem = { id: number; header: string; @@ -63,44 +36,42 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { - enoek: EnokDataItem[]; - setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; - search: string; -} +//the type of data the Maincomponent resives interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) =>{ - - const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); - const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); - - const handleClick = (column: string) => { - if (currentSortedColumn === column) { - // Toggle sort direction if clicking on the same column - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); - } else { - // Set new sorted column and reset sort direction - setCurrentSortedColumn(column); - setSortDirection('asc'); - } - }; - const [open, setOpen] = useState(false); +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { + //use state to keep track of which table column is sorted + const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); + //use state to keep track of which sort direction is used + const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); + + const handleClick = (column: string) => { + if (currentSortedColumn === column) { + // Toggle sort direction if clicking on the same column + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + } else { + // Set new sorted column and reset sort direction + setCurrentSortedColumn(column); + setSortDirection('asc'); + } + }; + //Usestate to store if the error popup should be shown or not + const [errorOpen, setErrorOpen] = useState(false); + //handles the closing of the error popup const handleClose = () => { - setOpen(false); + setErrorOpen(false); }; - - const judge = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number, bool: boolean) => { + //API call to either approve or reject an enoek suggestion + const judge = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number, bool: boolean) => { e.preventDefault() - + var token: string = "" var tokenBool = sessionStorage.getItem("TOKEN") if (tokenBool == null) { @@ -111,35 +82,38 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = console.log('URL To call: ' + IngressAPI + 'token used' + token) axios.put( IngressAPI + '/new-enoek', - { + { id: id, bool: bool, - sessionToken:token, + sessionToken: token, + }) + .then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setErrorOpen(true) }) - .then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) } return ( <> - <ScrollArea> - <AlertDialog open={open}> - <AlertDialogContent> - <AlertDialogHeader> - <AlertDialogTitle>Something went wrong</AlertDialogTitle> - </AlertDialogHeader> - <div> - Please try again or check your connection - </div> - <AlertDialogFooter> - <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> - </AlertDialogFooter> - </AlertDialogContent> - </AlertDialog> + <ScrollArea> + {/*Error message popup */} + {/*--------------------------*/} + <AlertDialog open={errorOpen}> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle>Something went wrong</AlertDialogTitle> + </AlertDialogHeader> + <div> + Please try again or check your connection + </div> + <AlertDialogFooter> + <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> + {/*--------------------------*/} <Table> <TableCaption> (づ ◕‿◕ )づ @@ -147,109 +121,111 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = <br /> </TableCaption> <TableHeader className=""> - <TableRow> - <SortableColumnHeader - column="description" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Description - </SortableColumnHeader> - <SortableColumnHeader - column="author" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Author - </SortableColumnHeader> - <SortableColumnHeader - column="startDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Start Date - </SortableColumnHeader> - <SortableColumnHeader - column="endDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - End Date - </SortableColumnHeader> - <SortableColumnHeader - column="process" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Process - </SortableColumnHeader> - <TableHead></TableHead> - </TableRow> - </TableHeader> - {enoek.sort((a,b)=>{ - // Ensure currentSortedColumn is defined and not null - if (!currentSortedColumn) { - return 0; - } - var valueA; - var valueB; - - switch (currentSortedColumn) { - case "description": - valueA = a.header.toLowerCase(); - valueB = b.header.toLowerCase(); - break; - case "author": - valueA = a.author.toLowerCase(); - valueB = b.author.toLowerCase(); - break; - case "startDate": - valueA = a.start_date; - valueB = b.start_date; - break; - case "endDate": - valueA = a.end_date; - valueB = b.end_date; - break; - case "process": - valueA = a.process; - valueB = b.process; - break; - default: - return 0; // No sorting if currentSortedColumn is not recognized - } - // Compare the values - if (valueA === undefined || valueB === undefined) { - return 0; // No sorting if valueA or valueB is undefined - } else { - // Use ternary operator to decide the comparison direction based on sortDirection - const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - return sortDirection === "asc" ? comparison : -comparison; - } - }).map((data, index) => ( - data.start_date != undefined && data.end_date != undefined && data.approved==null&& (data.header.toLowerCase().includes(search.toLowerCase())||data.author.toLowerCase().includes(search.toLowerCase()))? - <TableRow key={index}> - <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> - <TableCell>{data.author}</TableCell> - <TableCell>{new Date(data.start_date).toDateString()}</TableCell> - <TableCell>{new Date(data.end_date).toDateString()}</TableCell> - <TableCell>{data.process}</TableCell> - <br></br> - <Button size="sm" variant="outline" onClick={(e)=>{judge(e,data.id,true)}}>approve</Button> - <Button size="sm" variant="outline" onClick={(e)=>{judge(e,data.id,false)}}>reject</Button> - </TableRow> - : null - ))} - </Table> - </ScrollArea> - </> + {/*Table column names */} + <TableRow> + <SortableColumnHeader + column="description" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Description + </SortableColumnHeader> + <SortableColumnHeader + column="author" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Author + </SortableColumnHeader> + <SortableColumnHeader + column="startDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Start Date + </SortableColumnHeader> + <SortableColumnHeader + column="endDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + End Date + </SortableColumnHeader> + <SortableColumnHeader + column="process" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Process + </SortableColumnHeader> + <TableHead></TableHead> + </TableRow> + </TableHeader> + {/*Sort the data being show based upon the sorting direction and the column that is suppose to be sorted */} + {enoek.sort((a, b) => { + if (!currentSortedColumn) { + return 0; + } + var valueA; + var valueB; + //desides the values based on which column being sorted + switch (currentSortedColumn) { + case "description": + valueA = a.header.toLowerCase(); + valueB = b.header.toLowerCase(); + break; + case "author": + valueA = a.author.toLowerCase(); + valueB = b.author.toLowerCase(); + break; + case "startDate": + valueA = a.start_date; + valueB = b.start_date; + break; + case "endDate": + valueA = a.end_date; + valueB = b.end_date; + break; + case "process": + valueA = a.process; + valueB = b.process; + break; + default: + return 0; // No sorting if currentSortedColumn is not recognized + } + // Compare the values + if (valueA === undefined || valueB === undefined) { + return 0; // No sorting if valueA or valueB is undefined + } else { + //decide the comparison direction based on sortDirection + const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + return sortDirection === "asc" ? comparison : -comparison; + } + }).map((data, index) => ( + //if values isnt undefined/null, the enoek suggetion is null meaning it hasnt been approved or dissaproved yet and it includes what the user search for, display that part of data + data.start_date != undefined && data.end_date != undefined && data.approved == null && (data.header.toLowerCase().includes(search.toLowerCase()) || data.author.toLowerCase().includes(search.toLowerCase())) ? + <TableRow key={index}> + <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> + <TableCell>{data.author}</TableCell> + <TableCell>{new Date(data.start_date).toDateString()}</TableCell> + <TableCell>{new Date(data.end_date).toDateString()}</TableCell> + <TableCell>{data.process}</TableCell> + <br></br> + <Button size="sm" variant="outline" onClick={(e) => { judge(e, data.id, true) }}>approve</Button> + <Button size="sm" variant="outline" onClick={(e) => { judge(e, data.id, false) }}>reject</Button> + </TableRow> + : null + ))} + </Table> + </ScrollArea> + </> ); }; - - export default MainComponent \ No newline at end of file + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekMyMeasures.tsx b/Frontend/power-tracker/src/components/enoekMyMeasures.tsx index de449e033fa3f5013d929075c3a54d7afa08bac8..868b69116f47167e65534e3dd47c5002be731484 100644 --- a/Frontend/power-tracker/src/components/enoekMyMeasures.tsx +++ b/Frontend/power-tracker/src/components/enoekMyMeasures.tsx @@ -1,28 +1,7 @@ -import { Button } from "@/components/ui/button"; -import { DatePicker } from '@/components/ui/datepicker' import React from "react"; import { useState } from "react"; -import axios from 'axios'; -import { redirect } from 'react-router-dom'; -import { date } from "zod"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Table, - TableBody, TableCaption, TableCell, TableHead, @@ -30,28 +9,9 @@ import { TableRow, SortableColumnHeader, } from "@/components/ui/table" -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" import { ScrollArea } from "@/components/ui/scroll-area" +//Type for the enoek data type EnokDataItem = { id: number; header: string; @@ -62,35 +22,37 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { +//the type of data the Maincomponent resives +interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) =>{ +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { + //use state to keep track of which table column is sorted const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); + //use state to keep track of which sort direction is used const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); const handleClick = (column: string) => { if (currentSortedColumn === column) { - // Toggle sort direction if clicking on the same column - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + // Toggle sort direction if clicking on the same column + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { - // Set new sorted column and reset sort direction - setCurrentSortedColumn(column); - setSortDirection('asc'); + // Set new sorted column and reset sort direction + setCurrentSortedColumn(column); + setSortDirection('asc'); } }; return ( <> - <ScrollArea> + <ScrollArea> <Table> <TableCaption> (づ ◕‿◕ )づ @@ -98,107 +60,109 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = <br /> </TableCaption> <TableHeader className=""> - <TableRow> - <SortableColumnHeader - column="description" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Description - </SortableColumnHeader> - <SortableColumnHeader - column="author" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Author - </SortableColumnHeader> - <SortableColumnHeader - column="startDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Start Date - </SortableColumnHeader> - <SortableColumnHeader - column="endDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - End Date - </SortableColumnHeader> - <SortableColumnHeader - column="process" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Process - </SortableColumnHeader> - <TableHead></TableHead> - </TableRow> - </TableHeader> - {enoek.sort((a,b)=>{ - // Ensure currentSortedColumn is defined and not null - if (!currentSortedColumn) { - return 0; - } - var valueA; - var valueB; - - switch (currentSortedColumn) { - case "description": - valueA = a.header.toLowerCase(); - valueB = b.header.toLowerCase(); - break; - case "author": - valueA = a.author.toLowerCase(); - valueB = b.author.toLowerCase(); - break; - case "startDate": - valueA = a.start_date; - valueB = b.start_date; - break; - case "endDate": - valueA = a.end_date; - valueB = b.end_date; - break; - case "process": - valueA = a.process; - valueB = b.process; - break; - default: - return 0; // No sorting if currentSortedColumn is not recognized - } - // Compare the values - if (valueA === undefined || valueB === undefined) { - return 0; // No sorting if valueA or valueB is undefined - } else { - // Use ternary operator to decide the comparison direction based on sortDirection - const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - return sortDirection === "asc" ? comparison : -comparison; - } - }).map((data, index) => ( - data.start_date != undefined && data.end_date != undefined && (sessionStorage.getItem("FIRSTNAME") + " " + sessionStorage.getItem("LASTNAME"))==data.author&& (data.header.toLowerCase().includes(search.toLowerCase())||data.author.toLowerCase().includes(search.toLowerCase()))? - <TableRow key={index}> - <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> - <TableCell>{data.author}</TableCell> - <TableCell>{new Date(data.start_date).toDateString()}</TableCell> - <TableCell>{new Date(data.end_date).toDateString()}</TableCell> - <TableCell>{data.process}</TableCell> - </TableRow> - : null - ))} - </Table> - </ScrollArea> - </> + {/*Table column names */} + <TableRow> + <SortableColumnHeader + column="description" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Description + </SortableColumnHeader> + <SortableColumnHeader + column="author" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Author + </SortableColumnHeader> + <SortableColumnHeader + column="startDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Start Date + </SortableColumnHeader> + <SortableColumnHeader + column="endDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + End Date + </SortableColumnHeader> + <SortableColumnHeader + column="process" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Process + </SortableColumnHeader> + <TableHead></TableHead> + </TableRow> + </TableHeader> + {/*Sort the data being show based upon the sorting direction and the column that is suppose to be sorted */} + {enoek.sort((a, b) => { + if (!currentSortedColumn) { + return 0; + } + var valueA; + var valueB; + //desides the values based on which column being sorted + switch (currentSortedColumn) { + case "description": + valueA = a.header.toLowerCase(); + valueB = b.header.toLowerCase(); + break; + case "author": + valueA = a.author.toLowerCase(); + valueB = b.author.toLowerCase(); + break; + case "startDate": + valueA = a.start_date; + valueB = b.start_date; + break; + case "endDate": + valueA = a.end_date; + valueB = b.end_date; + break; + case "process": + valueA = a.process; + valueB = b.process; + break; + default: + return 0; // No sorting if currentSortedColumn is not recognized + } + // Compare the values + if (valueA === undefined || valueB === undefined) { + return 0; // No sorting if valueA or valueB is undefined + } else { + //decide the comparison direction based on sortDirection + const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + return sortDirection === "asc" ? comparison : -comparison; + } + }).map((data, index) => ( + //if values isnt undefined/null, the user is the author and it includes what the user search for, display that part of data + data.start_date != undefined && data.end_date != undefined && (sessionStorage.getItem("FIRSTNAME") + " " + sessionStorage.getItem("LASTNAME")) == data.author && (data.header.toLowerCase().includes(search.toLowerCase()) || data.author.toLowerCase().includes(search.toLowerCase())) ? + <TableRow key={index}> + <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> + <TableCell>{data.author}</TableCell> + <TableCell>{new Date(data.start_date).toDateString()}</TableCell> + <TableCell>{new Date(data.end_date).toDateString()}</TableCell> + <TableCell>{data.process}</TableCell> + </TableRow> + : null + ))} + </Table> + </ScrollArea> + </> ); }; - - - export default MainComponent \ No newline at end of file + + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/enoekRejectedMeasures.tsx b/Frontend/power-tracker/src/components/enoekRejectedMeasures.tsx index 4a00dc5470badf18fe26e33a9c5b76743c8f0f43..9db4594777bf6704774a26056490ec94494ef7ee 100644 --- a/Frontend/power-tracker/src/components/enoekRejectedMeasures.tsx +++ b/Frontend/power-tracker/src/components/enoekRejectedMeasures.tsx @@ -1,28 +1,7 @@ -import { Button } from "@/components/ui/button"; -import { DatePicker } from '@/components/ui/datepicker' import React from "react"; import { useState } from "react"; -import axios from 'axios'; -import { redirect } from 'react-router-dom'; -import { date } from "zod"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card" import { Table, - TableBody, TableCaption, TableCell, TableHead, @@ -30,28 +9,9 @@ import { TableRow, SortableColumnHeader, } from "@/components/ui/table" -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog" -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" import { ScrollArea } from "@/components/ui/scroll-area" +//Type for the enoek data type EnokDataItem = { id: number; header: string; @@ -62,35 +22,36 @@ type EnokDataItem = { active: boolean; process: string; approved: boolean | null; // Nullable boolean - }; - +}; - interface ManageAddingProps { +//the type of data the Maincomponent resives +interface ManageAddingProps { enoek: EnokDataItem[]; setData: React.Dispatch<React.SetStateAction<{ enoek: EnokDataItem[] }>>; search: string; } -const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) =>{ - +const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search }) => { + //use state to keep track of which table column is sorted const [currentSortedColumn, setCurrentSortedColumn] = useState<string | null>(null); + //use state to keep track of which sort direction is used const [sortDirection, setSortDirection] = useState<'asc' | 'desc' | null>(null); const handleClick = (column: string) => { if (currentSortedColumn === column) { - // Toggle sort direction if clicking on the same column - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + // Toggle sort direction if clicking on the same column + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { - // Set new sorted column and reset sort direction - setCurrentSortedColumn(column); - setSortDirection('asc'); + // Set new sorted column and reset sort direction + setCurrentSortedColumn(column); + setSortDirection('asc'); } }; return ( <> - <ScrollArea> + <ScrollArea> <Table> <TableCaption> (づ ◕‿◕ )づ @@ -98,105 +59,107 @@ const MainComponent: React.FC<ManageAddingProps> = ({ enoek, setData, search}) = <br /> </TableCaption> <TableHeader className=""> - <TableRow> - <SortableColumnHeader - column="description" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Description - </SortableColumnHeader> - <SortableColumnHeader - column="author" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Author - </SortableColumnHeader> - <SortableColumnHeader - column="startDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Start Date - </SortableColumnHeader> - <SortableColumnHeader - column="endDate" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - End Date - </SortableColumnHeader> - <SortableColumnHeader - column="process" - currentSortedColumn={currentSortedColumn} - sortDirection={sortDirection} - onClick={handleClick} - > - Process - </SortableColumnHeader> - <TableHead></TableHead> - </TableRow> - </TableHeader> - {enoek.sort((a,b)=>{ - // Ensure currentSortedColumn is defined and not null - if (!currentSortedColumn) { - return 0; - } - var valueA; - var valueB; - - switch (currentSortedColumn) { - case "description": - valueA = a.header.toLowerCase(); - valueB = b.header.toLowerCase(); - break; - case "author": - valueA = a.author.toLowerCase(); - valueB = b.author.toLowerCase(); - break; - case "startDate": - valueA = a.start_date; - valueB = b.start_date; - break; - case "endDate": - valueA = a.end_date; - valueB = b.end_date; - break; - case "process": - valueA = a.process; - valueB = b.process; - break; - default: - return 0; // No sorting if currentSortedColumn is not recognized - } - // Compare the values - if (valueA === undefined || valueB === undefined) { - return 0; // No sorting if valueA or valueB is undefined - } else { - // Use ternary operator to decide the comparison direction based on sortDirection - const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - return sortDirection === "asc" ? comparison : -comparison; - } - }).map((data, index) => ( - data.start_date != undefined && data.end_date != undefined && data.approved===false&& (data.header.toLowerCase().includes(search.toLowerCase())||data.author.toLowerCase().includes(search.toLowerCase()))? - <TableRow key={index}> - <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> - <TableCell>{data.author}</TableCell> - <TableCell>{new Date(data.start_date).toDateString()}</TableCell> - <TableCell>{new Date(data.end_date).toDateString()}</TableCell> - <TableCell>{data.process}</TableCell> - </TableRow> - : null - ))} - </Table> - </ScrollArea> - </> + {/*Table column names */} + <TableRow> + <SortableColumnHeader + column="description" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Description + </SortableColumnHeader> + <SortableColumnHeader + column="author" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Author + </SortableColumnHeader> + <SortableColumnHeader + column="startDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Start Date + </SortableColumnHeader> + <SortableColumnHeader + column="endDate" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + End Date + </SortableColumnHeader> + <SortableColumnHeader + column="process" + currentSortedColumn={currentSortedColumn} + sortDirection={sortDirection} + onClick={handleClick} + > + Process + </SortableColumnHeader> + <TableHead></TableHead> + </TableRow> + </TableHeader> + {/*Sort the data being show based upon the sorting direction and the column that is suppose to be sorted */} + {enoek.sort((a, b) => { + if (!currentSortedColumn) { + return 0; + } + var valueA; + var valueB; + //desides the values based on which column being sorted + switch (currentSortedColumn) { + case "description": + valueA = a.header.toLowerCase(); + valueB = b.header.toLowerCase(); + break; + case "author": + valueA = a.author.toLowerCase(); + valueB = b.author.toLowerCase(); + break; + case "startDate": + valueA = a.start_date; + valueB = b.start_date; + break; + case "endDate": + valueA = a.end_date; + valueB = b.end_date; + break; + case "process": + valueA = a.process; + valueB = b.process; + break; + default: + return 0; // No sorting if currentSortedColumn is not recognized + } + // Compare the values + if (valueA === undefined || valueB === undefined) { + return 0; // No sorting if valueA or valueB is undefined + } else { + //decide the comparison direction based on sortDirection + const comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + return sortDirection === "asc" ? comparison : -comparison; + } + }).map((data, index) => ( + //if values isnt undefined/null, the enoek suggetion is rejected or false and it includes what the user search for, display that part of data + data.start_date != undefined && data.end_date != undefined && data.approved === false && (data.header.toLowerCase().includes(search.toLowerCase()) || data.author.toLowerCase().includes(search.toLowerCase())) ? + <TableRow key={index}> + <TableCell><strong>{data.header}</strong> <br></br>{data.description}</TableCell> + <TableCell>{data.author}</TableCell> + <TableCell>{new Date(data.start_date).toDateString()}</TableCell> + <TableCell>{new Date(data.end_date).toDateString()}</TableCell> + <TableCell>{data.process}</TableCell> + </TableRow> + : null + ))} + </Table> + </ScrollArea> + </> ); }; - - export default MainComponent \ No newline at end of file + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/manageBuildDep.tsx b/Frontend/power-tracker/src/components/manageBuildDep.tsx index 35f0600c2af3fd532d2f073934866e896526da2b..959fbbb0fb255f490fe19e22b7533e3fe979fc8e 100644 --- a/Frontend/power-tracker/src/components/manageBuildDep.tsx +++ b/Frontend/power-tracker/src/components/manageBuildDep.tsx @@ -671,7 +671,7 @@ interface DisplayBuildingDepartmentDataProps extends ManagePopup { </div> ); -}; +} diff --git a/Frontend/power-tracker/src/components/manageGateway.tsx b/Frontend/power-tracker/src/components/manageGateway.tsx index b01fd0dfa23b2a745d4fb4392c5944bc7e8eb8cf..8074b64917afe4266ecda10fd367a679c775f770 100644 --- a/Frontend/power-tracker/src/components/manageGateway.tsx +++ b/Frontend/power-tracker/src/components/manageGateway.tsx @@ -288,6 +288,4 @@ async function fetchDataGateway(): Promise<Gateway[]> { } - - export default ManageGateways \ No newline at end of file diff --git a/Frontend/power-tracker/src/components/manageProcesses.tsx b/Frontend/power-tracker/src/components/manageProcesses.tsx index be50aa363a33d6f9411369cad0568db3a4923c38..91509a470e80b3afbb5339dbae153446d608b416 100644 --- a/Frontend/power-tracker/src/components/manageProcesses.tsx +++ b/Frontend/power-tracker/src/components/manageProcesses.tsx @@ -19,15 +19,15 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, } from "@/components/ui/accordion" const EgressAPI = import.meta.env.VITE_EAPI_URL const IngressAPI = import.meta.env.VITE_IAPI_URL -const ManageProcesses= () => { +const ManageProcesses = () => { // Initialize the state with the defined type const [processData, setProcessData] = useState({ process: [{ @@ -68,7 +68,7 @@ const ManageProcesses= () => { machineName: "", }] }); - + const handleEdit = (index: number, value: string, value2: string, value3: boolean) => { setEditIndex(index); setText(value); @@ -93,13 +93,13 @@ const ManageProcesses= () => { // Assuming fetchDataProcess is an async function that fetches data useEffect(() => { - fetchDataProcess().then((data) => { - setProcessData({ - ...processData, - process: data.map((prcoesses) => ({id: prcoesses.id, name: prcoesses.name, description: prcoesses.description })) - }); + fetchDataProcess().then((data) => { + setProcessData({ + ...processData, + process: data.map((prcoesses) => ({ id: prcoesses.id, name: prcoesses.name, description: prcoesses.description })) }); - },[]); + }); + }, []); useEffect(() => { fetchDataProcessMachine(processDropDownId).then((data) => { @@ -125,92 +125,92 @@ const ManageProcesses= () => { const [open, setOpen] = useState(false); const handleClose = () => { - setOpen(false); + setOpen(false); }; - -interface Process { - process: { + + interface Process { + process: { id: number, name: string; description: string; - }[]; -} -type setProcess = React.Dispatch<React.SetStateAction<{ - process: { + }[]; + } + type setProcess = React.Dispatch<React.SetStateAction<{ + process: { id: number, name: string; description: string; - }[]; -}>> -interface ProcessDataForFunction { - data: Process - setData: setProcess -} - -interface ProcessMachine { - processMachine: { - added: boolean; - machineEUI: string; - machineName: string; - }[]; -} -interface ProcessMachineInsert { - processMachine: { - added: boolean; - machineEUI: string; - processId: number; - }[]; -} -interface typeProcessMachineInsert { - + }[]; + }>> + interface ProcessDataForFunction { + data: Process + setData: setProcess + } + + interface ProcessMachine { + processMachine: { + added: boolean; + machineEUI: string; + machineName: string; + }[]; + } + interface ProcessMachineInsert { + processMachine: { + added: boolean; + machineEUI: string; + processId: number; + }[]; + } + interface typeProcessMachineInsert { + added: boolean; eui: string; processId: number; -} -type setProcessMachine = React.Dispatch<React.SetStateAction<{ - processMachine: { - added: boolean; - machineEUI: string; - machineName: string; - }[]; -}>> -interface ProcessMachineDataForFunction { - data: ProcessMachine - setData: setProcessMachine - setData2: setProcessMachine - processId: number -} -interface ProcessMachineDataForFunction2 { - data: ProcessMachine - data2: ProcessMachine - processId: number -} - -const addProcess = (e: React.FormEvent<HTMLFormElement>, name: string, description: string) => { - e.preventDefault() - var token: string = "" - var tokenBool = sessionStorage.getItem("TOKEN") - if (tokenBool == null) { + } + type setProcessMachine = React.Dispatch<React.SetStateAction<{ + processMachine: { + added: boolean; + machineEUI: string; + machineName: string; + }[]; + }>> + interface ProcessMachineDataForFunction { + data: ProcessMachine + setData: setProcessMachine + setData2: setProcessMachine + processId: number + } + interface ProcessMachineDataForFunction2 { + data: ProcessMachine + data2: ProcessMachine + processId: number + } + + const addProcess = (e: React.FormEvent<HTMLFormElement>, name: string, description: string) => { + e.preventDefault() + var token: string = "" + var tokenBool = sessionStorage.getItem("TOKEN") + if (tokenBool == null) { redirect('/') - } else { + } else { token = tokenBool - } - axios.post( - IngressAPI + '/new-process', - { - process_name: name, - description: description, - sessionToken:token } - ).then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) -} + axios.post( + IngressAPI + '/new-process', + { + process_name: name, + description: description, + sessionToken: token + } + ).then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setOpen(true) + }) + } const handleSave2 = (data1: any, data2: any, processId: any) => { ProcessDataInsertion({ data: data1, data2: data2, processId: processId }); @@ -220,228 +220,228 @@ const addProcess = (e: React.FormEvent<HTMLFormElement>, name: string, descripti data2: processMachineData2, processId, }) => { - var realArray: ProcessMachineInsert[] = []; - var oldArray: ProcessMachineInsert[] = []; - - processMachineData.processMachine.forEach((item) => { - realArray.push({ - processMachine: [{ - added: item.added, - machineEUI: item.machineEUI, - processId: processId - }] + var realArray: ProcessMachineInsert[] = []; + var oldArray: ProcessMachineInsert[] = []; + + processMachineData.processMachine.forEach((item) => { + realArray.push({ + processMachine: [{ + added: item.added, + machineEUI: item.machineEUI, + processId: processId + }] + }); }); - }); - processMachineData2.processMachine.forEach((item) => { - oldArray.push({ - processMachine: [{ - added: item.added, - machineEUI: item.machineEUI, - processId: processId - }] + processMachineData2.processMachine.forEach((item) => { + oldArray.push({ + processMachine: [{ + added: item.added, + machineEUI: item.machineEUI, + processId: processId + }] + }); }); - }); - editProcessMachine(realArray, oldArray) - return null -}; + editProcessMachine(realArray, oldArray) + return null + }; -const editProcessMachine = ( realData: ProcessMachineInsert[], oldData: ProcessMachineInsert[]) => { - var token: string = "" - var tokenBool = sessionStorage.getItem("TOKEN") - if (tokenBool == null) { - redirect('/') - } else { - token = tokenBool - } - const requestData: typeProcessMachineInsert[] = []; - realData.forEach((item, index) => { - if (oldData[index].processMachine) { - item.processMachine.forEach((item2, index2) => { - if ( oldData[index].processMachine[index2].added !== item2.added) { - requestData.push({ - added: item2.added, - processId: item2.processId, - eui: item2.machineEUI - }); - } - }); + const editProcessMachine = (realData: ProcessMachineInsert[], oldData: ProcessMachineInsert[]) => { + var token: string = "" + var tokenBool = sessionStorage.getItem("TOKEN") + if (tokenBool == null) { + redirect('/') + } else { + token = tokenBool } - }); - axios.put( + const requestData: typeProcessMachineInsert[] = []; + realData.forEach((item, index) => { + if (oldData[index].processMachine) { + item.processMachine.forEach((item2, index2) => { + if (oldData[index].processMachine[index2].added !== item2.added) { + requestData.push({ + added: item2.added, + processId: item2.processId, + eui: item2.machineEUI + }); + } + }); + } + }); + axios.put( IngressAPI + '/machineProcess', - { - data: requestData, - sessionToken:token, + { + data: requestData, + sessionToken: token, } - ).then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) -} - -const editProcess = ( oldName: string, newName: string, oldDescription: string, newDescription: string) => { - var token: string = "" - var tokenBool = sessionStorage.getItem("TOKEN") - if (tokenBool == null) { - redirect('/') - } else { - token = tokenBool + ).then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setOpen(true) + }) } - axios.put( - IngressAPI + '/new-process', - { - old_process_name : oldName, - new_process_name : newName, - old_description : oldDescription, - new_description : newDescription, + + const editProcess = (oldName: string, newName: string, oldDescription: string, newDescription: string) => { + var token: string = "" + var tokenBool = sessionStorage.getItem("TOKEN") + if (tokenBool == null) { + redirect('/') + } else { + token = tokenBool + } + axios.put( + IngressAPI + '/new-process', + { + old_process_name: oldName, + new_process_name: newName, + old_description: oldDescription, + new_description: newDescription, sessionToken: token } - ).then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) -} -const deleteProcess = (name: string, description:string) => { - - - var token: string = "" - var tokenBool = sessionStorage.getItem("TOKEN") - if (tokenBool == null) { - redirect('/') - } else { - token = tokenBool + ).then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setOpen(true) + }) } - axios.delete( - IngressAPI + '/new-process', - { - data:{ + const deleteProcess = (name: string, description: string) => { + + + var token: string = "" + var tokenBool = sessionStorage.getItem("TOKEN") + if (tokenBool == null) { + redirect('/') + } else { + token = tokenBool + } + axios.delete( + IngressAPI + '/new-process', + { + data: { process_name: name, description: description, sessionToken: token - } + } } - ).then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) -} - - -class Processes { - id: number = 0; - name: string = ""; - description: string = ""; - constructor(id: number, name: string, description: string) { - this.id = id, this.name = name; this.description = description; + ).then((res) => { + console.log(res.data) + }).catch((error) => { + console.log(error) + setOpen(true) + }) } -} - -async function fetchDataProcess(): Promise<Processes[]> { - var processArrray: Processes[] = [] - await axios.post(EgressAPI + '/process', - { - sessionToken: sessionStorage.getItem('TOKEN') - }).then((res) => { - res.data.process.forEach((element: any) => { - processArrray.push({ - id: element.id, - name: element.name, - description: element.description - }) - }); - }).catch((error) => { - console.log(error) - setOpen(true) - }) - return processArrray -} - -class ProcessesMachine { - added: boolean = false; - machineEUI: string = ""; - machineName: string = ""; - constructor(added: boolean, machineEUI: string, machineName: string) { - this.added = added, this.machineEUI = machineEUI; this.machineName = machineName; + + + class Processes { + id: number = 0; + name: string = ""; + description: string = ""; + constructor(id: number, name: string, description: string) { + this.id = id, this.name = name; this.description = description; + } } -} - -async function fetchDataProcessMachine(processId: number): Promise<ProcessesMachine[]> { - const processArray: ProcessesMachine[] = []; - try { - const res = await axios.post(EgressAPI + '/processMachine', { - process: processId, - sessionToken: sessionStorage.getItem('TOKEN') - }); - const { added, machines } = res.data; - // Iterate over the added array - added.forEach((add: boolean, index: number) => { - // Check if machines[index] exists - if (machines[index]) { - processArray.push({ - added: add, - machineEUI: machines[index].eui, - machineName: machines[index].name, + + async function fetchDataProcess(): Promise<Processes[]> { + var processArrray: Processes[] = [] + await axios.post(EgressAPI + '/process', + { + sessionToken: sessionStorage.getItem('TOKEN') + }).then((res) => { + res.data.process.forEach((element: any) => { + processArrray.push({ + id: element.id, + name: element.name, + description: element.description + }) }); - } - }); - return processArray; - } catch (error) { - console.log(error); - return []; + }).catch((error) => { + console.log(error) + setOpen(true) + }) + return processArrray + } + + class ProcessesMachine { + added: boolean = false; + machineEUI: string = ""; + machineName: string = ""; + constructor(added: boolean, machineEUI: string, machineName: string) { + this.added = added, this.machineEUI = machineEUI; this.machineName = machineName; + } + } + + async function fetchDataProcessMachine(processId: number): Promise<ProcessesMachine[]> { + const processArray: ProcessesMachine[] = []; + try { + const res = await axios.post(EgressAPI + '/processMachine', { + process: processId, + sessionToken: sessionStorage.getItem('TOKEN') + }); + const { added, machines } = res.data; + // Iterate over the added array + added.forEach((add: boolean, index: number) => { + // Check if machines[index] exists + if (machines[index]) { + processArray.push({ + added: add, + machineEUI: machines[index].eui, + machineName: machines[index].name, + }); + } + }); + return processArray; + } catch (error) { + console.log(error); + return []; + } } -} return ( <div className="flex h-[100%]"> <AlertDialog open={open}> - <AlertDialogContent> - <AlertDialogHeader> - <AlertDialogTitle>Something went wrong</AlertDialogTitle> - </AlertDialogHeader> - <div> - Please try again or check your connection - </div> - <AlertDialogFooter> - <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> - </AlertDialogFooter> - </AlertDialogContent> - </AlertDialog> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle>Something went wrong</AlertDialogTitle> + </AlertDialogHeader> + <div> + Please try again or check your connection + </div> + <AlertDialogFooter> + <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> <div className='w-[1100px]'> {/* Title and search bar */} <div className="w-[100%] h-[40px] flex justify-between content-center" style={{ marginTop: '5px' }}> - <h1 className="scroll-m-20 text-2xl font-semibold tracking-tight">Processes</h1> - <Input - className="h-[30px] w-[200px]" - type="text" - value={search} - onChange={(event) => setSearch(event.target.value) } - placeholder="Search.." - /> - </div> - - <Separator className="mb-[10px]" /> + <h1 className="scroll-m-20 text-2xl font-semibold tracking-tight">Processes</h1> + <Input + className="h-[30px] w-[200px]" + type="text" + value={search} + onChange={(event) => setSearch(event.target.value)} + placeholder="Search.." + /> + </div> + + <Separator className="mb-[10px]" /> {/* New process button */} - <AlertDialog> - <AlertDialogTrigger> - <Button className="w-[60px] h-[30px] mb-[10px] ml-[10px]" variant="default" size="icon" > - <Waypoints className="h-[20px]" /> - <Plus className="h-[18px]" /> - </Button> - </AlertDialogTrigger> - <AlertDialogContent> - <form onSubmit={(e) => {addProcess(e, nameInput, descriptionInput); }}> - <Input className="mb-[10px]" type="text" id="uname" value={nameInput} onChange={(event) => setNameInput(event.target.value)} placeholder="Process Name" required/> - <Textarea id="description" value={descriptionInput} onChange={(event) => setDescriptionInput(event.target.value)} placeholder="Description" required/> - <br/><br/> + <AlertDialog> + <AlertDialogTrigger> + <Button className="w-[60px] h-[30px] mb-[10px] ml-[10px]" variant="default" size="icon" > + <Waypoints className="h-[20px]" /> + <Plus className="h-[18px]" /> + </Button> + </AlertDialogTrigger> + <AlertDialogContent> + <form onSubmit={(e) => { addProcess(e, nameInput, descriptionInput); }}> + <Input className="mb-[10px]" type="text" id="uname" value={nameInput} onChange={(event) => setNameInput(event.target.value)} placeholder="Process Name" required /> + <Textarea id="description" value={descriptionInput} onChange={(event) => setDescriptionInput(event.target.value)} placeholder="Description" required /> + <br /><br /> <AlertDialogFooter> <AlertDialogAction> <Button type='submit' onClick={() => { @@ -461,52 +461,52 @@ async function fetchDataProcessMachine(processId: number): Promise<ProcessesMach <AlertDialogCancel>Cancel</AlertDialogCancel> </AlertDialogFooter> </form> - </AlertDialogContent> - </AlertDialog> + </AlertDialogContent> + </AlertDialog> - <Separator /> + <Separator /> <ul> {processData.process.map((process, index) => ( <li> <div className="buildingbar flex items-center"> - <Separator orientation='vertical' className="mr-[5px]"/> + <Separator orientation='vertical' className="mr-[5px]" /> <div className='flex flex-row'> <Waypoints className="mx-[20px] mt-[2px]" /> <strong><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">{process.name}</h4></strong> - </div> - <Separator orientation='vertical' className="mx-[5px]"/> + </div> + <Separator orientation='vertical' className="mx-[5px]" /> <div className="h-[100%] flex items-center justify-around"> - <AlertDialog> - <AlertDialogTrigger asChild> - <Button className="h-[30px] w-[60px]" variant="outline" onClick={()=>{setText(process.name); setDesc(process.description)}}>Edit</Button> - </AlertDialogTrigger> - <AlertDialogContent> - <AlertDialogHeader>Edit {process.name}</AlertDialogHeader> - <form onSubmit={(e)=>{e.preventDefault();editProcess(process.name, text ,process.description,desc); setText(""); setDesc("");}}> - <label>Process Name</label> - <Input className="mb-[20px]" type="text" placeholder="Process Name" value={text} onChange={(event) => setText(event.target.value)} required/> - <label>Description</label> - <Input className="mb-[20px]" type="text" placeholder="Description" value={desc} onChange={(event) => setDesc(event.target.value)} required/> - <AlertDialogAction type='submit'>Confirm</AlertDialogAction> - <AlertDialogCancel onClick={()=>{setText(""); setDesc("");}}>Cancel</AlertDialogCancel> - </form> - </AlertDialogContent> - </AlertDialog> - <AlertDialog> - <AlertDialogTrigger asChild> - <Button className="h-[30px] w-[60px]" variant="outline" >Delete</Button> - </AlertDialogTrigger> - <AlertDialogContent> - <AlertDialogHeader>Are you sure about deleting {process.name}?</AlertDialogHeader> - <AlertDialogAction onClick={()=>deleteProcess(process.name, process.description)}>DELETE</AlertDialogAction> - <AlertDialogCancel>Cancel</AlertDialogCancel> - </AlertDialogContent> - </AlertDialog> + <AlertDialog> + <AlertDialogTrigger asChild> + <Button className="h-[30px] w-[60px]" variant="outline" onClick={() => { setText(process.name); setDesc(process.description) }}>Edit</Button> + </AlertDialogTrigger> + <AlertDialogContent> + <AlertDialogHeader>Edit {process.name}</AlertDialogHeader> + <form onSubmit={(e) => { e.preventDefault(); editProcess(process.name, text, process.description, desc); setText(""); setDesc(""); }}> + <label>Process Name</label> + <Input className="mb-[20px]" type="text" placeholder="Process Name" value={text} onChange={(event) => setText(event.target.value)} required /> + <label>Description</label> + <Input className="mb-[20px]" type="text" placeholder="Description" value={desc} onChange={(event) => setDesc(event.target.value)} required /> + <AlertDialogAction type='submit'>Confirm</AlertDialogAction> + <AlertDialogCancel onClick={() => { setText(""); setDesc(""); }}>Cancel</AlertDialogCancel> + </form> + </AlertDialogContent> + </AlertDialog> + <AlertDialog> + <AlertDialogTrigger asChild> + <Button className="h-[30px] w-[60px]" variant="outline" >Delete</Button> + </AlertDialogTrigger> + <AlertDialogContent> + <AlertDialogHeader>Are you sure about deleting {process.name}?</AlertDialogHeader> + <AlertDialogAction onClick={() => deleteProcess(process.name, process.description)}>DELETE</AlertDialogAction> + <AlertDialogCancel>Cancel</AlertDialogCancel> + </AlertDialogContent> + </AlertDialog> </div> - <Separator orientation='vertical' className="ml-[5px]"/> + <Separator orientation='vertical' className="ml-[5px]" /> </div> - <Separator /> + <Separator /> </li> ))} </ul> @@ -514,18 +514,18 @@ async function fetchDataProcessMachine(processId: number): Promise<ProcessesMach <div className="center"> <AlertDialog> <AlertDialogTrigger asChild> - <Button size="sm" variant="outline" onClick={()=>{}}>Add machine to process</Button> + <Button size="sm" variant="outline" onClick={() => { }}>Add machine to process</Button> </AlertDialogTrigger> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Add machine to process</AlertDialogTitle> <label>Process</label> - <select onChange={(e) => { const selectedIndex = e.target.selectedIndex; setProcessDropDown(e.target.value); setProcessDropDownId(processData.process[selectedIndex-1].id)}} value={processDropDown}> + <select onChange={(e) => { const selectedIndex = e.target.selectedIndex; setProcessDropDown(e.target.value); setProcessDropDownId(processData.process[selectedIndex - 1].id) }} value={processDropDown}> <option value={""}>Select an option</option> {processData.process.map((pro, index) => ( pro.name !== "" ? ( <option key={index} value={pro.name}> - {pro.name} + {pro.name} </option> ) : null ))} @@ -547,24 +547,24 @@ async function fetchDataProcessMachine(processId: number): Promise<ProcessesMach }} /> {element.machineEUI} {element.machineName} - <br/> + <br /> </div> ))} </div> )} </AlertDialogHeader> <AlertDialogFooter> - <AlertDialogAction type='submit' onClick={()=>{handleSave2(processMachineData, processMachineData2, processDropDownId); console.log(processDropDownId)}}>Save</AlertDialogAction> + <AlertDialogAction type='submit' onClick={() => { handleSave2(processMachineData, processMachineData2, processDropDownId); console.log(processDropDownId) }}>Save</AlertDialogAction> <AlertDialogCancel>Cancel</AlertDialogCancel> </AlertDialogFooter> </AlertDialogContent> </AlertDialog> </div> </div> - <Separator className='mx-10' orientation="vertical"/> {/* Needs parent container to be set to 100% height for it to work as it takes on the height of parent container */} + <Separator className='mx-10' orientation="vertical" /> {/* Needs parent container to be set to 100% height for it to work as it takes on the height of parent container */} <div className="w-[400px]"> - </div> + </div> </div> ); }; diff --git a/Frontend/power-tracker/src/pages/adminUserConfig.tsx b/Frontend/power-tracker/src/pages/adminUserConfig.tsx index 374a5ded056127853d537298c6dc295a1ef05bb9..0914b70ced6f27b9edd2470c39e951440cc25105 100644 --- a/Frontend/power-tracker/src/pages/adminUserConfig.tsx +++ b/Frontend/power-tracker/src/pages/adminUserConfig.tsx @@ -634,7 +634,7 @@ interface ManageUsersProps { </main> </> ); -}; +} diff --git a/Frontend/power-tracker/src/pages/enoek.tsx b/Frontend/power-tracker/src/pages/enoek.tsx index 6a243eec70df824385dc673622371ead8c414b7b..5ff790e565cbfd8b04c770e08cafe1cc8d381669 100644 --- a/Frontend/power-tracker/src/pages/enoek.tsx +++ b/Frontend/power-tracker/src/pages/enoek.tsx @@ -26,258 +26,240 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog" +//Import the ip for egress and ingress API const EgressAPI = import.meta.env.VITE_EAPI_URL const IngressAPI = import.meta.env.VITE_IAPI_URL - +//enum for better clearity in later code enum Page { - overview = 0, - add = 1, - active = 2, - approved = 3, - reject = 4, - my = 5, - judge =6 - } + overview = 0, + add = 1, + active = 2, + approved = 3, + reject = 4, + my = 5, + judge = 6 +} -// GET THE DATA +// Class for typing for the fetching of data class EnoekClass { - id: number = 0; - header: string = ""; - description: string = ""; - author: string = ""; - start_date: Date | undefined = new Date(); - end_date: Date | undefined = new Date(); - active: boolean = false - process: string = "" - approved: any = null - constructor(id: number, header: string, description: string, author: string, start_date: Date | undefined, end_date: Date | undefined, active: boolean, process: string, approved: any) { - this.id = id; - this.header = header; - this.description = description; - this.author = author; - this.start_date = start_date; - this.end_date = end_date; - this.active = active; - this.process = process; - this.approved = approved; - } + id: number = 0; + header: string = ""; + description: string = ""; + author: string = ""; + start_date: Date | undefined = new Date(); + end_date: Date | undefined = new Date(); + active: boolean = false + process: string = "" + approved: any = null + constructor(id: number, header: string, description: string, author: string, start_date: Date | undefined, end_date: Date | undefined, active: boolean, process: string, approved: any) { + this.id = id; + this.header = header; + this.description = description; + this.author = author; + this.start_date = start_date; + this.end_date = end_date; + this.active = active; + this.process = process; + this.approved = approved; } - +} -const MainComponent= () =>{ - - const [page, setPage] = useState(Page.overview) - const [search, setSearch] = useState(""); +const MainComponent = () => { + //Constant to keep track of which Enoek page the user is on + //With deafualt on overview enoek page + const [page, setPage] = useState(Page.overview) - type EnokDataItem = { - id: number; - header: string; - description: string; - author: string; - start_date: Date | undefined; - end_date: Date | undefined; - active: boolean; - process: string; - approved: boolean | null; // Nullable boolean - }; - - const [enoekData, setEnoekData] = useState<{ enoek: EnokDataItem[] }>({ - enoek: [{ - id: 0, - header: "", - description: "", - author: "", - start_date: new Date(), - end_date: new Date(), - active: false, - process: "", - approved: null, // Nullable boolean - }], - }); - async function fetchData(): Promise<EnoekClass[]> { - var enoekData: EnoekClass[] = [] - await axios.post(EgressAPI + '/enoek', - { - sessionToken: sessionStorage.getItem('TOKEN') - }).then((res) => { - - res.data.enoek.forEach((element: any) => { - enoekData.push({ - id : element.id, - header : element.header, - description : element.description, - author : element.author, - start_date : element.start_date, - end_date : element.stop_date, - active : element.active, - process : element.process, - approved : element.approved, - }) - }); - }).catch((error) => { - console.log(error) - setOpen(true) - }) - - return enoekData - } - - - const deleteEnoek = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>, id: number) => { - e.preventDefault() - - var token: string = "" - var tokenBool = sessionStorage.getItem("TOKEN") - if (tokenBool == null) { - redirect('/') - } else { - token = tokenBool - } - console.log('URL To call: ' + IngressAPI + 'token used' + token) - axios.delete( - IngressAPI + '/new-enoek', - { - data:{ - id: id, - sessionToken: token - } - }) - .then((res)=>{ - console.log(res.data) - }).catch((error) => { - console.log(error) - setOpen(true) - }) - } - - useEffect(() => { + //search storage to manage to sort the diffrent tables that gets shown with search + const [search, setSearch] = useState(""); - fetchData().then((data) => { - setEnoekData({ - ...enoekData, - enoek: data.map((enoeks) => ({ id: enoeks.id, header: enoeks.header, description: enoeks.description, author: enoeks.author, start_date: enoeks.start_date, end_date: enoeks.end_date, active: enoeks.active, process: enoeks.process, approved: enoeks.approved })) - }); + //type for an const for sending the fetched data to the diffrent pages + type EnokDataItem = { + id: number; + header: string; + description: string; + author: string; + start_date: Date | undefined; + end_date: Date | undefined; + active: boolean; + process: string; + approved: boolean | null; // Nullable boolean + }; + //usestate const to store fetched data + const [enoekData, setEnoekData] = useState<{ enoek: EnokDataItem[] }>({ + enoek: [{ + id: 0, + header: "", + description: "", + author: "", + start_date: new Date(), + end_date: new Date(), + active: false, + process: "", + approved: null, // Nullable boolean + }], + }); + //fetching function to get the data from EgressAPI + async function fetchData(): Promise<EnoekClass[]> { + var enoekData: EnoekClass[] = [] + await axios.post(EgressAPI + '/enoek', + { + sessionToken: sessionStorage.getItem('TOKEN') + }).then((res) => { + + res.data.enoek.forEach((element: any) => { + enoekData.push({ + id: element.id, + header: element.header, + description: element.description, + author: element.author, + start_date: element.start_date, + end_date: element.stop_date, + active: element.active, + process: element.process, + approved: element.approved, + }) }); - }, []); + }).catch((error) => { + console.log(error) + setErrorOpen(true) //If error accures set this to true to make an error message appear + }) - let permissionString = sessionStorage.getItem("PERMISSION"); - let permissionInt -if (permissionString !== null) { + return enoekData + } + + //uses the fetch function and stores the data + useEffect(() => { + fetchData().then((data) => { + setEnoekData({ + ...enoekData, + enoek: data.map((enoeks) => ({ id: enoeks.id, header: enoeks.header, description: enoeks.description, author: enoeks.author, start_date: enoeks.start_date, end_date: enoeks.end_date, active: enoeks.active, process: enoeks.process, approved: enoeks.approved })) + }); + }); + }, []); + //Gets the premission level of the user + let permissionString = sessionStorage.getItem("PERMISSION"); + let permissionInt + if (permissionString !== null) { // Result is not null, it's safe to assign it to a variable - permissionInt = parseInt(permissionString, 10); -} -const [open, setOpen] = useState(false); + permissionInt = parseInt(permissionString, 10); + } + //usestate to show the error popup or not + const [errorOpen, setErrorOpen] = useState(false); + //const to close the error popup + const handleClose = () => { + setErrorOpen(false); + }; - const handleClose = () => { - setOpen(false); - }; + return ( + <> - return ( - <> - - <TopBar></TopBar> - <main> - <AlertDialog open={open}> - <AlertDialogContent> + <TopBar></TopBar> + <main> + {/*Error popup */} + {/*---------------*/} + <AlertDialog open={errorOpen}> + <AlertDialogContent> <AlertDialogHeader> - <AlertDialogTitle>Something went wrong</AlertDialogTitle> + <AlertDialogTitle>Something went wrong</AlertDialogTitle> </AlertDialogHeader> - <div> - Please try again or check your connection - </div> + <div> + Please try again or check your connection + </div> <AlertDialogFooter> - <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> + <AlertDialogCancel onClick={handleClose}>Close</AlertDialogCancel> </AlertDialogFooter> - </AlertDialogContent> + </AlertDialogContent> </AlertDialog> - <div className='leftbar'> - - {permissionInt!= undefined && permissionInt <= 2 && ( - <> - <Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.overview); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">All Measures</h4></Button> - <br></br> - </> - - )} - - {permissionInt!= undefined && permissionInt <= 2 && ( - <> - < Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.add); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Add Enoek</h4></Button> - <br/> - </> - - )} - {permissionInt!= undefined && permissionInt <= 2 && ( - <> - <Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.active); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Active Measures</h4></Button> - <br/> - </> - - )} - {permissionInt!= undefined && permissionInt <= 1 && ( - <> - <Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.approved); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Approved Measures</h4></Button> - <br/> - </> - - )} - {permissionInt!= undefined && permissionInt <= 1 && ( - <> - <Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.reject); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Rejected Measures</h4></Button> - <br/> - </> - - )} - {permissionInt!= undefined && permissionInt <= 2 && ( - <> - <Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.my); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">My measures</h4></Button> - <br/> - </> - - )} - {permissionInt!= undefined && permissionInt <= 1 && ( - <> - <Button className="mb-[10px]" variant="ghost" onClick={()=>{setPage(Page.judge); setSearch("");}}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Accept/reject measures</h4></Button> - <br/> - </> - - )} - </div> - - <div className="rightbar"> - <div className="w-[250px] h-[70px]"> - <Input type="text" className="outlined-input" value={search} onChange={(event) => setSearch(event.target.value) } placeholder="Search.."/> - </div> - {(() => { - switch (page) { - case Page.overview: - return <AllMeasures enoek={enoekData.enoek} setData={setEnoekData} search={search}></AllMeasures>; - case Page.add: - return <ManageAdding enoek={enoekData.enoek} setData={setEnoekData} search={search}></ManageAdding>; - case Page.active: - return <ActiveMeasures enoek={enoekData.enoek} setData={setEnoekData} search={search}></ActiveMeasures>; - case Page.approved: - return <Approved enoek={enoekData.enoek} setData={setEnoekData} search={search}></Approved>; - case Page.reject: - return <Rejected enoek={enoekData.enoek} setData={setEnoekData} search={search}></Rejected>; - case Page.my: - return <MyMeasures enoek={enoekData.enoek} setData={setEnoekData} search={search}></MyMeasures>; - case Page.judge: - return <Jugde enoek={enoekData.enoek} setData={setEnoekData} search={search}></Jugde>; - default: - return "sad sounds"; - } - })()} - - </div> - - - </main> - </> - ); - + {/*---------------*/} + <div className='leftbar'> + {/*Buttons to show the diffrent pages related to Enoek where it is shown if the user have the right permission level */} + {permissionInt != undefined && permissionInt <= 2 && ( + <> + <Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.overview); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">All Measures</h4></Button> + <br></br> + </> + + )} + + {permissionInt != undefined && permissionInt <= 2 && ( + <> + < Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.add); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Add Enoek</h4></Button> + <br /> + </> + + )} + {permissionInt != undefined && permissionInt <= 2 && ( + <> + <Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.active); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Active Measures</h4></Button> + <br /> + </> + + )} + {permissionInt != undefined && permissionInt <= 1 && ( + <> + <Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.approved); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Approved Measures</h4></Button> + <br /> + </> + + )} + {permissionInt != undefined && permissionInt <= 1 && ( + <> + <Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.reject); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Rejected Measures</h4></Button> + <br /> + </> + + )} + {permissionInt != undefined && permissionInt <= 2 && ( + <> + <Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.my); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">My measures</h4></Button> + <br /> + </> + + )} + {permissionInt != undefined && permissionInt <= 1 && ( + <> + <Button className="mb-[10px]" variant="ghost" onClick={() => { setPage(Page.judge); setSearch(""); }}><h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Accept/reject measures</h4></Button> + <br /> + </> + + )} + </div> + + <div className="rightbar"> + <div className="w-[250px] h-[70px]"> + <Input type="text" className="outlined-input" value={search} onChange={(event) => setSearch(event.target.value)} placeholder="Search.." /> + </div> + {(() => { + {/*Based upon the selected page the content shown differ */ } + switch (page) { + case Page.overview: + return <AllMeasures enoek={enoekData.enoek} setData={setEnoekData} search={search}></AllMeasures>; + case Page.add: + return <ManageAdding enoek={enoekData.enoek} setData={setEnoekData} search={search}></ManageAdding>; + case Page.active: + return <ActiveMeasures enoek={enoekData.enoek} setData={setEnoekData} search={search}></ActiveMeasures>; + case Page.approved: + return <Approved enoek={enoekData.enoek} setData={setEnoekData} search={search}></Approved>; + case Page.reject: + return <Rejected enoek={enoekData.enoek} setData={setEnoekData} search={search}></Rejected>; + case Page.my: + return <MyMeasures enoek={enoekData.enoek} setData={setEnoekData} search={search}></MyMeasures>; + case Page.judge: + return <Jugde enoek={enoekData.enoek} setData={setEnoekData} search={search}></Jugde>; + default: + return "sad sounds"; + } + })()} + + </div> + + + </main> + </> + ); + }; - - export default MainComponent \ No newline at end of file + +export default MainComponent \ No newline at end of file diff --git a/Frontend/power-tracker/src/pages/login.tsx b/Frontend/power-tracker/src/pages/login.tsx index 9dd4f5972cb013bfb695edd3760db4691a0ee0a6..c3ec79577246382c8d8b3c5ba459cf0b96363565 100644 --- a/Frontend/power-tracker/src/pages/login.tsx +++ b/Frontend/power-tracker/src/pages/login.tsx @@ -83,7 +83,7 @@ function Login() { console.log(error) setOpen(true); }) - }; + } return( <div id="loginpage"> {/* !! READ ABOUT USING "zod" https://zod.dev to allow client side validation && needed to make SHADUI form !! */}