Improved error handling and data fetching

This commit is contained in:
brian 2023-11-17 22:17:33 -05:00
parent c237d0063d
commit 2e4d7bc8c5
4 changed files with 64 additions and 90 deletions

View file

@ -1,5 +1,6 @@
import { getConfig, getRoutes, getServer } from '@/lib/serverActions'; import { getConfig, getRoutes, getServer } from '@/lib/serverActions';
import { NextApiRequest } from 'next'; import { NextApiRequest } from 'next';
import { revalidateTag } from 'next/cache';
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
export async function GET() { export async function GET() {
@ -30,6 +31,8 @@ export async function DELETE(req: Request) {
let index = routesMap.indexOf(targetRoute); let index = routesMap.indexOf(targetRoute);
revalidateTag('routes');
if(index == -1) return new NextResponse(JSON.stringify({error: true, message: "Route not found"}), { if(index == -1) return new NextResponse(JSON.stringify({error: true, message: "Route not found"}), {
status: 404, status: 404,
headers: { headers: {

View file

@ -1,26 +1,22 @@
"use client" "use client"
import { CardTitle, Card, CardDescription, CardHeader, CardContent } from '@/components/ui/card'; import { CardTitle, Card, CardDescription, CardHeader, CardContent, CardFooter } from '@/components/ui/card';
import { getRoutes } from '@/lib/serverActions';
import RouteCards from '@/components/RouteCards'; import RouteCards from '@/components/RouteCards';
import { useEffect, useState } from 'react'; import { Button } from '@/components/ui/button';
import { useRoutes } from '@/lib/clientActions';
async function fetchRoutes() {
let routes: Route[] = await getRoutes();
return routes;
}
export default function Home() { export default function Home() {
useEffect(() => { const { data: routes, error, isLoading } = useRoutes();
async function fetchData() { let routesMap: Route[] = routes as Route[];
const data: Route[] = await fetchRoutes();
setRoutes(data);
}
fetchData();
}, []);
const [routes, setRoutes] = useState<Route[]>([]) if (error) return <div>Error loading routes</div>
if (isLoading) return (
<div className='justify-center'>
Loading...
</div>
)
return ( return (
<main> <main>
@ -31,14 +27,17 @@ export default function Home() {
<CardDescription>Amount of configured routes</CardDescription> <CardDescription>Amount of configured routes</CardDescription>
</CardHeader> </CardHeader>
<CardContent className='flex items-center'> <CardContent className='flex items-center'>
<div className=''> <div>
<p className='text-xl font-semibold translate-y-2'>{routes.length}</p> <p className='text-xl font-semibold translate-y-2'>{routesMap.length}</p>
</div> </div>
</CardContent> </CardContent>
<CardFooter>
<Button className='translate-y-2.5'>Create</Button>
</CardFooter>
</Card> </Card>
</div> </div>
<div className='grid grid-flow-row-dense md:grid-cols-6 sm:grid-cols-2 p-2'> <div className='grid grid-flow-row-dense md:grid-cols-6 sm:grid-cols-2 p-2'>
<RouteCards routes={routes} /> <RouteCards routes={routes as Route[]} />
</div> </div>
</main> </main>
) )

View file

@ -3,83 +3,54 @@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./ui/card"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./ui/card";
import { getHosts, getRouteUpstreams } from '@/lib/utils'; import { getHosts, getRouteUpstreams } from '@/lib/utils';
import { deleteRoute } from '@/lib/serverActions';
import { toast } from './ui/use-toast'; import { toast } from './ui/use-toast';
import React, { useState } from 'react'; import React from 'react';
import useSWRMutation from 'swr/mutation';
import { deleteRoute } from '@/lib/clientActions';
class RouteCards extends React.Component<{routes: Route[]}> { export default function RouteCards({ routes }: { routes: Route[] }) {
constructor(props: { routes: Route[] }) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
state = { const { trigger } = useSWRMutation("/api/caddy/routes", deleteRoute, {
routes: this.props.routes, onError: () => {
} return toast({
title: "Error",
componentDidUpdate(prevProps: Readonly<{ routes: Route[]; }>, prevState: Readonly<{}>) { description: "There was an error deleting the route",
if (this.props.routes !== prevProps.routes) { variant: "destructive"
this.setState({ });
routes: this.props.routes },
onSuccess: () => {
toast({
title: "Success",
description: "Route deleted successfully!",
variant: "success"
}); });
} }
} });
handleDelete = async (route: Route) => {
let res = await deleteRoute(route);
if (res.error) return toast({
title: "Error",
description: res.message,
variant: "destructive"
});
this.state.routes.splice(this.state.routes.indexOf(route), 1)
this.setState({
routes: this.state.routes
});
return toast({
title: "Success",
description: "Route deleted successfully!",
variant: "success"
});
}
handleClick = (route: Route) => {
console.log(route);
}
render() {
return (
this.state.routes.map((route, index) => (
<div key={index} className='p-2'>
<Card key={index} className='w-[300px]'>
<CardHeader>
<CardTitle>{getHosts(route).values().next().value}</CardTitle>
{getHosts(route).map((route => <CardDescription key={route}>{route}</ CardDescription>))}
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm font-medium leading-none">
Tunneling to
</p>
<p className="text-sm text-muted-foreground">{getRouteUpstreams(route)?.map((upstream => upstream.dial))}</p>
</CardContent>
<CardFooter className="flex justify-between">
<Button onClick={() => { this.handleClick(route) }}>Edit</Button>
<Button variant="destructive" onClick={() => { this.handleDelete(route) }}>Delete</Button>
</CardFooter>
</Card>
</div>
))
)
}
return (
routes.map((route, index) => (
<div key={index} className='p-2'>
<Card key={index} className='w-[300px]'>
<CardHeader>
<CardTitle>{getHosts(route).values().next().value}</CardTitle>
{getHosts(route).map((route => <CardDescription key={route}>{route}</ CardDescription>))}
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm font-medium leading-none">
Tunneling to
</p>
<p className="text-sm text-muted-foreground">{getRouteUpstreams(route)?.map((upstream => upstream.dial))}</p>
</CardContent>
<CardFooter className="flex justify-between">
<Button onClick={() => { trigger(route) }}>Edit</Button>
<Button variant="destructive" onClick={() => { trigger(route) }}>Delete</Button>
</CardFooter>
</Card>
</div>
))
)
} }
export default RouteCards;

View file

@ -18,6 +18,7 @@
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"swr": "^2.2.4",
"tailwind-merge": "^2.0.0", "tailwind-merge": "^2.0.0",
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"
}, },