diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b71db99..15d201d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "bridzik-frontend", "version": "0.1.0", "dependencies": { + "bysquare": "^4.0.0", "qrcode.react": "^4.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -3152,6 +3153,22 @@ "dev": true, "license": "MIT" }, + "node_modules/bysquare": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/bysquare/-/bysquare-4.0.0.tgz", + "integrity": "sha512-V1gGJFn39TY9XnvQDbzMTgGau3wCatsH5+XNSC2wXGgYijIVUD0gkZGalNIub28R+XhM/zUvge+8ccMPJ2+bZw==", + "license": "Apache-2.0", + "dependencies": { + "lzma1": "0.2.0", + "validator": "^13.15.26" + }, + "bin": { + "bysquare": "lib/cli.js" + }, + "funding": { + "url": "https://github.com/sponsors/xseman" + } + }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -5096,6 +5113,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lzma1": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/lzma1/-/lzma1-0.2.0.tgz", + "integrity": "sha512-JkPtHHKANh2je4Gp2kTjtobUXf7+zzfGHYm9PPRnx+Mr4KhAqoyb8dwtCExGaxkJl5upwWStdkIJWpn28XWiHw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/xseman" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -6952,6 +6978,15 @@ "dev": true, "license": "MIT" }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index a9e5c6c..efa06a1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "bysquare": "^4.0.0", "qrcode.react": "^4.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4e42db6..0eab3d0 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,6 +9,7 @@ import Lobby from './pages/Lobby'; import GameTable from './pages/GameTable'; import Auth from './pages/Auth'; import History from './pages/History'; +import Donate from './pages/Donate'; // Admin pulls in recharts — lazy-load it so players never download that chunk. const AdminLayout = lazy(() => import('./pages/admin/AdminLayout')); @@ -107,6 +108,7 @@ function AppInner() { } /> } /> } /> + } /> } /> } /> }> diff --git a/frontend/src/pages/Donate.tsx b/frontend/src/pages/Donate.tsx new file mode 100644 index 0000000..f71e3f7 --- /dev/null +++ b/frontend/src/pages/Donate.tsx @@ -0,0 +1,113 @@ +import { useMemo, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { QRCodeSVG } from 'qrcode.react'; +import { encode, PaymentOptions, CurrencyCode } from 'bysquare/pay'; + +const IBAN = 'SK2583300000002803542822'; +const AMOUNTS = [1, 2, 5]; +const PAYMENT_NOTE = 'Bridzik'; +const BENEFICIARY = 'Liptak Timotej'; + +export default function Donate() { + const navigate = useNavigate(); + const [amount, setAmount] = useState(2); + const [custom, setCustom] = useState(''); + const [copied, setCopied] = useState(false); + + // Vlastna suma ma prednost pred presetom; akceptuje ciarku aj bodku. + const customParsed = parseFloat(custom.replace(',', '.')); + const customValid = custom !== '' && !isNaN(customParsed) && customParsed > 0; + const effectiveAmount = customValid ? Math.round(customParsed * 100) / 100 : amount; + + const qrValue = useMemo( + () => + encode({ + payments: [ + { + type: PaymentOptions.PaymentOrder, + amount: effectiveAmount, + currencyCode: CurrencyCode.EUR, + paymentNote: PAYMENT_NOTE, + beneficiary: { name: BENEFICIARY }, + bankAccounts: [{ iban: IBAN }], + }, + ], + }), + [effectiveAmount], + ); + + const copyIban = async () => { + await navigator.clipboard.writeText(IBAN); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+
+

Podpor Bridžik ☕

+ +
+ +
+

+ Bridžik je zadarmo. Ak sa ti hra páči, dobrovoľný príspevok pomôže s prevádzkou servera a rozvojom nových funkcionalít. +

+ +
+ {AMOUNTS.map((a) => ( + + ))} +
+ +
+ setCustom(e.target.value)} + className="flex-1 bg-transparent outline-none text-gold placeholder-green-dim font-serif font-semibold" + /> + +
+ +
+ +
+

+ Naskenuj QR kód bankovou aplikáciou (PAY by square) +

+ +
+ {IBAN} + +
+
+
+ ); +} diff --git a/frontend/src/pages/GameList.tsx b/frontend/src/pages/GameList.tsx index e599dbb..aa54c0d 100644 --- a/frontend/src/pages/GameList.tsx +++ b/frontend/src/pages/GameList.tsx @@ -38,6 +38,9 @@ export default function GameList() { +