feat: Integrate Girocode functionality for wire payments in OrdersTab and LoginComponent, enhance user experience with pending payment notifications, and update translations across multiple locales
This commit is contained in:
258
package-lock.json
generated
258
package-lock.json
generated
@@ -22,12 +22,14 @@
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"openai": "^4.0.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.1.0",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-router-dom": "^7.6.2",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"sepa-payment-qr-code": "^2.0.2",
|
||||
"sharp": "^0.34.2",
|
||||
"socket.io-client": "^4.7.5"
|
||||
},
|
||||
@@ -3982,7 +3984,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3992,7 +3993,6 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
@@ -4553,6 +4553,15 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001757",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz",
|
||||
@@ -4690,6 +4699,17 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-deep": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
|
||||
@@ -5252,6 +5272,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||
@@ -5400,6 +5429,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dns-packet": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
|
||||
@@ -5609,6 +5644,12 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
@@ -6933,6 +6974,15 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
@@ -7588,6 +7638,12 @@
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
},
|
||||
"node_modules/iban": {
|
||||
"version": "0.0.14",
|
||||
"resolved": "https://registry.npmjs.org/iban/-/iban-0.0.14.tgz",
|
||||
"integrity": "sha512-+rocNKk+Ga9m8Lr9fTMWd+87JnsBrucm0ZsIx5ROOarZlaDLmd+FKdbtvb0XyoBw9GAFOYG2GuLqoNB16d+p3w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@@ -7982,6 +8038,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-generator-function": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
|
||||
@@ -9420,7 +9485,6 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -9511,7 +9575,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -9667,6 +9730,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pngjs": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/possible-typed-array-names": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||
@@ -9879,6 +9951,23 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
|
||||
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dijkstrajs": "^1.0.1",
|
||||
"pngjs": "^5.0.0",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"qrcode": "bin/qrcode"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
@@ -10338,6 +10427,15 @@
|
||||
"entities": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
@@ -10348,6 +10446,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -10782,6 +10886,18 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/sepa-payment-qr-code": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sepa-payment-qr-code/-/sepa-payment-qr-code-2.0.2.tgz",
|
||||
"integrity": "sha512-TyyONY2Lzo4cjCTgSwAyb2oXYyWXvSaeSY0dIlsiXMpdTEAsR/ONB5jyE7/04+nPBGrLTpKKT3q1dw2LRPcz/g==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"iban": "0.0.14"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
@@ -10894,6 +11010,12 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/set-cookie-parser": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
||||
@@ -11423,6 +11545,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.matchall": {
|
||||
"version": "4.0.12",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
|
||||
@@ -11525,7 +11661,6 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
@@ -12724,6 +12859,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/which-typed-array": {
|
||||
"version": "1.1.19",
|
||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||
@@ -12763,6 +12904,20 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
@@ -12872,6 +13027,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
@@ -12888,6 +13049,93 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"locate-path": "^5.0.0",
|
||||
"path-exists": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-locate": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-try": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs/node_modules/p-locate": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-limit": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
||||
@@ -40,12 +40,14 @@
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"openai": "^4.0.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^19.1.0",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-i18next": "^15.6.0",
|
||||
"react-router-dom": "^7.6.2",
|
||||
"sanitize-html": "^2.17.0",
|
||||
"sepa-payment-qr-code": "^2.0.2",
|
||||
"sharp": "^0.34.2",
|
||||
"socket.io-client": "^4.7.5"
|
||||
},
|
||||
|
||||
@@ -23,6 +23,10 @@ import CartSyncDialog from './CartSyncDialog.js';
|
||||
import { localAndArchiveServer, mergeCarts } from '../utils/cartUtils.js';
|
||||
import config from '../config.js';
|
||||
import { withI18n } from '../i18n/withTranslation.js';
|
||||
import {
|
||||
hasPendingWirePaymentOrder,
|
||||
WIRE_PAYMENT_PENDING_EVENT,
|
||||
} from '../utils/wireGirocodeEligibility.js';
|
||||
import GoogleIcon from '@mui/icons-material/Google';
|
||||
|
||||
// Lazy load GoogleAuthProvider
|
||||
@@ -117,10 +121,28 @@ export class LoginComponent extends Component {
|
||||
localCartSync: [],
|
||||
serverCartSync: [],
|
||||
pendingNavigate: null,
|
||||
privacyConfirmed: sessionStorage.getItem('privacyConfirmed') === 'true'
|
||||
privacyConfirmed: sessionStorage.getItem('privacyConfirmed') === 'true',
|
||||
pendingWirePaymentOrders: false
|
||||
};
|
||||
}
|
||||
|
||||
refreshPendingWireOrders = () => {
|
||||
if (typeof window === 'undefined' || !window.socketManager) return;
|
||||
window.socketManager.emit('getOrders', (response) => {
|
||||
if (response.success && Array.isArray(response.orders)) {
|
||||
this.setState({
|
||||
pendingWirePaymentOrders: hasPendingWirePaymentOrder(response.orders),
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleWirePaymentPendingEvent = (e) => {
|
||||
if (e.detail && typeof e.detail.pending === 'boolean') {
|
||||
this.setState({ pendingWirePaymentOrders: e.detail.pending });
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// Make the open function available globally
|
||||
window.openLoginDrawer = this.handleOpen;
|
||||
@@ -128,17 +150,26 @@ export class LoginComponent extends Component {
|
||||
if (this.props.open) {
|
||||
this.setState({ open: true });
|
||||
}
|
||||
|
||||
if (this.state.isLoggedIn) {
|
||||
this.refreshPendingWireOrders();
|
||||
}
|
||||
window.addEventListener(WIRE_PAYMENT_PENDING_EVENT, this.handleWirePaymentPendingEvent);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.props.open !== prevProps.open) {
|
||||
this.setState({ open: this.props.open });
|
||||
}
|
||||
if (this.state.isLoggedIn && !prevState.isLoggedIn) {
|
||||
this.refreshPendingWireOrders();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Cleanup function to remove global reference when component unmounts
|
||||
window.openLoginDrawer = undefined;
|
||||
window.removeEventListener(WIRE_PAYMENT_PENDING_EVENT, this.handleWirePaymentPendingEvent);
|
||||
}
|
||||
|
||||
resetForm = () => {
|
||||
@@ -308,6 +339,7 @@ export class LoginComponent extends Component {
|
||||
|
||||
handleUserMenuClick = (event) => {
|
||||
this.setState({ anchorEl: event.currentTarget });
|
||||
this.refreshPendingWireOrders();
|
||||
};
|
||||
|
||||
handleUserMenuClose = () => {
|
||||
@@ -326,6 +358,7 @@ export class LoginComponent extends Component {
|
||||
isLoggedIn: false,
|
||||
isAdmin: false,
|
||||
anchorEl: null,
|
||||
pendingWirePaymentOrders: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -480,7 +513,8 @@ export class LoginComponent extends Component {
|
||||
cartSyncOpen,
|
||||
localCartSync,
|
||||
serverCartSync,
|
||||
privacyConfirmed
|
||||
privacyConfirmed,
|
||||
pendingWirePaymentOrders
|
||||
} = this.state;
|
||||
|
||||
const { open: openProp, handleClose: handleCloseProp } = this.props;
|
||||
@@ -520,8 +554,13 @@ export class LoginComponent extends Component {
|
||||
<MenuItem component={Link} to="/profile#cart" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>
|
||||
{this.props.t ? this.props.t('auth.menu.checkout') : 'Bestellabschluss'}
|
||||
</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#orders" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>
|
||||
{this.props.t ? this.props.t('auth.menu.orders') : 'Bestellungen'}
|
||||
<MenuItem component={Link} to="/profile#orders" onClick={this.handleUserMenuClose} sx={{ pl: 4, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
||||
<span>{this.props.t ? this.props.t('auth.menu.orders') : 'Bestellungen'}</span>
|
||||
{pendingWirePaymentOrders ? (
|
||||
<Typography component="span" sx={{ color: 'error.main', fontWeight: 700, fontSize: '0.875rem', flexShrink: 0 }} aria-label="!">
|
||||
[!]
|
||||
</Typography>
|
||||
) : null}
|
||||
</MenuItem>
|
||||
<MenuItem component={Link} to="/profile#settings" onClick={this.handleUserMenuClose} sx={{ pl: 4 }}>
|
||||
{this.props.t ? this.props.t('auth.menu.settings') : 'Einstellungen'}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React, { useState, useEffect, useCallback, Fragment } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { withI18n } from "../../i18n/withTranslation.js";
|
||||
import {
|
||||
@@ -24,6 +24,12 @@ import {
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import CancelIcon from "@mui/icons-material/Cancel";
|
||||
import OrderDetailsDialog from "./OrderDetailsDialog.js";
|
||||
import WireOrderGirocode from "./WireOrderGirocode.js";
|
||||
import {
|
||||
isWireGirocodeEligible,
|
||||
hasPendingWirePaymentOrder,
|
||||
WIRE_PAYMENT_PENDING_EVENT,
|
||||
} from "../../utils/wireGirocodeEligibility.js";
|
||||
|
||||
// Constants
|
||||
const getStatusTranslation = (status, t) => {
|
||||
@@ -100,8 +106,20 @@ const OrdersTab = ({ orderIdFromHash, t }) => {
|
||||
window.socketManager.emit("getOrders", (response) => {
|
||||
if (response.success) {
|
||||
setOrders(response.orders);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(WIRE_PAYMENT_PENDING_EVENT, {
|
||||
detail: {
|
||||
pending: hasPendingWirePaymentOrder(response.orders),
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
setError(response.error || "Failed to fetch orders.");
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(WIRE_PAYMENT_PENDING_EVENT, {
|
||||
detail: { pending: false },
|
||||
})
|
||||
);
|
||||
}
|
||||
setLoading(false);
|
||||
});
|
||||
@@ -216,89 +234,107 @@ const OrdersTab = ({ orderIdFromHash, t }) => {
|
||||
0
|
||||
);
|
||||
return (
|
||||
<TableRow key={order.orderId} hover>
|
||||
<TableCell>{order.orderId}</TableCell>
|
||||
<TableCell>
|
||||
{new Date(order.created_at).toLocaleDateString()}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
color: getStatusColor(order.status),
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: "1.2rem" }}>
|
||||
{getStatusEmoji(order.status)}
|
||||
</span>
|
||||
<Typography
|
||||
variant="body2"
|
||||
component="span"
|
||||
sx={{ fontWeight: "medium" }}
|
||||
<Fragment key={order.orderId}>
|
||||
<TableRow hover>
|
||||
<TableCell>{order.orderId}</TableCell>
|
||||
<TableCell>
|
||||
{new Date(order.created_at).toLocaleDateString()}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
color: getStatusColor(order.status),
|
||||
}}
|
||||
>
|
||||
{displayStatus}
|
||||
</Typography>
|
||||
</Box>
|
||||
{order.delivery_method === 'DHL' && order.trackingCode && (
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
<a
|
||||
href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${order.trackingCode}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ fontSize: '0.85rem', color: '#d40511' }}
|
||||
<span style={{ fontSize: "1.2rem" }}>
|
||||
{getStatusEmoji(order.status)}
|
||||
</span>
|
||||
<Typography
|
||||
variant="body2"
|
||||
component="span"
|
||||
sx={{ fontWeight: "medium" }}
|
||||
>
|
||||
📦 {t ? t('orders.trackShipment') : 'Sendung verfolgen'}
|
||||
</a>
|
||||
{displayStatus}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{order.items
|
||||
.filter(item => {
|
||||
// Exclude delivery items - backend uses deliveryMethod ID as item name
|
||||
const itemName = item.name || '';
|
||||
return itemName !== 'DHL' &&
|
||||
itemName !== 'DPD' &&
|
||||
itemName !== 'Sperrgut' &&
|
||||
itemName !== 'Abholung';
|
||||
})
|
||||
.reduce(
|
||||
(acc, item) => acc + item.quantity_ordered,
|
||||
0
|
||||
{order.delivery_method === 'DHL' && order.trackingCode && (
|
||||
<Box sx={{ mt: 0.5 }}>
|
||||
<a
|
||||
href={`https://www.dhl.de/de/privatkunden/dhl-sendungsverfolgung.html?piececode=${order.trackingCode}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ fontSize: '0.85rem', color: '#d40511' }}
|
||||
>
|
||||
📦 {t ? t('orders.trackShipment') : 'Sendung verfolgen'}
|
||||
</a>
|
||||
</Box>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{currencyFormatter.format(total)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center' }}>
|
||||
<Tooltip title={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="primary"
|
||||
onClick={() => handleViewDetails(order.orderId)}
|
||||
aria-label={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}
|
||||
>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{isOrderCancelable(order) && (
|
||||
<Tooltip title={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{order.items
|
||||
.filter(item => {
|
||||
// Exclude delivery items - backend uses deliveryMethod ID as item name
|
||||
const itemName = item.name || '';
|
||||
return itemName !== 'DHL' &&
|
||||
itemName !== 'DPD' &&
|
||||
itemName !== 'Sperrgut' &&
|
||||
itemName !== 'Abholung';
|
||||
})
|
||||
.reduce(
|
||||
(acc, item) => acc + item.quantity_ordered,
|
||||
0
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{currencyFormatter.format(total)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<Box sx={{ display: 'flex', gap: 1, justifyContent: 'center' }}>
|
||||
<Tooltip title={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => handleCancelClick(order)}
|
||||
aria-label={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}
|
||||
color="primary"
|
||||
onClick={() => handleViewDetails(order.orderId)}
|
||||
aria-label={t ? t('orders.tooltips.viewDetails') : 'Details anzeigen'}
|
||||
>
|
||||
<CancelIcon />
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{isOrderCancelable(order) && (
|
||||
<Tooltip title={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}>
|
||||
<IconButton
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => handleCancelClick(order)}
|
||||
aria-label={t ? t('orders.tooltips.cancelOrder') : 'Bestellung stornieren'}
|
||||
>
|
||||
<CancelIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{isWireGirocodeEligible(order) && (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={6}
|
||||
sx={{
|
||||
py: 2,
|
||||
px: { xs: 1, sm: 2 },
|
||||
verticalAlign: "top",
|
||||
bgcolor: "action.hover",
|
||||
borderTop: (theme) => `1px solid ${theme.palette.divider}`,
|
||||
}}
|
||||
>
|
||||
<WireOrderGirocode order={order} t={t} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
|
||||
177
src/components/profile/WireOrderGirocode.js
Normal file
177
src/components/profile/WireOrderGirocode.js
Normal file
@@ -0,0 +1,177 @@
|
||||
import React, { useEffect, useState, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Box, Typography, CircularProgress } from "@mui/material";
|
||||
import { buildWireGirocodeDataUrl } from "../../utils/wireGirocode.js";
|
||||
import {
|
||||
orderGrossTotal,
|
||||
isWireGirocodeEligible,
|
||||
} from "../../utils/wireGirocodeEligibility.js";
|
||||
import { WIRE_GIROCODE_RECIPIENT } from "../../config/wireGirocodeRecipient.js";
|
||||
|
||||
/**
|
||||
* Full-width row content: Girocode QR + bank details (pending + wire only).
|
||||
*/
|
||||
const WireOrderGirocode = ({ order, t }) => {
|
||||
const { i18n } = useTranslation();
|
||||
const [dataUrl, setDataUrl] = useState(null);
|
||||
const [genError, setGenError] = useState(false);
|
||||
|
||||
const eligible = isWireGirocodeEligible(order);
|
||||
|
||||
const amount = useMemo(() => {
|
||||
const raw = orderGrossTotal(order);
|
||||
return Math.round(raw * 100) / 100;
|
||||
}, [order]);
|
||||
|
||||
const amountFormatted = useMemo(() => {
|
||||
const locale = (i18n.language || "de").replace("_", "-");
|
||||
return new Intl.NumberFormat(locale, {
|
||||
style: "currency",
|
||||
currency: "EUR",
|
||||
}).format(amount);
|
||||
}, [amount, i18n.language]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!eligible || amount < 0.01) {
|
||||
setDataUrl(null);
|
||||
setGenError(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
setDataUrl(null);
|
||||
setGenError(false);
|
||||
|
||||
buildWireGirocodeDataUrl({
|
||||
amount,
|
||||
reference: order.orderId,
|
||||
})
|
||||
.then((url) => {
|
||||
if (!cancelled) setDataUrl(url);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setGenError(true);
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [eligible, amount, order.orderId]);
|
||||
|
||||
if (!eligible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hint = t
|
||||
? t("orders.girocode.hint")
|
||||
: "Mit Ihrer Banking-App scannen, um zu bezahlen.";
|
||||
const alt = t
|
||||
? t("orders.girocode.alt")
|
||||
: "Girocode für die Überweisung";
|
||||
|
||||
const paymentPending = t
|
||||
? t("orders.girocode.paymentPending")
|
||||
: "Diese Bestellung wartet auf Ihre Überweisung.";
|
||||
const payToAccount = t
|
||||
? t("orders.girocode.payToAccount")
|
||||
: "Bitte überweisen Sie den Betrag auf folgendes Konto:";
|
||||
const holder = t
|
||||
? t("orders.girocode.holder", { name: WIRE_GIROCODE_RECIPIENT.name })
|
||||
: `Kontoinhaber: ${WIRE_GIROCODE_RECIPIENT.name}`;
|
||||
const ibanLine = t
|
||||
? t("orders.girocode.iban", { iban: WIRE_GIROCODE_RECIPIENT.iban })
|
||||
: `IBAN: ${WIRE_GIROCODE_RECIPIENT.iban}`;
|
||||
const bicLine = t
|
||||
? t("orders.girocode.bic", { bic: WIRE_GIROCODE_RECIPIENT.bic })
|
||||
: `BIC: ${WIRE_GIROCODE_RECIPIENT.bic}`;
|
||||
const amountLine = t
|
||||
? t("orders.girocode.amount", { amount: amountFormatted })
|
||||
: `Betrag: ${amountFormatted}`;
|
||||
const purposeLine = t
|
||||
? t("orders.girocode.purpose", { orderId: order.orderId })
|
||||
: `Verwendungszweck: ${order.orderId}`;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: { xs: "column", sm: "row" },
|
||||
gap: { xs: 2, sm: 3 },
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flexShrink: 0, width: { xs: "100%", sm: "auto" } }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
component="div"
|
||||
sx={{ display: "block", mb: 0.5, lineHeight: 1.35, maxWidth: 220 }}
|
||||
>
|
||||
{hint}
|
||||
</Typography>
|
||||
{!dataUrl && !genError && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: 200,
|
||||
height: 200,
|
||||
}}
|
||||
>
|
||||
<CircularProgress size={28} />
|
||||
</Box>
|
||||
)}
|
||||
{genError && (
|
||||
<Typography variant="caption" color="text.secondary" component="div">
|
||||
{t
|
||||
? t("orders.girocode.error")
|
||||
: "QR-Code konnte nicht erzeugt werden."}
|
||||
</Typography>
|
||||
)}
|
||||
{dataUrl && (
|
||||
<img
|
||||
src={dataUrl}
|
||||
width={200}
|
||||
height={200}
|
||||
alt={alt}
|
||||
style={{ display: "block", borderRadius: 4 }}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box sx={{ flex: 1, minWidth: 0 }}>
|
||||
<Typography variant="body2" sx={{ mb: 1.5, lineHeight: 1.5 }}>
|
||||
{paymentPending}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ mb: 1, fontWeight: 600 }}>
|
||||
{payToAccount}
|
||||
</Typography>
|
||||
<Typography variant="body2" component="div" sx={{ lineHeight: 1.6 }}>
|
||||
{holder}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
component="div"
|
||||
sx={{
|
||||
lineHeight: 1.6,
|
||||
wordBreak: "break-all",
|
||||
fontFamily: "ui-monospace, monospace",
|
||||
}}
|
||||
>
|
||||
{ibanLine}
|
||||
</Typography>
|
||||
<Typography variant="body2" component="div" sx={{ lineHeight: 1.6 }}>
|
||||
{bicLine}
|
||||
</Typography>
|
||||
<Typography variant="body2" component="div" sx={{ mt: 1.5, lineHeight: 1.6 }}>
|
||||
{amountLine}
|
||||
</Typography>
|
||||
<Typography variant="body2" component="div" sx={{ lineHeight: 1.6 }}>
|
||||
{purposeLine}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default WireOrderGirocode;
|
||||
8
src/config/wireGirocodeRecipient.js
Normal file
8
src/config/wireGirocodeRecipient.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* SEPA Girocode recipient — must match server-side wire-transfer emails / QR attachments.
|
||||
*/
|
||||
export const WIRE_GIROCODE_RECIPIENT = {
|
||||
name: "Max Schön",
|
||||
iban: "DE35850503000221239693",
|
||||
bic: "OSDDDE81XXX",
|
||||
};
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "لم تقم بوضع أي طلبات بعد.",
|
||||
"trackShipment": "تتبع الشحنة",
|
||||
"girocode": {
|
||||
"hint": "امسح الرمز بتطبيق البنك للدفع.",
|
||||
"alt": "رمز Girocode للتحويل البنكي",
|
||||
"error": "تعذر إنشاء رمز الاستجابة السريعة.",
|
||||
"paymentPending": "هذا الطلب في انتظار التحويل البنكي.",
|
||||
"payToAccount": "يرجى تحويل المبلغ إلى الحساب التالي:",
|
||||
"holder": "صاحب الحساب: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "المبلغ: {{amount}}",
|
||||
"purpose": "المرجع: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "تفاصيل الطلب: {{orderId}}",
|
||||
"deliveryAddress": "عنوان التوصيل",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Все още не сте направили поръчки.",
|
||||
"trackShipment": "Проследи пратката",
|
||||
"girocode": {
|
||||
"hint": "Сканирайте с банковото си приложение, за да платите.",
|
||||
"alt": "Girocode за банков превод",
|
||||
"error": "QR кодът не можа да бъде генериран.",
|
||||
"paymentPending": "Тази поръчка очаква вашия банков превод.",
|
||||
"payToAccount": "Моля, преведете сумата по следната сметка:",
|
||||
"holder": "Титуляр: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Сума: {{amount}}",
|
||||
"purpose": "Основание: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Подробности за поръчка: {{orderId}}",
|
||||
"deliveryAddress": "Адрес за доставка",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Ještě jste neprovedli žádné objednávky.",
|
||||
"trackShipment": "Sledovat zásilku",
|
||||
"girocode": {
|
||||
"hint": "Naskenujte bankovní aplikací a zaplaťte.",
|
||||
"alt": "Girocode pro bankovní převod",
|
||||
"error": "QR kód se nepodařilo vygenerovat.",
|
||||
"paymentPending": "Tato objednávka čeká na váš bankovní převod.",
|
||||
"payToAccount": "Prosím převeďte částku na následující účet:",
|
||||
"holder": "Majitel účtu: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Částka: {{amount}}",
|
||||
"purpose": "Zpráva pro příjemce: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detaily objednávky: {{orderId}}",
|
||||
"deliveryAddress": "Dodací adresa",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Sie haben noch keine Bestellungen aufgegeben.",
|
||||
"trackShipment": "Sendung verfolgen",
|
||||
"girocode": {
|
||||
"hint": "Mit Ihrer Banking-App scannen, um zu bezahlen.",
|
||||
"alt": "Girocode für die Überweisung",
|
||||
"error": "QR-Code konnte nicht erzeugt werden.",
|
||||
"paymentPending": "Diese Bestellung wartet auf Ihre Überweisung.",
|
||||
"payToAccount": "Bitte überweisen Sie den Betrag auf folgendes Konto:",
|
||||
"holder": "Kontoinhaber: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Betrag: {{amount}}",
|
||||
"purpose": "Verwendungszweck: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Bestelldetails: {{orderId}}",
|
||||
"deliveryAddress": "Lieferadresse",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Δεν έχετε κάνει ακόμα καμία παραγγελία.",
|
||||
"trackShipment": "Παρακολούθηση αποστολής",
|
||||
"girocode": {
|
||||
"hint": "Σαρώστε με την εφαρμογή της τράπεζάς σας για πληρωμή.",
|
||||
"alt": "Girocode για τραπεζική μεταφορά",
|
||||
"error": "Δεν ήταν δυνατή η δημιουργία QR.",
|
||||
"paymentPending": "Η παραγγελία περιμένει την τραπεζική σας μεταφορά.",
|
||||
"payToAccount": "Μεταφέρετε το ποσό στον ακόλουθο λογαριασμό:",
|
||||
"holder": "Δικαιούχος: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Ποσό: {{amount}}",
|
||||
"purpose": "Αιτιολογία: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Λεπτομέρειες παραγγελίας: {{orderId}}",
|
||||
"deliveryAddress": "Διεύθυνση παράδοσης",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "You have not placed any orders yet.", // Sie haben noch keine Bestellungen aufgegeben.
|
||||
"trackShipment": "Track shipment", // Sendung verfolgen
|
||||
"girocode": {
|
||||
"hint": "Scan with your banking app to pay.", // Mit Ihrer Banking-App scannen...
|
||||
"alt": "Girocode for bank transfer", // Girocode für die Überweisung
|
||||
"error": "Could not generate QR code.", // QR-Code konnte nicht erzeugt werden.
|
||||
"paymentPending": "This order is awaiting your bank transfer.", // Diese Bestellung wartet...
|
||||
"payToAccount": "Please transfer the amount to the following account:", // Bitte überweisen...
|
||||
"holder": "Account holder: {{name}}", // Kontoinhaber
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Amount: {{amount}}", // Betrag
|
||||
"purpose": "Payment reference: {{orderId}}" // Verwendungszweck
|
||||
},
|
||||
"details": {
|
||||
"title": "Order details: {{orderId}}", // Bestelldetails: {{orderId}}
|
||||
"deliveryAddress": "Delivery address", // Lieferadresse
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Aún no has realizado ningún pedido.",
|
||||
"trackShipment": "Rastrear envío",
|
||||
"girocode": {
|
||||
"hint": "Escanee con la app de su banco para pagar.",
|
||||
"alt": "Girocode para transferencia bancaria",
|
||||
"error": "No se pudo generar el código QR.",
|
||||
"paymentPending": "Este pedido está pendiente de su transferencia bancaria.",
|
||||
"payToAccount": "Transfiera el importe a la siguiente cuenta:",
|
||||
"holder": "Titular de la cuenta: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Importe: {{amount}}",
|
||||
"purpose": "Concepto: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detalles del pedido: {{orderId}}",
|
||||
"deliveryAddress": "Dirección de entrega",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Vous n'avez pas encore passé de commandes.",
|
||||
"trackShipment": "Suivre l'envoi",
|
||||
"girocode": {
|
||||
"hint": "Scannez avec l'application de votre banque pour payer.",
|
||||
"alt": "Girocode pour virement bancaire",
|
||||
"error": "Impossible de générer le code QR.",
|
||||
"paymentPending": "Cette commande attend votre virement bancaire.",
|
||||
"payToAccount": "Veuillez virer le montant sur le compte suivant :",
|
||||
"holder": "Titulaire du compte : {{name}}",
|
||||
"iban": "IBAN : {{iban}}",
|
||||
"bic": "BIC : {{bic}}",
|
||||
"amount": "Montant : {{amount}}",
|
||||
"purpose": "Libellé : {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Détails de la commande : {{orderId}}",
|
||||
"deliveryAddress": "Adresse de livraison",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Još niste izvršili nijednu narudžbu.",
|
||||
"trackShipment": "Prati pošiljku",
|
||||
"girocode": {
|
||||
"hint": "Skenirajte bankovnom aplikacijom za plaćanje.",
|
||||
"alt": "Girocode za bankovni prijenos",
|
||||
"error": "QR kod nije moguće generirati.",
|
||||
"paymentPending": "Ova narudžba čeka vaš bankovni prijenos.",
|
||||
"payToAccount": "Molimo uplatite iznos na sljedeći račun:",
|
||||
"holder": "Vlasnik računa: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Iznos: {{amount}}",
|
||||
"purpose": "Svrha plaćanja: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detalji narudžbe: {{orderId}}",
|
||||
"deliveryAddress": "Adresa dostave",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Még nem adott le rendelést.",
|
||||
"trackShipment": "Szállítmány követése",
|
||||
"girocode": {
|
||||
"hint": "Olvasd be a banki alkalmazásoddal a fizetéshez.",
|
||||
"alt": "Girocode átutaláshoz",
|
||||
"error": "A QR-kód nem hozható létre.",
|
||||
"paymentPending": "A rendelés a banki átutalásodra vár.",
|
||||
"payToAccount": "Kérjük, utald át az összeget a következő számlára:",
|
||||
"holder": "Számlatulajdonos: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Összeg: {{amount}}",
|
||||
"purpose": "Közlemény: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Rendelés részletei: {{orderId}}",
|
||||
"deliveryAddress": "Szállítási cím",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Non hai ancora effettuato ordini.",
|
||||
"trackShipment": "Traccia spedizione",
|
||||
"girocode": {
|
||||
"hint": "Scansiona con l'app della tua banca per pagare.",
|
||||
"alt": "Girocode per bonifico bancario",
|
||||
"error": "Impossibile generare il codice QR.",
|
||||
"paymentPending": "Questo ordine è in attesa del bonifico bancario.",
|
||||
"payToAccount": "Trasferisci l'importo sul seguente conto:",
|
||||
"holder": "Intestatario: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Importo: {{amount}}",
|
||||
"purpose": "Causale: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Dettagli ordine: {{orderId}}",
|
||||
"deliveryAddress": "Indirizzo di consegna",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Nie złożyłeś jeszcze żadnych zamówień.",
|
||||
"trackShipment": "Śledź przesyłkę",
|
||||
"girocode": {
|
||||
"hint": "Zeskanuj aplikacją bankową, aby zapłacić.",
|
||||
"alt": "Girocode do przelewu",
|
||||
"error": "Nie udało się wygenerować kodu QR.",
|
||||
"paymentPending": "To zamówienie oczekuje na przelew.",
|
||||
"payToAccount": "Przelej kwotę na następujące konto:",
|
||||
"holder": "Właściciel konta: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Kwota: {{amount}}",
|
||||
"purpose": "Tytuł: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Szczegóły zamówienia: {{orderId}}",
|
||||
"deliveryAddress": "Adres dostawy",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Nu ați plasat încă nicio comandă.",
|
||||
"trackShipment": "Urmărește expedierea",
|
||||
"girocode": {
|
||||
"hint": "Scanează cu aplicația băncii pentru a plăti.",
|
||||
"alt": "Girocode pentru transfer bancar",
|
||||
"error": "Codul QR nu a putut fi generat.",
|
||||
"paymentPending": "Comanda așteaptă transferul bancar.",
|
||||
"payToAccount": "Vă rugăm să transferați suma în contul următor:",
|
||||
"holder": "Titular cont: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Sumă: {{amount}}",
|
||||
"purpose": "Detalii plată: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detalii comandă: {{orderId}}",
|
||||
"deliveryAddress": "Adresa de livrare",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Вы еще не сделали ни одного заказа.",
|
||||
"trackShipment": "Отследить отправление",
|
||||
"girocode": {
|
||||
"hint": "Отсканируйте в приложении банка для оплаты.",
|
||||
"alt": "Girocode для банковского перевода",
|
||||
"error": "Не удалось создать QR-код.",
|
||||
"paymentPending": "Заказ ожидает банковского перевода.",
|
||||
"payToAccount": "Переведите сумму на следующий счёт:",
|
||||
"holder": "Получатель: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Сумма: {{amount}}",
|
||||
"purpose": "Назначение платежа: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Детали заказа: {{orderId}}",
|
||||
"deliveryAddress": "Адрес доставки",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Ešte ste neuskutočnili žiadne objednávky.",
|
||||
"trackShipment": "Sledovať zásielku",
|
||||
"girocode": {
|
||||
"hint": "Naskenujte bankovou aplikáciou a zaplaťte.",
|
||||
"alt": "Girocode pre bankový prevod",
|
||||
"error": "QR kód sa nepodarilo vygenerovať.",
|
||||
"paymentPending": "Táto objednávka čaká na váš bankový prevod.",
|
||||
"payToAccount": "Prosím preveďte sumu na nasledujúci účet:",
|
||||
"holder": "Majiteľ účtu: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Suma: {{amount}}",
|
||||
"purpose": "Správa pre príjemcu: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detaily objednávky: {{orderId}}",
|
||||
"deliveryAddress": "Dodacia adresa",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Še niste oddali nobenega naročila.",
|
||||
"trackShipment": "Sledi pošiljki",
|
||||
"girocode": {
|
||||
"hint": "Skenirajte z bančno aplikacijo za plačilo.",
|
||||
"alt": "Girocode za bančno nakazilo",
|
||||
"error": "QR kode ni bilo mogoče ustvariti.",
|
||||
"paymentPending": "To naročilo čaka na vaše bančno nakazilo.",
|
||||
"payToAccount": "Prosimo, nakažite znesek na naslednji račun:",
|
||||
"holder": "Imetnik računa: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Znesek: {{amount}}",
|
||||
"purpose": "Namen plačila: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Podrobnosti naročila: {{orderId}}",
|
||||
"deliveryAddress": "Naslov za dostavo",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Nuk keni bërë ende asnjë porosi.",
|
||||
"trackShipment": "Ndjek dërgesën",
|
||||
"girocode": {
|
||||
"hint": "Skanoni me aplikacionin e bankës për të paguar.",
|
||||
"alt": "Girocode për transfertë bankare",
|
||||
"error": "Kodi QR nuk mund të gjenerohej.",
|
||||
"paymentPending": "Ky porosi pret transfertën tuaj bankare.",
|
||||
"payToAccount": "Ju lutemi transferoni shumën në llogarinë e mëposhtme:",
|
||||
"holder": "Titullari i llogarisë: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Shuma: {{amount}}",
|
||||
"purpose": "Qëllimi i pagesës: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detajet e porosisë: {{orderId}}",
|
||||
"deliveryAddress": "Adresa e dorëzimit",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Još niste napravili nijednu porudžbinu.",
|
||||
"trackShipment": "Prati pošiljku",
|
||||
"girocode": {
|
||||
"hint": "Skenirajte bankovnom aplikacijom da platite.",
|
||||
"alt": "Girocode za bankovni transfer",
|
||||
"error": "QR kod nije moguće generisati.",
|
||||
"paymentPending": "Ova porudžbina čeka vaš bankovni transfer.",
|
||||
"payToAccount": "Molimo uplatite iznos na sledeći račun:",
|
||||
"holder": "Vlasnik računa: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Iznos: {{amount}}",
|
||||
"purpose": "Svrha uplate: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Detalji porudžbine: {{orderId}}",
|
||||
"deliveryAddress": "Adresa za isporuku",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Du har inte lagt några beställningar än.",
|
||||
"trackShipment": "Spåra försändelse",
|
||||
"girocode": {
|
||||
"hint": "Skanna med din bankapp för att betala.",
|
||||
"alt": "Girocode för banköverföring",
|
||||
"error": "QR-koden kunde inte genereras.",
|
||||
"paymentPending": "Denna beställning väntar på din banköverföring.",
|
||||
"payToAccount": "Överför beloppet till följande konto:",
|
||||
"holder": "Kontoinnehavare: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Belopp: {{amount}}",
|
||||
"purpose": "Meddelande: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Orderdetaljer: {{orderId}}",
|
||||
"deliveryAddress": "Leveransadress",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Henüz sipariş vermediniz.",
|
||||
"trackShipment": "Gönderiyi takip et",
|
||||
"girocode": {
|
||||
"hint": "Ödemek için banka uygulamanızla taratın.",
|
||||
"alt": "Banka havalesi için Girocode",
|
||||
"error": "QR kodu oluşturulamadı.",
|
||||
"paymentPending": "Bu sipariş banka havalesini bekliyor.",
|
||||
"payToAccount": "Lütfen tutarı aşağıdaki hesaba gönderin:",
|
||||
"holder": "Hesap sahibi: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Tutar: {{amount}}",
|
||||
"purpose": "Açıklama: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Sipariş detayları: {{orderId}}",
|
||||
"deliveryAddress": "Teslimat adresi",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "Ви ще не робили замовлень.",
|
||||
"trackShipment": "Відстежити відправлення",
|
||||
"girocode": {
|
||||
"hint": "Відскануйте в додатку банку для оплати.",
|
||||
"alt": "Girocode для банківського переказу",
|
||||
"error": "Не вдалося згенерувати QR-код.",
|
||||
"paymentPending": "Замовлення очікує на банківський переказ.",
|
||||
"payToAccount": "Перекажіть суму на такий рахунок:",
|
||||
"holder": "Власник рахунку: {{name}}",
|
||||
"iban": "IBAN: {{iban}}",
|
||||
"bic": "BIC: {{bic}}",
|
||||
"amount": "Сума: {{amount}}",
|
||||
"purpose": "Призначення платежу: {{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "Деталі замовлення: {{orderId}}",
|
||||
"deliveryAddress": "Адреса доставки",
|
||||
|
||||
@@ -27,6 +27,18 @@ export default {
|
||||
},
|
||||
"noOrders": "您还没有下过任何订单。",
|
||||
"trackShipment": "跟踪发货",
|
||||
"girocode": {
|
||||
"hint": "使用银行应用扫码付款。",
|
||||
"alt": "银行转账 Girocode",
|
||||
"error": "无法生成二维码。",
|
||||
"paymentPending": "此订单等待您完成银行转账。",
|
||||
"payToAccount": "请将款项汇至以下账户:",
|
||||
"holder": "账户持有人:{{name}}",
|
||||
"iban": "IBAN:{{iban}}",
|
||||
"bic": "BIC:{{bic}}",
|
||||
"amount": "金额:{{amount}}",
|
||||
"purpose": "备注/用途:{{orderId}}"
|
||||
},
|
||||
"details": {
|
||||
"title": "订单详情: {{orderId}}",
|
||||
"deliveryAddress": "收货地址",
|
||||
|
||||
@@ -8,6 +8,8 @@ import {
|
||||
Tab,
|
||||
CircularProgress
|
||||
} from '@mui/material';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useLocation, useNavigate, Navigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -16,18 +18,25 @@ import OrdersTab from '../components/profile/OrdersTab.js';
|
||||
import SettingsTab from '../components/profile/SettingsTab.js';
|
||||
import CartTab from '../components/profile/CartTab.js';
|
||||
import LoginComponent from '../components/LoginComponent.js';
|
||||
import {
|
||||
hasPendingWirePaymentOrder,
|
||||
WIRE_PAYMENT_PENDING_EVENT,
|
||||
} from '../utils/wireGirocodeEligibility.js';
|
||||
|
||||
// Functional Profile Page Component
|
||||
const ProfilePage = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const [tabValue, setTabValue] = useState(0);
|
||||
const [user, setUser] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showLogin, setShowLogin] = useState(false);
|
||||
const [orderIdFromHash, setOrderIdFromHash] = useState(null);
|
||||
const [paymentCompletion, setPaymentCompletion] = useState(null);
|
||||
const [wirePaymentPending, setWirePaymentPending] = useState(false);
|
||||
|
||||
// @note Check for payment completion parameters from Stripe and Mollie redirects
|
||||
useEffect(() => {
|
||||
@@ -127,6 +136,31 @@ const ProfilePage = () => {
|
||||
}
|
||||
}, [location.hash, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!user || typeof window === 'undefined' || !window.socketManager) {
|
||||
setWirePaymentPending(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchWirePending = () => {
|
||||
window.socketManager.emit('getOrders', (response) => {
|
||||
if (response.success && Array.isArray(response.orders)) {
|
||||
setWirePaymentPending(hasPendingWirePaymentOrder(response.orders));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
fetchWirePending();
|
||||
|
||||
const onWirePending = (e) => {
|
||||
if (e.detail && typeof e.detail.pending === 'boolean') {
|
||||
setWirePaymentPending(e.detail.pending);
|
||||
}
|
||||
};
|
||||
window.addEventListener(WIRE_PAYMENT_PENDING_EVENT, onWirePending);
|
||||
return () => window.removeEventListener(WIRE_PAYMENT_PENDING_EVENT, onWirePending);
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
const checkUserLoggedIn = () => {
|
||||
const storedUser = sessionStorage.getItem('user');
|
||||
@@ -229,11 +263,16 @@ const ProfilePage = () => {
|
||||
<Container maxWidth="md" sx={{ py: { xs: 0, sm: 4 }, px: { xs: 0, sm: 3 } }}>
|
||||
<Paper elevation={{ xs: 0, sm: 2 }} sx={{ borderRadius: { xs: 0, sm: 2 }, overflow: 'hidden' }}>
|
||||
<Box sx={{ bgcolor: '#2e7d32', p: { xs: 2, sm: 3 }, color: 'white' }}>
|
||||
<Typography variant="h5" fontWeight="bold">
|
||||
{window.innerWidth < 600 ?
|
||||
(tabValue === 0 ? (t ? t('auth.menu.checkout') : 'Bestellabschluss') :
|
||||
tabValue === 1 ? (t ? t('auth.menu.orders') : 'Bestellungen') :
|
||||
tabValue === 2 ? (t ? t('auth.menu.settings') : 'Einstellungen') : (t ? t('auth.profile') : 'Mein Profil'))
|
||||
<Typography variant="h5" fontWeight="bold" component="div" sx={{ display: 'flex', alignItems: 'center', gap: 0.75, flexWrap: 'wrap' }}>
|
||||
{isMobile ?
|
||||
(<>
|
||||
{tabValue === 0 ? (t ? t('auth.menu.checkout') : 'Bestellabschluss') :
|
||||
tabValue === 1 ? (t ? t('auth.menu.orders') : 'Bestellungen') :
|
||||
tabValue === 2 ? (t ? t('auth.menu.settings') : 'Einstellungen') : (t ? t('auth.profile') : 'Mein Profil')}
|
||||
{tabValue === 1 && wirePaymentPending && (
|
||||
<Box component="span" sx={{ color: 'error.main', fontWeight: 700 }} aria-label="Ausstehende Überweisung">[!]</Box>
|
||||
)}
|
||||
</>)
|
||||
: (t ? t('auth.profile') : 'Mein Profil')
|
||||
}
|
||||
</Typography>
|
||||
@@ -266,7 +305,16 @@ const ProfilePage = () => {
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
label={t ? t('auth.menu.orders') : 'Bestellungen'}
|
||||
label={
|
||||
<Box sx={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 0.5 }}>
|
||||
<span>{t ? t('auth.menu.orders') : 'Bestellungen'}</span>
|
||||
{wirePaymentPending && (
|
||||
<Box component="span" sx={{ color: 'error.main', fontWeight: 700, fontSize: '0.85em', lineHeight: 1 }} aria-hidden>
|
||||
[!]
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
sx={{
|
||||
color: tabValue === 1 ? '#2e7d32' : 'inherit',
|
||||
fontWeight: 'bold'
|
||||
|
||||
27
src/utils/wireGirocode.js
Normal file
27
src/utils/wireGirocode.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import generateQrCode from "sepa-payment-qr-code";
|
||||
import QRCode from "qrcode";
|
||||
import { WIRE_GIROCODE_RECIPIENT } from "../config/wireGirocodeRecipient.js";
|
||||
|
||||
/**
|
||||
* @param {{ amount: number, reference: string }} options
|
||||
* @returns {Promise<string>} data URL for a PNG QR image
|
||||
*/
|
||||
export async function buildWireGirocodeDataUrl({ amount, reference }) {
|
||||
const epcText = generateQrCode({
|
||||
name: WIRE_GIROCODE_RECIPIENT.name,
|
||||
iban: WIRE_GIROCODE_RECIPIENT.iban,
|
||||
bic: WIRE_GIROCODE_RECIPIENT.bic,
|
||||
amount,
|
||||
unstructuredReference: String(reference),
|
||||
});
|
||||
|
||||
return QRCode.toDataURL(epcText, {
|
||||
errorCorrectionLevel: "M",
|
||||
width: 200,
|
||||
margin: 1,
|
||||
color: {
|
||||
dark: "#000000",
|
||||
light: "#FFFFFF",
|
||||
},
|
||||
});
|
||||
}
|
||||
27
src/utils/wireGirocodeEligibility.js
Normal file
27
src/utils/wireGirocodeEligibility.js
Normal file
@@ -0,0 +1,27 @@
|
||||
export function orderGrossTotal(order) {
|
||||
return order.items.reduce(
|
||||
(acc, item) => acc + item.price * item.quantity_ordered,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
export function getPaymentMethodRaw(order) {
|
||||
return String(order.paymentMethod || order.payment_method || "").toLowerCase();
|
||||
}
|
||||
|
||||
/** Pending wire-transfer orders that still need payment (show Girocode row). */
|
||||
export function isWireGirocodeEligible(order) {
|
||||
const amount = Math.round(orderGrossTotal(order) * 100) / 100;
|
||||
return (
|
||||
order.status === "pending" &&
|
||||
getPaymentMethodRaw(order) === "wire" &&
|
||||
amount >= 0.01
|
||||
);
|
||||
}
|
||||
|
||||
export function hasPendingWirePaymentOrder(orders) {
|
||||
return Array.isArray(orders) && orders.some(isWireGirocodeEligible);
|
||||
}
|
||||
|
||||
/** Browser CustomEvent name: detail.pending is boolean */
|
||||
export const WIRE_PAYMENT_PENDING_EVENT = "wirePaymentOrdersPending";
|
||||
Reference in New Issue
Block a user