mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-01 21:32:35 +02:00
wip
This commit is contained in:
@@ -1,12 +1,17 @@
|
|||||||
import { PersonStandingIcon } from 'lucide-react';
|
import { Loader2Icon, PersonStandingIcon } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { usePersonalizedRoadmap } from '../../hooks/use-personalized-roadmap';
|
import { usePersonalizedRoadmap } from '../../hooks/use-personalized-roadmap';
|
||||||
import { renderTopicProgress } from '../../lib/resource-progress';
|
import {
|
||||||
|
loadFreshProgress,
|
||||||
|
renderTopicProgress,
|
||||||
|
} from '../../lib/resource-progress';
|
||||||
import { PersonalizedRoadmapModal } from './PersonalizedRoadmapModal';
|
import { PersonalizedRoadmapModal } from './PersonalizedRoadmapModal';
|
||||||
import { useMutation } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
import { httpPost } from '../../lib/query-http';
|
import { httpPost } from '../../lib/query-http';
|
||||||
import { useToast } from '../../hooks/use-toast';
|
import { useToast } from '../../hooks/use-toast';
|
||||||
import { queryClient } from '../../stores/query-client';
|
import { queryClient } from '../../stores/query-client';
|
||||||
|
import { userResourceProgressOptions } from '../../queries/resource-progress';
|
||||||
|
import { useAuth } from '../../hooks/use-auth';
|
||||||
|
|
||||||
type BulkUpdateResourceProgressBody = {
|
type BulkUpdateResourceProgressBody = {
|
||||||
done: string[];
|
done: string[];
|
||||||
@@ -23,27 +28,38 @@ export function PersonalizedRoadmap(props: PersonalizedRoadmapProps) {
|
|||||||
const { roadmapId } = props;
|
const { roadmapId } = props;
|
||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const currentUser = useAuth();
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
const {
|
const { data: userResourceProgress } = useQuery(
|
||||||
mutate: bulkUpdateResourceProgress,
|
userResourceProgressOptions('roadmap', roadmapId),
|
||||||
isPending: isBulkUpdating,
|
|
||||||
isSuccess: isBulkUpdateSuccess,
|
|
||||||
} = useMutation(
|
|
||||||
{
|
|
||||||
mutationFn: (body: BulkUpdateResourceProgressBody) => {
|
|
||||||
return httpPost(`/v1-bulk-update-resource-progress/${roadmapId}`, body);
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error(
|
|
||||||
error?.message ?? 'Something went wrong, please try again.',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
queryClient,
|
queryClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { generatePersonalizedRoadmap } = usePersonalizedRoadmap({
|
const { mutate: bulkUpdateResourceProgress, isPending: isBulkUpdating } =
|
||||||
|
useMutation(
|
||||||
|
{
|
||||||
|
mutationFn: (body: BulkUpdateResourceProgressBody) => {
|
||||||
|
return httpPost(
|
||||||
|
`/v1-bulk-update-resource-progress/${roadmapId}`,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(
|
||||||
|
error?.message ?? 'Something went wrong, please try again.',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(
|
||||||
|
userResourceProgressOptions('roadmap', roadmapId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
queryClient,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { generatePersonalizedRoadmap, status } = usePersonalizedRoadmap({
|
||||||
roadmapId,
|
roadmapId,
|
||||||
onStart: () => {
|
onStart: () => {
|
||||||
setIsModalOpen(false);
|
setIsModalOpen(false);
|
||||||
@@ -55,6 +71,10 @@ export function PersonalizedRoadmap(props: PersonalizedRoadmapProps) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onFinish: (data) => {
|
onFinish: (data) => {
|
||||||
|
console.log('-'.repeat(20));
|
||||||
|
console.log('onFinish', data);
|
||||||
|
console.log('-'.repeat(20));
|
||||||
|
|
||||||
bulkUpdateResourceProgress({
|
bulkUpdateResourceProgress({
|
||||||
skipped: data.topicIds,
|
skipped: data.topicIds,
|
||||||
learning: [],
|
learning: [],
|
||||||
@@ -64,21 +84,65 @@ export function PersonalizedRoadmap(props: PersonalizedRoadmapProps) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { mutate: clearResourceProgress, isPending: isClearing } = useMutation(
|
||||||
|
{
|
||||||
|
mutationFn: () => {
|
||||||
|
return httpPost(`/v1-clear-resource-progress`, {
|
||||||
|
resourceId: roadmapId,
|
||||||
|
resourceType: 'roadmap',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(
|
||||||
|
error?.message ?? 'Something went wrong, please try again.',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('Progress cleared successfully.');
|
||||||
|
localStorage.removeItem(
|
||||||
|
`roadmap-${roadmapId}-${currentUser?.id}-progress`,
|
||||||
|
);
|
||||||
|
localStorage.removeItem(
|
||||||
|
`roadmap-${roadmapId}-${currentUser?.id}-favorite`,
|
||||||
|
);
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
queryClient,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isGenerating = status !== 'idle' || isBulkUpdating || isClearing;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<PersonalizedRoadmapModal
|
<PersonalizedRoadmapModal
|
||||||
roadmapId={roadmapId}
|
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSubmit={generatePersonalizedRoadmap}
|
onSubmit={(information) => {
|
||||||
|
const { skipped = [] } = userResourceProgress ?? {};
|
||||||
|
for (const topicId of skipped) {
|
||||||
|
renderTopicProgress(topicId, 'pending');
|
||||||
|
}
|
||||||
|
|
||||||
|
generatePersonalizedRoadmap(information);
|
||||||
|
}}
|
||||||
|
onClearProgress={() => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
clearResourceProgress();
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="group inline-flex items-center gap-1.5 border-b-2 border-b-transparent px-2 pb-2.5 text-sm font-normal text-gray-400 transition-colors hover:text-gray-700"
|
className="group inline-flex items-center gap-1.5 border-b-2 border-b-transparent px-2 pb-2.5 text-sm font-normal text-gray-400 transition-colors hover:text-gray-700"
|
||||||
onClick={() => setIsModalOpen(true)}
|
onClick={() => setIsModalOpen(true)}
|
||||||
|
disabled={isGenerating}
|
||||||
>
|
>
|
||||||
<PersonStandingIcon className="h-4 w-4 shrink-0" />
|
{isGenerating ? (
|
||||||
|
<Loader2Icon className="h-4 w-4 shrink-0 animate-spin" />
|
||||||
|
) : (
|
||||||
|
<PersonStandingIcon className="h-4 w-4 shrink-0" />
|
||||||
|
)}
|
||||||
<span>Personalized</span>
|
<span>Personalized</span>
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
import { PersonStandingIcon } from 'lucide-react';
|
import { PersonStandingIcon, XIcon } from 'lucide-react';
|
||||||
import { useId, useState, type FormEvent } from 'react';
|
import { useId, useState, type FormEvent } from 'react';
|
||||||
|
|
||||||
type PersonalizedRoadmapFormProps = {
|
type PersonalizedRoadmapFormProps = {
|
||||||
info?: string;
|
info?: string;
|
||||||
onSubmit: (info: string) => void;
|
onSubmit: (info: string) => void;
|
||||||
|
onClearProgress: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PersonalizedRoadmapForm(props: PersonalizedRoadmapFormProps) {
|
export function PersonalizedRoadmapForm(props: PersonalizedRoadmapFormProps) {
|
||||||
const { info: defaultInfo, onSubmit } = props;
|
const { info: defaultInfo, onSubmit, onClearProgress } = props;
|
||||||
|
|
||||||
const [info, setInfo] = useState(defaultInfo || '');
|
const [info, setInfo] = useState(defaultInfo || '');
|
||||||
const infoFieldId = useId();
|
const infoFieldId = useId();
|
||||||
@@ -34,7 +35,15 @@ export function PersonalizedRoadmapForm(props: PersonalizedRoadmapFormProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 flex items-center justify-end">
|
<div className="mt-2 grid grid-cols-2 gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex items-center gap-2 rounded-xl border border-gray-200 p-2 px-4 text-gray-600 hover:bg-gray-100 focus:outline-none"
|
||||||
|
onClick={onClearProgress}
|
||||||
|
>
|
||||||
|
<XIcon className="h-4 w-4" />
|
||||||
|
Clear Progress
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex items-center gap-2 rounded-xl bg-black p-2 px-4 text-white hover:opacity-90 focus:outline-none"
|
className="flex items-center gap-2 rounded-xl bg-black p-2 px-4 text-white hover:opacity-90 focus:outline-none"
|
||||||
|
@@ -4,17 +4,20 @@ import { Modal } from '../Modal';
|
|||||||
import { PersonalizedRoadmapForm } from './PersonalizedRoadmapForm';
|
import { PersonalizedRoadmapForm } from './PersonalizedRoadmapForm';
|
||||||
|
|
||||||
type PersonalizedRoadmapModalProps = {
|
type PersonalizedRoadmapModalProps = {
|
||||||
roadmapId: string;
|
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSubmit: (information: string) => void;
|
onSubmit: (information: string) => void;
|
||||||
|
onClearProgress: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PersonalizedRoadmapModal(props: PersonalizedRoadmapModalProps) {
|
export function PersonalizedRoadmapModal(props: PersonalizedRoadmapModalProps) {
|
||||||
const { roadmapId, onClose, onSubmit } = props;
|
const { onClose, onSubmit, onClearProgress } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal onClose={onClose} bodyClassName="rounded-2xl">
|
<Modal onClose={onClose} bodyClassName="rounded-2xl">
|
||||||
<PersonalizedRoadmapForm onSubmit={onSubmit} />
|
<PersonalizedRoadmapForm
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
onClearProgress={onClearProgress}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -206,7 +206,7 @@ export async function getResourceProgress(
|
|||||||
return progress;
|
return progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFreshProgress(
|
export async function loadFreshProgress(
|
||||||
resourceType: ResourceType,
|
resourceType: ResourceType,
|
||||||
resourceId: string,
|
resourceId: string,
|
||||||
) {
|
) {
|
||||||
|
Reference in New Issue
Block a user