diff --git a/frontend/src/components/ReceiptNotification.tsx b/frontend/src/components/ReceiptNotification.tsx new file mode 100644 index 0000000..5a0ebb2 --- /dev/null +++ b/frontend/src/components/ReceiptNotification.tsx @@ -0,0 +1,76 @@ +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import { motion, AnimatePresence } from 'motion/react' +import { QRCodeSVG } from 'qrcode.react' +import { IconCopyFilled, IconCheckFilled } from '@tabler/icons-react' +import { useStore } from '../store' + +export default function ReceiptNotification() { + const { t } = useTranslation() + const pending = useStore(s => s.pendingReceipt) + const dismiss = useStore(s => s.dismissReceipt) + const claim = useStore(s => s.claimPendingReceipt) + const [claiming, setClaiming] = useState(false) + const [copied, setCopied] = useState(false) + + const handleClaim = async () => { + if (!pending) return + setClaiming(true) + await claim(pending.receipt.receipt_id) + setClaiming(false) + } + + const copyUrl = () => { + if (!pending) return + navigator.clipboard.writeText(pending.receipt_url).then(() => { + setCopied(true) + setTimeout(() => setCopied(false), 1500) + }) + } + + return ( + + {pending && ( + +

{t('receipt.claimTitle')}

+
+

{t('receipt.solvedOn', { instance: pending.receipt.instance_url })}

+

{t('receipt.time', { time: pending.receipt.solve_time_secs })} - {t('receipt.hints', { hints: pending.receipt.hints_used })}

+

{t('receipt.date', { date: pending.receipt.solve_date })}

+ {pending.receipt.room_code && ( +

{t('receipt.room', { code: pending.receipt.room_code, mode: pending.receipt.mode })}

+ )} + {pending.receipt.players && pending.receipt.players.length > 0 && ( +

{t('receipt.with', { players: pending.receipt.players.join(', ') })}

+ )} +
+ +
+ +
+ +
+ {pending.receipt_url} + +
+ +
+ + +
+
+ )} +
+ ) +}