kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: web app work
rodzic
2c8eb5de2d
commit
109993943b
|
@ -0,0 +1,23 @@
|
|||
import { AppConsumersList } from '@/components/app-consumers-list'
|
||||
import { AppProjectsList } from '@/components/app-projects-list'
|
||||
|
||||
export function AppDashboard() {
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold mb-8'
|
||||
>
|
||||
Dashboard
|
||||
</h1>
|
||||
|
||||
<div className='flex gap-8 space-around'>
|
||||
<AppConsumersList />
|
||||
|
||||
<AppProjectsList />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import useInfiniteScroll from 'react-infinite-scroll-hook'
|
||||
|
||||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { useInfiniteQuery } from '@/lib/query-client'
|
||||
|
||||
export function AppIndex() {
|
||||
const ctx = useAuthenticatedAgentic()
|
||||
const limit = 10
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isError,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['projects', ctx?.api.authSession?.user?.id],
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
ctx!.api
|
||||
.listProjects({
|
||||
populate: ['lastPublishedDeployment'],
|
||||
offset: pageParam,
|
||||
limit
|
||||
})
|
||||
.then(async (projects) => {
|
||||
return {
|
||||
projects,
|
||||
offset: pageParam,
|
||||
limit,
|
||||
nextOffset:
|
||||
projects.length >= limit ? pageParam + projects.length : undefined
|
||||
}
|
||||
}),
|
||||
getNextPageParam: (lastGroup) => lastGroup?.nextOffset,
|
||||
enabled: !!ctx,
|
||||
initialPageParam: 0
|
||||
})
|
||||
|
||||
const [sentryRef] = useInfiniteScroll({
|
||||
loading: isLoading || isFetchingNextPage,
|
||||
hasNextPage,
|
||||
onLoadMore: fetchNextPage,
|
||||
disabled: !ctx || isError,
|
||||
rootMargin: '0px 0px 200px 0px'
|
||||
})
|
||||
|
||||
const projects = data ? data.pages.flatMap((p) => p.projects) : []
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<h1
|
||||
className='text-center text-balance leading-snug md:leading-none
|
||||
text-4xl font-extrabold'
|
||||
>
|
||||
Dashboard
|
||||
</h1>
|
||||
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<div className='mt-8'>
|
||||
<h2 className='text-xl font-semibold mb-4'>Your Projects</h2>
|
||||
|
||||
{isError ? (
|
||||
<p>Error fetching projects</p>
|
||||
) : !projects.length ? (
|
||||
<p>
|
||||
No projects found. Create your first project to get started!
|
||||
</p>
|
||||
) : (
|
||||
<div className='grid gap-4'>
|
||||
{projects.map((project) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
className='p-4 border rounded-lg hover:border-gray-400 transition-colors'
|
||||
href={`/app/projects/${project.identifier}`}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{hasNextPage && (
|
||||
<div ref={sentryRef} className=''>
|
||||
{isLoading || (isFetchingNextPage && <LoadingIndicator />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,57 +1,6 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import useInfiniteScroll from 'react-infinite-scroll-hook'
|
||||
|
||||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { useInfiniteQuery } from '@/lib/query-client'
|
||||
import { AppConsumersList } from '@/components/app-consumers-list'
|
||||
|
||||
export function AppConsumersIndex() {
|
||||
const ctx = useAuthenticatedAgentic()
|
||||
const limit = 10
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isError,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['consumers', ctx?.api.authSession?.user?.id],
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
ctx!.api
|
||||
.listConsumers({
|
||||
populate: ['project'],
|
||||
offset: pageParam,
|
||||
limit
|
||||
})
|
||||
.then(async (consumers) => {
|
||||
return {
|
||||
consumers,
|
||||
offset: pageParam,
|
||||
limit,
|
||||
nextOffset:
|
||||
consumers.length >= limit
|
||||
? pageParam + consumers.length
|
||||
: undefined
|
||||
}
|
||||
}),
|
||||
getNextPageParam: (lastGroup) => lastGroup?.nextOffset,
|
||||
enabled: !!ctx,
|
||||
initialPageParam: 0
|
||||
})
|
||||
|
||||
const [sentryRef] = useInfiniteScroll({
|
||||
loading: isLoading || isFetchingNextPage,
|
||||
hasNextPage,
|
||||
onLoadMore: fetchNextPage,
|
||||
disabled: !ctx || isError,
|
||||
rootMargin: '0px 0px 200px 0px'
|
||||
})
|
||||
|
||||
const consumers = data ? data.pages.flatMap((p) => p.consumers) : []
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
|
@ -62,46 +11,7 @@ export function AppConsumersIndex() {
|
|||
Your Subscriptions
|
||||
</h1>
|
||||
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<div className='mt-8'>
|
||||
{isError ? (
|
||||
<p>Error fetching customer subscriptions</p>
|
||||
) : !consumers.length ? (
|
||||
<p>
|
||||
No subscriptions found. Subscribe to your first project to get
|
||||
started!
|
||||
</p>
|
||||
) : (
|
||||
<div className='grid gap-4'>
|
||||
{consumers.map((consumer) => (
|
||||
<Link
|
||||
key={consumer.id}
|
||||
className='p-4 border rounded-lg hover:border-gray-400 transition-colors'
|
||||
href={`/app/consumers/${consumer.id}`}
|
||||
>
|
||||
<h3 className='font-medium'>{consumer.project.name}</h3>
|
||||
|
||||
<p className='text-sm text-gray-500'>
|
||||
{consumer.project.identifier}
|
||||
</p>
|
||||
|
||||
<pre className='max-w-lg'>
|
||||
{JSON.stringify(consumer, null, 2)}
|
||||
</pre>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{hasNextPage && (
|
||||
<div ref={sentryRef} className=''>
|
||||
{isLoading || (isFetchingNextPage && <LoadingIndicator />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<AppConsumersList />
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { AppIndex } from './app-index'
|
||||
import { AppDashboard } from './app-dashboard'
|
||||
|
||||
export default function AppIndexPage() {
|
||||
return <AppIndex />
|
||||
return <AppDashboard />
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { AppProjectsList } from '@/components/app-projects-list'
|
||||
|
||||
export function AppProjectsIndex() {
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<div className='flex gap-8 space-around'>
|
||||
<AppProjectsList />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { AppProjectsIndex } from './app-projects-index'
|
||||
|
||||
export default function AppProjectsIndexPage() {
|
||||
return <AppProjectsIndex />
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import useInfiniteScroll from 'react-infinite-scroll-hook'
|
||||
|
||||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { useInfiniteQuery } from '@/lib/query-client'
|
||||
|
||||
export function AppConsumersList() {
|
||||
const ctx = useAuthenticatedAgentic()
|
||||
const limit = 10
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isError,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['consumers', ctx?.api.authSession?.user?.id],
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
ctx!.api
|
||||
.listConsumers({
|
||||
populate: ['project'],
|
||||
offset: pageParam,
|
||||
limit
|
||||
})
|
||||
.then(async (consumers) => {
|
||||
return {
|
||||
consumers,
|
||||
offset: pageParam,
|
||||
limit,
|
||||
nextOffset:
|
||||
consumers.length >= limit
|
||||
? pageParam + consumers.length
|
||||
: undefined
|
||||
}
|
||||
}),
|
||||
getNextPageParam: (lastGroup) => lastGroup?.nextOffset,
|
||||
enabled: !!ctx,
|
||||
initialPageParam: 0
|
||||
})
|
||||
|
||||
const [sentryRef] = useInfiniteScroll({
|
||||
loading: isLoading || isFetchingNextPage,
|
||||
hasNextPage,
|
||||
onLoadMore: fetchNextPage,
|
||||
disabled: !ctx || isError,
|
||||
rootMargin: '0px 0px 200px 0px'
|
||||
})
|
||||
|
||||
const consumers = data ? data.pages.flatMap((p) => p.consumers) : []
|
||||
|
||||
return (
|
||||
<>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<div className='mt-8'>
|
||||
<h2 className='text-xl font-semibold mb-4'>Your Subscriptions</h2>
|
||||
|
||||
{isError ? (
|
||||
<p>Error fetching customer subscriptions</p>
|
||||
) : !consumers.length ? (
|
||||
<p>
|
||||
No subscriptions found. Subscribe to your first project to get
|
||||
started!
|
||||
</p>
|
||||
) : (
|
||||
<div className='grid gap-4'>
|
||||
{consumers.map((consumer) => (
|
||||
<Link
|
||||
key={consumer.id}
|
||||
className='p-4 border rounded-lg hover:border-gray-400 transition-colors overflow-hidden'
|
||||
href={`/app/consumers/${consumer.id}`}
|
||||
>
|
||||
<h3 className='font-medium'>{consumer.project.name}</h3>
|
||||
|
||||
<p className='text-sm text-gray-500'>
|
||||
{consumer.project.identifier}
|
||||
</p>
|
||||
|
||||
<pre className='max-w-lg'>
|
||||
{JSON.stringify(consumer, null, 2)}
|
||||
</pre>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{hasNextPage && (
|
||||
<div ref={sentryRef} className=''>
|
||||
{isLoading || (isFetchingNextPage && <LoadingIndicator />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import useInfiniteScroll from 'react-infinite-scroll-hook'
|
||||
|
||||
import { useAuthenticatedAgentic } from '@/components/agentic-provider'
|
||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||
import { useInfiniteQuery } from '@/lib/query-client'
|
||||
|
||||
export function AppProjectsList() {
|
||||
const ctx = useAuthenticatedAgentic()
|
||||
const limit = 10
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isError,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ['projects', ctx?.api.authSession?.user?.id],
|
||||
queryFn: ({ pageParam = 0 }) =>
|
||||
ctx!.api
|
||||
.listProjects({
|
||||
populate: ['lastPublishedDeployment'],
|
||||
offset: pageParam,
|
||||
limit
|
||||
})
|
||||
.then(async (projects) => {
|
||||
return {
|
||||
projects,
|
||||
offset: pageParam,
|
||||
limit,
|
||||
nextOffset:
|
||||
projects.length >= limit ? pageParam + projects.length : undefined
|
||||
}
|
||||
}),
|
||||
getNextPageParam: (lastGroup) => lastGroup?.nextOffset,
|
||||
enabled: !!ctx,
|
||||
initialPageParam: 0
|
||||
})
|
||||
|
||||
const [sentryRef] = useInfiniteScroll({
|
||||
loading: isLoading || isFetchingNextPage,
|
||||
hasNextPage,
|
||||
onLoadMore: fetchNextPage,
|
||||
disabled: !ctx || isError,
|
||||
rootMargin: '0px 0px 200px 0px'
|
||||
})
|
||||
|
||||
const projects = data ? data.pages.flatMap((p) => p.projects) : []
|
||||
|
||||
return (
|
||||
<>
|
||||
{!ctx || isLoading ? (
|
||||
<LoadingIndicator />
|
||||
) : (
|
||||
<div className='mt-8'>
|
||||
<h2 className='text-xl font-semibold mb-4'>Your Projects</h2>
|
||||
|
||||
{isError ? (
|
||||
<p>Error fetching projects</p>
|
||||
) : !projects.length ? (
|
||||
<p>No projects found. Create your first project to get started!</p>
|
||||
) : (
|
||||
<div className='grid gap-4'>
|
||||
{projects.map((project) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
className='p-4 border rounded-lg hover:border-gray-400 transition-colors overflow-hidden'
|
||||
href={`/app/projects/${project.identifier}`}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{hasNextPage && (
|
||||
<div ref={sentryRef} className=''>
|
||||
{isLoading || (isFetchingNextPage && <LoadingIndicator />)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
Ładowanie…
Reference in New Issue