-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): Use server components for workflows page + add error/loadin…
…g pages
- Loading branch information
1 parent
9bd13e0
commit 6de4f69
Showing
5 changed files
with
167 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Skeleton } from "@/components/ui/skeleton" | ||
|
||
export default function Loading() { | ||
return ( | ||
<div className="grid h-full grid-cols-12"> | ||
<div className="col-span-1"> | ||
<Skeleton className="h-full w-full" /> | ||
</div> | ||
<div className="col-span-8"> | ||
<Skeleton className="h-full w-full bg-transparent" /> | ||
</div> | ||
<div className="col-span-3"> | ||
<Skeleton className="h-full w-full" /> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
"use client" | ||
|
||
// Error components must be Client Components | ||
import { useEffect } from "react" | ||
|
||
import { AlertDestructive } from "@/components/alert-destructive" | ||
|
||
export default function Error({ | ||
error, | ||
reset, | ||
}: { | ||
error: Error & { digest?: string } | ||
reset: () => void | ||
}) { | ||
useEffect(() => { | ||
// Log the error to an error reporting service | ||
console.error(error) | ||
}, [error]) | ||
|
||
return ( | ||
<main className="container flex h-full w-full max-w-[400px] items-center justify-center"> | ||
<AlertDestructive message={error.message} reset={reset} /> | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Loader2 } from "lucide-react" | ||
|
||
export default function Loading() { | ||
return ( | ||
<div className="container flex h-full max-w-[800px] flex-col justify-center space-y-2 p-16"> | ||
<Loader2 className="mx-auto animate-spin text-gray-500" /> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,147 +1,17 @@ | ||
"use client" | ||
|
||
import React from "react" | ||
import Link from "next/link" | ||
import { useSessionContext } from "@/providers/session" | ||
import { useQuery } from "@tanstack/react-query" | ||
import { PlusCircle } from "lucide-react" | ||
import { redirect } from "next/navigation" | ||
import { createClient } from "@/utils/supabase/server" | ||
|
||
import { WorkflowMetadata } from "@/types/schemas" | ||
import { fetchAllWorkflows } from "@/lib/flow" | ||
import { cn } from "@/lib/utils" | ||
import { Button } from "@/components/ui/button" | ||
import { Skeleton } from "@/components/ui/skeleton" | ||
import { | ||
NewWorkflowDialog, | ||
NewWorkflowDialogTrigger, | ||
} from "@/components/new-workflow-dialog" | ||
import NoSSR from "@/components/no-ssr" | ||
import { WorkflowsDashboard } from "./workflows-dashboard" | ||
|
||
export default function Page() { | ||
return ( | ||
<NoSSR> | ||
<WorkflowsPage suppressHydrationWarning /> | ||
</NoSSR> | ||
) | ||
} | ||
function WorkflowsPage(props: React.HTMLAttributes<HTMLElement>) { | ||
const { session, isLoading: sessionIsLoading } = useSessionContext() | ||
export default async function Page() { | ||
const supabase = createClient() | ||
|
||
const { | ||
data: userWorkflows, | ||
isLoading, | ||
error, | ||
} = useQuery<WorkflowMetadata[], Error>({ | ||
queryKey: ["workflows"], | ||
queryFn: async () => { | ||
if (!session) { | ||
console.error("Invalid session") | ||
throw new Error("Invalid session") | ||
} | ||
console.log(session) | ||
const workflows = await fetchAllWorkflows(session) | ||
return workflows | ||
}, | ||
}) | ||
|
||
if (sessionIsLoading) { | ||
return ( | ||
<div | ||
className="container flex h-full w-full flex-col items-center justify-center space-y-4" | ||
{...props} | ||
> | ||
<Skeleton className="h-20 w-96" /> | ||
<Skeleton className="h-20 w-96" /> | ||
<Skeleton className="h-20 w-96" /> | ||
<Skeleton className="h-20 w-96" /> | ||
<Skeleton className="h-20 w-96" /> | ||
</div> | ||
) | ||
data: { session }, | ||
} = await supabase.auth.getSession() | ||
if (!session) { | ||
redirect("/login") | ||
} | ||
if (error) { | ||
throw new Error("Error fetching workflows") | ||
} | ||
|
||
return ( | ||
<div className="container flex h-full max-w-[800px] flex-col justify-center space-y-2 p-16"> | ||
<div className="flex w-full "> | ||
<div className="items-start space-y-2 text-left"> | ||
<h2 className="text-2xl font-bold tracking-tight">Workflows</h2> | ||
<p className="text-md text-muted-foreground"> | ||
Welcome back! Here's a list of your workflows. | ||
</p> | ||
</div> | ||
|
||
<NewWorkflowDialog> | ||
<NewWorkflowDialogTrigger asChild> | ||
<Button | ||
variant="outline" | ||
role="combobox" | ||
className="ml-auto space-x-2" | ||
onClick={() => { | ||
console.log("Create new workflow") | ||
}} | ||
> | ||
<PlusCircle className="mr-2 h-4 w-4" /> | ||
New | ||
</Button> | ||
</NewWorkflowDialogTrigger> | ||
</NewWorkflowDialog> | ||
</div> | ||
{isLoading ? ( | ||
<Skeleton className="h-20 w-full" /> | ||
) : ( | ||
<WorkflowList workflows={userWorkflows ?? []} /> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
||
interface WorkflowListProps { | ||
workflows: WorkflowMetadata[] | ||
} | ||
|
||
export function WorkflowList({ workflows }: WorkflowListProps) { | ||
return ( | ||
<div className="flex flex-col gap-2 pt-4"> | ||
{workflows.length === 0 ? ( | ||
<span className="my-4">No workflows created.</span> | ||
) : ( | ||
<> | ||
{workflows.map((wf) => ( | ||
<Link | ||
key={wf.id} | ||
href={`/workflows/${wf.id}`} | ||
className={cn( | ||
"flex min-h-24 min-w-[600px] flex-col items-start justify-start rounded-lg border p-6 text-left text-sm shadow-md transition-all hover:bg-accent", | ||
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white" | ||
)} | ||
> | ||
<div className="flex w-full flex-col gap-1"> | ||
<div className="flex items-center"> | ||
<div className="flex items-center gap-2"> | ||
<div className="font-semibold capitalize">{wf.title}</div> | ||
</div> | ||
<div className="ml-auto flex items-center space-x-2"> | ||
<span | ||
className={cn( | ||
"flex h-2 w-2 rounded-full ", | ||
wf.status === "online" ? "bg-green-400" : "bg-gray-400" | ||
)} | ||
/> | ||
<span className="text-xs text-muted-foreground"> | ||
Last run: 2 hours ago | ||
</span> | ||
</div> | ||
</div> | ||
<div className="text-xs font-medium text-muted-foreground"> | ||
{wf.description ?? ""} | ||
</div> | ||
</div> | ||
</Link> | ||
))} | ||
</> | ||
)} | ||
</div> | ||
) | ||
return <WorkflowsDashboard session={session} /> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { Suspense } from "react" | ||
import Link from "next/link" | ||
import { type Session } from "@supabase/supabase-js" | ||
import { PlusCircle } from "lucide-react" | ||
|
||
import { fetchAllWorkflows } from "@/lib/flow" | ||
import { cn } from "@/lib/utils" | ||
import { Button } from "@/components/ui/button" | ||
import { Skeleton } from "@/components/ui/skeleton" | ||
import { | ||
NewWorkflowDialog, | ||
NewWorkflowDialogTrigger, | ||
} from "@/components/new-workflow-dialog" | ||
|
||
interface WorkflowsDashboardProps extends React.HTMLAttributes<HTMLElement> { | ||
session: Session | ||
} | ||
|
||
export async function WorkflowsDashboard({ session }: WorkflowsDashboardProps) { | ||
return ( | ||
<div className="container flex h-full max-w-[800px] flex-col space-y-4 p-16"> | ||
<div className="flex w-full pt-16"> | ||
<div className="items-start space-y-2 text-left"> | ||
<h2 className="text-2xl font-bold tracking-tight">Workflows</h2> | ||
<p className="text-md text-muted-foreground"> | ||
Welcome back! Here's a list of your workflows. | ||
</p> | ||
</div> | ||
<NewWorkflowDialog> | ||
<NewWorkflowDialogTrigger asChild> | ||
<Button | ||
variant="outline" | ||
role="combobox" | ||
className="ml-auto space-x-2" | ||
> | ||
<PlusCircle className="mr-2 h-4 w-4" /> | ||
New | ||
</Button> | ||
</NewWorkflowDialogTrigger> | ||
</NewWorkflowDialog> | ||
</div> | ||
<Suspense | ||
fallback={ | ||
<div className="flex flex-col gap-2 pt-4"> | ||
<Skeleton className="h-24 w-full" /> | ||
<Skeleton className="h-24 w-full" /> | ||
<Skeleton className="h-24 w-full" /> | ||
<Skeleton className="h-24 w-full" /> | ||
</div> | ||
} | ||
> | ||
<WorkflowList session={session} /> | ||
</Suspense> | ||
</div> | ||
) | ||
} | ||
|
||
interface WorkflowListProps { | ||
session: Session | ||
} | ||
|
||
export async function WorkflowList({ session }: WorkflowListProps) { | ||
const workflows = await fetchAllWorkflows(session) | ||
return ( | ||
<div className="flex flex-col gap-2 pt-4"> | ||
{workflows.length === 0 ? ( | ||
<span className="my-4">No workflows created.</span> | ||
) : ( | ||
<> | ||
{workflows.map((wf) => ( | ||
<Link | ||
key={wf.id} | ||
href={`/workflows/${wf.id}`} | ||
className={cn( | ||
"flex min-h-24 min-w-[600px] flex-col items-start justify-start rounded-lg border p-6 text-left text-sm shadow-md transition-all hover:bg-accent", | ||
"dark:bg-muted dark:text-white dark:hover:bg-muted dark:hover:text-white" | ||
)} | ||
> | ||
<div className="flex w-full flex-col gap-1"> | ||
<div className="flex items-center"> | ||
<div className="flex items-center gap-2"> | ||
<div className="font-semibold capitalize">{wf.title}</div> | ||
</div> | ||
<div className="ml-auto flex items-center space-x-2"> | ||
<span | ||
className={cn( | ||
"flex h-2 w-2 rounded-full ", | ||
wf.status === "online" ? "bg-green-400" : "bg-gray-400" | ||
)} | ||
/> | ||
<span className="text-xs text-muted-foreground"> | ||
Last run: 2 hours ago | ||
</span> | ||
</div> | ||
</div> | ||
<div className="text-xs font-medium text-muted-foreground"> | ||
{wf.description ?? ""} | ||
</div> | ||
</div> | ||
</Link> | ||
))} | ||
</> | ||
)} | ||
</div> | ||
) | ||
} |