'use client' import { CategoryIcon } from '@/app/groups/[groupId]/expenses/category-icon' import { ReceiptExtractedInfo, extractExpenseInformationFromImage, } from '@/app/groups/[groupId]/expenses/create-from-receipt-button-actions' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { Drawer, DrawerContent, DrawerDescription, DrawerHeader, DrawerTitle, DrawerTrigger, } from '@/components/ui/drawer' import { ToastAction } from '@/components/ui/toast' import { useToast } from '@/components/ui/use-toast' import { useMediaQuery } from '@/lib/hooks' import { formatCurrency, formatDate, formatFileSize } from '@/lib/utils' import { trpc } from '@/trpc/client' import { ChevronRight, FileQuestion, Loader2, Receipt } from 'lucide-react' import { useLocale, useTranslations } from 'next-intl' import { getImageData, usePresignedUpload } from 'next-s3-upload' import Image from 'next/image' import { useRouter } from 'next/navigation' import { PropsWithChildren, ReactNode, useState } from 'react' import { useCurrentGroup } from '../current-group-context' const MAX_FILE_SIZE = 5 * 1024 ** 2 export function CreateFromReceiptButton() { const t = useTranslations('CreateFromReceipt') const isDesktop = useMediaQuery('(min-width: 640px)') const DialogOrDrawer = isDesktop ? CreateFromReceiptDialog : CreateFromReceiptDrawer return ( } title={ <> {t('Dialog.title')} Beta } description={<>{t('Dialog.description')}} > ) } function ReceiptDialogContent() { const { group } = useCurrentGroup() const { data: categoriesData } = trpc.categories.list.useQuery() const categories = categoriesData?.categories const locale = useLocale() const t = useTranslations('CreateFromReceipt') const [pending, setPending] = useState(false) const { uploadToS3, FileInput, openFileDialog } = usePresignedUpload() const { toast } = useToast() const router = useRouter() const [receiptInfo, setReceiptInfo] = useState< | null | (ReceiptExtractedInfo & { url: string; width?: number; height?: number }) >(null) const handleFileChange = async (file: File) => { if (file.size > MAX_FILE_SIZE) { toast({ title: t('TooBigToast.title'), description: t('TooBigToast.description', { maxSize: formatFileSize(MAX_FILE_SIZE, locale), size: formatFileSize(file.size, locale), }), variant: 'destructive', }) return } const upload = async () => { try { setPending(true) console.log('Uploading image…') let { url } = await uploadToS3(file) console.log('Extracting information from receipt…') const { amount, categoryId, date, title } = await extractExpenseInformationFromImage(url) const { width, height } = await getImageData(file) setReceiptInfo({ amount, categoryId, date, title, url, width, height }) } catch (err) { console.error(err) toast({ title: t('ErrorToast.title'), description: t('ErrorToast.description'), variant: 'destructive', action: ( upload()} > {t('ErrorToast.retry')} ), }) } finally { setPending(false) } } upload() } const receiptInfoCategory = (receiptInfo?.categoryId && categories?.find((c) => String(c.id) === receiptInfo.categoryId)) || null return (

{t('Dialog.body')}

{t('Dialog.titleLabel')}
{receiptInfo ? receiptInfo.title ?? : '…'}
{t('Dialog.categoryLabel')}
{receiptInfo ? ( receiptInfoCategory ? (
{receiptInfoCategory.grouping} {receiptInfoCategory.name}
) : ( ) ) : ( '' )}
{t('Dialog.amountLabel')}
{receiptInfo && group ? ( receiptInfo.amount ? ( <> {formatCurrency( group.currency, receiptInfo.amount, locale, true, )} ) : ( ) ) : ( '…' )}
{t('Dialog.dateLabel')}
{receiptInfo ? ( receiptInfo.date ? ( formatDate( new Date(`${receiptInfo?.date}T12:00:00.000Z`), locale, { dateStyle: 'medium' }, ) ) : ( ) ) : ( '…' )}

{t('Dialog.editNext')}

) } function Unknown() { const t = useTranslations('CreateFromReceipt') return (
{t('unknown')}
) } function CreateFromReceiptDialog({ trigger, title, description, children, }: PropsWithChildren<{ trigger: ReactNode title: ReactNode description: ReactNode }>) { return ( {trigger} {title} {description} {children} ) } function CreateFromReceiptDrawer({ trigger, title, description, children, }: PropsWithChildren<{ trigger: ReactNode title: ReactNode description: ReactNode }>) { return ( {trigger} {title} {description}
{children}
) }