Use auto generated ids for dashboards
This commit is contained in:
parent
bb08f31e6b
commit
4173629693
|
|
@ -2,17 +2,18 @@ import { DashboardContent } from '@/utils/dashboard/parseDashboard'
|
||||||
import { request } from './request'
|
import { request } from './request'
|
||||||
|
|
||||||
export type DashboardInfo = {
|
export type DashboardInfo = {
|
||||||
id: string
|
id: number
|
||||||
|
name: string
|
||||||
contents: string
|
contents: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getDashboards = () => request<DashboardInfo[]>('/api/dashboards')
|
export const getDashboards = () => request<DashboardInfo[]>('/api/dashboards')
|
||||||
|
|
||||||
export const getDashboard = (id: string) =>
|
export const getDashboard = (id: number) =>
|
||||||
request<DashboardInfo>(`/api/dashboards/${encodeURI(id)}`)
|
request<DashboardInfo>(`/api/dashboards/${id}`)
|
||||||
|
|
||||||
export const createDashboard = (body: {
|
export const createDashboard = (body: {
|
||||||
id: string
|
name: string
|
||||||
contents: DashboardContent
|
contents: DashboardContent
|
||||||
}) =>
|
}) =>
|
||||||
request<DashboardInfo>(`/api/dashboards`, {
|
request<DashboardInfo>(`/api/dashboards`, {
|
||||||
|
|
@ -28,10 +29,11 @@ export const updateDashboard = ({
|
||||||
id,
|
id,
|
||||||
...body
|
...body
|
||||||
}: {
|
}: {
|
||||||
id: string
|
id: number
|
||||||
|
name: string
|
||||||
contents: DashboardContent
|
contents: DashboardContent
|
||||||
}) =>
|
}) =>
|
||||||
request<DashboardInfo>(`/api/dashboards/${encodeURI(id)}`, {
|
request<DashboardInfo>(`/api/dashboards/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'content-type': 'application/json' },
|
headers: { 'content-type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
|
|
||||||
|
|
@ -1,95 +1,13 @@
|
||||||
import {
|
|
||||||
createDashboard,
|
|
||||||
getDashboards,
|
|
||||||
updateDashboard,
|
|
||||||
} from '@/api/dashboards'
|
|
||||||
import { UserLayout } from '@/layouts/UserLayout/UserLayout'
|
import { UserLayout } from '@/layouts/UserLayout/UserLayout'
|
||||||
import { createDashboardContent } from '@/utils/createDashboardContent'
|
|
||||||
import { parseDashboard } from '@/utils/dashboard/parseDashboard'
|
|
||||||
import { useEffect, useMemo, useState } from 'preact/hooks'
|
|
||||||
import { useQuery, useQueryClient } from 'react-query'
|
|
||||||
import { DashboardGrid } from './components/DashboardGrid/DashboardGrid'
|
import { DashboardGrid } from './components/DashboardGrid/DashboardGrid'
|
||||||
import { DashboardHeader } from './components/DashboardHeader/DashboardHeader'
|
import { DashboardHeader } from './components/DashboardHeader/DashboardHeader'
|
||||||
import { GRID_H_SNAP, GRID_WIDTH } from './constants'
|
|
||||||
import { DashboardContextProvider } from './contexts/DashboardContext'
|
import { DashboardContextProvider } from './contexts/DashboardContext'
|
||||||
import { BoxDefinition } from './types'
|
|
||||||
|
|
||||||
export const NewDashboardPage = () => {
|
export const NewDashboardPage = () => {
|
||||||
const queryClient = useQueryClient()
|
|
||||||
const dashboards = useQuery(['/dashboards'], getDashboards)
|
|
||||||
const dashboard = dashboards.data?.find((i) => i.id === 'default')
|
|
||||||
|
|
||||||
const dashboardContent = useMemo(
|
|
||||||
() => (dashboard ? parseDashboard(dashboard.contents) : undefined),
|
|
||||||
[dashboard]
|
|
||||||
)
|
|
||||||
|
|
||||||
const [boxes, setBoxes] = useState(dashboardContent?.boxes ?? [])
|
|
||||||
|
|
||||||
const handleChange = (cb: (boxes: BoxDefinition[]) => BoxDefinition[]) => {
|
|
||||||
const newBoxes = cb(boxes)
|
|
||||||
|
|
||||||
setBoxes(newBoxes)
|
|
||||||
|
|
||||||
if (dashboard && dashboardContent) {
|
|
||||||
updateDashboard({
|
|
||||||
id: dashboard?.id,
|
|
||||||
contents: {
|
|
||||||
...dashboardContent,
|
|
||||||
boxes: newBoxes,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleRefresh = () => {
|
|
||||||
queryClient.invalidateQueries(['/sensor/values'])
|
|
||||||
queryClient.invalidateQueries(['/sensor/values/latest'])
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleNewBox = () => {
|
|
||||||
const box = {
|
|
||||||
id: new Date().getTime().toString(),
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
w: GRID_WIDTH,
|
|
||||||
h: GRID_H_SNAP * 20,
|
|
||||||
}
|
|
||||||
|
|
||||||
const otherBoxes = boxes.map((b) => {
|
|
||||||
b.y += 200
|
|
||||||
|
|
||||||
return b
|
|
||||||
})
|
|
||||||
|
|
||||||
handleChange(() => [box, ...otherBoxes])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terrible code - ensure there's default dashboard
|
|
||||||
useEffect(() => {
|
|
||||||
if (dashboards.data && !dashboard) {
|
|
||||||
createDashboard({
|
|
||||||
id: 'default',
|
|
||||||
contents: createDashboardContent(),
|
|
||||||
}).then(() => {
|
|
||||||
dashboards.refetch()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [dashboards.data, dashboard])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setBoxes(dashboardContent?.boxes ?? [])
|
|
||||||
}, [dashboardContent])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardContextProvider>
|
<DashboardContextProvider>
|
||||||
<UserLayout
|
<UserLayout className="dashboard" header={<DashboardHeader />}>
|
||||||
className="dashboard"
|
<DashboardGrid />
|
||||||
header={
|
|
||||||
<DashboardHeader onRefresh={handleRefresh} onNewBox={handleNewBox} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DashboardGrid boxes={boxes} onChange={handleChange} />
|
|
||||||
</UserLayout>
|
</UserLayout>
|
||||||
</DashboardContextProvider>
|
</DashboardContextProvider>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
import { BoxDefinition } from '../../types'
|
import { useDashboardContext } from '../../contexts/DashboardContext'
|
||||||
import { normalizeBoxes } from '../../utils/normalizeBoxes'
|
import { normalizeBoxes } from '../../utils/normalizeBoxes'
|
||||||
import { EditableBox } from './components/EditableBox'
|
import { EditableBox } from './components/EditableBox'
|
||||||
|
|
||||||
type Props = {
|
export const DashboardGrid = () => {
|
||||||
boxes: BoxDefinition[]
|
const { isDashboardSelected, boxes, setBoxes } = useDashboardContext()
|
||||||
onChange: (cb: (previous: BoxDefinition[]) => BoxDefinition[]) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DashboardGrid = ({ boxes, onChange }: Props) => {
|
return isDashboardSelected ? (
|
||||||
return (
|
|
||||||
<div className="grid-sensors-container">
|
<div className="grid-sensors-container">
|
||||||
<div className="grid-sensors">
|
<div className="grid-sensors">
|
||||||
{boxes.map((b) => (
|
{boxes.map((b) => (
|
||||||
|
|
@ -17,7 +14,7 @@ export const DashboardGrid = ({ boxes, onChange }: Props) => {
|
||||||
key={b.id}
|
key={b.id}
|
||||||
boxes={boxes}
|
boxes={boxes}
|
||||||
onPosition={(p) => {
|
onPosition={(p) => {
|
||||||
onChange((previous) =>
|
setBoxes((previous) =>
|
||||||
normalizeBoxes(
|
normalizeBoxes(
|
||||||
previous.map((pb) =>
|
previous.map((pb) =>
|
||||||
pb.id === b.id ? { ...pb, x: p.x, y: p.y } : pb
|
pb.id === b.id ? { ...pb, x: p.x, y: p.y } : pb
|
||||||
|
|
@ -26,7 +23,7 @@ export const DashboardGrid = ({ boxes, onChange }: Props) => {
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
onResize={(s) => {
|
onResize={(s) => {
|
||||||
onChange((previous) =>
|
setBoxes((previous) =>
|
||||||
normalizeBoxes(
|
normalizeBoxes(
|
||||||
previous.map((pb) =>
|
previous.map((pb) =>
|
||||||
pb.id === b.id ? { ...pb, w: s.w, h: s.h } : pb
|
pb.id === b.id ? { ...pb, w: s.w, h: s.h } : pb
|
||||||
|
|
@ -35,12 +32,12 @@ export const DashboardGrid = ({ boxes, onChange }: Props) => {
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
onEdit={(newB) => {
|
onEdit={(newB) => {
|
||||||
onChange((previous) =>
|
setBoxes((previous) =>
|
||||||
previous.map((b) => (b.id === newB.id ? newB : b))
|
previous.map((b) => (b.id === newB.id ? newB : b))
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
onRemove={() => {
|
onRemove={() => {
|
||||||
onChange((previous) =>
|
setBoxes((previous) =>
|
||||||
normalizeBoxes(previous.filter((pb) => pb.id !== b.id))
|
normalizeBoxes(previous.filter((pb) => pb.id !== b.id))
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
@ -48,5 +45,7 @@ export const DashboardGrid = ({ boxes, onChange }: Props) => {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>Please create a new dashboard</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,54 @@
|
||||||
import { CancelIcon, FiltersIcon, PlusIcon, RefreshIcon } from '@/icons'
|
import { CancelIcon, FiltersIcon, PlusIcon, RefreshIcon } from '@/icons'
|
||||||
|
import { intervalToRange } from '@/utils/intervalToRange'
|
||||||
import { useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
|
import { GRID_H_SNAP, GRID_WIDTH } from '../../constants'
|
||||||
import { useDashboardContext } from '../../contexts/DashboardContext'
|
import { useDashboardContext } from '../../contexts/DashboardContext'
|
||||||
import { DashboardFilters } from './components/DashboardFilters'
|
import { DashboardFilters } from './components/DashboardFilters'
|
||||||
import { DashboardSwitch } from './components/DashboardSwitch'
|
import { DashboardSwitch } from './components/DashboardSwitch'
|
||||||
|
|
||||||
type Props = {
|
export const DashboardHeader = () => {
|
||||||
onNewBox: () => void
|
const { verticalMode, boxes, setBoxes, setFilter } = useDashboardContext()
|
||||||
onRefresh: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DashboardHeader = ({ onNewBox, onRefresh }: Props) => {
|
|
||||||
const { verticalMode } = useDashboardContext()
|
|
||||||
const [filtersShown, setFiltersShown] = useState(false)
|
const [filtersShown, setFiltersShown] = useState(false)
|
||||||
|
|
||||||
|
const handleRefresh = () => {
|
||||||
|
// @TODO: This is duplicate code, unify it somehow with dashboard filters
|
||||||
|
setFilter((v) => {
|
||||||
|
const range = intervalToRange(v.interval, v.customFrom, v.customTo)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
customFrom: range[0],
|
||||||
|
customTo: range[1],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNewBox = () => {
|
||||||
|
const box = {
|
||||||
|
id: new Date().getTime().toString(),
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
w: GRID_WIDTH,
|
||||||
|
h: GRID_H_SNAP * 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
const otherBoxes = boxes.map((b) => {
|
||||||
|
b.y += 200
|
||||||
|
|
||||||
|
return b
|
||||||
|
})
|
||||||
|
|
||||||
|
setBoxes(() => [box, ...otherBoxes])
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-head">
|
<div className="dashboard-head">
|
||||||
{verticalMode && (
|
{verticalMode && (
|
||||||
<>
|
<>
|
||||||
<button className="icon" onClick={onNewBox}>
|
<button className="icon" onClick={handleNewBox}>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
</button>
|
</button>
|
||||||
<button className="icon" onClick={onRefresh}>
|
<button className="icon" onClick={handleRefresh}>
|
||||||
<RefreshIcon />
|
<RefreshIcon />
|
||||||
</button>
|
</button>
|
||||||
<div className="filter-button" onClick={() => setFiltersShown(true)}>
|
<div className="filter-button" onClick={() => setFiltersShown(true)}>
|
||||||
|
|
@ -47,11 +75,11 @@ export const DashboardHeader = ({ onNewBox, onRefresh }: Props) => {
|
||||||
{!verticalMode && (
|
{!verticalMode && (
|
||||||
<>
|
<>
|
||||||
<DashboardSwitch />
|
<DashboardSwitch />
|
||||||
<button onClick={onNewBox}>Add box</button>
|
<button onClick={handleNewBox}>Add box</button>
|
||||||
<div className="spacer" />
|
<div className="spacer" />
|
||||||
<DashboardFilters />
|
<DashboardFilters />
|
||||||
<div className="spacer" />
|
<div className="spacer" />
|
||||||
<button onClick={onRefresh}>
|
<button onClick={handleRefresh}>
|
||||||
<RefreshIcon /> Refresh all
|
<RefreshIcon /> Refresh all
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
import {
|
||||||
|
createDashboard,
|
||||||
|
DashboardInfo,
|
||||||
|
updateDashboard,
|
||||||
|
} from '@/api/dashboards'
|
||||||
|
import { Modal } from '@/components/Modal'
|
||||||
|
import { createDashboardContent } from '@/utils/createDashboardContent'
|
||||||
|
import { useForm } from '@/utils/hooks/useForm'
|
||||||
|
import { useMutation, useQueryClient } from 'react-query'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
dashboard?: DashboardInfo
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DashboardModal = ({ dashboard, onClose }: Props) => {
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const createMutation = useMutation(createDashboard)
|
||||||
|
const updateMutation = useMutation(updateDashboard)
|
||||||
|
|
||||||
|
const { handleSubmit, register } = useForm({
|
||||||
|
defaultValue: () => ({
|
||||||
|
name: dashboard?.name ?? '',
|
||||||
|
}),
|
||||||
|
onSubmit: async (v) => {
|
||||||
|
if (updateMutation.isLoading || createMutation.isLoading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dashboard) {
|
||||||
|
await updateMutation.mutateAsync({
|
||||||
|
...dashboard,
|
||||||
|
name: v.name,
|
||||||
|
contents: JSON.parse(dashboard.contents),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await createMutation.mutateAsync({
|
||||||
|
name: v.name,
|
||||||
|
contents: createDashboardContent(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
queryClient.invalidateQueries(['/dashboards'])
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open onClose={onClose}>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="input">
|
||||||
|
<label>Name</label>
|
||||||
|
<input type="text" {...register('name')} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="actions">
|
||||||
|
<button className="cancel" onClick={onClose} type="button">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button>Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,27 +1,45 @@
|
||||||
import { getDashboards } from '@/api/dashboards'
|
import { DashboardInfo, getDashboards } from '@/api/dashboards'
|
||||||
import { PlusIcon } from '@/icons'
|
import { EditIcon, PlusIcon } from '@/icons'
|
||||||
|
import { useDashboardContext } from '@/pages/dashboard/contexts/DashboardContext'
|
||||||
import { useState } from 'preact/hooks'
|
import { useState } from 'preact/hooks'
|
||||||
import { useQuery } from 'react-query'
|
import { useQuery } from 'react-query'
|
||||||
|
import { DashboardModal } from './DashboardModal'
|
||||||
|
|
||||||
export const DashboardSwitch = () => {
|
export const DashboardSwitch = () => {
|
||||||
const [dashboard, setDashboard] = useState<string>()
|
const { dashboardId, setDashboardId, dashboard } = useDashboardContext()
|
||||||
|
const [showNew, setShowNew] = useState(false)
|
||||||
|
const [edited, setEdited] = useState<DashboardInfo>()
|
||||||
|
|
||||||
const dashboards = useQuery(['/dashboards'], getDashboards)
|
const dashboards = useQuery(['/dashboards'], getDashboards)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-switch">
|
<div className="dashboard-switch">
|
||||||
<select
|
<select
|
||||||
onChange={(e) => setDashboard(e.currentTarget.value)}
|
onChange={(e) => setDashboardId(+e.currentTarget.value)}
|
||||||
value={dashboard}
|
value={dashboardId}
|
||||||
>
|
>
|
||||||
{dashboards.data?.map((d) => (
|
{dashboards.data?.map((d) => (
|
||||||
<option key={d.id}>{d.id}</option>
|
<option key={d.id} value={d.id}>
|
||||||
|
{d.name}
|
||||||
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<button>
|
<button onClick={() => setEdited(dashboard)}>
|
||||||
|
<EditIcon /> Edit
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button onClick={() => setShowNew(true)}>
|
||||||
<PlusIcon /> Add
|
<PlusIcon /> Add
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
{showNew && <DashboardModal onClose={() => setShowNew(false)} />}
|
||||||
|
{edited && (
|
||||||
|
<DashboardModal
|
||||||
|
dashboard={edited}
|
||||||
|
onClose={() => setEdited(undefined)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,31 @@
|
||||||
|
import { DashboardInfo, getDashboard, updateDashboard } from '@/api/dashboards'
|
||||||
|
import { parseDashboard } from '@/utils/dashboard/parseDashboard'
|
||||||
import { useViewportSize } from '@/utils/hooks/useViewportSize'
|
import { useViewportSize } from '@/utils/hooks/useViewportSize'
|
||||||
import { intervalToRange } from '@/utils/intervalToRange'
|
import { intervalToRange } from '@/utils/intervalToRange'
|
||||||
import { ComponentChild, createContext } from 'preact'
|
import { ComponentChild, createContext } from 'preact'
|
||||||
import { StateUpdater, useContext, useMemo, useState } from 'preact/hooks'
|
import {
|
||||||
|
StateUpdater,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'preact/hooks'
|
||||||
|
import { useQuery } from 'react-query'
|
||||||
import { FilterValue } from '../components/DashboardHeader/components/DashboardFilters'
|
import { FilterValue } from '../components/DashboardHeader/components/DashboardFilters'
|
||||||
|
import { BoxDefinition } from '../types'
|
||||||
|
|
||||||
type DashboardContextType = {
|
type DashboardContextType = {
|
||||||
filter: FilterValue
|
filter: FilterValue
|
||||||
setFilter: StateUpdater<FilterValue>
|
setFilter: StateUpdater<FilterValue>
|
||||||
verticalMode: boolean
|
verticalMode: boolean
|
||||||
|
dashboardId: number
|
||||||
|
setDashboardId: StateUpdater<number>
|
||||||
|
boxes: BoxDefinition[]
|
||||||
|
setBoxes: (cb: (previous: BoxDefinition[]) => BoxDefinition[]) => void
|
||||||
|
isDashboardSelected: boolean
|
||||||
|
isDashboardLoading: boolean
|
||||||
|
dashboard: DashboardInfo | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const DashboardContext = createContext<DashboardContextType | null>(null)
|
const DashboardContext = createContext<DashboardContextType | null>(null)
|
||||||
|
|
@ -19,6 +37,46 @@ export const DashboardContextProvider = ({
|
||||||
}) => {
|
}) => {
|
||||||
const viewport = useViewportSize()
|
const viewport = useViewportSize()
|
||||||
|
|
||||||
|
const [dashboardId, setDashboardId] = useState(-1)
|
||||||
|
const isDashboardSelected = !isNaN(dashboardId) && dashboardId >= 0
|
||||||
|
|
||||||
|
const dashboard = useQuery(
|
||||||
|
['/dashboards', dashboardId],
|
||||||
|
() => getDashboard(dashboardId),
|
||||||
|
{ enabled: isDashboardSelected }
|
||||||
|
)
|
||||||
|
|
||||||
|
const dashboardContent = useMemo(
|
||||||
|
() =>
|
||||||
|
dashboard.data ? parseDashboard(dashboard.data.contents) : undefined,
|
||||||
|
[dashboard.data]
|
||||||
|
)
|
||||||
|
|
||||||
|
const [boxes, setBoxes] = useState(dashboardContent?.boxes ?? [])
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(cb: (boxes: BoxDefinition[]) => BoxDefinition[]) => {
|
||||||
|
const newBoxes = cb(boxes)
|
||||||
|
|
||||||
|
setBoxes(newBoxes)
|
||||||
|
|
||||||
|
if (dashboard.data && dashboardContent) {
|
||||||
|
updateDashboard({
|
||||||
|
...dashboard.data,
|
||||||
|
contents: {
|
||||||
|
...dashboardContent,
|
||||||
|
boxes: newBoxes,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[dashboard.data]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setBoxes(dashboardContent?.boxes ?? [])
|
||||||
|
}, [dashboardContent])
|
||||||
|
|
||||||
const [filter, setFilter] = useState<FilterValue>(() => {
|
const [filter, setFilter] = useState<FilterValue>(() => {
|
||||||
const range = intervalToRange('week', new Date(), new Date())
|
const range = intervalToRange('week', new Date(), new Date())
|
||||||
|
|
||||||
|
|
@ -28,8 +86,29 @@ export const DashboardContextProvider = ({
|
||||||
const verticalMode = viewport.width < 800
|
const verticalMode = viewport.width < 800
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({ filter, setFilter, verticalMode }),
|
() => ({
|
||||||
[filter, verticalMode]
|
filter,
|
||||||
|
setFilter,
|
||||||
|
verticalMode,
|
||||||
|
dashboardId,
|
||||||
|
setDashboardId,
|
||||||
|
boxes,
|
||||||
|
setBoxes: handleChange,
|
||||||
|
isDashboardSelected,
|
||||||
|
isDashboardLoading: dashboard.isLoading,
|
||||||
|
dashboard: dashboard.data,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
filter,
|
||||||
|
verticalMode,
|
||||||
|
dashboardId,
|
||||||
|
setDashboardId,
|
||||||
|
boxes,
|
||||||
|
handleChange,
|
||||||
|
isDashboardSelected,
|
||||||
|
dashboard.isLoading,
|
||||||
|
dashboard.data,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
ALTER TABLE dashboards RENAME TO dashboards_old;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dashboards (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
contents TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO dashboards (name, contents)
|
||||||
|
SELECT id, contents FROM dashboards_old;
|
||||||
|
|
||||||
|
DROP TABLE dashboards_old;
|
||||||
|
|
@ -3,16 +3,19 @@ package routes
|
||||||
import (
|
import (
|
||||||
"basic-sensor-receiver/app"
|
"basic-sensor-receiver/app"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type postDashboardBody struct {
|
type postDashboardBody struct {
|
||||||
Id string `json:"id"`
|
Id int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
Contents string `json:"contents"`
|
Contents string `json:"contents"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type putDashboardBody struct {
|
type putDashboardBody struct {
|
||||||
|
Name string `json:"name"`
|
||||||
Contents string `json:"contents"`
|
Contents string `json:"contents"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +34,11 @@ func GetDashboards(s *app.Server) gin.HandlerFunc {
|
||||||
|
|
||||||
func GetDashboardById(s *app.Server) gin.HandlerFunc {
|
func GetDashboardById(s *app.Server) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id, err := getIntParam(c, "id")
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
item, err := s.Services.Dashboards.GetById(id)
|
item, err := s.Services.Dashboards.GetById(id)
|
||||||
|
|
||||||
|
|
@ -53,7 +60,7 @@ func PostDashboard(s *app.Server) gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
item, err := s.Services.Dashboards.Create(body.Id, body.Contents)
|
item, err := s.Services.Dashboards.Create(body.Id, body.Name, body.Contents)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithError(500, err)
|
c.AbortWithError(500, err)
|
||||||
|
|
@ -66,7 +73,12 @@ func PostDashboard(s *app.Server) gin.HandlerFunc {
|
||||||
|
|
||||||
func PutDashboard(s *app.Server) gin.HandlerFunc {
|
func PutDashboard(s *app.Server) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id, err := getIntParam(c, "id")
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
body := putDashboardBody{}
|
body := putDashboardBody{}
|
||||||
|
|
||||||
if err := c.BindJSON(&body); err != nil {
|
if err := c.BindJSON(&body); err != nil {
|
||||||
|
|
@ -74,7 +86,7 @@ func PutDashboard(s *app.Server) gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
item, err := s.Services.Dashboards.Update(id, body.Contents)
|
item, err := s.Services.Dashboards.Update(id, body.Name, body.Contents)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.AbortWithError(500, err)
|
c.AbortWithError(500, err)
|
||||||
|
|
@ -84,3 +96,9 @@ func PutDashboard(s *app.Server) gin.HandlerFunc {
|
||||||
c.JSON(http.StatusOK, item)
|
c.JSON(http.StatusOK, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getIntParam(c *gin.Context, key string) (int64, error) {
|
||||||
|
value := c.Param(key)
|
||||||
|
|
||||||
|
return strconv.ParseInt(value, 10, 64)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,15 @@ type DashboardsService struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DashboardItem struct {
|
type DashboardItem struct {
|
||||||
Id string `json:"id"`
|
Id int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
Contents string `json:"contents"`
|
Contents string `json:"contents"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DashboardsService) GetList() ([]DashboardItem, error) {
|
func (s *DashboardsService) GetList() ([]DashboardItem, error) {
|
||||||
items := make([]DashboardItem, 0)
|
items := make([]DashboardItem, 0)
|
||||||
|
|
||||||
rows, err := s.ctx.DB.Query("SELECT id, contents FROM dashboards")
|
rows, err := s.ctx.DB.Query("SELECT id, name, contents FROM dashboards")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -23,7 +24,7 @@ func (s *DashboardsService) GetList() ([]DashboardItem, error) {
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
item := DashboardItem{}
|
item := DashboardItem{}
|
||||||
|
|
||||||
err := rows.Scan(&item.Id, &item.Contents)
|
err := rows.Scan(&item.Id, &item.Name, &item.Contents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -39,13 +40,14 @@ func (s *DashboardsService) GetList() ([]DashboardItem, error) {
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DashboardsService) Create(id string, contents string) (*DashboardItem, error) {
|
func (s *DashboardsService) Create(id int64, name string, contents string) (*DashboardItem, error) {
|
||||||
item := DashboardItem{
|
item := DashboardItem{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
Name: name,
|
||||||
Contents: contents,
|
Contents: contents,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.ctx.DB.Exec("INSERT INTO dashboards (id, contents) VALUES (?, ?)", item.Id, item.Contents)
|
_, err := s.ctx.DB.Exec("INSERT INTO dashboards (id, name, contents) VALUES (?, ?, ?)", item.Id, item.Name, item.Contents)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -54,13 +56,14 @@ func (s *DashboardsService) Create(id string, contents string) (*DashboardItem,
|
||||||
return &item, nil
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DashboardsService) Update(id string, contents string) (*DashboardItem, error) {
|
func (s *DashboardsService) Update(id int64, name string, contents string) (*DashboardItem, error) {
|
||||||
item := DashboardItem{
|
item := DashboardItem{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
Name: name,
|
||||||
Contents: contents,
|
Contents: contents,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.ctx.DB.Exec("UPDATE dashboards SET contents = ? WHERE id = ?", contents, id)
|
_, err := s.ctx.DB.Exec("UPDATE dashboards SET contents = ?, name = ? WHERE id = ?", item.Contents, item.Name, item.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -69,12 +72,12 @@ func (s *DashboardsService) Update(id string, contents string) (*DashboardItem,
|
||||||
return &item, nil
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DashboardsService) GetById(id string) (*DashboardItem, error) {
|
func (s *DashboardsService) GetById(id int64) (*DashboardItem, error) {
|
||||||
item := DashboardItem{}
|
item := DashboardItem{}
|
||||||
|
|
||||||
row := s.ctx.DB.QueryRow("SELECT id, contents FROM dashboards WHERE id = ?", id)
|
row := s.ctx.DB.QueryRow("SELECT id, name, contents FROM dashboards WHERE id = ?", id)
|
||||||
|
|
||||||
err := row.Scan(&item.Id, &item.Contents)
|
err := row.Scan(&item.Id, &item.Name, &item.Contents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue