feat: web things; loading

pull/715/head
Travis Fischer 2025-06-17 05:42:06 +07:00
rodzic 85ac47783d
commit 33fc9c331c
12 zmienionych plików z 167 dodań i 77 usunięć

Wyświetl plik

@ -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:",

Wyświetl plik

@ -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
</h1>
<pre>Auth Session: {JSON.stringify(ctx.api.authSession, null, 2)}</pre>
{!ctx || isLoading ? (
<LoadingIndicator />
) : (
<div className='mt-8'>
<h2 className='text-xl font-semibold mb-4'>Your Projects</h2>
{projects?.length === 0 ? (
<p>
No projects found. Create your first project to get started!
</p>
) : (
<div className='grid gap-4'>
{projects?.map((project) => (
<div
key={project.id}
className='p-4 border rounded-lg hover:border-gray-400 transition-colors'
>
<h3 className='font-medium'>{project.name}</h3>
<p className='text-sm text-gray-500'>
{project.identifier}
</p>
{project.lastPublishedDeployment && (
<p className='text-sm text-gray-500 mt-1'>
Last published:{' '}
{project.lastPublishedDeployment.version ||
project.lastPublishedDeployment.hash}
</p>
)}
</div>
))}
</div>
)}
</div>
)}
</section>
</>
)

Wyświetl plik

@ -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 <LoadingIndicator />
}

Wyświetl plik

@ -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 (
<html lang='en' suppressHydrationWarning>
<body className={`${geist.variable} antialiased`}>
<PostHogProvider>
<AgenticProvider>
<ThemeProvider
attribute='class'
defaultTheme='dark'
disableTransitionOnChange
>
<div className={styles.root}>
<Header />
<Providers>
<div className={styles.root}>
<Header />
<main className={cs(styles.main, 'pt-8 pb-16 px-4 md:px-0')}>
{children}
</main>
<main className={cs(styles.main, 'pt-8 pb-16 px-4 md:px-0')}>
{children}
</main>
<Toaster richColors />
<Footer />
</div>
</ThemeProvider>
</AgenticProvider>
</PostHogProvider>
<Toaster richColors />
<Footer />
</div>
<Bootstrap />
<Bootstrap />
</Providers>
</body>
</html>
)

Wyświetl plik

@ -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 (
<PostHogProvider>
<AgenticProvider>
<ThemeProvider
attribute='class'
defaultTheme='dark'
disableTransitionOnChange
>
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
</ThemeProvider>
</AgenticProvider>
</PostHogProvider>
)
}

Wyświetl plik

@ -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

Wyświetl plik

@ -11,5 +11,6 @@ export function Bootstrap() {
bootstrap()
}
// Return `null` so we can use this as a react component
return null
}

Wyświetl plik

@ -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%;

Wyświetl plik

@ -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 (
<AnimatePresence>
{isLoading ? (
<motion.div
className={cs(styles.loading, fill && styles.fill, className)}
initial={{ opacity: 1, ...initial }}
animate={{ opacity: 1, ...animate }}
exit={{ opacity: 0, ...exit }}
{...rest}
>
<Lottie
play
loop
animationData={
resolvedTheme === 'dark' ? loadingDark : loadingLight
}
className={styles.loadingAnimation}
/>
</motion.div>
) : null}
</AnimatePresence>
<Lottie
play
loop
animationData={resolvedTheme === 'dark' ? loadingDark : loadingLight}
className={cn(styles.loadingAnimation, className)}
/>
// <AnimatePresence>
// {isLoading ? (
// <motion.div
// className={cn(styles.loading, fill && styles.fill, className)}
// transition={{ duration: 10 }}
// exit={{ opacity: 0, ...exit }}
// {...rest}
// >
// <Lottie
// play
// loop
// animationData={
// resolvedTheme === 'dark' ? loadingDark : loadingLight
// }
// className={styles.loadingAnimation}
// />
// </motion.div>
// ) : null}
// </AnimatePresence>
)
}

Wyświetl plik

@ -15,6 +15,7 @@
}
.loadingAnimation {
pointer-events: none;
width: 200px;
height: 200px;
max-width: 50vw;

Wyświetl plik

@ -0,0 +1,3 @@
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient()

Wyświetl plik

@ -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