kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: marketplace public project detail page looking better now
rodzic
d14f214c08
commit
d85098b8b9
|
@ -51,7 +51,7 @@ export default function RootLayout({
|
||||||
<div className='relative w-full min-h-[100vh] flex flex-col items-center'>
|
<div className='relative w-full min-h-[100vh] flex flex-col items-center'>
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<main className='relative w-full flex-1 flex flex-col items-center gap-16 pt-16 pb-24 px-2 overflow-hidden'>
|
<main className='relative w-full flex-1 flex flex-col items-center gap-16 py-16 px-2 overflow-hidden'>
|
||||||
{children}
|
{children}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { parseProjectIdentifier } from '@agentic/platform-validators'
|
||||||
|
import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
|
import { toastError } from '@/lib/notifications'
|
||||||
|
|
||||||
|
import { MarketplacePublicProjectDetail } from '../marketplace-public-project-detail'
|
||||||
|
import {
|
||||||
|
type MarketplacePublicProjectDetailTab,
|
||||||
|
marketplacePublicProjectDetailTabsSet
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
|
export default async function PublicProjectDetailPageTabPage({
|
||||||
|
params
|
||||||
|
}: {
|
||||||
|
params: Promise<{
|
||||||
|
namespace: string
|
||||||
|
'project-slug': string
|
||||||
|
tab: string
|
||||||
|
}>
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
namespace: rawNamespace,
|
||||||
|
'project-slug': rawProjectSlug,
|
||||||
|
tab
|
||||||
|
} = await params
|
||||||
|
|
||||||
|
if (!marketplacePublicProjectDetailTabsSet.has(tab)) {
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const namespace = decodeURIComponent(rawNamespace)
|
||||||
|
const projectSlug = decodeURIComponent(rawProjectSlug)
|
||||||
|
|
||||||
|
const { projectIdentifier } = parseProjectIdentifier(
|
||||||
|
`${namespace}/${projectSlug}`,
|
||||||
|
{ strict: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MarketplacePublicProjectDetail
|
||||||
|
projectIdentifier={projectIdentifier}
|
||||||
|
tab={tab as MarketplacePublicProjectDetailTab}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} catch (err: any) {
|
||||||
|
void toastError(err, { label: 'Invalid project identifier' })
|
||||||
|
|
||||||
|
return notFound()
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
|
||||||
import { useAgentic } from '@/components/agentic-provider'
|
import { useAgentic } from '@/components/agentic-provider'
|
||||||
import { ExampleUsage } from '@/components/example-usage'
|
import { ExampleUsage } from '@/components/example-usage'
|
||||||
|
import { HeroButton } from '@/components/hero-button'
|
||||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||||
import { PageContainer } from '@/components/page-container'
|
import { PageContainer } from '@/components/page-container'
|
||||||
import { ProjectPricingPlans } from '@/components/project-pricing-plans'
|
import { ProjectPricingPlans } from '@/components/project-pricing-plans'
|
||||||
|
@ -24,12 +25,17 @@ import { GitHubIcon } from '@/icons/github'
|
||||||
import { toast, toastError } from '@/lib/notifications'
|
import { toast, toastError } from '@/lib/notifications'
|
||||||
import { useQuery } from '@/lib/query-client'
|
import { useQuery } from '@/lib/query-client'
|
||||||
|
|
||||||
const MAX_TOOLS_TO_SHOW = 5
|
import {
|
||||||
|
type MarketplacePublicProjectDetailTab,
|
||||||
|
MAX_TOOLS_TO_SHOW
|
||||||
|
} from './utils'
|
||||||
|
|
||||||
export function MarketplaceProjectIndex({
|
export function MarketplacePublicProjectDetail({
|
||||||
projectIdentifier
|
projectIdentifier,
|
||||||
|
tab = 'overview'
|
||||||
}: {
|
}: {
|
||||||
projectIdentifier: string
|
projectIdentifier: string
|
||||||
|
tab?: MarketplacePublicProjectDetailTab
|
||||||
}) {
|
}) {
|
||||||
const ctx = useAgentic()
|
const ctx = useAgentic()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
@ -184,7 +190,18 @@ export function MarketplaceProjectIndex({
|
||||||
<div className='flex flex-col gap-4 w-full'>
|
<div className='flex flex-col gap-4 w-full'>
|
||||||
<ProjectHeader project={project} />
|
<ProjectHeader project={project} />
|
||||||
|
|
||||||
<Tabs defaultValue='overview'>
|
<Tabs
|
||||||
|
value={tab}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
if (value === 'overview') {
|
||||||
|
router.push(`/marketplace/projects/${projectIdentifier}`)
|
||||||
|
} else {
|
||||||
|
router.push(
|
||||||
|
`/marketplace/projects/${projectIdentifier}/${value}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value='overview' className='cursor-pointer'>
|
<TabsTrigger value='overview' className='cursor-pointer'>
|
||||||
Overview
|
Overview
|
||||||
|
@ -393,7 +410,7 @@ export function MarketplaceProjectIndex({
|
||||||
function ProjectHeader({ project }: { project: Project }) {
|
function ProjectHeader({ project }: { project: Project }) {
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col gap-2'>
|
<div className='flex flex-col gap-2'>
|
||||||
<div className='flex flex-row gap-2.5 items-center'>
|
<div className='w-full flex flex-row gap-2.5 items-center'>
|
||||||
<img
|
<img
|
||||||
src={
|
src={
|
||||||
project.lastPublishedDeployment?.iconUrl ||
|
project.lastPublishedDeployment?.iconUrl ||
|
||||||
|
@ -404,13 +421,19 @@ function ProjectHeader({ project }: { project: Project }) {
|
||||||
className='aspect-square w-10 h-10'
|
className='aspect-square w-10 h-10'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h1 className='font-semibold text-balance text-3xl leading-tight'>
|
<h1 className='flex-1 font-semibold text-balance text-3xl leading-tight'>
|
||||||
{project.name}
|
{project.name}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
<HeroButton heroVariant='orange' className='justify-self-end'>
|
||||||
|
<Link href={`/marketplace/projects/${project.identifier}/pricing`}>
|
||||||
|
Subscribe to {project.identifier}
|
||||||
|
</Link>
|
||||||
|
</HeroButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='flex flex-row items-center'>
|
<div className='flex flex-row items-center'>
|
||||||
<div className='text-sm text-gray-500 flex flex-row gap-0.5 items-center hover:no-underline! no-underline!'>
|
<div className='text-sm text-muted-foreground flex flex-row gap-0.5 items-center hover:no-underline! no-underline!'>
|
||||||
<span>{project.identifier}</span>
|
<span>{project.identifier}</span>
|
||||||
|
|
||||||
{/* TODO: <CopyIcon className='w-4 h-4' /> */}
|
{/* TODO: <CopyIcon className='w-4 h-4' /> */}
|
||||||
|
@ -420,7 +443,9 @@ function ProjectHeader({ project }: { project: Project }) {
|
||||||
<Button asChild variant='link'>
|
<Button asChild variant='link'>
|
||||||
<Link
|
<Link
|
||||||
href={project.lastPublishedDeployment.websiteUrl}
|
href={project.lastPublishedDeployment.websiteUrl}
|
||||||
className='text-sm flex flex-row gap-1.5! items-center text-gray-500! py-1! px-2!'
|
className='text-sm flex flex-row gap-1.5! items-center text-muted-foreground! py-1! px-2!'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
>
|
>
|
||||||
<ExternalLinkIcon className='w-4 h-4' />
|
<ExternalLinkIcon className='w-4 h-4' />
|
||||||
|
|
||||||
|
@ -433,7 +458,9 @@ function ProjectHeader({ project }: { project: Project }) {
|
||||||
<Button asChild variant='link'>
|
<Button asChild variant='link'>
|
||||||
<Link
|
<Link
|
||||||
href={project.lastPublishedDeployment.sourceUrl}
|
href={project.lastPublishedDeployment.sourceUrl}
|
||||||
className='text-sm flex flex-row gap-1.5! items-center text-gray-500! py-1! px-2!'
|
className='text-sm flex flex-row gap-1.5! items-center text-muted-foreground! py-1! px-2!'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
>
|
>
|
||||||
<GitHubIcon className='w-4 h-4' />
|
<GitHubIcon className='w-4 h-4' />
|
||||||
|
|
|
@ -3,9 +3,9 @@ import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
import { toastError } from '@/lib/notifications'
|
import { toastError } from '@/lib/notifications'
|
||||||
|
|
||||||
import { MarketplaceProjectIndex } from './marketplace-project-index'
|
import { MarketplacePublicProjectDetail } from './marketplace-public-project-detail'
|
||||||
|
|
||||||
export default async function MarketplaceProjectIndexPage({
|
export default async function MarketplacePublicProjectDetailPage({
|
||||||
params
|
params
|
||||||
}: {
|
}: {
|
||||||
params: Promise<{
|
params: Promise<{
|
||||||
|
@ -25,7 +25,9 @@ export default async function MarketplaceProjectIndexPage({
|
||||||
{ strict: true }
|
{ strict: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
return <MarketplaceProjectIndex projectIdentifier={projectIdentifier} />
|
return (
|
||||||
|
<MarketplacePublicProjectDetail projectIdentifier={projectIdentifier} />
|
||||||
|
)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
void toastError(err, { label: 'Invalid project identifier' })
|
void toastError(err, { label: 'Invalid project identifier' })
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const MAX_TOOLS_TO_SHOW = 5
|
||||||
|
export const marketplacePublicProjectDetailTabs = [
|
||||||
|
'overview',
|
||||||
|
'tools',
|
||||||
|
'pricing',
|
||||||
|
'debug'
|
||||||
|
] as const
|
||||||
|
export const marketplacePublicProjectDetailTabsSet = new Set(
|
||||||
|
marketplacePublicProjectDetailTabs
|
||||||
|
)
|
||||||
|
export type MarketplacePublicProjectDetailTab =
|
||||||
|
(typeof marketplacePublicProjectDetailTabs)[number]
|
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 90 KiB Po Szerokość: | Wysokość: | Rozmiar: 80 KiB |
Plik binarny nie jest wyświetlany.
Przed Szerokość: | Wysokość: | Rozmiar: 90 KiB Po Szerokość: | Wysokość: | Rozmiar: 80 KiB |
Ładowanie…
Reference in New Issue