fund distribution
This commit is contained in:
Generated
+171
-29
@@ -28,6 +28,7 @@
|
|||||||
"react-native-calendars": "^1.1313.0",
|
"react-native-calendars": "^1.1313.0",
|
||||||
"react-native-dialog": "^9.3.0",
|
"react-native-dialog": "^9.3.0",
|
||||||
"react-native-elements": "^3.4.3",
|
"react-native-elements": "^3.4.3",
|
||||||
|
"react-native-fast-confetti": "^1.1.1",
|
||||||
"react-native-image-picker": "^8.2.1",
|
"react-native-image-picker": "^8.2.1",
|
||||||
"react-native-keychain": "^10.0.0",
|
"react-native-keychain": "^10.0.0",
|
||||||
"react-native-linear-gradient": "^2.8.3",
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
@@ -154,7 +155,6 @@
|
|||||||
"version": "7.27.3",
|
"version": "7.27.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
|
||||||
"integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
|
"integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.27.3"
|
"@babel/types": "^7.27.3"
|
||||||
@@ -183,7 +183,6 @@
|
|||||||
"version": "7.28.3",
|
"version": "7.28.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz",
|
||||||
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
|
"integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||||
@@ -205,7 +204,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz",
|
||||||
"integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==",
|
"integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.27.1",
|
"@babel/helper-annotate-as-pure": "^7.27.1",
|
||||||
@@ -249,7 +247,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
|
||||||
"integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==",
|
"integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/traverse": "^7.27.1",
|
"@babel/traverse": "^7.27.1",
|
||||||
@@ -293,7 +290,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
|
||||||
"integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
|
"integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.27.1"
|
"@babel/types": "^7.27.1"
|
||||||
@@ -333,7 +329,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz",
|
||||||
"integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==",
|
"integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-member-expression-to-functions": "^7.27.1",
|
"@babel/helper-member-expression-to-functions": "^7.27.1",
|
||||||
@@ -351,7 +346,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
|
||||||
"integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
|
"integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/traverse": "^7.27.1",
|
"@babel/traverse": "^7.27.1",
|
||||||
@@ -699,7 +693,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
|
||||||
"integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
|
"integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1"
|
"@babel/helper-plugin-utils": "^7.27.1"
|
||||||
@@ -817,7 +810,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
|
||||||
"integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
|
"integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1"
|
"@babel/helper-plugin-utils": "^7.27.1"
|
||||||
@@ -850,7 +842,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
|
||||||
"integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
|
"integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1"
|
"@babel/helper-plugin-utils": "^7.27.1"
|
||||||
@@ -934,7 +925,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz",
|
||||||
"integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==",
|
"integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-create-class-features-plugin": "^7.27.1",
|
"@babel/helper-create-class-features-plugin": "^7.27.1",
|
||||||
@@ -968,7 +958,6 @@
|
|||||||
"version": "7.28.4",
|
"version": "7.28.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz",
|
||||||
"integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==",
|
"integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||||
@@ -1271,7 +1260,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz",
|
||||||
"integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==",
|
"integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-module-transforms": "^7.27.1",
|
"@babel/helper-module-transforms": "^7.27.1",
|
||||||
@@ -1357,7 +1345,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz",
|
||||||
"integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==",
|
"integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1"
|
"@babel/helper-plugin-utils": "^7.27.1"
|
||||||
@@ -1442,7 +1429,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz",
|
||||||
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
|
"integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1",
|
"@babel/helper-plugin-utils": "^7.27.1",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
|
||||||
"integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
|
"integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1"
|
"@babel/helper-plugin-utils": "^7.27.1"
|
||||||
@@ -1713,7 +1698,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
|
||||||
"integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
|
"integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-plugin-utils": "^7.27.1"
|
"@babel/helper-plugin-utils": "^7.27.1"
|
||||||
@@ -1745,7 +1729,6 @@
|
|||||||
"version": "7.28.0",
|
"version": "7.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz",
|
||||||
"integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==",
|
"integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-annotate-as-pure": "^7.27.3",
|
"@babel/helper-annotate-as-pure": "^7.27.3",
|
||||||
@@ -1798,7 +1781,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
|
||||||
"integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
|
"integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
|
"@babel/helper-create-regexp-features-plugin": "^7.27.1",
|
||||||
@@ -1928,6 +1910,26 @@
|
|||||||
"@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
|
"@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/preset-typescript": {
|
||||||
|
"version": "7.27.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz",
|
||||||
|
"integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-plugin-utils": "^7.27.1",
|
||||||
|
"@babel/helper-validator-option": "^7.27.1",
|
||||||
|
"@babel/plugin-syntax-jsx": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-modules-commonjs": "^7.27.1",
|
||||||
|
"@babel/plugin-transform-typescript": "^7.27.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.28.4",
|
"version": "7.28.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
|
||||||
@@ -3654,6 +3656,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@shopify/react-native-skia": {
|
||||||
|
"version": "2.2.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@shopify/react-native-skia/-/react-native-skia-2.2.19.tgz",
|
||||||
|
"integrity": "sha512-K/mui7BEPfOkC4EGLR3tGn7dUGnQQQUBlTPShelTvhCbhvBey1VMiXLJrf3uEkm0nox3PRLgPC8/WyW6kSlxUQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"canvaskit-wasm": "0.40.0",
|
||||||
|
"react-reconciler": "0.31.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"setup-skia-web": "scripts/setup-canvaskit.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=19.0",
|
||||||
|
"react-native": ">=0.78",
|
||||||
|
"react-native-reanimated": ">=3.19.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react-native": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-native-reanimated": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@sideway/address": {
|
"node_modules/@sideway/address": {
|
||||||
"version": "4.1.5",
|
"version": "4.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
|
||||||
@@ -4127,6 +4156,13 @@
|
|||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@webgpu/types": {
|
||||||
|
"version": "0.1.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.21.tgz",
|
||||||
|
"integrity": "sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.11",
|
"version": "0.8.11",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||||
@@ -5044,6 +5080,16 @@
|
|||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/canvaskit-wasm": {
|
||||||
|
"version": "0.40.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvaskit-wasm/-/canvaskit-wasm-0.40.0.tgz",
|
||||||
|
"integrity": "sha512-Od2o+ZmoEw9PBdN/yCGvzfu0WVqlufBPEWNG452wY7E9aT8RBE+ChpZF526doOlg7zumO4iCS+RAeht4P0Gbpw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@webgpu/types": "0.1.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chalk": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -13439,6 +13485,21 @@
|
|||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-fast-confetti": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-fast-confetti/-/react-native-fast-confetti-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-5kJn+dgvEoeM9h4c4SqxeqMtYAfN8QhaB1JS/5yv+AGYjVnE1a4c0AAv4yFyetwvmWQ3b16C10a8eW0FlaFAyg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"workspaces": [
|
||||||
|
"example"
|
||||||
|
],
|
||||||
|
"peerDependencies": {
|
||||||
|
"@shopify/react-native-skia": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-reanimated": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-gesture-handler": {
|
"node_modules/react-native-gesture-handler": {
|
||||||
"version": "2.28.0",
|
"version": "2.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz",
|
||||||
@@ -13556,6 +13617,36 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-reanimated": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-qzmQiFrvjm62pRBcj97QI9Xckc3EjgHQoY1F2yjktd0kpjhoyePeuTEXjYRCAVIy7IV/1cfeSup34+zFThFoHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"react-native-is-edge-to-edge": "^1.2.1",
|
||||||
|
"semver": "7.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*",
|
||||||
|
"react-native-worklets": ">=0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-reanimated/node_modules/semver": {
|
||||||
|
"version": "7.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native-safe-area-context": {
|
"node_modules/react-native-safe-area-context": {
|
||||||
"version": "5.6.1",
|
"version": "5.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.1.tgz",
|
||||||
@@ -13683,6 +13774,44 @@
|
|||||||
"@types/react": ">=16.8.24"
|
"@types/react": ">=16.8.24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native-worklets": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-yETMNuCcivdYWteuG4eRqgiAk2DzRCrVAaEBIEWPo4emrf3BNjadFo85L5QvyEusrX9QKE3ZEAx8U5A/nbyFgg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-class-properties": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-classes": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-optional-chaining": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-template-literals": "^7.0.0-0",
|
||||||
|
"@babel/plugin-transform-unicode-regex": "^7.0.0-0",
|
||||||
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
|
"convert-source-map": "^2.0.0",
|
||||||
|
"semver": "7.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.0.0-0",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native-worklets/node_modules/semver": {
|
||||||
|
"version": "7.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-native/node_modules/commander": {
|
"node_modules/react-native/node_modules/commander": {
|
||||||
"version": "12.1.0",
|
"version": "12.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
|
||||||
@@ -13704,6 +13833,29 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-reconciler": {
|
||||||
|
"version": "0.31.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz",
|
||||||
|
"integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": "^0.25.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-reconciler/node_modules/scheduler": {
|
||||||
|
"version": "0.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||||
|
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
"node_modules/react-redux": {
|
"node_modules/react-redux": {
|
||||||
"version": "9.2.0",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||||
@@ -13822,14 +13974,12 @@
|
|||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
|
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/regenerate-unicode-properties": {
|
"node_modules/regenerate-unicode-properties": {
|
||||||
"version": "10.2.2",
|
"version": "10.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
|
||||||
"integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
|
"integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerate": "^1.4.2"
|
"regenerate": "^1.4.2"
|
||||||
@@ -13869,7 +14019,6 @@
|
|||||||
"version": "6.3.1",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.3.1.tgz",
|
||||||
"integrity": "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==",
|
"integrity": "sha512-DzcswPr252wEr7Qz8AyAVbfyBDKLoYp6eRA1We2Fa9qirRFSdtkP5sHr3yglDKy2BbA0fd2T+j/CUSKes3FeVQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerate": "^1.4.2",
|
"regenerate": "^1.4.2",
|
||||||
@@ -13887,14 +14036,12 @@
|
|||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
||||||
"integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
|
"integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/regjsparser": {
|
"node_modules/regjsparser": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
|
||||||
"integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
|
"integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jsesc": "~3.0.2"
|
"jsesc": "~3.0.2"
|
||||||
@@ -13907,7 +14054,6 @@
|
|||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
|
||||||
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
|
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"jsesc": "bin/jsesc"
|
"jsesc": "bin/jsesc"
|
||||||
@@ -15185,7 +15331,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
|
||||||
"integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
|
"integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
@@ -15195,7 +15340,6 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
|
||||||
"integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
|
"integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"unicode-canonical-property-names-ecmascript": "^2.0.0",
|
"unicode-canonical-property-names-ecmascript": "^2.0.0",
|
||||||
@@ -15209,7 +15353,6 @@
|
|||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
|
||||||
"integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
|
"integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
@@ -15219,7 +15362,6 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
|
||||||
"integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
|
"integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"react-native-calendars": "^1.1313.0",
|
"react-native-calendars": "^1.1313.0",
|
||||||
"react-native-dialog": "^9.3.0",
|
"react-native-dialog": "^9.3.0",
|
||||||
"react-native-elements": "^3.4.3",
|
"react-native-elements": "^3.4.3",
|
||||||
|
"react-native-fast-confetti": "^1.1.1",
|
||||||
"react-native-image-picker": "^8.2.1",
|
"react-native-image-picker": "^8.2.1",
|
||||||
"react-native-keychain": "^10.0.0",
|
"react-native-keychain": "^10.0.0",
|
||||||
"react-native-linear-gradient": "^2.8.3",
|
"react-native-linear-gradient": "^2.8.3",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import ManageJoinedGroup from "../screens/management/ManageJoinedGroup";
|
|||||||
import InviteScreen from "../screens/InviteScreen";
|
import InviteScreen from "../screens/InviteScreen";
|
||||||
import ManagementTabNavigation from "../screens/management/ManagementTabNavigation";
|
import ManagementTabNavigation from "../screens/management/ManagementTabNavigation";
|
||||||
import TransactionsList from "../screens/transaction/TransactionList";
|
import TransactionsList from "../screens/transaction/TransactionList";
|
||||||
|
import RandomSelectUserScreen from "../screens/management/RandomSelectUserScreen";
|
||||||
|
|
||||||
export type RootStackParamList = {
|
export type RootStackParamList = {
|
||||||
Splash: undefined;
|
Splash: undefined;
|
||||||
@@ -91,6 +92,7 @@ const linking = {
|
|||||||
<Stack.Screen name="Invite" component={InviteScreen} options={{ headerShown: false }} />
|
<Stack.Screen name="Invite" component={InviteScreen} options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="ManagementTabs" component={ManagementTabNavigation} options={{ headerShown: false }} />
|
<Stack.Screen name="ManagementTabs" component={ManagementTabNavigation} options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="TransactionList" component={TransactionsList} options={{ headerShown: false }} />
|
<Stack.Screen name="TransactionList" component={TransactionsList} options={{ headerShown: false }} />
|
||||||
|
<Stack.Screen name="RandomSelectUserScreen" component={RandomSelectUserScreen} options={{ headerShown: false }} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { CompositeScreenProps } from '@react-navigation/native';
|
|||||||
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
|
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
|
||||||
import { ManagementTabParamList } from './ManagementTabNavigation'; // Adjust path
|
import { ManagementTabParamList } from './ManagementTabNavigation'; // Adjust path
|
||||||
|
|
||||||
|
|
||||||
// --- Type Definitions ---
|
// --- Type Definitions ---
|
||||||
interface Participant {
|
interface Participant {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -30,6 +31,7 @@ interface Participant {
|
|||||||
role: 'ORGANIZER' | 'USER';
|
role: 'ORGANIZER' | 'USER';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface GroupDetails {
|
export interface GroupDetails {
|
||||||
id: string;
|
id: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
@@ -53,6 +55,7 @@ export interface GroupDetails {
|
|||||||
participants: Participant[];
|
participants: Participant[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface EditGroupPayload {
|
export interface EditGroupPayload {
|
||||||
groupName: string;
|
groupName: string;
|
||||||
description: string;
|
description: string;
|
||||||
@@ -66,18 +69,22 @@ export interface EditGroupPayload {
|
|||||||
lateFeeValue: number | null;
|
lateFeeValue: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Correct props for a screen inside a nested Tab Navigator
|
// Correct props for a screen inside a nested Tab Navigator
|
||||||
type EditGroupScreenProps = CompositeScreenProps<
|
type EditGroupScreenProps = CompositeScreenProps<
|
||||||
BottomTabScreenProps<ManagementTabParamList, 'EditGroup'>,
|
BottomTabScreenProps<ManagementTabParamList, 'EditGroup'>,
|
||||||
NativeStackScreenProps<RootStackParamList>
|
NativeStackScreenProps<RootStackParamList>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|
||||||
type EditingCard = 'groupInfo' | 'financialRules' | 'configuration' | null;
|
type EditingCard = 'groupInfo' | 'financialRules' | 'configuration' | null;
|
||||||
|
|
||||||
|
|
||||||
// --- Main Screen Component ---
|
// --- Main Screen Component ---
|
||||||
const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
||||||
const { groupId } = route.params;
|
const { groupId } = route.params;
|
||||||
|
|
||||||
|
|
||||||
const [editingCard, setEditingCard] = useState<EditingCard>(null);
|
const [editingCard, setEditingCard] = useState<EditingCard>(null);
|
||||||
const [groupDetails, setGroupDetails] = useState<GroupDetails | null>(null);
|
const [groupDetails, setGroupDetails] = useState<GroupDetails | null>(null);
|
||||||
const [editableData, setEditableData] = useState<EditGroupPayload>({
|
const [editableData, setEditableData] = useState<EditGroupPayload>({
|
||||||
@@ -99,7 +106,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
const [inviting, setInviting] = useState(false);
|
const [inviting, setInviting] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
// ... (All your handler functions: initializeEditableData, fetchGroupDetails, handleCancel, handleSave, etc. remain the same)
|
|
||||||
const initializeEditableData = (data: GroupDetails) => {
|
const initializeEditableData = (data: GroupDetails) => {
|
||||||
setEditableData({
|
setEditableData({
|
||||||
groupName: data.groupName,
|
groupName: data.groupName,
|
||||||
@@ -115,6 +122,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const fetchGroupDetails = useCallback(async () => {
|
const fetchGroupDetails = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
@@ -134,10 +142,12 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
}
|
}
|
||||||
}, [groupId]);
|
}, [groupId]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchGroupDetails();
|
fetchGroupDetails();
|
||||||
}, [fetchGroupDetails]);
|
}, [fetchGroupDetails]);
|
||||||
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
if (groupDetails) {
|
if (groupDetails) {
|
||||||
initializeEditableData(groupDetails);
|
initializeEditableData(groupDetails);
|
||||||
@@ -145,9 +155,11 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
setEditingCard(null);
|
setEditingCard(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (!groupDetails) return;
|
if (!groupDetails) return;
|
||||||
|
|
||||||
|
|
||||||
if (editableData.maxParticipants < groupDetails.totalParticipants) {
|
if (editableData.maxParticipants < groupDetails.totalParticipants) {
|
||||||
Alert.alert('Invalid Input', `Max participants cannot be less than the current count (${groupDetails.totalParticipants}).`);
|
Alert.alert('Invalid Input', `Max participants cannot be less than the current count (${groupDetails.totalParticipants}).`);
|
||||||
return;
|
return;
|
||||||
@@ -166,9 +178,11 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleStartGroup = async () => {
|
const handleStartGroup = async () => {
|
||||||
if (!groupDetails) return;
|
if (!groupDetails) return;
|
||||||
|
|
||||||
|
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Confirm Start",
|
"Confirm Start",
|
||||||
"Are you sure you want to start this group? This action cannot be undone.",
|
"Are you sure you want to start this group? This action cannot be undone.",
|
||||||
@@ -200,6 +214,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
try {
|
try {
|
||||||
const response = await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/invite/generate?groupId=${groupDetails.id}`, 'GET') as { data?: string };
|
const response = await apiCall(`https://exotic-humpback-mistakenly.ngrok-free.app/api/invite/generate?groupId=${groupDetails.id}`, 'GET') as { data?: string };
|
||||||
|
|
||||||
|
|
||||||
const inviteLink = response.data;
|
const inviteLink = response.data;
|
||||||
|
|
||||||
if (inviteLink) {
|
if (inviteLink) {
|
||||||
@@ -220,12 +235,13 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleUpdateField = (field: keyof EditGroupPayload, value: any) => {
|
const handleUpdateField = (field: keyof EditGroupPayload, value: any) => {
|
||||||
setEditableData(prev => ({ ...prev, [field]: value }));
|
setEditableData(prev => ({ ...prev, [field]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
// ... (renderField and renderCard functions also remain the same)
|
|
||||||
const renderField = (field: keyof EditGroupPayload, label: string, displayValue: string | number, isEditing: boolean) => {
|
const renderField = (field: keyof EditGroupPayload, label: string, displayValue: string | number, isEditing: boolean, isEditable: boolean) => {
|
||||||
const commonInputProps = { style: styles.input, placeholderTextColor: "#9CA3AF" };
|
const commonInputProps = { style: styles.input, placeholderTextColor: "#9CA3AF" };
|
||||||
|
|
||||||
const getInputForField = () => {
|
const getInputForField = () => {
|
||||||
@@ -276,6 +292,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
const renderCard = (title: string, fields: Array<{ field: keyof EditGroupPayload, label: string, displayValue: () => string | number }>, cardKey: EditingCard) => {
|
const renderCard = (title: string, fields: Array<{ field: keyof EditGroupPayload, label: string, displayValue: () => string | number }>, cardKey: EditingCard) => {
|
||||||
const isEditingThisCard = editingCard === cardKey;
|
const isEditingThisCard = editingCard === cardKey;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<View style={styles.cardHeader}>
|
<View style={styles.cardHeader}>
|
||||||
@@ -288,7 +305,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{fields.map(({ field, label, displayValue }) =>
|
{fields.map(({ field, label, displayValue }) =>
|
||||||
renderField(field, label, displayValue(), isEditingThisCard)
|
renderField(field, label, displayValue(), isEditingThisCard, isEditable)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isEditingThisCard && (
|
{isEditingThisCard && (
|
||||||
@@ -305,16 +322,18 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color="#4F46E5" /></View>;
|
if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color="#4F46E5" /></View>;
|
||||||
if (error) return <View style={styles.centered}><Text style={styles.errorText}>{error}</Text></View>;
|
if (error) return <View style={styles.centered}><Text style={styles.errorText}>{error}</Text></View>;
|
||||||
if (!groupDetails) return null;
|
if (!groupDetails) return null;
|
||||||
|
|
||||||
|
|
||||||
const isEditable = groupDetails.status === 'PENDING' || groupDetails.status === 'APPROVED';
|
const isEditable = groupDetails.status === 'PENDING' || groupDetails.status === 'APPROVED';
|
||||||
const isInvitable = groupDetails.status === 'APPROVED';
|
const isInvitable = groupDetails.status === 'APPROVED';
|
||||||
const canStartGroup = groupDetails.status === 'APPROVED' && groupDetails.totalParticipants >= groupDetails.minParticipants;
|
const canStartGroup = groupDetails.status === 'APPROVED' && groupDetails.totalParticipants >= groupDetails.minParticipants;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Use AppView as the root container with flex: 1
|
|
||||||
<AppView style={{ flex: 1, paddingHorizontal: 0 }}>
|
<AppView style={{ flex: 1, paddingHorizontal: 0 }}>
|
||||||
<View style={styles.headerContainer}>
|
<View style={styles.headerContainer}>
|
||||||
<Text style={styles.headerTitle}>Manage Group</Text>
|
<Text style={styles.headerTitle}>Manage Group</Text>
|
||||||
@@ -325,6 +344,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
contentContainerStyle={styles.contentContainer}
|
contentContainerStyle={styles.contentContainer}
|
||||||
@@ -335,20 +355,29 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
{ field: 'description', label: 'Description', displayValue: () => groupDetails.description },
|
{ field: 'description', label: 'Description', displayValue: () => groupDetails.description },
|
||||||
], 'groupInfo')}
|
], 'groupInfo')}
|
||||||
|
|
||||||
{renderCard('Financial Rules', [
|
|
||||||
|
{groupDetails.status === 'ACTIVE' ?
|
||||||
|
renderCard('Financial Rules', [
|
||||||
|
{ field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` },
|
||||||
|
{ field: 'latePaymentCharge', label: 'Late Fee', displayValue: () => `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}` },
|
||||||
|
{ field: 'maxParticipants', label: 'Current Participants', displayValue: () => `${groupDetails.totalParticipants}` },
|
||||||
|
], 'financialRules')
|
||||||
|
:
|
||||||
|
renderCard('Financial Rules', [
|
||||||
{ field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` },
|
{ field: 'contributionAmount', label: 'Contribution', displayValue: () => `₹${groupDetails.contributionAmount.toLocaleString('en-IN')}` },
|
||||||
{ field: 'maxParticipants', label: 'Max Participants', displayValue: () => `${groupDetails.totalParticipants} / ${groupDetails.maxParticipants}` },
|
{ field: 'maxParticipants', label: 'Max Participants', displayValue: () => `${groupDetails.totalParticipants} / ${groupDetails.maxParticipants}` },
|
||||||
{ field: 'minParticipants', label: 'Min Participants', displayValue: () => `${groupDetails.minParticipants}` },
|
{ field: 'minParticipants', label: 'Min Participants', displayValue: () => `${groupDetails.minParticipants}` },
|
||||||
{ field: 'latePaymentCharge', label: 'Late Fee', displayValue: () => `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}` },
|
{ field: 'latePaymentCharge', label: 'Late Fee', displayValue: () => `₹${(groupDetails.latePaymentCharge ?? 0).toLocaleString('en-IN')}` },
|
||||||
], 'financialRules')}
|
], 'financialRules')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
{renderCard('Configuration', [
|
{renderCard('Configuration', [
|
||||||
{ field: 'cycleType', label: 'Payment Cycle', displayValue: () => groupDetails.cycleType },
|
{ field: 'cycleType', label: 'Payment Cycle', displayValue: () => groupDetails.cycleType },
|
||||||
{ field: 'customCycleDays', label: 'Custom Days', displayValue: () => `${groupDetails.customCycleDays ?? ''} days` },
|
...(groupDetails.cycleType === 'CUSTOM' ? [{ field: 'customCycleDays' as keyof EditGroupPayload, label: 'Custom Days', displayValue: () => `${groupDetails.customCycleDays} days` }] : []),
|
||||||
{ field: 'visibility', label: 'Visibility', displayValue: () => groupDetails.visibility },
|
{ field: 'visibility', label: 'Visibility', displayValue: () => groupDetails.visibility },
|
||||||
], 'configuration')}
|
], 'configuration')}
|
||||||
|
|
||||||
{/* Participants Card ... */}
|
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<Text style={styles.cardTitle}>Participants ({groupDetails.totalParticipants})</Text>
|
<Text style={styles.cardTitle}>Participants ({groupDetails.totalParticipants})</Text>
|
||||||
{groupDetails.participants.length > 0 ? (
|
{groupDetails.participants.length > 0 ? (
|
||||||
@@ -369,8 +398,7 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
{/* --- FLOATING BUTTON AREA --- */}
|
|
||||||
{/* This View is a sibling of ScrollView, allowing it to float on top */}
|
|
||||||
{canStartGroup && (
|
{canStartGroup && (
|
||||||
<View style={styles.floatingButtonContainer}>
|
<View style={styles.floatingButtonContainer}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
@@ -390,10 +418,9 @@ const EditGroupScreen: React.FC<EditGroupScreenProps> = ({ route }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Styles ---
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: { flex: 1, backgroundColor: '#F3F4F6' },
|
container: { flex: 1, backgroundColor: '#F3F4F6' },
|
||||||
// Added more paddingBottom to ensure content doesn't hide behind the button AND the tab bar
|
|
||||||
contentContainer: { padding: 16, paddingTop: 0, paddingBottom: 150 },
|
contentContainer: { padding: 16, paddingTop: 0, paddingBottom: 150 },
|
||||||
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center' },
|
errorText: { color: '#EF4444', fontSize: 16, textAlign: 'center' },
|
||||||
@@ -437,15 +464,13 @@ const styles = StyleSheet.create({
|
|||||||
cancelButton: { backgroundColor: '#E5E7EB' },
|
cancelButton: { backgroundColor: '#E5E7EB' },
|
||||||
inviteButton: { backgroundColor: colors.primary },
|
inviteButton: { backgroundColor: colors.primary },
|
||||||
buttonText: { color: '#ffffffff', fontSize: 15, fontWeight: '600' },
|
buttonText: { color: '#ffffffff', fontSize: 15, fontWeight: '600' },
|
||||||
|
|
||||||
// --- FLOATING BUTTON STYLES ---
|
|
||||||
floatingButtonContainer: {
|
floatingButtonContainer: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 85, // Positioned above the tab bar
|
bottom: 85,
|
||||||
left: 20,
|
left: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
alignItems: 'center', // Center the button horizontally
|
alignItems: 'center',
|
||||||
zIndex: 1, // Ensure it's on top
|
zIndex: 1,
|
||||||
},
|
},
|
||||||
activateButton: {
|
activateButton: {
|
||||||
backgroundColor: colors.pastelOrange,
|
backgroundColor: colors.pastelOrange,
|
||||||
@@ -457,7 +482,7 @@ const styles = StyleSheet.create({
|
|||||||
shadowOffset: { width: 0, height: 4 },
|
shadowOffset: { width: 0, height: 4 },
|
||||||
shadowOpacity: 0.3,
|
shadowOpacity: 0.3,
|
||||||
shadowRadius: 4,
|
shadowRadius: 4,
|
||||||
width: '90%', // Adjust width as needed
|
width: '90%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
activateButtonText: {
|
activateButtonText: {
|
||||||
@@ -467,4 +492,5 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export default EditGroupScreen;
|
export default EditGroupScreen;
|
||||||
|
|||||||
+143
-180
@@ -1,15 +1,15 @@
|
|||||||
import React, { useEffect, useState, useCallback } from 'react';
|
import React, { useEffect, useState, useCallback } from 'react';
|
||||||
import { View, Text, StyleSheet, ActivityIndicator, ScrollView, RefreshControl, Alert, TouchableOpacity } from 'react-native';
|
import { View, Text, StyleSheet, ActivityIndicator, ScrollView, RefreshControl, Alert, TouchableOpacity, Modal, TextInput } from 'react-native';
|
||||||
import { useRoute, useNavigation, RouteProp } from '@react-navigation/native';
|
import { useRoute, RouteProp } from '@react-navigation/native';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
|
||||||
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
import { Ionicons } from "@react-native-vector-icons/ionicons";
|
||||||
import colors from '../../components/colors';
|
import colors from '../../components/colors';
|
||||||
import AppView from '../../components/AppView';
|
import AppView from '../../components/AppView';
|
||||||
import { apiCall } from '../../utils/ApiHelper';
|
import { apiCall } from '../../utils/ApiHelper';
|
||||||
|
|
||||||
// --- TYPE DEFINITIONS ---
|
// --- CONSTANTS ---
|
||||||
|
const API_BASE_URL = 'https://exotic-humpback-mistakenly.ngrok-free.app/api/payments';
|
||||||
|
|
||||||
// Generic API Response Wrapper
|
// --- TYPE DEFINITIONS ---
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
status: 'success' | 'error';
|
status: 'success' | 'error';
|
||||||
data: T;
|
data: T;
|
||||||
@@ -17,69 +17,54 @@ interface ApiResponse<T> {
|
|||||||
statusCode: number;
|
statusCode: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type for an individual participant in the payment details
|
|
||||||
interface Participant {
|
interface Participant {
|
||||||
userId: string;
|
userId: string;
|
||||||
userName: string;
|
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
mobile: string;
|
mobile: string;
|
||||||
contributionAmount: number;
|
contributionAmount: number;
|
||||||
paidAmount: number;
|
paidAmount: number;
|
||||||
|
payAmount?: number;
|
||||||
status: 'SUCCESS' | 'PENDING';
|
status: 'SUCCESS' | 'PENDING';
|
||||||
lateFee: number;
|
lateFee: number;
|
||||||
daysLate: number;
|
|
||||||
lateFeeWaived: boolean;
|
lateFeeWaived: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fully typed payment details based on your JSON response
|
|
||||||
interface PaymentDetails {
|
interface PaymentDetails {
|
||||||
groupId: string;
|
groupId: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
currentCycle: number;
|
currentCycle: number;
|
||||||
cycleType: string;
|
|
||||||
contributionAmount: number;
|
contributionAmount: number;
|
||||||
totalContributedAmount: number;
|
|
||||||
lateFeeMode: string;
|
|
||||||
lateFeeValue: number;
|
|
||||||
notReceivedAmount: number;
|
|
||||||
totalLateFee: number;
|
|
||||||
totalDueWithLateFee: number;
|
totalDueWithLateFee: number;
|
||||||
participants: Participant[];
|
participants: Participant[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assuming your navigation stack params look something like this
|
type PaymentStackParamList = { Payments: { groupId: string } };
|
||||||
type PaymentStackParamList = {
|
|
||||||
Payments: { groupId: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
type PaymentsRouteProp = RouteProp<PaymentStackParamList, 'Payments'>;
|
type PaymentsRouteProp = RouteProp<PaymentStackParamList, 'Payments'>;
|
||||||
type PaymentsNavigationProp = StackNavigationProp<PaymentStackParamList>;
|
|
||||||
|
|
||||||
const Payments: React.FC = () => {
|
const Payments: React.FC = () => {
|
||||||
const route = useRoute<PaymentsRouteProp>();
|
const route = useRoute<PaymentsRouteProp>();
|
||||||
const navigation = useNavigation<PaymentsNavigationProp>();
|
|
||||||
const { groupId } = route.params;
|
const { groupId } = route.params;
|
||||||
|
|
||||||
const [paymentDetails, setPaymentDetails] = useState<PaymentDetails | null>(null);
|
const [paymentDetails, setPaymentDetails] = useState<PaymentDetails | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
|
||||||
|
// State for the waiver modal
|
||||||
|
const [isModalVisible, setModalVisible] = useState(false);
|
||||||
|
const [currentParticipant, setCurrentParticipant] = useState<Participant | null>(null);
|
||||||
|
const [waiverAmount, setWaiverAmount] = useState('');
|
||||||
|
|
||||||
const fetchPaymentDetails = useCallback(async () => {
|
const fetchPaymentDetails = useCallback(async () => {
|
||||||
if (!refreshing) setLoading(true);
|
if (!refreshing) setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await apiCall(
|
const response = await apiCall(`${API_BASE_URL}/current/payment/details/${groupId}`, 'GET') as ApiResponse<PaymentDetails>;
|
||||||
`https://exotic-humpback-mistakenly.ngrok-free.app/api/payments/current/payment/details/${groupId}`,
|
|
||||||
'GET'
|
|
||||||
) as ApiResponse<PaymentDetails>;
|
|
||||||
|
|
||||||
if (response && response.statusCode === 200) {
|
if (response && response.statusCode === 200) {
|
||||||
setPaymentDetails(response.data);
|
setPaymentDetails(response.data);
|
||||||
} else {
|
} else {
|
||||||
Alert.alert("Error", response?.message || "Failed to fetch payment details.");
|
Alert.alert("Error", response?.message || "Failed to fetch details.");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Fetch payment details error:", error);
|
|
||||||
Alert.alert("Error", error.message || "An unexpected error occurred.");
|
Alert.alert("Error", error.message || "An unexpected error occurred.");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -91,205 +76,183 @@ const Payments: React.FC = () => {
|
|||||||
fetchPaymentDetails();
|
fetchPaymentDetails();
|
||||||
}, [fetchPaymentDetails]);
|
}, [fetchPaymentDetails]);
|
||||||
|
|
||||||
const onRefresh = () => {
|
const onRefresh = useCallback(() => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
|
fetchPaymentDetails();
|
||||||
|
}, [fetchPaymentDetails]);
|
||||||
|
|
||||||
|
const openWaiverDialog = (participant: Participant) => {
|
||||||
|
setCurrentParticipant(participant);
|
||||||
|
setWaiverAmount('');
|
||||||
|
setModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- UPDATED: handleWaiveFee now uses the correct API endpoint ---
|
||||||
|
const handleWaiveFee = async () => {
|
||||||
|
if (!currentParticipant || !paymentDetails) return;
|
||||||
|
|
||||||
|
const amountToWaive = parseFloat(waiverAmount);
|
||||||
|
|
||||||
|
if (isNaN(amountToWaive) || amountToWaive <= 0) {
|
||||||
|
Alert.alert("Invalid Input", "Please enter a waiver amount greater than zero.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountToWaive > currentParticipant.lateFee) {
|
||||||
|
Alert.alert("Invalid Amount", `Waiver cannot exceed the current late fee of ₹${currentParticipant.lateFee}.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Construct the exact URL as provided
|
||||||
|
const url = `${API_BASE_URL}/groups/${groupId}/cycles/${paymentDetails.currentCycle}/waive/${currentParticipant.userId}?amountToWaive=${amountToWaive}`;
|
||||||
|
|
||||||
|
const response = await apiCall(url, 'PUT', {}); // Body is empty as amount is in URL
|
||||||
|
|
||||||
|
if (response && response.statusCode === 200) {
|
||||||
|
Alert.alert("Success", `₹${amountToWaive} has been waived successfully.`);
|
||||||
|
setModalVisible(false);
|
||||||
|
onRefresh(); // Refresh data to reflect the change
|
||||||
|
} else {
|
||||||
|
throw new Error(response?.message || "Failed to apply waiver.");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
Alert.alert("API Error", error.message);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- ENHANCED: renderParticipant now shows late fee details ---
|
|
||||||
const renderParticipant = (participant: Participant) => {
|
const renderParticipant = (participant: Participant) => {
|
||||||
const isPending = participant.status === 'PENDING';
|
const isPending = participant.status === 'PENDING';
|
||||||
|
const canWaive = isPending && participant.lateFee > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={participant.userId} style={styles.participantCard}>
|
<View key={participant.userId} style={styles.participantCard}>
|
||||||
<View style={styles.participantInfo}>
|
<View style={styles.participantInfo}>
|
||||||
<Text style={styles.participantName}>{participant.firstName} {participant.lastName}</Text>
|
<Text style={styles.participantName}>{participant.firstName} {participant.lastName}</Text>
|
||||||
<Text style={styles.participantMobile}>{participant.mobile}</Text>
|
<Text style={styles.participantMobile}>{participant.mobile}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.paymentStatus}>
|
<View style={styles.paymentStatus}>
|
||||||
<Text style={[
|
<Text style={[styles.statusText, { color: isPending ? colors.warning : colors.success }]}>
|
||||||
styles.statusText,
|
|
||||||
{ color: isPending ? '#e0d626ff' : '#18b107ff' }
|
|
||||||
]}>
|
|
||||||
{participant.status}
|
{participant.status}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.amountText}>
|
<Text style={styles.amountText}>
|
||||||
{isPending ?`₹${participant.payAmount}`:`₹${participant.paidAmount}`}
|
{isPending ? `₹${participant.payAmount || 0}` : `₹${participant.paidAmount}`}
|
||||||
</Text>
|
</Text>
|
||||||
|
{isPending && participant.lateFee > 0 && (
|
||||||
{/* --- NEW: Conditionally render late payment stats --- */}
|
|
||||||
{isPending && participant.daysLate > 0 && (
|
|
||||||
<View style={styles.lateFeeContainer}>
|
|
||||||
<Text style={styles.lateFeeText}>Late Fee: ₹{participant.lateFee}</Text>
|
<Text style={styles.lateFeeText}>Late Fee: ₹{participant.lateFee}</Text>
|
||||||
<Text style={styles.lateFeeText}>Days Late: {participant.daysLate}</Text>
|
)}
|
||||||
|
{participant.lateFee === 0 && participant.lateFeeWaived && <Text style={styles.waivedText}>Fee Waived</Text>}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.menuContainer}>
|
||||||
|
{canWaive && (
|
||||||
|
<TouchableOpacity onPress={() => openWaiverDialog(participant)}>
|
||||||
|
<Ionicons name="ellipsis-vertical" size={22} color="#6c757d" />
|
||||||
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading && !refreshing) {
|
|
||||||
return <ActivityIndicator style={styles.centered} size="large" color={colors.primary} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paymentDetails) {
|
|
||||||
return (
|
|
||||||
<View style={styles.centered}>
|
|
||||||
<Text>No payment details found.</Text>
|
|
||||||
<TouchableOpacity onPress={fetchPaymentDetails} style={styles.retryButton}>
|
|
||||||
<Text style={styles.retryButtonText}>Tap to retry</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppView paddingHorizontal={0}>
|
<AppView paddingHorizontal={0}>
|
||||||
|
<Modal
|
||||||
|
transparent={true}
|
||||||
|
visible={isModalVisible}
|
||||||
|
animationType="fade"
|
||||||
|
onRequestClose={() => setModalVisible(false)}
|
||||||
|
>
|
||||||
|
<View style={styles.modalOverlay}>
|
||||||
|
<View style={styles.modalContainer}>
|
||||||
|
<Text style={styles.modalTitle}>Waive Late Fee</Text>
|
||||||
|
<Text style={styles.modalSubtitle}>
|
||||||
|
For {currentParticipant?.firstName}. Current late fee is ₹{currentParticipant?.lateFee}.
|
||||||
|
</Text>
|
||||||
|
<TextInput
|
||||||
|
style={styles.input}
|
||||||
|
keyboardType="numeric"
|
||||||
|
value={waiverAmount}
|
||||||
|
onChangeText={setWaiverAmount}
|
||||||
|
placeholder="Enter amount to waive"
|
||||||
|
/>
|
||||||
|
<View style={styles.modalButtonContainer}>
|
||||||
|
<TouchableOpacity style={[styles.modalButton, styles.cancelButton]} onPress={() => setModalVisible(false)}>
|
||||||
|
<Text style={styles.cancelButtonText}>Cancel</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={[styles.modalButton, styles.saveButton]} onPress={handleWaiveFee}>
|
||||||
|
<Text style={styles.saveButtonText}>Apply Waiver</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={styles.container}
|
style={styles.container}
|
||||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} colors={[colors.primary]} tintColor={colors.primary} />}
|
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||||
>
|
>
|
||||||
|
{loading && !refreshing ? (
|
||||||
|
<ActivityIndicator style={{marginTop: 50}} size="large" color={colors.primary} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
<View style={styles.header}>
|
<View style={styles.header}>
|
||||||
<Text style={styles.headerTitle}>{paymentDetails.groupName}</Text>
|
<Text style={styles.headerTitle}>{paymentDetails?.groupName}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.summaryContainer}>
|
<View style={styles.summaryContainer}>
|
||||||
<View style={styles.summaryBox}>
|
<View style={styles.summaryBox}>
|
||||||
<Text style={styles.summaryLabel}>Current Cycle</Text>
|
<Text style={styles.summaryLabel}>Cycle</Text>
|
||||||
<Text style={styles.summaryValue}>{paymentDetails.currentCycle}</Text>
|
<Text style={styles.summaryValue}>{paymentDetails?.currentCycle}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.summaryBox}>
|
<View style={styles.summaryBox}>
|
||||||
<Text style={styles.summaryLabel}>Contribution</Text>
|
<Text style={styles.summaryLabel}>Contribution</Text>
|
||||||
<Text style={styles.summaryValue}>₹{paymentDetails.contributionAmount}</Text>
|
<Text style={styles.summaryValue}>₹{paymentDetails?.contributionAmount}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.summaryBox}>
|
<View style={styles.summaryBox}>
|
||||||
<Text style={styles.summaryLabel}>Total Due</Text>
|
<Text style={styles.summaryLabel}>Total Due</Text>
|
||||||
<Text style={[styles.summaryValue, { color: colors.danger }]}>₹{paymentDetails.totalDueWithLateFee}</Text>
|
<Text style={[styles.summaryValue, { color: colors.danger }]}>₹{paymentDetails?.totalDueWithLateFee}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Text style={styles.sectionTitle}>Participants</Text>
|
<Text style={styles.sectionTitle}>Participants</Text>
|
||||||
{paymentDetails.participants.map(renderParticipant)}
|
{paymentDetails?.participants.map(renderParticipant)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppView>
|
</AppView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
centered: {
|
container: { flex: 1, backgroundColor: '#f4f6f8' },
|
||||||
flex: 1,
|
header: { padding: 20, paddingBottom: 15 },
|
||||||
justifyContent: 'center',
|
headerTitle: { color: '#000000', fontSize: 24, fontWeight: 'bold' },
|
||||||
alignItems: 'center',
|
summaryContainer: { flexDirection: 'row', justifyContent: 'space-around', padding: 20, backgroundColor: '#fff', marginHorizontal: 15, borderRadius: 12, elevation: 2 },
|
||||||
backgroundColor: '#f4f6f8',
|
summaryBox: { alignItems: 'center' },
|
||||||
},
|
summaryLabel: { fontSize: 14, color: '#6c757d', marginBottom: 4 },
|
||||||
container: {
|
summaryValue: { fontSize: 18, fontWeight: '700', color: colors.primary },
|
||||||
flex: 1,
|
sectionTitle: { fontSize: 20, fontWeight: 'bold', marginLeft: 20, marginTop: 20, marginBottom: 10 },
|
||||||
backgroundColor: '#f4f6f8',
|
participantCard: { backgroundColor: '#fff', marginHorizontal: 15, marginBottom: 10, borderRadius: 10, padding: 15, flexDirection: 'row', alignItems: 'center', elevation: 1 },
|
||||||
},
|
participantInfo: { flex: 1 },
|
||||||
header: {
|
participantName: { fontSize: 16, fontWeight: '600' },
|
||||||
padding: 20,
|
participantMobile: { fontSize: 14, color: '#6c757d', marginTop: 2 },
|
||||||
paddingBottom: 15,
|
paymentStatus: { alignItems: 'flex-end', marginHorizontal: 10 },
|
||||||
},
|
statusText: { fontSize: 14, fontWeight: 'bold', textTransform: 'uppercase' },
|
||||||
headerTitle: {
|
amountText: { fontSize: 14, color: '#495057', marginTop: 2 },
|
||||||
color: '#000000',
|
lateFeeText: { fontSize: 13, color: colors.danger, fontWeight: '600', marginTop: 4 },
|
||||||
fontSize: 24,
|
waivedText: { fontSize: 13, color: colors.success, fontWeight: '600', marginTop: 4 },
|
||||||
fontWeight: 'bold',
|
menuContainer: { justifyContent: 'center', alignItems: 'center', minWidth: 25 },
|
||||||
},
|
modalOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center' },
|
||||||
summaryContainer: {
|
modalContainer: { width: '85%', backgroundColor: 'white', borderRadius: 15, padding: 20, elevation: 10 },
|
||||||
flexDirection: 'row',
|
modalTitle: { fontSize: 20, fontWeight: 'bold', color: '#333', marginBottom: 5 },
|
||||||
justifyContent: 'space-around',
|
modalSubtitle: { fontSize: 14, color: '#666', marginBottom: 20 },
|
||||||
padding: 20,
|
input: { width: '100%', borderWidth: 1, borderColor: '#ddd', borderRadius: 8, paddingHorizontal: 15, paddingVertical: 12, fontSize: 16, marginBottom: 20 },
|
||||||
backgroundColor: '#fff',
|
modalButtonContainer: { flexDirection: 'row', justifyContent: 'flex-end' },
|
||||||
marginHorizontal: 15,
|
modalButton: { paddingVertical: 10, paddingHorizontal: 20, borderRadius: 8, marginLeft: 10 },
|
||||||
borderRadius: 12,
|
cancelButton: { backgroundColor: '#f0f0f0' },
|
||||||
elevation: 2,
|
cancelButtonText: { color: '#333', fontWeight: 'bold' },
|
||||||
shadowColor: '#000',
|
saveButton: { backgroundColor: colors.primary },
|
||||||
shadowOpacity: 0.05,
|
saveButtonText: { color: 'white', fontWeight: 'bold' },
|
||||||
shadowRadius: 10,
|
|
||||||
},
|
|
||||||
summaryBox: {
|
|
||||||
alignItems: 'center',
|
|
||||||
},
|
|
||||||
summaryLabel: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#6c757d',
|
|
||||||
marginBottom: 4,
|
|
||||||
},
|
|
||||||
summaryValue: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: '700',
|
|
||||||
color: colors.primary,
|
|
||||||
},
|
|
||||||
sectionTitle: {
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
marginLeft: 20,
|
|
||||||
marginTop: 20,
|
|
||||||
marginBottom: 10,
|
|
||||||
color: '#343a40',
|
|
||||||
},
|
|
||||||
participantCard: {
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
marginHorizontal: 15,
|
|
||||||
marginBottom: 10,
|
|
||||||
borderRadius: 10,
|
|
||||||
padding: 15,
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
elevation: 1,
|
|
||||||
},
|
|
||||||
participantInfo: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
participantName: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: '600',
|
|
||||||
color: '#212529',
|
|
||||||
},
|
|
||||||
participantMobile: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#6c757d',
|
|
||||||
marginTop: 2,
|
|
||||||
},
|
|
||||||
paymentStatus: {
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
},
|
|
||||||
statusText: {
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
textTransform: 'uppercase',
|
|
||||||
},
|
|
||||||
amountText: {
|
|
||||||
fontSize: 14,
|
|
||||||
color: '#495057',
|
|
||||||
marginTop: 2,
|
|
||||||
},
|
|
||||||
// --- NEW STYLES ---
|
|
||||||
lateFeeContainer: {
|
|
||||||
marginTop: 8,
|
|
||||||
paddingTop: 5,
|
|
||||||
borderTopWidth: 1,
|
|
||||||
borderTopColor: '#e9ecef',
|
|
||||||
alignItems: 'flex-end',
|
|
||||||
},
|
|
||||||
lateFeeText: {
|
|
||||||
fontSize: 13,
|
|
||||||
color: colors.danger,
|
|
||||||
fontWeight: '600',
|
|
||||||
},
|
|
||||||
retryButton: {
|
|
||||||
marginTop: 20,
|
|
||||||
paddingVertical: 10,
|
|
||||||
paddingHorizontal: 20,
|
|
||||||
borderRadius: 8,
|
|
||||||
backgroundColor: colors.primary,
|
|
||||||
},
|
|
||||||
retryButtonText: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Payments;
|
export default Payments;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import { View, Text, StyleSheet, TouchableOpacity, Animated, Easing, SafeAreaView, Dimensions, Alert } from 'react-native';
|
||||||
|
import { Confetti, ConfettiMethods } from 'react-native-fast-confetti';
|
||||||
|
|
||||||
|
import AppView from '../../components/AppView';
|
||||||
|
|
||||||
|
|
||||||
|
// --- PROPS & TYPE DEFINITIONS ---
|
||||||
|
export interface RandomWinnerScreenProps {
|
||||||
|
participants: Participant[];
|
||||||
|
excludedParticipantIds: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Participant {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CONSTANTS ---
|
||||||
|
const { width } = Dimensions.get('window');
|
||||||
|
const ITEM_HEIGHT = 70; // Height of each name item
|
||||||
|
const ANIMATION_DURATION = 8000; // 8 seconds for suspense
|
||||||
|
const SHUFFLE_MULTIPLIER = 5; // Repeats the list for a longer scroll effect
|
||||||
|
|
||||||
|
const RandomSelectUserScreen: React.FC = () => {
|
||||||
|
// --- MOCK DATA (Replace with navigation props) ---
|
||||||
|
const mockProps: RandomWinnerScreenProps = {
|
||||||
|
participants: [
|
||||||
|
{ id: '1', name: 'Vinay M.' },
|
||||||
|
{ id: '2', name: 'Nitesh Y.' },
|
||||||
|
{ id: '3', name: 'Alice' },
|
||||||
|
{ id: '4', name: 'Bob' },
|
||||||
|
{ id: '5', name: 'Charlie' },
|
||||||
|
{ id: '6', name: 'David' },
|
||||||
|
{ id: '7', name: 'Eve' },
|
||||||
|
],
|
||||||
|
excludedParticipantIds: ['4'], // Exclude Bob
|
||||||
|
};
|
||||||
|
// --- END MOCK DATA ---
|
||||||
|
|
||||||
|
const { participants, excludedParticipantIds } = mockProps;
|
||||||
|
|
||||||
|
const [eligibleParticipants, setEligibleParticipants] = useState<Participant[]>([]);
|
||||||
|
const [isSelecting, setIsSelecting] = useState(false);
|
||||||
|
const [winner, setWinner] = useState<Participant | null>(null);
|
||||||
|
const [animationList, setAnimationList] = useState<Participant[]>([]);
|
||||||
|
|
||||||
|
const scrollY = useRef(new Animated.Value(0)).current;
|
||||||
|
const confettiRef = useRef<ConfettiMethods>(null);
|
||||||
|
|
||||||
|
// Filter participants
|
||||||
|
useEffect(() => {
|
||||||
|
const eligible = participants.filter(p => !excludedParticipantIds.includes(p.id));
|
||||||
|
setEligibleParticipants(eligible);
|
||||||
|
}, [participants, excludedParticipantIds]);
|
||||||
|
|
||||||
|
const handleStartSelection = () => {
|
||||||
|
if (eligibleParticipants.length < 2) {
|
||||||
|
Alert.alert('Not enough eligible participants.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSelecting(true);
|
||||||
|
setWinner(null);
|
||||||
|
scrollY.setValue(0);
|
||||||
|
|
||||||
|
// 1. Secretly select winner
|
||||||
|
const winnerIndex = Math.floor(Math.random() * eligibleParticipants.length);
|
||||||
|
const selectedWinner = eligibleParticipants[winnerIndex];
|
||||||
|
|
||||||
|
// 2. Create long, shuffled list for animation
|
||||||
|
let listForAnimation: Participant[] = [];
|
||||||
|
for (let i = 0; i < SHUFFLE_MULTIPLIER; i++) {
|
||||||
|
const shuffled = [...eligibleParticipants].sort(() => Math.random() - 0.5);
|
||||||
|
listForAnimation.push(...shuffled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Place winner at the end
|
||||||
|
listForAnimation.push(selectedWinner);
|
||||||
|
setAnimationList(listForAnimation);
|
||||||
|
|
||||||
|
// 4. Calculate final scroll position
|
||||||
|
const finalY = -(listForAnimation.length - 1) * ITEM_HEIGHT;
|
||||||
|
|
||||||
|
// 5. Start animation
|
||||||
|
Animated.timing(scrollY, {
|
||||||
|
toValue: finalY,
|
||||||
|
duration: ANIMATION_DURATION,
|
||||||
|
useNativeDriver: true,
|
||||||
|
easing: Easing.bezier(0.3, 0, 0.1, 1),
|
||||||
|
}).start(() => {
|
||||||
|
setWinner(selectedWinner);
|
||||||
|
confettiRef.current?.start();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setIsSelecting(false);
|
||||||
|
setWinner(null);
|
||||||
|
scrollY.setValue(0);
|
||||||
|
confettiRef.current?.stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderListItem = ({ item, index }: { item: Participant, index: number }) => (
|
||||||
|
<View style={styles.itemContainer} key={`${item.id}-${index}`}>
|
||||||
|
<Text style={styles.itemName}>{item.name}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppView>
|
||||||
|
<Confetti ref={confettiRef} />
|
||||||
|
|
||||||
|
<Text style={styles.title}>Select a Winner</Text>
|
||||||
|
|
||||||
|
<View style={styles.selectionMachine}>
|
||||||
|
<View style={styles.winnerPointer} />
|
||||||
|
{isSelecting || winner ? (
|
||||||
|
<Animated.View style={{ transform: [{ translateY: scrollY }] }}>
|
||||||
|
{animationList.map((item, index) => renderListItem({ item, index }))}
|
||||||
|
</Animated.View>
|
||||||
|
) : (
|
||||||
|
<View style={styles.placeholderContainer}>
|
||||||
|
<Text style={styles.placeholderText}>Press Start to Select</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{winner && (
|
||||||
|
<View style={styles.winnerBanner}>
|
||||||
|
<Text style={styles.winnerText}>Winner is</Text>
|
||||||
|
<Text style={styles.winnerName}>{winner.name}!</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSelecting && !winner && (
|
||||||
|
<TouchableOpacity style={styles.startButton} onPress={handleStartSelection}>
|
||||||
|
<Text style={styles.startButtonText}>Start Selection</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{winner && (
|
||||||
|
<TouchableOpacity style={styles.resetButton} onPress={handleReset}>
|
||||||
|
<Text style={styles.resetButtonText}>Select Again</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</AppView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#1C1C1E',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingVertical: 40,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#FFFFFF',
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: 30,
|
||||||
|
},
|
||||||
|
selectionMachine: {
|
||||||
|
height: ITEM_HEIGHT,
|
||||||
|
width: '90%',
|
||||||
|
backgroundColor: '#2C2C2E',
|
||||||
|
borderRadius: 20,
|
||||||
|
overflow: 'hidden',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
winnerPointer: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: '105%',
|
||||||
|
height: ITEM_HEIGHT,
|
||||||
|
borderRadius: 20,
|
||||||
|
borderWidth: 3,
|
||||||
|
borderColor: '#FBC02D',
|
||||||
|
alignSelf: 'center',
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
itemContainer: {
|
||||||
|
height: ITEM_HEIGHT,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
itemName: {
|
||||||
|
fontSize: 36,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#FFF',
|
||||||
|
},
|
||||||
|
placeholderContainer: {
|
||||||
|
height: ITEM_HEIGHT,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
placeholderText: {
|
||||||
|
fontSize: 24,
|
||||||
|
color: '#666',
|
||||||
|
fontStyle: 'italic',
|
||||||
|
},
|
||||||
|
winnerBanner: {
|
||||||
|
marginTop: 30,
|
||||||
|
padding: 20,
|
||||||
|
backgroundColor: '#FBC02D',
|
||||||
|
borderRadius: 15,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
winnerText: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
winnerName: {
|
||||||
|
fontSize: 40,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
startButton: {
|
||||||
|
backgroundColor: '#007AFF',
|
||||||
|
paddingVertical: 18,
|
||||||
|
paddingHorizontal: 40,
|
||||||
|
borderRadius: 30,
|
||||||
|
marginTop: 40,
|
||||||
|
},
|
||||||
|
startButtonText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
resetButton: {
|
||||||
|
backgroundColor: '#333',
|
||||||
|
paddingVertical: 18,
|
||||||
|
paddingHorizontal: 40,
|
||||||
|
borderRadius: 30,
|
||||||
|
marginTop: 40,
|
||||||
|
},
|
||||||
|
resetButtonText: {
|
||||||
|
color: '#FFFFFF',
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RandomSelectUserScreen;
|
||||||
|
|
||||||
Reference in New Issue
Block a user