diff --git a/.gitignore b/.gitignore
index 4d29575..b84b002 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
+/api/node_modules
# testing
/coverage
@@ -13,11 +14,16 @@
# misc
.DS_Store
+.env
.env.local
.env.development.local
.env.test.local
.env.production.local
+/api/.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+
+/api/.env
+
diff --git a/api/README.md b/api/README.md
new file mode 100644
index 0000000..daf6336
--- /dev/null
+++ b/api/README.md
@@ -0,0 +1,87 @@
+# 邮件发送API
+
+这是一个简单的Express后端服务,用于处理联系表单的邮件发送功能。
+
+## 安装
+
+```bash
+cd api
+npm install
+```
+
+## 配置
+
+编辑 `.env` 文件,填入您的邮箱配置:
+
+```
+# 服务器配置
+PORT=3001
+
+# 邮件配置
+EMAIL_SERVICE=163 # 邮件服务提供商
+EMAIL_HOST=smtp.163.com # SMTP服务器地址
+EMAIL_PORT=465 # SMTP端口
+EMAIL_SECURE=true # 是否使用SSL/TLS
+EMAIL_USER=your-email@163.com # 发送邮件的邮箱地址
+EMAIL_PASS=your-password-or-app-password # 邮箱密码或授权码
+EMAIL_RECIPIENT=recipient@example.com # 接收邮件的邮箱地址(可选,默认与EMAIL_USER相同)
+```
+
+对于大部分邮箱服务商,您需要生成专门的"应用密码"或"授权码",而不是使用登录密码。例如:
+
+- 对于163邮箱,您需要在邮箱设置中开启SMTP服务并获取授权码
+- 对于Gmail,您需要开启两步验证并生成应用专用密码
+
+## 运行
+
+```bash
+# 开发模式(自动重启)
+npm run dev
+
+# 生产模式
+npm start
+```
+
+服务器将在 http://localhost:3001 上运行。
+
+## API端点
+
+### 发送邮件
+
+```
+POST /api/send-email
+```
+
+请求体示例:
+
+```json
+{
+ "name": "张三",
+ "email": "zhangsan@example.com",
+ "subject": "合作咨询",
+ "message": "我对您的项目很感兴趣,希望能进一步了解。"
+}
+```
+
+成功响应:
+
+```json
+{
+ "success": true,
+ "message": "邮件已成功发送"
+}
+```
+
+### 测试API
+
+```
+GET /api/test
+```
+
+响应示例:
+
+```json
+{
+ "message": "API服务器运行正常"
+}
+```
\ No newline at end of file
diff --git a/api/package-lock.json b/api/package-lock.json
new file mode 100644
index 0000000..8ad76d8
--- /dev/null
+++ b/api/package-lock.json
@@ -0,0 +1,1152 @@
+{
+ "name": "email-api",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "email-api",
+ "version": "1.0.0",
+ "dependencies": {
+ "body-parser": "^1.20.2",
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "nodemailer": "^6.9.7"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.13.0",
+ "raw-body": "2.5.2",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
+ "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.7",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
+ "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.3",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.7.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.3.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.13.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.19.0",
+ "serve-static": "1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+ "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "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==",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nodemailer": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
+ "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/nodemon": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
+ "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^3.1.2",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/nodemon/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true
+ },
+ "node_modules/qs": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "dependencies": {
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "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==",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.19.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/touch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+ "dev": true,
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "dev": true
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/api/package.json b/api/package.json
new file mode 100644
index 0000000..5efa5b8
--- /dev/null
+++ b/api/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "email-api",
+ "version": "1.0.0",
+ "description": "邮件发送API",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js",
+ "dev": "nodemon server.js"
+ },
+ "dependencies": {
+ "body-parser": "^1.20.2",
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "nodemailer": "^6.9.7"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+}
\ No newline at end of file
diff --git a/api/server.js b/api/server.js
new file mode 100644
index 0000000..900230c
--- /dev/null
+++ b/api/server.js
@@ -0,0 +1,97 @@
+const express = require('express');
+const bodyParser = require('body-parser');
+const cors = require('cors');
+const nodemailer = require('nodemailer');
+const dotenv = require('dotenv');
+
+// 加载环境变量
+dotenv.config();
+
+const app = express();
+const port = process.env.PORT || 3001;
+
+// 中间件
+app.use(cors());
+app.use(bodyParser.json());
+
+// 创建Nodemailer传输器
+const transporter = nodemailer.createTransport({
+ service: process.env.EMAIL_SERVICE || 'smtp', // 邮件服务商,例如:'gmail', 'qq', '163'等
+ host: process.env.EMAIL_HOST || 'smtp.163.com', // SMTP服务器
+ port: parseInt(process.env.EMAIL_PORT || '587'),
+ secure: process.env.EMAIL_SECURE === 'true', // true for 465, false for other ports
+ auth: {
+ user: process.env.EMAIL_USER || '', // 发送方邮箱
+ pass: process.env.EMAIL_PASS || '', // 授权码或密码
+ },
+});
+
+// 存储IP地址最后发送邮件的时间
+const lastEmailSentTime = new Map();
+
+// 速率限制中间件 - 60秒内只能发送一封邮件
+const rateLimitMiddleware = (req, res, next) => {
+ const ip = req.ip || req.connection.remoteAddress;
+ const currentTime = Date.now();
+ const lastTime = lastEmailSentTime.get(ip) || 0;
+ const timeDiff = currentTime - lastTime;
+
+ // 如果距离上次发送不足60秒,则拒绝请求
+ if (lastTime && timeDiff < 60000) {
+ const remainingTime = Math.ceil((60000 - timeDiff) / 1000);
+ return res.status(429).json({
+ error: `请求过于频繁,请在${remainingTime}秒后再试`,
+ remainingTime
+ });
+ }
+
+ // 更新最后发送时间并继续处理请求
+ lastEmailSentTime.set(ip, currentTime);
+ next();
+};
+
+// 邮件发送接口
+app.post('/api/send-email', rateLimitMiddleware, async (req, res) => {
+ const { name, email, subject, message } = req.body;
+
+ if (!name || !email || !message) {
+ return res.status(400).json({ error: '请填写所有必填字段' });
+ }
+
+ try {
+ // 邮件选项
+ const mailOptions = {
+ from: `"个人网站联系表单" <${process.env.EMAIL_USER}>`,
+ to: process.env.EMAIL_RECIPIENT || process.env.EMAIL_USER, // 接收方邮箱
+ subject: subject ? `新的联系表单消息: ${subject}` : '新的联系表单消息',
+ html: `
+
您有一条新的联系表单消息
+ 姓名: ${name}
+ 邮箱: ${email}
+ 主题: ${subject || '无'}
+ 消息:
+ ${message.replace(/\n/g, '
')}
+ `,
+ // 回复选项,回复时会发送到表单提交者的邮箱
+ replyTo: email
+ };
+
+ // 发送邮件
+ await transporter.sendMail(mailOptions);
+
+ return res.status(200).json({ success: true, message: '邮件已成功发送' });
+ } catch (error) {
+ console.error('发送邮件时出错:', error);
+ return res.status(500).json({ error: '发送邮件失败,请稍后再试' });
+ }
+});
+
+// 测试路由
+app.get('/api/test', (req, res) => {
+ res.json({ message: 'API服务器运行正常' });
+});
+
+// 启动服务器
+app.listen(port, () => {
+ console.log(`邮件服务API正在端口 ${port} 上运行`);
+});
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index b661584..badfe41 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,17 +8,33 @@
"name": "personal-portfolio",
"version": "0.1.0",
"dependencies": {
- "@testing-library/dom": "^10.4.0",
- "@testing-library/jest-dom": "^6.6.3",
- "@testing-library/react": "^16.2.0",
+ "@chakra-ui/button": "^2.0.0",
+ "@chakra-ui/color-mode": "^2.0.0",
+ "@chakra-ui/hooks": "^2.0.0",
+ "@chakra-ui/icon": "^3.0.0",
+ "@chakra-ui/icons": "^2.0.0",
+ "@chakra-ui/layout": "^2.0.0",
+ "@chakra-ui/react": "^2.8.0",
+ "@chakra-ui/theme-utils": "^2.0.0",
+ "@emotion/react": "^11.11.0",
+ "@emotion/styled": "^11.11.0",
+ "@testing-library/dom": "^9.3.3",
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.126",
- "@types/react": "^19.0.10",
- "@types/react-dom": "^19.0.4",
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
+ "@types/react": "^18.2.0",
+ "@types/react-dom": "^18.2.0",
+ "axios": "^1.6.2",
+ "framer-motion": "^10.16.4",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-icons": "^4.12.0",
+ "react-router-dom": "^6.20.0",
"react-scripts": "5.0.1",
+ "react-scroll": "^1.9.0",
+ "react-type-animation": "^3.2.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
}
@@ -1950,6 +1966,353 @@
"resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
"integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="
},
+ "node_modules/@chakra-ui/anatomy": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz",
+ "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg=="
+ },
+ "node_modules/@chakra-ui/breakpoint-utils": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/breakpoint-utils/-/breakpoint-utils-2.0.8.tgz",
+ "integrity": "sha512-Pq32MlEX9fwb5j5xx8s18zJMARNHlQZH2VH1RZgfgRDpp7DcEgtRW5AInfN5CfqdHLO1dGxA7I3MqEuL5JnIsA==",
+ "dependencies": {
+ "@chakra-ui/shared-utils": "2.0.5"
+ }
+ },
+ "node_modules/@chakra-ui/button": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/button/-/button-2.1.0.tgz",
+ "integrity": "sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA==",
+ "dependencies": {
+ "@chakra-ui/react-context": "2.1.0",
+ "@chakra-ui/react-use-merge-refs": "2.1.0",
+ "@chakra-ui/shared-utils": "2.0.5",
+ "@chakra-ui/spinner": "2.1.0"
+ },
+ "peerDependencies": {
+ "@chakra-ui/system": ">=2.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/color-mode": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz",
+ "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==",
+ "dependencies": {
+ "@chakra-ui/react-use-safe-layout-effect": "2.1.0"
+ },
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/hooks": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/hooks/-/hooks-2.4.4.tgz",
+ "integrity": "sha512-+gMwLIkabtddIL/GICU7JmnYtvfONP+fNiTfdYLV9/I1eyCz8igKgLmFJOGM6F+BpUev6hh+/+DX5ezGQ9VTbQ==",
+ "dependencies": {
+ "@chakra-ui/utils": "2.2.4",
+ "@zag-js/element-size": "0.31.1",
+ "copy-to-clipboard": "3.3.3",
+ "framesync": "6.1.2"
+ },
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/hooks/node_modules/@zag-js/element-size": {
+ "version": "0.31.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/element-size/-/element-size-0.31.1.tgz",
+ "integrity": "sha512-4T3yvn5NqqAjhlP326Fv+w9RqMIBbNN9H72g5q2ohwzhSgSfZzrKtjL4rs9axY/cw9UfMfXjRjEE98e5CMq7WQ=="
+ },
+ "node_modules/@chakra-ui/icon": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz",
+ "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==",
+ "dependencies": {
+ "@chakra-ui/shared-utils": "2.0.5"
+ },
+ "peerDependencies": {
+ "@chakra-ui/system": ">=2.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/icons": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/icons/-/icons-2.2.4.tgz",
+ "integrity": "sha512-l5QdBgwrAg3Sc2BRqtNkJpfuLw/pWRDwwT58J6c4PqQT6wzXxyNa8Q0PForu1ltB5qEiFb1kxr/F/HO1EwNa6g==",
+ "peerDependencies": {
+ "@chakra-ui/react": ">=2.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/layout": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/layout/-/layout-2.3.1.tgz",
+ "integrity": "sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg==",
+ "dependencies": {
+ "@chakra-ui/breakpoint-utils": "2.0.8",
+ "@chakra-ui/icon": "3.2.0",
+ "@chakra-ui/object-utils": "2.1.0",
+ "@chakra-ui/react-children-utils": "2.0.6",
+ "@chakra-ui/react-context": "2.1.0",
+ "@chakra-ui/shared-utils": "2.0.5"
+ },
+ "peerDependencies": {
+ "@chakra-ui/system": ">=2.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/object-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz",
+ "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ=="
+ },
+ "node_modules/@chakra-ui/react": {
+ "version": "2.10.6",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-2.10.6.tgz",
+ "integrity": "sha512-9cdzcUR3LV3E2as0QhZhHAH5qjbyspV12kU1E1Ibcv6/uKUi6bIfPfMSC6R/Tw8Beqhn2ClJFPqjtXzL+C0knQ==",
+ "dependencies": {
+ "@chakra-ui/hooks": "2.4.4",
+ "@chakra-ui/styled-system": "2.12.2",
+ "@chakra-ui/theme": "3.4.8",
+ "@chakra-ui/utils": "2.2.4",
+ "@popperjs/core": "^2.11.8",
+ "@zag-js/focus-visible": "^0.31.1",
+ "aria-hidden": "^1.2.3",
+ "react-fast-compare": "3.2.2",
+ "react-focus-lock": "^2.9.6",
+ "react-remove-scroll": "^2.5.7"
+ },
+ "peerDependencies": {
+ "@emotion/react": ">=11",
+ "@emotion/styled": ">=11",
+ "framer-motion": ">=4.0.0",
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/react-children-utils": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz",
+ "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==",
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/react-context": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz",
+ "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==",
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/react-use-merge-refs": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz",
+ "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==",
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/react-use-safe-layout-effect": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz",
+ "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==",
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/react-utils": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz",
+ "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==",
+ "peer": true,
+ "dependencies": {
+ "@chakra-ui/utils": "2.0.15"
+ },
+ "peerDependencies": {
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/react-utils/node_modules/@chakra-ui/utils": {
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz",
+ "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==",
+ "peer": true,
+ "dependencies": {
+ "@types/lodash.mergewith": "4.6.7",
+ "css-box-model": "1.2.1",
+ "framesync": "6.1.2",
+ "lodash.mergewith": "4.6.2"
+ }
+ },
+ "node_modules/@chakra-ui/react-utils/node_modules/@types/lodash.mergewith": {
+ "version": "4.6.7",
+ "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz",
+ "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==",
+ "peer": true,
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
+ "node_modules/@chakra-ui/react/node_modules/@chakra-ui/anatomy": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.3.6.tgz",
+ "integrity": "sha512-TjmjyQouIZzha/l8JxdBZN1pKZTj7sLpJ0YkFnQFyqHcbfWggW9jKWzY1E0VBnhtFz/xF3KC6UAVuZVSJx+y0g=="
+ },
+ "node_modules/@chakra-ui/react/node_modules/@chakra-ui/styled-system": {
+ "version": "2.12.2",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.12.2.tgz",
+ "integrity": "sha512-BlQ7i3+GYC0S0c72B+paa0sYo+QeNSMfz6fwQRFsc8A5Aax9i9lSdRL+vwJVC+k6r/0HWfRwk016R2RD2ihEwQ==",
+ "dependencies": {
+ "@chakra-ui/utils": "2.2.4",
+ "csstype": "^3.1.2"
+ }
+ },
+ "node_modules/@chakra-ui/react/node_modules/@chakra-ui/theme": {
+ "version": "3.4.8",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.4.8.tgz",
+ "integrity": "sha512-ZLMP2Gek38ZTIlj+sMZLsd1TW27yVdmUKMfBmjsr1psAeOa5bDBLKDszICjhEqk7gAbiWB7jr1/HzBXid4kduQ==",
+ "dependencies": {
+ "@chakra-ui/anatomy": "2.3.6",
+ "@chakra-ui/theme-tools": "2.2.8",
+ "@chakra-ui/utils": "2.2.4"
+ },
+ "peerDependencies": {
+ "@chakra-ui/styled-system": ">=2.8.0"
+ }
+ },
+ "node_modules/@chakra-ui/react/node_modules/@chakra-ui/theme-tools": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.2.8.tgz",
+ "integrity": "sha512-X2i2qgkG+k3DQfh/adn3zzM4Ty8QrGobVPjMl9rMrEYq3ac+pur6KVdVHy/SwwoPvB6S4i84uq7y35+KbJan9g==",
+ "dependencies": {
+ "@chakra-ui/anatomy": "2.3.6",
+ "@chakra-ui/utils": "2.2.4",
+ "color2k": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@chakra-ui/styled-system": ">=2.0.0"
+ }
+ },
+ "node_modules/@chakra-ui/shared-utils": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz",
+ "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q=="
+ },
+ "node_modules/@chakra-ui/spinner": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/spinner/-/spinner-2.1.0.tgz",
+ "integrity": "sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g==",
+ "dependencies": {
+ "@chakra-ui/shared-utils": "2.0.5"
+ },
+ "peerDependencies": {
+ "@chakra-ui/system": ">=2.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/styled-system": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz",
+ "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==",
+ "dependencies": {
+ "@chakra-ui/shared-utils": "2.0.5",
+ "csstype": "^3.1.2",
+ "lodash.mergewith": "4.6.2"
+ }
+ },
+ "node_modules/@chakra-ui/system": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz",
+ "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==",
+ "peer": true,
+ "dependencies": {
+ "@chakra-ui/color-mode": "2.2.0",
+ "@chakra-ui/object-utils": "2.1.0",
+ "@chakra-ui/react-utils": "2.0.12",
+ "@chakra-ui/styled-system": "2.9.2",
+ "@chakra-ui/theme-utils": "2.0.21",
+ "@chakra-ui/utils": "2.0.15",
+ "react-fast-compare": "3.2.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0",
+ "@emotion/styled": "^11.0.0",
+ "react": ">=18"
+ }
+ },
+ "node_modules/@chakra-ui/system/node_modules/@chakra-ui/utils": {
+ "version": "2.0.15",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz",
+ "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==",
+ "peer": true,
+ "dependencies": {
+ "@types/lodash.mergewith": "4.6.7",
+ "css-box-model": "1.2.1",
+ "framesync": "6.1.2",
+ "lodash.mergewith": "4.6.2"
+ }
+ },
+ "node_modules/@chakra-ui/system/node_modules/@types/lodash.mergewith": {
+ "version": "4.6.7",
+ "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz",
+ "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==",
+ "peer": true,
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
+ "node_modules/@chakra-ui/theme": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz",
+ "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==",
+ "dependencies": {
+ "@chakra-ui/anatomy": "2.2.2",
+ "@chakra-ui/shared-utils": "2.0.5",
+ "@chakra-ui/theme-tools": "2.1.2"
+ },
+ "peerDependencies": {
+ "@chakra-ui/styled-system": ">=2.8.0"
+ }
+ },
+ "node_modules/@chakra-ui/theme-tools": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz",
+ "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==",
+ "dependencies": {
+ "@chakra-ui/anatomy": "2.2.2",
+ "@chakra-ui/shared-utils": "2.0.5",
+ "color2k": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@chakra-ui/styled-system": ">=2.0.0"
+ }
+ },
+ "node_modules/@chakra-ui/theme-utils": {
+ "version": "2.0.21",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz",
+ "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==",
+ "dependencies": {
+ "@chakra-ui/shared-utils": "2.0.5",
+ "@chakra-ui/styled-system": "2.9.2",
+ "@chakra-ui/theme": "3.3.1",
+ "lodash.mergewith": "4.6.2"
+ }
+ },
+ "node_modules/@chakra-ui/utils": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.2.4.tgz",
+ "integrity": "sha512-nRpR9SnX7aLcJx7lKu8kgQWxdJso1oR/78HcBI+mzidvWdTykbTGdm5Q2R7S0PVH1IFBzBTgi6TiAjHvu96auA==",
+ "dependencies": {
+ "@types/lodash.mergewith": "4.6.9",
+ "lodash.mergewith": "4.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@csstools/normalize.css": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz",
@@ -2220,6 +2583,152 @@
"postcss-selector-parser": "^6.0.10"
}
},
+ "node_modules/@emotion/babel-plugin": {
+ "version": "11.13.5",
+ "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
+ "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/runtime": "^7.18.3",
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/serialize": "^1.3.3",
+ "babel-plugin-macros": "^3.1.0",
+ "convert-source-map": "^1.5.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-root": "^1.1.0",
+ "source-map": "^0.5.7",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
+ },
+ "node_modules/@emotion/babel-plugin/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@emotion/cache": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
+ "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/sheet": "^1.4.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "stylis": "4.2.0"
+ }
+ },
+ "node_modules/@emotion/hash": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+ "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+ },
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz",
+ "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==",
+ "dependencies": {
+ "@emotion/memoize": "^0.9.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
+ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="
+ },
+ "node_modules/@emotion/react": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
+ "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/cache": "^11.14.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2",
+ "@emotion/weak-memoize": "^0.4.0",
+ "hoist-non-react-statics": "^3.3.1"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/serialize": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
+ "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
+ "dependencies": {
+ "@emotion/hash": "^0.9.2",
+ "@emotion/memoize": "^0.9.0",
+ "@emotion/unitless": "^0.10.0",
+ "@emotion/utils": "^1.4.2",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@emotion/sheet": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
+ "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg=="
+ },
+ "node_modules/@emotion/styled": {
+ "version": "11.14.0",
+ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
+ "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "@emotion/babel-plugin": "^11.13.5",
+ "@emotion/is-prop-valid": "^1.3.0",
+ "@emotion/serialize": "^1.3.3",
+ "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
+ "@emotion/utils": "^1.4.2"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.0.0-rc.0",
+ "react": ">=16.8.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
+ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg=="
+ },
+ "node_modules/@emotion/use-insertion-effect-with-fallbacks": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
+ "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
+ "node_modules/@emotion/utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
+ "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA=="
+ },
+ "node_modules/@emotion/weak-memoize": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
+ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg=="
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
@@ -2905,6 +3414,23 @@
}
}
},
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.22.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
+ "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -3229,46 +3755,48 @@
}
},
"node_modules/@testing-library/dom": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz",
- "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==",
+ "version": "9.3.4",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
+ "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
"@types/aria-query": "^5.0.1",
- "aria-query": "5.3.0",
+ "aria-query": "5.1.3",
"chalk": "^4.1.0",
"dom-accessibility-api": "^0.5.9",
"lz-string": "^1.5.0",
"pretty-format": "^27.0.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=14"
}
},
"node_modules/@testing-library/dom/node_modules/aria-query": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
- "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+ "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
"dependencies": {
- "dequal": "^2.0.3"
+ "deep-equal": "^2.0.5"
}
},
"node_modules/@testing-library/jest-dom": {
- "version": "6.6.3",
- "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz",
- "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==",
+ "version": "5.17.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
+ "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==",
"dependencies": {
- "@adobe/css-tools": "^4.4.0",
+ "@adobe/css-tools": "^4.0.1",
+ "@babel/runtime": "^7.9.2",
+ "@types/testing-library__jest-dom": "^5.9.1",
"aria-query": "^5.0.0",
"chalk": "^3.0.0",
"css.escape": "^1.5.1",
- "dom-accessibility-api": "^0.6.3",
- "lodash": "^4.17.21",
+ "dom-accessibility-api": "^0.5.6",
+ "lodash": "^4.17.15",
"redent": "^3.0.0"
},
"engines": {
- "node": ">=14",
+ "node": ">=8",
"npm": ">=6",
"yarn": ">=1"
}
@@ -3285,35 +3813,47 @@
"node": ">=8"
}
},
- "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
- "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="
- },
"node_modules/@testing-library/react": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz",
- "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==",
+ "version": "13.4.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz",
+ "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==",
"dependencies": {
- "@babel/runtime": "^7.12.5"
+ "@babel/runtime": "^7.12.5",
+ "@testing-library/dom": "^8.5.0",
+ "@types/react-dom": "^18.0.0"
},
"engines": {
- "node": ">=18"
+ "node": ">=12"
},
"peerDependencies": {
- "@testing-library/dom": "^10.0.0",
- "@types/react": "^18.0.0 || ^19.0.0",
- "@types/react-dom": "^18.0.0 || ^19.0.0",
- "react": "^18.0.0 || ^19.0.0",
- "react-dom": "^18.0.0 || ^19.0.0"
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/@testing-library/dom": {
+ "version": "8.20.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz",
+ "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==",
+ "dependencies": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^5.0.1",
+ "aria-query": "5.1.3",
+ "chalk": "^4.1.0",
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "pretty-format": "^27.0.2"
},
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "@types/react-dom": {
- "optional": true
- }
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@testing-library/react/node_modules/aria-query": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
+ "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
+ "dependencies": {
+ "deep-equal": "^2.0.5"
}
},
"node_modules/@testing-library/user-event": {
@@ -3545,6 +4085,19 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
+ "node_modules/@types/lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw=="
+ },
+ "node_modules/@types/lodash.mergewith": {
+ "version": "4.6.9",
+ "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.9.tgz",
+ "integrity": "sha512-fgkoCAOF47K7sxrQ7Mlud2TH023itugZs2bUg8h/KzT+BnZNrR2jAOmaokbLunHNnobXVWOezAeNn/lZqwxkcw==",
+ "dependencies": {
+ "@types/lodash": "*"
+ }
+ },
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
@@ -3573,6 +4126,11 @@
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz",
"integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA=="
},
+ "node_modules/@types/prop-types": {
+ "version": "15.7.14",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
+ "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
+ },
"node_modules/@types/q": {
"version": "1.5.8",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz",
@@ -3589,19 +4147,20 @@
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="
},
"node_modules/@types/react": {
- "version": "19.0.10",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
- "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
+ "version": "18.3.18",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
+ "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"dependencies": {
+ "@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.0.4",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
- "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
+ "version": "18.3.5",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz",
+ "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
"peerDependencies": {
- "@types/react": "^19.0.0"
+ "@types/react": "^18.0.0"
}
},
"node_modules/@types/resolve": {
@@ -3662,6 +4221,14 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/testing-library__jest-dom": {
+ "version": "5.14.9",
+ "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz",
+ "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==",
+ "dependencies": {
+ "@types/jest": "*"
+ }
+ },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -4052,6 +4619,19 @@
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
+ "node_modules/@zag-js/dom-query": {
+ "version": "0.31.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/dom-query/-/dom-query-0.31.1.tgz",
+ "integrity": "sha512-oiuohEXAXhBxpzzNm9k2VHGEOLC1SXlXSbRPcfBZ9so5NRQUA++zCE7cyQJqGLTZR0t3itFLlZqDbYEXRrefwg=="
+ },
+ "node_modules/@zag-js/focus-visible": {
+ "version": "0.31.1",
+ "resolved": "https://registry.npmjs.org/@zag-js/focus-visible/-/focus-visible-0.31.1.tgz",
+ "integrity": "sha512-dbLksz7FEwyFoANbpIlNnd3bVm0clQSUsnP8yUVQucStZPsuWjCrhL2jlAbGNrTrahX96ntUMXHb/sM68TibFg==",
+ "dependencies": {
+ "@zag-js/dom-query": "0.31.1"
+ }
+ },
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -4303,6 +4883,17 @@
"sprintf-js": "~1.0.2"
}
},
+ "node_modules/aria-hidden": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
+ "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
@@ -4579,6 +5170,30 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
+ "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+ "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
@@ -5366,6 +5981,11 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
+ "node_modules/color2k": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz",
+ "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog=="
+ },
"node_modules/colord": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
@@ -5504,6 +6124,14 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
"node_modules/core-js": {
"version": "3.40.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz",
@@ -5594,6 +6222,15 @@
"postcss": "^8.4"
}
},
+ "node_modules/css-box-model": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
+ "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+ "peer": true,
+ "dependencies": {
+ "tiny-invariant": "^1.0.6"
+ }
+ },
"node_modules/css-declaration-sorter": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
@@ -6024,6 +6661,37 @@
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
"integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="
},
+ "node_modules/deep-equal": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
+ "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.0",
+ "call-bind": "^1.0.5",
+ "es-get-iterator": "^1.1.3",
+ "get-intrinsic": "^1.2.2",
+ "is-arguments": "^1.1.1",
+ "is-array-buffer": "^3.0.2",
+ "is-date-object": "^1.0.5",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "isarray": "^2.0.5",
+ "object-is": "^1.1.5",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.5.1",
+ "side-channel": "^1.0.4",
+ "which-boxed-primitive": "^1.0.2",
+ "which-collection": "^1.0.1",
+ "which-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -6104,14 +6772,6 @@
"node": ">= 0.8"
}
},
- "node_modules/dequal": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
- "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -6134,6 +6794,11 @@
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
},
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
+ },
"node_modules/detect-port-alt": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz",
@@ -6520,6 +7185,25 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-get-iterator": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+ "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "has-symbols": "^1.0.3",
+ "is-arguments": "^1.1.1",
+ "is-map": "^2.0.2",
+ "is-set": "^2.0.2",
+ "is-string": "^1.0.7",
+ "isarray": "^2.0.5",
+ "stop-iteration-iterator": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/es-iterator-helpers": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz",
@@ -7589,6 +8273,11 @@
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
+ "node_modules/find-root": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+ },
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -7619,6 +8308,17 @@
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="
},
+ "node_modules/focus-lock": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.6.tgz",
+ "integrity": "sha512-Ik/6OCk9RQQ0T5Xw+hKNLWrjSMtv51dD4GRmJjbD5a58TIEpI5a5iXagKVl3Z5UuyslMCA8Xwnu76jQob62Yhg==",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
@@ -7804,6 +8504,57 @@
"url": "https://github.com/sponsors/rawify"
}
},
+ "node_modules/framer-motion": {
+ "version": "10.18.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz",
+ "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ },
+ "optionalDependencies": {
+ "@emotion/is-prop-valid": "^0.8.2"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": {
+ "version": "0.8.8",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+ "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+ "optional": true,
+ "dependencies": {
+ "@emotion/memoize": "0.7.4"
+ }
+ },
+ "node_modules/framer-motion/node_modules/@emotion/memoize": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
+ "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
+ "optional": true
+ },
+ "node_modules/framesync": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz",
+ "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==",
+ "dependencies": {
+ "tslib": "2.4.0"
+ }
+ },
+ "node_modules/framesync/node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+ },
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -7922,6 +8673,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/get-own-enumerable-property-symbols": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
@@ -8220,6 +8979,19 @@
"he": "bin/he"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -8618,6 +9390,21 @@
"node": ">= 10"
}
},
+ "node_modules/is-arguments": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
+ "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-array-buffer": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
@@ -10381,11 +11168,21 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
+ "node_modules/lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
+ },
"node_modules/lodash.sortby": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
"integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
},
+ "node_modules/lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
+ },
"node_modules/lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -10820,6 +11617,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -12642,6 +13454,11 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
@@ -12759,9 +13576,12 @@
}
},
"node_modules/react": {
- "version": "19.0.0",
- "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
- "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
"engines": {
"node": ">=0.10.0"
}
@@ -12787,6 +13607,17 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "node_modules/react-clientside-effect": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.7.tgz",
+ "integrity": "sha512-gce9m0Pk/xYYMEojRI9bgvqQAkl6hm7ozQvqWPyQx+kULiatdHgkNM1QG4DQRx5N9BAzWSCJmt9mMV8/KsdgVg==",
+ "dependencies": {
+ "@babel/runtime": "^7.12.13"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@@ -12887,14 +13718,15 @@
}
},
"node_modules/react-dom": {
- "version": "19.0.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
- "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"dependencies": {
- "scheduler": "^0.25.0"
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
},
"peerDependencies": {
- "react": "^19.0.0"
+ "react": "^18.3.1"
}
},
"node_modules/react-error-overlay": {
@@ -12902,6 +13734,41 @@
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0.tgz",
"integrity": "sha512-SN/U6Ytxf1QGkw/9ve5Y+NxBbZM6Ht95tuXNMKs8EJyFa/Vy/+Co3stop3KBHARfn/giv+Lj1uUnTfOJ3moFEQ=="
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
+ },
+ "node_modules/react-focus-lock": {
+ "version": "2.13.6",
+ "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.13.6.tgz",
+ "integrity": "sha512-ehylFFWyYtBKXjAO9+3v8d0i+cnc1trGS0vlTGhzFW1vbFXVUTmR8s2tt/ZQG8x5hElg6rhENlLG1H3EZK0Llg==",
+ "dependencies": {
+ "@babel/runtime": "^7.0.0",
+ "focus-lock": "^1.3.6",
+ "prop-types": "^15.6.2",
+ "react-clientside-effect": "^1.2.7",
+ "use-callback-ref": "^1.3.3",
+ "use-sidecar": "^1.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
+ "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -12915,6 +13782,81 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-remove-scroll": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz",
+ "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.7",
+ "react-style-singleton": "^2.2.3",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.3",
+ "use-sidecar": "^1.1.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
+ "dependencies": {
+ "react-style-singleton": "^2.2.2",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.29.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz",
+ "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==",
+ "dependencies": {
+ "@remix-run/router": "1.22.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.29.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz",
+ "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==",
+ "dependencies": {
+ "@remix-run/router": "1.22.0",
+ "react-router": "6.29.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -12987,6 +13929,50 @@
}
}
},
+ "node_modules/react-scroll": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/react-scroll/-/react-scroll-1.9.3.tgz",
+ "integrity": "sha512-xv7FXqF3k63aSLNu4/NjFvRNI0ge7DmmmsbeGarP7LZVAlJMSjUuW3dTtLxp1Afijyv0lS2qwC0GiFHvx1KBHQ==",
+ "dependencies": {
+ "lodash.throttle": "^4.1.1",
+ "prop-types": "^15.7.2"
+ },
+ "peerDependencies": {
+ "react": "^15.5.4 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^15.5.4 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-type-animation": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/react-type-animation/-/react-type-animation-3.2.0.tgz",
+ "integrity": "sha512-WXTe0i3rRNKjmggPvT5ntye1QBt0ATGbijeW6V3cQe2W0jaMABXXlPPEdtofnS9tM7wSRHchEvI9SUw+0kUohw==",
+ "peerDependencies": {
+ "prop-types": "^15.5.4",
+ "react": ">= 15.0.0",
+ "react-dom": ">= 15.0.0"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -13543,9 +14529,12 @@
}
},
"node_modules/scheduler": {
- "version": "0.25.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
- "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
},
"node_modules/schema-utils": {
"version": "4.3.0",
@@ -14162,6 +15151,18 @@
"node": ">= 0.8"
}
},
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -14437,6 +15438,11 @@
"postcss": "^8.2.15"
}
},
+ "node_modules/stylis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
+ "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw=="
+ },
"node_modules/sucrase": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
@@ -14903,6 +15909,12 @@
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
},
+ "node_modules/tiny-invariant": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+ "peer": true
+ },
"node_modules/tmpl": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -14919,6 +15931,11 @@
"node": ">=8.0"
}
},
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
+ },
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -15301,6 +16318,47 @@
"requires-port": "^1.0.0"
}
},
+ "node_modules/use-callback-ref": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/package.json b/package.json
index 617225d..9fb63cf 100644
--- a/package.json
+++ b/package.json
@@ -3,17 +3,33 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "@testing-library/dom": "^10.4.0",
- "@testing-library/jest-dom": "^6.6.3",
- "@testing-library/react": "^16.2.0",
+ "@chakra-ui/button": "^2.0.0",
+ "@chakra-ui/color-mode": "^2.0.0",
+ "@chakra-ui/hooks": "^2.0.0",
+ "@chakra-ui/icon": "^3.0.0",
+ "@chakra-ui/icons": "^2.0.0",
+ "@chakra-ui/layout": "^2.0.0",
+ "@chakra-ui/react": "^2.8.0",
+ "@chakra-ui/theme-utils": "^2.0.0",
+ "@emotion/react": "^11.11.0",
+ "@emotion/styled": "^11.11.0",
+ "@testing-library/dom": "^9.3.3",
+ "@testing-library/jest-dom": "^5.17.0",
+ "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.126",
- "@types/react": "^19.0.10",
- "@types/react-dom": "^19.0.4",
- "react": "^19.0.0",
- "react-dom": "^19.0.0",
+ "@types/react": "^18.2.0",
+ "@types/react-dom": "^18.2.0",
+ "axios": "^1.6.2",
+ "framer-motion": "^10.16.4",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-icons": "^4.12.0",
+ "react-router-dom": "^6.20.0",
"react-scripts": "5.0.1",
+ "react-scroll": "^1.9.0",
+ "react-type-animation": "^3.2.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
diff --git a/src/App.tsx b/src/App.tsx
index a53698a..6064780 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,24 +1,23 @@
import React from 'react';
-import logo from './logo.svg';
-import './App.css';
+import Layout from './components/Layout';
+import Hero from './components/Hero';
+import Skills from './components/Skills';
+import Projects from './components/Projects';
+import Experience from './components/Experience';
+import Contact from './components/Contact';
+
+// 创建一个简单包装组件,而不使用ChakraProvider
function App() {
return (
-
-
+
);
}
diff --git a/src/components/Contact.tsx b/src/components/Contact.tsx
new file mode 100644
index 0000000..bb13e7d
--- /dev/null
+++ b/src/components/Contact.tsx
@@ -0,0 +1,311 @@
+import React, { useState } from "react";
+import {
+ Box,
+ Button,
+ Container,
+ Flex,
+ Heading,
+ Input,
+ Stack,
+ Text,
+ Textarea,
+ useToast,
+} from "@chakra-ui/react";
+import { FaEnvelope, FaPhone, FaMapMarkerAlt } from "react-icons/fa";
+import { motion } from "framer-motion";
+import axios from "axios";
+
+interface ContactInfoProps {
+ icon: React.ReactNode;
+ title: string;
+ content: string;
+}
+
+const ContactInfo = ({ icon, title, content }: ContactInfoProps) => {
+ return (
+
+
+ {icon}
+
+
+ {title}
+ {content}
+
+
+ );
+};
+
+const Contact = () => {
+ const [name, setName] = useState("");
+ const [email, setEmail] = useState("");
+ const [subject, setSubject] = useState("");
+ const [message, setMessage] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+ const [cooldown, setCooldown] = useState(0);
+ const toast = useToast();
+
+ const API_URL = process.env.REACT_APP_API_URL || "http://localhost:3001";
+
+ // 倒计时函数
+ React.useEffect(() => {
+ let timer: NodeJS.Timeout | null = null;
+
+ if (cooldown > 0) {
+ timer = setInterval(() => {
+ setCooldown((prev) => {
+ if (prev <= 1) {
+ if (timer) clearInterval(timer);
+ return 0;
+ }
+ return prev - 1;
+ });
+ }, 1000);
+ }
+
+ return () => {
+ if (timer) clearInterval(timer);
+ };
+ }, [cooldown]);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ // 如果在冷却时间内,不允许提交
+ if (cooldown > 0) {
+ toast({
+ title: "请求过于频繁",
+ description: `请在${cooldown}秒后再试`,
+ status: "warning",
+ duration: 3000,
+ isClosable: true,
+ position: "top"
+ });
+ return;
+ }
+
+ setLoading(true);
+ setError("");
+
+ try {
+ // 调用后端API发送邮件
+ const response = await axios.post(`${API_URL}/api/send-email`, {
+ name,
+ email,
+ subject,
+ message
+ });
+
+ // 显示成功消息
+ toast({
+ title: "发送成功",
+ description: "消息已发送,感谢您的留言,我会尽快回复您!",
+ status: "success",
+ duration: 5000,
+ isClosable: true,
+ position: "top"
+ });
+
+ // 重置表单
+ setName("");
+ setEmail("");
+ setSubject("");
+ setMessage("");
+ } catch (err: any) {
+ console.error("发送邮件失败:", err);
+
+ // 处理速率限制错误
+ if (err.response?.status === 429) {
+ const remainingTime = err.response.data.remainingTime || 60;
+ setCooldown(remainingTime);
+
+ toast({
+ title: "请求过于频繁",
+ description: `请在${remainingTime}秒后再试`,
+ status: "warning",
+ duration: 5000,
+ isClosable: true,
+ position: "top"
+ });
+ } else {
+ // 显示其他错误消息
+ setError(
+ err.response?.data?.error ||
+ "发送邮件失败,请稍后再试或直接通过电话联系我"
+ );
+
+ toast({
+ title: "发送失败",
+ description: err.response?.data?.error || "发送邮件失败,请稍后再试",
+ status: "error",
+ duration: 5000,
+ isClosable: true,
+ position: "top"
+ });
+ }
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+
+ 联系我
+
+
+ 如果您对我的专业技能和项目经验感兴趣,欢迎随时联系我
+
+
+
+
+
+
+ 联系方式
+
+
+
+
+
+
+
+ 期待与您就Java后台开发和大数据项目进行深入交流,无论是项目合作还是技术讨论,我都非常欢迎。
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Contact;
\ No newline at end of file
diff --git a/src/components/Experience.tsx b/src/components/Experience.tsx
new file mode 100644
index 0000000..ad4861c
--- /dev/null
+++ b/src/components/Experience.tsx
@@ -0,0 +1,140 @@
+import React from "react";
+import {
+ Box,
+ Container,
+ Heading,
+ Text,
+ Stack,
+ Flex,
+ Circle,
+} from "@chakra-ui/react";
+import { motion } from "framer-motion";
+
+interface ExperienceItemProps {
+ position: string;
+ company: string;
+ period: string;
+ description: string;
+}
+
+const ExperienceItem = ({ position, company, period, description }: ExperienceItemProps) => {
+ return (
+
+
+
+ {period.split("-")[0]}
+
+
+
+ {position}
+
+
+ {company}
+
+
+ {period}
+
+
+
+
+ {description}
+
+
+ );
+};
+
+const Experience = () => {
+ const experiences = [
+ {
+ position: "高级Java架构师",
+ company: "某科技有限公司",
+ period: "2020 - 至今",
+ description:
+ "负责公司核心业务系统的架构设计和技术选型,主导微服务架构重构和云原生改造,带领团队完成多个高并发、高可用的分布式系统开发。优化系统性能,解决系统扩展性问题,将系统响应时间提升40%,支持每日千万级交易量。",
+ },
+ {
+ position: "大数据开发工程师",
+ company: "某大数据公司",
+ period: "2018 - 2020",
+ description:
+ "负责公司数据处理平台的开发和优化,主要使用Hadoop、Spark和Flink技术栈,设计实现了实时和离线数据处理流程,开发数据ETL过程,构建数据仓库,为企业提供数据分析支持。成功将数据处理延迟从小时级降低到分钟级。",
+ },
+ {
+ position: "Java高级开发工程师",
+ company: "某互联网金融公司",
+ period: "2016 - 2018",
+ description:
+ "负责公司支付系统和风控系统的开发维护,使用Spring Boot、Spring Cloud微服务技术栈,设计实现高并发交易系统,优化数据库访问性能,开发分布式事务解决方案。系统日均处理交易50万笔,峰值TPS达到3000。",
+ },
+ {
+ position: "Java开发工程师",
+ company: "某软件开发公司",
+ period: "2014 - 2016",
+ description:
+ "参与企业级应用系统开发,负责后台服务和API开发,使用Spring、MyBatis等框架实现业务逻辑和数据访问层,编写单元测试和集成测试,参与code review和性能优化工作。",
+ },
+ ];
+
+ return (
+
+
+
+
+ 工作经验
+
+
+ 多年Java后台开发与大数据工程实践经验
+
+
+
+
+
+ {experiences.map((exp, index) => (
+
+
+
+ ))}
+
+
+
+
+
+ );
+};
+
+export default Experience;
\ No newline at end of file
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
new file mode 100644
index 0000000..d88f880
--- /dev/null
+++ b/src/components/Footer.tsx
@@ -0,0 +1,116 @@
+import React from "react";
+// 移除图标导入
+// import { FaTwitter, FaYoutube, FaGithub, FaLinkedin } from "react-icons/fa";
+// import { IconContext } from "react-icons";
+
+const footerStyle = {
+ background: "#f7fafc",
+ color: "#4a5568",
+ marginTop: "40px"
+};
+
+const containerStyle = {
+ maxWidth: "1200px",
+ margin: "0 auto",
+ padding: "40px 16px"
+};
+
+const gridStyle = {
+ display: "grid",
+ gridTemplateColumns: "2fr 1fr 1fr 1fr",
+ gap: "32px"
+};
+
+const stackStyle = {
+ display: "flex",
+ flexDirection: "column" as const,
+ gap: "24px"
+};
+
+const headingStyle = {
+ color: "#3182ce",
+ fontSize: "16px",
+ fontWeight: "bold"
+};
+
+const textStyle = {
+ fontSize: "14px"
+};
+
+const socialIconsStyle = {
+ display: "flex",
+ flexDirection: "row" as const,
+ gap: "24px"
+};
+
+const iconStyle = {
+ color: "#718096",
+ fontSize: "20px",
+ width: "20px",
+ height: "20px"
+};
+
+export default function Footer() {
+ const currentYear = new Date().getFullYear();
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx
new file mode 100644
index 0000000..6fe5cf4
--- /dev/null
+++ b/src/components/Hero.tsx
@@ -0,0 +1,158 @@
+import React from "react";
+import {
+ Box,
+ Button,
+ Container,
+ Flex,
+ Heading,
+ Image,
+ Stack,
+ Text,
+ Link,
+} from "@chakra-ui/react";
+import { Icon } from "@chakra-ui/icon";
+import { TypeAnimation } from 'react-type-animation';
+import { FaGithub, FaLinkedin } from 'react-icons/fa';
+import { motion } from "framer-motion";
+
+const Hero = () => {
+ // 由于useColorModeValue在当前版本可能不可用,我们使用简单的方式定义颜色
+ const textColor = "blue.600";
+ const headingColor = "gray.800";
+ const descriptionColor = "gray.600";
+ const hoverBg = "gray.200";
+
+ return (
+
+
+
+
+
+ 你好,我是
+
+
+
+ Java后台与大数据专家
+
+
+
+
+
+
+ 我是一名资深Java后台开发工程师和大数据专家,拥有多年企业级应用开发和大规模数据处理系统设计经验。
+ 我擅长解决高并发、高可用的系统架构问题,以及复杂数据分析和流处理解决方案。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Hero;
\ No newline at end of file
diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx
new file mode 100644
index 0000000..3139173
--- /dev/null
+++ b/src/components/Layout.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+import Navbar from "./Navbar";
+import Footer from "./Footer";
+
+interface LayoutProps {
+ children: React.ReactNode;
+}
+
+export default function Layout({ children }: LayoutProps) {
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
new file mode 100644
index 0000000..36f1d34
--- /dev/null
+++ b/src/components/Navbar.tsx
@@ -0,0 +1,241 @@
+import React, { useState, useEffect } from "react";
+
+const navbarStyle = {
+ position: "fixed" as const,
+ top: 0,
+ width: "100%",
+ zIndex: 100,
+ background: "white",
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.1)",
+ borderBottom: "1px solid #eaeaea"
+};
+
+const containerStyle = {
+ maxWidth: "1200px",
+ margin: "0 auto",
+ padding: "12px 16px",
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between"
+};
+
+const logoStyle = {
+ fontWeight: "bold",
+ fontSize: "20px",
+ color: "#3182ce"
+};
+
+const navLinksStyle = {
+ display: "flex",
+ gap: "32px"
+};
+
+const linkStyle = {
+ color: "#4a5568",
+ fontWeight: 500,
+ textDecoration: "none",
+ padding: "8px 0",
+ position: "relative" as const,
+ transition: "color 0.3s ease"
+};
+
+const activeLinkStyle = {
+ ...linkStyle,
+ color: "#3182ce",
+};
+
+const linkHoverStyle = {
+ color: "#3182ce",
+};
+
+const linkAfterStyle = {
+ content: '""',
+ position: "absolute" as const,
+ width: "0%",
+ height: "2px",
+ bottom: "0",
+ left: "50%",
+ backgroundColor: "#3182ce",
+ transition: "all 0.3s ease",
+ transform: "translateX(-50%)"
+};
+
+const activeLinkAfterStyle = {
+ ...linkAfterStyle,
+ width: "100%"
+};
+
+const buttonStyle = {
+ padding: "8px 16px",
+ backgroundColor: "#3182ce",
+ color: "white",
+ border: "none",
+ borderRadius: "4px",
+ cursor: "pointer",
+ transition: "background-color 0.3s ease",
+};
+
+const buttonHoverStyle = {
+ backgroundColor: "#2c5282",
+};
+
+const mobileMenuStyle = {
+ display: "none",
+ "@media (max-width: 768px)": {
+ display: "block"
+ }
+};
+
+const Navbar = () => {
+ const [activeSection, setActiveSection] = useState("home");
+ const [hoveredLink, setHoveredLink] = useState
(null);
+ const [isButtonHovered, setIsButtonHovered] = useState(false);
+
+ // 监听滚动事件,更新当前活动的导航项
+ useEffect(() => {
+ const handleScroll = () => {
+ const scrollPosition = window.scrollY;
+
+ // 获取各个部分的位置
+ const homeSection = document.getElementById("home")?.offsetTop || 0;
+ const skillsSection = document.getElementById("skills")?.offsetTop || 0;
+ const experienceSection = document.getElementById("experience")?.offsetTop || 0;
+ const projectsSection = document.getElementById("projects")?.offsetTop || 0;
+ const contactSection = document.getElementById("contact")?.offsetTop || 0;
+
+ // 设置一个偏移量,使导航项在滚动到相应部分之前就高亮显示
+ const offset = 100;
+
+ // 根据滚动位置更新活动的导航项
+ if (scrollPosition < skillsSection - offset) {
+ setActiveSection("home");
+ } else if (scrollPosition < experienceSection - offset) {
+ setActiveSection("skills");
+ } else if (scrollPosition < projectsSection - offset) {
+ setActiveSection("experience");
+ } else if (scrollPosition < contactSection - offset) {
+ setActiveSection("projects");
+ } else {
+ setActiveSection("contact");
+ }
+ };
+
+ window.addEventListener("scroll", handleScroll);
+
+ // 组件卸载时移除事件监听
+ return () => {
+ window.removeEventListener("scroll", handleScroll);
+ };
+ }, []);
+
+ // 获取链接样式
+ const getLinkStyle = (section: string) => {
+ const isActive = activeSection === section;
+ const isHovered = hoveredLink === section;
+
+ return {
+ ...(isActive ? activeLinkStyle : linkStyle),
+ ...(isHovered ? linkHoverStyle : {}),
+ "&::after": isActive ? activeLinkAfterStyle : isHovered ? { ...linkAfterStyle, width: "50%" } : linkAfterStyle
+ };
+ };
+
+ return (
+
+ );
+};
+
+export default Navbar;
\ No newline at end of file
diff --git a/src/components/Projects.tsx b/src/components/Projects.tsx
new file mode 100644
index 0000000..71c4d48
--- /dev/null
+++ b/src/components/Projects.tsx
@@ -0,0 +1,189 @@
+import React from "react";
+import {
+ Box,
+ Button,
+ Container,
+ Flex,
+ Heading,
+ Image,
+ SimpleGrid,
+ Stack,
+ Text,
+ Badge,
+ Link,
+} from "@chakra-ui/react";
+import { Icon } from "@chakra-ui/icon";
+import { motion } from "framer-motion";
+import { FaGithub, FaExternalLinkAlt } from "react-icons/fa";
+
+interface ProjectCardProps {
+ title: string;
+ description: string;
+ image: string;
+ technologies: string[];
+ github: string | null;
+ demo: string | null;
+}
+
+const ProjectCard = ({ title, description, image, technologies, github, demo }: ProjectCardProps) => {
+ return (
+
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+ {technologies.map((tech, index) => (
+
+ {tech}
+
+ ))}
+
+
+ {github && (
+
+
+
+ )}
+ {demo && (
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+const Projects = () => {
+ const projects = [
+ {
+ title: "分布式微服务电商平台",
+ description: "基于Spring Cloud构建的高可用电商系统,包含用户、商品、订单、支付等微服务,实现了服务注册发现、配置中心、负载均衡、熔断降级等功能。",
+ image: "https://images.unsplash.com/photo-1556742049-0cfed4f6a45d?q=80&w=2070",
+ technologies: ["Java", "Spring Boot", "Spring Cloud", "MySQL", "Redis", "RabbitMQ"],
+ github: "#",
+ demo: "#",
+ },
+ {
+ title: "实时数据分析平台",
+ description: "基于Kafka和Flink构建的实时数据处理平台,支持实时日志收集、指标计算、异常检测和业务监控,应用于电商、金融等多个场景。",
+ image: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070",
+ technologies: ["Java", "Flink", "Kafka", "Elasticsearch", "Kibana", "Prometheus"],
+ github: "#",
+ demo: "#",
+ },
+ {
+ title: "分布式数据仓库系统",
+ description: "构建基于Hadoop生态的数据仓库系统,实现离线数据的ETL处理、存储和分析,支持多维度数据分析和报表生成。",
+ image: "https://images.unsplash.com/photo-1504384308090-c894fdcc538d?q=80&w=2070",
+ technologies: ["Hadoop", "Hive", "Spark", "HBase", "Sqoop", "Airflow"],
+ github: "#",
+ demo: null,
+ },
+ {
+ title: "容器化微服务DevOps平台",
+ description: "搭建基于Kubernetes的微服务DevOps平台,实现应用的自动构建、部署、监控和运维,提高了开发和运维效率。",
+ image: "https://images.unsplash.com/photo-1667372393119-3d4c48d07fc9?q=80&w=2070",
+ technologies: ["Docker", "Kubernetes", "Jenkins", "Prometheus", "Grafana", "Istio"],
+ github: "#",
+ demo: null,
+ },
+ {
+ title: "分布式搜索引擎",
+ description: "基于Elasticsearch构建的分布式搜索系统,支持全文检索、模糊匹配、数据聚合等功能,应用于企业内部知识库和产品搜索。",
+ image: "https://images.unsplash.com/photo-1483058712412-4245e9b90334?q=80&w=2070",
+ technologies: ["Java", "Elasticsearch", "Spring Boot", "Redis", "MySQL", "Vue"],
+ github: "#",
+ demo: "#",
+ },
+ {
+ title: "机器学习推荐系统",
+ description: "基于Spark MLlib实现的个性化推荐系统,通过协同过滤和内容特征分析,为用户提供精准的产品和内容推荐。",
+ image: "https://images.unsplash.com/photo-1489549132488-d00b7eee80f1?q=80&w=2073",
+ technologies: ["Spark", "Python", "MLlib", "Kafka", "Spring Boot", "MySQL"],
+ github: "#",
+ demo: null,
+ },
+ ];
+
+ return (
+
+
+
+
+ 项目案例
+
+
+ 以下是我开发的一些代表性项目,涵盖企业级应用、大数据处理和云原生架构
+
+
+
+ {projects.map((project, index) => (
+
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export default Projects;
\ No newline at end of file
diff --git a/src/components/Skills.tsx b/src/components/Skills.tsx
new file mode 100644
index 0000000..c70fd80
--- /dev/null
+++ b/src/components/Skills.tsx
@@ -0,0 +1,167 @@
+import React from "react";
+import {
+ Box,
+ Container,
+ Flex,
+ Heading,
+ SimpleGrid,
+ Text,
+} from "@chakra-ui/react";
+import { Icon } from "@chakra-ui/icon";
+import { FaJava, FaDatabase, FaCloud, FaServer } from "react-icons/fa";
+import { motion } from "framer-motion";
+
+interface SkillData {
+ name: string;
+ level: number;
+}
+
+interface SkillCategoryProps {
+ title: string;
+ icon: React.ReactNode;
+ skills: SkillData[];
+}
+
+const SkillCategory = ({ title, icon, skills }: SkillCategoryProps) => {
+ const bgColor = "gray.50";
+ const borderColor = "gray.200";
+ const titleColor = "blue.600";
+
+ return (
+
+
+ {icon}
+
+ {title}
+
+
+
+ {skills.map((skill, index) => (
+
+
+ {skill.name}
+ {skill.level}%
+
+
+
+
+
+ ))}
+
+ );
+};
+
+const Skills = () => {
+ const headingColor = "gray.700";
+ const textColor = "gray.600";
+
+ const javaSkills = [
+ { name: "Java Core", level: 95 },
+ { name: "Spring Boot", level: 90 },
+ { name: "Spring Cloud", level: 85 },
+ { name: "Microservices", level: 88 },
+ { name: "JPA/Hibernate", level: 85 }
+ ];
+
+ const databaseSkills = [
+ { name: "MySQL", level: 90 },
+ { name: "PostgreSQL", level: 85 },
+ { name: "MongoDB", level: 80 },
+ { name: "Redis", level: 88 },
+ { name: "Oracle", level: 75 }
+ ];
+
+ const bigDataSkills = [
+ { name: "Hadoop", level: 85 },
+ { name: "Spark", level: 80 },
+ { name: "Kafka", level: 88 },
+ { name: "Flink", level: 78 },
+ { name: "Hive", level: 82 }
+ ];
+
+ const cloudSkills = [
+ { name: "Docker", level: 90 },
+ { name: "Kubernetes", level: 82 },
+ { name: "AWS", level: 78 },
+ { name: "Azure", level: 75 },
+ { name: "CI/CD", level: 85 }
+ ];
+
+ return (
+
+
+
+
+ 专业技能
+
+
+ 多年的企业级Java开发和大数据处理经验,专注于构建高性能、高可用的系统
+
+
+
+ }
+ skills={javaSkills}
+ />
+ }
+ skills={databaseSkills}
+ />
+ }
+ skills={bigDataSkills}
+ />
+ }
+ skills={cloudSkills}
+ />
+
+
+
+
+ );
+};
+
+export default Skills;
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 032464f..2ab0f26 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,13 +3,26 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
+import { CustomChakraProvider } from './theme/ChakraContext';
+
+// 使用全局变量
+declare global {
+ interface Window {
+ ChakraProvider: any;
+ }
+}
+
+// 移除未使用的变量
+// const ChakraProvider = window.ChakraProvider || React.Fragment;
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
-
+
+
+
);
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
new file mode 100644
index 0000000..bbf1f59
--- /dev/null
+++ b/src/pages/Home.tsx
@@ -0,0 +1,354 @@
+import React from "react";
+import {
+ Box,
+ Heading,
+ Container,
+ Text,
+ Flex,
+ Image,
+ VStack,
+ HStack,
+ Badge
+} from "@chakra-ui/react";
+import { Button } from "@chakra-ui/button";
+import { Stack, SimpleGrid } from "@chakra-ui/layout";
+import { Icon } from "@chakra-ui/icon";
+import { useColorModeValue } from "@chakra-ui/color-mode";
+import { createIcon } from "@chakra-ui/icons";
+import { Link as RouterLink } from "react-router-dom";
+import { TypeAnimation } from "react-type-animation";
+import { FaJava, FaDatabase, FaCloud, FaGithub } from "react-icons/fa";
+import { SiApachekafka, SiSpring, SiApachehadoop, SiApachespark } from "react-icons/si";
+import type { IconType } from "react-icons";
+
+export default function Home() {
+ return (
+
+ {/* Hero Section */}
+
+
+
+
+ Java全栈开发者
+
+
+
+
+
+
+
+ 我是一名拥有多年经验的Java开发者,专注于企业级应用开发、微服务架构设计和大数据处理。
+ 在这里,你可以了解我的技能、项目经验和技术博客。
+
+
+
+
+
+
+
+ 开始了解我吧!
+
+
+
+
+
+
+ {/* Skills Overview */}
+
+
+
+ 我的技术栈
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Featured Projects */}
+
+
+
+ 精选项目
+
+
+
+
+
+
+
+
+
+
+
+ {/* Call to Action */}
+
+
+
+
+ 准备好开始合作了吗?
+
+
+ 无论是全职机会、自由职业项目还是技术咨询,我都很乐意与您交流。
+ 让我们一起创造令人惊叹的技术解决方案!
+
+
+
+
+
+
+
+
+ );
+}
+
+// Components
+const SkillCard = ({ icon, title, description }: { icon: IconType, title: string, description: string }) => {
+ return (
+
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+ );
+};
+
+const ProjectCard = ({ title, description, tags, image }: {
+ title: string,
+ description: string,
+ tags: string[],
+ image: string
+}) => {
+ return (
+
+
+
+ {title}
+ {description}
+
+ {tags.map((tag) => (
+
+ {tag}
+
+ ))}
+
+
+
+ );
+};
+
+const Arrow = createIcon({
+ displayName: "Arrow",
+ viewBox: "0 0 72 24",
+ path: (
+
+ )
+});
\ No newline at end of file
diff --git a/src/theme/ChakraContext.tsx b/src/theme/ChakraContext.tsx
new file mode 100644
index 0000000..9c6f162
--- /dev/null
+++ b/src/theme/ChakraContext.tsx
@@ -0,0 +1,15 @@
+import React, { ReactNode } from 'react';
+import { ChakraProvider } from '@chakra-ui/react';
+import theme from './theme';
+
+interface ChakraProviderProps {
+ children: ReactNode;
+}
+
+export const CustomChakraProvider: React.FC = ({ children }) => {
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/src/theme/theme.ts b/src/theme/theme.ts
new file mode 100644
index 0000000..5f82ca3
--- /dev/null
+++ b/src/theme/theme.ts
@@ -0,0 +1,20 @@
+import { ThemeConfig, extendTheme } from '@chakra-ui/react';
+
+// 创建一个自定义主题
+const config: ThemeConfig = {
+ initialColorMode: 'light',
+ useSystemColorMode: false,
+};
+
+const theme = extendTheme({
+ config,
+ colors: {
+ brand: {
+ 900: "#1a365d",
+ 800: "#153e75",
+ 700: "#2a69ac",
+ },
+ },
+});
+
+export default theme;
\ No newline at end of file