Skip to content

Commit

Permalink
add graph panel, change names
Browse files Browse the repository at this point in the history
features:
- remove Display component. Unused.
- change some naming conventions: perspective -> graph type, document -> workbook
- implement a clear search function for adding graphs or changing graph types
- add GraphPanel instead of keeping all code in index so that prop drilling is more contained
- add a useEffect for default graph to display on load. Doesn't work for workbook change yet.
- errors panel moved to GraphPanel
  • Loading branch information
spiltbeans committed Jun 6, 2023
1 parent f39c272 commit b0fd53c
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 158 deletions.
82 changes: 51 additions & 31 deletions src/components/Controller.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useState, useEffect } from 'react'
import axios from 'axios'
import { useRouter } from 'next/router'
import type { SelectChangeEvent } from '@mui/material/Select'
Expand Down Expand Up @@ -30,44 +30,46 @@ export default function Controller(
{
isDefaultCollapsed = false,
documentOptions,
perspectiveOptions,
graphTypeOptions,
graphOptions,
onDocumentChange,
onWorkbookChange,
onYRangeChange,
onPerspectiveChange,
onGraphTypeChange,
onAddGraph,
onWarning

}: {
isDefaultCollapsed: boolean,
documentOptions: Array<string>,
perspectiveOptions: Array<string>,
graphTypeOptions: Array<string>,
graphOptions: Array<string>,
onDocumentChange: (d: string) => void,
onWorkbookChange: (d: string) => void,
onYRangeChange: (y: boolean) => void,
onPerspectiveChange: (p: string) => void,
onGraphTypeChange: (p: string) => void,
onAddGraph: (g: string) => void
onWarning: (w: string) => void,
}
) {
useEffect(()=>{
// this is a limited solution. if the workbook is changed and the graph type is clients
// or employees, the graph is not selected
changeGraphType('clients')
// eslint-disable-next-line
}, [])
const SPECIAL_DISPLAY = ['client_trends', 'employee_trends']

const [collapsed, setCollapsed] = useState(isDefaultCollapsed)
const [documentSelect, setDocument] = useState(documentOptions[0])
const [workbook, setWorkbook] = useState(documentOptions[0])

const [searchInp, setSearchInp] = useState('')
const [searchValue, setSearchValue] = useState<string | null>(null)

// display options
const [displayType, setDisplayType] = useState('clients')
const [graphType, setGraphType] = useState('clients')
const [yRelative, setYRelative] = useState(true)

const router = useRouter()

const handleDocumentChange = (e: SelectChangeEvent) => {
setDocument(e.target.value as string)
onDocumentChange(e.target.value as string)
}

// References
// https://gist.github.com/ndpniraj/2735c3af00a7c4cbe50602ffe6209fc3
// https://stackoverflow.com/questions/59233036/react-typescript-get-files-from-file-input
Expand All @@ -93,24 +95,42 @@ export default function Controller(

}

const handleWorkbookChange = (e: SelectChangeEvent) => {
clearSearch()

if (e.target.value === 'trends' && !SPECIAL_DISPLAY.includes(graphType)) {
changeGraphType('client_trends')
}

if (e.target.value !== 'trends' && SPECIAL_DISPLAY.includes(graphType)) {
changeGraphType('clients')
}

setWorkbook(e.target.value as string)
onWorkbookChange(e.target.value as string)
}

const handleChangeYRange = (event: React.MouseEvent<HTMLElement>, yRange: boolean) => {
const handleChangeYRange = (_event: React.MouseEvent<HTMLElement>, yRange: boolean) => {
if (yRange !== null) {
setYRelative(yRange)
onYRangeChange(yRange)
}
}
const handlePerspectiveChange = (e: React.MouseEvent<HTMLElement>, perspective: string) => {
if (perspective !== null) {
setDisplayType(perspective)
const handleGraphTypeChange = (_e: React.MouseEvent<HTMLElement>, type: string) => {
changeGraphType(type)
}

const changeGraphType = (type: string) => {
if (type !== null) {
setGraphType(type)
clearSearch()
onPerspectiveChange(perspective)
onGraphTypeChange(type)
}
}

const handleSearchChange = (_e: React.SyntheticEvent<Element, Event>, value: string) => setSearchInp(value)

const handleSearchValChange = (e: any, newVal: string | null) => setSearchValue(newVal)
const handleSearchValChange = (_e: any, newVal: string | null) => setSearchValue(newVal)

const handleAddGraph = () => {
clearSearch()
Expand All @@ -135,8 +155,8 @@ export default function Controller(
</h2>
<div>
<Select
value={documentSelect}
onChange={handleDocumentChange}
value={workbook}
onChange={handleWorkbookChange}
>
{documentOptions.map((document, idx) => {
return <MenuItem key={idx} value={document}>{document}</MenuItem>
Expand Down Expand Up @@ -176,15 +196,15 @@ export default function Controller(
</ToggleButtonGroup>


<ToggleButtonGroup value={displayType} exclusive onChange={handlePerspectiveChange}>
<ToggleButton className='text-xs' value='clients' disabled={!perspectiveOptions.includes('clients')}>Clients</ToggleButton>
<ToggleButton className='text-xs' value='employees' disabled={!perspectiveOptions.includes('employees')}>Employees</ToggleButton>
<ToggleButton className='text-xs' value='individual_employees' disabled={!perspectiveOptions.includes('individual_employees')}>Individual Employees</ToggleButton>
<ToggleButton className='text-xs' value='individual_clients' disabled={!perspectiveOptions.includes('individual_clients')}>Individual Clients</ToggleButton>
<ToggleButtonGroup value={graphType} exclusive onChange={handleGraphTypeChange}>
<ToggleButton className='text-xs' value='clients' disabled={!graphTypeOptions.includes('clients')}>Clients</ToggleButton>
<ToggleButton className='text-xs' value='employees' disabled={!graphTypeOptions.includes('employees')}>Employees</ToggleButton>
<ToggleButton className='text-xs' value='individual_employees' disabled={!graphTypeOptions.includes('individual_employees')}>Individual Employees</ToggleButton>
<ToggleButton className='text-xs' value='individual_clients' disabled={!graphTypeOptions.includes('individual_clients')}>Individual Clients</ToggleButton>
</ToggleButtonGroup>
<ToggleButtonGroup value={displayType} exclusive onChange={handlePerspectiveChange}>
<ToggleButton className='text-xs' value='client_trends' disabled={!perspectiveOptions.includes('client_trends')}>Client Trends</ToggleButton>
<ToggleButton className='text-xs' value='employee_trends' disabled={!perspectiveOptions.includes('employee_trends')}>Employee Trends</ToggleButton>
<ToggleButtonGroup value={graphType} exclusive onChange={handleGraphTypeChange}>
<ToggleButton className='text-xs' value='client_trends' disabled={!graphTypeOptions.includes('client_trends')}>Client Trends</ToggleButton>
<ToggleButton className='text-xs' value='employee_trends' disabled={!graphTypeOptions.includes('employee_trends')}>Employee Trends</ToggleButton>
</ToggleButtonGroup>
</div>

Expand Down
Empty file removed src/components/Display.tsx
Empty file.
141 changes: 141 additions & 0 deletions src/components/GraphPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// React
import * as React from 'react'
import { useState, useMemo } from 'react'

// Elements
import Box from '@mui/material/Box'
import Tabs from '@mui/material/Tabs'
import Tab from '@mui/material/Tab'

import Controller from '@/components/Controller'
import ChartSpace from '@/components/ChartSpace'
import Errors from '@/components/Errors'


export default function GraphPanel({ sheetsPayload, onWarning }: { sheetsPayload: SheetsPayload, onWarning: (w: string) => void }) {

const SPECIAL_DISPLAY = ['client_trends', 'employee_trends']

const [workbook, setWorkbook] = useState<string>(Object.keys(sheetsPayload?.data ?? {})[0] ?? '')

const [panel, setPanel] = useState<number>(0)

const [graphs, setGraphs] = useState<Array<string | undefined>>([])

// display options
const [graphType, setGraphType] = useState<string>('')
const [yRelative, setYRelative] = useState<boolean>(true)
const [autoSort, setAutoSort] = useState<boolean>(true)

const maxValue: number = useMemo(() => {

const d = sheetsPayload?.data?.[workbook]?.data

if (d === undefined) return 0

const graph_labels: Array<string> = (Object.keys(d?.[graphType] ?? {}))
const graph_values: Array<number> = graph_labels.map(label => (
(d?.[graphType]?.[label]).map((element: DataElement) => element.value)
)).flat()

return Math.max(...graph_values)
}, [sheetsPayload, graphType, workbook])

const selectedGraphs = useMemo(() => {
const available_graphs = sheetsPayload?.data?.[workbook]?.['data']?.[graphType] ?? {}
return graphs.reduce((previousValue: FormattedData, currentValue: string | undefined): FormattedData => {
if (currentValue === undefined) return previousValue

previousValue[currentValue] = available_graphs[currentValue]
return previousValue
}, {})
}, [graphType, graphs, sheetsPayload?.data, workbook])


const handleAddGraph = (g: string) => {
if (g !== null && g.length > 1 && !graphs.includes(g)) setGraphs((prev) => [g, ...prev])
}

const handleRemoveGraph = (g: string) => {
setGraphs(prev => prev.filter(e => e !== g))
}

const handleWorkbookChange = (w: string) => {
// rules:
// - if document gets changed to trend and display not client trend, change
// - if document gets changed to !trend and display client trend, change
// don't need to do any enforcement logic in perspective change because the buttons are disabled

if (w === 'trends' && !SPECIAL_DISPLAY.includes(graphType)) {
setAutoSort(false)
handleGraphTypeChange('client_trends')
}

if (w !== 'trends' && SPECIAL_DISPLAY.includes(graphType)) {
setAutoSort(true)
handleGraphTypeChange('clients')
}

setWorkbook(w)
setGraphs([])
}

const handleGraphTypeChange = (p: string) => {
if (p === 'clients' || p === 'employees') {
setGraphs(Object.keys(sheetsPayload.data?.[workbook]?.data?.[p] ?? {}))
} else {
setGraphs([])
}
setGraphType(p)
}

const handleChangePanel = (_event: React.SyntheticEvent, newValue: number) => {
setPanel(newValue)
}

return (

<>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={panel} onChange={handleChangePanel} aria-label="basic tabs example">
<Tab label="Graphs" />
<Tab label="Errors" />
</Tabs>
</Box>

{
panel === 0 ? (
<div id='display-space' className='flex w-full gap-4'>
<div id='data-charts' className={'flex flex-col w-3/4 border gap-4 py-4'}>
<ChartSpace
graphs={selectedGraphs}
autoSort={autoSort}
onRemoveGraph={handleRemoveGraph}
maxValue={yRelative ? undefined : maxValue}
/>
</div>

<div id='data-controller' className='w-fit border py-11'>
<Controller
isDefaultCollapsed={false}
documentOptions={Object.keys(sheetsPayload?.data ?? {})}
graphTypeOptions={Object.keys(sheetsPayload?.data?.[workbook]?.data ?? {})}
graphOptions={Object.keys(sheetsPayload?.data?.[workbook]?.data?.[graphType] ?? {})}
onWorkbookChange={handleWorkbookChange}
onYRangeChange={setYRelative}
onGraphTypeChange={handleGraphTypeChange}
onAddGraph={handleAddGraph}
onWarning={onWarning}
/>

</div>
</div>
) : (
<div id='data-input' className='flex flex-col justify-center gap-4'>
<Errors errors={sheetsPayload?.data?.[workbook]?.errors ?? {}} />
</div>
)
}
</ >
)
}
Loading

0 comments on commit b0fd53c

Please sign in to comment.