Add Sensor Hub functionality with components for managing sensors, including a service for API calls, a modal for selection, and a form for adding new sensors. Updated layout to integrate SensorHub component.
This commit is contained in:
@@ -11,6 +11,7 @@ import HorizontalLayout from '@layouts/HorizontalLayout'
|
||||
|
||||
// Component Imports
|
||||
import Providers from '@components/Providers'
|
||||
import SensorHub from '@components/SensorHub'
|
||||
import Navigation from '@components/layout/vertical/Navigation'
|
||||
import Header from '@components/layout/horizontal/Header'
|
||||
import Navbar from '@components/layout/vertical/Navbar'
|
||||
@@ -31,6 +32,7 @@ const Layout = async (props: ChildrenType) => {
|
||||
return (
|
||||
<Providers direction={direction}>
|
||||
<AuthGuard>
|
||||
<SensorHub>
|
||||
<LayoutWrapper
|
||||
systemMode={systemMode}
|
||||
verticalLayout={
|
||||
@@ -55,6 +57,7 @@ const Layout = async (props: ChildrenType) => {
|
||||
<i className='tabler-arrow-up' />
|
||||
</Button>
|
||||
</ScrollToTop>
|
||||
</SensorHub>
|
||||
</AuthGuard>
|
||||
</Providers>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useEffect, type ReactNode } from 'react'
|
||||
|
||||
// Hook Imports
|
||||
import { useSensorHub } from '@/hooks/useSensorHub'
|
||||
import { Box } from '@mui/material'
|
||||
import SensorHubView from '@/views/sensorHub'
|
||||
interface SensorHubProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function SensorHub({ children }: SensorHubProps) {
|
||||
const { hasSensorHub } = useSensorHub()
|
||||
|
||||
|
||||
return <> {children}
|
||||
{!hasSensorHub && <SensorHubView />}</>
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
|
||||
const SENSOR_HUB_STORAGE_KEY = 'sensor_hub'
|
||||
|
||||
export interface SensorHubInfo {
|
||||
id: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface UseSensorHubReturn {
|
||||
/** Sensor hub data from localStorage */
|
||||
sensorHub: SensorHubInfo | null
|
||||
/** Whether sensor_hub exists in localStorage */
|
||||
hasSensorHub: boolean
|
||||
/** Save sensor hub to localStorage */
|
||||
setSensorHub: (data: SensorHubInfo | null) => void
|
||||
/** Get headers to attach to API requests (e.g. X-Sensor-Hub-Id) */
|
||||
getSensorHubHeaders: () => Record<string, string>
|
||||
}
|
||||
|
||||
const parseSensorHub = (raw: string | null): SensorHubInfo | null => {
|
||||
if (!raw) return null
|
||||
try {
|
||||
const parsed = JSON.parse(raw)
|
||||
if (parsed && typeof parsed === 'object' && typeof parsed.id === 'string') {
|
||||
return parsed as SensorHubInfo
|
||||
}
|
||||
} catch {
|
||||
// ignore invalid JSON
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export const useSensorHub = (): UseSensorHubReturn => {
|
||||
const [sensorHub, setSensorHubState] = useState<SensorHubInfo | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
const stored = localStorage.getItem(SENSOR_HUB_STORAGE_KEY)
|
||||
setSensorHubState(parseSensorHub(stored))
|
||||
}, [])
|
||||
|
||||
const setSensorHub = useCallback((data: SensorHubInfo | null) => {
|
||||
if (typeof window === 'undefined') return
|
||||
if (data === null) {
|
||||
localStorage.removeItem(SENSOR_HUB_STORAGE_KEY)
|
||||
setSensorHubState(null)
|
||||
} else {
|
||||
localStorage.setItem(SENSOR_HUB_STORAGE_KEY, JSON.stringify(data))
|
||||
setSensorHubState(data)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const getSensorHubHeaders = useCallback((): Record<string, string> => {
|
||||
const hub = sensorHub ?? (typeof window !== 'undefined' ? parseSensorHub(localStorage.getItem(SENSOR_HUB_STORAGE_KEY)) : null)
|
||||
if (!hub?.id) return {}
|
||||
return {
|
||||
'X-Sensor-Hub-Id': hub.id
|
||||
}
|
||||
}, [sensorHub])
|
||||
|
||||
return {
|
||||
sensorHub,
|
||||
hasSensorHub: sensorHub !== null,
|
||||
setSensorHub,
|
||||
getSensorHubHeaders
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,5 @@ export * from './services/kanbanService'
|
||||
export * from './services/todoService'
|
||||
export * from './services/userManagementService'
|
||||
export * from './services/rolesPermissionsService'
|
||||
export * from './services/sensorHubService'
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Sensor Hub Service
|
||||
* Handles sensor hub API calls
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client'
|
||||
|
||||
export interface Sensor {
|
||||
name: string
|
||||
uuid_sensor: string
|
||||
last_updated: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface ListSensorsResponse {
|
||||
status?: string
|
||||
data: Sensor | Sensor[]
|
||||
}
|
||||
|
||||
export const sensorHubService = {
|
||||
/**
|
||||
* Get list of sensors
|
||||
*/
|
||||
async listSensors(): Promise<Sensor[]> {
|
||||
const response = await apiClient.get<ListSensorsResponse>('/api/sensor-hub/')
|
||||
const data = response?.data
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
return data
|
||||
}
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
return [data as Sensor]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import Typography from '@mui/material/Typography'
|
||||
|
||||
// Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
type FormSensorHubProps = {
|
||||
onBack: () => void
|
||||
}
|
||||
|
||||
const FormSensorHub = ({ onBack }: FormSensorHubProps) => {
|
||||
const [name, setName] = useState('')
|
||||
const [uuidSensor, setUuidSensor] = useState('')
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
// TODO: Call API to add sensor
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='flex items-center gap-2 mbe-2'>
|
||||
<Button
|
||||
variant='text'
|
||||
color='secondary'
|
||||
size='small'
|
||||
startIcon={<i className='tabler-arrow-right text-xl' />}
|
||||
onClick={onBack}
|
||||
>
|
||||
بازگشت
|
||||
</Button>
|
||||
{/* <Typography variant='h6'>افزودن سنسور جدید</Typography> */}
|
||||
</div>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Grid container spacing={4}>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='نام سنسور'
|
||||
placeholder='نام سنسور را وارد کنید'
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }}>
|
||||
<CustomTextField
|
||||
fullWidth
|
||||
label='شناسه سنسور (UUID)'
|
||||
placeholder='شناسه سنسور را وارد کنید'
|
||||
value={uuidSensor}
|
||||
onChange={e => setUuidSensor(e.target.value)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12 }} className='flex gap-2'>
|
||||
<Button variant='tonal' color='secondary' onClick={onBack}>
|
||||
انصراف
|
||||
</Button>
|
||||
<Button variant='contained' type='submit' startIcon={<i className='tabler-plus' />}>
|
||||
ذخیره سنسور
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormSensorHub
|
||||
@@ -0,0 +1,141 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState, useEffect } from 'react'
|
||||
import type { ChangeEvent } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Grid from '@mui/material/Grid2'
|
||||
import Button from '@mui/material/Button'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
|
||||
// Third-party Imports
|
||||
import DateObject from 'react-date-object'
|
||||
|
||||
// Type Imports
|
||||
import type { CustomInputHorizontalData } from '@core/components/custom-inputs/types'
|
||||
|
||||
// API Imports
|
||||
import { sensorHubService } from '@/libs/api'
|
||||
import type { Sensor } from '@/libs/api/services/sensorHubService'
|
||||
|
||||
// Component Imports
|
||||
import CustomInputHorizontal from '@core/components/custom-inputs/Horizontal'
|
||||
|
||||
type OptionSensorHubProps = {
|
||||
onConfirm?: (sensor: Sensor) => void
|
||||
}
|
||||
|
||||
const formatLastUpdated = (dateStr: string | null | undefined): string => {
|
||||
if (!dateStr) return '—'
|
||||
try {
|
||||
const d = new DateObject(new Date(dateStr)).convert('persian').setLocale('fa')
|
||||
|
||||
return d.format('YYYY/MM/DD')
|
||||
} catch {
|
||||
return dateStr
|
||||
}
|
||||
}
|
||||
|
||||
const sensorToOption = (sensor: Sensor, isFirst: boolean): CustomInputHorizontalData => ({
|
||||
title: sensor.name,
|
||||
meta: formatLastUpdated(sensor.last_updated),
|
||||
content: sensor.uuid_sensor,
|
||||
value: sensor.uuid_sensor,
|
||||
isSelected: isFirst
|
||||
})
|
||||
|
||||
const OptionSensorHub = ({ onConfirm }: OptionSensorHubProps) => {
|
||||
const [sensors, setSensors] = useState<Sensor[]>([])
|
||||
const [data, setData] = useState<CustomInputHorizontalData[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [selectedOption, setSelectedOption] = useState<string>('')
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSensors = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const sensorsList = await sensorHubService.listSensors()
|
||||
setSensors(sensorsList)
|
||||
const options = sensorsList.map((s, i) => sensorToOption(s, i === 0))
|
||||
setData(options)
|
||||
if (options.length > 0) {
|
||||
const selected = options.find(o => o.isSelected) ?? options[0]
|
||||
|
||||
setSelectedOption(selected.value)
|
||||
}
|
||||
} catch {
|
||||
setSensors([])
|
||||
setData([])
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchSensors()
|
||||
}, [])
|
||||
|
||||
const handleOptionChange = (prop: string | ChangeEvent<HTMLInputElement>) => {
|
||||
if (typeof prop === 'string') {
|
||||
setSelectedOption(prop)
|
||||
} else {
|
||||
setSelectedOption((prop.target as HTMLInputElement).value)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className='flex items-center justify-center py-12'>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
const selected = sensors.find(s => s.uuid_sensor === selectedOption)
|
||||
if (selected && onConfirm) {
|
||||
onConfirm(selected)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={e => e.preventDefault()}>
|
||||
<Grid container>
|
||||
{data.map(item => (
|
||||
<CustomInputHorizontal
|
||||
type='radio'
|
||||
key={item.value}
|
||||
data={item}
|
||||
gridProps={{
|
||||
size: { xs: 12 },
|
||||
className:
|
||||
'[&:first-of-type>*]:rounded-be-none [&:last-of-type>*]:rounded-bs-none [&:nth-of-type(2)>*]:rounded-none'
|
||||
}}
|
||||
selected={selectedOption}
|
||||
name='sensor-hub-option'
|
||||
handleChange={handleOptionChange}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
{onConfirm && (
|
||||
<div className='flex justify-end mts-4'>
|
||||
<Button
|
||||
variant='contained'
|
||||
sx={{mt:4}}
|
||||
color='primary'
|
||||
onClick={handleConfirm}
|
||||
startIcon={<i className='tabler-check text-xl' />}
|
||||
>
|
||||
تایید
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
export default OptionSensorHub
|
||||
@@ -0,0 +1,133 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Card from '@mui/material/Card'
|
||||
import CardHeader from '@mui/material/CardHeader'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
|
||||
// Third-party Imports
|
||||
import { createColumnHelper, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'
|
||||
import DateObject from 'react-date-object'
|
||||
|
||||
// API Imports
|
||||
import { sensorHubService } from '@/libs/api'
|
||||
import type { Sensor } from '@/libs/api/services/sensorHubService'
|
||||
|
||||
// Style Imports
|
||||
import styles from '@core/styles/table.module.css'
|
||||
|
||||
const formatToShamsi = (dateStr: string | null | undefined): string => {
|
||||
if (!dateStr) return '—'
|
||||
try {
|
||||
const d = new DateObject(new Date(dateStr)).convert('persian').setLocale('fa')
|
||||
|
||||
return d.format('YYYY/MM/DD')
|
||||
} catch {
|
||||
return dateStr
|
||||
}
|
||||
}
|
||||
|
||||
// Column Definitions
|
||||
const columnHelper = createColumnHelper<Sensor>()
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor('name', {
|
||||
cell: info => info.getValue(),
|
||||
header: 'Name'
|
||||
}),
|
||||
columnHelper.accessor('last_updated', {
|
||||
cell: info => formatToShamsi(info.getValue()),
|
||||
header: 'Last Update'
|
||||
}),
|
||||
columnHelper.accessor('uuid_sensor', {
|
||||
cell: info => info.getValue(),
|
||||
header: 'UUID'
|
||||
})
|
||||
]
|
||||
|
||||
const SensorHubTable = () => {
|
||||
const [data, setData] = useState<Sensor[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSensors = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
const sensors = await sensorHubService.listSensors()
|
||||
setData(sensors)
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to load sensors')
|
||||
setData([])
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchSensors()
|
||||
}, [])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
filterFns: {
|
||||
fuzzy: () => false
|
||||
}
|
||||
})
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Sensor Hub' />
|
||||
<div className='flex items-center justify-center p-12'>
|
||||
<CircularProgress />
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Sensor Hub' subheader={error} />
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader title='Sensor Hub' />
|
||||
<div className='overflow-x-auto'>
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th key={header.id}>
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map(row => (
|
||||
<tr key={row.id}>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default SensorHubTable
|
||||
@@ -0,0 +1,194 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
import type { Theme } from '@mui/material/styles'
|
||||
|
||||
// Hook Imports
|
||||
import { useSensorHub } from '@/hooks/useSensorHub'
|
||||
|
||||
// API Imports
|
||||
import type { Sensor } from '@/libs/api/services/sensorHubService'
|
||||
import Dialog from '@mui/material/Dialog'
|
||||
import DialogContent from '@mui/material/DialogContent'
|
||||
import DialogTitle from '@mui/material/DialogTitle'
|
||||
import Drawer from '@mui/material/Drawer'
|
||||
import Button from '@mui/material/Button'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import useMediaQuery from '@mui/material/useMediaQuery'
|
||||
import Fade from '@mui/material/Fade'
|
||||
|
||||
// Component Imports
|
||||
import OptionSensorHub from './OptionSensorHub'
|
||||
import FormSensorHub from './FormSensorHub'
|
||||
|
||||
type TableModalSheetProps = {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const backdropBlurSx = { backdropFilter: 'blur(4px)' }
|
||||
const transitionTimeout = { enter: 300, exit: 200 }
|
||||
|
||||
const DialogContentWithTransition = ({
|
||||
showAddForm,
|
||||
onShowAddForm,
|
||||
onBack,
|
||||
onConfirm
|
||||
}: {
|
||||
showAddForm: boolean
|
||||
onShowAddForm: () => void
|
||||
onBack: () => void
|
||||
onConfirm: (sensor: Sensor) => void
|
||||
}) => (
|
||||
<Fade key={showAddForm ? 'form' : 'options'} in timeout={transitionTimeout}>
|
||||
<div>
|
||||
{showAddForm ? (
|
||||
<FormSensorHub onBack={onBack} />
|
||||
) : (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='grid grid-cols-[1fr_auto] items-center gap-4'>
|
||||
<div className='flex flex-col gap-0.5'>
|
||||
<Typography variant='h6' fontWeight={600}>
|
||||
انتخاب سنسور
|
||||
</Typography>
|
||||
<Typography variant='body2' color='text.secondary' sx={{ lineHeight: 1.5 }}>
|
||||
سنسور مورد نظر را انتخاب کنید یا سنسور جدید اضافه کنید
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant='contained'
|
||||
color='primary'
|
||||
startIcon={<i className='tabler-plus text-xl' />}
|
||||
onClick={onShowAddForm}
|
||||
>
|
||||
اضافه کردن سنسور
|
||||
</Button>
|
||||
</div>
|
||||
<OptionSensorHub onConfirm={onConfirm} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Fade>
|
||||
)
|
||||
|
||||
const DrawerContentWithTransition = ({
|
||||
showAddForm,
|
||||
onShowAddForm,
|
||||
onBack,
|
||||
onConfirm
|
||||
}: {
|
||||
showAddForm: boolean
|
||||
onShowAddForm: () => void
|
||||
onBack: () => void
|
||||
onConfirm: (sensor: Sensor) => void
|
||||
}) => (
|
||||
<Fade key={showAddForm ? 'form' : 'options'} in timeout={transitionTimeout}>
|
||||
<div>
|
||||
{showAddForm ? (
|
||||
<FormSensorHub onBack={onBack} />
|
||||
) : (
|
||||
<div className='flex flex-col gap-4'>
|
||||
<div className='grid grid-cols-[1fr_auto] items-center gap-4'>
|
||||
<div className='flex flex-col gap-0.5'>
|
||||
<Typography variant='h6' fontWeight={600}>
|
||||
انتخاب سنسور
|
||||
</Typography>
|
||||
<Typography variant='body2' color='text.secondary' sx={{ lineHeight: 1.5 }}>
|
||||
سنسور مورد نظر را انتخاب کنید یا سنسور جدید اضافه کنید
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant='contained'
|
||||
color='primary'
|
||||
startIcon={<i className='tabler-plus text-xl' />}
|
||||
onClick={onShowAddForm}
|
||||
>
|
||||
اضافه کردن سنسور
|
||||
</Button>
|
||||
</div>
|
||||
<OptionSensorHub onConfirm={onConfirm} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Fade>
|
||||
)
|
||||
|
||||
const TableModalSheet = ({ open, onClose }: TableModalSheetProps) => {
|
||||
const [showAddForm, setShowAddForm] = useState(false)
|
||||
const isMobile = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'))
|
||||
const { setSensorHub } = useSensorHub()
|
||||
|
||||
const handleBack = () => setShowAddForm(false)
|
||||
|
||||
const handleConfirm = (sensor: Sensor) => {
|
||||
setSensorHub({ id: sensor.uuid_sensor, ...sensor })
|
||||
onClose()
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Drawer
|
||||
anchor='bottom'
|
||||
variant='temporary'
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
ModalProps={{ keepMounted: true }}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
sx: backdropBlurSx
|
||||
}
|
||||
}}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
maxHeight: '80vh',
|
||||
borderTopLeftRadius: 'var(--mui-shape-borderRadius)',
|
||||
borderTopRightRadius: 'var(--mui-shape-borderRadius)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className='flex items-center justify-between plb-4 pli-6 border-bs'>
|
||||
<Typography variant='h5'>Sensor Data</Typography>
|
||||
<IconButton size='small' onClick={onClose} aria-label='close'>
|
||||
<i className='tabler-x text-2xl' />
|
||||
</IconButton>
|
||||
</div>
|
||||
<div className='overflow-y-auto pli-6 plb-6' style={{ maxHeight: 'calc(80vh - 72px)' }}>
|
||||
<DrawerContentWithTransition
|
||||
showAddForm={showAddForm}
|
||||
onShowAddForm={() => setShowAddForm(true)}
|
||||
onBack={handleBack}
|
||||
onConfirm={handleConfirm}
|
||||
/>
|
||||
</div>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
fullWidth
|
||||
maxWidth='lg'
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
sx: backdropBlurSx
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
<DialogContent>
|
||||
<DialogContentWithTransition
|
||||
showAddForm={showAddForm}
|
||||
onShowAddForm={() => setShowAddForm(true)}
|
||||
onBack={handleBack}
|
||||
onConfirm={handleConfirm}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default TableModalSheet
|
||||
@@ -0,0 +1,22 @@
|
||||
'use client'
|
||||
|
||||
// React Imports
|
||||
import { useState } from 'react'
|
||||
|
||||
// MUI Imports
|
||||
import Button from '@mui/material/Button'
|
||||
|
||||
// Component Imports
|
||||
import TableModalSheet from './TableModalSheet'
|
||||
|
||||
const SensorHubView = () => {
|
||||
const [open, setOpen] = useState(true)
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableModalSheet open={open} onClose={() => setOpen(false)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SensorHubView
|
||||
Reference in New Issue
Block a user