diff --git a/apps/web/package.json b/apps/web/package.json index e85fe8ea..5ccd7a0a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,6 +30,8 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tooltip": "^1.2.7", "@tanstack/react-form": "^1.12.3", + "@tanstack/react-query": "^5.80.7", + "@tanstack/react-query-devtools": "^5.80.7", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "ky": "catalog:", diff --git a/apps/web/src/app/app/page.tsx b/apps/web/src/app/app/page.tsx index 8b7766e2..09c6ea40 100644 --- a/apps/web/src/app/app/page.tsx +++ b/apps/web/src/app/app/page.tsx @@ -1,12 +1,20 @@ 'use client' +import { useQuery } from '@tanstack/react-query' + import { useAuthenticatedAgentic } from '@/components/agentic-provider' +import { LoadingIndicator } from '@/components/loading-indicator' export default function AppIndexPage() { const ctx = useAuthenticatedAgentic() - - // TODO: loading - if (!ctx) return null + const { data: projects, isLoading } = useQuery({ + queryKey: ['projects'], + queryFn: () => + ctx?.api + .listProjects({ populate: ['lastPublishedDeployment'] }) + .catch(() => []), + enabled: !!ctx + }) return ( <> @@ -15,7 +23,39 @@ export default function AppIndexPage() { Authenticated Dashboard -
Auth Session: {JSON.stringify(ctx.api.authSession, null, 2)}
+ {!ctx || isLoading ? ( + + ) : ( +
+

Your Projects

+ {projects?.length === 0 ? ( +

+ No projects found. Create your first project to get started! +

+ ) : ( +
+ {projects?.map((project) => ( +
+

{project.name}

+

+ {project.identifier} +

+ {project.lastPublishedDeployment && ( +

+ Last published:{' '} + {project.lastPublishedDeployment.version || + project.lastPublishedDeployment.hash} +

+ )} +
+ ))} +
+ )} +
+ )} ) diff --git a/apps/web/src/app/auth/[provider]/success/success-page.tsx b/apps/web/src/app/auth/[provider]/success/success-page.tsx index 6b5b54b4..18dca26b 100644 --- a/apps/web/src/app/auth/[provider]/success/success-page.tsx +++ b/apps/web/src/app/auth/[provider]/success/success-page.tsx @@ -4,6 +4,7 @@ import { redirect, RedirectType, useSearchParams } from 'next/navigation' import { useEffect } from 'react' import { useUnauthenticatedAgentic } from '@/components/agentic-provider' +import { LoadingIndicator } from '@/components/loading-indicator' import { toastError } from '@/lib/notifications' export function SuccessPage({ @@ -41,6 +42,5 @@ export function SuccessPage({ })() }, [code, ctx]) - // TODO: show a loading state - return null + return } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 8ee8d91d..6784ec12 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -5,14 +5,12 @@ import cs from 'clsx' import { Geist } from 'next/font/google' import { Toaster } from 'sonner' -import { AgenticProvider } from '@/components/agentic-provider' import { Bootstrap } from '@/components/bootstrap' import { Footer } from '@/components/footer' import { Header } from '@/components/header' -import { PostHogProvider } from '@/components/posthog-provider' -import { ThemeProvider } from '@/components/theme-provider' import * as config from '@/lib/config' +import Providers from './providers' import styles from './styles.module.css' const geist = Geist({ @@ -50,28 +48,20 @@ export default function RootLayout({ return ( - - - -
-
+ +
+
-
- {children} -
+
+ {children} +
- -
-
- - - + +
+
- + + ) diff --git a/apps/web/src/app/providers.tsx b/apps/web/src/app/providers.tsx new file mode 100644 index 00000000..54b5cca2 --- /dev/null +++ b/apps/web/src/app/providers.tsx @@ -0,0 +1,30 @@ +'use client' + +import type React from 'react' +import { QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' + +import { AgenticProvider } from '@/components/agentic-provider' +import { PostHogProvider } from '@/components/posthog-provider' +import { ThemeProvider } from '@/components/theme-provider' +import { queryClient } from '@/lib/query-client' + +export default function Providers({ children }: { children: React.ReactNode }) { + return ( + + + + + {children} + + + + + + + ) +} diff --git a/apps/web/src/components/agentic-provider.tsx b/apps/web/src/components/agentic-provider.tsx index 5c54ff8d..82a86bd8 100644 --- a/apps/web/src/components/agentic-provider.tsx +++ b/apps/web/src/components/agentic-provider.tsx @@ -37,16 +37,16 @@ export function AgenticProvider({ children }: { children: ReactNode }) { api: new AgenticApiClient({ apiBaseUrl: config.apiBaseUrl, onUpdateAuth: (updatedAuthSession) => { - console.log('onUpdateAuth', updatedAuthSession) + // console.log('onUpdateAuth', updatedAuthSession) if ( !!authSession !== !!updatedAuthSession && authSession?.token !== updatedAuthSession?.token ) { - console.log('setAuthSession', updatedAuthSession) + // console.log('setAuthSession', updatedAuthSession) setAuthSession(updatedAuthSession) } else { - console.log('auth session not updated') + // console.log('auth session not updated') } } }), @@ -54,7 +54,7 @@ export function AgenticProvider({ children }: { children: ReactNode }) { }) useEffect(() => { - console.log('updating session from localStorage', authSession?.token) + // console.log('updating session from localStorage', authSession?.token) if (agenticContext.current) { if (authSession) { agenticContext.current.api.authSession = authSession diff --git a/apps/web/src/components/bootstrap.tsx b/apps/web/src/components/bootstrap.tsx index c4926552..b663574a 100644 --- a/apps/web/src/components/bootstrap.tsx +++ b/apps/web/src/components/bootstrap.tsx @@ -11,5 +11,6 @@ export function Bootstrap() { bootstrap() } + // Return `null` so we can use this as a react component return null } diff --git a/apps/web/src/components/header/styles.module.css b/apps/web/src/components/header/styles.module.css index e83d5ee0..b385bd48 100644 --- a/apps/web/src/components/header/styles.module.css +++ b/apps/web/src/components/header/styles.module.css @@ -26,13 +26,6 @@ backdrop-filter: saturate(180%) blur(20px); } -/* Workaround for Firefox not supporting backdrop-filter yet */ -@-moz-document url-prefix() { - :global(.dark) .header { - background: hsla(203, 8%, 20%, 0.8); - } -} - .headerContent { align-self: center; width: 100%; diff --git a/apps/web/src/components/loading-indicator/index.tsx b/apps/web/src/components/loading-indicator/index.tsx index fd4c1587..89fb377c 100644 --- a/apps/web/src/components/loading-indicator/index.tsx +++ b/apps/web/src/components/loading-indicator/index.tsx @@ -1,10 +1,11 @@ 'use client' -import cs from 'clsx' -import { AnimatePresence, motion } from 'motion/react' +// import { AnimatePresence, motion } from 'motion/react' import dynamic from 'next/dynamic' import { useTheme } from 'next-themes' +import { cn } from '@/lib/utils' + import loadingDark from './loading-dark.json' import loadingLight from './loading-light.json' import styles from './styles.module.css' @@ -13,44 +14,35 @@ const Lottie = dynamic(() => import('react-lottie-player'), { ssr: false }) -export function LoadingIndicator({ - isLoading = true, - fill = false, - className, - initial, - animate, - exit, - ...rest -}: { - isLoading?: boolean - fill?: boolean - className?: string - initial?: any - animate?: any - exit?: any -}) { +export function LoadingIndicator({ className }: { className?: string }) { const { resolvedTheme } = useTheme() return ( - - {isLoading ? ( - - - - ) : null} - + + + // + // {isLoading ? ( + // + // + // + // ) : null} + // ) } diff --git a/apps/web/src/components/loading-indicator/styles.module.css b/apps/web/src/components/loading-indicator/styles.module.css index 38c30e88..106b53e8 100644 --- a/apps/web/src/components/loading-indicator/styles.module.css +++ b/apps/web/src/components/loading-indicator/styles.module.css @@ -15,6 +15,7 @@ } .loadingAnimation { + pointer-events: none; width: 200px; height: 200px; max-width: 50vw; diff --git a/apps/web/src/lib/query-client.ts b/apps/web/src/lib/query-client.ts new file mode 100644 index 00000000..9f692601 --- /dev/null +++ b/apps/web/src/lib/query-client.ts @@ -0,0 +1,3 @@ +import { QueryClient } from '@tanstack/react-query' + +export const queryClient = new QueryClient() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7ee7761b..173ef1a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -536,6 +536,12 @@ importers: '@tanstack/react-form': specifier: ^1.12.3 version: 1.12.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-query': + specifier: ^5.80.7 + version: 5.80.7(react@19.1.0) + '@tanstack/react-query-devtools': + specifier: ^5.80.7 + version: 5.80.7(@tanstack/react-query@5.80.7(react@19.1.0))(react@19.1.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -3261,6 +3267,12 @@ packages: '@tanstack/form-core@1.12.3': resolution: {integrity: sha512-H59XYP8Jxg8vT4IYIZa1BHkYiyiZqFcLSD2HpxefHP/vlG06/spCySVe/vGAP7IJgHHSAlEqBhQoy1Mg2ruTRA==} + '@tanstack/query-core@5.80.7': + resolution: {integrity: sha512-s09l5zeUKC8q7DCCCIkVSns8zZrK4ZDT6ryEjxNBFi68G4z2EBobBS7rdOY3r6W1WbUDpc1fe5oY+YO/+2UVUg==} + + '@tanstack/query-devtools@5.80.0': + resolution: {integrity: sha512-D6gH4asyjaoXrCOt5vG5Og/YSj0D/TxwNQgtLJIgWbhbWCC/emu2E92EFoVHh4ppVWg1qT2gKHvKyQBEFZhCuA==} + '@tanstack/react-form@1.12.3': resolution: {integrity: sha512-IlWLVKizjV+CzMgzaSac61bS4/UeSL2gceGOVIv+gKs2SriDIyylJd8AcqsKE/kNplm1K0NYXIF2Vk/re+JfOg==} peerDependencies: @@ -3273,6 +3285,17 @@ packages: vinxi: optional: true + '@tanstack/react-query-devtools@5.80.7': + resolution: {integrity: sha512-7Dz/19fVo0i+jgLVBabV5vfGOlLyN5L1w8w1/ogFhe6ItNNsNA+ZgNTbtiKpbR3CcX2WDRRTInz1uMSmHzTsoQ==} + peerDependencies: + '@tanstack/react-query': ^5.80.7 + react: ^18 || ^19 + + '@tanstack/react-query@5.80.7': + resolution: {integrity: sha512-u2F0VK6+anItoEvB3+rfvTO9GEh2vb00Je05OwlUe/A0lkJBgW1HckiY3f9YZa+jx6IOe4dHPh10dyp9aY3iRQ==} + peerDependencies: + react: ^18 || ^19 + '@tanstack/react-store@0.7.1': resolution: {integrity: sha512-qUTEKdId6QPWGiWyKAPf/gkN29scEsz6EUSJ0C3HgLMgaqTAyBsQ2sMCfGVcqb+kkhEXAdjleCgH6LAPD6f2sA==} peerDependencies: @@ -9013,6 +9036,10 @@ snapshots: dependencies: '@tanstack/store': 0.7.1 + '@tanstack/query-core@5.80.7': {} + + '@tanstack/query-devtools@5.80.0': {} + '@tanstack/react-form@1.12.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/form-core': 1.12.3 @@ -9023,6 +9050,17 @@ snapshots: transitivePeerDependencies: - react-dom + '@tanstack/react-query-devtools@5.80.7(@tanstack/react-query@5.80.7(react@19.1.0))(react@19.1.0)': + dependencies: + '@tanstack/query-devtools': 5.80.0 + '@tanstack/react-query': 5.80.7(react@19.1.0) + react: 19.1.0 + + '@tanstack/react-query@5.80.7(react@19.1.0)': + dependencies: + '@tanstack/query-core': 5.80.7 + react: 19.1.0 + '@tanstack/react-store@0.7.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/store': 0.7.1