import { useEffect, useRef, useState } from 'preact/hooks'; import { wireframeJSONToSVG } from 'roadmap-renderer'; import { Spinner } from '../ReactIcons/Spinner'; import { httpGet, httpPut } from '../../lib/http'; import { renderTopicProgress } from '../../lib/resource-progress'; import '../FrameRenderer/FrameRenderer.css'; import { useOutsideClick } from '../../hooks/use-outside-click'; import { useKeydown } from '../../hooks/use-keydown'; import type { TeamResourceConfig } from './RoadmapSelector'; import { useToast } from '../../hooks/use-toast'; export type ProgressMapProps = { teamId: string; resourceId: string; resourceType: 'roadmap' | 'best-practice'; defaultRemovedItems?: string[]; setTeamResourceConfig: (config: TeamResourceConfig) => void; onClose: () => void; }; export function UpdateTeamResourceModal(props: ProgressMapProps) { const { defaultRemovedItems = [], resourceId, resourceType, teamId, setTeamResourceConfig, onClose, } = props; const containerEl = useRef(null); const popupBodyEl = useRef(null); const toast = useToast(); const [isLoading, setIsLoading] = useState(true); const [isUpdating, setIsUpdating] = useState(false); const [removedItems, setRemovedItems] = useState(defaultRemovedItems); useEffect(() => { function onTopicClick(e: any) { const groupEl = e.target.closest('.clickable-group'); const groupId = groupEl?.dataset?.groupId; if (!groupId) { return; } const normalizedGroupId = groupId.replace(/^\d+-/, ''); if (removedItems.includes(normalizedGroupId)) { setRemovedItems((prev) => prev.filter((id) => id !== normalizedGroupId) ); renderTopicProgress(normalizedGroupId, 'reset' as any); } else { setRemovedItems((prev) => [...prev, normalizedGroupId]); renderTopicProgress(normalizedGroupId, 'removed'); } } document.addEventListener('click', onTopicClick); return () => { document.removeEventListener('click', onTopicClick); }; }, [removedItems]); let resourceJsonUrl = 'https://roadmap.sh'; if (resourceType === 'roadmap') { resourceJsonUrl += `/${resourceId}.json`; } else { resourceJsonUrl += `/best-practices/${resourceId}.json`; } async function renderResource(jsonUrl: string) { const res = await fetch(jsonUrl); const json = await res.json(); const svg = await wireframeJSONToSVG(json, { fontURL: '/fonts/balsamiq.woff2', }); containerEl.current?.replaceChildren(svg); // Render team configuration removedItems.forEach((topicId: string) => { renderTopicProgress(topicId, 'removed'); }); } useKeydown('Escape', () => { onClose(); }); useOutsideClick(popupBodyEl, () => { onClose(); }); async function onSaveChanges() { if (removedItems.length === 0) { return; } setIsUpdating(true); const { error, response } = await httpPut( `${ import.meta.env.PUBLIC_API_URL }/v1-update-team-resource-config/${teamId}`, { teamId: teamId, resourceId: resourceId, resourceType: resourceType, removed: removedItems, } ); if (error || !response) { toast.error(error?.message || 'Error adding roadmap'); return; } setTeamResourceConfig(response); onClose(); } useEffect(() => { if ( !containerEl.current || !resourceJsonUrl || !resourceId || !resourceType || !teamId ) { return; } renderResource(resourceJsonUrl) .catch((err) => { console.error(err); toast.error('Something went wrong. Please try again!'); }) .finally(() => { setIsLoading(false); }); }, []); return (
); }