Skip to content
Snippets Groups Projects
Select Git revision
  • 2dff353ba71ffbd394c7aa05fabec5054df04ac2
  • master default protected
  • 69-resize-image-before-upload
  • 60-add-match-salamander-modal-to-edit-salamander
  • 50-fix-server-error-message
  • 48-fix-gradle
  • 31-camera-communicate-with-api-and-delete-from-cache-2
  • 20-changing-verification-step-in-profile-to-modal
  • 4-add-all-basic-views
  • 1-setup
10 results

CustomDialog.js

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    overview.tsx 21.60 KiB
    import './overview.css' 
    //import BarChart from '../components/LineChart';
    //import NavBar from "../components/navbar"
    import React, { useEffect, useState } from "react";
    import LineChart from '../components/LineChart';
    import { RefreshCw, Minus, Plus } from 'lucide-react';
    import TopBar from '@/components/topbar';
    import axios from 'axios';
    import {
      Accordion,
      AccordionContent,
      AccordionItem,
      AccordionTrigger,
    } from "@/components/ui/accordion"
    import { Button } from '@/components/ui/button';
    import { DatePicker } from '@/components/ui/datepicker'
    import { Input } from '@/components/ui/input';
    import { Separator } from "@/components/ui/separator"
    import { Checkbox } from "@/components/ui/checkbox"
    
    
    
    const IngressAPI: String = import.meta.env.VITE_IAPI_URL
    const EgressAPI: String = import.meta.env.VITE_EAPI_URL
    
    const colorList: string[] = [
      'rgb(75, 33, 192)',
      'rgb(75, 192, 192)',
      'rgb(75, 192, 142)',
      'rgb(75, 192, 192)',
      'rgb(241, 212, 192)',
      'rgb(75, 1, 1)',
    ]
    
    const millisInDays: number = 86400000
    
    function Overview() {
    
      //list over building data, in a way that linechart can understand
      const [buildingData, setBuildingData] = useState({
        labels: [""],
        datasets: [
          {
            label: "",
            data: [0],
            borderColor: 'rgb(75, 192, 192)',
            tension: 0.1
          }
        ],
      });
    
      const [rebuild, setRebuild] = useState(false) //"flipping" this will rebuild the page (letting you see changes)
    
      const [recall, setRecall] = useState(false)   //"flipping" this will re-call the databse, with the updated parameters
    
      const [startDate, setStartDate] = React.useState<Date>()
      const [endDate, setEndDate] = React.useState<Date>()
    
      const [buildingsState, setBuildingsState] = useState({
        buildings: [new Building("", new Dataset("", []))],
        processes: [new Process("", "", new Dataset("", []))],
        labels: [new Date()],
        checked: [false]
      })
    
      const [interval, setInterval] = useState(20)  //the interval between each packet of data
      const [since, setSince] = useState(2)         //how long the first packet of data should be fetched from
      const [start, setStart] = useState(0)
      
      useEffect(() => {
        if (startDate != undefined && endDate != undefined) {
          var sinceStart = new Date().getTime() - startDate.getTime()
          var sinceEnd = new Date().getTime() - endDate.getTime()
          
          sinceStart = Math.floor(sinceStart/86400000)  //millisec to days (86,400,000 milliseconds per day) and turning it to an integer
          sinceEnd = Math.floor(sinceEnd/86400000)
          
          setSince(sinceEnd)
          setStart(sinceStart)
        }
      }, [startDate, endDate])
    
      useEffect(() => {
        var buildingpromise: Promise<PageData> = fetchData(interval, since, start)
        buildingpromise.then(page => {
          //sets the state of the buildings
          setBuildingsState({
            buildings: page.buildings,
            processes: page.processes,
            labels: page.labels,
            checked: setChecked(page.buildings, page.processes),
          })
          //sets the building data, and creates the datasets for graph
        })
      }, [recall])
    
      //This effect is supposed to ONLY update if one of the checkboxes are checked/unchecked
      //And thus changing the provided data
      useEffect(() => {
        setBuildingData({
          labels: getLabels(buildingsState.labels),
          datasets: presentData(buildingsState.buildings, buildingsState.processes)
        })
      }, [buildingsState.checked])
    
      return(
          <>
            <TopBar></TopBar>
            
            <main>
    
              <div className = "rightbar">
                <div className="h-[100%] w-[100%] relative overflow-auto">
                  <LineChart data={buildingData}/>
                </div>
              </div>
    
              <div className = "leftbar">
                <div>
    
                  {/* GOTTA IMPLEMENT FUNCTIONALITY TO THE 2 BUTTONS TO CHNAGE INOUT */}
                  <label className='text-primary text-sm'>Minuites between readings</label>
                  <div className='flex'>
                    <Input className="w-[280px] h-[40px]" type='number' value={interval} onChange={event => setInterval(parseInt(event.target.value))}></Input>
                    <div className="flex flex-col h-[40px] ml-[5px]">
                      <Button className='h-[20px] w-[25px] rounded-none rounded-r-lg' variant="ghost" size="icon" onClick={_ => setInterval(interval+1)}>
                        <Plus className='h-[20px]'></Plus>
                      </Button>
                      <Button className='h-[20px] w-[25px] rounded-none rounded-r-lg' variant="ghost" size="icon" onClick={_ => setInterval(interval-1)}>
                        <Minus className='h-[20px]'></Minus>
                      </Button>
                    </div>
                  </div>
                  
                  {/* DATE FROM PICKER */}
                  <label className='text-primary text-sm'>Date From</label>
                  <div className='flex'>
                    <DatePicker width={280} dates={endDate} setDate={setEndDate} allowedDate={startDate} />
                    <div className='flex flex-col h-[40px] ml-[5px]'>
                      <Button className='h-[20px] w-[25px] rounded-none rounded-r-lg' variant="ghost" size="icon" onClick={_ => {  //TODO: CLEAN UP THESE LATER ------------------------------- NO REALLY, THEY SUCK ATM
                        var date = new Date;
                        if(startDate !== undefined){
                          date.setTime(startDate.getTime() + 1 * millisInDays);
                          setStartDate(date);
                        }
                        }}>
                        <Plus className='h-[20px]'></Plus>
                      </Button>
                      <Button className='h-[20px] w-[25px] rounded-none rounded-r-lg' variant="ghost" size="icon" onClick={_ => {
                        var date = new Date; var edate = endDate
                        if(startDate !== undefined){
                          if(edate === undefined) edate = new Date(1)
                          if (edate.getTime() < startDate.getTime()){
                          date.setTime(startDate.getTime() - 1 * millisInDays);
                          setStartDate(date);
                        }}}}>
                        <Minus className='h-[22px]'></Minus>
                      </Button>
                    </div> 
                  </div>
    
                  {/* DATE TO PICKER */}
                  <label className='text-primary text-sm'>Date To</label>
                  <div className='flex mb-[10px]'>
                    <DatePicker width={280} dates={startDate} setDate={setStartDate} allowedDate={new Date()}/>
                    <div className='flex flex-col h-[40px] ml-[5px]'>
                      <Button className='h-[20px] w-[25px] rounded-none rounded-r-lg' variant="ghost" size="icon" onClick={e => {  //TODO: CLEAN UP THESE LATER ------------------------------- NO REALLY, THEY SUCK ATM
                      var date = new Date; var sdate = startDate
                      if(endDate !== undefined){
                        if(sdate === undefined) sdate = new Date(1)
                        if (sdate.getTime() > endDate.getTime()){
                        date.setTime(endDate.getTime() + 1 * millisInDays);
                        setEndDate(date);
                      }}}}>
                        <Plus className='h-[20px]'></Plus>
                      </Button>
                    <Button className='h-[20px] w-[25px] rounded-none rounded-r-lg' variant="ghost" size="icon" onClick={e => {
                      var date = new Date;
                      if(endDate !== undefined){
                        date.setTime(endDate.getTime() - 1 * millisInDays);
                        setEndDate(date);
                      }
                      }}>
                        <Minus className='h-[22px]'></Minus>
                      </Button>
                    </div>
                  </div>
                  
                  {/* DATE HELPER BUTTONS */}
                  <div className="flex items-center justify-between mb-[5px]">
                    <Button className="w-[100px] h-[30px]" variant="outline" onClick={_ => {
                        var sdate: Date = new Date;
                        var edate: Date = new Date;
                        if(startDate !== undefined && endDate !== undefined){
                          sdate.setTime(startDate.getTime() - 1 * millisInDays); edate.setTime(endDate.getTime() - 1 * millisInDays)
                          setStartDate(sdate);  setEndDate(edate)
                        }}}>-1 day
                    </Button>
                    <Separator orientation="vertical"></Separator>
                    <Button className="w-[100px] h-[30px]" variant="outline" onClick={_ => {
                        var sdate: Date = new Date;
                        var edate: Date = new Date;
                        if(startDate !== undefined && endDate !== undefined){
                          sdate.setTime(startDate.getTime() - 7 * millisInDays); edate.setTime(endDate.getTime() - 7 * millisInDays)
                          setStartDate(sdate);  setEndDate(edate)
                        }}}>-1 week
                    </Button>
                    </div>
                    <Separator className='mb-[5px]'></Separator>
                    <div className="flex items-center justify-between">
                      <Button className="w-[100px] h-[30px]" variant="outline" onClick={_ => {setStartDate(new Date()); var d = new Date(); d.setTime(d.getTime() - 1 * millisInDays); setEndDate(d); setInterval(10)}}>Last day</Button>
                      <Separator orientation="vertical"></Separator>
                      <Button className="w-[100px] h-[30px]" variant="outline" onClick={_ => {setStartDate(new Date()); var d = new Date(); d.setTime(d.getTime() - 7 * millisInDays); setEndDate(d); setInterval(30)}}>Last week</Button>
                    </div>
                  </div>
                  <br/>
                  <div className="flex items-center align-center justify-center mb-[10px]"><Button className="h-[25px] w-[25px]" variant="outline" size="icon" onClick={_ => setRecall(!recall)}><RefreshCw className='h-[15px] w-[15px]'/></Button></div>
                
                {/*  */}
                <div className ="rounded-md border shadow-md px-[5px]">
                  {buildingsState.buildings !== null ?  (
                    buildingsState.buildings.map((building, i) => {
                      return (
                        <Accordion type='single' collapsible>
                          <AccordionItem value={i.toString()}>
                          <div id="accordion-bar">
                            <AccordionTrigger className='mx-[5px]'>
                              <label>{building.name}</label>
                            </AccordionTrigger>
                            <Separator orientation='vertical'></Separator>
                            <div className='w-[100%] h-[100%] flex justify-center items-center'>
                              <Checkbox 
                                checked={building.active} 
                                onCheckedChange={_ => {
                                  building.active = !building.active; 
                                  buildingsState.checked = setChecked(buildingsState.buildings, []); 
                                  setRebuild(!rebuild)}}
                              />
                            </div>
                          </div>
                          <AccordionContent>
                          {
                          building.departments.map((department, i) => {
                            return (
                              <div key = {i}>
                                  <Accordion type='single' collapsible>
                                    <AccordionItem value={i.toString()}>
                                    <div id="accordion-bar">
                                      <AccordionTrigger className='mr-[5px] ml-[15px]'>
                                        <label>{department.name}</label>
                                      </AccordionTrigger>
                                      <Separator orientation='vertical'></Separator>
                                      <div className='w-[100%] h-[100%] flex justify-center items-center'>
                                        <Checkbox
                                          checked={department.active} 
                                          onCheckedChange={_ => {
                                            department.active = !department.active; 
                                            buildingsState.checked = setChecked(buildingsState.buildings, []); 
                                            setRebuild(!rebuild)}} 
                                        />
                                      </div>
                                    </div>
                                    {department.machines.map(machine => {
                                        return(
                                        <AccordionContent className='h-[40px]'>
                                        <div id="accordion-machine">
                                          <div className='mr-[5px] ml-[30px] flex items-center'>
                                            <label>{machine.name}</label>
                                          </div>
                                          <Separator orientation='vertical'></Separator>
                                          <div className='w-[100%] h-[100%] flex justify-center items-center'>
                                            <Checkbox
                                              checked={machine.active} 
                                              onCheckedChange={_ => {
                                                machine.active = !machine.active; 
                                                buildingsState.checked = setChecked(buildingsState.buildings, []); 
                                                setRebuild(!rebuild)}} 
                                            />
                                          </div>
                                        </div>
                                        </AccordionContent>
                                      )
                                    })}
                                    </AccordionItem>
                                  </Accordion>
                              </div>
                            )
                            })}
                          </AccordionContent>
                          </AccordionItem>
                        </Accordion>
                        )
                    })) : (
                      <div></div>
                    )}
                  </div>
                  <br/>
                  <div className ="rounded-md border shadow-md px-[5px]">
                  {buildingsState.processes !== null ? (
                    buildingsState.processes.map((process, i) => {
                      return (
                        <Accordion type='single' collapsible>
                          <AccordionItem value={i.toString()}>
                          <div id="accordion-bar">
                            <AccordionTrigger className='mx-[5px]'>
                                <label>{process.name}</label>
                            </AccordionTrigger>
                            <Separator orientation='vertical'></Separator>
                            <div className='w-[100%] h-[100%] flex justify-center items-center'>
                              <Checkbox 
                                checked={process.active} 
                                onCheckedChange={_ => {
                                  process.active = !process.active; 
                                  buildingsState.checked = setChecked([], buildingsState.processes); 
                                  setRebuild(!rebuild)}}
                              />
                            </div>
                          </div> 
                            {process.machines.map(machine => {
                              return(
                              <AccordionContent>
                                <div id="accordion-machine">
                                  <div className='mr-[5px] ml-[30px] flex items-center'>
                                    <label>{machine.name}</label>
                                  </div>
                                  <Separator orientation='vertical'></Separator>
                                  <div className='w-[100%] h-[100%] flex justify-center items-center'>
                                    <Checkbox 
                                      checked={machine.active} 
                                      onCheckedChange={_ => {
                                        machine.active = !machine.active; 
                                        buildingsState.checked = setChecked([], buildingsState.processes); 
                                      setRebuild(!rebuild)}} 
                                    />
                                  </div>
                                </div>
                              </AccordionContent>
                              )})}
                          </AccordionItem>
                          </Accordion>
                          )
                  })) : (
                    <div></div>
                  )}
                </div>
              </div>
    
            </main>
          </>
      )
    }
    
    async function fetchData(interval: number, since: number, start: number): Promise<PageData> {
      var pageData: PageData = new PageData
      var buildings: Building[] = []
      var processes: Process[] = []
      await axios.post(EgressAPI + '/overview',
      {
        sessionToken: sessionStorage.getItem('TOKEN')
      }, {params: {
        since,
        interval,
        start
      }}).then((res) => {
        //fetch labels, the timestamps in the graph
        pageData.labels = res.data.labels
        //get the buildigns, departments and the individual machines
        res.data.buildings.forEach((building: any) => {
          var dataset: Dataset
          dataset = building.dataset
          dataset.tension = 0.1
          dataset.borderColor = 'rgb(75, 32, 192)'
          var bIdx = buildings.push(new Building(building.name, dataset)) - 1 
          
          building.departments.forEach((department: any) => {
            dataset = department.dataset
            dataset.tension = 0.1
            dataset.borderColor = 'rgb(75, 192, 41)'
            var dIdx = buildings[bIdx].departments.push(new Department(department.name, dataset)) - 1
    
            department.machines.forEach((machine: any) => {
              dataset = machine.dataset
              dataset.tension = 0.1
              dataset.borderColor = 'rgb(255, 192, 192)'
              buildings[bIdx].departments[dIdx].machines.push(new Machine(machine.eui, machine.name, dataset))
            })
          })
        });
        //get the processes, and their machines
        res.data.processes.forEach((process: any) => {
          var dataset: Dataset
          dataset = process.dataset
          dataset.tension = 0.1
          dataset.borderColor = 'rgb(75, 32, 192)'
          var pIdx = processes.push(new Process(process.name, process.desription, dataset)) - 1 
    
          process.machines.forEach((machine: any) => {
            dataset = machine.dataset
            dataset.tension = 0.1
            dataset.borderColor = 'rgb(255, 192, 192)'
            processes[pIdx].machines.push(new Machine(machine.eui, machine.name, dataset))
          })
        })
        console.log(res.data.processes)
      }).catch((error) => {
        console.log(error)
      })
      //return at end of function
      pageData.buildings = buildings
      pageData.processes = processes
      return pageData 
    }
    
    //Function that creates datasets 
    function presentData(buildings: Building[], processes: Process[]): Dataset[] {
      var datasets: Dataset[] = []
      buildings.forEach((building, idx) => {
        if(building.active) {
          datasets.push(buildings[idx].dataset)
        }
        building.departments.forEach(department => {
          if(department.active) {
            datasets.push(department.dataset)
          }
          department.machines.forEach(machine => {
            if (machine.active) {
              datasets.push(machine.dataset)
            }
          })
        })
      })
      processes.forEach(process => {
        if (process.active) {
          datasets.push(process.dataset)
        }
        process.machines.forEach(machine => {
          if (machine.active) {
            datasets.push(machine.dataset)
          }
        })
      })
      return datasets
    }
    
    //returns an array of strings, given a building (aka the timestamps). returns empty list on fail
    function getLabels(dateList: Date[]): string[] {
      var labels: string[] = []
      if (dateList !== null){
        labels = dateList.map(date => new Date(date).toString().substring(4,24))
      }
      
      return labels
    }
    
    
    //returns a list of booleans
    function setChecked(buildings: Building[], processes: Process[]): boolean[] {
      var checkedList: boolean[] = []
      buildings.forEach(building => {
        checkedList.push(building.active)
        building.departments.forEach(department => {
          checkedList.push(department.active)
          department.machines.forEach(machine => {
            checkedList.push(machine.active)
          })
        })
      })
      processes.forEach(process => {
        checkedList.push(process.active)
        process.machines.forEach(machine => {
          checkedList.push(machine.active)
        })
      })
      return checkedList
    }
    
    class Building {
      departments: Department[] = []
      name: string = ""
      active: boolean = false
      dataset: Dataset
      
      constructor(name: string, dataset: Dataset) {
        this.name = name
        this.dataset = dataset
      }
    }
    
    class Department {
      machines: Machine[] = []
      name: string = "No department"
      active: boolean = false
      dataset: Dataset
    
      constructor(name: string, dataset: Dataset) {
        this.name = name
        this.machines = []
        this.dataset = dataset
      }
    }
    
    class Process {
      name: string = ""
      description: string = ""
      active: boolean = false
      machines: Machine[] = []
      dataset: Dataset
    
      constructor(name: string, description: string, dataset: Dataset) {
        this.name = name; this.description = description
        this.dataset = dataset
      }
    }
    
    class Machine {
      EUI: string =""
      name: string = ""
      active: boolean = false
      dataset: Dataset
    
      constructor(EUI: string, name: string, dataset: Dataset) {
        this.EUI = EUI; this.name = name
        this.dataset = dataset
      }
    }
    
    
    class Dataset {
        label: string = ""
        data: number[] = []
        borderColor: string = 'rgb(75, 192, 192)'
        tension: number = 0.1
    
        constructor(label: string, data: number[], borderColor?: string, tension?: number) {
          this.label = label; this.data = data;
          if(borderColor) this.borderColor = borderColor
          if (tension) this.tension = tension
        }
    }
    
    class PageData {
      buildings: Building[] = []
      processes: Process[] = []
      labels: Date[] = []
    }
    
    export default Overview