Roadmap to becoming a developer in 2022
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

230 lines
7.7 KiB

import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useOutsideClick } from '../../hooks/use-outside-click';
import CheckIcon from '../../icons/check.svg';
import DownIcon from '../../icons/down.svg';
import ProgressIcon from '../../icons/progress.svg';
import ResetIcon from '../../icons/reset.svg';
import SpinnerIcon from '../../icons/spinner.svg';
import { isLoggedIn } from '../../lib/jwt';
import {
ResourceProgressType,
ResourceType,
getTopicStatus,
renderTopicProgress,
updateResourceProgress,
} from '../../lib/resource-progress';
type TopicProgressButtonProps = {
topicId: string;
resourceId: string;
resourceType: ResourceType;
onClose: () => void;
};
const statusColors: Record<ResourceProgressType, string> = {
done: 'bg-green-500',
learning: 'bg-yellow-500',
pending: 'bg-gray-300',
skipped: 'bg-black',
};
export function TopicProgressButton(props: TopicProgressButtonProps) {
const { topicId, resourceId, resourceType, onClose } = props;
const [isUpdatingProgress, setIsUpdatingProgress] = useState(true);
const [progress, setProgress] = useState<ResourceProgressType>('pending');
const [showChangeStatus, setShowChangeStatus] = useState(false);
const changeStatusRef = useRef<HTMLDivElement>(null);
useOutsideClick(changeStatusRef, () => {
setShowChangeStatus(false);
});
const isGuest = useMemo(() => !isLoggedIn(), []);
useEffect(() => {
if (!topicId || !resourceId || !resourceType) {
return;
}
setIsUpdatingProgress(true);
getTopicStatus({ topicId, resourceId, resourceType })
.then((status) => {
setIsUpdatingProgress(false);
setProgress(status);
})
.catch(console.error);
}, [topicId, resourceId, resourceType]);
const handleUpdateResourceProgress = (progress: ResourceProgressType) => {
setIsUpdatingProgress(true);
updateResourceProgress(
{
topicId,
resourceId,
resourceType,
},
progress
)
.then(() => {
setProgress(progress);
onClose();
renderTopicProgress(topicId, progress);
})
.catch((err) => {
alert(err.message);
console.error(err);
})
.finally(() => {
setIsUpdatingProgress(false);
});
};
const allowMarkingSkipped = ['pending', 'learning', 'done'].includes(progress);
const allowMarkingDone = ['skipped', 'pending', 'learning'].includes(progress);
const allowMarkingLearning = ['done', 'skipped', 'pending'].includes(progress);
const allowMarkingPending = ['skipped', 'done', 'learning'].includes(progress);
if (isUpdatingProgress) {
return (
<button className="inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white p-1 px-2 text-sm text-black">
<img alt="Check" class="h-4 w-4 animate-spin" src={SpinnerIcon} />
<span className="ml-2">Updating Status..</span>
</button>
);
}
return (
<div className="relative inline-flex rounded-md border border-gray-300">
<span className="inline-flex cursor-default items-center p-1 px-2 text-sm text-black">
<span class="flex h-2 w-2">
<span
class={`relative inline-flex h-2 w-2 rounded-full ${statusColors[progress]}`}
></span>
</span>
<span className="ml-2 capitalize">
{progress === 'learning' ? 'In Progress' : progress}
</span>
</span>
<button
className="inline-flex cursor-pointer items-center rounded-br-md rounded-tr-md border-l border-l-gray-300 bg-gray-100 p-1 px-2 text-sm text-black hover:bg-gray-200"
onClick={() => setShowChangeStatus(true)}
>
<span className="mr-0.5">Update Status</span>
<img alt="Check" class="h-4 w-4" src={DownIcon} />
</button>
{showChangeStatus && (
<div
className="absolute right-0 top-full mt-1 flex min-w-[128px] flex-col divide-y rounded-md border border-gray-200 bg-white shadow-md [&>button:first-child]:rounded-t-md [&>button:last-child]:rounded-b-md"
ref={changeStatusRef!}
>
{allowMarkingDone && (
<button
class="px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
onClick={() => handleUpdateResourceProgress('done')}
>
<span
class={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['done']}`}
></span>
Done
</button>
)}
{allowMarkingPending && (
<button
class="px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
onClick={() => handleUpdateResourceProgress('pending')}
>
<span
class={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['pending']}`}
></span>
Pending
</button>
)}
{allowMarkingLearning && (
<button
class="px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
onClick={() => handleUpdateResourceProgress('learning')}
>
<span
class={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['learning']}`}
></span>
In Progress
</button>
)}
{allowMarkingSkipped && (
<button
class="px-3 py-1.5 text-left text-sm text-gray-800 hover:bg-gray-100"
onClick={() => handleUpdateResourceProgress('skipped')}
>
<span
class={`mr-2 inline-block h-2 w-2 rounded-full ${statusColors['skipped']}`}
></span>
Skip
</button>
)}
</div>
)}
</div>
);
if (isGuest) {
return (
<div className="flex items-center gap-2">
<button
data-popup="login-popup"
className="inline-flex items-center rounded-md bg-green-600 p-1 px-2 text-sm text-white hover:bg-green-700"
onClick={onClose}
>
<img alt="Check" class="w-3" src={CheckIcon} />
<span className="ml-2">Mark as Done</span>
</button>
<button
data-popup="login-popup"
className="inline-flex items-center rounded-md bg-[#dad1fd] p-1 px-2 text-sm text-[#0E033B] hover:bg-[#C4B6FC]"
onClick={onClose}
>
<img alt="Learning" class="w-4" src={ProgressIcon} />
<span className="ml-2">In Progress</span>
</button>
</div>
);
}
return (
<div class="flex items-center gap-2 rounded-md">
{allowMarkingDone && (
<button
className="inline-flex items-center rounded-md border border-green-600 bg-green-600 p-1 px-2 text-sm text-white hover:bg-green-700"
onClick={() => handleUpdateResourceProgress('done')}
>
<img alt="Check" class="w-3" src={CheckIcon} />
<span className="ml-1">Done</span>
</button>
)}
{allowMarkingLearning && (
<button
className="inline-flex items-center rounded-md bg-[#dad1fd] p-1 px-2 text-sm text-[#0E033B] hover:bg-[#C4B6FC]"
onClick={() => handleUpdateResourceProgress('learning')}
>
<img alt="Learning" className="w-4" src={ProgressIcon} />
<span className="ml-1">Doing</span>
</button>
)}
{allowMarkingPending && (
<button
className="inline-flex items-center rounded-md border border-red-600 bg-red-600 p-1 px-2 text-sm text-white hover:bg-red-700"
onClick={() => handleUpdateResourceProgress('pending')}
>
<img alt="Check" class="h-4" src={ResetIcon} />
<span className="ml-2">Pending</span>
</button>
)}
</div>
);
}