feature/WNPT-740 #3
21 changed files with 679 additions and 162 deletions
149
package-lock.json
generated
149
package-lock.json
generated
|
|
@ -81,6 +81,7 @@
|
||||||
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.7",
|
"@babel/code-frame": "^7.29.7",
|
||||||
"@babel/generator": "^7.29.7",
|
"@babel/generator": "^7.29.7",
|
||||||
|
|
@ -290,28 +291,6 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/core": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@emnapi/wasi-threads": "1.2.1",
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/runtime": {
|
|
||||||
"version": "1.11.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
|
|
||||||
"integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/wasi-threads": {
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
|
|
@ -683,9 +662,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -702,9 +678,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -721,9 +694,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -740,9 +710,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -759,9 +726,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -778,9 +742,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -797,9 +758,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -816,9 +774,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0-or-later",
|
"license": "LGPL-3.0-or-later",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -835,9 +790,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -860,9 +812,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -885,9 +834,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -910,9 +856,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -935,9 +878,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -960,9 +900,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -985,9 +922,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -1010,9 +944,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -1280,9 +1211,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -1299,9 +1227,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -1318,9 +1243,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -1337,9 +1259,6 @@
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2282,9 +2201,6 @@
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2302,9 +2218,6 @@
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2322,9 +2235,6 @@
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2342,9 +2252,6 @@
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2506,6 +2413,7 @@
|
||||||
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
|
"integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
|
|
@ -2516,6 +2424,7 @@
|
||||||
"integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==",
|
"integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.2.0"
|
"@types/react": "^19.2.0"
|
||||||
}
|
}
|
||||||
|
|
@ -2565,6 +2474,7 @@
|
||||||
"integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==",
|
"integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.67.0",
|
"@typescript-eslint/scope-manager": "8.67.0",
|
||||||
"@typescript-eslint/types": "8.67.0",
|
"@typescript-eslint/types": "8.67.0",
|
||||||
|
|
@ -2921,9 +2831,6 @@
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2938,9 +2845,6 @@
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2955,9 +2859,6 @@
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2972,9 +2873,6 @@
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -2989,9 +2887,6 @@
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -3006,9 +2901,6 @@
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -3023,9 +2915,6 @@
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -3040,9 +2929,6 @@
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -3057,9 +2943,6 @@
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -3074,9 +2957,6 @@
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -3175,6 +3055,7 @@
|
||||||
"integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
|
"integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
|
|
@ -3530,6 +3411,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.11.12",
|
"baseline-browser-mapping": "^2.11.12",
|
||||||
"caniuse-lite": "^1.0.30001809",
|
"caniuse-lite": "^1.0.30001809",
|
||||||
|
|
@ -4146,6 +4028,7 @@
|
||||||
"integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
|
"integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
|
|
@ -4331,6 +4214,7 @@
|
||||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rtsao/scc": "^1.1.0",
|
"@rtsao/scc": "^1.1.0",
|
||||||
"array-includes": "^3.1.9",
|
"array-includes": "^3.1.9",
|
||||||
|
|
@ -5818,9 +5702,6 @@
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5842,9 +5723,6 @@
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5866,9 +5744,6 @@
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -5890,9 +5765,6 @@
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|
@ -6576,6 +6448,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz",
|
||||||
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
|
"integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
|
@ -6585,6 +6458,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
|
||||||
"integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
|
"integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
|
|
@ -7361,6 +7235,7 @@
|
||||||
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|
@ -7523,6 +7398,7 @@
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
@ -7843,6 +7719,7 @@
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
||||||
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import ProfileListMenu from "@/components/profile/profile-menu";
|
||||||
|
import ProfilePageHeadLine from "@/components/profile/profile-page-head-line";
|
||||||
|
import ProfilePersonDetailCard from "@/components/profile/profile-person-detail-card";
|
||||||
|
|
||||||
|
export default function ProfilePage() {
|
||||||
|
return <section>
|
||||||
|
<ProfilePageHeadLine title='پروفایل من' />
|
||||||
|
<ProfilePersonDetailCard />
|
||||||
|
<ProfileListMenu />
|
||||||
|
</section>
|
||||||
|
}
|
||||||
16
src/app/(customer)/(shop)/profile/(routes)/wallet/page.tsx
Normal file
16
src/app/(customer)/(shop)/profile/(routes)/wallet/page.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import ProfilePageHeadLine from '@/components/profile/profile-page-head-line'
|
||||||
|
import TransactionHistory from '@/components/wallet/transaction-history'
|
||||||
|
import WalletBlancCard from '@/components/wallet/wallet-blanc-card'
|
||||||
|
|
||||||
|
const WalletPage = () => {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<ProfilePageHeadLine title='کیف پول من' />
|
||||||
|
<WalletBlancCard />
|
||||||
|
<ProfilePageHeadLine title='تاریخچه تراکنشها' />
|
||||||
|
<TransactionHistory />
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WalletPage
|
||||||
28
src/app/(customer)/(shop)/profile/layout.tsx
Normal file
28
src/app/(customer)/(shop)/profile/layout.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { requireSession } from "@/lib/auth/session";
|
||||||
|
import { getAccount } from "@/lib/api/account";
|
||||||
|
import { AccountProvider } from "@/lib/providers/account-provider";
|
||||||
|
|
||||||
|
export default async function DashboardLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
await requireSession("/login");
|
||||||
|
|
||||||
|
let account: Awaited<ReturnType<typeof getAccount>> | null = null;
|
||||||
|
let error: string | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
account = await getAccount();
|
||||||
|
} catch (e: any) {
|
||||||
|
error = e?.message ?? "خطا در دریافت اطلاعات پروفایل";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="max-w-5xl w-full p-4 mx-auto">
|
||||||
|
<AccountProvider account={account} error={error}>
|
||||||
|
{children}
|
||||||
|
</AccountProvider>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import Link from "next/link";
|
|
||||||
import { requireSession } from "@/lib/auth/session";
|
|
||||||
import { LogoutButton } from "@/components/customer/logout-button";
|
|
||||||
|
|
||||||
// Minimal placeholder — winofyfrontenddoc.md's bottom-nav references a "پروفایل"
|
|
||||||
// tab, but no profile screen is documented yet in either doc.
|
|
||||||
export default async function ProfilePage() {
|
|
||||||
await requireSession("/profile");
|
|
||||||
return (
|
|
||||||
<main className="mx-auto flex w-full max-w-md flex-1 flex-col gap-3 px-4 py-6">
|
|
||||||
<h1 className="text-title-large font-extrabold text-foreground">پروفایل</h1>
|
|
||||||
<Link href="/orders" className="rounded-xl border border-border p-4 text-body-large">
|
|
||||||
سفارشهای من
|
|
||||||
</Link>
|
|
||||||
<Link href="/addresses" className="rounded-xl border border-border p-4 text-body-large">
|
|
||||||
آدرسهای من
|
|
||||||
</Link>
|
|
||||||
<div className="rounded-xl border border-border p-4">
|
|
||||||
<LogoutButton />
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -15,8 +15,8 @@ export const metadata: Metadata = {
|
||||||
|
|
||||||
export default function RootLayout({ children }: LayoutProps<"/">) {
|
export default function RootLayout({ children }: LayoutProps<"/">) {
|
||||||
return (
|
return (
|
||||||
<html lang="fa" dir="rtl" className={`${vazirmatn.variable} h-full antialiased`}>
|
<html lang="fa" dir="rtl" className={`${vazirmatn.variable} h-full antialiased`} suppressHydrationWarning>
|
||||||
<body className="min-h-full flex flex-col bg-background text-foreground">
|
<body className="min-h-full flex flex-col bg-background text-foreground" suppressHydrationWarning>
|
||||||
<QueryProvider>{children}</QueryProvider>
|
<QueryProvider>{children}</QueryProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
35
src/components/profile/profile-exit-account.tsx
Normal file
35
src/components/profile/profile-exit-account.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { ChevronLeft, LogOutIcon } from "lucide-react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useTransition } from "react";
|
||||||
|
|
||||||
|
const ProfileExitAccount = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
|
function handleLogout() {
|
||||||
|
startTransition(async () => {
|
||||||
|
await fetch("/api/auth/logout", { method: "POST" });
|
||||||
|
router.push("/");
|
||||||
|
router.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-between w-full py-3 cursor-pointer"
|
||||||
|
onClick={handleLogout}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="bg-error/10 p-2 rounded-lg">
|
||||||
|
<LogOutIcon className="w-4 h-4 text-error" />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-semibold text-error">خروج</span>
|
||||||
|
</div>
|
||||||
|
<ChevronLeft className="w-5 h-5 text-error" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfileExitAccount;
|
||||||
38
src/components/profile/profile-menu-item.tsx
Normal file
38
src/components/profile/profile-menu-item.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { ChevronLeft, User } from 'lucide-react';
|
||||||
|
|
||||||
|
interface ProfileMenuItemInterface {
|
||||||
|
href: string;
|
||||||
|
text: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
isLast?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfileMenuItem = ({
|
||||||
|
href,
|
||||||
|
text,
|
||||||
|
icon,
|
||||||
|
isLast = false,
|
||||||
|
}: ProfileMenuItemInterface) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={href}
|
||||||
|
className={`
|
||||||
|
flex items-center justify-between w-full py-3
|
||||||
|
${!isLast ? 'border-b border-border' : ''}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="bg-gray-50 p-2 rounded-lg">
|
||||||
|
{icon || <User className="w-4 h-4 text-primary-action" />}
|
||||||
|
</div>
|
||||||
|
<span className="text-sm text-gray-700">{text}</span>
|
||||||
|
</div>
|
||||||
|
<ChevronLeft className="w-5 h-5 text-gray-500" />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfileMenuItem;
|
||||||
70
src/components/profile/profile-menu.tsx
Normal file
70
src/components/profile/profile-menu.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ShoppingBag,
|
||||||
|
MapPin,
|
||||||
|
Wallet,
|
||||||
|
Bell,
|
||||||
|
CircleQuestionMark,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import ProfileMenuItem from './profile-menu-item';
|
||||||
|
import ProfileExitAccount from './profile-exit-account';
|
||||||
|
|
||||||
|
const menuItems = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
href: '/addresses',
|
||||||
|
text: 'سعادتآباد',
|
||||||
|
icon: <MapPin className="size-5 text-primary-action" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
href: '/orders',
|
||||||
|
text: 'سفارشهای من',
|
||||||
|
icon: <ShoppingBag className="size-5 text-primary-action" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
href: '/addresses',
|
||||||
|
text: 'آدرسهای من',
|
||||||
|
icon: <MapPin className="size-5 text-primary-action" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
href: '/profile/wallet',
|
||||||
|
text: 'کیف پول',
|
||||||
|
icon: <Wallet className="size-5 text-primary-action" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
href: '/profile/notifications',
|
||||||
|
text: 'اعلانها',
|
||||||
|
icon: <Bell className="size-5 text-primary-action" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
href: '/profile/support',
|
||||||
|
text: 'پشتیبانی',
|
||||||
|
icon: <CircleQuestionMark className="size-5 text-primary-action" />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ProfileMenu = () => {
|
||||||
|
return (
|
||||||
|
<div className="w-full">
|
||||||
|
{menuItems.map((item, index) => (
|
||||||
|
<ProfileMenuItem
|
||||||
|
key={item.id}
|
||||||
|
href={item.href}
|
||||||
|
text={item.text}
|
||||||
|
icon={item.icon}
|
||||||
|
isLast={index === menuItems.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div className="w-full h-px bg-border mt-4" />
|
||||||
|
<ProfileExitAccount />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfileMenu;
|
||||||
11
src/components/profile/profile-page-head-line.tsx
Normal file
11
src/components/profile/profile-page-head-line.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
interface ProfilePageHeadLineInterface {
|
||||||
|
title: String;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfilePageHeadLine = ({ title }: ProfilePageHeadLineInterface) => {
|
||||||
|
return <h1 className="font-bold text-2xl">{title}</h1>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProfilePageHeadLine
|
||||||
70
src/components/profile/profile-person-detail-card.tsx
Normal file
70
src/components/profile/profile-person-detail-card.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
'use client';
|
||||||
|
import { useAccount } from "@/lib/providers/account-provider";
|
||||||
|
import { getDisplayNameHelper, getInitialsHelper } from "@/lib/utils/account-utils";
|
||||||
|
|
||||||
|
const ProfilePersonDetailCard = () => {
|
||||||
|
const { data, error, isLoading } = useAccount();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="w-full min-h-[128px] rounded-2xl bg-teal-light my-6 py-4 px-10 flex items-center justify-center">
|
||||||
|
<p className="text-sm text-gray-500">در حال بارگذاری...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="w-full min-h-[128px] rounded-2xl bg-teal-light my-6 py-4 px-10 flex items-center justify-center">
|
||||||
|
<p className="text-sm text-destructive">{error}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<div className="w-full min-h-[128px] rounded-2xl bg-teal-light my-6 py-4 px-10 flex items-center justify-center">
|
||||||
|
<p className="text-sm text-gray-500">اطلاعات پروفایل یافت نشد</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayName = getDisplayNameHelper(data);
|
||||||
|
const initials = getInitialsHelper(data);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full min-h-[128px] rounded-2xl bg-teal-light my-6 py-4 px-10 flex items-center justify-center">
|
||||||
|
<div className="flex flex-row items-center w-full">
|
||||||
|
<div className="size-16 bg-primary-action rounded-full flex items-center justify-center text-lg font-bold text-white overflow-hidden shrink-0">
|
||||||
|
{data.avatar ? (
|
||||||
|
<img
|
||||||
|
src={data.avatar}
|
||||||
|
alt={displayName}
|
||||||
|
className="size-full object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span>{initials}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col mr-6 items-start">
|
||||||
|
<p className="text-lg font-bold">{displayName}</p>
|
||||||
|
|
||||||
|
{data.phone_number && (
|
||||||
|
<p className="text-sm text-gray-700 my-2 font-normal" dir="ltr">
|
||||||
|
{data.phone_number}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="text-sm font-semibold text-primary-action cursor-pointer underline"
|
||||||
|
onClick={() => alert('latter...')}
|
||||||
|
>
|
||||||
|
ویرایش پروفایل
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProfilePersonDetailCard;
|
||||||
38
src/components/wallet/transaction-history.tsx
Normal file
38
src/components/wallet/transaction-history.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
import TransactionItem from './transaction-item';
|
||||||
|
|
||||||
|
const transactions = [
|
||||||
|
{ id: 1, amount: 250000, description: 'واریز از کارت', type: 'deposit' },
|
||||||
|
{ id: 2, amount: 45000, description: 'پرداخت فاکتور', type: 'withdraw' },
|
||||||
|
{ id: 3, amount: 120000, description: 'واریز نقدی', type: 'deposit' },
|
||||||
|
{ id: 4, amount: 780000, description: 'حقوق ماهیانه', type: 'deposit' },
|
||||||
|
{ id: 5, amount: 320000, description: 'خرید از فروشگاه', type: 'withdraw' },
|
||||||
|
{ id: 6, amount: 560000, description: 'واریز چک', type: 'deposit' },
|
||||||
|
{ id: 7, amount: 95000, description: 'شارژ اینترنت', type: 'withdraw' },
|
||||||
|
{ id: 8, amount: 1500000, description: 'پرداخت وام', type: 'withdraw' },
|
||||||
|
{ id: 9, amount: 430000, description: 'واریز از همراه', type: 'deposit' },
|
||||||
|
{ id: 10, amount: 210000, description: 'خرید آنلاین', type: 'withdraw' },
|
||||||
|
{ id: 11, amount: 670000, description: 'پاداش عملکرد', type: 'deposit' },
|
||||||
|
{ id: 12, amount: 185000, description: 'پرداخت قبوض', type: 'withdraw' },
|
||||||
|
{ id: 13, amount: 920000, description: 'فروش کالا', type: 'deposit' },
|
||||||
|
{ id: 14, amount: 380000, description: 'انتقال به دوست', type: 'withdraw' },
|
||||||
|
{ id: 15, amount: 110000, description: 'بازگشت وجه', type: 'deposit' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const TransactionHistory = () => {
|
||||||
|
return (
|
||||||
|
<div className="w-full my-8">
|
||||||
|
{transactions.map((item, index) => (
|
||||||
|
<TransactionItem
|
||||||
|
key={item.id}
|
||||||
|
amount={item.amount}
|
||||||
|
description={item.description}
|
||||||
|
type={item.type}
|
||||||
|
isLast={index === transactions.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionHistory;
|
||||||
49
src/components/wallet/transaction-item.tsx
Normal file
49
src/components/wallet/transaction-item.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ArrowDownIcon, ArrowUp } from 'lucide-react';
|
||||||
|
import { formatPriceWithToman } from '@/lib/utils/priceUtils';
|
||||||
|
|
||||||
|
interface TransactionItemProps {
|
||||||
|
amount: number;
|
||||||
|
description: string;
|
||||||
|
type: 'deposit' | 'withdraw';
|
||||||
|
isLast?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionItem = ({
|
||||||
|
amount,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
isLast = false,
|
||||||
|
}: TransactionItemProps) => {
|
||||||
|
const isDeposit = type === 'deposit';
|
||||||
|
const Icon = isDeposit ? ArrowDownIcon : ArrowUp;
|
||||||
|
|
||||||
|
const amountColor = isDeposit ? 'text-success' : 'text-error';
|
||||||
|
const bgColor = isDeposit ? 'bg-success/10' : 'bg-error/10';
|
||||||
|
const iconColor = isDeposit ? 'text-success' : 'text-error';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
flex items-center justify-between w-full py-3
|
||||||
|
${!isLast ? 'border-b border-border' : ''}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className={`p-2 rounded-full ${bgColor}`}>
|
||||||
|
<Icon className={`w-5 h-5 rotate-45 ${iconColor}`} />
|
||||||
|
</div>
|
||||||
|
<span className="text-sm font-normal flex mr-4">{description}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-end gap-1">
|
||||||
|
<span className={`text-sm font-medium ${amountColor}`}>
|
||||||
|
{isDeposit ? '+' : '-'} {formatPriceWithToman(amount)}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-gray-500">۱۴۰۳/۰۸/۲۴</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionItem;
|
||||||
52
src/components/wallet/wallet-blanc-card.tsx
Normal file
52
src/components/wallet/wallet-blanc-card.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { formatPriceWithToman } from '@/lib/utils/priceUtils';
|
||||||
|
import { LogOutIcon, PlusIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
const WalletBlancCard = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="
|
||||||
|
w-full min-h-12 rounded-2xl my-6 py-6 px-6
|
||||||
|
flex flex-col justify-start
|
||||||
|
bg-gradient-to-l from-primary to-primary-action
|
||||||
|
shadow-[0_12px_24px] shadow-primary/25
|
||||||
|
border border-primary/25
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<h4 className="text-sm font-medium text-gray-300">
|
||||||
|
موجودی کیف پول
|
||||||
|
</h4>
|
||||||
|
<h1 className="text-2xl font-bold my-4 text-primary-foreground">
|
||||||
|
{formatPriceWithToman(35000000)}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="flex w-full gap-4">
|
||||||
|
<button
|
||||||
|
className="
|
||||||
|
w-full bg-white h-12 rounded-xl cursor-pointer
|
||||||
|
flex items-center justify-center gap-2
|
||||||
|
"
|
||||||
|
onClick={() => alert('latter...')}
|
||||||
|
>
|
||||||
|
<PlusIcon className="text-primary-action size-4" />
|
||||||
|
<span className="text-primary-action">شارژ کیف پول</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="
|
||||||
|
w-full h-12 rounded-xl cursor-pointer
|
||||||
|
flex items-center justify-center gap-2
|
||||||
|
border border-primary-foreground
|
||||||
|
"
|
||||||
|
onClick={() => alert('latter...')}
|
||||||
|
>
|
||||||
|
<LogOutIcon className="text-primary-foreground size-4 -rotate-90" />
|
||||||
|
<span className="text-primary-foreground">برداشت</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WalletBlancCard;
|
||||||
8
src/lib/api/account.ts
Normal file
8
src/lib/api/account.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
"use server";
|
||||||
|
|
||||||
|
import { gooyalFetch } from "./gooyal-accounts";
|
||||||
|
import type { Account } from "@/types/api";
|
||||||
|
|
||||||
|
export async function getAccount(): Promise<Account> {
|
||||||
|
return gooyalFetch<Account>("/users/api/account/");
|
||||||
|
}
|
||||||
65
src/lib/api/gooyal-accounts.ts
Normal file
65
src/lib/api/gooyal-accounts.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import "server-only";
|
||||||
|
import { getValidSession } from "@/lib/auth/session";
|
||||||
|
import { ApiError, type WinofyErrorDetails } from "./errors";
|
||||||
|
import { logWinofyRequest, logWinofyResponse } from "./dev-logger";
|
||||||
|
|
||||||
|
const BASE_URL = process.env.GOOYAL_ACCOUNTS_BASE_URL!;
|
||||||
|
|
||||||
|
interface GooyalFetchOptions extends Omit<RequestInit, "body"> {
|
||||||
|
auth?: boolean;
|
||||||
|
params?: Record<string, string | number | boolean | undefined>;
|
||||||
|
body?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function gooyalFetch<T>(path: string, options: GooyalFetchOptions = {}): Promise<T> {
|
||||||
|
const { auth = true, params, headers, body, ...rest } = options;
|
||||||
|
const method = rest.method ?? "GET";
|
||||||
|
|
||||||
|
const url = new URL(BASE_URL.replace(/\/$/, "") + path);
|
||||||
|
if (params) {
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (value !== undefined) url.searchParams.set(key, String(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalHeaders = new Headers(headers);
|
||||||
|
const hasBody = body !== undefined;
|
||||||
|
if (hasBody && !finalHeaders.has("Content-Type")) {
|
||||||
|
finalHeaders.set("Content-Type", "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth) {
|
||||||
|
const session = await getValidSession();
|
||||||
|
if (session) {
|
||||||
|
finalHeaders.set("Authorization", `Bearer ${session.accessToken}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logWinofyRequest?.(method, url.toString(), body);
|
||||||
|
|
||||||
|
const res = await fetch(url.toString(), {
|
||||||
|
...rest,
|
||||||
|
headers: finalHeaders,
|
||||||
|
body: hasBody ? JSON.stringify(body) : undefined,
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 204) {
|
||||||
|
logWinofyResponse?.(method, url.toString(), res.status, null);
|
||||||
|
return undefined as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json().catch(() => null);
|
||||||
|
logWinofyResponse?.(method, url.toString(), res.status, data);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const envelope = data as { status_message?: string; details?: WinofyErrorDetails } | null;
|
||||||
|
throw new ApiError(
|
||||||
|
res.status,
|
||||||
|
envelope?.status_message ?? res.statusText,
|
||||||
|
envelope?.details ?? null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data as T;
|
||||||
|
}
|
||||||
42
src/lib/providers/account-provider.tsx
Normal file
42
src/lib/providers/account-provider.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { createContext, useContext, type ReactNode } from "react";
|
||||||
|
import type { Account } from "@/types/api";
|
||||||
|
|
||||||
|
type AccountContextValue = {
|
||||||
|
data: Account | null;
|
||||||
|
error: string | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const AccountContext = createContext<AccountContextValue | undefined>(undefined);
|
||||||
|
|
||||||
|
export function AccountProvider({
|
||||||
|
children,
|
||||||
|
account,
|
||||||
|
error = null,
|
||||||
|
}: {
|
||||||
|
children: ReactNode;
|
||||||
|
account: Account | null;
|
||||||
|
error?: string | null;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<AccountContext.Provider
|
||||||
|
value={{
|
||||||
|
data: account,
|
||||||
|
error,
|
||||||
|
isLoading: false,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AccountContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAccount() {
|
||||||
|
const ctx = useContext(AccountContext);
|
||||||
|
if (ctx === undefined) {
|
||||||
|
throw new Error("useAccount must be used within AccountProvider");
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
49
src/lib/utils/account-utils.ts
Normal file
49
src/lib/utils/account-utils.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useAccount } from '@/lib/providers/account-provider';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function getInitialsHelper(account: {
|
||||||
|
name?: string | null;
|
||||||
|
first_name?: string | null;
|
||||||
|
last_name?: string | null;
|
||||||
|
username?: string | null;
|
||||||
|
}): string {
|
||||||
|
if (account.name?.trim()) {
|
||||||
|
const parts = account.name.trim().split(/\s+/);
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
||||||
|
}
|
||||||
|
return parts[0].slice(0, 2).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.first_name || account.last_name) {
|
||||||
|
const first = account.first_name?.[0] ?? '';
|
||||||
|
const last = account.last_name?.[0] ?? '';
|
||||||
|
return (first + last).toUpperCase() || '؟';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (account.username) {
|
||||||
|
return account.username.slice(0, 2).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return '؟';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDisplayNameHelper(account: {
|
||||||
|
name?: string | null;
|
||||||
|
first_name?: string | null;
|
||||||
|
last_name?: string | null;
|
||||||
|
username?: string | null;
|
||||||
|
}): string {
|
||||||
|
if (account.name?.trim()) return account.name.trim();
|
||||||
|
|
||||||
|
const fullName = [account.first_name, account.last_name]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
if (fullName) return fullName;
|
||||||
|
|
||||||
|
return account.username || 'کاربر';
|
||||||
|
}
|
||||||
42
src/lib/utils/priceUtils.ts
Normal file
42
src/lib/utils/priceUtils.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Converts Rial to Toman (divides by 10)
|
||||||
|
*/
|
||||||
|
export const convertRialToToman = (rialAmount: number): number => {
|
||||||
|
return Math.round(rialAmount / 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats number with comma separators (e.g., 3,000,000)
|
||||||
|
*/
|
||||||
|
export const formatNumberWithCommas = (amount: number): string => {
|
||||||
|
return amount.toLocaleString('fa-IR'); // for Persian digits
|
||||||
|
// Or for English digits: return amount.toLocaleString('en-US');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function: receives amount in Rial or Toman, returns formatted with comma separators and "Toman" label
|
||||||
|
*
|
||||||
|
* @param amount - input amount (in Rial or Toman)
|
||||||
|
* @param inputIsRial - whether the input amount is in Rial (default: true)
|
||||||
|
* @returns string - formatted amount with Toman label (e.g., "۳,۰۰۰,۰۰۰ تومان")
|
||||||
|
*/
|
||||||
|
export const formatPriceWithToman = (
|
||||||
|
amount: number,
|
||||||
|
inputIsRial: boolean = true
|
||||||
|
): string => {
|
||||||
|
// If amount is in Rial, convert to Toman first
|
||||||
|
const tomanAmount = inputIsRial ? convertRialToToman(amount) : amount;
|
||||||
|
|
||||||
|
// Add comma separators
|
||||||
|
const formatted = formatNumberWithCommas(tomanAmount);
|
||||||
|
|
||||||
|
// Return with Toman label
|
||||||
|
return `${formatted} تومان`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simpler version: just take an amount in Toman and format it
|
||||||
|
*/
|
||||||
|
export const formatTomanPrice = (tomanAmount: number): string => {
|
||||||
|
return `${formatNumberWithCommas(tomanAmount)} تومان`;
|
||||||
|
};
|
||||||
|
|
@ -310,3 +310,42 @@ export interface SellerAnalytics {
|
||||||
chart: SalesChartPoint[];
|
chart: SalesChartPoint[];
|
||||||
top_products: TopProduct[];
|
top_products: TopProduct[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---- Account / Profile ----------------------------------------------------
|
||||||
|
|
||||||
|
export type Gender = "m" | "f" | "other" | null;
|
||||||
|
export type EducationDegree =
|
||||||
|
| "primary"
|
||||||
|
| "secondary"
|
||||||
|
| "diploma"
|
||||||
|
| "bachelor"
|
||||||
|
| "master"
|
||||||
|
| "phd"
|
||||||
|
| string
|
||||||
|
| null;
|
||||||
|
|
||||||
|
export interface Account {
|
||||||
|
uuid: string;
|
||||||
|
username: string;
|
||||||
|
email: string | null;
|
||||||
|
phone_number: string | null;
|
||||||
|
first_name: string | null;
|
||||||
|
last_name: string | null;
|
||||||
|
name: string | null;
|
||||||
|
avatar: string | null;
|
||||||
|
gender: Gender;
|
||||||
|
education_degree: EducationDegree;
|
||||||
|
education_field: string | null;
|
||||||
|
birthdate: string | null; // "YYYY-MM-DD"
|
||||||
|
iban: string | null;
|
||||||
|
iban_verified: boolean;
|
||||||
|
province: number | null;
|
||||||
|
province_name: string | null;
|
||||||
|
city: number | null;
|
||||||
|
city_name: string | null;
|
||||||
|
postal_code: string | null;
|
||||||
|
address: string | null;
|
||||||
|
date_joined: string;
|
||||||
|
last_update: string;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue