import { useEffect, useMemo, useRef, useState, type RefObject } from 'react'; import { useOutsideClick } from '../../hooks/use-outside-click'; import { useKeydown } from '../../hooks/use-keydown'; import { httpGet } from '../../lib/http'; import type { ResourceType } from '../../lib/resource-progress'; import { topicSelectorAll } from '../../lib/resource-progress'; import CloseIcon from '../../icons/close.svg'; import { deleteUrlParam, getUrlParams } from '../../lib/browser'; import { useAuth } from '../../hooks/use-auth'; import type { GetRoadmapResponse } from '../CustomRoadmap/CustomRoadmap'; import { ReadonlyEditor } from '../../../editor/readonly-editor'; import { ProgressLoadingError } from './ProgressLoadingError'; import { UserProgressModalHeader } from './UserProgressModalHeader'; export type ProgressMapProps = { userId?: string; resourceId: string; resourceType: ResourceType; onClose?: () => void; isCustomResource?: boolean; }; type UserProgressResponse = { user: { _id: string; name: string; }; progress: { total: number; done: string[]; learning: string[]; skipped: string[]; }; }; export function UserCustomProgressModal(props: ProgressMapProps) { const { resourceId, resourceType, userId: propUserId, onClose: onModalClose, isCustomResource, } = props; const { s: userId = propUserId } = getUrlParams(); if (!userId) { return null; } const resourceSvgEl = useRef(null); const popupBodyEl = useRef(null); const currentUser = useAuth(); const [roadmap, setRoadmap] = useState(null); const [showModal, setShowModal] = useState(!!userId); const [progressResponse, setProgressResponse] = useState(); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(''); async function getUserProgress( userId: string, resourceType: string, resourceId: string, ): Promise { const { error, response } = await httpGet( `${ import.meta.env.PUBLIC_API_URL }/v1-get-user-progress/${userId}?resourceType=${resourceType}&resourceId=${resourceId}`, ); if (error || !response) { throw error || new Error('Something went wrong. Please try again!'); } return response; } async function getRoadmapSVG(): Promise { const { error, response: roadmapData } = await httpGet( `${import.meta.env.PUBLIC_API_URL}/v1-get-roadmap/${resourceId}`, ); if (error || !roadmapData) { throw error || new Error('Something went wrong. Please try again!'); } setRoadmap(roadmapData); return roadmapData; } function onClose() { deleteUrlParam('s'); setError(''); setShowModal(false); if (onModalClose) { onModalClose(); } else { window.location.reload(); } } useKeydown('Escape', () => { onClose(); }); useOutsideClick(popupBodyEl, () => { onClose(); }); useEffect(() => { if (!resourceId || !resourceType || !userId) { return; } setIsLoading(true); Promise.all([ getRoadmapSVG(), getUserProgress(userId, resourceType, resourceId), ]) .then(([_, user]) => { if (!user) { return; } setProgressResponse(user); }) .catch((err) => { setError(err?.message || 'Something went wrong. Please try again!'); }) .finally(() => { setIsLoading(false); }); }, [userId]); if (currentUser?.id === userId) { deleteUrlParam('s'); return null; } if (!showModal) { return null; } if (isLoading || error) { return ; } return (
) => { const { done = [], learning = [], skipped = [], } = progressResponse?.progress || {}; done?.forEach((topicId: string) => { topicSelectorAll(topicId, wrapperRef?.current!).forEach( (el) => { el.classList.add('done'); }, ); }); learning?.forEach((topicId: string) => { topicSelectorAll(topicId, wrapperRef?.current!).forEach( (el) => { el.classList.add('learning'); }, ); }); skipped?.forEach((topicId: string) => { topicSelectorAll(topicId, wrapperRef?.current!).forEach( (el) => { el.classList.add('skipped'); }, ); }); }} fontFamily="Balsamiq Sans" fontURL="/fonts/balsamiq.woff2" />
); }