瀏覽代碼

fix: 增加pwa以及修改充值页面

Before 8 月之前
父節點
當前提交
ae6f6cc843

+ 13 - 1
.env.development

@@ -1,3 +1,15 @@
+# 环境
+#baseurl
 NEXT_PUBLIC_BASE_URL=http://192.168.0.84:8800
-
+#share link
 NEXT_PUBLIC_SHARE_URL=http://192.168.0.84:3000
+
+#firebase
+NEXT_PUBLIC_FIREBASE_APIKEY=AIzaSyDAWORGKhdyzb5KeqTi535VmD5gN2Cdle8
+NEXT_PUBLIC_FIREBASE_AUTHDOMAIN=bcwin-a99b1.firebaseapp.com
+NEXT_PUBLIC_FIREBASE_PROJECTID=bcwin-a99b1
+NEXT_PUBLIC_FIREBASE_STORAGEBUCKET=bcwin-a99b1.appspot.com
+NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID=1055413612814
+NEXT_PUBLIC_FIREBASE_APPID=1:1055413612814:web:7a563237de8e43849d275f
+NEXT_PUBLIC_FIREBASE_MEASUREMENTID=G-467M2BYJMS
+NEXT_PUBLIC_FIREBASE_KEYS=BAOsT7kii-ctLzGrgXe_wYhfuxlme1v4njnD0uPSKp3DpSnrUa2e709b9dRaeYVU7jF_qIx1y9tEv0CvilDCdnM

+ 12 - 1
.env.production

@@ -1,3 +1,14 @@
+#baseurl
 NEXT_PUBLIC_BASE_URL=https://api.bcwin777.bet
-
+#share link
 NEXT_PUBLIC_SHARE_URL=https://bcwin777.bet
+
+#firebase
+NEXT_PUBLIC_FIREBASE_APIKEY=AIzaSyDAWORGKhdyzb5KeqTi535VmD5gN2Cdle8
+NEXT_PUBLIC_FIREBASE_AUTHDOMAIN=bcwin-a99b1.firebaseapp.com
+NEXT_PUBLIC_FIREBASE_PROJECTID=bcwin-a99b1
+NEXT_PUBLIC_FIREBASE_STORAGEBUCKET=bcwin-a99b1.appspot.com
+NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID=1055413612814
+NEXT_PUBLIC_FIREBASE_APPID=1:1055413612814:web:7a563237de8e43849d275f
+NEXT_PUBLIC_FIREBASE_MEASUREMENTID=G-467M2BYJMS
+NSXT_PUBLIC_FIREBASE_KEYS=BAOsT7kii-ctLzGrgXe_wYhfuxlme1v4njnD0uPSKp3DpSnrUa2e709b9dRaeYVU7jF_qIx1y9tEv0CvilDCdnM

+ 12 - 1
.env.test

@@ -1,3 +1,14 @@
+#baseurl
 NEXT_PUBLIC_BASE_URL=http://192.168.0.84:8800
-
+#sharelink
 NEXT_PUBLIC_SHARE_URL=http://192.168.0.84:3000
+#firebase
+NEXT_PUBLIC_FIREBASE_APIKEY=AIzaSyDAWORGKhdyzb5KeqTi535VmD5gN2Cdle8
+NEXT_PUBLIC_FIREBASE_AUTHDOMAIN=bcwin-a99b1.firebaseapp.com
+NEXT_PUBLIC_FIREBASE_PROJECTID=bcwin-a99b1
+NEXT_PUBLIC_FIREBASE_STORAGEBUCKET=bcwin-a99b1.appspot.com
+NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID=1055413612814
+NEXT_PUBLIC_FIREBASE_APPID=1:1055413612814:web:7a563237de8e43849d275f
+NEXT_PUBLIC_FIREBASE_MEASUREMENTID=G-467M2BYJMS
+NSXT_PUBLIC_FIREBASE_KEYS=BAOsT7kii-ctLzGrgXe_wYhfuxlme1v4njnD0uPSKp3DpSnrUa2e709b9dRaeYVU7jF_qIx1y9tEv0CvilDCdnM
+

+ 11 - 1
.env.uat

@@ -1,3 +1,13 @@
+#baseurl
 NEXT_PUBLIC_BASE_URL=http://206.168.191.124:8800
-
+#sharelink
 NEXT_PUBLIC_SHARE_URL=http://206.168.191.124:3000
+#firebase
+NEXT_PUBLIC_FIREBASE_APIKEY=AIzaSyDAWORGKhdyzb5KeqTi535VmD5gN2Cdle8
+NEXT_PUBLIC_FIREBASE_AUTHDOMAIN=bcwin-a99b1.firebaseapp.com
+NEXT_PUBLIC_FIREBASE_PROJECTID=bcwin-a99b1
+NEXT_PUBLIC_FIREBASE_STORAGEBUCKET=bcwin-a99b1.appspot.com
+NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID=1055413612814
+NEXT_PUBLIC_FIREBASE_APPID=1:1055413612814:web:7a563237de8e43849d275f
+NEXT_PUBLIC_FIREBASE_MEASUREMENTID=G-467M2BYJMS
+NSXT_PUBLIC_FIREBASE_KEYS=BAOsT7kii-ctLzGrgXe_wYhfuxlme1v4njnD0uPSKp3DpSnrUa2e709b9dRaeYVU7jF_qIx1y9tEv0CvilDCdnM

+ 21 - 0
firebase-debug.log

@@ -0,0 +1,21 @@
+[debug] [2024-10-24T05:55:58.547Z] ----------------------------------------------------------------------
+[debug] [2024-10-24T05:55:58.548Z] Command:       D:\apps\nodejs\node.exe D:\apps\nodejs\node_global\node_modules\firebase-tools\lib\bin\firebase.js init
+[debug] [2024-10-24T05:55:58.549Z] CLI Version:   13.23.0
+[debug] [2024-10-24T05:55:58.549Z] Platform:      win32
+[debug] [2024-10-24T05:55:58.549Z] Node Version:  v18.20.4
+[debug] [2024-10-24T05:55:58.549Z] Time:          Thu Oct 24 2024 13:55:58 GMT+0800 (中国标准时间)
+[debug] [2024-10-24T05:55:58.549Z] ----------------------------------------------------------------------
+[debug] 
+[debug] [2024-10-24T05:55:58.553Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
+[debug] [2024-10-24T05:55:58.553Z] > authorizing via signed-in user (834299862@qq.com)
+[info] 
+     ######## #### ########  ######## ########     ###     ######  ########
+     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
+     ######    ##  ########  ######   ########  #########  ######  ######
+     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
+     ##       #### ##     ## ######## ########  ##     ##  ######  ########
+
+You're about to initialize a Firebase project in this directory:
+
+  D:\project\game-h5
+

+ 4 - 2
messages/br.json

@@ -142,7 +142,7 @@
     "Oferecer": "Oferecer",
     "DepositarAgora": "Depositar Agora",
     "RechargeSuc": "充值成功",
-    "depositTips": "We give two percent of each charge to help the power stations"
+    "depositTips": "We give 2% of each charge to help the power stations"
   },
   "WithdrawPage": {
     "Certifique": "Certifique-se de que o CPF está em seu nome para evitar atrasos.",
@@ -355,7 +355,9 @@
     "emailReg": "The E-mail is malformed",
     "username": "Name",
     "usernameReg": "The Name cannot be empty",
-
+    "amount": "The Amount cannot be empty",
+    "amountMaxReg": "Maximum amount {amount}",
+    "amountMinReg": "Minimum amount {amount}",
     "card": "CPF Number",
     "cardReg": "The CPF is malformed",
     "address": "Address",

+ 5 - 3
messages/en.json

@@ -142,7 +142,7 @@
     "Oferecer": "Oferecer",
     "DepositarAgora": "Depositar Agora",
     "RechargeSuc": "充值成功",
-    "depositTips": "We give two percent of each charge to help the power stations"
+    "depositTips": "We give 2% of each charge to help the power stations"
   },
   "WithdrawPage": {
     "Certifique": "Certifique-se de que o CPF está em seu nome para evitar atrasos.",
@@ -355,7 +355,9 @@
     "emailReg": "The E-mail is malformed",
     "username": "Name",
     "usernameReg": "The Name cannot be empty",
-
+    "amount": "The Amount cannot be empty",
+    "amountMaxReg": "Maximum amount {amount}",
+    "amountMinReg": "Minimum amount {amount}",
     "card": "CPF Number",
     "cardReg": "The CPF is malformed",
     "address": "Address",
@@ -1012,7 +1014,7 @@
         ],
         "tails": []
       }
-      ]
+    ]
   },
 
   "policy": {

+ 51 - 7
messages/es.json

@@ -84,7 +84,10 @@
     "registerGoogletext": "Registre-se com Google+",
     "domainName": "© BCWIN777.bet todos direitos reservados",
     "childTips": "This product is intended for users over the age of 18"
-
+  },
+  "confirmPwdPage": {
+    "title": "Change the Contrasinha",
+    "complete": "Complete"
   },
   "ResetPhonePage": {
     "enterCorrectphone":"Please enter a valid mobile phone numbe",
@@ -139,7 +142,7 @@
     "Oferecer": "Oferecer",
     "DepositarAgora": "Depositar Agora",
     "RechargeSuc": "充值成功",
-    "depositTips": "We give two percent of each charge to help the power stations"
+    "depositTips": "We give 2% of each charge to help the power stations"
   },
   "WithdrawPage": {
     "Certifique": "Certifique-se de que o CPF está em seu nome para evitar atrasos.",
@@ -194,6 +197,7 @@
     "R$":"R$",
     "TotalPago":"Total pago",
     "Não":"Não pago",
+    "receive": "don't receive",
     "TRANSFERIR":"TRANSFERIR PARA A CARTEIRA",
     "Valor":"Valor mínimo de transferência de  10 BRL",
     "title1":"Compartilhe com sua comunidade social",
@@ -293,18 +297,56 @@
   },
   "code": {
     "200": "successful",
-    "1005": "Invalid username or password.",
+    "400": "Parameter error",
+    "401": "privilege grant failed",
+    "403":"Request Rejected",
+    "500": "System error",
+    "800":"Resource locking in progress",
+    "1000": "unknown error",
+    "1001":"The resource already exists",
+    "1002": "The resource does not exist",
+    "1003":"Business logic does not pass",
+    "1004": "Status code error",
+    "1005": "Wrong password",
+    "1006": "Insufficient inventory prevents purchasing",
+    "1007":"SKU resource does not exist",
+    "1008": "Phone number is incorrectly formatted",
     "1009": "There is no such user",
-    "1010": "The phone number is already connected to another client.",
-    "1008": "Phone number is incorrectly formatted"
+    "1010": "The user is registered",
+    "1011":"Too many errors are prohibited",
+    "1012": "User banned",
+    "1013": "Password conflicts",
+    "1014": "The verification code is incorrect",
+    "1015": "The balance is insufficient",
+    "1016": "Mismatched identity information",
+    "1017": "The invitation code is incorrect",
+    "1018": "The withdrawal channel is closed",
+    "1200":"Insufficient inventory",
+    "1201": "",
+    "1202":"",
+    "1300": "",
+    "1301":"",
+    "1400": "",
+    "1401":"",
+    "1402": "",
+    "1403":"",
+    "1404": "",
+    "1405":"",
+    "1406": "",
+    "1407":"",
+    "1501": "",
+    "1502":"",
+    "1503": ""
   },
-  "这是占位符": "",
   "form": {
     "phone":"Phone Number",
     "phoneReg": "The phone number cannot be empty",
     "phoneMinReg": "Please enter the correct phone number",
     "password": "Password",
     "passwordReg": "The password cannot be empty",
+    "newPwd": "Please enter a new password",
+    "checkPwd": "Please enter the new password again",
+    "checkPwdReg": "The password is inconsistent twice",
     "passwordMinReg": "The password cannot be less than 6 digits and more than 20 digits",
     "birthday": "Birthday",
     "birthdayReg": "The birthday cannot be empty",
@@ -313,7 +355,9 @@
     "emailReg": "The E-mail is malformed",
     "username": "Name",
     "usernameReg": "The Name cannot be empty",
-
+    "amount": "The Amount cannot be empty",
+    "amountMaxReg": "Maximum amount {amount}",
+    "amountMinReg": "Minimum amount {amount}",
     "card": "CPF Number",
     "cardReg": "The CPF is malformed",
     "address": "Address",

+ 1 - 4
package.json

@@ -4,19 +4,15 @@
   "private": true,
   "scripts": {
     "dev": "env-cmd -f .env.development next dev",
-
     "dev:prod": "env-cmd -f .env.production next dev",
     "build:prod": "env-cmd -f .env.production next build",
     "start:prod": "env-cmd -f .env.production next start",
-
     "dev:test": "env-cmd -f .env.test  next dev",
     "build:test": "env-cmd -f .env.test next build",
     "start:test": "env-cmd -f .env.test next start",
-
     "dev:uat": "env-cmd -f .env.uat next dev",
     "build:uat": "env-cmd -f .env.uat next build",
     "start:uat": "env-cmd -f .env.uat next start",
-
     "build": "env-cmd -f .env.production next build",
     "lint": "next lint",
     "preinstall": "npx only-allow pnpm"
@@ -28,6 +24,7 @@
     "clsx": "^2.1.1",
     "dayjs": "^1.11.11",
     "env-cmd": "^10.1.0",
+    "firebase": "^11.0.1",
     "framer-motion": "^11.3.2",
     "js-cookie": "^3.0.5",
     "next": "14.2.4",

+ 762 - 0
pnpm-lock.yaml

@@ -26,6 +26,9 @@ importers:
       env-cmd:
         specifier: ^10.1.0
         version: 10.1.0
+      firebase:
+        specifier: ^11.0.1
+        version: 11.0.1
       framer-motion:
         specifier: ^11.3.2
         version: 11.3.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -131,6 +134,216 @@ packages:
     resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
 
+  '@firebase/analytics-compat@0.2.15':
+    resolution: {integrity: sha512-C5to422Sr8FkL0MPwXcIecbMnF4o2Ll7MtoWvIm4Q/LPJvvM+tWa1DiU+LzsCdsd1/CYE9EIW9Ma3ko9XnAAYw==}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/analytics-types@0.8.2':
+    resolution: {integrity: sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==}
+
+  '@firebase/analytics@0.10.9':
+    resolution: {integrity: sha512-FrvW6u6xDBKXUGYUy1WIUh0J9tvbppMsk90mig0JhHST8iLveKu/dIBVeVE/ZYZhmXy4fkI7SPSWvD1V0O4tXw==}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/app-check-compat@0.3.16':
+    resolution: {integrity: sha512-AxIGzLRXrTFNL+H6V+4BO0w/gERloROfRbWI/FoJUnQd0qPZIzyfdHZBbThFzFGLfDt/mVs2kdjYFx/l9I8NhQ==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/app-check-interop-types@0.3.2':
+    resolution: {integrity: sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==}
+
+  '@firebase/app-check-types@0.5.2':
+    resolution: {integrity: sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==}
+
+  '@firebase/app-check@0.8.9':
+    resolution: {integrity: sha512-YzVn1mMLzD2JboMPVVO0Pe20YOgWzrF+aXoAmmd0v3xec051n83YpxSUZbacL69uYvk0dHrEsbea44QtQ5WPDA==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/app-compat@0.2.45':
+    resolution: {integrity: sha512-5rYbXq1ndtMTg+07oH4WrkYuP+NZq61uzVwW1hlmybp/gr4cXq2SfaP9fc6/9IzTKmu3dh3H0fjj++HG7Z7o/w==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/app-types@0.9.2':
+    resolution: {integrity: sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==}
+
+  '@firebase/app@0.10.15':
+    resolution: {integrity: sha512-he6qlG3pmwL+LHdG/BrSMBQeJzzutciq4fpXN3lGa1uSwYSijJ24VtakS/bP2X9SiDf8jGywJ4u+OgXAenJsNg==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/auth-compat@0.5.15':
+    resolution: {integrity: sha512-jz6k1ridPiecKI8CBRiqCM6IMOhwYp2MD+YvoxnMiK8nQLSTm57GvHETlPNX3WlbyQnCjMCOvrAhe27whyxAEg==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/auth-interop-types@0.2.3':
+    resolution: {integrity: sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==}
+
+  '@firebase/auth-types@0.12.2':
+    resolution: {integrity: sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==}
+    peerDependencies:
+      '@firebase/app-types': 0.x
+      '@firebase/util': 1.x
+
+  '@firebase/auth@1.8.0':
+    resolution: {integrity: sha512-/O7UDWE5S5ux456fzNHSLx/0YN/Kykw/WyAzgDQ6wvkddZhSEmPX19EzxgsFldzhuFjsl5uOZTz8kzlosCiJjg==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app': 0.x
+      '@react-native-async-storage/async-storage': ^1.18.1
+    peerDependenciesMeta:
+      '@react-native-async-storage/async-storage':
+        optional: true
+
+  '@firebase/component@0.6.10':
+    resolution: {integrity: sha512-OsNbEKyz9iLZSmMUhsl6+kCADzte00iisJIRUspnUqvDCX+RSGZOBIqekukv/jN177ovjApBQNFaxSYIDc/SyQ==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/data-connect@0.1.1':
+    resolution: {integrity: sha512-RBJ7XE/a3oXFv31Jlw8cbMRdsxQoI8F3L7xm4n93ab+bIr1NQUiYGgW9L7TTw7obdNev91ZnW0xfqJtXcPA5yA==}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/database-compat@2.0.0':
+    resolution: {integrity: sha512-2xlODKWwf/vNAxCmou0GFhymx2pqZKkhXMN9B5aiTjZ6+81sOxGim53ELY2lj+qKG2IvgiCYFc4X+ZJA2Ad5vg==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/database-types@1.0.6':
+    resolution: {integrity: sha512-sMI7IynSZBsyGbUugc8PKE1jwKbnvaieAz/RxuM57PZQNCi6Rteiviwcw/jqZOX6igqYJwXWZ3UzKOZo2nUDRA==}
+
+  '@firebase/database@1.0.9':
+    resolution: {integrity: sha512-EkiPSKSu2TJJGtOjyISASf3UFpFJDil1lMbfqnxilfbmIsilvC8DzgjuLoYD+eOitcug4wtU9Fh1tt2vgBhskA==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/firestore-compat@0.3.39':
+    resolution: {integrity: sha512-CsK8g34jNeHx95LISDRTcArJLonW+zJCqHI1Ez9WNiLAK2X8FeQ4UiD+RwOwxAIR+t2a6xED/5Fe6ZIqx7MuoQ==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/firestore-types@3.0.2':
+    resolution: {integrity: sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==}
+    peerDependencies:
+      '@firebase/app-types': 0.x
+      '@firebase/util': 1.x
+
+  '@firebase/firestore@4.7.4':
+    resolution: {integrity: sha512-K2nq4w+NF8J1waGawY5OHLawP/Aw5CYxyDstVv1NZemGPcM3U+LZ9EPaXr1PatYIrPA7fS4DxZoWcbB0aGJ8Zg==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/functions-compat@0.3.15':
+    resolution: {integrity: sha512-eiHpc6Sd9Y/SNhBsGi944SapiFbfTPKsiSUQ74QxNSs0yoxvABeIRolVMFk4TokP57NGmstGYpYte02XGNPcYw==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/functions-types@0.6.2':
+    resolution: {integrity: sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==}
+
+  '@firebase/functions@0.11.9':
+    resolution: {integrity: sha512-dhO5IUfQRCsrc20YD20nSOX+QCT+cH6N86HlZOLz2XgyEFgzOdBQnUot4EabBJQRkMBI7fZWUrbYfRcnov53ug==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/installations-compat@0.2.10':
+    resolution: {integrity: sha512-YTonkcVz3AK7RF8xFhvs5CwDuJ0xbzzCJIwXoV14gnzdYbMgy6vWlUUbzkvbtEDXzPRHB0n7aGZl56oy9dLOFw==}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/installations-types@0.5.2':
+    resolution: {integrity: sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==}
+    peerDependencies:
+      '@firebase/app-types': 0.x
+
+  '@firebase/installations@0.6.10':
+    resolution: {integrity: sha512-TuGSOMqkFrllxa0X/8VZIqBCRH4POndU/iWKWkRmkh12+/xKSpdp+y/kWaVbsySrelltan6LeYlcYPmLibWbwg==}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/logger@0.4.3':
+    resolution: {integrity: sha512-Th42bWJg18EF5bJwhRosn2M/eYxmbWCwXZr4hHX7ltO0SE3QLrpgiMKeRBR/NW7vJke7i0n3i8esbCW2s93qBw==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/messaging-compat@0.2.13':
+    resolution: {integrity: sha512-9ootPClS6m2c2KIzo7AqSHaWzAw28zWcjQPjVv7WeQDu6wjufpbOg+7tuVzb+gqpF9Issa3lDoYOwlO0ZudO3g==}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/messaging-interop-types@0.2.2':
+    resolution: {integrity: sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==}
+
+  '@firebase/messaging@0.12.13':
+    resolution: {integrity: sha512-YLa8PWl+BgiOVR5WOyzl21fVJFJeBRfniNuN25d9DBrQzppSAahuN6yS+vt1OIjvZNPN4pZ/lcRLYupbGu4W0w==}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/performance-compat@0.2.10':
+    resolution: {integrity: sha512-0h1qYkF6I79DSSpHfTQFvb91fo8shmmwiPzWFYAPdPK02bSWpKwVssNYlZX2iUnumxerDMbl7dWN+Im/W3bnXA==}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/performance-types@0.2.2':
+    resolution: {integrity: sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==}
+
+  '@firebase/performance@0.6.10':
+    resolution: {integrity: sha512-x/mNYKGxq7A+QV0EiEZeD2S+E+kw+UcZ8FXuE7qDJyGGt/0Wd+bIIL7RakG/VrFt7/UYc//nKygDc7/Ig7sOmQ==}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/remote-config-compat@0.2.10':
+    resolution: {integrity: sha512-fIi5OB2zk0zpChMV/tTd0oEZcZI8TlwQDlLlcrDpMOV5l5dqd0JNlWKh6Fwmh4izmytk+rZIAIpnak/NjGVesQ==}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/remote-config-types@0.3.2':
+    resolution: {integrity: sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==}
+
+  '@firebase/remote-config@0.4.10':
+    resolution: {integrity: sha512-jTRjy3TdqzVna19m5a1HEHE5BG4Z3BQTxBgvQRTmMKlHacx4QS0CToAas7R9M9UkxpgFcVuAE7FpWIOWQGCEWw==}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/storage-compat@0.3.13':
+    resolution: {integrity: sha512-15kje7JALswRCBKsCSvKg5FbqUYykaIMqMbZRD7I6uVRWwdyTvez5MBQfMhBia2JcEmPiDpXhJTXH4PAWFiA8g==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app-compat': 0.x
+
+  '@firebase/storage-types@0.8.2':
+    resolution: {integrity: sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==}
+    peerDependencies:
+      '@firebase/app-types': 0.x
+      '@firebase/util': 1.x
+
+  '@firebase/storage@0.13.3':
+    resolution: {integrity: sha512-B5HiJ7isYKaT4dOEV43f2ySdhQxzq+SQEm7lqXebJ8AYCsebdHrgGzrPR0LR962xGjPzJHFKx63gA8Be/P2MCw==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app': 0.x
+
+  '@firebase/util@1.10.1':
+    resolution: {integrity: sha512-AIhFnCCjM8FmCqSNlNPTuOk3+gpHC1RkeNUBLtPbcqGYpN5MxI5q7Yby+rxycweOZOCboDzfIj8WyaY4tpQG/g==}
+    engines: {node: '>=18.0.0'}
+
+  '@firebase/vertexai@1.0.0':
+    resolution: {integrity: sha512-48N3Lp/9GgiCCRfrSdHS+Y1IiMdYXvnHFO/f+HL1PgUtBq7WQ/fWmYOX3mzAN36zvytq13nb68ImF+GALopp+Q==}
+    engines: {node: '>=18.0.0'}
+    peerDependencies:
+      '@firebase/app': 0.x
+      '@firebase/app-types': 0.x
+
+  '@firebase/webchannel-wrapper@1.0.2':
+    resolution: {integrity: sha512-3F4iA2E+NtdMbOU0XC1cHE8q6MqpGIKRj62oGOF38S6AAx5VHR9cXmoDUSj7ejvTAT7m6jxuEeQkHeq0F+mU2w==}
+
   '@floating-ui/core@1.6.5':
     resolution: {integrity: sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==}
 
@@ -158,6 +371,15 @@ packages:
   '@formatjs/intl-localematcher@0.5.4':
     resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==}
 
+  '@grpc/grpc-js@1.9.15':
+    resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==}
+    engines: {node: ^8.13.0 || >=10.10.0}
+
+  '@grpc/proto-loader@0.7.13':
+    resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==}
+    engines: {node: '>=6'}
+    hasBin: true
+
   '@humanwhocodes/config-array@0.11.14':
     resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
     engines: {node: '>=10.10.0'}
@@ -273,6 +495,36 @@ packages:
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
 
+  '@protobufjs/aspromise@1.1.2':
+    resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
+
+  '@protobufjs/base64@1.1.2':
+    resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
+
+  '@protobufjs/codegen@2.0.4':
+    resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
+
+  '@protobufjs/eventemitter@1.1.0':
+    resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
+
+  '@protobufjs/fetch@1.1.0':
+    resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
+
+  '@protobufjs/float@1.0.2':
+    resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
+
+  '@protobufjs/inquire@1.1.0':
+    resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
+
+  '@protobufjs/path@1.1.2':
+    resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
+
+  '@protobufjs/pool@1.1.0':
+    resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
+
+  '@protobufjs/utf8@1.1.0':
+    resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
+
   '@rc-component/mini-decimal@1.1.0':
     resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==}
     engines: {node: '>=8.x'}
@@ -551,6 +803,10 @@ packages:
   client-only@0.0.1:
     resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
 
+  cliui@8.0.1:
+    resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+    engines: {node: '>=12'}
+
   clsx@2.1.1:
     resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
     engines: {node: '>=6'}
@@ -713,6 +969,10 @@ packages:
     resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
     engines: {node: '>= 0.4'}
 
+  escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+
   escape-string-regexp@4.0.0:
     resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
     engines: {node: '>=10'}
@@ -840,6 +1100,10 @@ packages:
   fastq@1.17.1:
     resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
 
+  faye-websocket@0.11.4:
+    resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==}
+    engines: {node: '>=0.8.0'}
+
   file-entry-cache@6.0.1:
     resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
     engines: {node: ^10.12.0 || >=12.0.0}
@@ -852,6 +1116,9 @@ packages:
     resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
     engines: {node: '>=10'}
 
+  firebase@11.0.1:
+    resolution: {integrity: sha512-qsFb8dMcQINEDhJteG7RP+GqwgSRvfyiexQqHd5JToDdm87i9I2rGC4XQsGawKGxzKwZ/ISdgwNWxXAFYdCC6A==}
+
   flat-cache@3.2.0:
     resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
     engines: {node: ^10.12.0 || >=12.0.0}
@@ -911,6 +1178,10 @@ packages:
   functions-have-names@1.2.3:
     resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
 
+  get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+
   get-intrinsic@1.2.4:
     resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
     engines: {node: '>= 0.4'}
@@ -990,6 +1261,12 @@ packages:
     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     engines: {node: '>= 0.4'}
 
+  http-parser-js@0.5.8:
+    resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==}
+
+  idb@7.1.1:
+    resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
+
   ignore@5.3.1:
     resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
     engines: {node: '>= 4'}
@@ -1215,12 +1492,18 @@ packages:
     resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
     engines: {node: '>=10'}
 
+  lodash.camelcase@4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
   lodash.merge@4.6.2:
     resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
 
   lodash@4.17.21:
     resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
 
+  long@5.2.3:
+    resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
+
   loose-envify@1.4.0:
     resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
     hasBin: true
@@ -1546,6 +1829,10 @@ packages:
   prop-types@15.8.1:
     resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
 
+  protobufjs@7.4.0:
+    resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==}
+    engines: {node: '>=12.0.0'}
+
   proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
 
@@ -1605,6 +1892,10 @@ packages:
     resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
     engines: {node: '>= 0.4'}
 
+  require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+
   resize-observer-polyfill@1.5.1:
     resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
 
@@ -1642,6 +1933,9 @@ packages:
     resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
     engines: {node: '>=0.4'}
 
+  safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
   safe-regex-test@1.0.3:
     resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
     engines: {node: '>= 0.4'}
@@ -1884,6 +2178,14 @@ packages:
   util-deprecate@1.0.2:
     resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
 
+  websocket-driver@0.7.4:
+    resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==}
+    engines: {node: '>=0.8.0'}
+
+  websocket-extensions@0.1.4:
+    resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==}
+    engines: {node: '>=0.8.0'}
+
   which-boxed-primitive@1.0.2:
     resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
 
@@ -1919,11 +2221,23 @@ packages:
   wrappy@1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 
+  y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+
   yaml@2.4.5:
     resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
     engines: {node: '>= 14'}
     hasBin: true
 
+  yargs-parser@21.1.1:
+    resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+    engines: {node: '>=12'}
+
+  yargs@17.7.2:
+    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+    engines: {node: '>=12'}
+
   yocto-queue@0.1.0:
     resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
     engines: {node: '>=10'}
@@ -1974,6 +2288,323 @@ snapshots:
 
   '@eslint/js@8.57.0': {}
 
+  '@firebase/analytics-compat@0.2.15(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/analytics': 0.10.9(@firebase/app@0.10.15)
+      '@firebase/analytics-types': 0.8.2
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+
+  '@firebase/analytics-types@0.8.2': {}
+
+  '@firebase/analytics@0.10.9(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/installations': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/app-check-compat@0.3.16(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-check': 0.8.9(@firebase/app@0.10.15)
+      '@firebase/app-check-types': 0.5.2
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+
+  '@firebase/app-check-interop-types@0.3.2': {}
+
+  '@firebase/app-check-types@0.5.2': {}
+
+  '@firebase/app-check@0.8.9(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/app-compat@0.2.45':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/app-types@0.9.2': {}
+
+  '@firebase/app@0.10.15':
+    dependencies:
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      idb: 7.1.1
+      tslib: 2.6.3
+
+  '@firebase/auth-compat@0.5.15(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/auth': 1.8.0(@firebase/app@0.10.15)
+      '@firebase/auth-types': 0.12.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.1)
+      '@firebase/component': 0.6.10
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+      - '@firebase/app-types'
+      - '@react-native-async-storage/async-storage'
+
+  '@firebase/auth-interop-types@0.2.3': {}
+
+  '@firebase/auth-types@0.12.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.1)':
+    dependencies:
+      '@firebase/app-types': 0.9.2
+      '@firebase/util': 1.10.1
+
+  '@firebase/auth@1.8.0(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/component@0.6.10':
+    dependencies:
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/data-connect@0.1.1(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/auth-interop-types': 0.2.3
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/database-compat@2.0.0':
+    dependencies:
+      '@firebase/component': 0.6.10
+      '@firebase/database': 1.0.9
+      '@firebase/database-types': 1.0.6
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/database-types@1.0.6':
+    dependencies:
+      '@firebase/app-types': 0.9.2
+      '@firebase/util': 1.10.1
+
+  '@firebase/database@1.0.9':
+    dependencies:
+      '@firebase/app-check-interop-types': 0.3.2
+      '@firebase/auth-interop-types': 0.2.3
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      faye-websocket: 0.11.4
+      tslib: 2.6.3
+
+  '@firebase/firestore-compat@0.3.39(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/firestore': 4.7.4(@firebase/app@0.10.15)
+      '@firebase/firestore-types': 3.0.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.1)
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+      - '@firebase/app-types'
+
+  '@firebase/firestore-types@3.0.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.1)':
+    dependencies:
+      '@firebase/app-types': 0.9.2
+      '@firebase/util': 1.10.1
+
+  '@firebase/firestore@4.7.4(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      '@firebase/webchannel-wrapper': 1.0.2
+      '@grpc/grpc-js': 1.9.15
+      '@grpc/proto-loader': 0.7.13
+      tslib: 2.6.3
+
+  '@firebase/functions-compat@0.3.15(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/functions': 0.11.9(@firebase/app@0.10.15)
+      '@firebase/functions-types': 0.6.2
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+
+  '@firebase/functions-types@0.6.2': {}
+
+  '@firebase/functions@0.11.9(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/app-check-interop-types': 0.3.2
+      '@firebase/auth-interop-types': 0.2.3
+      '@firebase/component': 0.6.10
+      '@firebase/messaging-interop-types': 0.2.2
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/installations-compat@0.2.10(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/installations': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/installations-types': 0.5.2(@firebase/app-types@0.9.2)
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+      - '@firebase/app-types'
+
+  '@firebase/installations-types@0.5.2(@firebase/app-types@0.9.2)':
+    dependencies:
+      '@firebase/app-types': 0.9.2
+
+  '@firebase/installations@0.6.10(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/util': 1.10.1
+      idb: 7.1.1
+      tslib: 2.6.3
+
+  '@firebase/logger@0.4.3':
+    dependencies:
+      tslib: 2.6.3
+
+  '@firebase/messaging-compat@0.2.13(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/messaging': 0.12.13(@firebase/app@0.10.15)
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+
+  '@firebase/messaging-interop-types@0.2.2': {}
+
+  '@firebase/messaging@0.12.13(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/installations': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/messaging-interop-types': 0.2.2
+      '@firebase/util': 1.10.1
+      idb: 7.1.1
+      tslib: 2.6.3
+
+  '@firebase/performance-compat@0.2.10(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/performance': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/performance-types': 0.2.2
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+
+  '@firebase/performance-types@0.2.2': {}
+
+  '@firebase/performance@0.6.10(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/installations': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/remote-config-compat@0.2.10(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/remote-config': 0.4.10(@firebase/app@0.10.15)
+      '@firebase/remote-config-types': 0.3.2
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+
+  '@firebase/remote-config-types@0.3.2': {}
+
+  '@firebase/remote-config@0.4.10(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/installations': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/storage-compat@0.3.13(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app-compat': 0.2.45
+      '@firebase/component': 0.6.10
+      '@firebase/storage': 0.13.3(@firebase/app@0.10.15)
+      '@firebase/storage-types': 0.8.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.1)
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+    transitivePeerDependencies:
+      - '@firebase/app'
+      - '@firebase/app-types'
+
+  '@firebase/storage-types@0.8.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.1)':
+    dependencies:
+      '@firebase/app-types': 0.9.2
+      '@firebase/util': 1.10.1
+
+  '@firebase/storage@0.13.3(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/component': 0.6.10
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/util@1.10.1':
+    dependencies:
+      tslib: 2.6.3
+
+  '@firebase/vertexai@1.0.0(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)':
+    dependencies:
+      '@firebase/app': 0.10.15
+      '@firebase/app-check-interop-types': 0.3.2
+      '@firebase/app-types': 0.9.2
+      '@firebase/component': 0.6.10
+      '@firebase/logger': 0.4.3
+      '@firebase/util': 1.10.1
+      tslib: 2.6.3
+
+  '@firebase/webchannel-wrapper@1.0.2': {}
+
   '@floating-ui/core@1.6.5':
     dependencies:
       '@floating-ui/utils': 0.2.5
@@ -2013,6 +2644,18 @@ snapshots:
     dependencies:
       tslib: 2.6.3
 
+  '@grpc/grpc-js@1.9.15':
+    dependencies:
+      '@grpc/proto-loader': 0.7.13
+      '@types/node': 20.14.11
+
+  '@grpc/proto-loader@0.7.13':
+    dependencies:
+      lodash.camelcase: 4.3.0
+      long: 5.2.3
+      protobufjs: 7.4.0
+      yargs: 17.7.2
+
   '@humanwhocodes/config-array@0.11.14':
     dependencies:
       '@humanwhocodes/object-schema': 2.0.3
@@ -2099,6 +2742,29 @@ snapshots:
   '@pkgjs/parseargs@0.11.0':
     optional: true
 
+  '@protobufjs/aspromise@1.1.2': {}
+
+  '@protobufjs/base64@1.1.2': {}
+
+  '@protobufjs/codegen@2.0.4': {}
+
+  '@protobufjs/eventemitter@1.1.0': {}
+
+  '@protobufjs/fetch@1.1.0':
+    dependencies:
+      '@protobufjs/aspromise': 1.1.2
+      '@protobufjs/inquire': 1.1.0
+
+  '@protobufjs/float@1.0.2': {}
+
+  '@protobufjs/inquire@1.1.0': {}
+
+  '@protobufjs/path@1.1.2': {}
+
+  '@protobufjs/pool@1.1.0': {}
+
+  '@protobufjs/utf8@1.1.0': {}
+
   '@rc-component/mini-decimal@1.1.0':
     dependencies:
       '@babel/runtime': 7.24.8
@@ -2448,6 +3114,12 @@ snapshots:
 
   client-only@0.0.1: {}
 
+  cliui@8.0.1:
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+
   clsx@2.1.1: {}
 
   color-convert@2.0.1:
@@ -2679,6 +3351,8 @@ snapshots:
       is-date-object: 1.0.5
       is-symbol: 1.0.4
 
+  escalade@3.2.0: {}
+
   escape-string-regexp@4.0.0: {}
 
   eslint-config-next@14.2.4(eslint@8.57.0)(typescript@5.5.3):
@@ -2899,6 +3573,10 @@ snapshots:
     dependencies:
       reusify: 1.0.4
 
+  faye-websocket@0.11.4:
+    dependencies:
+      websocket-driver: 0.7.4
+
   file-entry-cache@6.0.1:
     dependencies:
       flat-cache: 3.2.0
@@ -2912,6 +3590,39 @@ snapshots:
       locate-path: 6.0.0
       path-exists: 4.0.0
 
+  firebase@11.0.1:
+    dependencies:
+      '@firebase/analytics': 0.10.9(@firebase/app@0.10.15)
+      '@firebase/analytics-compat': 0.2.15(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)
+      '@firebase/app': 0.10.15
+      '@firebase/app-check': 0.8.9(@firebase/app@0.10.15)
+      '@firebase/app-check-compat': 0.3.16(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)
+      '@firebase/app-compat': 0.2.45
+      '@firebase/app-types': 0.9.2
+      '@firebase/auth': 1.8.0(@firebase/app@0.10.15)
+      '@firebase/auth-compat': 0.5.15(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)
+      '@firebase/data-connect': 0.1.1(@firebase/app@0.10.15)
+      '@firebase/database': 1.0.9
+      '@firebase/database-compat': 2.0.0
+      '@firebase/firestore': 4.7.4(@firebase/app@0.10.15)
+      '@firebase/firestore-compat': 0.3.39(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)
+      '@firebase/functions': 0.11.9(@firebase/app@0.10.15)
+      '@firebase/functions-compat': 0.3.15(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)
+      '@firebase/installations': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/installations-compat': 0.2.10(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)
+      '@firebase/messaging': 0.12.13(@firebase/app@0.10.15)
+      '@firebase/messaging-compat': 0.2.13(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)
+      '@firebase/performance': 0.6.10(@firebase/app@0.10.15)
+      '@firebase/performance-compat': 0.2.10(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)
+      '@firebase/remote-config': 0.4.10(@firebase/app@0.10.15)
+      '@firebase/remote-config-compat': 0.2.10(@firebase/app-compat@0.2.45)(@firebase/app@0.10.15)
+      '@firebase/storage': 0.13.3(@firebase/app@0.10.15)
+      '@firebase/storage-compat': 0.3.13(@firebase/app-compat@0.2.45)(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)
+      '@firebase/util': 1.10.1
+      '@firebase/vertexai': 1.0.0(@firebase/app-types@0.9.2)(@firebase/app@0.10.15)
+    transitivePeerDependencies:
+      - '@react-native-async-storage/async-storage'
+
   flat-cache@3.2.0:
     dependencies:
       flatted: 3.3.1
@@ -2960,6 +3671,8 @@ snapshots:
 
   functions-have-names@1.2.3: {}
 
+  get-caller-file@2.0.5: {}
+
   get-intrinsic@1.2.4:
     dependencies:
       es-errors: 1.3.0
@@ -3058,6 +3771,10 @@ snapshots:
     dependencies:
       function-bind: 1.1.2
 
+  http-parser-js@0.5.8: {}
+
+  idb@7.1.1: {}
+
   ignore@5.3.1: {}
 
   immutable@4.3.6: {}
@@ -3272,10 +3989,14 @@ snapshots:
     dependencies:
       p-locate: 5.0.0
 
+  lodash.camelcase@4.3.0: {}
+
   lodash.merge@4.6.2: {}
 
   lodash@4.17.21: {}
 
+  long@5.2.3: {}
+
   loose-envify@1.4.0:
     dependencies:
       js-tokens: 4.0.0
@@ -3531,6 +4252,21 @@ snapshots:
       object-assign: 4.1.1
       react-is: 16.13.1
 
+  protobufjs@7.4.0:
+    dependencies:
+      '@protobufjs/aspromise': 1.1.2
+      '@protobufjs/base64': 1.1.2
+      '@protobufjs/codegen': 2.0.4
+      '@protobufjs/eventemitter': 1.1.0
+      '@protobufjs/fetch': 1.1.0
+      '@protobufjs/float': 1.0.2
+      '@protobufjs/inquire': 1.1.0
+      '@protobufjs/path': 1.1.2
+      '@protobufjs/pool': 1.1.0
+      '@protobufjs/utf8': 1.1.0
+      '@types/node': 20.14.11
+      long: 5.2.3
+
   proxy-from-env@1.1.0: {}
 
   punycode@2.3.1: {}
@@ -3595,6 +4331,8 @@ snapshots:
       es-errors: 1.3.0
       set-function-name: 2.0.2
 
+  require-directory@2.1.1: {}
+
   resize-observer-polyfill@1.5.1: {}
 
   resolve-from@4.0.0: {}
@@ -3632,6 +4370,8 @@ snapshots:
       has-symbols: 1.0.3
       isarray: 2.0.5
 
+  safe-buffer@5.2.1: {}
+
   safe-regex-test@1.0.3:
     dependencies:
       call-bind: 1.0.7
@@ -3919,6 +4659,14 @@ snapshots:
 
   util-deprecate@1.0.2: {}
 
+  websocket-driver@0.7.4:
+    dependencies:
+      http-parser-js: 0.5.8
+      safe-buffer: 5.2.1
+      websocket-extensions: 0.1.4
+
+  websocket-extensions@0.1.4: {}
+
   which-boxed-primitive@1.0.2:
     dependencies:
       is-bigint: 1.0.4
@@ -3977,8 +4725,22 @@ snapshots:
 
   wrappy@1.0.2: {}
 
+  y18n@5.0.8: {}
+
   yaml@2.4.5: {}
 
+  yargs-parser@21.1.1: {}
+
+  yargs@17.7.2:
+    dependencies:
+      cliui: 8.0.1
+      escalade: 3.2.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 21.1.1
+
   yocto-queue@0.1.0: {}
 
   zustand@4.5.4(@types/react@18.3.3)(react@18.3.1):

+ 84 - 0
public/firebase-messaging-sw.js

@@ -0,0 +1,84 @@
+importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-app-compat.js');
+importScripts('https://www.gstatic.com/firebasejs/10.13.2/firebase-messaging-compat.js');
+// if ('serviceWorker' in navigator) {
+//   navigator.serviceWorker.register('./firebase-messaging-sw.js').then(function (reg) {
+//     console.log('reg: ', reg.scope);
+//   }).catch(function (err) {
+//     console.log('err: ', err);
+//   })
+// }
+class CustomPushEvent extends Event {
+  constructor(data) {
+    super('push')
+
+    Object.assign(this, data)
+    this.custom = true
+  }
+}
+
+console.log(`🚀🚀🚀🚀🚀-> in firebase-messaging-sw.js on 19`,process);
+firebase.initializeApp({
+  apiKey: process.env.NEXT_PUBLIC_FIREBASE_APIKEY,
+  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTHDOMAIN,
+  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECTID,
+  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGEBUCKET,
+  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID,
+  appId: process.env.NEXT_PUBLIC_FIREBASE_APPID,
+  measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENTID,
+});
+
+// Retrieve an instance of Firebase Messaging so that it can handle background
+// messages.
+self.addEventListener('push', (e) => {
+  // Skip if event is our own custom event
+  if (e.custom) return;
+
+  // Kep old event data to override
+  let oldData = e.data
+
+  // Create a new event to dispatch, pull values from notification key and put it in data key,
+  // and then remove notification key
+  let newEvent = new CustomPushEvent({
+    data: {
+      json() {
+        let newData = oldData.json()
+        newData.data = {
+          ...newData.data,
+          ...newData.notification
+        }
+        delete newData.notification
+        return newData
+      },
+    },
+    waitUntil: e.waitUntil.bind(e),
+  })
+
+  // Stop event propagation
+  e.stopImmediatePropagation()
+
+  // Dispatch the new wrapped event
+  dispatchEvent(newEvent)
+})
+
+const messaging = firebase.messaging();
+
+messaging.onBackgroundMessage( (payload) => {
+  console.log(`🚀🚀🚀🚀🚀-> in firebase-messaging-sw.js on 24`,payload)
+  // Customize notification here
+  const notificationTitle = payload.data.title;
+  const notificationOptions = {
+    body: payload.data.body,
+    icon: '/logo.png',
+    data: payload.data
+  };
+  return self.registration.showNotification(notificationTitle,
+    notificationOptions);
+});
+
+self.onnotificationclick = function (event) {
+  console.log(`🚀🚀🚀🚀🚀-> in firebase-messaging-sw.js on 91`,event);
+  //example
+  const endpoint = event.notification.data.type;
+  self.clients.openWindow('/' + endpoint || "")
+  event.notification.close()
+}

二進制
public/icon-192x192.png


二進制
public/icon-512x512.png


+ 1 - 1
src/api/user.ts

@@ -171,7 +171,7 @@ export const getUserMoneyApi = () => {
 };
 // 前台用户充值
 export const getUserRechargeApi = (data: any) => {
-    return server.post<any>({
+    return server.post<{ pay_url?: string }>({
         url: "/v1/api/user/user_recharge",
         data,
         toast: true,

+ 181 - 63
src/app/[locale]/(TabBar)/(ordinary)/deposit/DepositData.tsx

@@ -1,51 +1,110 @@
 "use client";
 import { DepositsTypes } from "@/api/depositsApi";
 import { getUserRechargeApi } from "@/api/user";
-import actions from "@/app/[locale]/(TabBar)/(ordinary)/deposit/actions";
+import Box from "@/components/Box";
 import ButtonOwn from "@/components/ButtonOwn";
-import { Toast } from "antd-mobile";
+import { neReg } from "@/utils";
+import { Form, Input, Toast } from "antd-mobile";
+import { FormInstance } from "antd-mobile/es/components/form";
 import { useTranslations } from "next-intl";
-import { FC, Fragment, useState } from "react";
+import { FC, Fragment, useRef, useState } from "react";
 import "./page.scss";
 
 interface Props {
     deposits: DepositsTypes[];
 }
-
+const MobileFiled = ({
+    value,
+    onChange,
+    products,
+}: {
+    value?: any;
+    onChange?: (value: any) => void;
+    products: DepositsTypes["products"];
+}) => {
+    const t = useTranslations();
+    return (
+        <>
+            <Input placeholder={t("form.card")} type={"number"} />
+        </>
+    );
+};
 const DepositData: FC<Props> = (props) => {
     const { deposits } = props;
-    const t = useTranslations("DepositPage");
+    const t = useTranslations();
 
     const [activeType, setActiveType] = useState<DepositsTypes>(deposits[0]);
 
-    let [amount, setAmount] = useState("");
-    const ChangeAmount = (e: any) => {
-        let newAmount = e.target.value.replace(/[^0-9]/g, "");
-        if (newAmount > activeType.max_amount) newAmount = activeType.max_amount;
-        // if (newAmount < activeType.min_amount) newAmount = activeType.min_amount;
-        setAmount(newAmount);
-    };
+    const formInstanceRef = useRef<FormInstance>(null);
+    let [amount, setAmount] = useState<number | undefined>(undefined);
+    // const ChangeAmount = (e: any) => {
+    //     let newAmount = e.target.value.replace(/[^0-9]/g, "");
+    //     if (newAmount > activeType.max_amount) newAmount = activeType.max_amount;
+    //     // if (newAmount < activeType.min_amount) newAmount = activeType.min_amount;
+    //     setAmount(newAmount);
+    // };
 
-    const userRechargeRequest = async () => {
-        if (+amount < activeType.min_amount) {
-            Toast.show(`Min  ${activeType.min_amount}`);
-            return;
-        }
-        let res = await getUserRechargeApi({
-            amount: parseInt(amount),
-            channel_id: activeType.id,
-        });
-        if (res.code === 200) {
-            setAmount("");
-            Toast.show({ icon: "success", content: t("RechargeSuc"), maskClickable: false });
-            await actions();
-        }
-    };
+    // const userRechargeRequest = async () => {
+    //     if (!amount) return;
+    //     if (amount < activeType.min_amount) {
+    //         Toast.show(`Min  ${activeType.min_amount}`);
+    //         return;
+    //     }
+    //     let res = await getUserRechargeApi({
+    //         amount: amount,
+    //         channel_id: activeType.id,
+    //     });
+    //     console.log(`🚀🚀🚀🚀🚀-> in DepositData.tsx on 38`, res);
+    //     if (res.code === 200) {
+    //         setAmount(undefined);
+    //         Toast.show({ icon: "success", content: t("RechargeSuc"), maskClickable: false });
+    //         await actions();
+    //     }
+    // };
     const titleChangeHandler = (item: DepositsTypes, index: number) => {
-        setAmount("");
+        setAmount(undefined);
         setActiveType(item);
+        formInstanceRef.current?.resetFields();
     };
 
+    const isStrictMode = true;
+
+    const onFinish = (values: any) => {
+        const params = { ...values, channel_id: activeType.id, amount: +values.amount };
+        getUserRechargeApi(params)
+            .then((res) => {
+                formInstanceRef.current?.resetFields();
+                Toast.show({ icon: "success", content: t("code.200"), maskClickable: false });
+                setAmount(undefined);
+                if (res.data.pay_url) {
+                    window.open(res.data.pay_url);
+                }
+            })
+            .catch((error) => {
+                Toast.show({ content: t(`code${error.data.code}`), maskClickable: false });
+            });
+    };
+    const onValuesChange = (changeValues: any) => {
+        if (changeValues.amount) {
+            setAmount(changeValues.amount);
+        }
+    };
+    const amountChange = (value: number) => {
+        formInstanceRef.current?.setFieldValue("amount", value);
+        setAmount(value);
+    };
+    const amountValidator = (rules: any, value: any) => {
+        if (!value) return Promise.reject(new Error(t("form.amount")));
+        if (+value < activeType.min_amount)
+            return Promise.reject(
+                new Error(t("form.amountMinReg", { amount: activeType.min_amount }))
+            );
+        if (+value > activeType.max_amount)
+            return Promise.reject(
+                new Error(t("form.amountMaxReg", { amount: activeType.max_amount }))
+            );
+        return Promise.resolve();
+    };
     return (
         <div className="deposit-box">
             <div className="img-box"></div>
@@ -68,45 +127,104 @@ const DepositData: FC<Props> = (props) => {
                 })}
             </div>
 
-            <div className={"flex flex-col"}>
-                <div className={"flex-1"}>
-                    <div className="amount-box">
-                        <span>{t("Montante")} (BRL):</span>
-                        <input
-                            type="text"
-                            value={amount}
-                            onChange={ChangeAmount}
-                            placeholder={`Mín. ${activeType.min_amount}`}
-                        />
-                    </div>
-                    <ul className="ul-box">
-                        {activeType.products.map((item, index) => (
-                            <li
-                                className={+amount == item.amount ? "active" : ""}
-                                key={index}
-                                onClick={() => setAmount(`${item.amount}`)}
+            <Box className={"custom-form"} style={{ padding: 0 }}>
+                <Form
+                    style={{
+                        "--border-bottom": "none",
+                        "--border-top": "none",
+                        "--border-inner": "none",
+                    }}
+                    ref={formInstanceRef}
+                    footer={
+                        <>
+                            <ButtonOwn active>{t("DepositPage.DepositarAgora")}</ButtonOwn>
+                        </>
+                    }
+                    // initialValues={params.current}
+                    onFinish={onFinish}
+                    onValuesChange={onValuesChange}
+                >
+                    {isStrictMode ? (
+                        <>
+                            <Form.Item
+                                name="user_name"
+                                label=""
+                                rules={[{ required: true, message: t("form.usernameReg") }]}
                             >
-                                {!!item.badge && <span className="hot"></span>}
-                                <div className="amountContent">
-                                    {/* <span className="iconfont icon-unit-brl"></span> */}
-                                    <span className="iconfont">R$</span>
-                                    <span> {item.amount}</span>
-                                </div>
-                                <span className="amountTips">{t("Oferecer")} 100%</span>
-                            </li>
-                        ))}
-                    </ul>
-                </div>
-            </div>
+                                <Input placeholder={t("form.username")} />
+                            </Form.Item>
+                            <Form.Item
+                                name="passport"
+                                label=""
+                                rules={[
+                                    { required: true, message: t("form.cardReg"), pattern: neReg },
+                                    { min: 11, message: t("form.cardReg") },
+                                ]}
+                            >
+                                <Input
+                                    placeholder={t("form.card")}
+                                    maxLength={11}
+                                    type={"number"}
+                                />
+                            </Form.Item>
+                        </>
+                    ) : null}
+                    <Form.Item
+                        name="amount"
+                        label=""
+                        rules={[{ required: true, type: "number", validator: amountValidator }]}
+                    >
+                        <Input
+                            placeholder={`${t("DepositPage.Montante")}(BRL): Mín. ${activeType.min_amount}`}
+                            type={"number"}
+                            maxLength={activeType.max_amount}
+                        />
+                    </Form.Item>
 
-            <div className="topUp">
-                <ButtonOwn active={amount !== ""} callbackFun={userRechargeRequest}>
-                    {t("DepositarAgora")}
-                </ButtonOwn>
+                    <div className={"flex flex-col"}>
+                        <div className={"flex-1"}>
+                            {/*<div className="amount-box">*/}
+                            {/*    <span>{t("Montante")} (BRL):</span>*/}
+                            {/*    <input*/}
+                            {/*        type="text"*/}
+                            {/*        value={amount}*/}
+                            {/*        onChange={ChangeAmount}*/}
+                            {/*        placeholder={`Mín. ${activeType.min_amount}`}*/}
+                            {/*    />*/}
+                            {/*</div>*/}
+                            <ul className="ul-box">
+                                {activeType.products.map((item, index) => (
+                                    <li
+                                        className={amount == item.amount ? "active" : ""}
+                                        key={index}
+                                        onClick={() => amountChange(item.amount)}
+                                    >
+                                        {!!item.badge && <span className="hot"></span>}
+                                        <div className="amountContent">
+                                            {/* <span className="iconfont icon-unit-brl"></span> */}
+                                            <span className="iconfont">R$</span>
+                                            <span> {item.amount}</span>
+                                        </div>
+                                        <span className="amountTips">
+                                            {t("DepositPage.Oferecer")} 100%
+                                        </span>
+                                    </li>
+                                ))}
+                            </ul>
+                        </div>
+                    </div>
+                </Form>
                 <div className={"mt-[5px] text-[0.12rem] text-primary-color"}>
-                    {t("depositTips")}
+                    {t("DepositPage.depositTips")}
                 </div>
-            </div>
+            </Box>
+
+            {/*<div className="topUp">*/}
+            {/*    <ButtonOwn active={amount !== ""} callbackFun={userRechargeRequest}>*/}
+            {/*        {t("DepositarAgora")}*/}
+            {/*    </ButtonOwn>*/}
+            {/*<div className={"mt-[5px] text-[0.12rem] text-primary-color"}>{t("depositTips")}</div>*/}
+            {/*</div>*/}
         </div>
     );
 };

+ 1 - 1
src/app/[locale]/(TabBar)/(ordinary)/deposit/page.scss

@@ -67,7 +67,7 @@
       -ms-flex-pack: justify;
       justify-content: space-between;
       flex-wrap: wrap;
-      margin-top: .09rem;
+      //margin-top: .09rem;
       display: flex;
       -webkit-box-align: center;
       -ms-flex-align: center;

+ 0 - 2
src/app/[locale]/(TabBar)/(ordinary)/profile/ProfileHeader.tsx

@@ -28,7 +28,6 @@ type Props = {
 };
 const VipCard = (props: { userVip: UserVipInfo }) => {
     const { userVip } = props;
-    console.log(`🚀🚀🚀🚀🚀-> in ProfileHeader.tsx on 31`, userVip);
     const t = useTranslations("ProfilePage");
 
     // Vip 图标
@@ -86,7 +85,6 @@ const WalletCard = (props: { userMoney: Wallet }) => {
     };
 
     const walletHandler = (key: string) => {
-        console.log(`🚀🚀🚀🚀🚀-> in ProfileHeader.tsx on 86`, key);
         router.push("/wallet");
     };
     return (

+ 1 - 2
src/app/[locale]/(TabBar)/[[...share]]/@popupWidget/page.tsx

@@ -1,7 +1,6 @@
 import { NoticeRep, PromotionRep } from "@/api/home";
 import { server } from "@/utils/server";
 import HomeMessage from "../_home/HomeMessage";
-import HomePromotion from "../_home/HomePromotion";
 const getPromotions = async () => {
     return server
         .request<PromotionRep[], { summery: { showType: 1 | 2 } }>({
@@ -36,7 +35,7 @@ const Page = async () => {
             {/*站内信*/}
             <HomeMessage notices={notices} />
             {/* 站内弹窗广告  */}
-            <HomePromotion data={promotions?.data || []} type={promotions?.summery.showType || 1} />
+            {/*<HomePromotion data={promotions?.data || []} type={promotions?.summery.showType || 1} />*/}
         </>
     );
 };

+ 3 - 2
src/app/[locale]/(TabBar)/[[...share]]/_home/HomePromotion.tsx

@@ -28,13 +28,14 @@ const HomePromotion: FC<Props> = (props) => {
             }
             return false;
         };
-        setVisible(shouldShowPromotion());
+        let flag = shouldShowPromotion();
+        setVisible(flag);
     }, []);
 
     const closeHandler = () => {
         setVisible(false);
         if (type === 1) {
-            sessionStorage.setItem("isClosePromotion", "2024-10-22");
+            sessionStorage.setItem("isClosePromotion", dayjs().format("YYYY-MM-DD"));
         }
 
         if (type === 2) {

+ 13 - 7
src/app/[locale]/(navbar)/withdraw/WithdrawWidget.tsx

@@ -2,6 +2,7 @@
 import { ChannelType, getWithDrawApi, WithDrawType } from "@/api/withdraw";
 import Box from "@/components/Box";
 import ButtonOwn from "@/components/ButtonOwn";
+import Empty from "@/components/Empty";
 import { useWalletStore } from "@/stores/useWalletStore";
 import { isEmail } from "@/utils";
 import { ActionSheet, Form, Input, Toast } from "antd-mobile";
@@ -101,12 +102,16 @@ const WithdrawWidget: FC<Props> = (props) => {
 
     const score = useWalletStore((state) => state.score)!;
 
+    const formRef = useRef<FormInstance>(null);
+
     const [activeWallet, setActiveWallet] = useState<WithDrawType>(channels[0]);
-    const walletAction = activeWallet.channels?.map((item) => ({
-        text: ChannelEnum[item.type],
-        key: item.id,
-        ...item,
-    }));
+    const walletAction =
+        activeWallet &&
+        activeWallet.channels?.map((item) => ({
+            text: ChannelEnum[item.type],
+            key: item.id,
+            ...item,
+        }));
 
     const defaultActions: Array<Action & { id: number }> = [
         { text: "CPF", key: "CPF", id: ChannelEnum.CPF },
@@ -155,8 +160,6 @@ const WithdrawWidget: FC<Props> = (props) => {
         return Promise.resolve();
     };
 
-    const formRef = useRef<FormInstance>(null);
-
     const onFinish = (value: any) => {
         const params = { ...value, ...value.channel, amount: +value.amount };
         getWithDrawApi(params)
@@ -169,6 +172,9 @@ const WithdrawWidget: FC<Props> = (props) => {
                 Toast.show(t(`code.${error.data.code}`));
             });
     };
+
+    if (!activeWallet) return <Empty />;
+
     return (
         <>
             <Box className={"custom-form"}>

+ 112 - 45
src/app/[locale]/providers.tsx

@@ -4,25 +4,92 @@ import Loading from "@/components/Loading";
 import { useSystemStore } from "@/stores/useSystemStore";
 import { setHtmlFontSize } from "@/utils";
 import { setCookies } from "@/utils/Cookies";
-import { ConfigProvider } from "antd-mobile";
+import { ConfigProvider, Dialog } from "antd-mobile";
 import enUS from "antd-mobile/es/locales/en-US";
 import { useLocale } from "next-intl";
 import { ThemeProviderProps } from "next-themes/dist/types";
-import { ReactNode, useEffect, useLayoutEffect, useRef } from "react";
+import { ReactNode, useLayoutEffect, useRef } from "react";
 import { Swiper, SwiperClass, SwiperSlide } from "swiper/react";
+
+import { useDebounceEffect } from "ahooks";
+import { initializeApp } from "firebase/app";
+import { getMessaging, getToken, onMessage } from "firebase/messaging";
 export interface ProvidersProps {
     children: ReactNode;
     themeProps?: Omit<ThemeProviderProps, "children">;
 }
-export const Providers = ({ children, themeProps }: ProvidersProps) => {
+
+const initFirebase = () => {
+    // 是否是https
+    if (document.location.protocol.indexOf("https") === -1) return;
+    //  浏览器是否支持 且是 pwa
+    if (!window.Notification) {
+        Dialog.alert({
+            getContainer: null,
+            bodyStyle: { background: "#fff" },
+            title: "提示",
+            confirmText: "我知道了",
+            content: (
+                <>
+                    <div className={"text-center"}>
+                        <p>当前版本浏览器不支持通知</p>
+                        <p>请更换或升级浏览器获得更好使用体验</p>
+                    </div>
+                </>
+            ),
+        });
+        return;
+    }
+    console.log(`🚀🚀🚀🚀🚀-> in providers.tsx on 44`);
+
+    //  是否开启通知
+    // new Notification("这是标题", {
+    //     body: "这是正文",
+    //     icon: "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
+    //     requireInteraction: true,
+    //     image: "https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png",
+    // });
+    if (Notification.permission === "default") {
+        // 征求用户的许可
+        Notification.requestPermission();
+    }
+    if (Notification.permission === "denied") return;
+
+    const app = initializeApp({
+        apiKey: process.env.NEXT_PUBLIC_FIREBASE_APIKEY,
+        authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTHDOMAIN,
+        projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECTID,
+        storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGEBUCKET,
+        messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGINGSENDERID,
+        appId: process.env.NEXT_PUBLIC_FIREBASE_APPID,
+        measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENTID,
+    });
+    const messaging = getMessaging(app);
+    // 针对单机测试,或者服务端需要使用这个key可以放开
+    console.log(`🚀🚀🚀🚀🚀-> in providers.tsx on 68`);
+    // if (process.env.NODE_ENV === "development") {
+    getToken(messaging, {
+        vapidKey: process.env.NEXT_PUBLIC_FIREBASE_KEYS,
+    }).then((res) => {
+        console.log(`🚀🚀🚀🚀🚀-> `, res);
+    });
+    // }
+    onMessage(messaging, (payload) => {
+        const notifica = new Notification(payload.data?.title || "", {
+            body: payload.data?.body,
+            icon: payload.data?.image,
+            ...payload.data,
+        });
+    });
+};
+
+const Layout = ({ children, themeProps }: ProvidersProps) => {
     const { isCollapse, setCollapse } = useSystemStore((state) => ({
         isCollapse: state.isCollapse,
         setCollapse: state.setCollapse,
     }));
     const local = useLocale();
-    useEffect(() => {
-        setCookies("language", local === "br" ? "pt" : local);
-    });
+
     const swiperRef = useRef<SwiperClass>();
     const homeContainerRef = useRef<HTMLDivElement>(null);
 
@@ -35,53 +102,53 @@ export const Providers = ({ children, themeProps }: ProvidersProps) => {
         setCollapse(false);
     };
 
-    const openSliderHandler = () => {
-        if (isCollapse) {
-            swiperRef.current?.slideNext();
-        } else {
-            swiperRef.current?.slidePrev();
-        }
-    };
-
     useLayoutEffect(() => {
         // 调用响应式方法
         setHtmlFontSize();
+        // 多语言兼容, 因为管理系统的静态数据是pt,但是页面路由需要br,  所以需要一致
+        setCookies("language", local === "br" ? "pt" : local);
     }, []);
 
     return (
-        <ConfigProvider locale={enUS}>
-            <div id="app" className="bg-black">
-                <Swiper
-                    resistanceRatio={10}
-                    initialSlide={2}
-                    slidesPerView={"auto"}
-                    onSlidePrevTransitionStart={startHandler}
-                    onSlideNextTransitionEnd={endHandler}
-                    slideToClickedSlide
-                    onSwiper={(swiper) => {
-                        swiperRef.current = swiper;
-                    }}
-                    allowTouchMove={false}
-                >
-                    <SwiperSlide>
-                        <section className="relative flex h-[100vh] items-center justify-center">
-                            <Loading />
-                        </section>
-                    </SwiperSlide>
+        <div id="app" className="bg-black">
+            <Swiper
+                resistanceRatio={10}
+                initialSlide={2}
+                slidesPerView={"auto"}
+                onSlidePrevTransitionStart={startHandler}
+                onSlideNextTransitionEnd={endHandler}
+                slideToClickedSlide
+                onSwiper={(swiper) => {
+                    swiperRef.current = swiper;
+                }}
+                allowTouchMove={false}
+            >
+                <SwiperSlide>
+                    <section className="relative flex h-[100vh] items-center justify-center">
+                        <Loading />
+                    </section>
+                </SwiperSlide>
 
-                    <SwiperSlide style={{ width: "70%" }} className={"bg-[rgb(31,31,31)]"}>
-                        <section className="relative h-[100vh]">
-                            <Sidebar></Sidebar>
-                        </section>
-                    </SwiperSlide>
+                <SwiperSlide style={{ width: "70%" }} className={"bg-[rgb(31,31,31)]"}>
+                    <section className="relative h-[100vh]">{<Sidebar></Sidebar>}</section>
+                </SwiperSlide>
 
-                    <SwiperSlide style={{ width: "100%" }}>
-                        <section className="relative h-[100%]" ref={homeContainerRef}>
-                            {children}
-                        </section>
-                    </SwiperSlide>
-                </Swiper>
-            </div>
+                <SwiperSlide style={{ width: "100%" }}>
+                    <section className="relative h-[100%]" ref={homeContainerRef}>
+                        {children}
+                    </section>
+                </SwiperSlide>
+            </Swiper>
+        </div>
+    );
+};
+export const Providers = ({ children, themeProps }: ProvidersProps) => {
+    useDebounceEffect(() => {
+        initFirebase();
+    }, []);
+    return (
+        <ConfigProvider locale={enUS}>
+            <Layout>{children}</Layout>
         </ConfigProvider>
     );
 };

+ 1 - 0
src/app/globals.scss

@@ -151,6 +151,7 @@ input[type=number] {
     margin-bottom: 0.1rem;
     padding: 0.06rem 0.1rem;
   }
+
   .adm-list-item {
     padding-left: 0 ;
   }

+ 25 - 0
src/app/manifest.ts

@@ -0,0 +1,25 @@
+import type { MetadataRoute } from "next";
+
+export default function manifest(): MetadataRoute.Manifest {
+    return {
+        name: "BCWIN",
+        short_name: "BCWIN",
+        description: "BCWIN",
+        start_url: "/",
+        display: "standalone",
+        background_color: "#ffffff",
+        theme_color: "#000000",
+        icons: [
+            {
+                src: "/icon-192x192.png",
+                sizes: "192x192",
+                type: "image/png",
+            },
+            {
+                src: "/icon-512x512.png",
+                sizes: "512x512",
+                type: "image/png",
+            },
+        ],
+    };
+}

+ 7 - 0
src/utils/index.ts

@@ -53,3 +53,10 @@ export const neReg = /^[A-Za-z0-9]+$/;
 export const isNE = (value: string) => {
     return neReg.test(value);
 };
+
+/**
+ * @description 是否是pwa
+ */
+export const isInStandaloneMode = () =>
+    window.matchMedia("(display-mode: standalone)").matches ||
+    document.referrer.includes("android-app://");