207 lines
5.1 KiB
JavaScript

import { useCallback, useContext, useEffect, useState, useRef } from 'react'
import PropTypes from 'prop-types'
import {
Card,
Button,
Space,
Typography,
Flex,
Modal,
Spin,
Alert,
Dropdown
} from 'antd'
import { LoadingOutlined } from '@ant-design/icons'
import PlusIcon from '../../Icons/PlusIcon'
import { AuthContext } from '../context/AuthContext'
import { ApiServerContext } from '../context/ApiServerContext'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import ReloadIcon from '../../Icons/ReloadIcon'
import NewNote from '../Management/Notes/NewNote'
import NoteItem from './NoteItem'
const { Text } = Typography
const NotesPanel = ({ _id, type }) => {
const [newNoteOpen, setNewNoteOpen] = useState(false)
const [loading, setLoading] = useState(true)
const [initialized, setInitialized] = useState(false)
const [error, setError] = useState(null)
const [notes, setNotes] = useState(null)
const [expandedNotes, setExpandedNotes] = useState({})
const subscribeToObjectTypeUpdatesRef = useRef(null)
const { token } = useContext(AuthContext)
const { fetchNotes, connected, subscribeToObjectTypeUpdates } =
useContext(ApiServerContext)
const fetchData = useCallback(
async (id) => {
try {
const newData = await fetchNotes(id)
return newData
} catch (error) {
setError(error)
return null
}
},
[fetchNotes]
)
const generateNotes = useCallback(
async (id) => {
const notesData = await fetchData(id)
setLoading(false)
if (notesData == null) {
return null
}
if (notesData.length <= 0) {
return (
<Card>
<Flex
justify='center'
gap={'small'}
style={{ height: '100%' }}
align='center'
>
<Text type='secondary'>
<InfoCircleIcon />
</Text>
<Text type='secondary'>No notes added.</Text>
</Flex>
</Card>
)
}
return notesData.map((note) => (
<NoteItem
key={note._id}
note={note}
expandedNotes={expandedNotes}
setExpandedNotes={setExpandedNotes}
/>
))
},
[fetchData, expandedNotes]
)
const handleReloadData = useCallback(async () => {
setNotes(await generateNotes(_id))
}, [_id, generateNotes])
useEffect(() => {
if (connected == true && subscribeToObjectTypeUpdatesRef.current == null) {
subscribeToObjectTypeUpdatesRef.current = subscribeToObjectTypeUpdates(
'note',
(noteData) => {
if (noteData.parent._id == _id) {
handleReloadData()
}
}
)
}
return () => {
if (connected == true && subscribeToObjectTypeUpdatesRef.current) {
subscribeToObjectTypeUpdatesRef.current()
subscribeToObjectTypeUpdatesRef.current = null
}
}
}, [_id, subscribeToObjectTypeUpdates, connected, handleReloadData])
useEffect(() => {
if (connected == true && token != null && !initialized) {
handleReloadData()
setInitialized(true)
}
}, [token, handleReloadData, initialized, connected])
const actionItems = {
items: [
{
label: 'New Note',
key: 'newNote',
icon: <PlusIcon />
},
{ type: 'divider' },
{
label: 'Reload Notes',
key: 'reloadNotes',
icon: <ReloadIcon />
}
],
onClick: ({ key }) => {
if (key === 'reloadNotes') {
setLoading(true)
handleReloadData()
} else if (key === 'newNote') {
setNewNoteOpen(true)
}
}
}
return (
<Flex vertical gap='large' style={{ width: '100%' }}>
<Flex justify='space-between'>
<Space size={'small'}>
<Dropdown menu={actionItems} disabled={loading}>
<Button disabled={loading}>Actions</Button>
</Dropdown>
</Space>
<Space size={'small'}>
<Button
type='primary'
icon={<PlusIcon />}
disabled={loading}
onClick={() => {
setNewNoteOpen(true)
}}
/>
</Space>
</Flex>
<Space direction='vertical' size='middle' style={{ width: '100%' }}>
<Spin indicator={<LoadingOutlined />} spinning={loading}>
{error ? (
<Alert message={error?.message} type='error' showIcon={true} />
) : (
<Flex vertical gap={'middle'}>
{notes}
</Flex>
)}
</Spin>
</Space>
<Modal
open={newNoteOpen}
width={800}
destroyOnHidden={true}
footer={null}
onCancel={() => {
setNewNoteOpen(false)
}}
>
<NewNote
onOk={() => {
setNewNoteOpen(false)
}}
reset={newNoteOpen}
defaultValues={{
parent: { _id },
parentType: type
}}
/>
</Modal>
</Flex>
)
}
NotesPanel.propTypes = {
_id: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
onNewNote: PropTypes.func
}
export default NotesPanel