datajunction-ui
Version:
DataJunction Metrics Platform UI
227 lines (209 loc) • 6.32 kB
JSX
import { useContext, useState, useRef, useEffect } from 'react';
import DJClientContext from '../../providers/djclient';
import EyeIcon from '../../icons/EyeIcon';
import ExpandedIcon from '../../icons/ExpandedIcon';
import CollapsedIcon from '../../icons/CollapsedIcon';
const EVENT_TYPES = ['delete', 'update'];
export default function WatchButton({ node }) {
if (!node || !node.name || !node.type) {
return null;
}
const djClient = useContext(DJClientContext).DataJunctionAPI;
const [selectedEvents, setSelectedEvents] = useState([]);
const [loading, setLoading] = useState(false);
const [dropdownOpen, setDropdownOpen] = useState(false);
const dropdownRef = useRef();
// Load existing preferences
useEffect(() => {
const loadPreferences = async () => {
try {
const preferences = await djClient.getNotificationPreferences({
entity_name: node.name,
});
const matched = preferences.find(item =>
item.alert_types.includes('web'),
);
if (matched) {
setSelectedEvents(matched.activity_types);
}
} catch (err) {
console.error('Failed to load notification preferences', err);
}
};
loadPreferences();
}, [djClient, node.name, node.type]);
// Close dropdown on outside click
useEffect(() => {
const handleClickOutside = event => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setDropdownOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const toggleEvent = async event => {
const isSelected = selectedEvents.includes(event);
try {
setLoading(true);
let updatedEvents;
if (!isSelected) {
updatedEvents = Array.from(new Set([...selectedEvents, event]));
} else {
updatedEvents = selectedEvents.filter(e => e !== event);
}
if (updatedEvents.length === 0) {
await djClient.unsubscribeFromNotifications({
entity_type: 'node',
entity_name: node.name,
});
} else {
await djClient.subscribeToNotifications({
entity_type: 'node',
entity_name: node.name,
activity_types: updatedEvents,
alert_types: ['web'],
});
}
setSelectedEvents(updatedEvents);
} catch (err) {
console.error('Failed to update preference', err);
} finally {
setLoading(false);
}
};
const handleWatchClick = async () => {
try {
setLoading(true);
if (selectedEvents.length === 0) {
await djClient.subscribeToNotifications({
entity_type: 'node',
entity_name: node.name,
activity_types: EVENT_TYPES,
alert_types: ['web'],
});
setSelectedEvents(EVENT_TYPES);
} else {
await djClient.unsubscribeFromNotifications({
entity_type: 'node',
entity_name: node.name,
});
setSelectedEvents([]);
}
} catch (err) {
console.error('Watch toggle failed', err);
} finally {
setLoading(false);
}
};
return (
<div
className="btn-group"
ref={dropdownRef}
style={{
position: 'relative',
display: 'inline-flex',
verticalAlign: 'middle',
}}
>
<button
className="button-3"
onClick={handleWatchClick}
disabled={loading}
style={{
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
marginRight: 0,
height: '2.5rem',
display: 'flex',
alignItems: 'center',
gap: '6px',
}}
>
<EyeIcon />
Watch
{selectedEvents.length > 0 && (
<span
style={{
backgroundColor: '#e2e6ed',
color: '#333',
padding: '2px 6px',
borderRadius: '999px',
fontSize: '0.75rem',
fontWeight: 500,
lineHeight: 1,
}}
>
{selectedEvents.length}
</span>
)}
</button>
<button
className="button-3"
onClick={() => setDropdownOpen(prev => !prev)}
disabled={loading}
style={{
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
marginLeft: 0,
height: '2.5rem',
}}
aria-label="Toggle dropdown"
>
{dropdownOpen ? <ExpandedIcon /> : <CollapsedIcon />}
</button>
{dropdownOpen && (
<ul
className="p-2"
style={{
display: 'block',
minWidth: '220px',
position: 'absolute',
top: '100%',
right: 0,
zIndex: 9999,
backgroundColor: '#fff',
border: '1px solid #ccc',
borderRadius: '0.5rem',
marginTop: '0.25rem',
boxShadow: '0px 6px 16px rgba(0, 0, 0, 0.1)',
padding: '0.5rem 0',
}}
>
{EVENT_TYPES.map(event => {
const isSelected = selectedEvents.includes(event);
return (
<li
key={event}
onClick={() => toggleEvent(event)}
style={{
display: 'flex',
alignItems: 'center',
padding: '0.5rem 1rem',
cursor: 'pointer',
backgroundColor: isSelected ? '#f0f4f8' : 'transparent',
fontWeight: isSelected ? '600' : '400',
fontSize: '0.9rem',
color: '#333',
borderLeft: isSelected
? '4px solid #7983ff'
: '4px solid transparent',
transition: 'background 0.2s',
}}
>
<input
className="form-check-input"
type="checkbox"
checked={isSelected}
readOnly
style={{ marginRight: '0.75rem' }}
/>
{event}
</li>
);
})}
</ul>
)}
</div>
);
}