207 lines
5.1 KiB
JavaScript
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
|