stuff done
This commit is contained in:
114
app/[locale]/(root)/(shop)/cart/cart.tsx
Normal file
114
app/[locale]/(root)/(shop)/cart/cart.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
'use client'
|
||||
|
||||
import {Trash2} from 'lucide-react'
|
||||
import {useTranslations} from 'next-intl'
|
||||
import Image from 'next/image'
|
||||
import {useState} from 'react'
|
||||
|
||||
import CartPostSubmit from '@/app/[locale]/(root)/(shop)/cart/post-submit'
|
||||
import styles from '@/components/pages/cart/cart.module.scss'
|
||||
import CartItems from '@/components/pages/cart/items'
|
||||
import RegisteredOrderForm from '@/components/pages/cart/registered-order-form'
|
||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'
|
||||
import {Link} from '@/i18n/routing'
|
||||
import {dump} from '@/lib/utils'
|
||||
import EmptyCartImage from '@/public/images/empty-cart.svg'
|
||||
import useCartStore from '@/store/cart-store'
|
||||
import {SessionUser} from '@/types/auth'
|
||||
import {Button} from '@/ui/button'
|
||||
|
||||
export default function Cart({user}: {user?: SessionUser | null}) {
|
||||
const t = useTranslations('cart')
|
||||
const [submitResult, setSubmitResult] = useState({})
|
||||
const {cartItems, clearCart} = useCartStore()
|
||||
const totalSum = cartItems.reduce(
|
||||
(total, product) => total + parseFloat(product.price) * product.quantity,
|
||||
0
|
||||
)
|
||||
|
||||
const resultSubmit = (result: any) => {
|
||||
setSubmitResult(result)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-1'>
|
||||
<div className='container'>
|
||||
<section className='bw-cart-wrapper mx-auto my-8 max-w-[640px] text-brand-violet'>
|
||||
{cartItems && cartItems.length > 0 ? (
|
||||
<>
|
||||
<div className='mb-6 flex items-center justify-between border-b border-b-brand-violet pb-2'>
|
||||
<h1 className='text-3xl font-bold'>{t('basket')}</h1>
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
className='rounded-0 h-[3rem] w-[3rem] px-0 text-brand-violet'
|
||||
onClick={() => clearCart()}
|
||||
title={t('clear_cart')}
|
||||
>
|
||||
<Trash2 size={24} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<header className='flex text-xl'>
|
||||
<div className='col'>{t('title')}</div>
|
||||
<div className='flex-none'>{t('quantity')}</div>
|
||||
<div className='col text-right'>{t('amount')}</div>
|
||||
</header>
|
||||
<CartItems cartItems={cartItems} />
|
||||
|
||||
<footer className='my-8 flex py-4 text-xl'>
|
||||
<div className='col'></div>
|
||||
<div className='flex-none'>{t('total')}:</div>
|
||||
<div className='col text-right font-bold'>
|
||||
{totalSum.toFixed(2)} грн
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<section className={styles.bwOrderForm}>
|
||||
<h2 className='pb-9'>Оформлення замовлення</h2>
|
||||
<Tabs defaultValue='registered' className='w-full'>
|
||||
<TabsList className='grid w-full grid-cols-2'>
|
||||
<TabsTrigger value='registered'>
|
||||
Постійний клієнт
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value='quick-order'>
|
||||
Швидке замовлення
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value='registered' className='mt-5'>
|
||||
<RegisteredOrderForm
|
||||
styles={styles.registeredForm}
|
||||
user={user}
|
||||
onSubmitHandler={resultSubmit}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value='quick-order'>quick-order</TabsContent>
|
||||
</Tabs>
|
||||
</section>
|
||||
</>
|
||||
) : Object.keys(submitResult).length === 0 ? (
|
||||
<div className='flex flex-col items-center justify-center gap-y-8'>
|
||||
<Image
|
||||
src={EmptyCartImage}
|
||||
sizes='88vw'
|
||||
alt={t('empty')}
|
||||
unoptimized={true}
|
||||
style={{
|
||||
width: '88%',
|
||||
height: 'auto',
|
||||
margin: '1rem auto'
|
||||
}}
|
||||
/>
|
||||
<Link href={'/catalog'} className='px-6 py-2'>
|
||||
<button className='rounded border border-brand-violet bg-transparent px-4 py-2 font-semibold text-brand-violet hover:border-transparent hover:bg-brand-yellow-300 hover:text-brand-violet-700'>
|
||||
{t('do_purchase')}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<CartPostSubmit result={submitResult} />
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
236
app/[locale]/(root)/(shop)/cart/nova-post.tsx
Normal file
236
app/[locale]/(root)/(shop)/cart/nova-post.tsx
Normal file
@@ -0,0 +1,236 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList
|
||||
} from 'cmdk'
|
||||
import {
|
||||
Check,
|
||||
ChevronsUpDown,
|
||||
MapPinCheck,
|
||||
MapPinPlus,
|
||||
Warehouse
|
||||
} from 'lucide-react'
|
||||
import {useLocale, useTranslations} from 'next-intl'
|
||||
import {useState} from 'react'
|
||||
import {useDebouncedCallback} from 'use-debounce'
|
||||
|
||||
import {
|
||||
type Settlement,
|
||||
type Warehouse as WarehouseType,
|
||||
formatSettlement,
|
||||
getApi
|
||||
} from '@/lib/nova-post-helper'
|
||||
import {cn} from '@/lib/utils'
|
||||
import {dump} from '@/lib/utils'
|
||||
import {Button} from '@/ui/button'
|
||||
import {Command} from '@/ui/command'
|
||||
import {Popover, PopoverContent, PopoverTrigger} from '@/ui/popover'
|
||||
|
||||
const url = '/api/nova-post'
|
||||
|
||||
export default function NovaPost({onSelectHandler}: {onSelectHandler: any}) {
|
||||
const t = useTranslations('cart.post')
|
||||
const [citiesOpen, setCitiesOpen] = useState(false)
|
||||
const [warehousesOpen, setWarehousesOpen] = useState(false)
|
||||
const [citiesValue, setCitiesValue] = useState('')
|
||||
const [cityRef, setCityRef] = useState('')
|
||||
//const [warehouseRef, setWarehouseRef] = useState('')
|
||||
const [warehousesValue, setWarehousesValue] = useState('')
|
||||
const [cities, setCities] = useState([])
|
||||
const [warehouses, setWarehouses] = useState([])
|
||||
const locale = useLocale()
|
||||
|
||||
const handleCitySearch = useDebouncedCallback(
|
||||
async (e: string): Promise<void> => {
|
||||
if (e.length < 3) {
|
||||
setCities([])
|
||||
return
|
||||
}
|
||||
|
||||
const response = await getApi(url + `?scope=cities&q=` + encodeURI(e))
|
||||
|
||||
if (response.ok) {
|
||||
let json = await response.json()
|
||||
setCities(json)
|
||||
} else {
|
||||
setCities([])
|
||||
}
|
||||
},
|
||||
1000
|
||||
)
|
||||
|
||||
const handleWarehouseSearch = async (e: string): Promise<void> => {
|
||||
const response = await getApi(`${url}?scope=warehouses&q=${e}`)
|
||||
|
||||
if (response.ok) {
|
||||
let json = await response.json()
|
||||
setWarehouses(json)
|
||||
} else {
|
||||
setWarehouses([])
|
||||
}
|
||||
}
|
||||
|
||||
const cityDescription = (citiesValue: string): string => {
|
||||
const city: Settlement | undefined = cities.find(
|
||||
(city: Settlement) => city.Description === citiesValue
|
||||
)
|
||||
|
||||
if (!city) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return formatSettlement(city, locale)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='py-2'>
|
||||
<div>
|
||||
<Popover open={citiesOpen} onOpenChange={setCitiesOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
role='combobox'
|
||||
/*aria-expanded={open}*/
|
||||
className='w-full justify-between border border-brand-violet'
|
||||
>
|
||||
<span className='inline-flex items-center gap-x-3'>
|
||||
{citiesValue ? (
|
||||
<>
|
||||
<MapPinCheck />
|
||||
{cityDescription(citiesValue)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<MapPinPlus />
|
||||
{t('findSettlement')}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<ChevronsUpDown className='opacity-50' />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-[640px] p-2'>
|
||||
<Command>
|
||||
<CommandInput
|
||||
className='border border-brand-violet p-2'
|
||||
placeholder={t('startSearchSettlement')}
|
||||
onValueChange={(e: string) => handleCitySearch(e)}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty className='my-1'>{t('notFount')}</CommandEmpty>
|
||||
<CommandGroup className='max-h-[320px] w-full overflow-y-auto'>
|
||||
{cities.map((city: Settlement) => (
|
||||
<CommandItem
|
||||
className='my-2 flex'
|
||||
key={city?.Ref}
|
||||
value={city?.Description}
|
||||
onSelect={(currentValue: string) => {
|
||||
setCitiesValue(
|
||||
currentValue === citiesValue ? '' : currentValue
|
||||
)
|
||||
setCityRef(
|
||||
currentValue === citiesValue ? '' : city?.Ref
|
||||
)
|
||||
|
||||
handleWarehouseSearch(
|
||||
currentValue === citiesValue ? '' : city?.Ref
|
||||
).then(console.log)
|
||||
|
||||
setCitiesOpen(false)
|
||||
}}
|
||||
>
|
||||
{formatSettlement(city, locale)}
|
||||
<Check
|
||||
className={cn(
|
||||
'ml-auto',
|
||||
citiesValue === city?.Description
|
||||
? 'opacity-100'
|
||||
: 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
{cityRef !== '' && (
|
||||
<div className='pt-3'>
|
||||
<Popover open={warehousesOpen} onOpenChange={setWarehousesOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant='outline'
|
||||
role='combobox'
|
||||
/*aria-expanded={open}*/
|
||||
className='w-full justify-between'
|
||||
>
|
||||
<span className='inline-flex items-center gap-x-3'>
|
||||
<Warehouse />
|
||||
{warehousesValue ? warehousesValue : t('selectWarehouse')}
|
||||
</span>
|
||||
<ChevronsUpDown className='opacity-50' />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className='w-[640px] p-2'>
|
||||
<Command>
|
||||
<CommandInput
|
||||
className='p-2'
|
||||
placeholder={t('startSearchWarehouse')}
|
||||
/*onValueChange={(e: string) => handleCitySearch(e)}*/
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandEmpty className='my-1'>{t('notFount')}</CommandEmpty>
|
||||
<CommandGroup className='max-h-[320px] w-full overflow-y-auto'>
|
||||
{warehouses.map((warehouse: WarehouseType) => (
|
||||
<CommandItem
|
||||
className='my-2 flex'
|
||||
key={warehouse.Ref}
|
||||
value={warehouse.Description}
|
||||
onSelect={(currentValue: string) => {
|
||||
setWarehousesValue(
|
||||
currentValue === warehousesValue ? '' : currentValue
|
||||
)
|
||||
/*setWarehouseRef(
|
||||
currentValue === warehousesValue
|
||||
? ''
|
||||
: warehouse.Ref
|
||||
)*/
|
||||
onSelectHandler(
|
||||
currentValue === warehousesValue
|
||||
? {}
|
||||
: {
|
||||
Ref: warehouse.Ref,
|
||||
Description: warehouse.Description,
|
||||
DescriptionRu: warehouse.DescriptionRu
|
||||
}
|
||||
)
|
||||
setWarehousesOpen(false)
|
||||
}}
|
||||
>
|
||||
{warehouse.Description}
|
||||
<Check
|
||||
className={cn(
|
||||
'ml-auto',
|
||||
warehousesValue === warehouse.Description
|
||||
? 'opacity-100'
|
||||
: 'opacity-0'
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,46 +1,18 @@
|
||||
'use client'
|
||||
import Cart from '@/app/[locale]/(root)/(shop)/cart/cart'
|
||||
import {auth} from '@/auth'
|
||||
import {SessionUser} from '@/types/auth'
|
||||
|
||||
import {useTranslations} from 'next-intl'
|
||||
export default async function Page() {
|
||||
const session = await auth()
|
||||
if (!session) {
|
||||
return <Cart />
|
||||
}
|
||||
|
||||
import CartItems from '@/components/pages/cart/items'
|
||||
import useCartStore from '@/store/cart-store'
|
||||
const {user} = session
|
||||
|
||||
export default function Cart() {
|
||||
const t = useTranslations('cart')
|
||||
const {cartItems} = useCartStore()
|
||||
const totalSum = cartItems.reduce(
|
||||
(total, product) => total + parseFloat(product.price) * product.quantity,
|
||||
0
|
||||
)
|
||||
|
||||
// const subtotal = items.reduce(
|
||||
// (total, item) => total + item.price * item.quantity,
|
||||
// 0
|
||||
// )
|
||||
// const total = subtotal
|
||||
|
||||
return (
|
||||
<div className='mt-1'>
|
||||
<div className='container'>
|
||||
<section className='bw-cart-wrapper mx-auto my-8 max-w-[640px] text-brand-violet'>
|
||||
<h1 className='mb-6 border-b border-b-brand-violet pb-6 text-3xl font-bold'>
|
||||
{t('basket')}
|
||||
</h1>
|
||||
<header className='flex text-xl'>
|
||||
<div className='col'>{t('title')}</div>
|
||||
<div className='flex-none'>{t('quantity')}</div>
|
||||
<div className='col text-right'>{t('amount')}</div>
|
||||
</header>
|
||||
<CartItems />
|
||||
<footer className='my-8 flex border-y border-y-brand-violet py-4 text-xl'>
|
||||
<div className='col'></div>
|
||||
<div className='flex-none'>{t('total')}:</div>
|
||||
<div className='col text-right font-bold'>
|
||||
{totalSum.toFixed(2)} грн
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
return session ? (
|
||||
<Cart user={user as unknown as SessionUser} />
|
||||
) : (
|
||||
<Cart user={null} />
|
||||
)
|
||||
}
|
||||
|
||||
23
app/[locale]/(root)/(shop)/cart/post-submit.tsx
Normal file
23
app/[locale]/(root)/(shop)/cart/post-submit.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
export default function CartPostSubmit({result}: any) {
|
||||
if (result?.success) {
|
||||
return (
|
||||
<div>
|
||||
<h1 className='text-2xl'>
|
||||
Номер Вашого замовлення:{' '}
|
||||
<span className='font-semibold text-brand-violet-950'>
|
||||
{result?.success}
|
||||
</span>{' '}
|
||||
</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className='text-2xl'>
|
||||
Сталася помилка:{' '}
|
||||
<span className='font-semibold text-red-800'>{result?.error}</span>{' '}
|
||||
</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user