+ );
+};
diff --git a/wata-board-frontend/src/contracts/README.md b/wata-board-frontend/src/contracts/README.md
new file mode 100644
index 00000000..11199447
--- /dev/null
+++ b/wata-board-frontend/src/contracts/README.md
@@ -0,0 +1,54 @@
+# nepa_client_v2 JS
+
+JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `nepa_client_v2` via Soroban RPC.
+
+This library was automatically generated by Soroban CLI using a command similar to:
+
+```bash
+soroban contract bindings ts \
+ --rpc-url https://soroban-testnet.stellar.org \
+ --network-passphrase "Test SDF Network ; September 2015" \
+ --contract-id CDRRJ7IPYDL36YSK5ZQLBG3LICULETIBXX327AGJQNTWXNKY2UMDO4DA \
+ --output-dir ./path/to/nepa_client_v2
+```
+
+The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily.
+
+# To publish or not to publish
+
+This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command.
+
+But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path:
+
+```json
+"dependencies": {
+ "nepa_client_v2": "./path/to/this/folder"
+}
+```
+
+However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract.
+
+```json
+"scripts": {
+ "postinstall": "soroban contract bindings ts --rpc-url https://soroban-testnet.stellar.org --network-passphrase \"Test SDF Network ; September 2015\" --id CDRRJ7IPYDL36YSK5ZQLBG3LICULETIBXX327AGJQNTWXNKY2UMDO4DA --name nepa_client_v2"
+}
+```
+
+Obviously you need to adjust the above command based on the actual command you used to generate the library.
+
+# Use it
+
+Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods:
+
+```js
+import { Contract, networks } from "nepa_client_v2"
+
+const contract = new Contract({
+ ...networks.futurenet, // for example; check which networks this library exports
+ rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers
+})
+
+contract.|
+```
+
+As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code.
diff --git a/wata-board-frontend/src/contracts/package-lock.json b/wata-board-frontend/src/contracts/package-lock.json
new file mode 100644
index 00000000..41c04eb2
--- /dev/null
+++ b/wata-board-frontend/src/contracts/package-lock.json
@@ -0,0 +1,774 @@
+{
+ "name": "nepa_client_v2",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "nepa_client_v2",
+ "version": "0.0.0",
+ "dependencies": {
+ "@stellar/stellar-sdk": "^14.1.1",
+ "buffer": "6.0.3"
+ },
+ "devDependencies": {
+ "typescript": "^5.6.2"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
+ "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "1.8.0"
+ },
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@stellar/js-xdr": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz",
+ "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@stellar/stellar-base": {
+ "version": "14.0.4",
+ "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-14.0.4.tgz",
+ "integrity": "sha512-UbNW6zbdOBXJwLAV2mMak0bIC9nw3IZVlQXkv2w2dk1jgCbJjy3oRVC943zeGE5JAm0Z9PHxrIjmkpGhayY7kw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@noble/curves": "^1.9.6",
+ "@stellar/js-xdr": "^3.1.2",
+ "base32.js": "^0.1.0",
+ "bignumber.js": "^9.3.1",
+ "buffer": "^6.0.3",
+ "sha.js": "^2.4.12"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@stellar/stellar-sdk": {
+ "version": "14.5.0",
+ "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-14.5.0.tgz",
+ "integrity": "sha512-Uzjq+An/hUA+Q5ERAYPtT0+MMiwWnYYWMwozmZMjxjdL2MmSjucBDF8Q04db6K/ekU4B5cHuOfsdlrfaxQYblw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@stellar/stellar-base": "^14.0.4",
+ "axios": "^1.13.3",
+ "bignumber.js": "^9.3.1",
+ "commander": "^14.0.2",
+ "eventsource": "^2.0.2",
+ "feaxios": "^0.0.23",
+ "randombytes": "^2.1.0",
+ "toml": "^3.0.0",
+ "urijs": "^1.19.1"
+ },
+ "bin": {
+ "stellar-js": "bin/stellar-js"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axios": {
+ "version": "1.13.5",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
+ "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/base32.js": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz",
+ "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eventsource": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
+ "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/feaxios": {
+ "version": "0.0.23",
+ "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.23.tgz",
+ "integrity": "sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==",
+ "license": "MIT",
+ "dependencies": {
+ "is-retry-allowed": "^3.0.0"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-retry-allowed": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz",
+ "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "license": "MIT"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.12",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz",
+ "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+ "license": "(MIT AND BSD-3-Clause)",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "safe-buffer": "^5.2.1",
+ "to-buffer": "^1.2.0"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/to-buffer": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
+ "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
+ "license": "MIT",
+ "dependencies": {
+ "isarray": "^2.0.5",
+ "safe-buffer": "^5.2.1",
+ "typed-array-buffer": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/toml": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
+ "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
+ "license": "MIT"
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/urijs": {
+ "version": "1.19.11",
+ "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz",
+ "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==",
+ "license": "MIT"
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.20",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
+ "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ }
+ }
+}
diff --git a/wata-board-frontend/src/contracts/package.json b/wata-board-frontend/src/contracts/package.json
new file mode 100644
index 00000000..05904883
--- /dev/null
+++ b/wata-board-frontend/src/contracts/package.json
@@ -0,0 +1,17 @@
+{
+ "version": "0.0.0",
+ "name": "nepa_client_v2",
+ "type": "module",
+ "exports": "./dist/index.js",
+ "typings": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc"
+ },
+ "dependencies": {
+ "@stellar/stellar-sdk": "^14.1.1",
+ "buffer": "6.0.3"
+ },
+ "devDependencies": {
+ "typescript": "^5.6.2"
+ }
+}
diff --git a/wata-board-frontend/src/contracts/src/index.ts b/wata-board-frontend/src/contracts/src/index.ts
new file mode 100644
index 00000000..8f9a19e8
--- /dev/null
+++ b/wata-board-frontend/src/contracts/src/index.ts
@@ -0,0 +1,86 @@
+import { Buffer } from "buffer";
+import { Address } from "@stellar/stellar-sdk";
+import {
+ AssembledTransaction,
+ Client as ContractClient,
+ ClientOptions as ContractClientOptions,
+ MethodOptions,
+ Result,
+ Spec as ContractSpec,
+} from "@stellar/stellar-sdk/contract";
+import type {
+ u32,
+ i32,
+ u64,
+ i64,
+ u128,
+ i128,
+ u256,
+ i256,
+ Option,
+ Timepoint,
+ Duration,
+} from "@stellar/stellar-sdk/contract";
+export * from "@stellar/stellar-sdk";
+export * as contract from "@stellar/stellar-sdk/contract";
+export * as rpc from "@stellar/stellar-sdk/rpc";
+
+if (typeof window !== "undefined") {
+ //@ts-ignore Buffer exists
+ window.Buffer = window.Buffer || Buffer;
+}
+
+
+export const networks = {
+ testnet: {
+ networkPassphrase: "Test SDF Network ; September 2015",
+ contractId: "CDRRJ7IPYDL36YSK5ZQLBG3LICULETIBXX327AGJQNTWXNKY2UMDO4DA",
+ rpcUrl: "https://soroban-testnet.stellar.org",
+ },
+ mainnet: {
+ networkPassphrase: "Public Global Stellar Network ; September 2015",
+ contractId: "MAINNET_CONTRACT_ID_HERE", // Replace with actual mainnet contract ID
+ rpcUrl: "https://soroban.stellar.org",
+ }
+} as const
+
+
+export interface Client {
+ /**
+ * Construct and simulate a pay_bill transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
+ */
+ pay_bill: ({meter_id, amount}: {meter_id: string, amount: u32}, options?: MethodOptions) => Promise>
+
+ /**
+ * Construct and simulate a get_total_paid transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
+ */
+ get_total_paid: ({meter_id}: {meter_id: string}, options?: MethodOptions) => Promise>
+
+}
+export class Client extends ContractClient {
+ static async deploy(
+ /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */
+ options: MethodOptions &
+ Omit & {
+ /** The hash of the Wasm blob, which must already be installed on-chain. */
+ wasmHash: Buffer | string;
+ /** Salt used to generate the contract's ID. Passed through to {@link Operation.createCustomContract}. Default: random. */
+ salt?: Buffer | Uint8Array;
+ /** The format used to decode `wasmHash`, if it's provided as a string. */
+ format?: "hex" | "base64";
+ }
+ ): Promise> {
+ return ContractClient.deploy(null, options)
+ }
+ constructor(public readonly options: ContractClientOptions) {
+ super(
+ new ContractSpec([ "AAAAAAAAAAAAAAAIcGF5X2JpbGwAAAACAAAAAAAAAAhtZXRlcl9pZAAAABAAAAAAAAAABmFtb3VudAAAAAAABAAAAAA=",
+ "AAAAAAAAAAAAAAAOZ2V0X3RvdGFsX3BhaWQAAAAAAAEAAAAAAAAACG1ldGVyX2lkAAAAEAAAAAEAAAAE" ]),
+ options
+ )
+ }
+ public readonly fromJSON = {
+ pay_bill: this.txFromJSON,
+ get_total_paid: this.txFromJSON
+ }
+}
\ No newline at end of file
diff --git a/wata-board-frontend/src/contracts/tsconfig.json b/wata-board-frontend/src/contracts/tsconfig.json
new file mode 100644
index 00000000..acac1422
--- /dev/null
+++ b/wata-board-frontend/src/contracts/tsconfig.json
@@ -0,0 +1,98 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+ /* Language and Environment */
+ "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+ /* Modules */
+ "module": "NodeNext", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ "moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
+ /* JavaScript Support */
+ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+ /* Emit */
+ "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ "outDir": "./dist", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ // "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ // "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+ /* Type Checking */
+ // "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ },
+ "include": [
+ "src/*"
+ ]
+}
\ No newline at end of file
diff --git a/wata-board-frontend/src/hooks/useConnectivity.ts b/wata-board-frontend/src/hooks/useConnectivity.ts
new file mode 100644
index 00000000..3ad23188
--- /dev/null
+++ b/wata-board-frontend/src/hooks/useConnectivity.ts
@@ -0,0 +1,345 @@
+import { useState, useEffect, useCallback, useRef } from 'react';
+
+export interface ConnectivityStatus {
+ isOnline: boolean;
+ isOffline: boolean;
+ connectionType: string | null;
+ effectiveType: string | null;
+ downlink: number | null;
+ rtt: number | null;
+ saveData: boolean;
+}
+
+export interface OfflineAction {
+ id: string;
+ type: 'payment' | 'review' | 'other';
+ data: any;
+ timestamp: number;
+ retryCount: number;
+}
+
+const defaultConnectivityStatus: ConnectivityStatus = {
+ isOnline: navigator.onLine,
+ isOffline: !navigator.onLine,
+ connectionType: null,
+ effectiveType: null,
+ downlink: null,
+ rtt: null,
+ saveData: false,
+};
+
+export function useConnectivity() {
+ const [connectivity, setConnectivity] = useState(defaultConnectivityStatus);
+ const [offlineActions, setOfflineActions] = useState([]);
+ const [isReconnecting, setIsReconnecting] = useState(false);
+ const serviceWorkerRef = useRef(null);
+
+ // Get detailed connection information
+ const getConnectionInfo = useCallback((): Partial => {
+ const connection = (navigator as any).connection ||
+ (navigator as any).mozConnection ||
+ (navigator as any).webkitConnection;
+
+ if (!connection) {
+ return {};
+ }
+
+ return {
+ connectionType: connection.type || 'unknown',
+ effectiveType: connection.effectiveType || 'unknown',
+ downlink: connection.downlink || null,
+ rtt: connection.rtt || null,
+ saveData: connection.saveData || false,
+ };
+ }, []);
+
+ // Update connectivity status
+ const updateConnectivity = useCallback((online: boolean) => {
+ const connectionInfo = getConnectionInfo();
+ const newStatus: ConnectivityStatus = {
+ isOnline: online,
+ isOffline: !online,
+ ...connectionInfo,
+ saveData: connectionInfo.saveData || false,
+ };
+
+ setConnectivity(newStatus);
+
+ // Notify service worker
+ if (serviceWorkerRef.current?.active) {
+ serviceWorkerRef.current.active.postMessage({
+ type: 'CONNECTIVITY_CHANGE',
+ isOnline: online,
+ });
+ }
+
+ // Trigger reconnection process if coming back online
+ if (online && offlineActions.length > 0) {
+ setIsReconnecting(true);
+ // We'll trigger processOfflineActions in a separate effect or after state update
+ }
+ }, [getConnectionInfo, offlineActions.length]);
+
+ // Handle reconnection when status changes to online
+ useEffect(() => {
+ if (connectivity.isOnline && offlineActions.length > 0 && !isReconnecting) {
+ setIsReconnecting(true);
+ processOfflineActions();
+ }
+ }, [connectivity.isOnline]);
+
+ // Process offline actions when back online
+ const processOfflineActions = useCallback(async () => {
+ if (offlineActions.length === 0) return;
+
+ console.log(`[Connectivity] Processing ${offlineActions.length} offline actions`);
+
+ for (const action of offlineActions) {
+ try {
+ await retryAction(action);
+ // Remove from state and DB
+ setOfflineActions(prev => prev.filter(a => a.id !== action.id));
+ await removeOfflineAction(action.id);
+ } catch (error) {
+ console.error(`[Connectivity] Failed to retry action ${action.id}:`, error);
+
+ // Update retry count
+ setOfflineActions(prev =>
+ prev.map(a =>
+ a.id === action.id
+ ? { ...a, retryCount: a.retryCount + 1 }
+ : a
+ )
+ );
+ }
+ }
+
+ setIsReconnecting(false);
+ }, [offlineActions]);
+
+ // Retry a specific action
+ const retryAction = useCallback(async (action: OfflineAction): Promise => {
+ switch (action.type) {
+ case 'payment':
+ const response = await fetch('/api/payment', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(action.data),
+ });
+
+ if (!response.ok) {
+ throw new Error(`Payment retry failed: ${response.statusText}`);
+ }
+ break;
+
+ default:
+ throw new Error(`Unknown action type: ${action.type}`);
+ }
+ }, []);
+
+ // Open IndexedDB for offline storage
+ const openOfflineDB = useCallback((): Promise => {
+ return new Promise((resolve, reject) => {
+ const request = indexedDB.open('wata-board-offline', 1);
+
+ request.onerror = () => reject(request.error);
+ request.onsuccess = () => resolve(request.result);
+
+ request.onupgradeneeded = () => {
+ const db = request.result;
+ if (!db.objectStoreNames.contains('actions')) {
+ db.createObjectStore('actions', { keyPath: 'id' });
+ }
+ };
+ });
+ }, []);
+
+ // Remove offline action from IndexedDB
+ const removeOfflineAction = useCallback(async (id: string) => {
+ try {
+ const db = await openOfflineDB();
+ const transaction = db.transaction(['actions'], 'readwrite');
+ const store = transaction.objectStore('actions');
+ return new Promise((resolve, reject) => {
+ const request = store.delete(id);
+ request.onsuccess = () => resolve();
+ request.onerror = () => reject(request.error);
+ });
+ } catch (error) {
+ console.error('[Connectivity] Failed to remove offline action:', error);
+ }
+ }, [openOfflineDB]);
+
+ // Queue action for offline processing
+ const queueOfflineAction = useCallback((type: OfflineAction['type'], data: any) => {
+ const action: OfflineAction = {
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+ type,
+ data,
+ timestamp: Date.now(),
+ retryCount: 0,
+ };
+
+ setOfflineActions(prev => [...prev, action]);
+
+ // Store in IndexedDB for persistence
+ storeOfflineAction(action);
+
+ console.log(`[Connectivity] Queued offline action: ${action.id}`);
+ }, []);
+
+ // Store offline action in IndexedDB
+ const storeOfflineAction = useCallback(async (action: OfflineAction) => {
+ try {
+ const db = await openOfflineDB();
+ const transaction = db.transaction(['actions'], 'readwrite');
+ const store = transaction.objectStore('actions');
+ return new Promise((resolve, reject) => {
+ const request = store.add(action);
+ request.onsuccess = () => resolve();
+ request.onerror = () => reject(request.error);
+ });
+ } catch (error) {
+ console.error('[Connectivity] Failed to store offline action:', error);
+ }
+ }, [openOfflineDB]);
+
+ // Load offline actions from IndexedDB
+ const loadOfflineActions = useCallback(async () => {
+ try {
+ const db = await openOfflineDB();
+ const transaction = db.transaction(['actions'], 'readonly');
+ const store = transaction.objectStore('actions');
+
+ return new Promise((resolve, reject) => {
+ const request = store.getAll();
+ request.onsuccess = () => {
+ setOfflineActions(request.result || []);
+ resolve();
+ };
+ request.onerror = () => reject(request.error);
+ });
+ } catch (error) {
+ console.error('[Connectivity] Failed to load offline actions:', error);
+ }
+ }, [openOfflineDB]);
+
+ // Clear offline actions
+ const clearOfflineActions = useCallback(async () => {
+ setOfflineActions([]);
+
+ try {
+ const db = await openOfflineDB();
+ const transaction = db.transaction(['actions'], 'readwrite');
+ const store = transaction.objectStore('actions');
+ return new Promise((resolve, reject) => {
+ const request = store.clear();
+ request.onsuccess = () => resolve();
+ request.onerror = () => reject(request.error);
+ });
+ } catch (error) {
+ console.error('[Connectivity] Failed to clear offline actions:', error);
+ }
+ }, [openOfflineDB]);
+
+ // Manual connectivity check
+ const checkConnectivity = useCallback(async (): Promise => {
+ try {
+ const response = await fetch('/api/health', {
+ method: 'HEAD',
+ cache: 'no-cache',
+ signal: AbortSignal.timeout(5000),
+ });
+
+ const isOnline = response.ok;
+ updateConnectivity(isOnline);
+ return isOnline;
+ } catch (error) {
+ updateConnectivity(false);
+ return false;
+ }
+ }, [updateConnectivity]);
+
+ // Initialize service worker registration
+ useEffect(() => {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then((registration) => {
+ serviceWorkerRef.current = registration;
+
+ const handleMessage = (event: MessageEvent) => {
+ if (event.data?.type === 'CONNECTIVITY_STATUS') {
+ updateConnectivity(event.data.isOnline);
+ }
+ };
+
+ navigator.serviceWorker.addEventListener('message', handleMessage);
+ return () => navigator.serviceWorker.removeEventListener('message', handleMessage);
+ });
+ }
+ }, [updateConnectivity]);
+
+ // Set up event listeners for connectivity changes
+ useEffect(() => {
+ const handleOnline = () => updateConnectivity(true);
+ const handleOffline = () => updateConnectivity(false);
+
+ window.addEventListener('online', handleOnline);
+ window.addEventListener('offline', handleOffline);
+
+ const connection = (navigator as any).connection;
+ if (connection) {
+ const handleConnectionChange = () => {
+ updateConnectivity(navigator.onLine);
+ };
+ connection.addEventListener('change', handleConnectionChange);
+
+ return () => {
+ window.removeEventListener('online', handleOnline);
+ window.removeEventListener('offline', handleOffline);
+ connection.removeEventListener('change', handleConnectionChange);
+ };
+ }
+
+ return () => {
+ window.removeEventListener('online', handleOnline);
+ window.removeEventListener('offline', handleOffline);
+ };
+ }, [updateConnectivity]);
+
+ // Load offline actions on mount
+ useEffect(() => {
+ loadOfflineActions();
+ }, [loadOfflineActions]);
+
+ // Periodic connectivity check when offline
+ useEffect(() => {
+ if (!connectivity.isOnline) {
+ const interval = setInterval(checkConnectivity, 30000);
+ return () => clearInterval(interval);
+ }
+ }, [connectivity.isOnline, checkConnectivity]);
+
+ return {
+ connectivity,
+ offlineActions,
+ isReconnecting,
+ queueOfflineAction,
+ clearOfflineActions,
+ checkConnectivity,
+ retryAction,
+ };
+}
+
+export function useOfflineCapability(feature: 'payment' | 'review' | 'general') {
+ const { connectivity } = useConnectivity();
+
+ return {
+ isAvailable: connectivity.isOnline,
+ isOffline: connectivity.isOffline,
+ isSlowConnection: connectivity.effectiveType === 'slow-2g' || connectivity.effectiveType === '2g',
+ isDataSaver: connectivity.saveData,
+ canRetry: connectivity.isOnline && !connectivity.isOffline,
+ };
+}
diff --git a/wata-board-frontend/src/hooks/useFeeEstimation.ts b/wata-board-frontend/src/hooks/useFeeEstimation.ts
new file mode 100644
index 00000000..6284b4cb
--- /dev/null
+++ b/wata-board-frontend/src/hooks/useFeeEstimation.ts
@@ -0,0 +1,74 @@
+/**
+ * React hook for fee estimation
+ * Provides a simple interface for estimating and managing transaction fees
+ */
+
+import { useState, useCallback } from 'react';
+import { feeEstimationService } from '../services/feeEstimation';
+import type { FeeEstimate } from '../services/feeEstimation';
+
+export interface UseFeeEstimationReturn {
+ estimate: FeeEstimate | null;
+ isLoading: boolean;
+ error: string | null;
+ estimateFee: (amount: string, destination?: string) => Promise;
+ clearEstimate: () => void;
+ getFeeRecommendations: () => Promise;
+}
+
+export function useFeeEstimation(): UseFeeEstimationReturn {
+ const [estimate, setEstimate] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const estimateFee = useCallback(async (amount: string, destination?: string) => {
+ if (!amount || parseFloat(amount) <= 0) {
+ setEstimate(null);
+ setError(null);
+ return null;
+ }
+
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ const feeEstimate = await feeEstimationService.estimatePaymentFee(amount, destination);
+ setEstimate(feeEstimate);
+ return feeEstimate;
+ } catch (err) {
+ const errorMessage = err instanceof Error ? err.message : 'Failed to estimate fees';
+ setError(errorMessage);
+ setEstimate(null);
+ return null;
+ } finally {
+ setIsLoading(false);
+ }
+ }, []);
+
+ const clearEstimate = useCallback(() => {
+ setEstimate(null);
+ setError(null);
+ setIsLoading(false);
+ }, []);
+
+ const getFeeRecommendations = useCallback(async () => {
+ try {
+ return await feeEstimationService.getFeeRecommendations();
+ } catch (err) {
+ const errorMessage = err instanceof Error ? err.message : 'Failed to get fee recommendations';
+ setError(errorMessage);
+ return null;
+ }
+ }, []);
+
+ return {
+ estimate,
+ isLoading,
+ error,
+ estimateFee,
+ clearEstimate,
+ getFeeRecommendations
+ };
+}
+
+export default useFeeEstimation;
diff --git a/wata-board-frontend/src/hooks/useRTL.ts b/wata-board-frontend/src/hooks/useRTL.ts
new file mode 100644
index 00000000..a8043874
--- /dev/null
+++ b/wata-board-frontend/src/hooks/useRTL.ts
@@ -0,0 +1,66 @@
+import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import { isRTL, getTextDirection } from '../i18n';
+
+export const useRTL = () => {
+ const { i18n } = useTranslation();
+ const [isRTLDirection, setIsRTLDirection] = useState(isRTL(i18n.language));
+ const [textDirection, setTextDirection] = useState(getTextDirection(i18n.language));
+
+ useEffect(() => {
+ // Update RTL state when language changes
+ setIsRTLDirection(isRTL(i18n.language));
+ setTextDirection(getTextDirection(i18n.language));
+
+ // Update document direction
+ document.documentElement.dir = textDirection;
+ document.documentElement.lang = i18n.language;
+ }, [i18n.language, textDirection]);
+
+ // Helper function to get appropriate margin/padding classes for RTL
+ const getDirectionalClass = (left: string, right: string) => {
+ return isRTLDirection ? right : left;
+ };
+
+ // Helper function to get appropriate text alignment
+ const getTextAlignClass = () => {
+ return isRTLDirection ? 'text-right' : 'text-left';
+ };
+
+ // Helper function to get appropriate flex direction
+ const getFlexDirection = () => {
+ return isRTLDirection ? 'flex-row-reverse' : 'flex-row';
+ };
+
+ // Helper function to get appropriate float classes
+ const getFloatClass = (left: string, right: string) => {
+ return isRTLDirection ? right : left;
+ };
+
+ // Helper function to get appropriate border radius for RTL
+ const getBorderRadiusClass = (left: string, right: string) => {
+ return isRTLDirection ? right : left;
+ };
+
+ // Helper function to get appropriate transform for RTL
+ const getTransformClass = (transform: string) => {
+ if (isRTLDirection && transform.includes('translateX')) {
+ return transform.replace(/translateX\(([^)]+)\)/, (match, p1) => {
+ const value = p1.replace('-', '');
+ return `translateX(-${value})`;
+ });
+ }
+ return transform;
+ };
+
+ return {
+ isRTL: isRTLDirection,
+ textDirection,
+ getDirectionalClass,
+ getTextAlignClass,
+ getFlexDirection,
+ getFloatClass,
+ getBorderRadiusClass,
+ getTransformClass
+ };
+};
diff --git a/wata-board-frontend/src/hooks/useRateLimit.ts b/wata-board-frontend/src/hooks/useRateLimit.ts
new file mode 100644
index 00000000..39a691ed
--- /dev/null
+++ b/wata-board-frontend/src/hooks/useRateLimit.ts
@@ -0,0 +1,180 @@
+import { useState, useCallback, useEffect } from 'react';
+
+export interface RateLimitStatus {
+ allowed: boolean;
+ remainingRequests: number;
+ resetTime: Date;
+ queued?: boolean;
+ queuePosition?: number;
+}
+
+export interface UseRateLimitReturn {
+ status: RateLimitStatus | null;
+ isLoading: boolean;
+ error: string | null;
+ checkRateLimit: (userId: string) => Promise;
+ resetStatus: () => void;
+ canMakeRequest: boolean;
+ timeUntilReset: number;
+ queueLength: number;
+}
+
+// In-memory rate limiting for frontend (fallback/backend sync would be better)
+class FrontendRateLimiter {
+ private userRequests: Map = new Map();
+ private readonly windowMs = 60 * 1000; // 1 minute
+ private readonly maxRequests = 5;
+
+ checkLimit(userId: string): RateLimitStatus {
+ const now = Date.now();
+ const windowStart = now - this.windowMs;
+
+ // Clean old requests
+ const userRequestTimes = this.userRequests.get(userId) || [];
+ const validRequests = userRequestTimes.filter(time => time >= windowStart);
+ this.userRequests.set(userId, validRequests);
+
+ const currentCount = validRequests.length;
+ const oldestRequest = validRequests.length > 0 ? Math.min(...validRequests) : now;
+
+ return {
+ allowed: currentCount < this.maxRequests,
+ remainingRequests: Math.max(0, this.maxRequests - currentCount),
+ resetTime: new Date(oldestRequest + this.windowMs),
+ queued: false
+ };
+ }
+
+ recordRequest(userId: string): void {
+ const now = Date.now();
+ const userRequestTimes = this.userRequests.get(userId) || [];
+ userRequestTimes.push(now);
+ this.userRequests.set(userId, userRequestTimes);
+ }
+
+ resetUser(userId: string): void {
+ this.userRequests.delete(userId);
+ }
+}
+
+const frontendRateLimiter = new FrontendRateLimiter();
+
+export function useRateLimit(): UseRateLimitReturn {
+ const [status, setStatus] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [timeUntilReset, setTimeUntilReset] = useState(0);
+
+ const checkRateLimit = useCallback(async (userId: string) => {
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ // Simulate API call to check rate limit
+ // In production, this would call your backend API
+ await new Promise(resolve => setTimeout(resolve, 100));
+
+ const rateLimitStatus = frontendRateLimiter.checkLimit(userId);
+ setStatus(rateLimitStatus);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to check rate limit');
+ } finally {
+ setIsLoading(false);
+ }
+ }, []);
+
+ const resetStatus = useCallback(() => {
+ setStatus(null);
+ setError(null);
+ setTimeUntilReset(0);
+ }, []);
+
+ // Update time until reset every second
+ useEffect(() => {
+ if (!status?.resetTime) return;
+
+ const interval = setInterval(() => {
+ const now = Date.now();
+ const resetTime = status.resetTime.getTime();
+ const remaining = Math.max(0, resetTime - now);
+ setTimeUntilReset(remaining);
+
+ if (remaining === 0) {
+ // Reset completed, check status again
+ if (status.allowed === false) {
+ checkRateLimit('current_user'); // Would use actual user ID
+ }
+ }
+ }, 1000);
+
+ return () => clearInterval(interval);
+ }, [status?.resetTime, status?.allowed, checkRateLimit]);
+
+ const canMakeRequest = status?.allowed ?? true;
+ const queueLength = status?.queuePosition ? status.queuePosition : 0;
+
+ return {
+ status,
+ isLoading,
+ error,
+ checkRateLimit,
+ resetStatus,
+ canMakeRequest,
+ timeUntilReset,
+ queueLength
+ };
+}
+
+export function usePaymentWithRateLimit() {
+ const rateLimit = useRateLimit();
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [paymentError, setPaymentError] = useState(null);
+
+ const processPayment = useCallback(async (
+ paymentFunction: () => Promise,
+ userId: string
+ ) => {
+ // Check rate limit first
+ await rateLimit.checkRateLimit(userId);
+
+ if (!rateLimit.canMakeRequest) {
+ setPaymentError(`Rate limit exceeded. Please wait ${Math.ceil(rateLimit.timeUntilReset / 1000)} seconds.`);
+ return { success: false, error: 'Rate limit exceeded' };
+ }
+
+ setIsProcessing(true);
+ setPaymentError(null);
+
+ try {
+ // Record the request
+ frontendRateLimiter.recordRequest(userId);
+
+ // Execute the payment
+ const result = await paymentFunction();
+
+ // Update rate limit status
+ await rateLimit.checkRateLimit(userId);
+
+ return { success: true, data: result };
+ } catch (error) {
+ const errorMessage = error instanceof Error ? error.message : 'Payment failed';
+ setPaymentError(errorMessage);
+
+ // If payment failed, don't count it against rate limit
+ frontendRateLimiter.resetUser(userId);
+ await rateLimit.checkRateLimit(userId);
+
+ return { success: false, error: errorMessage };
+ } finally {
+ setIsProcessing(false);
+ }
+ }, [rateLimit]);
+
+ return {
+ ...rateLimit,
+ isProcessing,
+ paymentError,
+ processPayment,
+ clearPaymentError: () => setPaymentError(null)
+ };
+}
diff --git a/wata-board-frontend/src/hooks/useRating.ts b/wata-board-frontend/src/hooks/useRating.ts
new file mode 100644
index 00000000..2987e646
--- /dev/null
+++ b/wata-board-frontend/src/hooks/useRating.ts
@@ -0,0 +1,305 @@
+import { useState, useCallback } from 'react';
+import { Server, Networks, TransactionBuilder, Operation, BASE_FEE } from '@stellar/stellar-sdk';
+import { isConnected, requestAccess, signTransaction } from "@stellar/freighter-api";
+import { getCurrentNetworkConfig } from '../utils/network-config';
+
+export interface Review {
+ reviewer: string;
+ rating: number;
+ comment: string;
+ timestamp: number;
+ transaction_hash: string;
+}
+
+export interface RatingStats {
+ total_reviews: number;
+ average_rating: number;
+ rating_counts: number[];
+}
+
+export interface RatingHookReturn {
+ submitReview: (rating: number, comment: string) => Promise<{ success: boolean; txHash?: string; error?: string }>;
+ getUserReview: (userAddress: string) => Promise;
+ getAllReviews: () => Promise;
+ getRatingStats: () => Promise;
+ verifyReview: (userAddress: string, txHash: string) => Promise;
+ isLoading: boolean;
+ error: string | null;
+}
+
+export const useRating = (): RatingHookReturn => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const networkConfig = getCurrentNetworkConfig();
+ const server = new Server(networkConfig.rpcUrl);
+
+ const submitReview = useCallback(async (rating: number, comment: string): Promise<{ success: boolean; txHash?: string; error?: string }> => {
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ // Check if wallet is connected
+ if (!(await isConnected())) {
+ throw new Error('Please connect your wallet first');
+ }
+
+ // Validate inputs
+ if (rating < 1 || rating > 5) {
+ throw new Error('Rating must be between 1 and 5');
+ }
+
+ if (comment.length > 500) {
+ throw new Error('Review comment must be less than 500 characters');
+ }
+
+ if (comment.trim().length === 0) {
+ throw new Error('Review comment cannot be empty');
+ }
+
+ // Get user's public key
+ const publicKey = await requestAccess();
+ if (!publicKey) {
+ throw new Error('Could not get wallet access');
+ }
+
+ // Load user account
+ const account = await server.loadAccount(publicKey);
+
+ // Create a dummy transaction to get a transaction hash
+ // In a real implementation, you might want to use a small XLM transfer
+ // or create a custom transaction type for reviews
+ const transaction = new TransactionBuilder(account, {
+ fee: BASE_FEE,
+ networkPassphrase: networkConfig.networkPassphrase,
+ })
+ .addOperation(Operation.payment({
+ destination: publicKey, // Self-transfer for minimal cost
+ asset: Operation.payment({
+ destination: networkConfig.contractId,
+ asset: 'native',
+ amount: '0.0000001', // Minimum amount
+ }).asset,
+ amount: '0.0000001',
+ }))
+ .setTimeout(30)
+ .build();
+
+ // Sign the transaction
+ const signedTransaction = await signTransaction(transaction.toXDR());
+
+ // Submit the transaction
+ const result = await server.submitTransaction(signedTransaction);
+
+ // Now call the smart contract to submit the review
+ const contract = new Contract(networkConfig.contractId);
+
+ const reviewTx = new TransactionBuilder(account, {
+ fee: BASE_FEE,
+ networkPassphrase: networkConfig.networkPassphrase,
+ })
+ .addOperation(Operation.invokeContractFunction({
+ contract: contract.contractId(),
+ function: 'submit_review',
+ args: [
+ // reviewer: Address
+ new Address(publicKey).toScVal(),
+ // rating: i64
+ new XdrLargeInt('i64', rating).toScVal(),
+ // comment: String
+ new String(comment).toScVal(),
+ // transaction_hash: String
+ new String(result.hash).toScVal(),
+ ],
+ }))
+ .setTimeout(30)
+ .build();
+
+ // Sign and submit the review transaction
+ const signedReviewTx = await signTransaction(reviewTx.toXDR());
+ const reviewResult = await server.submitTransaction(signedReviewTx);
+
+ return {
+ success: true,
+ txHash: reviewResult.hash,
+ };
+
+ } catch (err: any) {
+ const errorMessage = err?.message || 'Failed to submit review';
+ setError(errorMessage);
+ return {
+ success: false,
+ error: errorMessage,
+ };
+ } finally {
+ setIsLoading(false);
+ }
+ }, [networkConfig, server]);
+
+ const getUserReview = useCallback(async (userAddress: string): Promise => {
+ try {
+ const contract = new Contract(networkConfig.contractId);
+
+ const tx = new TransactionBuilder(new Account(networkConfig.contractId, '1'), {
+ fee: BASE_FEE,
+ networkPassphrase: networkConfig.networkPassphrase,
+ })
+ .addOperation(Operation.invokeContractFunction({
+ contract: contract.contractId(),
+ function: 'get_user_review',
+ args: [
+ new Address(userAddress).toScVal(),
+ ],
+ }))
+ .setTimeout(30)
+ .build();
+
+ // Simulate the transaction to get the result
+ const result = await server.simulateTransaction(tx);
+
+ if (result.result && result.result !== '0') {
+ // Parse the review from the result
+ // This is a simplified parsing - you'd need to properly parse the XDR result
+ const reviewData = JSON.parse(result.result);
+
+ return {
+ reviewer: reviewData.reviewer,
+ rating: reviewData.rating,
+ comment: reviewData.comment,
+ timestamp: reviewData.timestamp,
+ transaction_hash: reviewData.transaction_hash,
+ };
+ }
+
+ return null;
+ } catch (err: any) {
+ console.error('Error getting user review:', err);
+ return null;
+ }
+ }, [networkConfig, server]);
+
+ const getAllReviews = useCallback(async (): Promise => {
+ try {
+ const contract = new Contract(networkConfig.contractId);
+
+ const tx = new TransactionBuilder(new Account(networkConfig.contractId, '1'), {
+ fee: BASE_FEE,
+ networkPassphrase: networkConfig.networkPassphrase,
+ })
+ .addOperation(Operation.invokeContractFunction({
+ contract: contract.contractId(),
+ function: 'get_all_reviews',
+ args: [],
+ }))
+ .setTimeout(30)
+ .build();
+
+ // Simulate the transaction to get the result
+ const result = await server.simulateTransaction(tx);
+
+ if (result.result && result.result !== '0') {
+ // Parse the reviews from the result
+ // This is a simplified parsing - you'd need to properly parse the XDR result
+ const reviewsData = JSON.parse(result.result);
+
+ return reviewsData.map((review: any) => ({
+ reviewer: review.reviewer,
+ rating: review.rating,
+ comment: review.comment,
+ timestamp: review.timestamp,
+ transaction_hash: review.transaction_hash,
+ }));
+ }
+
+ return [];
+ } catch (err: any) {
+ console.error('Error getting all reviews:', err);
+ return [];
+ }
+ }, [networkConfig, server]);
+
+ const getRatingStats = useCallback(async (): Promise => {
+ try {
+ const contract = new Contract(networkConfig.contractId);
+
+ const tx = new TransactionBuilder(new Account(networkConfig.contractId, '1'), {
+ fee: BASE_FEE,
+ networkPassphrase: networkConfig.networkPassphrase,
+ })
+ .addOperation(Operation.invokeContractFunction({
+ contract: contract.contractId(),
+ function: 'get_rating_stats',
+ args: [],
+ }))
+ .setTimeout(30)
+ .build();
+
+ // Simulate the transaction to get the result
+ const result = await server.simulateTransaction(tx);
+
+ if (result.result && result.result !== '0') {
+ // Parse the stats from the result
+ // This is a simplified parsing - you'd need to properly parse the XDR result
+ const statsData = JSON.parse(result.result);
+
+ return {
+ total_reviews: statsData.total_reviews,
+ average_rating: statsData.average_rating / 10, // Convert back from *10
+ rating_counts: statsData.rating_counts,
+ };
+ }
+
+ return {
+ total_reviews: 0,
+ average_rating: 0,
+ rating_counts: [0, 0, 0, 0, 0],
+ };
+ } catch (err: any) {
+ console.error('Error getting rating stats:', err);
+ return {
+ total_reviews: 0,
+ average_rating: 0,
+ rating_counts: [0, 0, 0, 0, 0],
+ };
+ }
+ }, [networkConfig, server]);
+
+ const verifyReview = useCallback(async (userAddress: string, txHash: string): Promise => {
+ try {
+ const contract = new Contract(networkConfig.contractId);
+
+ const tx = new TransactionBuilder(new Account(networkConfig.contractId, '1'), {
+ fee: BASE_FEE,
+ networkPassphrase: networkConfig.networkPassphrase,
+ })
+ .addOperation(Operation.invokeContractFunction({
+ contract: contract.contractId(),
+ function: 'verify_review',
+ args: [
+ new Address(userAddress).toScVal(),
+ new String(txHash).toScVal(),
+ ],
+ }))
+ .setTimeout(30)
+ .build();
+
+ // Simulate the transaction to get the result
+ const result = await server.simulateTransaction(tx);
+
+ return result.result === 'true';
+ } catch (err: any) {
+ console.error('Error verifying review:', err);
+ return false;
+ }
+ }, [networkConfig, server]);
+
+ return {
+ submitReview,
+ getUserReview,
+ getAllReviews,
+ getRatingStats,
+ verifyReview,
+ isLoading,
+ error,
+ };
+};
diff --git a/wata-board-frontend/src/hooks/useWalletBalance.ts b/wata-board-frontend/src/hooks/useWalletBalance.ts
new file mode 100644
index 00000000..c79d2f44
--- /dev/null
+++ b/wata-board-frontend/src/hooks/useWalletBalance.ts
@@ -0,0 +1,207 @@
+/**
+ * React hook for wallet balance management
+ * Provides real-time balance updates and wallet state management
+ */
+
+import { useState, useEffect, useCallback, useRef } from 'react';
+import { walletBalanceService } from '../services/walletBalance';
+import type { WalletBalance, BalanceInfo } from '../services/walletBalance';
+import { isConnected } from '../utils/wallet-bridge';
+
+export interface UseWalletBalanceReturn {
+ balance: WalletBalance | null;
+ isLoading: boolean;
+ error: string | null;
+ isConnected: boolean;
+ isLowBalance: boolean;
+ lastUpdated: Date | null;
+ refreshBalance: () => Promise;
+ getBalanceByAsset: (assetCode: string) => BalanceInfo | null;
+ formatBalance: (balance: string, decimals?: number) => string;
+ isSufficientBalance: (amount: number, includeReserve?: boolean) => boolean;
+}
+
+export function useWalletBalance(autoRefresh: boolean = true): UseWalletBalanceReturn {
+ const [balance, setBalance] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [walletConnected, setWalletConnected] = useState(false);
+ const [lastUpdated, setLastUpdated] = useState(null);
+
+ const mountedRef = useRef(true);
+ const subscriptionRef = useRef<(() => void) | null>(null);
+
+ // Check wallet connection status
+ const checkWalletConnection = useCallback(async () => {
+ try {
+ const result = await isConnected();
+ const connected = !!result.isConnected;
+ console.log('[useWalletBalance] isConnected result:', JSON.stringify(result));
+ setWalletConnected(connected);
+ return connected;
+ } catch (err) {
+ console.error('[useWalletBalance] Failed to check wallet connection:', err);
+ setWalletConnected(false);
+ return false;
+ }
+ }, []);
+
+ // Refresh balance
+ const refreshBalance = useCallback(async () => {
+ if (!mountedRef.current) return;
+
+ const connected = await checkWalletConnection();
+ if (!connected) {
+ setBalance(null);
+ setError('Wallet not connected');
+ setLastUpdated(null);
+ return;
+ }
+
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ const walletBalance = await walletBalanceService.refreshBalance();
+ if (mountedRef.current) {
+ setBalance(walletBalance);
+ setLastUpdated(walletBalance?.lastUpdated || new Date());
+ setError(null);
+ }
+ } catch (err) {
+ if (mountedRef.current) {
+ const errorMessage = err instanceof Error ? err.message : 'Failed to fetch balance';
+ setError(errorMessage);
+ console.error('Balance refresh failed:', err);
+ }
+ } finally {
+ if (mountedRef.current) {
+ setIsLoading(false);
+ }
+ }
+ }, [checkWalletConnection]);
+
+ // Get balance by asset code
+ const getBalanceByAsset = useCallback((assetCode: string): BalanceInfo | null => {
+ if (!balance) return null;
+ return walletBalanceService.getBalanceByAsset(balance, assetCode);
+ }, [balance]);
+
+ // Format balance for display
+ const formatBalance = useCallback((balanceStr: string, decimals: number = 7): string => {
+ return walletBalanceService.formatBalance(balanceStr, decimals);
+ }, []);
+
+ // Check if balance is sufficient for transaction
+ const isSufficientBalance = useCallback((amount: number, includeReserve: boolean = true): boolean => {
+ if (!balance) return false;
+ return walletBalanceService.isSufficientBalance(balance, amount, includeReserve);
+ }, [balance]);
+
+ // Check if balance is low
+ const isLowBalance = balance ? walletBalanceService.isLowBalance(balance) : false;
+
+ // Initialize and setup real-time updates
+ useEffect(() => {
+ mountedRef.current = true;
+
+ // Initial load
+ refreshBalance();
+
+ // Subscribe to balance updates
+ subscriptionRef.current = walletBalanceService.subscribe((newBalance) => {
+ if (mountedRef.current) {
+ setBalance(newBalance);
+ setLastUpdated(newBalance.lastUpdated);
+ setError(null);
+ setIsLoading(false);
+ }
+ });
+
+ // Start real-time updates if enabled
+ if (autoRefresh) {
+ walletBalanceService.startRealTimeUpdates(15000); // Update every 15 seconds
+ }
+
+ // Periodic connection check
+ const connectionCheckInterval = setInterval(checkWalletConnection, 5000);
+
+ return () => {
+ mountedRef.current = false;
+
+ // Cleanup subscription
+ if (subscriptionRef.current) {
+ subscriptionRef.current();
+ subscriptionRef.current = null;
+ }
+
+ // Stop real-time updates
+ walletBalanceService.stopRealTimeUpdates();
+
+ // Clear connection check
+ clearInterval(connectionCheckInterval);
+ };
+ }, [refreshBalance, autoRefresh, checkWalletConnection]);
+
+ return {
+ balance,
+ isLoading,
+ error,
+ isConnected: walletConnected,
+ isLowBalance,
+ lastUpdated,
+ refreshBalance,
+ getBalanceByAsset,
+ formatBalance,
+ isSufficientBalance
+ };
+}
+
+// Hook for balance history (optional feature)
+export function useBalanceHistory() {
+ const [history, setHistory] = useState([]);
+
+ const addToHistory = useCallback((balance: WalletBalance) => {
+ setHistory((prev: WalletBalance[]) => {
+ const newHistory = [...prev, balance];
+ // Keep only last 50 entries
+ return newHistory.slice(-50);
+ });
+ }, []);
+
+ const clearHistory = useCallback(() => {
+ setHistory([]);
+ }, []);
+
+ return {
+ history,
+ isLoading: false,
+ addToHistory,
+ clearHistory
+ };
+}
+
+// Hook for balance notifications
+export function useBalanceNotifications() {
+ const [notifications, setNotifications] = useState([]);
+
+ const addNotification = useCallback((message: string) => {
+ setNotifications((prev: string[]) => [...prev, message]);
+ // Auto-remove after 5 seconds
+ setTimeout(() => {
+ setNotifications((prev: string[]) => prev.slice(1));
+ }, 5000);
+ }, []);
+
+ const clearNotifications = useCallback(() => {
+ setNotifications([]);
+ }, []);
+
+ return {
+ notifications,
+ addNotification,
+ clearNotifications
+ };
+}
+
+export default useWalletBalance;
diff --git a/wata-board-frontend/src/i18n/index.ts b/wata-board-frontend/src/i18n/index.ts
new file mode 100644
index 00000000..662b9924
--- /dev/null
+++ b/wata-board-frontend/src/i18n/index.ts
@@ -0,0 +1,168 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+
+// Import translation files
+import en from './locales/en.json';
+import es from './locales/es.json';
+import fr from './locales/fr.json';
+import de from './locales/de.json';
+import zh from './locales/zh.json';
+import ar from './locales/ar.json';
+import hi from './locales/hi.json';
+import pt from './locales/pt.json';
+import ru from './locales/ru.json';
+import ja from './locales/ja.json';
+
+// Supported languages configuration
+export const supportedLanguages = [
+ { code: 'en', name: 'English', nativeName: 'English', dir: 'ltr', flag: '🇺🇸' },
+ { code: 'es', name: 'Spanish', nativeName: 'Español', dir: 'ltr', flag: '🇪🇸' },
+ { code: 'fr', name: 'French', nativeName: 'Français', dir: 'ltr', flag: '🇫🇷' },
+ { code: 'de', name: 'German', nativeName: 'Deutsch', dir: 'ltr', flag: '🇩🇪' },
+ { code: 'zh', name: 'Chinese', nativeName: '中文', dir: 'ltr', flag: '🇨🇳' },
+ { code: 'ar', name: 'Arabic', nativeName: 'العربية', dir: 'rtl', flag: '🇸🇦' },
+ { code: 'hi', name: 'Hindi', nativeName: 'हिन्दी', dir: 'ltr', flag: '🇮🇳' },
+ { code: 'pt', name: 'Portuguese', nativeName: 'Português', dir: 'ltr', flag: '🇧🇷' },
+ { code: 'ru', name: 'Russian', nativeName: 'Русский', dir: 'ltr', flag: '🇷🇺' },
+ { code: 'ja', name: 'Japanese', nativeName: '日本語', dir: 'ltr', flag: '🇯🇵' }
+] as const;
+
+// Resources object with all translations
+const resources = {
+ en: { translation: en },
+ es: { translation: es },
+ fr: { translation: fr },
+ de: { translation: de },
+ zh: { translation: zh },
+ ar: { translation: ar },
+ hi: { translation: hi },
+ pt: { translation: pt },
+ ru: { translation: ru },
+ ja: { translation: ja }
+};
+
+// Default language
+const defaultLanguage = 'en';
+
+// Initialize i18n
+i18n
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ resources,
+ fallbackLng: defaultLanguage,
+ debug: process.env.NODE_ENV === 'development',
+
+ // Language detection configuration
+ detection: {
+ order: ['localStorage', 'navigator', 'htmlTag', 'path', 'subdomain'],
+ caches: ['localStorage'],
+ lookupLocalStorage: 'wata-board-language',
+ checkWhitelist: true
+ },
+
+ // Interpolation configuration
+ interpolation: {
+ escapeValue: false,
+ formatSeparator: ',',
+ format: function(value, format, lng) {
+ if (format === 'uppercase') return value.toUpperCase();
+ if (format === 'lowercase') return value.toLowerCase();
+ if (format === 'capitalize') return value.charAt(0).toUpperCase() + value.slice(1);
+ return value;
+ }
+ },
+
+ // React configuration
+ react: {
+ useSuspense: false,
+ bindI18n: 'languageChanged',
+ bindI18nStore: 'added removed',
+ transEmptyNodeValue: '',
+ transSupportBasicHtmlNodes: true,
+ transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'em', 'span']
+ },
+
+ // Whitelist of supported languages
+ supportedLngs: supportedLanguages.map(lang => lang.code),
+
+ // Load configuration
+ load: 'languageOnly',
+
+ // Preload languages for better performance
+ preload: ['en', 'es', 'fr', 'de', 'zh']
+ });
+
+// Export i18n instance and utilities
+export default i18n;
+
+// Helper function to get current language info
+export const getCurrentLanguage = () => {
+ const currentCode = i18n.language;
+ return supportedLanguages.find(lang => lang.code === currentCode) || supportedLanguages[0];
+};
+
+// Helper function to change language
+export const changeLanguage = async (languageCode: string) => {
+ try {
+ await i18n.changeLanguage(languageCode);
+
+ // Update document direction for RTL languages
+ const langInfo = supportedLanguages.find(lang => lang.code === languageCode);
+ if (langInfo) {
+ document.documentElement.dir = langInfo.dir;
+ document.documentElement.lang = languageCode;
+ }
+
+ // Store preference
+ localStorage.setItem('wata-board-language', languageCode);
+
+ return true;
+ } catch (error) {
+ console.error('Failed to change language:', error);
+ return false;
+ }
+};
+
+// Helper function to get text direction for a language
+export const getTextDirection = (languageCode: string) => {
+ const langInfo = supportedLanguages.find(lang => lang.code === languageCode);
+ return langInfo?.dir || 'ltr';
+};
+
+// Helper function to check if language is RTL
+export const isRTL = (languageCode?: string) => {
+ const langCode = languageCode || i18n.language;
+ return getTextDirection(langCode) === 'rtl';
+};
+
+// Helper function to format numbers with locale
+export const formatNumber = (number: number, options?: Intl.NumberFormatOptions) => {
+ const locale = i18n.language === 'zh' ? 'zh-CN' : i18n.language;
+ return new Intl.NumberFormat(locale, options).format(number);
+};
+
+// Helper function to format currency
+export const formatCurrency = (amount: number, currency = 'XLM') => {
+ const locale = i18n.language === 'zh' ? 'zh-CN' : i18n.language;
+ return new Intl.NumberFormat(locale, {
+ style: 'currency',
+ currency: currency,
+ minimumFractionDigits: 7,
+ maximumFractionDigits: 7
+ }).format(amount);
+};
+
+// Helper function to format dates
+export const formatDate = (date: Date, options?: Intl.DateTimeFormatOptions) => {
+ const locale = i18n.language === 'zh' ? 'zh-CN' : i18n.language;
+ return new Intl.DateTimeFormat(locale, options).format(date);
+};
+
+// Helper function to get plural form
+export const getPluralForm = (count: number) => {
+ const locale = i18n.language;
+ const pluralRules = new Intl.PluralRules(locale);
+ return pluralRules.select(count);
+};
diff --git a/wata-board-frontend/src/i18n/locales/ar.json b/wata-board-frontend/src/i18n/locales/ar.json
new file mode 100644
index 00000000..98398c1b
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/ar.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "واتا-بورد",
+ "tagline": "مدفوعات الخدمات العامة اللامركزية على بلوكشين ستيلار",
+ "description": "ادفع فواتير الخدمات العامة باستخدام العملة المشفرة على شبكة ستيلار"
+ },
+ "navigation": {
+ "home": "دفع الفاتورة",
+ "about": "من نحن",
+ "contact": "اتصل بنا",
+ "rate": "قيّمنا",
+ "language": "اللغة"
+ },
+ "payment": {
+ "form": {
+ "title": "معلومات الدفع",
+ "meterNumber": "رقم العداد",
+ "meterPlaceholder": "مثال: METER-123",
+ "meterDescription": "أدخل رقم عداد الخدمات كما هو موضح في فاتورتك",
+ "amount": "المبلغ",
+ "amountPlaceholder": "رقم صحيح",
+ "amountDescription": "أدخل مبلغ الدفع بالعملة XLM الصحيحة",
+ "payButton": "دفع الفاتورة",
+ "processing": "جاري المعالجة...",
+ "rateLimited": "تم الوصول للحد",
+ "requiresExtension": "يتطلب إضافة فرايتر. حد 5 معاملات في الدقيقة."
+ },
+ "status": {
+ "title": "الحالة",
+ "ready": "جاهز.",
+ "installWallet": "يرجى تثبيت إضافة محفظة فرايتر!",
+ "enterMeter": "يرجى إدخال رقم العداد.",
+ "enterValidAmount": "يرجى إدخال مبلغ صحيح أكبر من 0.",
+ "wholeNumber": "يجب أن يكون المبلغ رقماً صحيحاً.",
+ "amountTooLarge": "المبلغ كبير جداً.",
+ "insufficientBalance": "الرصيد غير كافٍ. يرجى إضافة المزيد من XLM إلى محفظتك.",
+ "estimatingFees": "جاري تقدير رسوم المعاملة... يرجى الانتظار.",
+ "paymentSuccess": "تم الدفع بنجاح! معرف المعاملة: {{id}}...",
+ "paymentFailed": "فشل الدفع: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "تقدير رسوم المعاملة",
+ "calculating": "(جاري الحساب...)",
+ "calculatingFees": "جاري حساب الرسوم المقدرة...",
+ "paymentAmount": "مبلغ الدفع",
+ "estimatedNetworkFee": "رسوم الشبكة المقدرة",
+ "totalCost": "إجمالي التكلفة",
+ "unableToEstimate": "لا يمكن تقدير الرسوم في الوقت الحالي"
+ },
+ "rateLimit": {
+ "title": "حالة حد المعدل",
+ "requestsAvailable": "{{count}}/5 طلبات متاحة",
+ "rateLimited": "تم الوصول للحد. إعادة تعيين في {{time}}",
+ "queue": "الطابور: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "رصيد المحفظة",
+ "available": "الرصيد المتاح",
+ "total": "إجمالي الرصيد",
+ "loading": "جاري تحميل الرصيد...",
+ "error": "فشل تحميل الرصيد",
+ "reconnect": "يرجى إعادة الاتصال بمحفظتك",
+ "insufficient": "الرصيد غير كافٍ لهذه المعاملة",
+ "lowBalance": "تحذير انخفاض الرصيد"
+ },
+ "connection": {
+ "connected": "متصل",
+ "disconnected": "غير متصل",
+ "connecting": "جاري الاتصال...",
+ "connectWallet": "اتصل بالمحفظة",
+ "switchWallet": "تبديل المحفظة"
+ }
+ },
+ "network": {
+ "mainnet": "الشبكة الرئيسية",
+ "testnet": "شبكة الاختبار",
+ "switchNetwork": "تبديل الشبكة",
+ "currentNetwork": "الشبكة الحالية"
+ },
+ "offline": {
+ "banner": {
+ "title": "أنت غير متصل",
+ "description": "بعض الميزات قد لا تكون متاحة. سيتم وضع الإجراءات في الطابور ومعالجتها عند عودتك إلى الاتصال.",
+ "retry": "إعادة محاولة الاتصال",
+ "details": "تفاصيل الاتصال"
+ },
+ "status": {
+ "online": "متصل",
+ "offline": "غير متصل",
+ "connecting": "جاري الاتصال...",
+ "queueStatus": "{{count}} إجراء في الطابور",
+ "queueStatus_plural": "{{count}} إجراءات في الطابور"
+ },
+ "queue": {
+ "title": "طابور عدم الاتصال",
+ "description": "سيتم معالجة {{count}} إجراء عند عودتك إلى الاتصال",
+ "description_plural": "سيتم معالجة {{count}} إجراءات عند عودتك إلى الاتصال"
+ },
+ "error": {
+ "paymentOffline": "الدفع في الطابور للمعالجة عند عودتك إلى الاتصال",
+ "generalOffline": "الإجراء في الطابور للمعالجة عند عودتك إلى الاتصال"
+ }
+ },
+ "about": {
+ "title": "حول واتا-بورد",
+ "description": "واتا-بورد هي منصة دفع خدمات عامة لامركزية مبنية على بلوكشين ستيلار. نتمكن من دفع فواتير الخدمات الآمنة والشفافة والفعالة باستخدام العملة المشفرة.",
+ "features": {
+ "title": "الميزات الرئيسية",
+ "secure": "معاملات آمنة",
+ "secureDescription": "مبنية على بلوكشين ستيلار مع أمان على مستوى المؤسسات",
+ "fast": "دفعات سريعة",
+ "fastDescription": "تسوية شبه فورية مع رسوم معاملات منخفضة",
+ "transparent": "سجلات شفافة",
+ "transparentDescription": "جميع المعاملات مسجلة على البلوكشين العام",
+ "decentralized": "لامركزي",
+ "decentralizedDescription": "لا يوجد نقطة واحدة للفشل أو التحكم"
+ },
+ "howItWorks": {
+ "title": "كيف تعمل",
+ "step1": "اتصل بمحفظتك",
+ "step2": "أدخل تفاصيل العداد",
+ "step3": "أكد الدفع",
+ "step4": "تمت معالجة الدفع"
+ }
+ },
+ "contact": {
+ "title": "اتصل بنا",
+ "description": "تواصل مع فريقنا للحصول على الدعم أو الأسئلة أو الملاحظات.",
+ "form": {
+ "name": "الاسم",
+ "namePlaceholder": "أدخل اسمك",
+ "email": "البريد الإلكتروني",
+ "emailPlaceholder": "أدخل بريدك الإلكتروني",
+ "subject": "الموضوع",
+ "subjectPlaceholder": "أدخل الموضوع",
+ "message": "الرسالة",
+ "messagePlaceholder": "أدخل رسالتك",
+ "sendButton": "إرسال الرسالة",
+ "sending": "جاري الإرسال..."
+ },
+ "info": {
+ "email": "البريد الإلكتروني",
+ "support": "الدعم",
+ "website": "الموقع الإلكتروني"
+ }
+ },
+ "rate": {
+ "title": "قيم تجربتك",
+ "description": "ساعدنا في التحسين من خلال تقييم تجربتك مع واتا-بورد.",
+ "rating": {
+ "excellent": "ممتاز",
+ "good": "جيد",
+ "average": "متوسط",
+ "poor": "ضعيف",
+ "terrible": "سيء جداً"
+ },
+ "review": {
+ "title": "اكتب مراجعة",
+ "placeholder": "شارك تجربتك مع واتا-بورد...",
+ "submit": "إرسال المراجعة",
+ "thankYou": "شكراً على ملاحظاتك!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "خطأ",
+ "unknown": "حدث خطأ غير معروف",
+ "tryAgain": "يرجى المحاولة مرة أخرى",
+ "contactSupport": "اتصل بالدعم إذا استمرت المشكلة"
+ },
+ "network": {
+ "title": "خطأ في الشبكة",
+ "description": "لا يمكن الاتصال بالشبكة",
+ "checkConnection": "يرجى فحص اتصالك بالإنترنت"
+ },
+ "wallet": {
+ "title": "خطأ في المحفظة",
+ "notConnected": "المحفظة غير متصلة",
+ "notInstalled": "محفظة فرايتر غير مثبتة",
+ "installFreighter": "يرجى تثبيت إضافة محفظة فرايتر"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "تخطي إلى المحتوى الرئيسي",
+ "skipToNavigation": "تخطي إلى التنقل",
+ "menuToggle": "تبديل قائمة التنقل",
+ "closeMenu": "إغلاق القائمة",
+ "openMenu": "فتح القائمة",
+ "loading": "جاري التحميل...",
+ "error": "خطأ",
+ "success": "نجح",
+ "warning": "تحذير",
+ "info": "معلومات"
+ },
+ "common": {
+ "cancel": "إلغاء",
+ "confirm": "تأكيد",
+ "save": "حفظ",
+ "delete": "حذف",
+ "edit": "تحرير",
+ "close": "إغلاق",
+ "back": "رجوع",
+ "next": "التالي",
+ "previous": "السابق",
+ "submit": "إرسال",
+ "reset": "إعادة تعيين",
+ "clear": "مسح",
+ "search": "بحث",
+ "filter": "تصفية",
+ "sort": "ترتيب",
+ "loading": "جاري التحميل...",
+ "error": "خطأ",
+ "success": "نجح",
+ "retry": "إعادة المحاولة",
+ "refresh": "تحديث",
+ "copy": "نسخ",
+ "copied": "تم النسخ!",
+ "learnMore": "تعلم المزيد",
+ "viewDetails": "عرض التفاصيل",
+ "hideDetails": "إخفاء التفاصيل"
+ },
+ "time": {
+ "seconds": "ثانية",
+ "seconds_plural": "ثواني",
+ "minutes": "دقيقة",
+ "minutes_plural": "دقائق",
+ "hours": "ساعة",
+ "hours_plural": "ساعات",
+ "days": "يوم",
+ "days_plural": "أيام",
+ "weeks": "أسبوع",
+ "weeks_plural": "أسابيع",
+ "months": "شهر",
+ "months_plural": "أشهر",
+ "years": "سنة",
+ "years_plural": "سنوات",
+ "ago": "منذ {{time}}",
+ "in": "في {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "هذا الحقل مطلوب",
+ "invalid": "تنسيق غير صالح",
+ "tooShort": "يجب أن يحتوي على الأقل {{min}} حرف",
+ "tooLong": "لا يجب أن يحتوي على أكثر من {{max}} حرف",
+ "invalidEmail": "يرجى إدخال عنوان بريد إلكتروني صالح",
+ "invalidNumber": "يرجى إدخال رقم صالح",
+ "minValue": "يجب أن يكون على الأقل {{min}}",
+ "maxValue": "لا يجب أن يتجاوز {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/de.json b/wata-board-frontend/src/i18n/locales/de.json
new file mode 100644
index 00000000..abaaa594
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/de.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Dezentralisierte Versorgungsunternehmenzahlungen auf der Stellar-Blockchain",
+ "description": "Bezahlen Sie Ihre Versorgerrechnungen mit Kryptowährung auf dem Stellar-Netzwerk"
+ },
+ "navigation": {
+ "home": "Rechnung Bezahlen",
+ "about": "Über Uns",
+ "contact": "Kontakt",
+ "rate": "Bewerten Sie Uns",
+ "language": "Sprache"
+ },
+ "payment": {
+ "form": {
+ "title": "Zahlungsinformationen",
+ "meterNumber": "Zählernummer",
+ "meterPlaceholder": "z.B. ZÄHLER-123",
+ "meterDescription": "Geben Sie Ihre Versorgerzählernummer wie auf Ihrer Rechnung angegeben ein",
+ "amount": "Betrag",
+ "amountPlaceholder": "Ganzzahl",
+ "amountDescription": "Geben Sie den Zahlungsbetrag in ganzer XLM ein",
+ "payButton": "Rechnung bezahlen",
+ "processing": "Verarbeitung...",
+ "rateLimited": "Limit Erreicht",
+ "requiresExtension": "Benötigt Freighter-Erweiterung. Limit von 5 Transaktionen pro Minute."
+ },
+ "status": {
+ "title": "Status",
+ "ready": "Bereit.",
+ "installWallet": "Bitte installieren Sie die Freighter Wallet-Erweiterung!",
+ "enterMeter": "Bitte geben Sie eine Zählernummer ein.",
+ "enterValidAmount": "Bitte geben Sie einen gültigen Betrag größer als 0 ein.",
+ "wholeNumber": "Der Betrag muss eine Ganzzahl sein.",
+ "amountTooLarge": "Der Betrag ist zu groß.",
+ "insufficientBalance": "Unzureichendes Guthaben. Bitte fügen Sie mehr XLM zu Ihrer Wallet hinzu.",
+ "estimatingFees": "Schätzung der Transaktionsgebühren... Bitte warten.",
+ "paymentSuccess": "Zahlung erfolgreich! Transaktions-ID: {{id}}...",
+ "paymentFailed": "Zahlung fehlgeschlagen: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Transaktionsgebühren-Schätzung",
+ "calculating": "(Berechnen...)",
+ "calculatingFees": "Berechnung der geschätzten Gebühren...",
+ "paymentAmount": "Zahlungsbetrag",
+ "estimatedNetworkFee": "Geschätzte Netzwerkgebühr",
+ "totalCost": "Gesamtkosten",
+ "unableToEstimate": "Gebühren können derzeit nicht geschätzt werden"
+ },
+ "rateLimit": {
+ "title": "Ratenlimit-Status",
+ "requestsAvailable": "{{count}}/5 Anfragen verfügbar",
+ "rateLimited": "Limit erreicht. Zurücksetzung in {{time}}",
+ "queue": "Warteschlange: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Wallet-Guthaben",
+ "available": "Verfügbares Guthaben",
+ "total": "Gesamtguthaben",
+ "loading": "Guthaben wird geladen...",
+ "error": "Fehler beim Laden des Guthabens",
+ "reconnect": "Bitte verbinden Sie Ihre Wallet erneut",
+ "insufficient": "Unzureichendes Guthaben für diese Transaktion",
+ "lowBalance": "Niedriges Guthaben Warnung"
+ },
+ "connection": {
+ "connected": "Verbunden",
+ "disconnected": "Getrennt",
+ "connecting": "Verbinden...",
+ "connectWallet": "Wallet Verbinden",
+ "switchWallet": "Wallet Wechseln"
+ }
+ },
+ "network": {
+ "mainnet": "HAUPTNETZ",
+ "testnet": "TESTNETZ",
+ "switchNetwork": "Netzwerk Wechseln",
+ "currentNetwork": "Aktuelles Netzwerk"
+ },
+ "offline": {
+ "banner": {
+ "title": "Sie sind offline",
+ "description": "Einige Funktionen sind möglicherweise nicht verfügbar. Aktionen werden in die Warteschlange gestellt und verarbeitet, wenn Sie wieder online sind.",
+ "retry": "Verbindung Wiederherstellen",
+ "details": "Verbindungsdetails"
+ },
+ "status": {
+ "online": "Online",
+ "offline": "Offline",
+ "connecting": "Verbinden...",
+ "queueStatus": "{{count}} Aktion in Warteschlange",
+ "queueStatus_plural": "{{count}} Aktionen in Warteschlange"
+ },
+ "queue": {
+ "title": "Offline-Warteschlange",
+ "description": "{{count}} Aktion wird verarbeitet, wenn Sie wieder online sind",
+ "description_plural": "{{count}} Aktionen werden verarbeitet, wenn Sie wieder online sind"
+ },
+ "error": {
+ "paymentOffline": "Zahlung in Warteschlange für wenn Sie wieder online sind",
+ "generalOffline": "Aktion in Warteschlange für wenn Sie wieder online sind"
+ }
+ },
+ "about": {
+ "title": "Über Wata-Board",
+ "description": "Wata-Board ist eine dezentralisierte Versorgerzahlungsplattform, die auf der Stellar-Blockchain aufgebaut ist. Wir ermöglichen sichere, transparente und effiziente Versorgerrechnungszahlungen mit Kryptowährung.",
+ "features": {
+ "title": "Hauptfunktionen",
+ "secure": "Sichere Transaktionen",
+ "secureDescription": "Aufgebaut auf der Stellar-Blockchain mit unternehmenssicherer Sicherheit",
+ "fast": "Schnelle Zahlungen",
+ "fastDescription": "Nahezu sofortige Abwicklung mit niedrigen Transaktionsgebühren",
+ "transparent": "Transparente Aufzeichnungen",
+ "transparentDescription": "Alle Transaktionen werden auf der öffentlichen Blockchain aufgezeichnet",
+ "decentralized": "Dezentralisiert",
+ "decentralizedDescription": "Kein einzelner Ausfallpunkt oder Kontrollpunkt"
+ },
+ "howItWorks": {
+ "title": "Wie Es Funktioniert",
+ "step1": "Verbinden Sie Ihre Wallet",
+ "step2": "Zählerdetails eingeben",
+ "step3": "Zahlung bestätigen",
+ "step4": "Zahlung verarbeitet"
+ }
+ },
+ "contact": {
+ "title": "Kontaktieren Sie Uns",
+ "description": "Kontaktieren Sie unser Team für Support, Fragen oder Feedback.",
+ "form": {
+ "name": "Name",
+ "namePlaceholder": "Geben Sie Ihren Namen ein",
+ "email": "E-Mail",
+ "emailPlaceholder": "Geben Sie Ihre E-Mail ein",
+ "subject": "Betreff",
+ "subjectPlaceholder": "Betreff eingeben",
+ "message": "Nachricht",
+ "messagePlaceholder": "Geben Sie Ihre Nachricht ein",
+ "sendButton": "Nachricht Senden",
+ "sending": "Senden..."
+ },
+ "info": {
+ "email": "E-Mail",
+ "support": "Support",
+ "website": "Website"
+ }
+ },
+ "rate": {
+ "title": "Bewerten Sie Ihre Erfahrung",
+ "description": "Helfen Sie uns uns zu verbessern, indem Sie Ihre Erfahrung mit Wata-Board bewerten.",
+ "rating": {
+ "excellent": "Ausgezeichnet",
+ "good": "Gut",
+ "average": "Durchschnittlich",
+ "poor": "Schlecht",
+ "terrible": "Schrecklich"
+ },
+ "review": {
+ "title": "Bewertung Schreiben",
+ "placeholder": "Teilen Sie Ihre Erfahrung mit Wata-Board...",
+ "submit": "Bewertung Einreichen",
+ "thankYou": "Vielen Dank für Ihr Feedback!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Fehler",
+ "unknown": "Ein unbekannter Fehler ist aufgetreten",
+ "tryAgain": "Bitte versuchen Sie es erneut",
+ "contactSupport": "Kontaktieren Sie den Support, wenn das Problem weiterhin besteht"
+ },
+ "network": {
+ "title": "Netzwerkfehler",
+ "description": "Keine Verbindung zum Netzwerk möglich",
+ "checkConnection": "Bitte überprüfen Sie Ihre Internetverbindung"
+ },
+ "wallet": {
+ "title": "Wallet-Fehler",
+ "notConnected": "Wallet nicht verbunden",
+ "notInstalled": "Freighter Wallet nicht installiert",
+ "installFreighter": "Bitte installieren Sie die Freighter Wallet-Erweiterung"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Zum Hauptinhalt springen",
+ "skipToNavigation": "Zur Navigation springen",
+ "menuToggle": "Navigationsmenü umschalten",
+ "closeMenu": "Menü schließen",
+ "openMenu": "Menü öffnen",
+ "loading": "Laden...",
+ "error": "Fehler",
+ "success": "Erfolg",
+ "warning": "Warnung",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Abbrechen",
+ "confirm": "Bestätigen",
+ "save": "Speichern",
+ "delete": "Löschen",
+ "edit": "Bearbeiten",
+ "close": "Schließen",
+ "back": "Zurück",
+ "next": "Weiter",
+ "previous": "Vorherige",
+ "submit": "Einreichen",
+ "reset": "Zurücksetzen",
+ "clear": "Löschen",
+ "search": "Suchen",
+ "filter": "Filtern",
+ "sort": "Sortieren",
+ "loading": "Laden...",
+ "error": "Fehler",
+ "success": "Erfolg",
+ "retry": "Wiederholen",
+ "refresh": "Aktualisieren",
+ "copy": "Kopieren",
+ "copied": "Kopiert!",
+ "learnMore": "Mehr Erfahren",
+ "viewDetails": "Details Anzeigen",
+ "hideDetails": "Details Ausblenden"
+ },
+ "time": {
+ "seconds": "Sekunde",
+ "seconds_plural": "Sekunden",
+ "minutes": "Minute",
+ "minutes_plural": "Minuten",
+ "hours": "Stunde",
+ "hours_plural": "Stunden",
+ "days": "Tag",
+ "days_plural": "Tage",
+ "weeks": "Woche",
+ "weeks_plural": "Wochen",
+ "months": "Monat",
+ "months_plural": "Monate",
+ "years": "Jahr",
+ "years_plural": "Jahre",
+ "ago": "vor {{time}}",
+ "in": "in {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "Dieses Feld ist erforderlich",
+ "invalid": "Ungültiges Format",
+ "tooShort": "Muss mindestens {{min}} Zeichen enthalten",
+ "tooLong": "Darf nicht mehr als {{max}} Zeichen enthalten",
+ "invalidEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
+ "invalidNumber": "Bitte geben Sie eine gültige Zahl ein",
+ "minValue": "Muss mindestens {{min}} sein",
+ "maxValue": "Darf nicht mehr als {{max}} sein"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/en.json b/wata-board-frontend/src/i18n/locales/en.json
new file mode 100644
index 00000000..781820ae
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/en.json
@@ -0,0 +1,260 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Decentralized utility payments on Stellar blockchain",
+ "description": "Pay your utility bills using cryptocurrency on the Stellar network",
+ "footer": {
+ "tagline": "Modern payment solutions for the decentralized web"
+ }
+ },
+ "navigation": {
+ "home": "Pay Bill",
+ "about": "About",
+ "contact": "Contact",
+ "rate": "Rate Us",
+ "language": "Language"
+ },
+ "payment": {
+ "form": {
+ "title": "Payment Information",
+ "meterNumber": "Meter number",
+ "meterPlaceholder": "e.g. METER-123",
+ "meterDescription": "Enter your utility meter number as shown on your bill",
+ "amount": "Amount",
+ "amountPlaceholder": "Whole number",
+ "amountDescription": "Enter the payment amount in whole XLM",
+ "payButton": "Pay bill",
+ "processing": "Processing...",
+ "rateLimited": "Rate Limited",
+ "requiresExtension": "Requires Freighter extension. 5 transactions per minute limit."
+ },
+ "status": {
+ "title": "Status",
+ "ready": "Ready.",
+ "installWallet": "Please install Freighter Wallet extension!",
+ "enterMeter": "Please enter a meter number.",
+ "enterValidAmount": "Please enter a valid amount greater than 0.",
+ "wholeNumber": "Amount must be a whole number.",
+ "amountTooLarge": "Amount is too large.",
+ "insufficientBalance": "Insufficient balance. Please add more XLM to your wallet.",
+ "estimatingFees": "Estimating transaction fees... Please wait.",
+ "paymentSuccess": "Payment successful! Transaction ID: {{id}}...",
+ "paymentFailed": "Payment failed: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Transaction Fee Estimation",
+ "calculating": "(Calculating...)",
+ "calculatingFees": "Calculating estimated fees...",
+ "paymentAmount": "Payment Amount",
+ "estimatedNetworkFee": "Estimated Network Fee",
+ "totalCost": "Total Cost",
+ "unableToEstimate": "Unable to estimate fees at this time"
+ },
+ "rateLimit": {
+ "title": "Rate Limit Status",
+ "requestsAvailable": "{{count}}/5 requests available",
+ "rateLimited": "Rate limited. Reset in {{time}}",
+ "queue": "Queue: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Wallet Balance",
+ "available": "Available Balance",
+ "total": "Total Balance",
+ "loading": "Loading balance...",
+ "error": "Failed to load balance",
+ "reconnect": "Please reconnect your wallet",
+ "insufficient": "Insufficient balance for this transaction",
+ "lowBalance": "Low balance warning"
+ },
+ "connection": {
+ "connected": "Connected",
+ "disconnected": "Disconnected",
+ "connecting": "Connecting...",
+ "connectWallet": "Connect Wallet",
+ "switchWallet": "Switch Wallet"
+ }
+ },
+ "network": {
+ "mainnet": "MAINNET",
+ "testnet": "TESTNET",
+ "switchNetwork": "Switch Network",
+ "currentNetwork": "Current Network"
+ },
+ "offline": {
+ "banner": {
+ "title": "You're offline",
+ "description": "Some features may be unavailable. Actions will be queued and processed when you're back online.",
+ "retry": "Retry Connection",
+ "details": "Connection Details"
+ },
+ "status": {
+ "online": "Online",
+ "offline": "Offline",
+ "connecting": "Connecting...",
+ "queueStatus": "{{count}} queued action",
+ "queueStatus_plural": "{{count}} queued actions"
+ },
+ "queue": {
+ "title": "Offline Queue",
+ "description": "{{count}} action will be processed when you're back online",
+ "description_plural": "{{count}} actions will be processed when you're back online"
+ },
+ "error": {
+ "paymentOffline": "Payment queued for when you're back online",
+ "generalOffline": "Action queued for when you're back online"
+ }
+ },
+ "about": {
+ "title": "About Wata-Board",
+ "description": "Wata-Board is a decentralized utility payment platform built on the Stellar blockchain. We enable secure, transparent, and efficient utility bill payments using cryptocurrency.",
+ "features": {
+ "title": "Key Features",
+ "secure": "Secure Transactions",
+ "secureDescription": "Built on Stellar blockchain with enterprise-grade security",
+ "fast": "Fast Payments",
+ "fastDescription": "Near-instant settlement with low transaction fees",
+ "transparent": "Transparent Records",
+ "transparentDescription": "All transactions are recorded on the public blockchain",
+ "decentralized": "Decentralized",
+ "decentralizedDescription": "No single point of failure or control"
+ },
+ "howItWorks": {
+ "title": "How It Works",
+ "step1": "Connect your wallet",
+ "step2": "Enter meter details",
+ "step3": "Confirm payment",
+ "step4": "Payment processed"
+ }
+ },
+ "contact": {
+ "title": "Contact Us",
+ "description": "Get in touch with our team for support, questions, or feedback.",
+ "form": {
+ "name": "Name",
+ "namePlaceholder": "Enter your name",
+ "email": "Email",
+ "emailPlaceholder": "Enter your email",
+ "subject": "Subject",
+ "subjectPlaceholder": "Enter subject",
+ "message": "Message",
+ "messagePlaceholder": "Enter your message",
+ "sendButton": "Send Message",
+ "sending": "Sending..."
+ },
+ "info": {
+ "email": "Email",
+ "support": "Support",
+ "website": "Website"
+ }
+ },
+ "rate": {
+ "title": "Rate Your Experience",
+ "description": "Help us improve by rating your experience with Wata-Board.",
+ "rating": {
+ "excellent": "Excellent",
+ "good": "Good",
+ "average": "Average",
+ "poor": "Poor",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Write a Review",
+ "placeholder": "Share your experience with Wata-Board...",
+ "submit": "Submit Review",
+ "thankYou": "Thank you for your feedback!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Error",
+ "unknown": "An unknown error occurred",
+ "tryAgain": "Please try again",
+ "contactSupport": "Contact support if the problem persists"
+ },
+ "network": {
+ "title": "Network Error",
+ "description": "Unable to connect to the network",
+ "checkConnection": "Please check your internet connection"
+ },
+ "wallet": {
+ "title": "Wallet Error",
+ "notConnected": "Wallet not connected",
+ "notInstalled": "Freighter wallet not installed",
+ "installFreighter": "Please install Freighter wallet extension"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Skip to main content",
+ "skipToNavigation": "Skip to navigation",
+ "menuToggle": "Toggle navigation menu",
+ "closeMenu": "Close menu",
+ "openMenu": "Open menu",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "save": "Save",
+ "delete": "Delete",
+ "edit": "Edit",
+ "close": "Close",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "submit": "Submit",
+ "reset": "Reset",
+ "clear": "Clear",
+ "search": "Search",
+ "filter": "Filter",
+ "sort": "Sort",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "retry": "Retry",
+ "refresh": "Refresh",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "learnMore": "Learn More",
+ "viewDetails": "View Details",
+ "hideDetails": "Hide Details"
+ },
+ "time": {
+ "seconds": "second",
+ "seconds_plural": "seconds",
+ "minutes": "minute",
+ "minutes_plural": "minutes",
+ "hours": "hour",
+ "hours_plural": "hours",
+ "days": "day",
+ "days_plural": "days",
+ "weeks": "week",
+ "weeks_plural": "weeks",
+ "months": "month",
+ "months_plural": "months",
+ "years": "year",
+ "years_plural": "years",
+ "ago": "{{time}} ago",
+ "in": "in {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "This field is required",
+ "invalid": "Invalid format",
+ "tooShort": "Must be at least {{min}} characters",
+ "tooLong": "Must be no more than {{max}} characters",
+ "invalidEmail": "Please enter a valid email address",
+ "invalidNumber": "Please enter a valid number",
+ "minValue": "Must be at least {{min}}",
+ "maxValue": "Must be no more than {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/es.json b/wata-board-frontend/src/i18n/locales/es.json
new file mode 100644
index 00000000..85016115
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/es.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Pagos de servicios públicos descentralizados en la blockchain Stellar",
+ "description": "Paga tus facturas de servicios públicos usando criptomoneda en la red Stellar"
+ },
+ "navigation": {
+ "home": "Pagar Factura",
+ "about": "Acerca de",
+ "contact": "Contacto",
+ "rate": "Califícanos",
+ "language": "Idioma"
+ },
+ "payment": {
+ "form": {
+ "title": "Información de Pago",
+ "meterNumber": "Número de medidor",
+ "meterPlaceholder": "ej. MEDIDOR-123",
+ "meterDescription": "Ingresa tu número de medidor de servicios como aparece en tu factura",
+ "amount": "Cantidad",
+ "amountPlaceholder": "Número entero",
+ "amountDescription": "Ingresa el monto del pago en XLM entero",
+ "payButton": "Pagar factura",
+ "processing": "Procesando...",
+ "rateLimited": "Límite Alcanzado",
+ "requiresExtension": "Requiere la extensión Freighter. Límite de 5 transacciones por minuto."
+ },
+ "status": {
+ "title": "Estado",
+ "ready": "Listo.",
+ "installWallet": "¡Por favor instala la extensión Freighter Wallet!",
+ "enterMeter": "Por favor ingresa un número de medidor.",
+ "enterValidAmount": "Por favor ingresa una cantidad válida mayor a 0.",
+ "wholeNumber": "La cantidad debe ser un número entero.",
+ "amountTooLarge": "La cantidad es demasiado grande.",
+ "insufficientBalance": "Saldo insuficiente. Por favor agrega más XLM a tu billetera.",
+ "estimatingFees": "Estimando tarifas de transacción... Por favor espera.",
+ "paymentSuccess": "¡Pago exitoso! ID de transacción: {{id}}...",
+ "paymentFailed": "Pago fallido: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Estimación de Tarifa de Transacción",
+ "calculating": "(Calculando...)",
+ "calculatingFees": "Calculando tarifas estimadas...",
+ "paymentAmount": "Monto del Pago",
+ "estimatedNetworkFee": "Tarifa de Red Estimada",
+ "totalCost": "Costo Total",
+ "unableToEstimate": "No se pueden estimar las tarifas en este momento"
+ },
+ "rateLimit": {
+ "title": "Estado del Límite de Tasa",
+ "requestsAvailable": "{{count}}/5 solicitudes disponibles",
+ "rateLimited": "Límite alcanzado. Reinicio en {{time}}",
+ "queue": "Cola: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Saldo de Billetera",
+ "available": "Saldo Disponible",
+ "total": "Saldo Total",
+ "loading": "Cargando saldo...",
+ "error": "Error al cargar saldo",
+ "reconnect": "Por favor reconecta tu billetera",
+ "insufficient": "Saldo insuficiente para esta transacción",
+ "lowBalance": "Advertencia de saldo bajo"
+ },
+ "connection": {
+ "connected": "Conectado",
+ "disconnected": "Desconectado",
+ "connecting": "Conectando...",
+ "connectWallet": "Conectar Billetera",
+ "switchWallet": "Cambiar Billetera"
+ }
+ },
+ "network": {
+ "mainnet": "RED PRINCIPAL",
+ "testnet": "RED DE PRUEBA",
+ "switchNetwork": "Cambiar Red",
+ "currentNetwork": "Red Actual"
+ },
+ "offline": {
+ "banner": {
+ "title": "Estás desconectado",
+ "description": "Algunas funciones pueden no estar disponibles. Las acciones se pondrán en cola y se procesarán cuando vuelvas a estar en línea.",
+ "retry": "Reintentar Conexión",
+ "details": "Detalles de Conexión"
+ },
+ "status": {
+ "online": "En línea",
+ "offline": "Desconectado",
+ "connecting": "Conectando...",
+ "queueStatus": "{{count}} acción en cola",
+ "queueStatus_plural": "{{count}} acciones en cola"
+ },
+ "queue": {
+ "title": "Cola Desconectada",
+ "description": "{{count}} acción se procesará cuando vuelvas a estar en línea",
+ "description_plural": "{{count}} acciones se procesarán cuando vuelvas a estar en línea"
+ },
+ "error": {
+ "paymentOffline": "Pago en cola para cuando vuelvas a estar en línea",
+ "generalOffline": "Acción en cola para cuando vuelvas a estar en línea"
+ }
+ },
+ "about": {
+ "title": "Acerca de Wata-Board",
+ "description": "Wata-Board es una plataforma de pago de servicios públicos descentralizada construida en la blockchain Stellar. Permitimos pagos de facturas de servicios seguros, transparentes y eficientes usando criptomoneda.",
+ "features": {
+ "title": "Características Clave",
+ "secure": "Transacciones Seguras",
+ "secureDescription": "Construido en la blockchain Stellar con seguridad de nivel empresarial",
+ "fast": "Pagos Rápidos",
+ "fastDescription": "Liquidación casi instantánea con tarifas de transacción bajas",
+ "transparent": "Registros Transparentes",
+ "transparentDescription": "Todas las transacciones se registran en la blockchain pública",
+ "decentralized": "Descentralizado",
+ "decentralizedDescription": "Sin punto único de falla o control"
+ },
+ "howItWorks": {
+ "title": "Cómo Funciona",
+ "step1": "Conecta tu billetera",
+ "step2": "Ingresa detalles del medidor",
+ "step3": "Confirma el pago",
+ "step4": "Pago procesado"
+ }
+ },
+ "contact": {
+ "title": "Contáctanos",
+ "description": "Comunícate con nuestro equipo para soporte, preguntas o retroalimentación.",
+ "form": {
+ "name": "Nombre",
+ "namePlaceholder": "Ingresa tu nombre",
+ "email": "Correo electrónico",
+ "emailPlaceholder": "Ingresa tu correo",
+ "subject": "Asunto",
+ "subjectPlaceholder": "Ingresa el asunto",
+ "message": "Mensaje",
+ "messagePlaceholder": "Ingresa tu mensaje",
+ "sendButton": "Enviar Mensaje",
+ "sending": "Enviando..."
+ },
+ "info": {
+ "email": "Correo electrónico",
+ "support": "Soporte",
+ "website": "Sitio web"
+ }
+ },
+ "rate": {
+ "title": "Califica Tu Experiencia",
+ "description": "Ayúdanos a mejorar calificando tu experiencia con Wata-Board.",
+ "rating": {
+ "excellent": "Excelente",
+ "good": "Bueno",
+ "average": "Promedio",
+ "poor": "Pobre",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Escribe una Reseña",
+ "placeholder": "Comparte tu experiencia con Wata-Board...",
+ "submit": "Enviar Reseña",
+ "thankYou": "¡Gracias por tu retroalimentación!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Error",
+ "unknown": "Ocurrió un error desconocido",
+ "tryAgain": "Por favor intenta de nuevo",
+ "contactSupport": "Contacta soporte si el problema persiste"
+ },
+ "network": {
+ "title": "Error de Red",
+ "description": "No se puede conectar a la red",
+ "checkConnection": "Por favor verifica tu conexión a internet"
+ },
+ "wallet": {
+ "title": "Error de Billetera",
+ "notConnected": "Billetera no conectada",
+ "notInstalled": "Billetera Freighter no instalada",
+ "installFreighter": "Por favor instala la extensión de billetera Freighter"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Saltar al contenido principal",
+ "skipToNavigation": "Saltar a la navegación",
+ "menuToggle": "Alternar menú de navegación",
+ "closeMenu": "Cerrar menú",
+ "openMenu": "Abrir menú",
+ "loading": "Cargando...",
+ "error": "Error",
+ "success": "Éxito",
+ "warning": "Advertencia",
+ "info": "Información"
+ },
+ "common": {
+ "cancel": "Cancelar",
+ "confirm": "Confirmar",
+ "save": "Guardar",
+ "delete": "Eliminar",
+ "edit": "Editar",
+ "close": "Cerrar",
+ "back": "Atrás",
+ "next": "Siguiente",
+ "previous": "Anterior",
+ "submit": "Enviar",
+ "reset": "Restablecer",
+ "clear": "Limpiar",
+ "search": "Buscar",
+ "filter": "Filtrar",
+ "sort": "Ordenar",
+ "loading": "Cargando...",
+ "error": "Error",
+ "success": "Éxito",
+ "retry": "Reintentar",
+ "refresh": "Actualizar",
+ "copy": "Copiar",
+ "copied": "¡Copiado!",
+ "learnMore": "Aprende Más",
+ "viewDetails": "Ver Detalles",
+ "hideDetails": "Ocultar Detalles"
+ },
+ "time": {
+ "seconds": "segundo",
+ "seconds_plural": "segundos",
+ "minutes": "minuto",
+ "minutes_plural": "minutos",
+ "hours": "hora",
+ "hours_plural": "horas",
+ "days": "día",
+ "days_plural": "días",
+ "weeks": "semana",
+ "weeks_plural": "semanas",
+ "months": "mes",
+ "months_plural": "meses",
+ "years": "año",
+ "years_plural": "años",
+ "ago": "hace {{time}}",
+ "in": "en {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "Este campo es requerido",
+ "invalid": "Formato inválido",
+ "tooShort": "Debe tener al menos {{min}} caracteres",
+ "tooLong": "No debe tener más de {{max}} caracteres",
+ "invalidEmail": "Por favor ingresa una dirección de correo válida",
+ "invalidNumber": "Por favor ingresa un número válido",
+ "minValue": "Debe ser al menos {{min}}",
+ "maxValue": "No debe ser más de {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/fr.json b/wata-board-frontend/src/i18n/locales/fr.json
new file mode 100644
index 00000000..96dc8919
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/fr.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Paiements de services publics décentralisés sur la blockchain Stellar",
+ "description": "Payez vos factures de services publics en utilisant la cryptomonnaie sur le réseau Stellar"
+ },
+ "navigation": {
+ "home": "Payer Facture",
+ "about": "À Propos",
+ "contact": "Contact",
+ "rate": "Nous Noter",
+ "language": "Langue"
+ },
+ "payment": {
+ "form": {
+ "title": "Informations de Paiement",
+ "meterNumber": "Numéro de compteur",
+ "meterPlaceholder": "ex. COMPTEUR-123",
+ "meterDescription": "Entrez votre numéro de compteur de services comme indiqué sur votre facture",
+ "amount": "Montant",
+ "amountPlaceholder": "Nombre entier",
+ "amountDescription": "Entrez le montant du paiement en XLM entier",
+ "payButton": "Payer facture",
+ "processing": "Traitement...",
+ "rateLimited": "Limite Atteinte",
+ "requiresExtension": "Nécessite l'extension Freighter. Limite de 5 transactions par minute."
+ },
+ "status": {
+ "title": "Statut",
+ "ready": "Prêt.",
+ "installWallet": "Veuillez installer l'extension Freighter Wallet !",
+ "enterMeter": "Veuillez entrer un numéro de compteur.",
+ "enterValidAmount": "Veuillez entrer un montant valide supérieur à 0.",
+ "wholeNumber": "Le montant doit être un nombre entier.",
+ "amountTooLarge": "Le montant est trop élevé.",
+ "insufficientBalance": "Solde insuffisant. Veuillez ajouter plus de XLM à votre portefeuille.",
+ "estimatingFees": "Estimation des frais de transaction... Veuillez patienter.",
+ "paymentSuccess": "Paiement réussi ! ID de transaction : {{id}}...",
+ "paymentFailed": "Paiement échoué : {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Estimation des Frais de Transaction",
+ "calculating": "(Calcul...)",
+ "calculatingFees": "Calcul des frais estimés...",
+ "paymentAmount": "Montant du Paiement",
+ "estimatedNetworkFee": "Frais de Réseau Estimés",
+ "totalCost": "Coût Total",
+ "unableToEstimate": "Impossible d'estimer les frais pour le moment"
+ },
+ "rateLimit": {
+ "title": "Statut de Limite de Taux",
+ "requestsAvailable": "{{count}}/5 requêtes disponibles",
+ "rateLimited": "Limite atteinte. Réinitialisation dans {{time}}",
+ "queue": "File d'attente : {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Solde du Portefeuille",
+ "available": "Solde Disponible",
+ "total": "Solde Total",
+ "loading": "Chargement du solde...",
+ "error": "Échec du chargement du solde",
+ "reconnect": "Veuillez reconnecter votre portefeuille",
+ "insufficient": "Solde insuffisant pour cette transaction",
+ "lowBalance": "Avertissement de solde faible"
+ },
+ "connection": {
+ "connected": "Connecté",
+ "disconnected": "Déconnecté",
+ "connecting": "Connexion...",
+ "connectWallet": "Connecter Portefeuille",
+ "switchWallet": "Changer Portefeuille"
+ }
+ },
+ "network": {
+ "mainnet": "RÉSEAU PRINCIPAL",
+ "testnet": "RÉSEAU DE TEST",
+ "switchNetwork": "Changer Réseau",
+ "currentNetwork": "Réseau Actuel"
+ },
+ "offline": {
+ "banner": {
+ "title": "Vous êtes hors ligne",
+ "description": "Certaines fonctionnalités peuvent ne pas être disponibles. Les actions seront mises en file d'attente et traitées lorsque vous serez de nouveau en ligne.",
+ "retry": "Réessayer la Connexion",
+ "details": "Détails de Connexion"
+ },
+ "status": {
+ "online": "En ligne",
+ "offline": "Hors ligne",
+ "connecting": "Connexion...",
+ "queueStatus": "{{count}} action en file d'attente",
+ "queueStatus_plural": "{{count}} actions en file d'attente"
+ },
+ "queue": {
+ "title": "File d'Attente Hors Ligne",
+ "description": "{{count}} action sera traitée lorsque vous serez de nouveau en ligne",
+ "description_plural": "{{count}} actions seront traitées lorsque vous serez de nouveau en ligne"
+ },
+ "error": {
+ "paymentOffline": "Paiement en file d'attente pour lorsque vous serez de nouveau en ligne",
+ "generalOffline": "Action en file d'attente pour lorsque vous serez de nouveau en ligne"
+ }
+ },
+ "about": {
+ "title": "À Propos de Wata-Board",
+ "description": "Wata-Board est une plateforme de paiement de services publics décentralisée construite sur la blockchain Stellar. Nous permettons des paiements de factures de services sécurisés, transparents et efficaces en utilisant la cryptomonnaie.",
+ "features": {
+ "title": "Caractéristiques Clés",
+ "secure": "Transactions Sécurisées",
+ "secureDescription": "Construit sur la blockchain Stellar avec une sécurité de niveau entreprise",
+ "fast": "Paiements Rapides",
+ "fastDescription": "Règlement quasi instantané avec des frais de transaction bas",
+ "transparent": "Registres Transparents",
+ "transparentDescription": "Toutes les transactions sont enregistrées sur la blockchain publique",
+ "decentralized": "Décentralisé",
+ "decentralizedDescription": "Aucun point unique de défaillance ou de contrôle"
+ },
+ "howItWorks": {
+ "title": "Comment Ça Marche",
+ "step1": "Connectez votre portefeuille",
+ "step2": "Entrez les détails du compteur",
+ "step3": "Confirmez le paiement",
+ "step4": "Paiement traité"
+ }
+ },
+ "contact": {
+ "title": "Contactez-nous",
+ "description": "Contactez notre équipe pour du support, des questions ou des commentaires.",
+ "form": {
+ "name": "Nom",
+ "namePlaceholder": "Entrez votre nom",
+ "email": "Email",
+ "emailPlaceholder": "Entrez votre email",
+ "subject": "Sujet",
+ "subjectPlaceholder": "Entrez le sujet",
+ "message": "Message",
+ "messagePlaceholder": "Entrez votre message",
+ "sendButton": "Envoyer Message",
+ "sending": "Envoi..."
+ },
+ "info": {
+ "email": "Email",
+ "support": "Support",
+ "website": "Site web"
+ }
+ },
+ "rate": {
+ "title": "Notez Votre Expérience",
+ "description": "Aidez-nous à nous améliorer en notant votre expérience avec Wata-Board.",
+ "rating": {
+ "excellent": "Excellent",
+ "good": "Bon",
+ "average": "Moyen",
+ "poor": "Mauvais",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Écrire un Avis",
+ "placeholder": "Partagez votre expérience avec Wata-Board...",
+ "submit": "Soumettre Avis",
+ "thankYou": "Merci pour vos commentaires !"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Erreur",
+ "unknown": "Une erreur inconnue s'est produite",
+ "tryAgain": "Veuillez réessayer",
+ "contactSupport": "Contactez le support si le problème persiste"
+ },
+ "network": {
+ "title": "Erreur Réseau",
+ "description": "Impossible de se connecter au réseau",
+ "checkConnection": "Veuillez vérifier votre connexion internet"
+ },
+ "wallet": {
+ "title": "Erreur de Portefeuille",
+ "notConnected": "Portefeuille non connecté",
+ "notInstalled": "Portefeuille Freighter non installé",
+ "installFreighter": "Veuillez installer l'extension de portefeuille Freighter"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Aller au contenu principal",
+ "skipToNavigation": "Aller à la navigation",
+ "menuToggle": "Basculer le menu de navigation",
+ "closeMenu": "Fermer le menu",
+ "openMenu": "Ouvrir le menu",
+ "loading": "Chargement...",
+ "error": "Erreur",
+ "success": "Succès",
+ "warning": "Avertissement",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Annuler",
+ "confirm": "Confirmer",
+ "save": "Sauvegarder",
+ "delete": "Supprimer",
+ "edit": "Modifier",
+ "close": "Fermer",
+ "back": "Retour",
+ "next": "Suivant",
+ "previous": "Précédent",
+ "submit": "Soumettre",
+ "reset": "Réinitialiser",
+ "clear": "Effacer",
+ "search": "Rechercher",
+ "filter": "Filtrer",
+ "sort": "Trier",
+ "loading": "Chargement...",
+ "error": "Erreur",
+ "success": "Succès",
+ "retry": "Réessayer",
+ "refresh": "Actualiser",
+ "copy": "Copier",
+ "copied": "Copié !",
+ "learnMore": "En Savoir Plus",
+ "viewDetails": "Voir Détails",
+ "hideDetails": "Masquer Détails"
+ },
+ "time": {
+ "seconds": "seconde",
+ "seconds_plural": "secondes",
+ "minutes": "minute",
+ "minutes_plural": "minutes",
+ "hours": "heure",
+ "hours_plural": "heures",
+ "days": "jour",
+ "days_plural": "jours",
+ "weeks": "semaine",
+ "weeks_plural": "semaines",
+ "months": "mois",
+ "months_plural": "mois",
+ "years": "an",
+ "years_plural": "ans",
+ "ago": "il y a {{time}}",
+ "in": "dans {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "Ce champ est requis",
+ "invalid": "Format invalide",
+ "tooShort": "Doit contenir au moins {{min}} caractères",
+ "tooLong": "Ne doit pas dépasser {{max}} caractères",
+ "invalidEmail": "Veuillez entrer une adresse email valide",
+ "invalidNumber": "Veuillez entrer un nombre valide",
+ "minValue": "Doit être au moins {{min}}",
+ "maxValue": "Ne doit pas dépasser {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/hi.json b/wata-board-frontend/src/i18n/locales/hi.json
new file mode 100644
index 00000000..4649c7e2
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/hi.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Decentralized utility payments on Stellar blockchain",
+ "description": "Pay your utility bills using cryptocurrency on the Stellar network"
+ },
+ "navigation": {
+ "home": "Pay Bill",
+ "about": "About",
+ "contact": "Contact",
+ "rate": "Rate Us",
+ "language": "Language"
+ },
+ "payment": {
+ "form": {
+ "title": "Payment Information",
+ "meterNumber": "Meter number",
+ "meterPlaceholder": "e.g. METER-123",
+ "meterDescription": "Enter your utility meter number as shown on your bill",
+ "amount": "Amount",
+ "amountPlaceholder": "Whole number",
+ "amountDescription": "Enter the payment amount in whole XLM",
+ "payButton": "Pay bill",
+ "processing": "Processing...",
+ "rateLimited": "Rate Limited",
+ "requiresExtension": "Requires Freighter extension. 5 transactions per minute limit."
+ },
+ "status": {
+ "title": "Status",
+ "ready": "Ready.",
+ "installWallet": "Please install Freighter Wallet extension!",
+ "enterMeter": "Please enter a meter number.",
+ "enterValidAmount": "Please enter a valid amount greater than 0.",
+ "wholeNumber": "Amount must be a whole number.",
+ "amountTooLarge": "Amount is too large.",
+ "insufficientBalance": "Insufficient balance. Please add more XLM to your wallet.",
+ "estimatingFees": "Estimating transaction fees... Please wait.",
+ "paymentSuccess": "Payment successful! Transaction ID: {{id}}...",
+ "paymentFailed": "Payment failed: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Transaction Fee Estimation",
+ "calculating": "(Calculating...)",
+ "calculatingFees": "Calculating estimated fees...",
+ "paymentAmount": "Payment Amount",
+ "estimatedNetworkFee": "Estimated Network Fee",
+ "totalCost": "Total Cost",
+ "unableToEstimate": "Unable to estimate fees at this time"
+ },
+ "rateLimit": {
+ "title": "Rate Limit Status",
+ "requestsAvailable": "{{count}}/5 requests available",
+ "rateLimited": "Rate limited. Reset in {{time}}",
+ "queue": "Queue: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Wallet Balance",
+ "available": "Available Balance",
+ "total": "Total Balance",
+ "loading": "Loading balance...",
+ "error": "Failed to load balance",
+ "reconnect": "Please reconnect your wallet",
+ "insufficient": "Insufficient balance for this transaction",
+ "lowBalance": "Low balance warning"
+ },
+ "connection": {
+ "connected": "Connected",
+ "disconnected": "Disconnected",
+ "connecting": "Connecting...",
+ "connectWallet": "Connect Wallet",
+ "switchWallet": "Switch Wallet"
+ }
+ },
+ "network": {
+ "mainnet": "MAINNET",
+ "testnet": "TESTNET",
+ "switchNetwork": "Switch Network",
+ "currentNetwork": "Current Network"
+ },
+ "offline": {
+ "banner": {
+ "title": "You're offline",
+ "description": "Some features may be unavailable. Actions will be queued and processed when you're back online.",
+ "retry": "Retry Connection",
+ "details": "Connection Details"
+ },
+ "status": {
+ "online": "Online",
+ "offline": "Offline",
+ "connecting": "Connecting...",
+ "queueStatus": "{{count}} queued action",
+ "queueStatus_plural": "{{count}} queued actions"
+ },
+ "queue": {
+ "title": "Offline Queue",
+ "description": "{{count}} action will be processed when you're back online",
+ "description_plural": "{{count}} actions will be processed when you're back online"
+ },
+ "error": {
+ "paymentOffline": "Payment queued for when you're back online",
+ "generalOffline": "Action queued for when you're back online"
+ }
+ },
+ "about": {
+ "title": "About Wata-Board",
+ "description": "Wata-Board is a decentralized utility payment platform built on the Stellar blockchain. We enable secure, transparent, and efficient utility bill payments using cryptocurrency.",
+ "features": {
+ "title": "Key Features",
+ "secure": "Secure Transactions",
+ "secureDescription": "Built on Stellar blockchain with enterprise-grade security",
+ "fast": "Fast Payments",
+ "fastDescription": "Near-instant settlement with low transaction fees",
+ "transparent": "Transparent Records",
+ "transparentDescription": "All transactions are recorded on the public blockchain",
+ "decentralized": "Decentralized",
+ "decentralizedDescription": "No single point of failure or control"
+ },
+ "howItWorks": {
+ "title": "How It Works",
+ "step1": "Connect your wallet",
+ "step2": "Enter meter details",
+ "step3": "Confirm payment",
+ "step4": "Payment processed"
+ }
+ },
+ "contact": {
+ "title": "Contact Us",
+ "description": "Get in touch with our team for support, questions, or feedback.",
+ "form": {
+ "name": "Name",
+ "namePlaceholder": "Enter your name",
+ "email": "Email",
+ "emailPlaceholder": "Enter your email",
+ "subject": "Subject",
+ "subjectPlaceholder": "Enter subject",
+ "message": "Message",
+ "messagePlaceholder": "Enter your message",
+ "sendButton": "Send Message",
+ "sending": "Sending..."
+ },
+ "info": {
+ "email": "Email",
+ "support": "Support",
+ "website": "Website"
+ }
+ },
+ "rate": {
+ "title": "Rate Your Experience",
+ "description": "Help us improve by rating your experience with Wata-Board.",
+ "rating": {
+ "excellent": "Excellent",
+ "good": "Good",
+ "average": "Average",
+ "poor": "Poor",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Write a Review",
+ "placeholder": "Share your experience with Wata-Board...",
+ "submit": "Submit Review",
+ "thankYou": "Thank you for your feedback!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Error",
+ "unknown": "An unknown error occurred",
+ "tryAgain": "Please try again",
+ "contactSupport": "Contact support if the problem persists"
+ },
+ "network": {
+ "title": "Network Error",
+ "description": "Unable to connect to the network",
+ "checkConnection": "Please check your internet connection"
+ },
+ "wallet": {
+ "title": "Wallet Error",
+ "notConnected": "Wallet not connected",
+ "notInstalled": "Freighter wallet not installed",
+ "installFreighter": "Please install Freighter wallet extension"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Skip to main content",
+ "skipToNavigation": "Skip to navigation",
+ "menuToggle": "Toggle navigation menu",
+ "closeMenu": "Close menu",
+ "openMenu": "Open menu",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "save": "Save",
+ "delete": "Delete",
+ "edit": "Edit",
+ "close": "Close",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "submit": "Submit",
+ "reset": "Reset",
+ "clear": "Clear",
+ "search": "Search",
+ "filter": "Filter",
+ "sort": "Sort",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "retry": "Retry",
+ "refresh": "Refresh",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "learnMore": "Learn More",
+ "viewDetails": "View Details",
+ "hideDetails": "Hide Details"
+ },
+ "time": {
+ "seconds": "second",
+ "seconds_plural": "seconds",
+ "minutes": "minute",
+ "minutes_plural": "minutes",
+ "hours": "hour",
+ "hours_plural": "hours",
+ "days": "day",
+ "days_plural": "days",
+ "weeks": "week",
+ "weeks_plural": "weeks",
+ "months": "month",
+ "months_plural": "months",
+ "years": "year",
+ "years_plural": "years",
+ "ago": "{{time}} ago",
+ "in": "in {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "This field is required",
+ "invalid": "Invalid format",
+ "tooShort": "Must be at least {{min}} characters",
+ "tooLong": "Must be no more than {{max}} characters",
+ "invalidEmail": "Please enter a valid email address",
+ "invalidNumber": "Please enter a valid number",
+ "minValue": "Must be at least {{min}}",
+ "maxValue": "Must be no more than {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/ja.json b/wata-board-frontend/src/i18n/locales/ja.json
new file mode 100644
index 00000000..4649c7e2
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/ja.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Decentralized utility payments on Stellar blockchain",
+ "description": "Pay your utility bills using cryptocurrency on the Stellar network"
+ },
+ "navigation": {
+ "home": "Pay Bill",
+ "about": "About",
+ "contact": "Contact",
+ "rate": "Rate Us",
+ "language": "Language"
+ },
+ "payment": {
+ "form": {
+ "title": "Payment Information",
+ "meterNumber": "Meter number",
+ "meterPlaceholder": "e.g. METER-123",
+ "meterDescription": "Enter your utility meter number as shown on your bill",
+ "amount": "Amount",
+ "amountPlaceholder": "Whole number",
+ "amountDescription": "Enter the payment amount in whole XLM",
+ "payButton": "Pay bill",
+ "processing": "Processing...",
+ "rateLimited": "Rate Limited",
+ "requiresExtension": "Requires Freighter extension. 5 transactions per minute limit."
+ },
+ "status": {
+ "title": "Status",
+ "ready": "Ready.",
+ "installWallet": "Please install Freighter Wallet extension!",
+ "enterMeter": "Please enter a meter number.",
+ "enterValidAmount": "Please enter a valid amount greater than 0.",
+ "wholeNumber": "Amount must be a whole number.",
+ "amountTooLarge": "Amount is too large.",
+ "insufficientBalance": "Insufficient balance. Please add more XLM to your wallet.",
+ "estimatingFees": "Estimating transaction fees... Please wait.",
+ "paymentSuccess": "Payment successful! Transaction ID: {{id}}...",
+ "paymentFailed": "Payment failed: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Transaction Fee Estimation",
+ "calculating": "(Calculating...)",
+ "calculatingFees": "Calculating estimated fees...",
+ "paymentAmount": "Payment Amount",
+ "estimatedNetworkFee": "Estimated Network Fee",
+ "totalCost": "Total Cost",
+ "unableToEstimate": "Unable to estimate fees at this time"
+ },
+ "rateLimit": {
+ "title": "Rate Limit Status",
+ "requestsAvailable": "{{count}}/5 requests available",
+ "rateLimited": "Rate limited. Reset in {{time}}",
+ "queue": "Queue: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Wallet Balance",
+ "available": "Available Balance",
+ "total": "Total Balance",
+ "loading": "Loading balance...",
+ "error": "Failed to load balance",
+ "reconnect": "Please reconnect your wallet",
+ "insufficient": "Insufficient balance for this transaction",
+ "lowBalance": "Low balance warning"
+ },
+ "connection": {
+ "connected": "Connected",
+ "disconnected": "Disconnected",
+ "connecting": "Connecting...",
+ "connectWallet": "Connect Wallet",
+ "switchWallet": "Switch Wallet"
+ }
+ },
+ "network": {
+ "mainnet": "MAINNET",
+ "testnet": "TESTNET",
+ "switchNetwork": "Switch Network",
+ "currentNetwork": "Current Network"
+ },
+ "offline": {
+ "banner": {
+ "title": "You're offline",
+ "description": "Some features may be unavailable. Actions will be queued and processed when you're back online.",
+ "retry": "Retry Connection",
+ "details": "Connection Details"
+ },
+ "status": {
+ "online": "Online",
+ "offline": "Offline",
+ "connecting": "Connecting...",
+ "queueStatus": "{{count}} queued action",
+ "queueStatus_plural": "{{count}} queued actions"
+ },
+ "queue": {
+ "title": "Offline Queue",
+ "description": "{{count}} action will be processed when you're back online",
+ "description_plural": "{{count}} actions will be processed when you're back online"
+ },
+ "error": {
+ "paymentOffline": "Payment queued for when you're back online",
+ "generalOffline": "Action queued for when you're back online"
+ }
+ },
+ "about": {
+ "title": "About Wata-Board",
+ "description": "Wata-Board is a decentralized utility payment platform built on the Stellar blockchain. We enable secure, transparent, and efficient utility bill payments using cryptocurrency.",
+ "features": {
+ "title": "Key Features",
+ "secure": "Secure Transactions",
+ "secureDescription": "Built on Stellar blockchain with enterprise-grade security",
+ "fast": "Fast Payments",
+ "fastDescription": "Near-instant settlement with low transaction fees",
+ "transparent": "Transparent Records",
+ "transparentDescription": "All transactions are recorded on the public blockchain",
+ "decentralized": "Decentralized",
+ "decentralizedDescription": "No single point of failure or control"
+ },
+ "howItWorks": {
+ "title": "How It Works",
+ "step1": "Connect your wallet",
+ "step2": "Enter meter details",
+ "step3": "Confirm payment",
+ "step4": "Payment processed"
+ }
+ },
+ "contact": {
+ "title": "Contact Us",
+ "description": "Get in touch with our team for support, questions, or feedback.",
+ "form": {
+ "name": "Name",
+ "namePlaceholder": "Enter your name",
+ "email": "Email",
+ "emailPlaceholder": "Enter your email",
+ "subject": "Subject",
+ "subjectPlaceholder": "Enter subject",
+ "message": "Message",
+ "messagePlaceholder": "Enter your message",
+ "sendButton": "Send Message",
+ "sending": "Sending..."
+ },
+ "info": {
+ "email": "Email",
+ "support": "Support",
+ "website": "Website"
+ }
+ },
+ "rate": {
+ "title": "Rate Your Experience",
+ "description": "Help us improve by rating your experience with Wata-Board.",
+ "rating": {
+ "excellent": "Excellent",
+ "good": "Good",
+ "average": "Average",
+ "poor": "Poor",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Write a Review",
+ "placeholder": "Share your experience with Wata-Board...",
+ "submit": "Submit Review",
+ "thankYou": "Thank you for your feedback!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Error",
+ "unknown": "An unknown error occurred",
+ "tryAgain": "Please try again",
+ "contactSupport": "Contact support if the problem persists"
+ },
+ "network": {
+ "title": "Network Error",
+ "description": "Unable to connect to the network",
+ "checkConnection": "Please check your internet connection"
+ },
+ "wallet": {
+ "title": "Wallet Error",
+ "notConnected": "Wallet not connected",
+ "notInstalled": "Freighter wallet not installed",
+ "installFreighter": "Please install Freighter wallet extension"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Skip to main content",
+ "skipToNavigation": "Skip to navigation",
+ "menuToggle": "Toggle navigation menu",
+ "closeMenu": "Close menu",
+ "openMenu": "Open menu",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "save": "Save",
+ "delete": "Delete",
+ "edit": "Edit",
+ "close": "Close",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "submit": "Submit",
+ "reset": "Reset",
+ "clear": "Clear",
+ "search": "Search",
+ "filter": "Filter",
+ "sort": "Sort",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "retry": "Retry",
+ "refresh": "Refresh",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "learnMore": "Learn More",
+ "viewDetails": "View Details",
+ "hideDetails": "Hide Details"
+ },
+ "time": {
+ "seconds": "second",
+ "seconds_plural": "seconds",
+ "minutes": "minute",
+ "minutes_plural": "minutes",
+ "hours": "hour",
+ "hours_plural": "hours",
+ "days": "day",
+ "days_plural": "days",
+ "weeks": "week",
+ "weeks_plural": "weeks",
+ "months": "month",
+ "months_plural": "months",
+ "years": "year",
+ "years_plural": "years",
+ "ago": "{{time}} ago",
+ "in": "in {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "This field is required",
+ "invalid": "Invalid format",
+ "tooShort": "Must be at least {{min}} characters",
+ "tooLong": "Must be no more than {{max}} characters",
+ "invalidEmail": "Please enter a valid email address",
+ "invalidNumber": "Please enter a valid number",
+ "minValue": "Must be at least {{min}}",
+ "maxValue": "Must be no more than {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/pt.json b/wata-board-frontend/src/i18n/locales/pt.json
new file mode 100644
index 00000000..4649c7e2
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/pt.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Decentralized utility payments on Stellar blockchain",
+ "description": "Pay your utility bills using cryptocurrency on the Stellar network"
+ },
+ "navigation": {
+ "home": "Pay Bill",
+ "about": "About",
+ "contact": "Contact",
+ "rate": "Rate Us",
+ "language": "Language"
+ },
+ "payment": {
+ "form": {
+ "title": "Payment Information",
+ "meterNumber": "Meter number",
+ "meterPlaceholder": "e.g. METER-123",
+ "meterDescription": "Enter your utility meter number as shown on your bill",
+ "amount": "Amount",
+ "amountPlaceholder": "Whole number",
+ "amountDescription": "Enter the payment amount in whole XLM",
+ "payButton": "Pay bill",
+ "processing": "Processing...",
+ "rateLimited": "Rate Limited",
+ "requiresExtension": "Requires Freighter extension. 5 transactions per minute limit."
+ },
+ "status": {
+ "title": "Status",
+ "ready": "Ready.",
+ "installWallet": "Please install Freighter Wallet extension!",
+ "enterMeter": "Please enter a meter number.",
+ "enterValidAmount": "Please enter a valid amount greater than 0.",
+ "wholeNumber": "Amount must be a whole number.",
+ "amountTooLarge": "Amount is too large.",
+ "insufficientBalance": "Insufficient balance. Please add more XLM to your wallet.",
+ "estimatingFees": "Estimating transaction fees... Please wait.",
+ "paymentSuccess": "Payment successful! Transaction ID: {{id}}...",
+ "paymentFailed": "Payment failed: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Transaction Fee Estimation",
+ "calculating": "(Calculating...)",
+ "calculatingFees": "Calculating estimated fees...",
+ "paymentAmount": "Payment Amount",
+ "estimatedNetworkFee": "Estimated Network Fee",
+ "totalCost": "Total Cost",
+ "unableToEstimate": "Unable to estimate fees at this time"
+ },
+ "rateLimit": {
+ "title": "Rate Limit Status",
+ "requestsAvailable": "{{count}}/5 requests available",
+ "rateLimited": "Rate limited. Reset in {{time}}",
+ "queue": "Queue: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Wallet Balance",
+ "available": "Available Balance",
+ "total": "Total Balance",
+ "loading": "Loading balance...",
+ "error": "Failed to load balance",
+ "reconnect": "Please reconnect your wallet",
+ "insufficient": "Insufficient balance for this transaction",
+ "lowBalance": "Low balance warning"
+ },
+ "connection": {
+ "connected": "Connected",
+ "disconnected": "Disconnected",
+ "connecting": "Connecting...",
+ "connectWallet": "Connect Wallet",
+ "switchWallet": "Switch Wallet"
+ }
+ },
+ "network": {
+ "mainnet": "MAINNET",
+ "testnet": "TESTNET",
+ "switchNetwork": "Switch Network",
+ "currentNetwork": "Current Network"
+ },
+ "offline": {
+ "banner": {
+ "title": "You're offline",
+ "description": "Some features may be unavailable. Actions will be queued and processed when you're back online.",
+ "retry": "Retry Connection",
+ "details": "Connection Details"
+ },
+ "status": {
+ "online": "Online",
+ "offline": "Offline",
+ "connecting": "Connecting...",
+ "queueStatus": "{{count}} queued action",
+ "queueStatus_plural": "{{count}} queued actions"
+ },
+ "queue": {
+ "title": "Offline Queue",
+ "description": "{{count}} action will be processed when you're back online",
+ "description_plural": "{{count}} actions will be processed when you're back online"
+ },
+ "error": {
+ "paymentOffline": "Payment queued for when you're back online",
+ "generalOffline": "Action queued for when you're back online"
+ }
+ },
+ "about": {
+ "title": "About Wata-Board",
+ "description": "Wata-Board is a decentralized utility payment platform built on the Stellar blockchain. We enable secure, transparent, and efficient utility bill payments using cryptocurrency.",
+ "features": {
+ "title": "Key Features",
+ "secure": "Secure Transactions",
+ "secureDescription": "Built on Stellar blockchain with enterprise-grade security",
+ "fast": "Fast Payments",
+ "fastDescription": "Near-instant settlement with low transaction fees",
+ "transparent": "Transparent Records",
+ "transparentDescription": "All transactions are recorded on the public blockchain",
+ "decentralized": "Decentralized",
+ "decentralizedDescription": "No single point of failure or control"
+ },
+ "howItWorks": {
+ "title": "How It Works",
+ "step1": "Connect your wallet",
+ "step2": "Enter meter details",
+ "step3": "Confirm payment",
+ "step4": "Payment processed"
+ }
+ },
+ "contact": {
+ "title": "Contact Us",
+ "description": "Get in touch with our team for support, questions, or feedback.",
+ "form": {
+ "name": "Name",
+ "namePlaceholder": "Enter your name",
+ "email": "Email",
+ "emailPlaceholder": "Enter your email",
+ "subject": "Subject",
+ "subjectPlaceholder": "Enter subject",
+ "message": "Message",
+ "messagePlaceholder": "Enter your message",
+ "sendButton": "Send Message",
+ "sending": "Sending..."
+ },
+ "info": {
+ "email": "Email",
+ "support": "Support",
+ "website": "Website"
+ }
+ },
+ "rate": {
+ "title": "Rate Your Experience",
+ "description": "Help us improve by rating your experience with Wata-Board.",
+ "rating": {
+ "excellent": "Excellent",
+ "good": "Good",
+ "average": "Average",
+ "poor": "Poor",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Write a Review",
+ "placeholder": "Share your experience with Wata-Board...",
+ "submit": "Submit Review",
+ "thankYou": "Thank you for your feedback!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Error",
+ "unknown": "An unknown error occurred",
+ "tryAgain": "Please try again",
+ "contactSupport": "Contact support if the problem persists"
+ },
+ "network": {
+ "title": "Network Error",
+ "description": "Unable to connect to the network",
+ "checkConnection": "Please check your internet connection"
+ },
+ "wallet": {
+ "title": "Wallet Error",
+ "notConnected": "Wallet not connected",
+ "notInstalled": "Freighter wallet not installed",
+ "installFreighter": "Please install Freighter wallet extension"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Skip to main content",
+ "skipToNavigation": "Skip to navigation",
+ "menuToggle": "Toggle navigation menu",
+ "closeMenu": "Close menu",
+ "openMenu": "Open menu",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "save": "Save",
+ "delete": "Delete",
+ "edit": "Edit",
+ "close": "Close",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "submit": "Submit",
+ "reset": "Reset",
+ "clear": "Clear",
+ "search": "Search",
+ "filter": "Filter",
+ "sort": "Sort",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "retry": "Retry",
+ "refresh": "Refresh",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "learnMore": "Learn More",
+ "viewDetails": "View Details",
+ "hideDetails": "Hide Details"
+ },
+ "time": {
+ "seconds": "second",
+ "seconds_plural": "seconds",
+ "minutes": "minute",
+ "minutes_plural": "minutes",
+ "hours": "hour",
+ "hours_plural": "hours",
+ "days": "day",
+ "days_plural": "days",
+ "weeks": "week",
+ "weeks_plural": "weeks",
+ "months": "month",
+ "months_plural": "months",
+ "years": "year",
+ "years_plural": "years",
+ "ago": "{{time}} ago",
+ "in": "in {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "This field is required",
+ "invalid": "Invalid format",
+ "tooShort": "Must be at least {{min}} characters",
+ "tooLong": "Must be no more than {{max}} characters",
+ "invalidEmail": "Please enter a valid email address",
+ "invalidNumber": "Please enter a valid number",
+ "minValue": "Must be at least {{min}}",
+ "maxValue": "Must be no more than {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/ru.json b/wata-board-frontend/src/i18n/locales/ru.json
new file mode 100644
index 00000000..4649c7e2
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/ru.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "Decentralized utility payments on Stellar blockchain",
+ "description": "Pay your utility bills using cryptocurrency on the Stellar network"
+ },
+ "navigation": {
+ "home": "Pay Bill",
+ "about": "About",
+ "contact": "Contact",
+ "rate": "Rate Us",
+ "language": "Language"
+ },
+ "payment": {
+ "form": {
+ "title": "Payment Information",
+ "meterNumber": "Meter number",
+ "meterPlaceholder": "e.g. METER-123",
+ "meterDescription": "Enter your utility meter number as shown on your bill",
+ "amount": "Amount",
+ "amountPlaceholder": "Whole number",
+ "amountDescription": "Enter the payment amount in whole XLM",
+ "payButton": "Pay bill",
+ "processing": "Processing...",
+ "rateLimited": "Rate Limited",
+ "requiresExtension": "Requires Freighter extension. 5 transactions per minute limit."
+ },
+ "status": {
+ "title": "Status",
+ "ready": "Ready.",
+ "installWallet": "Please install Freighter Wallet extension!",
+ "enterMeter": "Please enter a meter number.",
+ "enterValidAmount": "Please enter a valid amount greater than 0.",
+ "wholeNumber": "Amount must be a whole number.",
+ "amountTooLarge": "Amount is too large.",
+ "insufficientBalance": "Insufficient balance. Please add more XLM to your wallet.",
+ "estimatingFees": "Estimating transaction fees... Please wait.",
+ "paymentSuccess": "Payment successful! Transaction ID: {{id}}...",
+ "paymentFailed": "Payment failed: {{error}}"
+ },
+ "feeEstimation": {
+ "title": "Transaction Fee Estimation",
+ "calculating": "(Calculating...)",
+ "calculatingFees": "Calculating estimated fees...",
+ "paymentAmount": "Payment Amount",
+ "estimatedNetworkFee": "Estimated Network Fee",
+ "totalCost": "Total Cost",
+ "unableToEstimate": "Unable to estimate fees at this time"
+ },
+ "rateLimit": {
+ "title": "Rate Limit Status",
+ "requestsAvailable": "{{count}}/5 requests available",
+ "rateLimited": "Rate limited. Reset in {{time}}",
+ "queue": "Queue: {{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "Wallet Balance",
+ "available": "Available Balance",
+ "total": "Total Balance",
+ "loading": "Loading balance...",
+ "error": "Failed to load balance",
+ "reconnect": "Please reconnect your wallet",
+ "insufficient": "Insufficient balance for this transaction",
+ "lowBalance": "Low balance warning"
+ },
+ "connection": {
+ "connected": "Connected",
+ "disconnected": "Disconnected",
+ "connecting": "Connecting...",
+ "connectWallet": "Connect Wallet",
+ "switchWallet": "Switch Wallet"
+ }
+ },
+ "network": {
+ "mainnet": "MAINNET",
+ "testnet": "TESTNET",
+ "switchNetwork": "Switch Network",
+ "currentNetwork": "Current Network"
+ },
+ "offline": {
+ "banner": {
+ "title": "You're offline",
+ "description": "Some features may be unavailable. Actions will be queued and processed when you're back online.",
+ "retry": "Retry Connection",
+ "details": "Connection Details"
+ },
+ "status": {
+ "online": "Online",
+ "offline": "Offline",
+ "connecting": "Connecting...",
+ "queueStatus": "{{count}} queued action",
+ "queueStatus_plural": "{{count}} queued actions"
+ },
+ "queue": {
+ "title": "Offline Queue",
+ "description": "{{count}} action will be processed when you're back online",
+ "description_plural": "{{count}} actions will be processed when you're back online"
+ },
+ "error": {
+ "paymentOffline": "Payment queued for when you're back online",
+ "generalOffline": "Action queued for when you're back online"
+ }
+ },
+ "about": {
+ "title": "About Wata-Board",
+ "description": "Wata-Board is a decentralized utility payment platform built on the Stellar blockchain. We enable secure, transparent, and efficient utility bill payments using cryptocurrency.",
+ "features": {
+ "title": "Key Features",
+ "secure": "Secure Transactions",
+ "secureDescription": "Built on Stellar blockchain with enterprise-grade security",
+ "fast": "Fast Payments",
+ "fastDescription": "Near-instant settlement with low transaction fees",
+ "transparent": "Transparent Records",
+ "transparentDescription": "All transactions are recorded on the public blockchain",
+ "decentralized": "Decentralized",
+ "decentralizedDescription": "No single point of failure or control"
+ },
+ "howItWorks": {
+ "title": "How It Works",
+ "step1": "Connect your wallet",
+ "step2": "Enter meter details",
+ "step3": "Confirm payment",
+ "step4": "Payment processed"
+ }
+ },
+ "contact": {
+ "title": "Contact Us",
+ "description": "Get in touch with our team for support, questions, or feedback.",
+ "form": {
+ "name": "Name",
+ "namePlaceholder": "Enter your name",
+ "email": "Email",
+ "emailPlaceholder": "Enter your email",
+ "subject": "Subject",
+ "subjectPlaceholder": "Enter subject",
+ "message": "Message",
+ "messagePlaceholder": "Enter your message",
+ "sendButton": "Send Message",
+ "sending": "Sending..."
+ },
+ "info": {
+ "email": "Email",
+ "support": "Support",
+ "website": "Website"
+ }
+ },
+ "rate": {
+ "title": "Rate Your Experience",
+ "description": "Help us improve by rating your experience with Wata-Board.",
+ "rating": {
+ "excellent": "Excellent",
+ "good": "Good",
+ "average": "Average",
+ "poor": "Poor",
+ "terrible": "Terrible"
+ },
+ "review": {
+ "title": "Write a Review",
+ "placeholder": "Share your experience with Wata-Board...",
+ "submit": "Submit Review",
+ "thankYou": "Thank you for your feedback!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "Error",
+ "unknown": "An unknown error occurred",
+ "tryAgain": "Please try again",
+ "contactSupport": "Contact support if the problem persists"
+ },
+ "network": {
+ "title": "Network Error",
+ "description": "Unable to connect to the network",
+ "checkConnection": "Please check your internet connection"
+ },
+ "wallet": {
+ "title": "Wallet Error",
+ "notConnected": "Wallet not connected",
+ "notInstalled": "Freighter wallet not installed",
+ "installFreighter": "Please install Freighter wallet extension"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "Skip to main content",
+ "skipToNavigation": "Skip to navigation",
+ "menuToggle": "Toggle navigation menu",
+ "closeMenu": "Close menu",
+ "openMenu": "Open menu",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "warning": "Warning",
+ "info": "Information"
+ },
+ "common": {
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "save": "Save",
+ "delete": "Delete",
+ "edit": "Edit",
+ "close": "Close",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "submit": "Submit",
+ "reset": "Reset",
+ "clear": "Clear",
+ "search": "Search",
+ "filter": "Filter",
+ "sort": "Sort",
+ "loading": "Loading...",
+ "error": "Error",
+ "success": "Success",
+ "retry": "Retry",
+ "refresh": "Refresh",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "learnMore": "Learn More",
+ "viewDetails": "View Details",
+ "hideDetails": "Hide Details"
+ },
+ "time": {
+ "seconds": "second",
+ "seconds_plural": "seconds",
+ "minutes": "minute",
+ "minutes_plural": "minutes",
+ "hours": "hour",
+ "hours_plural": "hours",
+ "days": "day",
+ "days_plural": "days",
+ "weeks": "week",
+ "weeks_plural": "weeks",
+ "months": "month",
+ "months_plural": "months",
+ "years": "year",
+ "years_plural": "years",
+ "ago": "{{time}} ago",
+ "in": "in {{time}}"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "This field is required",
+ "invalid": "Invalid format",
+ "tooShort": "Must be at least {{min}} characters",
+ "tooLong": "Must be no more than {{max}} characters",
+ "invalidEmail": "Please enter a valid email address",
+ "invalidNumber": "Please enter a valid number",
+ "minValue": "Must be at least {{min}}",
+ "maxValue": "Must be no more than {{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/i18n/locales/zh.json b/wata-board-frontend/src/i18n/locales/zh.json
new file mode 100644
index 00000000..f79f8b5b
--- /dev/null
+++ b/wata-board-frontend/src/i18n/locales/zh.json
@@ -0,0 +1,257 @@
+{
+ "app": {
+ "title": "Wata-Board",
+ "tagline": "基于Stellar区块链的去中心化公用事业支付",
+ "description": "在Stellar网络上使用加密货币支付您的公用事业账单"
+ },
+ "navigation": {
+ "home": "支付账单",
+ "about": "关于我们",
+ "contact": "联系我们",
+ "rate": "评价我们",
+ "language": "语言"
+ },
+ "payment": {
+ "form": {
+ "title": "支付信息",
+ "meterNumber": "电表编号",
+ "meterPlaceholder": "例如:METER-123",
+ "meterDescription": "请输入您账单上显示的电表编号",
+ "amount": "金额",
+ "amountPlaceholder": "整数",
+ "amountDescription": "请输入整数XLM的支付金额",
+ "payButton": "支付账单",
+ "processing": "处理中...",
+ "rateLimited": "达到限制",
+ "requiresExtension": "需要Freighter扩展。每分钟5笔交易限制。"
+ },
+ "status": {
+ "title": "状态",
+ "ready": "准备就绪。",
+ "installWallet": "请安装Freighter钱包扩展!",
+ "enterMeter": "请输入电表编号。",
+ "enterValidAmount": "请输入大于0的有效金额。",
+ "wholeNumber": "金额必须是整数。",
+ "amountTooLarge": "金额太大。",
+ "insufficientBalance": "余额不足。请向您的钱包添加更多XLM。",
+ "estimatingFees": "估算交易费用中...请稍候。",
+ "paymentSuccess": "支付成功!交易ID:{{id}}...",
+ "paymentFailed": "支付失败:{{error}}"
+ },
+ "feeEstimation": {
+ "title": "交易费用估算",
+ "calculating": "(计算中...)",
+ "calculatingFees": "计算估算费用中...",
+ "paymentAmount": "支付金额",
+ "estimatedNetworkFee": "估算网络费用",
+ "totalCost": "总成本",
+ "unableToEstimate": "目前无法估算费用"
+ },
+ "rateLimit": {
+ "title": "速率限制状态",
+ "requestsAvailable": "{{count}}/5个可用请求",
+ "rateLimited": "达到限制。{{time}}后重置",
+ "queue": "队列:{{count}}"
+ }
+ },
+ "wallet": {
+ "balance": {
+ "title": "钱包余额",
+ "available": "可用余额",
+ "total": "总余额",
+ "loading": "加载余额中...",
+ "error": "加载余额失败",
+ "reconnect": "请重新连接您的钱包",
+ "insufficient": "此交易余额不足",
+ "lowBalance": "低余额警告"
+ },
+ "connection": {
+ "connected": "已连接",
+ "disconnected": "已断开",
+ "connecting": "连接中...",
+ "connectWallet": "连接钱包",
+ "switchWallet": "切换钱包"
+ }
+ },
+ "network": {
+ "mainnet": "主网",
+ "testnet": "测试网",
+ "switchNetwork": "切换网络",
+ "currentNetwork": "当前网络"
+ },
+ "offline": {
+ "banner": {
+ "title": "您处于离线状态",
+ "description": "某些功能可能不可用。操作将被排队并在您重新上线时处理。",
+ "retry": "重试连接",
+ "details": "连接详情"
+ },
+ "status": {
+ "online": "在线",
+ "offline": "离线",
+ "connecting": "连接中...",
+ "queueStatus": "{{count}}个排队操作",
+ "queueStatus_plural": "{{count}}个排队操作"
+ },
+ "queue": {
+ "title": "离线队列",
+ "description": "{{count}}个操作将在您重新上线时处理",
+ "description_plural": "{{count}}个操作将在您重新上线时处理"
+ },
+ "error": {
+ "paymentOffline": "支付已排队,将在您重新上线时处理",
+ "generalOffline": "操作已排队,将在您重新上线时处理"
+ }
+ },
+ "about": {
+ "title": "关于Wata-Board",
+ "description": "Wata-Board是一个基于Stellar区块链构建的去中心化公用事业支付平台。我们使用加密货币实现安全、透明和高效的公用事业账单支付。",
+ "features": {
+ "title": "主要功能",
+ "secure": "安全交易",
+ "secureDescription": "基于Stellar区块链构建,具有企业级安全性",
+ "fast": "快速支付",
+ "fastDescription": "近乎即时的结算,交易费用低",
+ "transparent": "透明记录",
+ "transparentDescription": "所有交易都记录在公共区块链上",
+ "decentralized": "去中心化",
+ "decentralizedDescription": "没有单点故障或控制"
+ },
+ "howItWorks": {
+ "title": "工作原理",
+ "step1": "连接您的钱包",
+ "step2": "输入电表详情",
+ "step3": "确认支付",
+ "step4": "支付处理完成"
+ }
+ },
+ "contact": {
+ "title": "联系我们",
+ "description": "联系我们的团队获取支持、问题或反馈。",
+ "form": {
+ "name": "姓名",
+ "namePlaceholder": "输入您的姓名",
+ "email": "电子邮件",
+ "emailPlaceholder": "输入您的电子邮件",
+ "subject": "主题",
+ "subjectPlaceholder": "输入主题",
+ "message": "消息",
+ "messagePlaceholder": "输入您的消息",
+ "sendButton": "发送消息",
+ "sending": "发送中..."
+ },
+ "info": {
+ "email": "电子邮件",
+ "support": "支持",
+ "website": "网站"
+ }
+ },
+ "rate": {
+ "title": "评价您的体验",
+ "description": "通过评价您使用Wata-Board的体验来帮助我们改进。",
+ "rating": {
+ "excellent": "优秀",
+ "good": "良好",
+ "average": "一般",
+ "poor": "较差",
+ "terrible": "很差"
+ },
+ "review": {
+ "title": "写评论",
+ "placeholder": "分享您使用Wata-Board的体验...",
+ "submit": "提交评论",
+ "thankYou": "感谢您的反馈!"
+ }
+ },
+ "errors": {
+ "general": {
+ "title": "错误",
+ "unknown": "发生未知错误",
+ "tryAgain": "请重试",
+ "contactSupport": "如果问题持续存在,请联系支持"
+ },
+ "network": {
+ "title": "网络错误",
+ "description": "无法连接到网络",
+ "checkConnection": "请检查您的互联网连接"
+ },
+ "wallet": {
+ "title": "钱包错误",
+ "notConnected": "钱包未连接",
+ "notInstalled": "未安装Freighter钱包",
+ "installFreighter": "请安装Freighter钱包扩展"
+ }
+ },
+ "accessibility": {
+ "skipToMain": "跳转到主要内容",
+ "skipToNavigation": "跳转到导航",
+ "menuToggle": "切换导航菜单",
+ "closeMenu": "关闭菜单",
+ "openMenu": "打开菜单",
+ "loading": "加载中...",
+ "error": "错误",
+ "success": "成功",
+ "warning": "警告",
+ "info": "信息"
+ },
+ "common": {
+ "cancel": "取消",
+ "confirm": "确认",
+ "save": "保存",
+ "delete": "删除",
+ "edit": "编辑",
+ "close": "关闭",
+ "back": "返回",
+ "next": "下一步",
+ "previous": "上一步",
+ "submit": "提交",
+ "reset": "重置",
+ "clear": "清除",
+ "search": "搜索",
+ "filter": "筛选",
+ "sort": "排序",
+ "loading": "加载中...",
+ "error": "错误",
+ "success": "成功",
+ "retry": "重试",
+ "refresh": "刷新",
+ "copy": "复制",
+ "copied": "已复制!",
+ "learnMore": "了解更多",
+ "viewDetails": "查看详情",
+ "hideDetails": "隐藏详情"
+ },
+ "time": {
+ "seconds": "秒",
+ "seconds_plural": "秒",
+ "minutes": "分钟",
+ "minutes_plural": "分钟",
+ "hours": "小时",
+ "hours_plural": "小时",
+ "days": "天",
+ "days_plural": "天",
+ "weeks": "周",
+ "weeks_plural": "周",
+ "months": "月",
+ "months_plural": "月",
+ "years": "年",
+ "years_plural": "年",
+ "ago": "{{time}}前",
+ "in": "{{time}}后"
+ },
+ "currency": {
+ "xlm": "XLM",
+ "usd": "USD",
+ "eur": "EUR"
+ },
+ "validation": {
+ "required": "此字段为必填项",
+ "invalid": "格式无效",
+ "tooShort": "必须至少包含{{min}}个字符",
+ "tooLong": "不能超过{{max}}个字符",
+ "invalidEmail": "请输入有效的电子邮件地址",
+ "invalidNumber": "请输入有效数字",
+ "minValue": "必须至少为{{min}}",
+ "maxValue": "不能超过{{max}}"
+ }
+}
diff --git a/wata-board-frontend/src/index.css b/wata-board-frontend/src/index.css
new file mode 100644
index 00000000..ef17f923
--- /dev/null
+++ b/wata-board-frontend/src/index.css
@@ -0,0 +1,303 @@
+@import "tailwindcss";
+
+:root {
+ font-family: system-ui, sans-serif;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+html,
+body {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+}
+
+/* Mobile-first responsive improvements */
+@media (max-width: 640px) {
+
+ /* Ensure proper viewport scaling on mobile */
+ html {
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ }
+
+ /* Improve touch targets */
+ button,
+ input,
+ textarea,
+ select {
+ min-height: 44px;
+ min-width: 44px;
+ }
+
+ /* Prevent horizontal scroll */
+ body {
+ overflow-x: hidden;
+ }
+
+ /* Improve readability on mobile */
+ .text-responsive {
+ font-size: clamp(0.875rem, 2.5vw, 1rem);
+ }
+}
+
+/* Tablet improvements */
+@media (min-width: 641px) and (max-width: 1024px) {
+ .text-responsive {
+ font-size: clamp(0.9rem, 1.5vw, 1.1rem);
+ }
+}
+
+/* Smooth animations for mobile menu */
+.mobile-menu-enter {
+ animation: slideDown 0.3s ease-out;
+}
+
+.mobile-menu-exit {
+ animation: slideUp 0.3s ease-out;
+}
+
+/* Touch-friendly focus styles */
+@media (hover: none) and (pointer: coarse) {
+
+ button:focus-visible,
+ input:focus-visible,
+ textarea:focus-visible,
+ select:focus-visible {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+ }
+}
+
+/* Improve scrolling performance */
+.smooth-scroll {
+ scroll-behavior: smooth;
+ -webkit-overflow-scrolling: touch;
+}
+
+/* Prevent zoom on input focus in iOS */
+@supports (-webkit-touch-callout: none) {
+
+ input[type="text"],
+ input[type="number"],
+ input[type="email"],
+ input[type="password"],
+ textarea {
+ font-size: 16px;
+ }
+}
+
+/* Accessibility improvements */
+.sr-only {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ clip: rect(0, 0, 0, 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}
+
+.skip-link {
+ position: absolute !important;
+ top: -40px !important;
+ left: 6px !important;
+ background: #0ea5e9 !important;
+ color: white !important;
+ padding: 8px !important;
+ text-decoration: none !important;
+ border-radius: 4px !important;
+ z-index: 100 !important;
+ transition: top 0.3s !important;
+}
+
+.skip-link:focus {
+ top: 6px !important;
+}
+
+/* High contrast mode support */
+@media (prefers-contrast: high) {
+ .high-contrast {
+ filter: contrast(1.5) !important;
+ }
+
+ /* Ensure text remains readable in high contrast */
+ .text-slate-100 {
+ color: #ffffff !important;
+ }
+
+ .text-slate-300 {
+ color: #e2e8f0 !important;
+ }
+}
+
+/* Reduced motion support */
+@media (prefers-reduced-motion: reduce) {
+
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ scroll-behavior: auto !important;
+ }
+}
+
+/* Keyboard navigation focus styles */
+.keyboard-navigation *:focus {
+ outline: 2px solid #0ea5e9 !important;
+ outline-offset: 2px !important;
+}
+
+/* Screen reader only content */
+.sr-only {
+ position: absolute !important;
+ width: 1px !important;
+ height: 1px !important;
+ padding: 0 !important;
+ margin: -1px !important;
+ overflow: hidden !important;
+ clip: rect(0, 0, 0, 0) !important;
+ white-space: nowrap !important;
+ border: 0 !important;
+}
+
+/* Focus visible improvements */
+.focus-visible {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+/* ARIA live regions */
+[aria-live="polite"] {
+ position: absolute;
+ left: -10000px;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+}
+
+[aria-live="assertive"] {
+ position: absolute;
+ left: -10000px;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+}
+
+/* Enhanced button accessibility */
+button:focus {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+button:focus:not(:focus-visible) {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+/* Enhanced input accessibility */
+input:focus {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+input:focus:not(:focus-visible) {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+/* Link accessibility */
+a:focus {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+a:focus:not(:focus-visible) {
+ outline: 2px solid #0ea5e9;
+ outline-offset: 2px;
+}
+
+/* Form field accessibility */
+fieldset {
+ border: none;
+ padding: 0;
+ margin: 0;
+}
+
+legend {
+ padding: 0;
+ margin-bottom: 0.5rem;
+ font-weight: 600;
+ color: #e2e8f0;
+}
+
+/* Error and status accessibility */
+[role="alert"] {
+ background-color: #991b1b;
+ color: #ffffff;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ margin: 1rem 0;
+}
+
+[role="status"] {
+ padding: 0.5rem;
+ border-radius: 0.25rem;
+}
+
+/* Table accessibility for future use */
+table {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+th,
+td {
+ padding: 0.75rem;
+ text-align: left;
+ border-bottom: 1px solid #475569;
+}
+
+th {
+ background-color: #1e293b;
+ font-weight: 600;
+ color: #ffffff;
+}
+
+/* List accessibility */
+ul,
+ol {
+ padding-left: 1.5rem;
+}
+
+li {
+ margin-bottom: 0.5rem;
+}
+
+/* Code accessibility */
+code {
+ background-color: #1e293b;
+ color: #ffffff;
+ padding: 0.25rem 0.5rem;
+ border-radius: 0.25rem;
+ font-family: monospace;
+ font-size: 0.875rem;
+}
+
+pre {
+ background-color: #1e293b;
+ color: #ffffff;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ overflow-x: auto;
+ font-family: monospace;
+ font-size: 0.875rem;
+}
\ No newline at end of file
diff --git a/wata-board-frontend/src/main.tsx b/wata-board-frontend/src/main.tsx
new file mode 100644
index 00000000..20f50739
--- /dev/null
+++ b/wata-board-frontend/src/main.tsx
@@ -0,0 +1,35 @@
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.tsx'
+import { registerServiceWorker, listenToServiceWorkerMessages } from './utils/serviceWorkerRegistration'
+import './i18n'
+
+// Register service worker for offline support
+registerServiceWorker().then((result) => {
+ if (result.success) {
+ console.log('[Main] Service worker registered successfully');
+
+ // Listen for service worker messages
+ const cleanup = listenToServiceWorkerMessages((message) => {
+ console.log('[Main] Message from service worker:', message);
+
+ // Handle connectivity status updates
+ if (message.type === 'CONNECTIVITY_STATUS') {
+ console.log('[Main] Connectivity status changed:', message.isOnline);
+ // You can dispatch this to React state management here if needed
+ }
+ });
+
+ // Cleanup on page unload
+ window.addEventListener('beforeunload', cleanup);
+ } else {
+ console.warn('[Main] Service worker registration failed:', result.error);
+ }
+});
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
diff --git a/wata-board-frontend/src/pages/About.tsx b/wata-board-frontend/src/pages/About.tsx
new file mode 100644
index 00000000..0e1f36d7
--- /dev/null
+++ b/wata-board-frontend/src/pages/About.tsx
@@ -0,0 +1,53 @@
+function About() {
+ return (
+
+
+
+
About Wata-Board
+
+
+
+ Wata-Board is a decentralized utility payment platform built on the Stellar blockchain.
+ We enable users to pay their utility bills (water, electricity) securely and transparently
+ using cryptocurrency.
+
+
+
+
Our Mission
+
+ To democratize utility payments by leveraging blockchain technology for fast,
+ secure, and borderless transactions. We believe everyone deserves access to
+ reliable and transparent utility payment systems.
+
+
+
+
+
How It Works
+
+
Connect your Freighter wallet
+
Enter your meter number
+
Input the amount you want to pay
+
Confirm the transaction on the blockchain
+
+
+
+
+
Technology
+
+ Built on Stellar/Soroban smart contracts, Wata-Board ensures immutable and
+ verifiable payment records. All transactions are recorded on the blockchain,
+ providing complete transparency.
+