kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: web things; loading
rodzic
85ac47783d
commit
33fc9c331c
|
@ -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:",
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -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 />
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -11,5 +11,6 @@ export function Bootstrap() {
|
|||
bootstrap()
|
||||
}
|
||||
|
||||
// Return `null` so we can use this as a react component
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -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%;
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
}
|
||||
|
||||
.loadingAnimation {
|
||||
pointer-events: none;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
max-width: 50vw;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import { QueryClient } from '@tanstack/react-query'
|
||||
|
||||
export const queryClient = new QueryClient()
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue