diff --git a/Frontend/power-tracker/src/components/manageBuildDep.tsx b/Frontend/power-tracker/src/components/manageBuildDep.tsx index e2731cb1261c8acf7e565fed4d72298da9a32c6c..e6ef052129b2c63e087db2a2a834b2c522180ea8 100644 --- a/Frontend/power-tracker/src/components/manageBuildDep.tsx +++ b/Frontend/power-tracker/src/components/manageBuildDep.tsx @@ -197,6 +197,7 @@ function BuildDepSubpage () { <div> {/* Building Name */} <div className="buildingbar"> + <Separator orientation='vertical' className="mr-[5px]"/> <AccordionTrigger> <div className='flex flex-row'> <Factory className="mx-[20px]" /> @@ -208,11 +209,13 @@ function BuildDepSubpage () { <Button className="h-[30px] w-[60px]" variant="outline">Edit</Button> <Button className="h-[30px] w-[60px]" variant="outline">Delete</Button> </div> + <Separator orientation='vertical' className="ml-[5px]"/> </div> {/* In building dropdown */} <AccordionContent> {/* Add department button */} <div className="depbar"> + <Separator orientation='vertical' className="mr-[5px]"/> <AlertDialog> <AlertDialogTrigger asChild> <Button className="w-[60px] h-[30px] mb-[10px] ml-[20px] mt-[10px]" variant="default" size="icon" onClick={() => { @@ -264,12 +267,14 @@ function BuildDepSubpage () { <div/> <div/> <div/> <Separator orientation='vertical' className="mx-[5px]"/> <div/> + <Separator orientation='vertical' className="ml-[5px]"/> </div> {/* Department list */} <ul> {buildDep.departments.map((department, index1) => ( <li key={index1}> <div className="depbar"> + <Separator orientation='vertical' className="mr-[5px]"/> <p className="h-[35px] flex leading-7 items-start items-center"> <Warehouse className="h-[20px] mr-[20px] ml-[40px]" /> <h5 className="scroll-m-20 text-lg tracking-tight">{department.name}</h5> @@ -284,6 +289,7 @@ function BuildDepSubpage () { </div> <Separator orientation='vertical' className="mx-[5px]"/> <div></div> + <Separator orientation='vertical' className="ml-[5px]"/> </div> </li> ))} @@ -315,8 +321,12 @@ function BuildDepSubpage () { </button> </form> )}*/} - <div className="h-[10px] grid grid-cols-[1fr_auto_130px]"> - <Separator orientation='vertical' className="mx-[5px] col-start-2"/> + <div className="h-[10px] grid grid-cols-[auto_1fr_auto_130px_auto]"> + <Separator orientation='vertical' className="mr-[5px]"/> + <div/> + <Separator orientation='vertical' className="mx-[5px]"/> + <div/> + <Separator orientation='vertical' className="ml-[5px]"/> </div> <Separator/> </AccordionContent> diff --git a/Frontend/power-tracker/src/components/manageGateway.tsx b/Frontend/power-tracker/src/components/manageGateway.tsx index 5efeabe5a9e670d3f5c5acccd3881c5caf198458..d4328ad51f8c4a23cd185316d4b5f4e4a0e08b51 100644 --- a/Frontend/power-tracker/src/components/manageGateway.tsx +++ b/Frontend/power-tracker/src/components/manageGateway.tsx @@ -3,6 +3,36 @@ import { Button } from "@/components/ui/button"; import "../pages/login.css" import axios from 'axios'; import { redirect } from 'react-router-dom'; +import { Input } from "@/components/ui/input" +import { Separator } from "@/components/ui/separator" +import { Plus } from 'lucide-react'; +import { Router } from 'lucide-react'; +import { Textarea } from "@/components/ui/textarea" +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "@/components/ui/resizable" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog" const EgressAPI = import.meta.env.VITE_EAPI_URL const IngressAPI = import.meta.env.VITE_IAPI_URL @@ -29,39 +59,92 @@ function ManageGateways() { }, [setGatewayData]); return ( - <div> - <h1>GATEWAYS</h1> - <input type="text" className="outlined-input" value={search} onChange={(event) => setSearch(event.target.value) } placeholder="Search.."/> - <ul> - {gatewayData.gateway.map((gates, index) => ( - gates.eui_gate.includes(search.toLowerCase()) || gates.name.includes(search.toLowerCase()) ? - <li key={index}> - {gates.eui_gate} {gates.name} - </li>:null - ))} - </ul> - <h1 id="header">Add new gateway</h1> - <form onSubmit={(e) => {addGateway(e, euiInput, nameInput); setEuiInput(""); setNameInput("");}}> - <p>EUI</p> - <input type="text" id="uname" value={euiInput} onChange={(event) => setEuiInput(event.target.value)} required/> - <p>Name</p> - <input type="text" id="name" value={nameInput} onChange={(event) => setNameInput(event.target.value)} required/> - <br/> <br/> - <Button type='submit' onClick={() => { - setGatewayData(prevState => ({ - ...prevState, - gateway: [ - ...prevState.gateway, - { - eui_gate: euiInput, - name: nameInput - } - ] - })); - }}>Submit gateway! - </Button> - </form> - </div> + <ResizablePanelGroup className='flex h-[100%]' direction="horizontal"> + <ResizablePanel minSize={51}> + {/* Title and search bar */} + <div className="w-[100%] h-[40px] flex justify-between content-center"> + <h1 className="scroll-m-20 text-2xl font-semibold tracking-tight">Buildings and Departments</h1> + <Input + className="h-[30px] w-[200px] mr-[10px]" + type="text" + value={search} + onChange={(event) => setSearch(event.target.value) } + placeholder="Search.." + /> + </div> + + <Separator className="mb-[10px]" /> + + {/* New building button */} + <AlertDialog> + <AlertDialogTrigger> + <Button className="w-[60px] h-[30px] mb-[10px] ml-[10px]" variant="default" size="icon" > + <Router className="h-[20px]" /> + <Plus className="h-[18px]" /> + </Button> + </AlertDialogTrigger> + <AlertDialogContent> + <form onSubmit={(e) => {addGateway(e, euiInput, nameInput); setEuiInput(""); setNameInput("");}}> + <p>EUI</p> + <Input type="text" id="uname" value={euiInput} onChange={(event) => setEuiInput(event.target.value)} required/> + <p>Name</p> + <Input type="text" id="name" value={nameInput} onChange={(event) => setNameInput(event.target.value)} required/> + <br/> <br/> + </form> + <AlertDialogFooter> + <AlertDialogCancel>Cancel</AlertDialogCancel> + <AlertDialogAction type='submit' onClick={() => { + setGatewayData(prevState => ({ + ...prevState, + gateway: [ + ...prevState.gateway, + { + eui_gate: euiInput, + name: nameInput + } + ] + })); + }}>Submit gateway! + </AlertDialogAction> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> + + <Table className=''> + <TableCaption>Gateways</TableCaption> + <TableHeader> + <TableRow> + <TableHead>Name</TableHead> + <TableHead>ID</TableHead> + <TableHead></TableHead> + <TableHead className="text-right"></TableHead> + </TableRow> + </TableHeader> + <TableBody> + {gatewayData.gateway.map((gates, index) => ( + <TableRow> + <TableCell className="min-w-[200px] max-w-[300px] scroll-m-20 text-xl font-semibold tracking-tight flex"> + <Router className="mx-[20px]" /> + <h4 className="scroll-m-20 text-xl font-semibold tracking-tight">{gates.name}</h4> + </TableCell> + <TableCell className="w-[400px]">{gates.eui_gate}</TableCell> + <TableCell className="w-[1fr] flex justify-end"> + <Button className="h-[30px] w-[60px] mr-[5px]" variant="outline">Edit</Button> + <Button className="h-[30px] w-[60px]" variant="outline">Delete</Button> + </TableCell> + </TableRow> + ))} + </TableBody> + </Table> + + </ResizablePanel> + + <ResizableHandle withHandle className="mx-[10px]" /> + + <ResizablePanel defaultSize={75} className="w-[400px]"> + + </ResizablePanel> + </ResizablePanelGroup> ) } diff --git a/Frontend/power-tracker/src/components/manageProcesses.tsx b/Frontend/power-tracker/src/components/manageProcesses.tsx index 65e47839c2e05af0b6be21339f14c8a922c840e3..8db2204bba63cb3783ed029a7eb333109f9cd0df 100644 --- a/Frontend/power-tracker/src/components/manageProcesses.tsx +++ b/Frontend/power-tracker/src/components/manageProcesses.tsx @@ -2,17 +2,28 @@ import { useEffect, useState } from 'react' import { Button } from "@/components/ui/button"; import axios from 'axios'; import { redirect } from 'react-router-dom'; +import { Waypoints } from 'lucide-react'; +import { Separator } from "@/components/ui/separator" +import { Plus } from 'lucide-react'; +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, + AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" - +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion" const EgressAPI = import.meta.env.VITE_EAPI_URL const IngressAPI = import.meta.env.VITE_IAPI_URL @@ -112,117 +123,134 @@ const ManageProcesses= () => { }, [processDropDownId]); return ( - <div> - <h1>Processes:</h1> - <input type="text" className="outlined-input" value={search} onChange={(event) => setSearch(event.target.value) } placeholder="Search.."/> - <ul> - {processData.process.map((process, index) => ( - process.name.toLowerCase().includes(search.toLowerCase()) ? - <li key={index}> - {editIndex === index ? ( - <form onSubmit={(_) => { handleSave(); processData.process[index].name = text; processData.process[index].description = desc; }} className="form-container"> - <input - type="text" - value={text} - onChange={(e) => setText(e.target.value)} - onFocus={() => setTextFocus(true)} - onBlur={() => { setTextFocus(false) }} - autoFocus={nameOrNot} - required - /> - <input - type="text" - value={desc} - onChange={(e) => setDesc(e.target.value)} - onFocus={() => setDescFocus(true)} - onBlur={() => { setDescFocus(false) }} - autoFocus={!nameOrNot} - required - /> - <Button type='submit' onClick={() => {}}>Save</Button> - <Button onClick={() => handleCancelEdit()}>Cancel</Button> - </form> - ) : ( - <div> - <span onClick={() => handleEdit(index, process.name, process.description, true)} title="Edit process"> - {process.name} - </span> - <span onClick={() => handleEdit(index, process.name, process.description, false)} title="Edit process"> - {" " + process.description} - </span> + <div className="flex h-[100%]"> + <div className='w-[1100px]'> + {/* Title and search bar */} + <div className="w-[100%] h-[40px] flex justify-between content-center"> + <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/> + <AlertDialogFooter> + <AlertDialogAction> + <Button type='submit' onClick={() => { + setProcessData(prevState => ({ + ...prevState, + process: [ + ...prevState.process, + { + id: 0, + name: nameInput, + description: descriptionInput + } + ] + })); + }}>Submit Process!</Button> + </AlertDialogAction> + <AlertDialogCancel>Cancel</AlertDialogCancel> + </AlertDialogFooter> + </form> + </AlertDialogContent> + </AlertDialog> + + <Separator /> + + <ul> + {processData.process.map((process, index) => ( + <li> + <div className="buildingbar flex items-center"> + <Separator orientation='vertical' className="mr-[5px]"/> + <div className='flex flex-row'> + <Waypoints className="mx-[20px] mt-[2px]" /> + <h4 className="scroll-m-20 text-xl font-semibold tracking-tight">{process.name}</h4> + </div> + <Separator orientation='vertical' className="mx-[5px]"/> + <div className="h-[100%] flex items-center justify-around"> + <Button className="h-[30px] w-[60px]" variant="outline">Edit</Button> + <Button className="h-[30px] w-[60px]" variant="outline">Delete</Button> </div> - )} - </li>:null - ))} - </ul> - <h1 id="header">Add process</h1> - <form onSubmit={(e) => {addProcess(e, nameInput, descriptionInput); }}> - <input 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/> - <Button type='submit' onClick={() => { - setProcessData(prevState => ({ - ...prevState, - process: [ - ...prevState.process, - { - id: 0, - name: nameInput, - description: descriptionInput - } - ] - })); - }}>Submit Process!</Button> - </form> - <div className="center"> - <AlertDialog> - <AlertDialogTrigger asChild> - <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}> - <option value={""}>Select an option</option> - {processData.process.map((pro, index) => ( - pro.name !== "" ? ( - <option key={index} value={pro.name}> - {pro.name} - </option> - ) : null - ))} - </select> - {processDropDown !== "" && ( - <div> - {processMachineData.processMachine.map((element, index) => ( - <div key={index}> - <input - type="checkbox" - checked={element.added} - onChange={() => { - const updatedProcessMachineData = [...processMachineData.processMachine]; - updatedProcessMachineData[index].added = !element.added; - setProcessMachineData({ - ...processMachineData, - processMachine: updatedProcessMachineData - }); - }} - /> - {element.machineEUI} {element.machineName} - <br/> - </div> + <Separator orientation='vertical' className="ml-[5px]"/> + </div> + <Separator /> + </li> + ))} + </ul> + + <div className="center"> + <AlertDialog> + <AlertDialogTrigger asChild> + <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}> + <option value={""}>Select an option</option> + {processData.process.map((pro, index) => ( + pro.name !== "" ? ( + <option key={index} value={pro.name}> + {pro.name} + </option> + ) : null ))} - </div> - )} - </AlertDialogHeader> - <AlertDialogFooter> - <AlertDialogCancel>Cancel</AlertDialogCancel> - <AlertDialogAction type='submit' onClick={()=>{handleSave2(processMachineData, processMachineData2, processDropDownId); console.log(processDropDownId)}}>Save</AlertDialogAction> - </AlertDialogFooter> - </AlertDialogContent> - </AlertDialog> + </select> + {processDropDown !== "" && ( + <div> + {processMachineData.processMachine.map((element, index) => ( + <div key={index}> + <input + type="checkbox" + checked={element.added} + onChange={() => { + const updatedProcessMachineData = [...processMachineData.processMachine]; + updatedProcessMachineData[index].added = !element.added; + setProcessMachineData({ + ...processMachineData, + processMachine: updatedProcessMachineData + }); + }} + /> + {element.machineEUI} {element.machineName} + <br/> + </div> + ))} + </div> + )} + </AlertDialogHeader> + <AlertDialogFooter> + <AlertDialogCancel>Cancel</AlertDialogCancel> + <AlertDialogAction type='submit' onClick={()=>{handleSave2(processMachineData, processMachineData2, processDropDownId); console.log(processDropDownId)}}>Save</AlertDialogAction> + </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 */} + <div className="w-[400px]"> + + </div> </div> ); }; diff --git a/Frontend/power-tracker/src/components/ui/textarea.tsx b/Frontend/power-tracker/src/components/ui/textarea.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9f9a6dc56193b728feda6d9b1a3d9302b8880f0b --- /dev/null +++ b/Frontend/power-tracker/src/components/ui/textarea.tsx @@ -0,0 +1,24 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +export interface TextareaProps + extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {} + +const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>( + ({ className, ...props }, ref) => { + return ( + <textarea + className={cn( + "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background 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", + className + )} + ref={ref} + {...props} + /> + ) + } +) +Textarea.displayName = "Textarea" + +export { Textarea } diff --git a/Frontend/power-tracker/src/index.css b/Frontend/power-tracker/src/index.css index 15c98ef5201dd4af3f61076b4c09b78b77e9a0d7..354173d1f0ff57c3b6f149cc2edd31ff2145174b 100644 --- a/Frontend/power-tracker/src/index.css +++ b/Frontend/power-tracker/src/index.css @@ -101,14 +101,14 @@ main{ .buildingbar{ display: grid; - grid-template-columns: 1fr auto 130px; + grid-template-columns: auto 1fr auto 130px auto; grid-template-rows: 40px; height: 100%; } .depbar{ display: grid; - grid-template-columns: 180px 1fr 60px 130px auto 130px; + grid-template-columns: auto 180px 1fr 60px 130px auto 130px auto; grid-template-rows: 40px; height: 100%; }