diff --git a/.vscode/settings.json b/.vscode/settings.json index 7b016a8..e012065 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "java.compile.nullAnalysis.mode": "automatic" + "java.compile.nullAnalysis.mode": "automatic", + "java.configuration.updateBuildConfiguration": "interactive" } \ No newline at end of file diff --git a/README.md b/README.md index 42a1f51..b0ebbcd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,121 @@ -# Open Source Social Networking Applications for Gen-Z Entrepreneurs +# BizBuch -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) \ No newline at end of file +

+ Open-source social networking without monopoly.
+ Self-hostable • Community-owned • Server-choice enabled +

+ +

+ + Build Status + + + License + + + Stars + + + Issues + + + Pull Requests + + + Last Commit + + + Repo Size + +

+ +--- + +## 🚀 About + +**BizBuch** is an open-source social networking application originally built to connect entrepreneurs. +It has now evolved into a broader mission: **ending the monopoly of centralized social networking** by enabling anyone to host their own social network. + +Unlike traditional social platforms where a single company owns the servers, data, rules, and reach — BizBuch can be deployed by **anyone** on a dedicated server (**on-prem** or **cloud**) to create independent community networks. + +--- + + + + + +## ✨ Key Idea + +Centralized social networks often mean: + +- one company controls the infrastructure +- one platform owns user data +- one authority decides moderation policies +- users are locked into the same network + +BizBuch flips that model. + +✅ **Self-host your own server** +✅ **Communities control rules and identity** +✅ **Users can choose which server to join** + +--- + +## 🌍 Community Servers (Niche Networks) + +BizBuch enables servers to be created around a niche or purpose, such as: + +- 🌱 Environment & sustainability +- 🎭 Culture, traditions & communities +- ✈️ Travel and tourism groups +- 🚀 Entrepreneurship & startups +- 🏢 Companies / internal networks +- 🏘️ Cities, local neighborhoods & communities + +Each deployment becomes its own independent social network — built and owned by the community running it. + +--- + +## ⚙️ Advanced Setting (Coming Soon) + +BizBuch will include an **Advanced Setting** screen *(coming soon)* where users can: + +- Select the server they want to join +- Connect to that server's community network +- Switch servers based on interest, region, or purpose + +This enables a decentralized-style experience: +> **One app, many communities, user choice.** + +--- + +## ✅ Project Goals + +- Provide a self-hostable social networking platform +- Reduce dependency on centralized social monopolies +- Enable niche and community-owned networks +- Support server selection across deployments + +--- + +## 🧩 Contributing + +Contributions are welcome! +If you’d like to improve BizBuch, feel free to: + +- open issues for bugs / feature requests +- submit pull requests +- improve documentation + +--- + +## 📄 License + +This project is licensed under the **MIT License**. +See [`LICENSE`](./LICENSE) for details. + +--- + +## ⭐ Support + +If you like the project, consider giving it a ⭐ on GitHub — it helps the project grow!- 🎭 Culture, traditions & community networks diff --git a/package-lock.json b/package-lock.json index f6fad4f..08aaefb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { - "name": "startup_overflow", - "version": "0.0.1", + "name": "BizBuch", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "startup_overflow", - "version": "0.0.1", + "name": "BizBuch", + "version": "2.0.0", + "license": "MIT", "dependencies": { "@callstack/react-theme-provider": "^3.0.9", "@fortawesome/fontawesome-svg-core": "^6.4.0", @@ -64,12 +65,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -78,29 +79,29 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -117,9 +118,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.5.tgz", - "integrity": "sha512-fcdRcWahONYo+JRnJg1/AekOacGvKx12Gu0qXJXFi2WBqQA1i7+O5PaxRB7kxE/Op94dExnCiiar6T09pvdHpA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.6.tgz", + "integrity": "sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==", "dev": true, "license": "MIT", "dependencies": { @@ -136,13 +137,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -165,12 +166,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -181,18 +182,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", - "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.5", + "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "engines": { @@ -221,17 +222,17 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", - "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.6.tgz", + "integrity": "sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "debug": "^4.4.1", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", "lodash.debounce": "^4.0.8", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -261,27 +262,27 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -304,9 +305,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -331,15 +332,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -390,40 +391,40 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", - "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2" + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -500,14 +501,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", - "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -610,13 +611,13 @@ } }, "node_modules/@babel/plugin-syntax-export-default-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.27.1.tgz", - "integrity": "sha512-eBC/3KSekshx19+N40MzjWqJd7KTEdOoLesAfa4IDFI8eRz5a47i5Oszus6zG/cwIXN63YhgLOMSSNJx49sENg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.28.6.tgz", + "integrity": "sha512-Svlx1fjJFnNz0LZeUaybRukSxZI3KkpApUmIRzEdXC5k8ErTOz0OD0kNrICi5Vc3GlpP5ZCeRyRO+mfWTSz+iQ==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -626,13 +627,13 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", - "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.28.6.tgz", + "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -642,13 +643,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -658,12 +659,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -697,13 +698,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -815,13 +816,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -864,15 +865,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.6.tgz", + "integrity": "sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -882,14 +883,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { @@ -916,13 +917,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", - "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -932,14 +933,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -949,14 +950,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", - "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.3", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -966,18 +967,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", - "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.4" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -987,14 +988,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1021,14 +1022,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1054,14 +1055,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz", + "integrity": "sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1087,14 +1088,14 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", - "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1104,13 +1105,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz", - "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1188,13 +1189,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1220,13 +1221,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz", - "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1269,14 +1270,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1355,13 +1356,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1371,13 +1372,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1387,17 +1388,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz", - "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.4" + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1424,13 +1425,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1440,13 +1441,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz", - "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1473,14 +1474,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1490,15 +1491,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1540,17 +1541,17 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", + "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-syntax-jsx": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1592,13 +1593,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz", - "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.6.tgz", + "integrity": "sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1608,14 +1609,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1678,13 +1679,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { @@ -1743,17 +1744,17 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz", - "integrity": "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" + "@babel/plugin-syntax-typescript": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1779,14 +1780,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1813,14 +1814,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1830,76 +1831,76 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz", - "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.6.tgz", + "integrity": "sha512-GaTI4nXDrs7l0qaJ6Rg06dtOXTBCG6TMDB44zbqofCIC4PqC7SEvmFFtpxzCDw9W5aJ7RKVshgXTLvLdBFV/qw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", + "@babel/compat-data": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.28.0", - "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.6", + "@babel/plugin-transform-async-to-generator": "^7.28.6", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.5", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.28.3", - "@babel/plugin-transform-classes": "^7.28.4", - "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-dotall-regex": "^7.28.6", "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.28.6", "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.0", - "@babel/plugin-transform-exponentiation-operator": "^7.28.5", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.28.4", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.28.5", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.28.4", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.6", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", @@ -1930,40 +1931,40 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", "debug": "^4.3.1" }, "engines": { @@ -1972,17 +1973,17 @@ }, "node_modules/@babel/traverse--for-generate-function-map": { "name": "@babel/traverse", - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", "debug": "^4.3.1" }, "engines": { @@ -1990,9 +1991,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -2035,9 +2036,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3460,40 +3461,31 @@ "license": "MIT" }, "node_modules/@react-native/virtualized-lists": { - "version": "0.82.1", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.82.1.tgz", - "integrity": "sha512-f5zpJg9gzh7JtCbsIwV+4kP3eI0QBuA93JGmwFRd4onQ3DnCjV2J5pYqdWtM95sjSKK1dyik59Gj01lLeKqs1Q==", + "version": "0.72.8", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz", + "integrity": "sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==", + "dev": true, "license": "MIT", "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, - "engines": { - "node": ">= 20.19.4" - }, "peerDependencies": { - "@types/react": "^19.1.1", - "react": "*", "react-native": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } } }, "node_modules/@react-navigation/bottom-tabs": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.7.3.tgz", - "integrity": "sha512-aqsutx8auVaPySf8p165Cqj8K2s8e96uXYViU01sEGrBHdOg/pdR7XPiLoqmpsYcRifwZJlZwLAENhzyqdL/DQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.10.1.tgz", + "integrity": "sha512-MirOzKEe/rRwPSE9HMrS4niIo0LyUhewlvd01TpzQ1ipuXjH2wJbzAM9gS/r62zriB6HMHz2OY6oIRduwQJtTw==", "license": "MIT", "dependencies": { - "@react-navigation/elements": "^2.8.1", + "@react-navigation/elements": "^2.9.5", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { - "@react-navigation/native": "^7.1.19", + "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", @@ -3501,12 +3493,12 @@ } }, "node_modules/@react-navigation/core": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.13.0.tgz", - "integrity": "sha512-Fc/SO23HnlGnkou/z8JQUzwEMvhxuUhr4rdPTIZp/c8q1atq3k632Nfh8fEiGtk+MP1wtIvXdN2a5hBIWpLq3g==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.14.0.tgz", + "integrity": "sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g==", "license": "MIT", "dependencies": { - "@react-navigation/routers": "^7.5.1", + "@react-navigation/routers": "^7.5.3", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", @@ -3520,9 +3512,9 @@ } }, "node_modules/@react-navigation/elements": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.8.1.tgz", - "integrity": "sha512-MLmuS5kPAeAFFOylw89WGjgEFBqGj/KBK6ZrFrAOqLnTqEzk52/SO1olb5GB00k6ZUCDZKJOp1BrLXslxE6TgQ==", + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.5.tgz", + "integrity": "sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g==", "license": "MIT", "dependencies": { "color": "^4.2.3", @@ -3531,7 +3523,7 @@ }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", - "@react-navigation/native": "^7.1.19", + "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" @@ -3543,12 +3535,12 @@ } }, "node_modules/@react-navigation/native": { - "version": "7.1.19", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.19.tgz", - "integrity": "sha512-fM7q8di4Q8sp2WUhiUWOe7bEDRyRhbzsKQOd5N2k+lHeCx3UncsRYuw4Q/KN0EovM3wWKqMMmhy/YWuEO04kgw==", + "version": "7.1.28", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.28.tgz", + "integrity": "sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==", "license": "MIT", "dependencies": { - "@react-navigation/core": "^7.13.0", + "@react-navigation/core": "^7.14.0", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", @@ -3560,18 +3552,18 @@ } }, "node_modules/@react-navigation/native-stack": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.6.2.tgz", - "integrity": "sha512-CB6chGNLwJYiyOeyCNUKx33yT7XJSwRZIeKHf4S1vs+Oqu3u9zMnvGUIsesNgbgX0xy16gBqYsrWgr0ZczBTtA==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.10.1.tgz", + "integrity": "sha512-8jt7olKysn07HuKKSjT/ahZZTV+WaZa96o9RI7gAwh7ATlUDY02rIRttwvCyjovhSjD9KCiuJ+Hd4kwLidHwJw==", "license": "MIT", "dependencies": { - "@react-navigation/elements": "^2.8.1", + "@react-navigation/elements": "^2.9.5", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0", "warn-once": "^0.1.1" }, "peerDependencies": { - "@react-navigation/native": "^7.1.19", + "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", @@ -3579,25 +3571,26 @@ } }, "node_modules/@react-navigation/routers": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.1.tgz", - "integrity": "sha512-pxipMW/iEBSUrjxz2cDD7fNwkqR4xoi0E/PcfTQGCcdJwLoaxzab5kSadBLj1MTJyT0YRrOXL9umHpXtp+Dv4w==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", + "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", "license": "MIT", "dependencies": { "nanoid": "^3.3.11" } }, "node_modules/@react-navigation/stack": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-7.6.2.tgz", - "integrity": "sha512-DVWYYTj5/QXYlFuO8Y232aDcJ4SFABQs9m99BYPs+hepjXzXivva9L4rbmsymFtqEPASx69dzX+nIKzlnxyqpw==", + "version": "7.6.16", + "resolved": "https://registry.npmjs.org/@react-navigation/stack/-/stack-7.6.16.tgz", + "integrity": "sha512-kYmogwU2jpxmjIrGO2P9PJCwgBHiju7OdItRkhFEHHppwU4jJQx/ViJtJ9ib3G4pqpurFdmqn1YsVB4bf7Zmxw==", "license": "MIT", "dependencies": { - "@react-navigation/elements": "^2.8.1", - "color": "^4.2.3" + "@react-navigation/elements": "^2.9.5", + "color": "^4.2.3", + "use-latest-callback": "^0.2.4" }, "peerDependencies": { - "@react-navigation/native": "^7.1.19", + "@react-navigation/native": "^7.1.28", "react": ">= 18.2.0", "react-native": "*", "react-native-gesture-handler": ">= 2.0.0", @@ -3961,9 +3954,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.90.13", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.13.tgz", - "integrity": "sha512-3VzxSkv4ojPPHu0WfOwZ/W5CuN7evAXPzQS+Py2glGxk59Wp+k2T/wgRfrgXAcX1kCTvD9RYUcVEHkMXkEN5jw==", + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", "license": "MIT", "funding": { "type": "github", @@ -3971,12 +3964,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.90.13", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.13.tgz", - "integrity": "sha512-i6DY9wnghE0ghHJfDrnnFNatn4CNBzMZv4xPzKB7Lb9zMAoImAxPKoGK9gLOm79aopDa07p6ytlFFWotvwj3DQ==", + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.20.tgz", + "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.90.13" + "@tanstack/query-core": "5.90.20" }, "funding": { "type": "github", @@ -4087,31 +4080,32 @@ } }, "node_modules/@types/node": { - "version": "24.10.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", - "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@types/react": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", - "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", + "version": "19.2.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz", + "integrity": "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==", "devOptional": true, "license": "MIT", "dependencies": { - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-native": { - "version": "0.70.19", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.19.tgz", - "integrity": "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg==", + "version": "0.72.8", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.72.8.tgz", + "integrity": "sha512-St6xA7+EoHN5mEYfdWnfYt0e8u6k2FR0P9s2arYgakQGFgU1f9FlPrIEcj0X24pLCF5c5i3WVuLCUdiCYHmOoA==", "dev": true, "license": "MIT", "dependencies": { + "@react-native/virtualized-lists": "^0.72.4", "@types/react": "*" } }, @@ -4137,6 +4131,16 @@ "@types/react-native": "^0.70" } }, + "node_modules/@types/react-native-vector-icons/node_modules/@types/react-native": { + "version": "0.70.19", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.19.tgz", + "integrity": "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-test-renderer": { "version": "19.1.0", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz", @@ -4154,9 +4158,9 @@ "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.34", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", - "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -4169,21 +4173,20 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", - "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", + "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/type-utils": "8.46.3", - "@typescript-eslint/utils": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/type-utils": "8.53.1", + "@typescript-eslint/utils": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4193,23 +4196,23 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.3", + "@typescript-eslint/parser": "^8.53.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", - "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", + "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4224,15 +4227,15 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", - "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz", + "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.3", - "@typescript-eslint/types": "^8.46.3", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.53.1", + "@typescript-eslint/types": "^8.53.1", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4246,14 +4249,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", - "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz", + "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3" + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4264,9 +4267,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", - "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz", + "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==", "dev": true, "license": "MIT", "engines": { @@ -4281,17 +4284,17 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", - "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz", + "integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3", - "@typescript-eslint/utils": "8.46.3", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1", + "@typescript-eslint/utils": "8.53.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4306,9 +4309,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", - "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", + "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", "dev": true, "license": "MIT", "engines": { @@ -4320,22 +4323,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", - "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz", + "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.3", - "@typescript-eslint/tsconfig-utils": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/visitor-keys": "8.46.3", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/project-service": "8.53.1", + "@typescript-eslint/tsconfig-utils": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4362,16 +4364,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", - "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz", + "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.3", - "@typescript-eslint/types": "8.46.3", - "@typescript-eslint/typescript-estree": "8.46.3" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4386,13 +4388,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", - "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz", + "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/types": "8.53.1", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4424,9 +4426,9 @@ "license": "ISC" }, "node_modules/@vscode/sudo-prompt": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.1.tgz", - "integrity": "sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz", + "integrity": "sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw==", "devOptional": true, "license": "MIT" }, @@ -4887,14 +4889,14 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", - "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", + "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.7", - "@babel/helper-define-polyfill-provider": "^0.6.5", + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.6", "semver": "^6.3.1" }, "peerDependencies": { @@ -4916,13 +4918,13 @@ } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", - "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", + "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5" + "@babel/helper-define-polyfill-provider": "^0.6.6" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5021,9 +5023,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -5076,27 +5078,6 @@ "ms": "2.0.0" } }, - "node_modules/body-parser/node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5104,16 +5085,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/body-parser/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -5143,9 +5114,9 @@ } }, "node_modules/browserslist": { - "version": "4.27.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", - "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "funding": [ { "type": "opencollective", @@ -5162,11 +5133,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.19", - "caniuse-lite": "^1.0.30001751", - "electron-to-chromium": "^1.5.238", - "node-releases": "^2.0.26", - "update-browserslist-db": "^1.1.4" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -5294,9 +5265,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001753", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", - "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", "funding": [ { "type": "opencollective", @@ -5652,13 +5623,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.46.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.46.0.tgz", - "integrity": "sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==", + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.48.0.tgz", + "integrity": "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==", "devOptional": true, "license": "MIT", "dependencies": { - "browserslist": "^4.26.3" + "browserslist": "^4.28.1" }, "funding": { "type": "opencollective", @@ -5803,9 +5774,9 @@ "license": "CC0-1.0" }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "devOptional": true, "license": "MIT" }, @@ -5907,9 +5878,9 @@ } }, "node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -6125,9 +6096,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.244", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz", - "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==", + "version": "1.5.278", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.278.tgz", + "integrity": "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw==", "license": "ISC" }, "node_modules/emittery": { @@ -6181,9 +6152,9 @@ } }, "node_modules/envinfo": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.20.0.tgz", - "integrity": "sha512-+zUomDcLXsVkQ37vUqWBvQwLaLlj8eZPSi61llaEFAVBY5mhcXdaSw1pSJVl4yTYD5g/gEfpNl28YYk4IPvrrg==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "devOptional": true, "license": "MIT", "bin": { @@ -6212,23 +6183,27 @@ } }, "node_modules/errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.2.tgz", + "integrity": "sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw==", "devOptional": true, "license": "MIT", "dependencies": { - "accepts": "~1.3.7", + "accepts": "~1.3.8", "escape-html": "~1.0.3" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", "dev": true, "license": "MIT", "dependencies": { @@ -6315,27 +6290,27 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.24.1", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", + "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", + "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", + "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" }, "engines": { @@ -6558,9 +6533,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.0.1.tgz", - "integrity": "sha512-EE44T0OSMCeXhDrrdsbKAhprobKkPtJTbQz5yEktysNpHeDZTAL1SfDTNKmcFfJkY6yrQLtTKZALrD3j/Gpmiw==", + "version": "29.12.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.12.1.tgz", + "integrity": "sha512-Rxo7r4jSANMBkXLICJKS0gjacgyopfNAsoS0e3R9AHnjoKuQOaaPfmsDJPi8UWwygI099OV/K/JhpYRVkxD4AA==", "dev": true, "license": "MIT", "dependencies": { @@ -6847,9 +6822,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -7035,9 +7010,9 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "devOptional": true, "license": "ISC", "dependencies": { @@ -7649,25 +7624,29 @@ "license": "MIT" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -9270,9 +9249,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true, "license": "MIT" }, @@ -10856,37 +10835,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/raw-body/node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react": { "version": "19.1.1", "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", @@ -10940,9 +10888,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.69.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.69.0.tgz", - "integrity": "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==", + "version": "7.71.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz", + "integrity": "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -10956,9 +10904,9 @@ } }, "node_modules/react-is": { - "version": "19.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz", - "integrity": "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", "license": "MIT" }, "node_modules/react-native": { @@ -11070,9 +11018,9 @@ } }, "node_modules/react-native-gesture-handler": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.29.1.tgz", - "integrity": "sha512-du3qmv0e3Sm7qsd9SfmHps+AggLiylcBBQ8ztz7WUtd8ZjKs5V3kekAbi9R2W9bRLSg47Ntp4GGMYZOhikQdZA==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.30.0.tgz", + "integrity": "sha512-5YsnKHGa0X9C8lb5oCnKm0fLUPM6CRduvUUw2Bav4RIj/C3HcFh4RIUnF8wgG6JQWCL1//gRx4v+LVWgcIQdGA==", "license": "MIT", "dependencies": { "@egjs/hammerjs": "^2.0.17", @@ -11160,9 +11108,9 @@ } }, "node_modules/react-native-screens": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.18.0.tgz", - "integrity": "sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.20.0.tgz", + "integrity": "sha512-wg3ILSd8yHM2YMsWqDjr1+Rxj1qn9CrzZ8qAqDXYd+jf6p3GIMwi+NugFUbRBRZMXs3MNEXCS1vAkvc2ZwpaAA==", "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", @@ -11189,9 +11137,9 @@ } }, "node_modules/react-native-svg-transformer": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-1.5.2.tgz", - "integrity": "sha512-eW4hOtrd30s4SRdN4X1XYxTCu1czsxDGQKmfQ3RFbZMN5yw4ZmiKGGr+lXbQW4uDGZvSoGd9FHL1f+rgGoKg8Q==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/react-native-svg-transformer/-/react-native-svg-transformer-1.5.3.tgz", + "integrity": "sha512-M4uFg5pUt35OMgjD4rWWbwd6PmxV96W7r/gQTTa+iZA5B+jO6aURhzAZGLHSrg1Kb91cKG0Rildy9q1WJvYstg==", "license": "MIT", "dependencies": { "@svgr/core": "^8.1.0", @@ -11269,6 +11217,29 @@ "node": ">=10" } }, + "node_modules/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.82.1", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.82.1.tgz", + "integrity": "sha512-f5zpJg9gzh7JtCbsIwV+4kP3eI0QBuA93JGmwFRd4onQ3DnCjV2J5pYqdWtM95sjSKK1dyik59Gj01lLeKqs1Q==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.1", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-native/node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -11679,24 +11650,24 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -11717,6 +11688,15 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -11730,9 +11710,9 @@ } }, "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -11748,15 +11728,15 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" @@ -11834,9 +11814,9 @@ "license": "ISC" }, "node_modules/sf-symbols-typescript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.1.0.tgz", - "integrity": "sha512-ezT7gu/SHTPIOEEoG6TF+O0m5eewl0ZDAO4AtdBi5HjsrUI6JdCG17+Q8+aKp0heM06wZKApRCn5olNbs0Wb/A==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz", + "integrity": "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==", "license": "MIT", "engines": { "node": ">=10" @@ -12462,9 +12442,9 @@ "license": "CC0-1.0" }, "node_modules/terser": { - "version": "5.44.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", - "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -12544,6 +12524,54 @@ "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -12572,9 +12600,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -12820,9 +12848,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -13045,9 +13073,9 @@ "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -13137,15 +13165,18 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { diff --git a/src/application/post/handler/CreatePostHandler.ts b/src/application/post/handler/CreatePostHandler.ts index c58d716..7e75265 100644 --- a/src/application/post/handler/CreatePostHandler.ts +++ b/src/application/post/handler/CreatePostHandler.ts @@ -5,7 +5,7 @@ export class CreatePostHandler { constructor(private readonly postRepository: IPostRepository) {} async handle(post: PostRequestBody): Promise { - await this.postRepository.create(post); + const response = await this.postRepository.create(post); return true; } } diff --git a/src/assets/images/azadi.png b/src/assets/images/azadi.png deleted file mode 100644 index 3471592..0000000 Binary files a/src/assets/images/azadi.png and /dev/null differ diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png deleted file mode 100644 index 439c12a..0000000 Binary files a/src/assets/images/logo.png and /dev/null differ diff --git a/src/assets/images/logo.svg b/src/assets/images/logo.svg index 1e6893e..9b3b078 100644 --- a/src/assets/images/logo.svg +++ b/src/assets/images/logo.svg @@ -1,236 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/splash.svg b/src/assets/images/splash.svg index 711fe0f..e4b2b81 100644 --- a/src/assets/images/splash.svg +++ b/src/assets/images/splash.svg @@ -1,14 +1,73 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/screenshots/Screenshot_1.png b/src/assets/screenshots/Screenshot_1.png new file mode 100644 index 0000000..8366cbe Binary files /dev/null and b/src/assets/screenshots/Screenshot_1.png differ diff --git a/src/assets/screenshots/Screenshot_2.png b/src/assets/screenshots/Screenshot_2.png new file mode 100644 index 0000000..8432b16 Binary files /dev/null and b/src/assets/screenshots/Screenshot_2.png differ diff --git a/src/assets/screenshots/Screenshot_3.png b/src/assets/screenshots/Screenshot_3.png new file mode 100644 index 0000000..2335820 Binary files /dev/null and b/src/assets/screenshots/Screenshot_3.png differ diff --git a/src/core/config.ts b/src/core/config.ts index 84dc2dc..01a90ec 100644 --- a/src/core/config.ts +++ b/src/core/config.ts @@ -1,7 +1,83 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; + +const DEFAULT_IP = '192.168.0.5'; +const DEFAULT_PORT = '8000'; + +class ConfigManager { + private static instance: ConfigManager; + private ip: string = DEFAULT_IP; + private port: string = DEFAULT_PORT; + private initialized: boolean = false; + + private constructor() {} + + static getInstance(): ConfigManager { + if (!ConfigManager.instance) { + ConfigManager.instance = new ConfigManager(); + } + return ConfigManager.instance; + } + + async initialize(): Promise { + if (this.initialized) return; + + try { + const savedIp = await AsyncStorage.getItem('serverIp'); + const savedPort = await AsyncStorage.getItem('serverPort'); + + if (savedIp) this.ip = savedIp; + if (savedPort) this.port = savedPort; + + this.initialized = true; + } catch (error) { + console.error('Error loading config:', error); + } + } + + getApiBaseUrl(): string { + return `http://${this.ip}:${this.port}/`; + } + + getChatWsUrl(): string { + return `ws://${this.ip}:${this.port}/`; + } + + getIp(): string { + return this.ip; + } + + getPort(): string { + return this.port; + } + + async setIp(newIp: string): Promise { + this.ip = newIp; + await AsyncStorage.setItem('serverIp', newIp); + } + + async setPort(newPort: string): Promise { + this.port = newPort; + await AsyncStorage.setItem('serverPort', newPort); + } + + async resetToDefaults(): Promise { + this.ip = DEFAULT_IP; + this.port = DEFAULT_PORT; + await AsyncStorage.removeItem('serverIp'); + await AsyncStorage.removeItem('serverPort'); + } +} + +export const configManager = ConfigManager.getInstance(); + +// Legacy Config object for backward compatibility const Config = { - API_BASE_URL: 'http://192.168.1.11:8000/', - CHAT_API_URL: 'http://192.168.1.11:8000/', - CHAT_WS_URL: 'ws://192.168.1.11:8000/', + get API_BASE_URL() { + return configManager.getApiBaseUrl(); + }, + get CHAT_WS_URL() { + return configManager.getChatWsUrl(); + }, }; export default Config; diff --git a/src/core/http.ts b/src/core/http.ts index 4cc477c..b493dad 100644 --- a/src/core/http.ts +++ b/src/core/http.ts @@ -122,56 +122,4 @@ export async function deleteAuth( const data = await response.json().catch(() => ({})); throw new Error(data?.message || 'Request failed'); } -} - -// Chat API functions (port 8001) -export async function getChatAuth( - endpoint: string, - headers: Record = {} -): Promise { - const token = await AsyncStorage.getItem('authToken'); - - const response = await fetch(`${Config.CHAT_API_URL}${endpoint}`, { - method: 'GET', - headers: { - accept: 'application/json', - Authorization: `Bearer ${token}`, - ...headers, - }, - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message || 'Request failed'); - } - - return data as T; -} - -export async function postChatAuth( - endpoint: string, - body: unknown, - headers: Record = {} -): Promise { - const token = await AsyncStorage.getItem('authToken'); - - const response = await fetch(`${Config.CHAT_API_URL}${endpoint}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - accept: 'application/json', - Authorization: `Bearer ${token}`, - ...headers, - }, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data?.message || 'Request failed'); - } - - return data as T; -} +} \ No newline at end of file diff --git a/src/data/repositories/ChatRepository.ts b/src/data/repositories/ChatRepository.ts index be26b16..8fa2922 100644 --- a/src/data/repositories/ChatRepository.ts +++ b/src/data/repositories/ChatRepository.ts @@ -2,40 +2,40 @@ import { IChatRepository } from "../../domain/chat/repositories/IChatRepository"; import { Conversation } from "../../domain/chat/entities/Conversation"; import { Message, MessagesResponse, SendMessagePayload } from "../../domain/chat/entities/Message"; -import { getChatAuth, postChatAuth } from "../../core/http"; +import { getAuth, postAuth } from "../../core/http"; export class ChatRepository implements IChatRepository { async getConversations(): Promise { - const response = await getChatAuth("chat/conversations/"); + const response = await getAuth("chat/conversations/"); return response; } async getConversationById(conversationId: number): Promise { - const response = await getChatAuth(`chat/conversations/${conversationId}/`); + const response = await getAuth(`chat/conversations/${conversationId}/`); return response; } async startConversation(userId: number): Promise { - const response = await postChatAuth("chat/conversations/start/", { + const response = await postAuth("chat/conversations/start/", { user_id: userId, }); return response; } async getMessages(conversationId: number, page: number = 1): Promise { - const response = await getChatAuth( + const response = await getAuth( `chat/conversations/${conversationId}/messages/?page=${page}` ); return response; } async sendMessage(payload: SendMessagePayload): Promise { - const response = await postChatAuth("chat/send/", payload); + const response = await postAuth("chat/send/", payload); return response; } async markAsRead(conversationId: number, messageIds: number[]): Promise { - await postChatAuth(`chat/conversations/${conversationId}/read/`, { + await postAuth(`chat/conversations/${conversationId}/read/`, { message_ids: messageIds, }); } diff --git a/src/data/repositories/PostRepository.ts b/src/data/repositories/PostRepository.ts index 2aeee94..0abeded 100644 --- a/src/data/repositories/PostRepository.ts +++ b/src/data/repositories/PostRepository.ts @@ -4,12 +4,13 @@ import { postAuth, getAuth } from '../../core/http'; export class PostRepository implements IPostRepository { async getAllPosts(): Promise { - return await getAuth('posts'); + const allPosts = await getAuth('posts'); + return allPosts; } async getPostById(id: string): Promise { - return await getAuth(`posts/${id}`); + return await getAuth(`posts/${id}`); } async create(postBody: PostRequestBody): Promise { - await postAuth('posts/', postBody); + return await postAuth('posts/', postBody); } } diff --git a/src/data/repositories/ProfileRepository.ts b/src/data/repositories/ProfileRepository.ts index 55b703f..f1bb6f4 100644 --- a/src/data/repositories/ProfileRepository.ts +++ b/src/data/repositories/ProfileRepository.ts @@ -1,6 +1,6 @@ import { Profile } from "../../domain/user/entities/Profile"; -import { IProfileRepository, UpdateProfileData } from "../../domain/user/repositories/IProfileRepository"; -import { getAuth, patchAuth } from "../../core/http"; +import { IProfileRepository, UpdateProfileData, AddWorkExperienceData, AddEducationData } from "../../domain/user/repositories/IProfileRepository"; +import { getAuth, patchAuth, postAuth } from "../../core/http"; export class ProfileRepository implements IProfileRepository { async getCurrentUserProfile(): Promise { @@ -19,4 +19,16 @@ export class ProfileRepository implements IProfileRepository { console.log("Updating profile with data:", data); return patchAuth("profiles/me/", data); } + + async addWorkExperience(data: AddWorkExperienceData): Promise { + await postAuth("profiles/me/work-experiences/", data); + } + + async addEducation(data: AddEducationData): Promise { + await postAuth("profiles/me/education/", data); + } + + async followUser(userId: number): Promise { + await postAuth(`profiles/${userId}/follow/`, {}); + } } diff --git a/src/data/services/MediaApi.ts b/src/data/services/MediaApi.ts index 363242c..a6e1b85 100644 --- a/src/data/services/MediaApi.ts +++ b/src/data/services/MediaApi.ts @@ -7,7 +7,7 @@ export interface PresignedUpload { export class MediaApi { async getPresignedUrl(): Promise { - const res = await postAuth('/uploads/presign/', { + const res = await postAuth('uploads/presign/', { contentType: 'image/jpeg', }); diff --git a/src/domain/auth/entities/Auth.ts b/src/domain/auth/entities/Auth.ts index 8c9655c..ff40c73 100644 --- a/src/domain/auth/entities/Auth.ts +++ b/src/domain/auth/entities/Auth.ts @@ -19,6 +19,7 @@ export interface AuthUser { email: string; first_name: string; last_name: string; + profile_photo?: string; } export type AuthResponse = { diff --git a/src/domain/post/entities/Post.ts b/src/domain/post/entities/Post.ts index 480194f..85cad6d 100644 --- a/src/domain/post/entities/Post.ts +++ b/src/domain/post/entities/Post.ts @@ -3,6 +3,7 @@ import { Author } from './Author'; export interface PostResponseBody { id: string; author: string; + author_avatar: string; content: string; image_url?: string; timestamp: string; diff --git a/src/domain/user/repositories/IProfileRepository.ts b/src/domain/user/repositories/IProfileRepository.ts index 1fde211..0bed5cd 100644 --- a/src/domain/user/repositories/IProfileRepository.ts +++ b/src/domain/user/repositories/IProfileRepository.ts @@ -27,8 +27,37 @@ export interface UpdateProfileData { is_public?: boolean; } +export interface AddWorkExperienceData { + company_name: string; + job_title: string; + location: string; + employment_type: string; + start_year: number; + start_month: number; + end_year?: number; + end_month?: number; + is_current: boolean; + company_logo_key?: string; + description: string; + skills: string; +} + +export interface AddEducationData { + name: string; + degrees: string; + field_of_study?: string; + start_year: number; + end_year?: number; + is_current: boolean; + school_logo_key?: string; + description: string; +} + export interface IProfileRepository { getCurrentUserProfile(): Promise; getProfileById(userId: number): Promise; updateProfile(data: UpdateProfileData): Promise; + addWorkExperience(data: AddWorkExperienceData): Promise; + addEducation(data: AddEducationData): Promise; + followUser(userId: number): Promise; } diff --git a/src/presentation/components/molecules/ContactInfoSection.tsx b/src/presentation/components/molecules/ContactInfoSection.tsx new file mode 100644 index 0000000..695d682 --- /dev/null +++ b/src/presentation/components/molecules/ContactInfoSection.tsx @@ -0,0 +1,135 @@ +import React, { useMemo } from 'react'; +import { View, Text, ViewStyle, TextStyle, TouchableOpacity } from 'react-native'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { faGlobe, faPhone, faLink } from '@fortawesome/free-solid-svg-icons'; +import { IconDefinition } from '@fortawesome/fontawesome-svg-core'; +import { theme } from '../../theme'; + +interface ContactItem { + type: 'website' | 'phone' | 'linkedin' | 'twitter'; + label: string; + value: string; +} + +interface ContactInfoSectionProps { + items: ContactItem[]; + onItemPress?: (item: ContactItem) => void; +} + +const iconMap: Record = { + website: faGlobe, + phone: faPhone, + linkedin: faLink, + twitter: faLink, +}; + +/** + * ContactInfoSection Molecule + * Atomic Design: Molecule - Contact information display + * SOLID: Single Responsibility - Display contact info items + */ +export const ContactInfoSection: React.FC = ({ + items, + onItemPress, +}) => { + const containerStyle = useMemo( + () => ({ + backgroundColor: theme.colors.white, + marginBottom: 12, + paddingHorizontal: 16, + paddingVertical: 16, + }), + [], + ); + + const titleStyle = useMemo( + () => ({ + fontSize: 16, + fontWeight: '600', + color: theme.colors.gray900, + marginBottom: 16, + }), + [], + ); + + const rowStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + marginBottom: 16, + }), + [], + ); + + const iconContainerStyle = useMemo( + () => ({ + width: 44, + height: 44, + borderRadius: 22, + backgroundColor: `${theme.colors.primary}15`, + alignItems: 'center', + justifyContent: 'center', + }), + [], + ); + + const infoContainerStyle = useMemo( + () => ({ + marginLeft: 12, + flex: 1, + }), + [], + ); + + const labelStyle = useMemo( + () => ({ + fontSize: 12, + color: theme.colors.gray500, + marginBottom: 2, + }), + [], + ); + + const valueStyle = useMemo( + () => ({ + fontSize: 14, + color: theme.colors.gray700, + }), + [], + ); + + const linkStyle = useMemo( + () => ({ + fontSize: 14, + color: theme.colors.primary, + }), + [], + ); + + if (items.length === 0) return null; + + return ( + + Contact Information + {items.map((item, index) => ( + onItemPress?.(item)} + disabled={!onItemPress} + activeOpacity={onItemPress ? 0.7 : 1} + > + + + + + {item.label} + + {item.value} + + + + ))} + + ); +}; diff --git a/src/presentation/components/molecules/EducationCard.tsx b/src/presentation/components/molecules/EducationCard.tsx new file mode 100644 index 0000000..e440087 --- /dev/null +++ b/src/presentation/components/molecules/EducationCard.tsx @@ -0,0 +1,91 @@ +import React, { useMemo } from 'react'; +import { View, Text, Image, ViewStyle, TextStyle, ImageStyle } from 'react-native'; +import { theme } from '../../theme'; + +interface EducationCardProps { + schoolName: string; + degree?: string; + schoolLogo?: string; + duration?: string; + isLast?: boolean; +} + +/** + * EducationCard Molecule + * Atomic Design: Molecule - Education item display + * SOLID: Single Responsibility - Display single education entry + */ +export const EducationCard: React.FC = ({ + schoolName, + degree, + schoolLogo, + duration, + isLast = false, +}) => { + const containerStyle = useMemo( + () => ({ + flexDirection: 'row', + marginBottom: isLast ? 0 : 12, + paddingBottom: isLast ? 0 : 12, + borderBottomWidth: isLast ? 0 : 1, + borderColor: theme.colors.border, + }), + [isLast], + ); + + const logoStyle = useMemo( + () => ({ + width: 50, + height: 50, + borderRadius: 8, + marginRight: 12, + backgroundColor: theme.colors.gray100, + }), + [], + ); + + const infoContainerStyle = useMemo( + () => ({ + flex: 1, + }), + [], + ); + + const schoolStyle = useMemo( + () => ({ + fontSize: 15, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const degreeStyle = useMemo( + () => ({ + fontSize: 14, + color: theme.colors.gray600, + marginTop: 2, + }), + [], + ); + + const durationStyle = useMemo( + () => ({ + fontSize: 13, + color: theme.colors.gray500, + marginTop: 4, + }), + [], + ); + + return ( + + {schoolLogo && } + + {schoolName} + {degree && {degree}} + {duration && {duration}} + + + ); +}; diff --git a/src/presentation/components/molecules/LogoSection.tsx b/src/presentation/components/molecules/LogoSection.tsx index 8ff2018..3398a6a 100644 --- a/src/presentation/components/molecules/LogoSection.tsx +++ b/src/presentation/components/molecules/LogoSection.tsx @@ -1,16 +1,13 @@ import React, { useMemo } from 'react'; import { View, ViewStyle, TextStyle } from 'react-native'; -import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; -import { faFire } from '@fortawesome/free-solid-svg-icons'; -import { GradientCircle } from '../atoms/GradientCircle'; import { GradientText } from '../atoms/GradientText'; import { theme } from '../../theme'; +import Logo from '../../../assets/images/logo.svg'; const { logoSection } = theme.components; interface LogoSectionProps { title?: string; - iconSize?: number; logoSize?: number; } @@ -19,11 +16,10 @@ interface LogoSectionProps { * Atomic Design: Molecule - App logo with title * Single Responsibility: Display branding elements * SOLID: Open/Closed - Styles from theme - * Reuses: GradientCircle atom, GradientText atom + * Reuses: GradientText atom, Logo SVG */ export const LogoSection: React.FC = ({ title = 'BizBuch', - iconSize = logoSection.defaultIconSize, logoSize = logoSection.defaultLogoSize, }) => { const containerStyle = useMemo( @@ -45,9 +41,7 @@ export const LogoSection: React.FC = ({ return ( - - - + {title} ); diff --git a/src/presentation/components/molecules/LogoutButton.tsx b/src/presentation/components/molecules/LogoutButton.tsx new file mode 100644 index 0000000..16ae24e --- /dev/null +++ b/src/presentation/components/molecules/LogoutButton.tsx @@ -0,0 +1,58 @@ +import React, { useMemo } from 'react'; +import { View, Text, TouchableOpacity, ViewStyle, TextStyle } from 'react-native'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { faSignOutAlt } from '@fortawesome/free-solid-svg-icons'; +import { theme } from '../../theme'; + +interface LogoutButtonProps { + onPress: () => void; +} + +/** + * LogoutButton Molecule + * Atomic Design: Molecule - Logout button with icon + * SOLID: Single Responsibility - Display and handle logout action + */ +export const LogoutButton: React.FC = ({ onPress }) => { + const containerStyle = useMemo( + () => ({ + paddingHorizontal: 16, + paddingVertical: 24, + paddingBottom: 40, + }), + [], + ); + + const buttonStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 8, + paddingVertical: 14, + borderRadius: 24, + backgroundColor: theme.colors.white, + borderWidth: 1.5, + borderColor: '#dc2626', + }), + [], + ); + + const textStyle = useMemo( + () => ({ + color: '#dc2626', + fontWeight: '600', + fontSize: 15, + }), + [], + ); + + return ( + + + + Sign Out + + + ); +}; diff --git a/src/presentation/components/molecules/NewsFeedHeader.tsx b/src/presentation/components/molecules/NewsFeedHeader.tsx index b2770e8..2f9659c 100644 --- a/src/presentation/components/molecules/NewsFeedHeader.tsx +++ b/src/presentation/components/molecules/NewsFeedHeader.tsx @@ -4,6 +4,7 @@ import { FeedNavigationProp } from '../../navigation/news-feed-navigation/NewsFe import { LogoSection } from './LogoSection'; import { ProfileAvatarButton } from './ProfileAvatarButton'; import { useNewsFeedHeader } from '../../../ui/hooks/useNewsFeedHeader'; +import { useUserAvatar } from '../../../ui/hooks/useUserAvatar'; import { theme } from '../../theme'; const { newsFeedHeader } = theme.components; @@ -21,6 +22,7 @@ interface NewsFeedHeaderProps { */ export const NewsFeedHeader: React.FC = ({ navigation }) => { const { handleProfilePress } = useNewsFeedHeader({ navigation }); + const { avatarUri } = useUserAvatar(); const headerStyle = useMemo( () => ({ @@ -40,7 +42,7 @@ export const NewsFeedHeader: React.FC = ({ navigation }) => diff --git a/src/presentation/components/molecules/ProfileActionButtons.tsx b/src/presentation/components/molecules/ProfileActionButtons.tsx new file mode 100644 index 0000000..542a685 --- /dev/null +++ b/src/presentation/components/molecules/ProfileActionButtons.tsx @@ -0,0 +1,101 @@ +import React, { useMemo } from 'react'; +import { View, Text, TouchableOpacity, ViewStyle, TextStyle } from 'react-native'; +import { theme } from '../../theme'; + +interface ProfileActionButtonsProps { + isCurrentUser: boolean; + onEditProfile?: () => void; + onActivityLog?: () => void; + onMessage?: () => void; + onFollow?: () => void; +} + +/** + * ProfileActionButtons Molecule + * Atomic Design: Molecule - Profile action buttons + * SOLID: Single Responsibility - Display action buttons based on user context + */ +export const ProfileActionButtons: React.FC = ({ + isCurrentUser, + onEditProfile, + onActivityLog, + onMessage, + onFollow, +}) => { + const containerStyle = useMemo( + () => ({ + flexDirection: 'row', + gap: 12, + paddingHorizontal: 16, + paddingBottom: 20, + backgroundColor: theme.colors.white, + }), + [], + ); + + const primaryButtonStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: theme.colors.primary, + paddingVertical: 14, + borderRadius: 24, + alignItems: 'center', + }), + [], + ); + + const primaryButtonTextStyle = useMemo( + () => ({ + color: theme.colors.white, + fontWeight: '600', + fontSize: 15, + }), + [], + ); + + const secondaryButtonStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: theme.colors.white, + paddingVertical: 14, + borderRadius: 24, + alignItems: 'center', + borderWidth: 1.5, + borderColor: theme.colors.primary, + }), + [], + ); + + const secondaryButtonTextStyle = useMemo( + () => ({ + color: theme.colors.primary, + fontWeight: '600', + fontSize: 15, + }), + [], + ); + + return ( + + {isCurrentUser ? ( + <> + + Edit Profile + + + Activity Log + + + ) : ( + <> + + Follow + + + Message + + + )} + + ); +}; diff --git a/src/presentation/components/molecules/ProfileCoverSection.tsx b/src/presentation/components/molecules/ProfileCoverSection.tsx new file mode 100644 index 0000000..6cc5f40 --- /dev/null +++ b/src/presentation/components/molecules/ProfileCoverSection.tsx @@ -0,0 +1,47 @@ +import React, { useMemo } from 'react'; +import { View, Image, ViewStyle, ImageStyle } from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; +import { theme } from '../../theme'; + +interface ProfileCoverSectionProps { + coverImageUri?: string; +} + +/** + * ProfileCoverSection Molecule + * Atomic Design: Molecule - Cover image with gradient overlay + * SOLID: Single Responsibility - Display profile cover image + */ +export const ProfileCoverSection: React.FC = ({ + coverImageUri, +}) => { + const gradientStyle = useMemo( + () => ({ + width: '100%', + height: 180, + }), + [], + ); + + const imageStyle = useMemo( + () => ({ + width: '100%', + height: '100%', + opacity: 0.6, + }), + [], + ); + + return ( + + {coverImageUri && ( + + )} + + ); +}; diff --git a/src/presentation/components/molecules/ProfileInfoSection.tsx b/src/presentation/components/molecules/ProfileInfoSection.tsx new file mode 100644 index 0000000..a268d89 --- /dev/null +++ b/src/presentation/components/molecules/ProfileInfoSection.tsx @@ -0,0 +1,161 @@ +import React, { useMemo } from 'react'; +import { View, Text, Image, ViewStyle, TextStyle, ImageStyle } from 'react-native'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { faMapMarkerAlt, faCheckCircle, faStar } from '@fortawesome/free-solid-svg-icons'; +import { theme } from '../../theme'; + +interface ProfileInfoSectionProps { + avatarUri?: string; + displayName: string; + headline?: string; + location?: string; + bio?: string; + isVerified?: boolean; + isPremium?: boolean; +} + +/** + * ProfileInfoSection Molecule + * Atomic Design: Molecule - Profile avatar, name, headline, location, bio + * SOLID: Single Responsibility - Display user info section + */ +export const ProfileInfoSection: React.FC = ({ + avatarUri, + displayName, + headline, + location, + bio, + isVerified, + isPremium, +}) => { + const containerStyle = useMemo( + () => ({ + alignItems: 'center', + paddingTop: 0, + paddingBottom: 20, + paddingHorizontal: 16, + backgroundColor: theme.colors.white, + }), + [], + ); + + const avatarContainerStyle = useMemo( + () => ({ + marginTop: -64, + marginBottom: 16, + borderRadius: 64, + padding: 4, + backgroundColor: theme.colors.white, + }), + [], + ); + + const avatarStyle = useMemo( + () => ({ + width: 120, + height: 120, + borderRadius: 60, + backgroundColor: theme.colors.gray200, + }), + [], + ); + + const nameRowStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + marginBottom: 8, + }), + [], + ); + + const nameStyle = useMemo( + () => ({ + fontSize: 24, + fontWeight: '700', + color: theme.colors.gray900, + }), + [], + ); + + const headlineStyle = useMemo( + () => ({ + fontSize: 16, + fontWeight: '500', + color: theme.colors.gray600, + marginBottom: 8, + textAlign: 'center', + }), + [], + ); + + const locationRowStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + marginBottom: 12, + }), + [], + ); + + const locationTextStyle = useMemo( + () => ({ + fontSize: 14, + color: theme.colors.gray500, + marginLeft: 8, + }), + [], + ); + + const bioStyle = useMemo( + () => ({ + fontSize: 14, + color: theme.colors.gray600, + textAlign: 'center', + marginBottom: 16, + paddingHorizontal: 20, + lineHeight: 20, + }), + [], + ); + + return ( + + + + + + + {displayName} + {isVerified && ( + + )} + {isPremium && ( + + )} + + + {headline && {headline}} + + {location && ( + + + {location} + + )} + + {bio && {bio}} + + ); +}; diff --git a/src/presentation/components/molecules/ProfileSection.tsx b/src/presentation/components/molecules/ProfileSection.tsx new file mode 100644 index 0000000..f722330 --- /dev/null +++ b/src/presentation/components/molecules/ProfileSection.tsx @@ -0,0 +1,80 @@ +import React, { useMemo } from 'react'; +import { View, Text, TouchableOpacity, ViewStyle, TextStyle } from 'react-native'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { faPlus } from '@fortawesome/free-solid-svg-icons'; +import { theme } from '../../theme'; + +interface ProfileSectionProps { + title: string; + onAddPress?: () => void; + showAddButton?: boolean; + children: React.ReactNode; +} + +/** + * ProfileSection Molecule + * Atomic Design: Molecule - Reusable section card with title + * SOLID: Single Responsibility - Section container with optional add button + * SOLID: Open/Closed - Content passed via children + */ +export const ProfileSection: React.FC = ({ + title, + onAddPress, + showAddButton = false, + children, +}) => { + const containerStyle = useMemo( + () => ({ + backgroundColor: theme.colors.white, + marginBottom: 12, + paddingHorizontal: 16, + paddingVertical: 16, + }), + [], + ); + + const headerRowStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: 16, + }), + [], + ); + + const titleStyle = useMemo( + () => ({ + fontSize: 16, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const addButtonStyle = useMemo( + () => ({ + width: 32, + height: 32, + borderRadius: 16, + backgroundColor: `${theme.colors.primary}15`, + alignItems: 'center', + justifyContent: 'center', + }), + [], + ); + + return ( + + + {title} + {showAddButton && onAddPress && ( + + + + )} + + {children} + + ); +}; diff --git a/src/presentation/components/molecules/ProfileStatsRow.tsx b/src/presentation/components/molecules/ProfileStatsRow.tsx new file mode 100644 index 0000000..e3e7fc0 --- /dev/null +++ b/src/presentation/components/molecules/ProfileStatsRow.tsx @@ -0,0 +1,91 @@ +import React, { useMemo } from 'react'; +import { View, Text, ViewStyle, TextStyle } from 'react-native'; +import { theme } from '../../theme'; + +interface ProfileStatsRowProps { + connectionsCount: number; + followersCount: number; + postsCount: number; +} + +/** + * ProfileStatsRow Molecule + * Atomic Design: Molecule - Profile statistics display + * SOLID: Single Responsibility - Display connections, followers, posts counts + */ +export const ProfileStatsRow: React.FC = ({ + connectionsCount, + followersCount, + postsCount, +}) => { + const containerStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-around', + marginBottom: 20, + borderTopWidth: 1, + borderBottomWidth: 1, + borderColor: theme.colors.border, + paddingVertical: 16, + marginHorizontal: -16, + paddingHorizontal: 16, + backgroundColor: theme.colors.white, + }), + [], + ); + + const statBoxStyle = useMemo( + () => ({ + alignItems: 'center', + flex: 1, + }), + [], + ); + + const dividerStyle = useMemo( + () => ({ + width: 1, + height: 40, + backgroundColor: theme.colors.gray200, + }), + [], + ); + + const numberStyle = useMemo( + () => ({ + fontSize: 22, + fontWeight: '700', + color: theme.colors.gray900, + }), + [], + ); + + const labelStyle = useMemo( + () => ({ + fontSize: 13, + color: theme.colors.gray500, + marginTop: 4, + }), + [], + ); + + return ( + + + {connectionsCount} + Connections + + + + {followersCount} + Followers + + + + {postsCount} + Posts + + + ); +}; diff --git a/src/presentation/components/molecules/SkillsList.tsx b/src/presentation/components/molecules/SkillsList.tsx new file mode 100644 index 0000000..fc633eb --- /dev/null +++ b/src/presentation/components/molecules/SkillsList.tsx @@ -0,0 +1,71 @@ +import React, { useMemo } from 'react'; +import { View, Text, ViewStyle, TextStyle } from 'react-native'; +import { theme } from '../../theme'; + +interface SkillBadgeProps { + skill: string; +} + +/** + * SkillBadge Atom + * Atomic Design: Atom - Single skill badge display + * SOLID: Single Responsibility - Display one skill + */ +export const SkillBadge: React.FC = ({ skill }) => { + const badgeStyle = useMemo( + () => ({ + backgroundColor: `${theme.colors.primary}15`, + paddingVertical: 8, + paddingHorizontal: 14, + borderRadius: 20, + borderWidth: 1, + borderColor: theme.colors.primary, + }), + [], + ); + + const textStyle = useMemo( + () => ({ + fontSize: 13, + color: theme.colors.primary, + fontWeight: '500', + }), + [], + ); + + return ( + + {skill} + + ); +}; + +interface SkillsListProps { + skills: string[]; +} + +/** + * SkillsList Molecule + * Atomic Design: Molecule - Skills list display + * SOLID: Single Responsibility - Display list of skills + */ +export const SkillsList: React.FC = ({ skills }) => { + const containerStyle = useMemo( + () => ({ + flexDirection: 'row', + flexWrap: 'wrap', + gap: 8, + }), + [], + ); + + if (!skills || skills.length === 0) return null; + + return ( + + {skills.map((skill, index) => ( + + ))} + + ); +}; diff --git a/src/presentation/components/molecules/ViewProfileHeader.tsx b/src/presentation/components/molecules/ViewProfileHeader.tsx new file mode 100644 index 0000000..098e40f --- /dev/null +++ b/src/presentation/components/molecules/ViewProfileHeader.tsx @@ -0,0 +1,79 @@ +import React, { useMemo } from 'react'; +import { View, Text, TouchableOpacity, ViewStyle, TextStyle } from 'react-native'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; +import { theme } from '../../theme'; + +interface ViewProfileHeaderProps { + title?: string; + onBack: () => void; +} + +/** + * ViewProfileHeader Molecule + * Atomic Design: Molecule - Profile screen header with back button + * SOLID: Single Responsibility - Display header with navigation + */ +export const ViewProfileHeader: React.FC = ({ + title = 'Profile', + onBack, +}) => { + const containerStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 12, + backgroundColor: theme.colors.white, + borderBottomWidth: 1, + borderColor: theme.colors.border, + }), + [], + ); + + const backButtonStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }), + [], + ); + + const backTextStyle = useMemo( + () => ({ + fontSize: 15, + color: theme.colors.primary, + fontWeight: '500', + }), + [], + ); + + const titleStyle = useMemo( + () => ({ + fontSize: 17, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const placeholderStyle = useMemo( + () => ({ + width: 70, + }), + [], + ); + + return ( + + + + Back + + {title} + + + ); +}; diff --git a/src/presentation/components/molecules/WorkExperienceCard.tsx b/src/presentation/components/molecules/WorkExperienceCard.tsx new file mode 100644 index 0000000..d84ac57 --- /dev/null +++ b/src/presentation/components/molecules/WorkExperienceCard.tsx @@ -0,0 +1,94 @@ +import React, { useMemo } from 'react'; +import { View, Text, Image, ViewStyle, TextStyle, ImageStyle } from 'react-native'; +import { theme } from '../../theme'; + +interface WorkExperienceCardProps { + jobTitle: string; + companyName: string; + companyLogo?: string; + location?: string; + duration?: string; + isLast?: boolean; +} + +/** + * WorkExperienceCard Molecule + * Atomic Design: Molecule - Work experience item display + * SOLID: Single Responsibility - Display single work experience + */ +export const WorkExperienceCard: React.FC = ({ + jobTitle, + companyName, + companyLogo, + location, + duration, + isLast = false, +}) => { + const containerStyle = useMemo( + () => ({ + flexDirection: 'row', + marginBottom: isLast ? 0 : 12, + paddingBottom: isLast ? 0 : 12, + borderBottomWidth: isLast ? 0 : 1, + borderColor: theme.colors.border, + }), + [isLast], + ); + + const logoStyle = useMemo( + () => ({ + width: 50, + height: 50, + borderRadius: 8, + marginRight: 12, + backgroundColor: theme.colors.gray100, + }), + [], + ); + + const infoContainerStyle = useMemo( + () => ({ + flex: 1, + }), + [], + ); + + const titleStyle = useMemo( + () => ({ + fontSize: 15, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const companyStyle = useMemo( + () => ({ + fontSize: 14, + color: theme.colors.gray600, + marginTop: 2, + }), + [], + ); + + const detailStyle = useMemo( + () => ({ + fontSize: 13, + color: theme.colors.gray500, + marginTop: 4, + }), + [], + ); + + return ( + + {companyLogo && } + + {jobTitle} + {companyName} + {location && 📍 {location}} + {duration && {duration}} + + + ); +}; diff --git a/src/presentation/components/organisms/ConnectionBanner.tsx b/src/presentation/components/organisms/ConnectionBanner.tsx index 2f68a7a..217f61d 100644 --- a/src/presentation/components/organisms/ConnectionBanner.tsx +++ b/src/presentation/components/organisms/ConnectionBanner.tsx @@ -6,6 +6,7 @@ import { ErrorBanner } from './ErrorBanner'; import { WarningBanner } from './WarningBanner'; interface ConnectionBannerProps { + conversationId: number | null; isConnecting: boolean; isConnected: boolean; connectionError: WebSocketConnectionError | null; @@ -22,6 +23,7 @@ interface ConnectionBannerProps { * SOLID: Dependency Inversion - Depends on BannerStateResolverService */ export const ConnectionBanner: React.FC = ({ + conversationId, isConnecting, isConnected, connectionError, @@ -29,6 +31,11 @@ export const ConnectionBanner: React.FC = ({ onRetry, onGoBack, }) => { + // Don't show any banner if we haven't attempted to connect yet + if (!conversationId) { + return null; + } + const bannerState = BannerStateResolverService.resolve({ isConnecting, isConnected, diff --git a/src/presentation/components/organisms/PostCard.tsx b/src/presentation/components/organisms/PostCard.tsx index d6356b2..cc3ee3c 100644 --- a/src/presentation/components/organisms/PostCard.tsx +++ b/src/presentation/components/organisms/PostCard.tsx @@ -70,7 +70,7 @@ export const PostCard: React.FC = ({ { + label: string; + value: T; +} + +interface PickerModalState { + visible: boolean; + title: string; + options: PickerOption[]; + currentValue: any; + onSelect: (value: any) => void; +} + +interface AddEducationScreenTemplateProps { + // Form props + control: Control; + errors: FieldErrors; + rules: any; + + // State + isCurrent: boolean; + isLoading: boolean; + isSubmitDisabled: boolean; + + // Options + yearOptions: PickerOption[]; + degreeOptions: PickerOption[]; + + // Actions + onSubmit: () => void; + onBack: () => void; + onToggleIsCurrent: (value: boolean) => void; +} + +/** + * AddEducationScreenTemplate + * Atomic Design: Template - Layout structure for adding education + * SOLID Principles: + * - Single Responsibility: Layout orchestration only + * - Open/Closed: Content passed via props + * - Dependency Inversion: Depends on abstractions + */ +export const AddEducationScreenTemplate: React.FC< + AddEducationScreenTemplateProps +> = ({ + control, + errors, + rules, + isCurrent, + isLoading, + isSubmitDisabled, + yearOptions, + degreeOptions, + onSubmit, + onBack, + onToggleIsCurrent, +}) => { + // Picker modal state + const [pickerModal, setPickerModal] = useState({ + visible: false, + title: '', + options: [], + currentValue: undefined, + onSelect: () => {}, + }); + + const openPicker = useCallback( + ( + title: string, + options: PickerOption[], + currentValue: T | undefined, + onSelect: (value: T) => void, + ) => { + setPickerModal({ + visible: true, + title, + options, + currentValue, + onSelect, + }); + }, + [], + ); + + const closePicker = useCallback(() => { + setPickerModal((prev) => ({ ...prev, visible: false })); + }, []); + + const handlePickerSelect = useCallback( + (value: any) => { + pickerModal.onSelect(value); + closePicker(); + }, + [pickerModal, closePicker], + ); + + // Styles + const containerStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: theme.colors.white, + }), + [], + ); + + const headerStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: theme.colors.gray200, + }), + [], + ); + + const backButtonStyle = useMemo( + () => ({ + padding: 8, + }), + [], + ); + + const headerTitleStyle = useMemo( + () => ({ + fontSize: 18, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const saveButtonStyle = useMemo( + () => ({ + backgroundColor: isSubmitDisabled + ? theme.colors.gray300 + : theme.colors.primary, + paddingHorizontal: 20, + paddingVertical: 8, + borderRadius: 20, + }), + [isSubmitDisabled], + ); + + const saveButtonTextStyle = useMemo( + () => ({ + color: theme.colors.white, + fontWeight: '600', + fontSize: 14, + }), + [], + ); + + const scrollContentStyle = useMemo( + () => ({ + padding: 16, + paddingBottom: 40, + }), + [], + ); + + const rowStyle = useMemo( + () => ({ + flexDirection: 'row', + gap: 12, + }), + [], + ); + + const halfInputStyle = useMemo( + () => ({ + flex: 1, + }), + [], + ); + + const switchRowStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: 12, + marginBottom: 16, + }), + [], + ); + + const switchLabelStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray900, + }), + [], + ); + + const pickerContainerStyle = useMemo( + () => ({ + marginBottom: 20, + }), + [], + ); + + const pickerLabelStyle = useMemo( + () => ({ + fontSize: 14, + fontWeight: '500', + color: theme.colors.gray700, + marginBottom: 8, + }), + [], + ); + + const pickerStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: theme.colors.gray50, + borderWidth: 1, + borderColor: theme.colors.gray300, + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 14, + }), + [], + ); + + const pickerTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray900, + }), + [], + ); + + const placeholderTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray400, + }), + [], + ); + + const errorTextStyle = useMemo( + () => ({ + fontSize: 12, + color: theme.colors.red500, + marginTop: 4, + }), + [], + ); + + // Modal styles + const modalOverlayStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'flex-end', + }), + [], + ); + + const modalContentStyle = useMemo( + () => ({ + backgroundColor: theme.colors.white, + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + maxHeight: '70%', + }), + [], + ); + + const modalHeaderStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingVertical: 16, + borderBottomWidth: 1, + borderBottomColor: theme.colors.gray200, + }), + [], + ); + + const modalTitleStyle = useMemo( + () => ({ + fontSize: 18, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const modalCloseButtonStyle = useMemo( + () => ({ + padding: 4, + }), + [], + ); + + const optionItemStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingVertical: 16, + borderBottomWidth: 1, + borderBottomColor: theme.colors.gray100, + }), + [], + ); + + const optionTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray900, + }), + [], + ); + + const selectedOptionTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.primary, + fontWeight: '600', + }), + [], + ); + + // Helper to render picker field + const renderPickerField = ( + label: string, + value: T | undefined, + options: PickerOption[], + onChange: (value: T) => void, + error?: string, + required?: boolean, + ) => { + const selectedOption = options.find((opt) => opt.value === value); + const displayValue = selectedOption?.label; + + return ( + + + {label} + {required && *} + + openPicker(label, options, value, onChange)} + > + + {displayValue || 'Select...'} + + + + {error && {error}} + + ); + }; + + // Render option item for FlatList + const renderOptionItem = useCallback( + ({ item }: { item: PickerOption }) => { + const isSelected = item.value === pickerModal.currentValue; + return ( + handlePickerSelect(item.value)} + > + + {item.label} + + {isSelected && ( + + )} + + ); + }, + [pickerModal.currentValue, handlePickerSelect, optionItemStyle, optionTextStyle, selectedOptionTextStyle], + ); + + return ( + + {/* Header */} + + + + + + Add Education + + + {isLoading ? ( + + ) : ( + Save + )} + + + + {/* Form Content */} + + {/* School Information */} + + ( + + )} + /> + + ( + + )} + /> + + ( + + )} + /> + + + {/* Duration */} + + {/* Is Current Switch */} + + I'm currently studying here + + + + {/* Start/End Year */} + + + + renderPickerField( + 'Start Year', + value, + yearOptions, + onChange, + errors.start_year?.message, + true, + ) + } + /> + + {!isCurrent && ( + + + renderPickerField( + 'End Year', + value, + yearOptions, + onChange, + errors.end_year?.message, + ) + } + /> + + )} + + + + {/* Additional Details */} + + ( + + )} + /> + + + + {/* Picker Modal */} + + + e.stopPropagation()}> + {/* Modal Header */} + + {pickerModal.title} + + + + + + {/* Options List */} + String(item.value)} + renderItem={renderOptionItem} + showsVerticalScrollIndicator={true} + initialScrollIndex={ + pickerModal.currentValue + ? Math.max( + 0, + pickerModal.options.findIndex( + (opt) => opt.value === pickerModal.currentValue, + ) - 2, + ) + : 0 + } + getItemLayout={(_, index) => ({ + length: 57, + offset: 57 * index, + index, + })} + /> + + + + + ); +}; diff --git a/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx b/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx new file mode 100644 index 0000000..dd91f8e --- /dev/null +++ b/src/presentation/components/templates/AddWorkExperienceScreenTemplate.tsx @@ -0,0 +1,701 @@ +import React, { useMemo, useState, useCallback } from 'react'; +import { + ScrollView, + View, + Text, + TouchableOpacity, + Switch, + ViewStyle, + TextStyle, + ActivityIndicator, + Modal, + FlatList, + Pressable, +} from 'react-native'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import { Control, FieldErrors, Controller } from 'react-hook-form'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { faArrowLeft, faChevronDown, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { AddWorkExperienceFormValues, EmploymentType } from '../../../ui/form-types/AddWorkExperienceForm.types'; +import { theme } from '../../theme'; + +// Atoms +import { FormInput } from '../atoms/FormInput'; + +// Molecules +import { FormSection } from '../molecules/FormSection'; + +interface PickerOption { + label: string; + value: T; +} + +interface PickerModalState { + visible: boolean; + title: string; + options: PickerOption[]; + currentValue: any; + onSelect: (value: any) => void; +} + +interface AddWorkExperienceScreenTemplateProps { + // Form props + control: Control; + errors: FieldErrors; + rules: any; + + // State + isCurrent: boolean; + isLoading: boolean; + isSubmitDisabled: boolean; + + // Options + yearOptions: PickerOption[]; + monthOptions: PickerOption[]; + employmentTypeOptions: PickerOption[]; + + // Actions + onSubmit: () => void; + onBack: () => void; + onToggleIsCurrent: (value: boolean) => void; +} + +/** + * AddWorkExperienceScreenTemplate + * Atomic Design: Template - Layout structure for adding work experience + * SOLID Principles: + * - Single Responsibility: Layout orchestration only + * - Open/Closed: Content passed via props + * - Dependency Inversion: Depends on abstractions + */ +export const AddWorkExperienceScreenTemplate: React.FC< + AddWorkExperienceScreenTemplateProps +> = ({ + control, + errors, + rules, + isCurrent, + isLoading, + isSubmitDisabled, + yearOptions, + monthOptions, + employmentTypeOptions, + onSubmit, + onBack, + onToggleIsCurrent, +}) => { + // Picker modal state + const [pickerModal, setPickerModal] = useState({ + visible: false, + title: '', + options: [], + currentValue: undefined, + onSelect: () => {}, + }); + + const openPicker = useCallback( + ( + title: string, + options: PickerOption[], + currentValue: T | undefined, + onSelect: (value: T) => void, + ) => { + setPickerModal({ + visible: true, + title, + options, + currentValue, + onSelect, + }); + }, + [], + ); + + const closePicker = useCallback(() => { + setPickerModal((prev) => ({ ...prev, visible: false })); + }, []); + + const handlePickerSelect = useCallback( + (value: any) => { + pickerModal.onSelect(value); + closePicker(); + }, + [pickerModal, closePicker], + ); + + // Styles + const containerStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: theme.colors.white, + }), + [], + ); + + const headerStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: theme.colors.gray200, + }), + [], + ); + + const backButtonStyle = useMemo( + () => ({ + padding: 8, + }), + [], + ); + + const headerTitleStyle = useMemo( + () => ({ + fontSize: 18, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const saveButtonStyle = useMemo( + () => ({ + backgroundColor: isSubmitDisabled + ? theme.colors.gray300 + : theme.colors.primary, + paddingHorizontal: 20, + paddingVertical: 8, + borderRadius: 20, + }), + [isSubmitDisabled], + ); + + const saveButtonTextStyle = useMemo( + () => ({ + color: theme.colors.white, + fontWeight: '600', + fontSize: 14, + }), + [], + ); + + const scrollContentStyle = useMemo( + () => ({ + padding: 16, + paddingBottom: 40, + }), + [], + ); + + const rowStyle = useMemo( + () => ({ + flexDirection: 'row', + gap: 12, + }), + [], + ); + + const halfInputStyle = useMemo( + () => ({ + flex: 1, + }), + [], + ); + + const switchRowStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: 12, + marginBottom: 16, + }), + [], + ); + + const switchLabelStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray900, + }), + [], + ); + + const pickerContainerStyle = useMemo( + () => ({ + marginBottom: 20, + }), + [], + ); + + const pickerLabelStyle = useMemo( + () => ({ + fontSize: 14, + fontWeight: '500', + color: theme.colors.gray700, + marginBottom: 8, + }), + [], + ); + + const pickerStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: theme.colors.gray50, + borderWidth: 1, + borderColor: theme.colors.gray300, + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 14, + }), + [], + ); + + const pickerTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray900, + }), + [], + ); + + const placeholderTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray400, + }), + [], + ); + + const errorTextStyle = useMemo( + () => ({ + fontSize: 12, + color: theme.colors.red500, + marginTop: 4, + }), + [], + ); + + // Modal styles + const modalOverlayStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'flex-end', + }), + [], + ); + + const modalContentStyle = useMemo( + () => ({ + backgroundColor: theme.colors.white, + borderTopLeftRadius: 20, + borderTopRightRadius: 20, + maxHeight: '70%', + }), + [], + ); + + const modalHeaderStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingVertical: 16, + borderBottomWidth: 1, + borderBottomColor: theme.colors.gray200, + }), + [], + ); + + const modalTitleStyle = useMemo( + () => ({ + fontSize: 18, + fontWeight: '600', + color: theme.colors.gray900, + }), + [], + ); + + const modalCloseButtonStyle = useMemo( + () => ({ + padding: 4, + }), + [], + ); + + const optionItemStyle = useMemo( + () => ({ + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingVertical: 16, + borderBottomWidth: 1, + borderBottomColor: theme.colors.gray100, + }), + [], + ); + + const optionTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.gray900, + }), + [], + ); + + const selectedOptionTextStyle = useMemo( + () => ({ + fontSize: 16, + color: theme.colors.primary, + fontWeight: '600', + }), + [], + ); + + // Helper to render picker field + const renderPickerField = ( + label: string, + value: T | undefined, + options: PickerOption[], + onChange: (value: T) => void, + error?: string, + required?: boolean, + ) => { + const selectedOption = options.find((opt) => opt.value === value); + const displayValue = selectedOption?.label; + + return ( + + + {label} + {required && *} + + openPicker(label, options, value, onChange)} + > + + {displayValue || 'Select...'} + + + + {error && {error}} + + ); + }; + + // Render option item for FlatList + const renderOptionItem = useCallback( + ({ item }: { item: PickerOption }) => { + const isSelected = item.value === pickerModal.currentValue; + return ( + handlePickerSelect(item.value)} + > + + {item.label} + + {isSelected && ( + + )} + + ); + }, + [pickerModal.currentValue, handlePickerSelect, optionItemStyle, optionTextStyle, selectedOptionTextStyle], + ); + + return ( + + {/* Header */} + + + + + + Add Work Experience + + + {isLoading ? ( + + ) : ( + Save + )} + + + + {/* Form Content */} + + {/* Basic Information */} + + ( + + )} + /> + + ( + + )} + /> + + ( + + )} + /> + + + renderPickerField( + 'Employment Type', + value, + employmentTypeOptions, + onChange, + errors.employment_type?.message, + true, + ) + } + /> + + + {/* Duration */} + + {/* Is Current Switch */} + + I currently work here + + + + {/* Start Date */} + + + + renderPickerField( + 'Start Month', + value, + monthOptions, + onChange, + errors.start_month?.message, + true, + ) + } + /> + + + + renderPickerField( + 'Start Year', + value, + yearOptions, + onChange, + errors.start_year?.message, + true, + ) + } + /> + + + + {/* End Date (only if not current) */} + {!isCurrent && ( + + + + renderPickerField( + 'End Month', + value, + monthOptions, + onChange, + errors.end_month?.message, + ) + } + /> + + + + renderPickerField( + 'End Year', + value, + yearOptions, + onChange, + errors.end_year?.message, + ) + } + /> + + + )} + + + {/* Additional Details */} + + ( + + )} + /> + + ( + + )} + /> + + + + {/* Picker Modal */} + + + e.stopPropagation()}> + {/* Modal Header */} + + {pickerModal.title} + + + + + + {/* Options List */} + String(item.value)} + renderItem={renderOptionItem} + showsVerticalScrollIndicator={true} + initialScrollIndex={ + pickerModal.currentValue + ? Math.max( + 0, + pickerModal.options.findIndex( + (opt) => opt.value === pickerModal.currentValue, + ) - 2, + ) + : 0 + } + getItemLayout={(_, index) => ({ + length: 57, + offset: 57 * index, + index, + })} + /> + + + + + ); +}; diff --git a/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx b/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx new file mode 100644 index 0000000..4aac194 --- /dev/null +++ b/src/presentation/components/templates/AdvancedSettingsScreenTemplate.tsx @@ -0,0 +1,349 @@ +import React from 'react'; +import { + View, + Text, + TextInput, + TouchableOpacity, + StyleSheet, + ScrollView, + ActivityIndicator, +} from 'react-native'; +import { Controller, Control, FieldErrors } from 'react-hook-form'; +import { FontAwesomeIcon } from '@fortawesome/react-native-fontawesome'; +import { + faArrowLeft, + faServer, + faRotateLeft, +} from '@fortawesome/free-solid-svg-icons'; +import { theme } from '../../theme'; +import { AdvancedSettingsFormValues } from '../../../ui/form-types/AdvancedSettingsForm.types'; +import { AdvancedSettingsValidationRules } from '../../../ui/form-hooks/useAdvancedSettingsForm'; +import { SafeAreaView } from 'react-native-safe-area-context'; + +/** + * AdvancedSettingsScreenTemplate Props + * SOLID: Interface Segregation - Only required props + */ +interface AdvancedSettingsScreenTemplateProps { + // Form + control: Control; + errors: FieldErrors; + rules: AdvancedSettingsValidationRules; + + // Loading states + isLoading: boolean; + isSaving: boolean; + + // Computed + previewUrl: string; + isSubmitDisabled: boolean; + + // Actions + onSubmit: () => void; + onGoBack: () => void; + onReset: () => void; +} + +/** + * AdvancedSettingsScreenTemplate + * SOLID: Single Responsibility - Handle UI layout only + * Atomic Design: Template - Page-level layout composition + */ +export const AdvancedSettingsScreenTemplate: React.FC< + AdvancedSettingsScreenTemplateProps +> = ({ + control, + errors, + rules, + isLoading, + isSaving, + previewUrl, + isSubmitDisabled, + onSubmit, + onGoBack, + onReset, +}) => { + if (isLoading) { + return ( + + + + ); + } + + return ( + + {/* Header */} + + + + + Advanced Settings + + + + + {/* Server Configuration Section */} + + + + Server Configuration + + + + Configure the server IP address and port for API connections. + + + {/* IP Address Input */} + + Server IP Address + ( + + )} + /> + {errors.ip && ( + {errors.ip.message} + )} + + + {/* Port Input */} + + Port + ( + + )} + /> + {errors.port && ( + {errors.port.message} + )} + + + {/* Current URL Preview */} + + API URL Preview: + {previewUrl} + + + + {/* Action Buttons */} + + + {isSaving ? ( + + ) : ( + Save Settings + )} + + + + + Reset to Defaults + + + + {/* Info Note */} + + + ℹ️ Changes will take effect immediately for all new API requests. + + + + + ); +}; + + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: theme.colors.background, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: theme.colors.background, + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: theme.colors.border, + backgroundColor: '#fff', + }, + backButton: { + padding: 8, + }, + headerTitle: { + fontSize: 18, + fontWeight: '600', + color: theme.colors.gray900, + }, + headerRight: { + width: 36, + }, + content: { + flex: 1, + padding: 16, + }, + section: { + backgroundColor: '#fff', + borderRadius: 12, + padding: 16, + marginBottom: 16, + shadowColor: '#000', + shadowOffset: { width: 0, height: 1 }, + shadowOpacity: 0.05, + shadowRadius: 2, + elevation: 1, + }, + sectionHeader: { + flexDirection: 'row', + alignItems: 'center', + gap: 10, + marginBottom: 8, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '600', + color: theme.colors.gray900, + }, + sectionDescription: { + fontSize: 14, + color: theme.colors.gray500, + marginBottom: 20, + }, + inputGroup: { + marginBottom: 16, + }, + label: { + fontSize: 14, + fontWeight: '500', + color: theme.colors.gray900, + marginBottom: 8, + }, + input: { + borderWidth: 1, + borderColor: theme.colors.border, + borderRadius: 8, + paddingHorizontal: 14, + paddingVertical: 12, + fontSize: 16, + color: theme.colors.gray900, + backgroundColor: theme.colors.background, + }, + inputError: { + borderColor: theme.colors.red500, + }, + errorText: { + color: theme.colors.red500, + fontSize: 12, + marginTop: 4, + }, + previewContainer: { + backgroundColor: theme.colors.background, + borderRadius: 8, + padding: 12, + marginTop: 8, + }, + previewLabel: { + fontSize: 12, + color: theme.colors.gray500, + marginBottom: 4, + }, + previewUrl: { + fontSize: 14, + fontFamily: 'monospace', + color: theme.colors.primary, + }, + buttonContainer: { + gap: 12, + marginBottom: 16, + }, + saveButton: { + backgroundColor: theme.colors.primary, + borderRadius: 8, + paddingVertical: 14, + alignItems: 'center', + }, + disabledButton: { + opacity: 0.7, + }, + saveButtonText: { + color: '#fff', + fontSize: 16, + fontWeight: '600', + }, + resetButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 8, + paddingVertical: 12, + borderWidth: 1, + borderColor: theme.colors.red500, + borderRadius: 8, + }, + resetButtonText: { + color: theme.colors.red500, + fontSize: 14, + fontWeight: '500', + }, + infoContainer: { + backgroundColor: '#DBEAFE', + borderRadius: 8, + padding: 12, + marginBottom: 32, + }, + infoText: { + fontSize: 13, + color: '#1E40AF', + lineHeight: 18, + }, +}); diff --git a/src/presentation/components/templates/ChatScreenTemplate.tsx b/src/presentation/components/templates/ChatScreenTemplate.tsx index a99df75..7ed2eb9 100644 --- a/src/presentation/components/templates/ChatScreenTemplate.tsx +++ b/src/presentation/components/templates/ChatScreenTemplate.tsx @@ -16,6 +16,7 @@ interface ChatScreenTemplateProps { isWebSocketConnected: boolean; // Banner props + conversationId: number | null; isWebSocketConnecting: boolean; wsConnectionError: WebSocketConnectionError | null; reconnectAttempts: number; @@ -50,6 +51,7 @@ export const ChatScreenTemplate: React.FC = ({ isWebSocketConnected, // Banner + conversationId, isWebSocketConnecting, wsConnectionError, reconnectAttempts, @@ -81,6 +83,7 @@ export const ChatScreenTemplate: React.FC = ({ /> void; onCloseFeelingPicker: () => void; onSelectFeeling: (feeling: Feeling) => void; + + // User + avatarUri: string; } /** @@ -91,6 +94,9 @@ export const CreatePostScreenTemplate: React.FC = onOpenFeelingPicker, onCloseFeelingPicker, onSelectFeeling, + + // User + avatarUri }) => { const containerStyle = useMemo( () => ({ @@ -111,7 +117,7 @@ export const CreatePostScreenTemplate: React.FC = diff --git a/src/presentation/components/templates/ViewProfileScreenTemplate.tsx b/src/presentation/components/templates/ViewProfileScreenTemplate.tsx new file mode 100644 index 0000000..d992ee3 --- /dev/null +++ b/src/presentation/components/templates/ViewProfileScreenTemplate.tsx @@ -0,0 +1,231 @@ +import React, { useMemo } from 'react'; +import { ScrollView, RefreshControl, ViewStyle } from 'react-native'; +import { SafeAreaView } from 'react-native-safe-area-context'; +import { Profile } from '../../../domain/user/entities/Profile'; +import { theme } from '../../theme'; + +// Molecules +import { CenteredLoader } from '../molecules/CenteredLoader'; +import { CenteredError } from '../molecules/CenteredError'; +import { ViewProfileHeader } from '../molecules/ViewProfileHeader'; +import { ProfileCoverSection } from '../molecules/ProfileCoverSection'; +import { ProfileInfoSection } from '../molecules/ProfileInfoSection'; +import { ProfileStatsRow } from '../molecules/ProfileStatsRow'; +import { ProfileActionButtons } from '../molecules/ProfileActionButtons'; +import { ProfileSection } from '../molecules/ProfileSection'; +import { WorkExperienceCard } from '../molecules/WorkExperienceCard'; +import { EducationCard } from '../molecules/EducationCard'; +import { SkillsList } from '../molecules/SkillsList'; +import { ContactInfoSection } from '../molecules/ContactInfoSection'; +import { LogoutButton } from '../molecules/LogoutButton'; + +interface ViewProfileScreenTemplateProps { + // Loading/Error states + isLoading: boolean; + isError: boolean; + errorMessage?: string; + isRefetching: boolean; + + // User data + user: Profile | null | undefined; + isCurrentUser: boolean; + primaryLocation?: string; + + // Actions + onRefresh: () => void; + onBack: () => void; + onEditProfile: () => void; + onActivityLog: () => void; + onMessage: () => void; + onFollow: () => void; + onAddWorkExperience: () => void; + onAddEducation: () => void; + onLogout: () => void; +} + +/** + * ViewProfileScreenTemplate + * Atomic Design: Template - Defines the layout structure for view profile screen + * SOLID Principles: + * - Single Responsibility: Layout orchestration only + * - Open/Closed: Content passed via props, easily extensible + * - Dependency Inversion: Depends on abstractions (molecules) + */ +export const ViewProfileScreenTemplate: React.FC< + ViewProfileScreenTemplateProps +> = ({ + // Loading/Error + isLoading, + isError, + errorMessage, + isRefetching, + + // User data + user, + isCurrentUser, + primaryLocation, + + // Actions + onRefresh, + onBack, + onEditProfile, + onActivityLog, + onMessage, + onFollow, + onAddWorkExperience, + onAddEducation, + onLogout, +}) => { + const containerStyle = useMemo( + () => ({ + flex: 1, + backgroundColor: theme.colors.gray50, + }), + [], + ); + + const scrollContentStyle = useMemo( + () => ({ + flexGrow: 1, + }), + [], + ); + + // Loading state + if (isLoading) { + return ; + } + + // Error state + if (isError || !user) { + return ; + } + + // Build contact items + const contactItems = []; + if (user.website) { + contactItems.push({ + type: 'website' as const, + label: 'Website', + value: user.website, + }); + } + if (user.phone) { + contactItems.push({ + type: 'phone' as const, + label: 'Phone', + value: user.phone, + }); + } + if (user.linkedin_url) { + contactItems.push({ + type: 'linkedin' as const, + label: 'LinkedIn', + value: user.linkedin_url, + }); + } + if (user.twitter_url) { + contactItems.push({ + type: 'twitter' as const, + label: 'Twitter', + value: user.twitter_url, + }); + } + + return ( + + + } + > + {/* Header */} + + + {/* Cover Image */} + + + {/* User Info */} + + + {/* Stats Row */} + + + {/* Action Buttons */} + + + {/* Skills */} + {user.skills && user.skills.length > 0 && ( + + + + )} + + {/* Work Experience */} + + {user.work_experiences.map((exp, index) => ( + + ))} + + + {/* Education */} + + {user.educations.map((edu, index) => ( + + ))} + + + {/* Contact Information */} + {contactItems.length > 0 && } + + {/* Logout Button - Only for current user */} + {isCurrentUser && } + + + ); +}; diff --git a/src/presentation/navigation/RootStackParamList.ts b/src/presentation/navigation/RootStackParamList.ts index df428aa..7c2591c 100644 --- a/src/presentation/navigation/RootStackParamList.ts +++ b/src/presentation/navigation/RootStackParamList.ts @@ -17,4 +17,11 @@ export type RootStackParamList = { ActivityLog: undefined; Messages: undefined; Chat: { userId: number }; + + // Profile Management Screens + AddWorkExperience: undefined; + AddEducation: undefined; + + // Settings Screens + AdvancedSettings: undefined; }; diff --git a/src/presentation/navigation/auth-screen-navigation/AuthScreenNavigation.tsx b/src/presentation/navigation/auth-screen-navigation/AuthScreenNavigation.tsx index 8bac592..d0160ce 100644 --- a/src/presentation/navigation/auth-screen-navigation/AuthScreenNavigation.tsx +++ b/src/presentation/navigation/auth-screen-navigation/AuthScreenNavigation.tsx @@ -7,6 +7,7 @@ import { RootStackParamList } from '../RootStackParamList'; import { BottomNavigationTemplate } from '../../components/templates/BottomNavigationTemplate'; import WelcomeScreen from '../../screens/WelcomeScreen'; import SplashScreen from '../../screens/SplashScreen'; +import { AdvancedSettingsScreen } from '../../screens/AdvancedSettingsScreen'; const Stack = createNativeStackNavigator(); @@ -19,6 +20,7 @@ const AuthScreenNavigation: React.FC = () => ( + ); diff --git a/src/presentation/navigation/network-screen-navigation/NetworkScreenNavigator.tsx b/src/presentation/navigation/network-screen-navigation/NetworkScreenNavigator.tsx index f4a3710..19e17e8 100644 --- a/src/presentation/navigation/network-screen-navigation/NetworkScreenNavigator.tsx +++ b/src/presentation/navigation/network-screen-navigation/NetworkScreenNavigator.tsx @@ -1,6 +1,8 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { ViewProfileScreen } from '../../screens/ViewProfileScreen'; import { EditProfileScreen } from '../../screens/EditProfileScreen'; +import { AddWorkExperienceScreen } from '../../screens/AddWorkExperienceScreen'; +import { AddEducationScreen } from '../../screens/AddEducationScreen'; import { RootStackParamList } from '../RootStackParamList'; import { ChatScreen } from '../../screens/ChatScreen'; import { NetworkScreen } from '../../screens/NetworkScreen'; @@ -14,6 +16,8 @@ export const NetworkScreenNavigator: React.FC = () => ( + + ); diff --git a/src/presentation/navigation/news-feed-navigation/NewsFeedScreenNavigator.tsx b/src/presentation/navigation/news-feed-navigation/NewsFeedScreenNavigator.tsx index aaee1ce..2ed624b 100644 --- a/src/presentation/navigation/news-feed-navigation/NewsFeedScreenNavigator.tsx +++ b/src/presentation/navigation/news-feed-navigation/NewsFeedScreenNavigator.tsx @@ -4,6 +4,9 @@ import NewsFeedScreen from '../../screens/NewsFeedScreen'; import { ViewProfileScreen } from '../../screens/ViewProfileScreen'; import { EditProfileScreen } from '../../screens/EditProfileScreen'; import { ActivityLogScreen } from '../../screens/ActivityLogScreen'; +import { AddWorkExperienceScreen } from '../../screens/AddWorkExperienceScreen'; +import { AddEducationScreen } from '../../screens/AddEducationScreen'; +import { ChatScreen } from '../../screens/ChatScreen'; const Stack = createNativeStackNavigator(); @@ -13,5 +16,8 @@ export const NewsFeedScreenNavigator: React.FC = () => ( + + + ); diff --git a/src/presentation/screens/AddEducationScreen.tsx b/src/presentation/screens/AddEducationScreen.tsx new file mode 100644 index 0000000..71679b1 --- /dev/null +++ b/src/presentation/screens/AddEducationScreen.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { AddEducationScreenTemplate } from '../components/templates/AddEducationScreenTemplate'; +import { useAddEducationScreen } from '../../ui/hooks/useAddEducationScreen'; + +/** + * AddEducationScreen Page + * Atomic Design: Page - Final screen with real data + * SOLID Principles: + * - Single Responsibility: Connect data to template + * - Open/Closed: Extended through hook and template + * - Dependency Inversion: Depends on abstractions + */ +export const AddEducationScreen: React.FC = () => { + const { + // Form + control, + errors, + rules, + + // State + isCurrent, + isLoading, + isSubmitDisabled, + + // Options + yearOptions, + degreeOptions, + + // Actions + onSubmit, + handleGoBack, + handleToggleIsCurrent, + } = useAddEducationScreen(); + + return ( + + ); +}; diff --git a/src/presentation/screens/AddWorkExperienceScreen.tsx b/src/presentation/screens/AddWorkExperienceScreen.tsx new file mode 100644 index 0000000..7eaf948 --- /dev/null +++ b/src/presentation/screens/AddWorkExperienceScreen.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { AddWorkExperienceScreenTemplate } from '../components/templates/AddWorkExperienceScreenTemplate'; +import { useAddWorkExperienceScreen } from '../../ui/hooks/useAddWorkExperienceScreen'; + +/** + * AddWorkExperienceScreen Page + * Atomic Design: Page - Final screen with real data + * SOLID Principles: + * - Single Responsibility: Connect data to template + * - Open/Closed: Extended through hook and template + * - Dependency Inversion: Depends on abstractions + */ +export const AddWorkExperienceScreen: React.FC = () => { + const { + // Form + control, + errors, + rules, + + // State + isCurrent, + isLoading, + isSubmitDisabled, + + // Options + yearOptions, + monthOptions, + employmentTypeOptions, + + // Actions + onSubmit, + handleGoBack, + handleToggleIsCurrent, + } = useAddWorkExperienceScreen(); + + return ( + + ); +}; diff --git a/src/presentation/screens/AdvancedSettingsScreen.tsx b/src/presentation/screens/AdvancedSettingsScreen.tsx new file mode 100644 index 0000000..96d1480 --- /dev/null +++ b/src/presentation/screens/AdvancedSettingsScreen.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { RootStackParamList } from '../navigation/RootStackParamList'; +import { useAdvancedSettingsScreen } from '../../ui/hooks/useAdvancedSettingsScreen'; +import { AdvancedSettingsScreenTemplate } from '../components/templates/AdvancedSettingsScreenTemplate'; + +type AdvancedSettingsScreenProps = NativeStackScreenProps< + RootStackParamList, + 'AdvancedSettings' +>; + +/** + * AdvancedSettingsScreen + * SOLID: Single Responsibility - Compose hook and template + * SOLID: Dependency Inversion - Depends on abstractions (hook, template) + * Atomic Design: Screen - Connects business logic to UI template + */ +export const AdvancedSettingsScreen: React.FC = () => { + const { + control, + errors, + rules, + isLoading, + isSaving, + previewUrl, + isSubmitDisabled, + onSubmit, + handleGoBack, + handleReset, + } = useAdvancedSettingsScreen(); + + return ( + + ); +}; diff --git a/src/presentation/screens/ChatScreen.tsx b/src/presentation/screens/ChatScreen.tsx index 357dafa..7eecbf0 100644 --- a/src/presentation/screens/ChatScreen.tsx +++ b/src/presentation/screens/ChatScreen.tsx @@ -23,6 +23,7 @@ export const ChatScreen: React.FC = ({ const { flatListRef, + conversationId, otherParticipantName, isConversationLoading, messages, @@ -53,6 +54,7 @@ export const ChatScreen: React.FC = ({ onBackPress={() => navigation.goBack()} isWebSocketConnected={isWebSocketConnected} // Banner + conversationId={conversationId} isWebSocketConnecting={isWebSocketConnecting} wsConnectionError={wsConnectionError} reconnectAttempts={reconnectAttempts} diff --git a/src/presentation/screens/CreatePostScreen.tsx b/src/presentation/screens/CreatePostScreen.tsx index b4f90eb..6f345f4 100644 --- a/src/presentation/screens/CreatePostScreen.tsx +++ b/src/presentation/screens/CreatePostScreen.tsx @@ -37,6 +37,9 @@ export const CreatePostScreen: React.FC = () => { onOpenFeelingPicker, onCloseFeelingPicker, onSelectFeeling, + + // User + avatarUri } = useCreatePostScreen(); return ( @@ -63,6 +66,9 @@ export const CreatePostScreen: React.FC = () => { onOpenFeelingPicker={onOpenFeelingPicker} onCloseFeelingPicker={onCloseFeelingPicker} onSelectFeeling={onSelectFeeling} + + // User + avatarUri={avatarUri} /> ); }; diff --git a/src/presentation/screens/LoginScreen.tsx b/src/presentation/screens/LoginScreen.tsx index b59bb61..75dab42 100644 --- a/src/presentation/screens/LoginScreen.tsx +++ b/src/presentation/screens/LoginScreen.tsx @@ -37,9 +37,13 @@ const LoginScreen: React.FC = () => { }) => { try { await mutateAsync(data, { - onSuccess: data => { - AsyncStorage.setItem('authToken', data.access); - AsyncStorage.setItem('refreshToken', data.refresh); + onSuccess: async (response) => { + await AsyncStorage.setItem('authToken', response.access); + await AsyncStorage.setItem('refreshToken', response.refresh); + // Cache user profile photo for header avatar + if (response.user.profile_photo) { + await AsyncStorage.setItem('userProfilePhoto', response.user.profile_photo); + } navigation.replace('BizBuch'); }, }); diff --git a/src/presentation/screens/SplashScreen.tsx b/src/presentation/screens/SplashScreen.tsx index 98519e3..4e782f8 100644 --- a/src/presentation/screens/SplashScreen.tsx +++ b/src/presentation/screens/SplashScreen.tsx @@ -12,6 +12,7 @@ import { useNavigation } from '@react-navigation/native'; import { useVerifyToken } from '../../ui/hooks/useVerifyToken'; import { WelcomeNavigationProp } from '../navigation/auth-screen-navigation/AuthScreenStackParamList'; import Logo from '../../assets/images/splash.svg'; +import { configManager } from '../../core/config'; const { width } = Dimensions.get('window'); const AnimatedLogo = Animated.createAnimatedComponent(Logo); @@ -28,6 +29,9 @@ const SplashScreen: React.FC = () => { useEffect(() => { const verifyToken = async () => { + // Initialize config manager to load saved IP settings + await configManager.initialize(); + const token = await AsyncStorage.getItem('authToken'); if (token) { @@ -35,7 +39,11 @@ const SplashScreen: React.FC = () => { await mutateAsync( { token }, { - onSuccess: () => { + onSuccess: async (response) => { + // Cache user profile photo for header avatar + if (response.user?.profile_photo) { + await AsyncStorage.setItem('userProfilePhoto', response.user.profile_photo); + } navigation.replace('BizBuch'); }, onError: () => { diff --git a/src/presentation/screens/ViewProfileScreen.tsx b/src/presentation/screens/ViewProfileScreen.tsx index ad27466..322608a 100644 --- a/src/presentation/screens/ViewProfileScreen.tsx +++ b/src/presentation/screens/ViewProfileScreen.tsx @@ -1,691 +1,70 @@ -import React from "react"; -import { - View, - Text, - Image, - TouchableOpacity, - StyleSheet, - ScrollView, - ActivityIndicator, - RefreshControl, - FlatList, -} from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; -import { FontAwesomeIcon } from "@fortawesome/react-native-fontawesome"; -import { - faMapMarkerAlt, - faGlobe, - faLink, - faPhone, - faCheckCircle, - faStar, - faArrowLeft, - faEnvelope, - faBriefcase, -} from "@fortawesome/free-solid-svg-icons"; -import LinearGradient from "react-native-linear-gradient"; -import { useViewProfile } from "../../ui/hooks/useViewProfile"; -import { ViewProfileScreenProps } from "../navigation/network-screen-navigation/NetworkScreenStackParamList"; -import { Profile } from "../../domain/user/entities/Profile"; -import { theme } from "../theme"; +import React from 'react'; +import { ViewProfileScreenProps } from '../navigation/network-screen-navigation/NetworkScreenStackParamList'; +import { ViewProfileScreenTemplate } from '../components/templates/ViewProfileScreenTemplate'; +import { useViewProfileScreen } from '../../ui/hooks/useViewProfileScreen'; /** - * ViewProfileScreen - * SOLID: Single Responsibility - Display profile data - * SOLID: Open/Closed - Handles both current user (profiles/me) and other users (profiles/{id}) - * + * ViewProfileScreen Page + * Atomic Design: Page - Final screen with real data + * SOLID Principles: + * - Single Responsibility: Connect data to template + * - Open/Closed: Extended through hook and template + * - Dependency Inversion: Depends on abstractions (useViewProfileScreen, ViewProfileScreenTemplate) + * * Navigation: * - From Network tab: userId provided → calls profiles/{id} * - From NewsFeed tab profile icon: no userId → calls profiles/me */ export const ViewProfileScreen: React.FC = ({ route, - navigation, }) => { const { userId } = route.params ?? {}; - - const isCurrentUser = userId === undefined || userId === null; - - console.log("ViewProfileScreen - userId:", userId, "isCurrentUser:", isCurrentUser); - - // Fetch profile - uses profiles/me if no userId, otherwise profiles/{id} - const { data: user, isLoading, isError, error, refetch, isRefetching } = useViewProfile(userId); - console.log("ViewProfileScreen - isLoading:", isLoading, "isError:", isError, "user:", user, "error:", error); - - if (isLoading) { - return ( - - - - ); - } - - if (isError || !user) { - return ( - - Failed to load profile - {error?.message || 'Unknown error'} - - ); - } - - const primaryLocation = user.locations?.find((loc) => loc.is_primary); - const currentExperience = user.work_experiences?.find( - (exp) => exp.is_current - ); - const currentEducation = user.educations?.find((edu) => edu.is_current); + const { + // State + user, + isLoading, + isError, + error, + isRefetching, + isCurrentUser, + + // Derived data + primaryLocation, + + // Actions + refetch, + handleGoBack, + handleEditProfile, + handleActivityLog, + handleMessage, + handleFollow, + handleAddWorkExperience, + handleAddEducation, + handleLogout, + } = useViewProfileScreen(userId); return ( - - - } - > - {/* Header */} - - navigation.goBack()} style={styles.backButton}> - - Back - - Profile - - - - {/* Cover Image with Gradient Overlay */} - - {user.cover_image && ( - - )} - - - {/* User Info Section */} - - - - - - - {user.display_name} - {user.is_verified && ( - - )} - {user.is_premium && ( - - )} - - - {user.headline && ( - {user.headline} - )} - - {primaryLocation && ( - - - {primaryLocation.location} - - )} - - {user.bio && {user.bio}} - - {/* Stats Row */} - - - - {parseInt(user.connections_count) || 0} - - Connections - - - - - {parseInt(user.followers_count) || 0} - - Followers - - - - - {parseInt(user.posts_count) || 0} - - Posts - - - - {/* Action Buttons */} - - {isCurrentUser ? ( - <> - navigation.navigate('EditProfile')} - > - Edit Profile - - navigation.navigate('ActivityLog')} - > - Activity Log - - - ) : ( - <> - - Connect - - - Follow - - - )} - - - - {/* Current Work Experience */} - {currentExperience && ( - - Current Position - - {currentExperience.company_logo && ( - - )} - - - {currentExperience.job_title} - - - {currentExperience.company_name} - - {currentExperience.location && ( - - 📍 {currentExperience.location} - - )} - - - - )} - - {/* Current Education */} - {currentEducation && ( - - Education - - {currentEducation.school_logo && ( - - )} - - {currentEducation.name} - {currentEducation.degrees} - {currentEducation.duration} - - - - )} - - {/* Skills */} - {user.skills && user.skills.length > 0 && ( - - Skills - - {user.skills.map((skill, index) => ( - - {skill} - - ))} - - - )} - - {/* Work Experiences */} - {user.work_experiences && user.work_experiences.length > 0 && ( - - Work Experience - {user.work_experiences.map((exp, index) => ( - - {exp.company_logo && ( - - )} - - {exp.job_title} - {exp.company_name} - {exp.location} - - - ))} - - )} - - {/* Education Section */} - {user.educations && user.educations.length > 0 && ( - - Education - {user.educations.map((edu, index) => ( - - {edu.school_logo && ( - - )} - - {edu.name} - {edu.degrees} - {edu.duration} - - - ))} - - )} - - {/* Contact Information */} - - Contact Information - {user.website && ( - - - - - - Website - {user.website} - - - )} - {user.phone && ( - - - - - - Phone - {user.phone} - - - )} - {user.linkedin_url && ( - - - - - - LinkedIn - {user.linkedin_url} - - - )} - {user.twitter_url && ( - - - - - - Twitter - {user.twitter_url} - - - )} - - - + ); }; - -const styles = StyleSheet.create({ - container: { flex: 1, backgroundColor: theme.colors.gray50 }, - scrollContent: { flexGrow: 1 }, - loadingContainer: { - flex: 1, - justifyContent: "center", - alignItems: "center", - backgroundColor: theme.colors.white, - }, - - header: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - paddingHorizontal: 16, - paddingVertical: 12, - backgroundColor: theme.colors.white, - borderBottomWidth: 1, - borderColor: theme.colors.border, - }, - backButton: { - flexDirection: "row", - alignItems: "center", - gap: 8, - }, - backText: { - fontSize: 15, - color: theme.colors.primary, - fontWeight: "500", - }, - headerTitle: { - fontSize: 17, - fontWeight: "600", - color: theme.colors.gray900, - }, - - coverGradient: { - width: "100%", - height: 180, - }, - coverImage: { - width: "100%", - height: "100%", - opacity: 0.6, - }, - - profileSection: { - alignItems: "center", - paddingVertical: 20, - paddingHorizontal: 16, - backgroundColor: theme.colors.white, - marginBottom: 12, - }, - avatarContainer: { - marginTop: -64, - marginBottom: 16, - borderRadius: 64, - padding: 4, - backgroundColor: theme.colors.white, - }, - avatar: { - width: 120, - height: 120, - borderRadius: 60, - backgroundColor: theme.colors.gray200, - }, - nameRow: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - marginBottom: 8, - }, - name: { - fontSize: 24, - fontWeight: "700", - color: theme.colors.gray900, - }, - verifiedIcon: { - marginLeft: 8, - }, - premiumIcon: { - marginLeft: 4, - }, - headline: { - fontSize: 16, - fontWeight: "500", - color: theme.colors.gray600, - marginBottom: 8, - textAlign: "center", - }, - locationRow: { - flexDirection: "row", - alignItems: "center", - marginBottom: 12, - }, - location: { - fontSize: 14, - color: theme.colors.gray500, - marginLeft: 8, - }, - bio: { - fontSize: 14, - color: theme.colors.gray600, - textAlign: "center", - marginBottom: 16, - paddingHorizontal: 20, - lineHeight: 20, - }, - - statsRow: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-around", - marginBottom: 20, - borderTopWidth: 1, - borderBottomWidth: 1, - borderColor: theme.colors.border, - paddingVertical: 16, - marginHorizontal: -16, - paddingHorizontal: 16, - }, - statBox: { - alignItems: "center", - flex: 1, - }, - statDivider: { - width: 1, - height: 40, - backgroundColor: theme.colors.gray200, - }, - statNumber: { - fontSize: 22, - fontWeight: "700", - color: theme.colors.gray900, - }, - statLabel: { - fontSize: 13, - color: theme.colors.gray500, - marginTop: 4, - }, - - buttonRow: { - flexDirection: "row", - gap: 12, - marginHorizontal: -16, - paddingHorizontal: 16, - }, - primaryButton: { - flex: 1, - backgroundColor: theme.colors.primary, - paddingVertical: 14, - borderRadius: 24, - alignItems: "center", - }, - primaryButtonText: { - color: theme.colors.white, - fontWeight: "600", - fontSize: 15, - }, - secondaryButton: { - flex: 1, - backgroundColor: theme.colors.white, - paddingVertical: 14, - borderRadius: 24, - alignItems: "center", - borderWidth: 1.5, - borderColor: theme.colors.primary, - }, - secondaryButtonText: { - color: theme.colors.primary, - fontWeight: "600", - fontSize: 15, - }, - - section: { - backgroundColor: theme.colors.white, - marginBottom: 12, - paddingHorizontal: 16, - paddingVertical: 16, - }, - sectionTitle: { - fontSize: 16, - fontWeight: "600", - color: theme.colors.gray900, - marginBottom: 16, - }, - - workCard: { - flexDirection: "row", - marginBottom: 12, - paddingBottom: 12, - borderBottomWidth: 1, - borderColor: theme.colors.border, - }, - companyLogo: { - width: 50, - height: 50, - borderRadius: 8, - marginRight: 12, - backgroundColor: theme.colors.gray100, - }, - workInfo: { - flex: 1, - }, - jobTitle: { - fontSize: 15, - fontWeight: "600", - color: theme.colors.gray900, - }, - companyName: { - fontSize: 14, - color: theme.colors.gray600, - marginTop: 2, - }, - workLocation: { - fontSize: 13, - color: theme.colors.gray500, - marginTop: 4, - }, - workDuration: { - fontSize: 13, - color: theme.colors.gray500, - marginTop: 4, - }, - - educationCard: { - flexDirection: "row", - marginBottom: 12, - paddingBottom: 12, - borderBottomWidth: 1, - borderColor: theme.colors.border, - }, - schoolLogo: { - width: 50, - height: 50, - borderRadius: 8, - marginRight: 12, - backgroundColor: theme.colors.gray100, - }, - educationInfo: { - flex: 1, - }, - schoolName: { - fontSize: 15, - fontWeight: "600", - color: theme.colors.gray900, - }, - degree: { - fontSize: 14, - color: theme.colors.gray600, - marginTop: 2, - }, - duration: { - fontSize: 13, - color: theme.colors.gray500, - marginTop: 4, - }, - - skillsContainer: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - }, - skillBadge: { - backgroundColor: `${theme.colors.primary}15`, - paddingVertical: 8, - paddingHorizontal: 14, - borderRadius: 20, - borderWidth: 1, - borderColor: theme.colors.primary, - }, - skillText: { - fontSize: 13, - color: theme.colors.primary, - fontWeight: "500", - }, - - contactRow: { - flexDirection: "row", - alignItems: "center", - marginBottom: 16, - }, - contactIconContainer: { - width: 44, - height: 44, - borderRadius: 22, - backgroundColor: `${theme.colors.primary}15`, - alignItems: "center", - justifyContent: "center", - }, - contactInfo: { - marginLeft: 12, - flex: 1, - }, - contactLabel: { - fontSize: 12, - color: theme.colors.gray500, - marginBottom: 2, - }, - contactText: { - fontSize: 14, - color: theme.colors.gray700, - }, - contactLink: { - fontSize: 14, - color: theme.colors.primary, - }, -}); diff --git a/src/presentation/screens/WelcomeScreen.tsx b/src/presentation/screens/WelcomeScreen.tsx index 17b9387..b23ae51 100644 --- a/src/presentation/screens/WelcomeScreen.tsx +++ b/src/presentation/screens/WelcomeScreen.tsx @@ -47,7 +47,7 @@ const WelcomeScreen: React.FC = () => {