mirror of
https://github.com/luxfi/market.git
synced 2026-09-24 00:25:57 +00:00
Clean up to single static page
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Desktop, TouchDevice } from './scroll-snap'
|
||||
import { Header } from '@luxfi/ui'
|
||||
|
||||
type PageProps = {
|
||||
params: { slug: string };
|
||||
searchParams?: { [key: string]: string | string[] | undefined };
|
||||
}
|
||||
|
||||
import siteDef from '@/site-def'
|
||||
|
||||
const Page: React.FC<PageProps> = ({
|
||||
searchParams
|
||||
}) => {
|
||||
// see src/middleware.ts
|
||||
const agent = searchParams?.agent
|
||||
|
||||
if (agent === 'desktop') {
|
||||
return (<>
|
||||
<Header siteDef={siteDef}/>
|
||||
<Desktop />
|
||||
</>)
|
||||
}
|
||||
else if (agent === 'tablet') {
|
||||
return (<>
|
||||
<Header siteDef={siteDef}/>
|
||||
<TouchDevice isTablet={true} />
|
||||
</>)
|
||||
}
|
||||
return (<>
|
||||
<Header siteDef={siteDef}/>
|
||||
<TouchDevice isTablet={false} />
|
||||
</>)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -1,99 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { ApplyTypography } from '@hanzo/ui/primitives'
|
||||
import { DrawerMargin, Footer } from '@luxfi/ui'
|
||||
|
||||
import BannerComponent from '@/blocks/components/banner-block'
|
||||
import { landing } from '@/content'
|
||||
import siteDef from '@/site-def'
|
||||
|
||||
const MOBILE_VID_CONSTRAINT = {
|
||||
w: 360,
|
||||
h: 400
|
||||
}
|
||||
|
||||
// using this approach:
|
||||
// https://stackoverflow.com/a/76066443/11645689
|
||||
// @hanzo/ui/style/global.css
|
||||
const swipeOuter = 'snap-start snap-always h-[100vh] '
|
||||
// 44+16=60, 80+8=88
|
||||
const swipeInner = 'pt-[60px] md:pt-[88px] pb-[24px] '
|
||||
const swipeInnerDesk = swipeInner + 'h-full '
|
||||
const swipeInnerTouch = swipeInner + 'h-[100svh] '
|
||||
|
||||
const Desktop: React.FC = () => (<>
|
||||
|
||||
{landing.banners.map((banner, index) => (
|
||||
<div key={index} className={swipeOuter + 'px-4 lg:px-6 flex flex-col' }>
|
||||
<ApplyTypography
|
||||
asTag='section'
|
||||
className={
|
||||
swipeInnerDesk +
|
||||
// must use justify-between to trigger the 'grow' of the video (below)
|
||||
'w-full h-full flex flex-col items-center justify-between self-stretch grow '
|
||||
}
|
||||
>
|
||||
<BannerComponent
|
||||
block={banner}
|
||||
agent='desktop'
|
||||
grouping='separate'
|
||||
groupClx={{
|
||||
title: 'flex-none',
|
||||
media: 'grow',
|
||||
cta: 'flex-none xs:w-full md:w-auto md:min-w-[500px]'
|
||||
}}
|
||||
/>
|
||||
</ApplyTypography>
|
||||
<DrawerMargin clx='grow-0 shrink-0'/>
|
||||
</div>
|
||||
))}
|
||||
<div key='last' className={swipeOuter + 'swipe flex flex-col justify-start items-stretch'} >
|
||||
<Footer siteDef={siteDef} className='grow-0 w-full lg:mx-auto sm:pt-6 border-t-0 flex flex-col justify-between md:justify-start'/>
|
||||
<DrawerMargin clx='grow-0 shrink-0'/>
|
||||
</div>
|
||||
</>)
|
||||
|
||||
|
||||
const TouchDevice: React.FC<{
|
||||
isTablet: boolean
|
||||
}> = ({
|
||||
isTablet,
|
||||
}) => {
|
||||
|
||||
return (<>
|
||||
{landing.banners.map((banner, index) => (
|
||||
<div key={index} className={swipeOuter + 'flex flex-col px-4 xs:px-2'}>
|
||||
<ApplyTypography
|
||||
asTag='section'
|
||||
className={'w-full flex flex-col items-stretch grow ' + swipeInnerTouch +
|
||||
(isTablet ? 'gap-2 justify-around lg:justify-start lg:gap-24 '
|
||||
:
|
||||
'justify-between pb-4')
|
||||
}
|
||||
>
|
||||
<BannerComponent
|
||||
block={banner}
|
||||
agent={isTablet ? 'tablet' : 'phone'}
|
||||
groupClx={{
|
||||
title: 'md:pt-12 lg:pt-32',
|
||||
cta: 'md:mt-32 md:w-pr-70 lg:w-pr-60 md:self-center'
|
||||
}}
|
||||
videoConstraint={isTablet ? undefined : MOBILE_VID_CONSTRAINT}
|
||||
videoSize={isTablet ? 'lg' : 'md'}
|
||||
/>
|
||||
</ApplyTypography>
|
||||
<DrawerMargin clx='grow-0 shrink-0'/>
|
||||
</div>
|
||||
))}
|
||||
<div key='lastest' className={swipeOuter + 'flex flex-col'}>
|
||||
<Footer siteDef={siteDef} className={swipeInnerTouch + 'pl-[32px] pr-[24px] flex flex-col justify-between border-none grow'} noHorizPadding/>
|
||||
<DrawerMargin clx='grow-0 shrink-0'/>
|
||||
</div>
|
||||
</>)
|
||||
}
|
||||
|
||||
export {
|
||||
Desktop,
|
||||
TouchDevice
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import React from 'react'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import { Footer, Header, Main } from '@luxfi/ui'
|
||||
import type ProductDetailBlock from '@/blocks/def/product-detail-block'
|
||||
import ProductDetailBlockComponent from '@/blocks/components/product-detail-block'
|
||||
import { products } from '@/content'
|
||||
|
||||
import siteDef from '../../site-def'
|
||||
|
||||
type Props = {
|
||||
params: { slug: 'silver' | 'gold' | 'coin' | 'credit' | 'validator' | 'key' | 'uranium' }
|
||||
searchParams?: { [key: string]: string | string[] | undefined }
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
|
||||
const products = [
|
||||
'silver',
|
||||
'gold',
|
||||
'coin',
|
||||
'credit',
|
||||
'validator',
|
||||
'key',
|
||||
'uranium',
|
||||
]
|
||||
|
||||
return products.map((p) => ({
|
||||
slug: p,
|
||||
}))
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params}: Props) {
|
||||
const title = params.slug
|
||||
const capitalized = title.charAt(0).toUpperCase() + title.slice(1)
|
||||
return { title: capitalized }
|
||||
}
|
||||
|
||||
const ProductPage = ({ params, searchParams }: Props) => {
|
||||
|
||||
const product = products[params.slug] as ProductDetailBlock
|
||||
|
||||
if (!product) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
// see src/middleware.ts
|
||||
const agent = searchParams?.agent
|
||||
|
||||
return (<>
|
||||
<Header siteDef={siteDef}/>
|
||||
<Main className='md:flex-row md:gap-4'>
|
||||
<ProductDetailBlockComponent block={product} agent={agent as string}/>
|
||||
</Main>
|
||||
<div className='border-t'></div>
|
||||
<Footer siteDef={siteDef} className='w-full pt-16 lg:mx-auto ' />
|
||||
</>)
|
||||
}
|
||||
|
||||
export default ProductPage
|
||||
@@ -1,17 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { cn } from '@hanzo/ui/util'
|
||||
import { CheckoutPanel, Main } from '@luxfi/ui'
|
||||
|
||||
const Page: React.FC = () => (
|
||||
<Main /* id='CHECKOUT_MAIN' */ headerSpace={false} standardWidth={false} className={cn(
|
||||
'!px-0 !py-0',
|
||||
'w-full h-[100vh]',
|
||||
'animate-in md:zoom-in-90',
|
||||
'shadow-lg bg-background'
|
||||
)}>
|
||||
<CheckoutPanel clx='w-full h-full' />
|
||||
</Main>
|
||||
)
|
||||
|
||||
export default Page
|
||||
@@ -1,39 +0,0 @@
|
||||
'use client'
|
||||
import React, { useEffect } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
|
||||
import { cn } from '@hanzo/ui/util'
|
||||
|
||||
import { AddToCartWidget, useCommerce } from '@hanzo/commerce'
|
||||
|
||||
const AddPanel: React.FC<{
|
||||
clx?: string
|
||||
}> = observer(({
|
||||
clx='',
|
||||
}) => {
|
||||
|
||||
const cmmc = useCommerce()
|
||||
|
||||
useEffect(() => {
|
||||
cmmc.setCurrentItem('LXM-CR-E-24G')
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-10 items-center mt-10', clx)}>
|
||||
<p>Current Sku: {cmmc.currentItem ? cmmc.currentItem.sku : 'none'}</p>
|
||||
{cmmc.currentItem && (
|
||||
<AddToCartWidget item={cmmc.currentItem} className='w-[260px]'/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
/*
|
||||
{cmmc.currentItem ? (
|
||||
<MediaStack media={cmmc.currentItem} constrainTo={{w: 250, h: 250}} />
|
||||
) : (
|
||||
<Skeleton className='h-[250px]' />
|
||||
)}
|
||||
*/
|
||||
|
||||
export default AddPanel
|
||||
@@ -1,122 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { z } from 'zod'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
Input,
|
||||
ScrollArea,
|
||||
} from '@hanzo/ui/primitives'
|
||||
|
||||
import { useCommerce } from '@hanzo/commerce'
|
||||
import { peekDump } from '@hanzo/commerce/debug'
|
||||
|
||||
import { BuyButton } from '@luxfi/ui'
|
||||
|
||||
const INITIAL_SKU_PATH = 'LXM-CR'
|
||||
|
||||
const FormSchema = z.object({
|
||||
skupath: z.custom<string>((v) => (
|
||||
(typeof v === 'string' && v.split('-').length >= 2),
|
||||
"Sku Path must have at least two levels."
|
||||
))
|
||||
})
|
||||
|
||||
const InputForm: React.FC<{
|
||||
onSubmit: (data: z.infer<typeof FormSchema>) => void
|
||||
onClear: (() => void) | undefined
|
||||
className?: string
|
||||
}> = ({
|
||||
onSubmit,
|
||||
onClear,
|
||||
className
|
||||
}) => {
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
defaultValues: {
|
||||
skupath: '',
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className={className}>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='skupath'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>sku path</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder='eg, LXM-CR' {...field} className='border placeholder:text-muted-3' />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className={'grid gap-1 w-full min-w-0 ' + (onClear ? 'grid-cols-2': 'grid-cols-1')}>
|
||||
<Button className={(!!onClear ? 'shrink' : '')} type='submit'>Submit</Button>
|
||||
{onClear && <Button className={(!!onClear ? 'shrink' : '')} onClick={() => {form.reset(); onClear()}}>Clear</Button>}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
const BuyTab: React.FC = () => {
|
||||
|
||||
const cmmc = useCommerce()
|
||||
|
||||
const [skuPath, setSkuPath] = useState<string | undefined>(INITIAL_SKU_PATH)
|
||||
const [json, setJSON] = useState<string | undefined>(undefined)
|
||||
const [error, setError] = useState<string | undefined>(undefined)
|
||||
|
||||
const handleSubmit = (data: z.infer<typeof FormSchema>): void => {
|
||||
const _skuPath = data.skupath.toUpperCase()
|
||||
setSkuPath(_skuPath)
|
||||
const result = cmmc.peek(_skuPath)
|
||||
if (typeof result === 'string') {
|
||||
setError(result)
|
||||
setJSON(undefined)
|
||||
}
|
||||
else {
|
||||
setJSON(peekDump(result))
|
||||
setError(undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClear = () => {
|
||||
setSkuPath(undefined)
|
||||
setJSON(undefined)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='w-full md:w-[400px] md:mx-auto flex flex-col items-center gap-8'>
|
||||
<InputForm
|
||||
onSubmit={handleSubmit}
|
||||
onClear={skuPath ? handleClear : undefined}
|
||||
className='flex flex-col items-stretch w-full md:w-[250px] md:mx-auto'
|
||||
/>
|
||||
<div className='w-full'>
|
||||
<p className='text-muted text-center'>peek</p>
|
||||
<ScrollArea className='border rounded h-[300px] w-full p-4 text-muted flex flex-col gap-2'>
|
||||
{skuPath && <pre>PATH: {skuPath}</pre>}
|
||||
{error && <p className='text-destructive'>{error}</p>}
|
||||
{json && <pre>{json}</pre> }
|
||||
</ScrollArea>
|
||||
</div>
|
||||
{skuPath && <BuyButton skuPath={skuPath} className='' >Buy</BuyButton>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BuyTab
|
||||
@@ -1,43 +0,0 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
import {
|
||||
Tabs,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
TabsContent
|
||||
} from '@hanzo/ui/primitives'
|
||||
|
||||
import { Header, Main } from '@luxfi/ui'
|
||||
|
||||
import siteDef from '@/site-def'
|
||||
|
||||
import AddPanel from './_page/add-panel'
|
||||
import BuyPanel from './_page/buy-panel'
|
||||
|
||||
type Props = {
|
||||
searchParams?: { [key: string]: string | string[] | undefined }
|
||||
}
|
||||
|
||||
const Page = ({ searchParams }: Props ) => {
|
||||
|
||||
return (<>
|
||||
<Header siteDef={siteDef}/>
|
||||
<Main>
|
||||
<Tabs defaultValue='buy' className='w-full sm:w-[500px]'>
|
||||
<TabsList className='grid w-full grid-cols-2'>
|
||||
<TabsTrigger value='buy'>Buy</TabsTrigger>
|
||||
<TabsTrigger value='add'>Add</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value='buy'>
|
||||
<BuyPanel />
|
||||
</TabsContent>
|
||||
<TabsContent value='add'>
|
||||
<AddPanel clx=''/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</Main>
|
||||
</>)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -1,19 +0,0 @@
|
||||
import Link from "next/link"
|
||||
|
||||
export default function NotFoundComponent() {
|
||||
return (
|
||||
<div className="flex min-h-[100dvh] flex-col items-center justify-center bg-black text-white font-inter">
|
||||
<div className="mx-auto max-w-md text-center">
|
||||
<h1 className="text-[96px] font-bold">404</h1>
|
||||
<p className="mt-4 text-2xl">Oops, page not found</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="mt-6 inline-flex items-center rounded-md bg-white px-4 py-2 text-sm font-medium text-black shadow-sm transition-colors hover:bg-gray focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2"
|
||||
prefetch={false}
|
||||
>
|
||||
Go to Homepage
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user