1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-09 00:30:40 +02:00

Handle error screen

This commit is contained in:
Kamran Ahmed
2025-05-22 13:48:36 +01:00
parent 041f090a47
commit 85fec7a6af
5 changed files with 47 additions and 28 deletions

View File

@@ -2,8 +2,7 @@ import './RoadmapAIChat.css';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { import {
roadmapDetailsOptions, roadmapJSONOptions
roadmapJSONOptions,
} from '../../queries/roadmap'; } from '../../queries/roadmap';
import { queryClient } from '../../stores/query-client'; import { queryClient } from '../../stores/query-client';
import { import {
@@ -14,7 +13,7 @@ import {
useRef, useRef,
useState, useState,
} from 'react'; } from 'react';
import { BotIcon, Loader2Icon, PauseCircleIcon, SendIcon } from 'lucide-react'; import { BotIcon, Frown, Loader2Icon, PauseCircleIcon, SendIcon } from 'lucide-react';
import { ChatEditor } from '../ChatEditor/ChatEditor'; import { ChatEditor } from '../ChatEditor/ChatEditor';
import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree'; import { roadmapTreeMappingOptions } from '../../queries/roadmap-tree';
import { type AllowedAIChatRole } from '../GenerateCourse/AICourseLessonChat'; import { type AllowedAIChatRole } from '../GenerateCourse/AICourseLessonChat';
@@ -72,12 +71,7 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
const [streamedMessage, setStreamedMessage] = const [streamedMessage, setStreamedMessage] =
useState<React.ReactNode | null>(null); useState<React.ReactNode | null>(null);
const { data: roadmapDetailsData } = useQuery( const { data: roadmapDetail, error: roadmapDetailError } = useQuery(
roadmapDetailsOptions(roadmapId),
queryClient,
);
const { data: roadmapJSONData } = useQuery(
roadmapJSONOptions(roadmapId), roadmapJSONOptions(roadmapId),
queryClient, queryClient,
); );
@@ -94,20 +88,20 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
const roadmapContainerRef = useRef<HTMLDivElement>(null); const roadmapContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
if (!roadmapJSONData || !roadmapContainerRef.current) { if (!roadmapDetail || !roadmapContainerRef.current) {
return; return;
} }
roadmapContainerRef.current.replaceChildren(roadmapJSONData.svg); roadmapContainerRef.current.replaceChildren(roadmapDetail.svg);
}, [roadmapJSONData]); }, [roadmapDetail]);
useEffect(() => { useEffect(() => {
if (!roadmapTreeData || !roadmapJSONData || !roadmapDetailsData) { if (!roadmapTreeData || !roadmapDetail) {
return; return;
} }
setIsLoading(false); setIsLoading(false);
}, [roadmapTreeData, roadmapJSONData, roadmapDetailsData]); }, [roadmapTreeData, roadmapDetail]);
const abortControllerRef = useRef<AbortController | null>(null); const abortControllerRef = useRef<AbortController | null>(null);
const handleChatSubmit = (json: JSONContent) => { const handleChatSubmit = (json: JSONContent) => {
@@ -282,6 +276,18 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
scrollToBottom(); scrollToBottom();
}, []); }, []);
if (roadmapDetailError) {
return (
<div className="flex flex-grow flex-col items-center justify-center">
<Frown className="mb-4 size-16" />
<h1 className="mb-2 text-2xl font-bold">There was an error</h1>
<p className="max-w-sm text-balance text-gray-500">
{roadmapDetailError.message}
</p>
</div>
);
}
return ( return (
<div className="flex flex-grow flex-row"> <div className="flex flex-grow flex-row">
<div className="relative h-full flex-grow overflow-y-scroll"> <div className="relative h-full flex-grow overflow-y-scroll">
@@ -290,13 +296,15 @@ export function RoadmapAIChat(props: RoadmapAIChatProps) {
<Loader2Icon className="size-6 animate-spin stroke-[2.5]" /> <Loader2Icon className="size-6 animate-spin stroke-[2.5]" />
</div> </div>
)} )}
{roadmapJSONData?.json && !isLoading && ( {roadmapDetail?.json && !isLoading && (
<div className="mx-auto max-w-[968px] px-4"> <div>
<ChatRoadmapRenderer <div className="mx-auto max-w-[968px] px-4">
roadmapId={roadmapId} <ChatRoadmapRenderer
nodes={roadmapJSONData?.json.nodes} roadmapId={roadmapId}
edges={roadmapJSONData?.json.edges} nodes={roadmapDetail?.json.nodes}
/> edges={roadmapDetail?.json.edges}
/>
</div>
</div> </div>
)} )}
</div> </div>

View File

@@ -80,6 +80,8 @@ export async function httpCall<ResponseType = AppResponse>(
if (!response.ok) { if (!response.ok) {
if (data.errors) { if (data.errors) {
throw new FetchError(response?.status, data.message); throw new FetchError(response?.status, data.message);
} else if (data.message) {
throw new FetchError(response?.status, data.message);
} else { } else {
throw new Error('An unexpected error occurred'); throw new Error('An unexpected error occurred');
} }

View File

@@ -59,7 +59,7 @@ export const GET: APIRoute = async function ({ params, request, props }) {
); );
if (!fs.existsSync(roadmapFilePath)) { if (!fs.existsSync(roadmapFilePath)) {
return new Response(JSON.stringify({ error: 'Roadmap file not found' }), { return new Response(JSON.stringify({ message: 'Roadmap not found' }), {
status: 404, status: 404,
}); });
} }

View File

@@ -21,8 +21,7 @@ const { roadmapId } = Astro.params as Props;
wrapperClassName='flex-row p-0 lg:p-0 overflow-hidden' wrapperClassName='flex-row p-0 lg:p-0 overflow-hidden'
client:load client:load
> >
<!-- Make it client:load please --> <RoadmapAIChat roadmapId={roadmapId} client:load />
<RoadmapAIChat roadmapId={roadmapId} client:only='react' />
<CheckSubscriptionVerification client:load /> <CheckSubscriptionVerification client:load />
</AITutorLayout> </AITutorLayout>
</SkeletonLayout> </SkeletonLayout>

View File

@@ -2,15 +2,25 @@ import { queryOptions } from '@tanstack/react-query';
import { httpGet } from '../lib/query-http'; import { httpGet } from '../lib/query-http';
import { type Node, type Edge, renderFlowJSON } from '@roadmapsh/editor'; import { type Node, type Edge, renderFlowJSON } from '@roadmapsh/editor';
type RoadmapJSON = {
_id: string;
title: string;
description: string;
slug: string;
nodes: Node[];
edges: Edge[];
createdAt: string;
updatedAt: string;
};
export function roadmapJSONOptions(roadmapId: string) { export function roadmapJSONOptions(roadmapId: string) {
return queryOptions({ return queryOptions({
queryKey: ['roadmap-json', roadmapId], queryKey: ['roadmap-json', roadmapId],
queryFn: async () => { queryFn: async () => {
const baseUrl = import.meta.env.PUBLIC_APP_URL; const baseUrl = import.meta.env.PUBLIC_APP_URL;
const roadmapJSON = await httpGet<{ const roadmapJSON = await httpGet<RoadmapJSON>(
nodes: Node[]; `${baseUrl}/${roadmapId}.json`,
edges: Edge[]; );
}>(`${baseUrl}/${roadmapId}.json`);
const svg = await renderFlowJSON(roadmapJSON); const svg = await renderFlowJSON(roadmapJSON);