diff --git a/.gitignore b/.gitignore index cb468b1..e420099 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,5 @@ dist-ssr *.sln *.sw? -.env \ No newline at end of file +.env +.vercel diff --git a/package.json b/package.json index e8cecc8..24c6a60 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ ] }, "dependencies": { + "@emailjs/browser": "^4.4.1", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@mui/icons-material": "^7.1.2", @@ -57,6 +58,7 @@ "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^8.34.1", "@typescript-eslint/parser": "^8.34.1", + "@vercel/node": "^5.3.0", "@vitejs/plugin-react": "^4.4.1", "babel-plugin-import": "^1.13.8", "eslint": "^8.57.1", @@ -79,5 +81,8 @@ "typescript": "~5.8.3", "typescript-eslint": "^8.30.1", "vite": "^6.3.5" + }, + "engines": { + "node": "18.x" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c6a0e8..c8e6901 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@emailjs/browser': + specifier: ^4.4.1 + version: 4.4.1 '@emotion/react': specifier: ^11.14.0 version: 11.14.0(@types/react@19.1.8)(react@19.1.0) @@ -69,6 +72,9 @@ importers: '@typescript-eslint/parser': specifier: ^8.34.1 version: 8.35.0(eslint@8.57.1)(typescript@5.8.3) + '@vercel/node': + specifier: ^5.3.0 + version: 5.3.0(rollup@4.44.0) '@vitejs/plugin-react': specifier: ^4.4.1 version: 4.6.0(vite@6.3.5(@types/node@24.0.3)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)) @@ -294,6 +300,34 @@ packages: resolution: {integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==} engines: {node: '>=v18'} + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@edge-runtime/format@2.2.1': + resolution: {integrity: sha512-JQTRVuiusQLNNLe2W9tnzBlV/GvSVcozLl4XZHk5swnRZ/v6jp8TqR8P7sqmJsQqblDZ3EztcWmLDbhRje/+8g==} + engines: {node: '>=16'} + + '@edge-runtime/node-utils@2.3.0': + resolution: {integrity: sha512-uUtx8BFoO1hNxtHjp3eqVPC/mWImGb2exOfGjMLUoipuWgjej+f4o/VP4bUI8U40gu7Teogd5VTeZUkGvJSPOQ==} + engines: {node: '>=16'} + + '@edge-runtime/ponyfill@2.4.2': + resolution: {integrity: sha512-oN17GjFr69chu6sDLvXxdhg0Qe8EZviGSuqzR9qOiKh4MhFYGdBBcqRNzdmYeAdeRzOW2mM9yil4RftUQ7sUOA==} + engines: {node: '>=16'} + + '@edge-runtime/primitives@4.1.0': + resolution: {integrity: sha512-Vw0lbJ2lvRUqc7/soqygUX216Xb8T3WBZ987oywz6aJqRxcwSVWwr9e+Nqo2m9bxobA9mdbWNNoRY6S9eko1EQ==} + engines: {node: '>=16'} + + '@edge-runtime/vm@3.2.0': + resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==} + engines: {node: '>=16'} + + '@emailjs/browser@4.4.1': + resolution: {integrity: sha512-DGSlP9sPvyFba3to2A50kDtZ+pXVp/0rhmqs2LmbMS3I5J8FSOgLwzY2Xb4qfKlOVHh29EAutLYwe5yuEZmEFg==} + engines: {node: '>=14.0.0'} + '@emnapi/core@1.4.3': resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} @@ -529,6 +563,10 @@ packages: resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@fastify/busboy@2.1.1': + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + '@feature-sliced/eslint-config@0.1.1': resolution: {integrity: sha512-6g2xgY3TVv24N1828U5gnfT7/R2aNiRghhCs45j6mbltLQik0CR7puKJhZSmhLqczMDM8TRqLVNhaCdvdgZpWw==} peerDependencies: @@ -767,6 +805,14 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -785,6 +831,14 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@mapbox/node-pre-gyp@2.0.0': + resolution: {integrity: sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==} + engines: {node: '>=18'} + hasBin: true + '@mui/core-downloads-tracker@7.1.2': resolution: {integrity: sha512-0gLO1PvbJwSYe5ji021tGj6HFqrtEPMGKK4L1zWwRbhzrWWUumUJvMvJUsIgWQIYQsgOnhq9k2Fc1BxLGHDsAg==} @@ -891,6 +945,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -927,6 +985,15 @@ packages: '@rolldown/pluginutils@1.0.0-beta.19': resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} + '@rollup/pluginutils@5.2.0': + resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.44.0': resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} cpu: [arm] @@ -1046,6 +1113,21 @@ packages: peerDependencies: react: ^18 || ^19 + '@ts-morph/common@0.11.1': + resolution: {integrity: sha512-7hWZS0NRpEsNV8vWJzg7FEz6V8MaLNeJOmwmghqUXTpzk16V1LLZhdo+4QvE/+zv4cVci0OviuJFnqhEfoV3+g==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} @@ -1070,6 +1152,9 @@ packages: '@types/history@4.7.11': resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -1079,6 +1164,9 @@ packages: '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + '@types/node@16.18.11': + resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==} + '@types/node@24.0.3': resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} @@ -1270,6 +1358,23 @@ packages: cpu: [x64] os: [win32] + '@vercel/build-utils@10.6.1': + resolution: {integrity: sha512-E6O45bInBcKFDtliPADlNpIMutPjzGepYVfV2GyXdxf+00k6wMAlTQ/HbgWhvErOvy7TkZxFxrkRghAWnGK+UA==} + + '@vercel/error-utils@2.0.3': + resolution: {integrity: sha512-CqC01WZxbLUxoiVdh9B/poPbNpY9U+tO1N9oWHwTl5YAZxcqXmmWJ8KNMFItJCUUWdY3J3xv8LvAuQv2KZ5YdQ==} + + '@vercel/nft@0.29.2': + resolution: {integrity: sha512-A/Si4mrTkQqJ6EXJKv5EYCDQ3NL6nJXxG8VGXePsaiQigsomHYQC9xSpX8qGk7AEZk4b1ssbYIqJ0ISQQ7bfcA==} + engines: {node: '>=18'} + hasBin: true + + '@vercel/node@5.3.0': + resolution: {integrity: sha512-NeE5c7dRt9PXUzq7zUA+rj94l7AoXBw2cE+xK0hIoYDcWbIJVYBhbkBtzNdZx8CGncUJ2wMq01gn8pCwoQ0xYA==} + + '@vercel/static-config@3.1.1': + resolution: {integrity: sha512-IRtKnm9N1Uqd2ayIbLPjRtdwcl1GTWvqF1PuEVNm9O43kmoI+m9VpGlW8oga+5LQq1LmJ2Y67zHr7NbjrH1rrw==} + '@vitejs/plugin-react@4.6.0': resolution: {integrity: sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -1280,22 +1385,42 @@ packages: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.6.3: + resolution: {integrity: sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -1323,6 +1448,9 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1376,6 +1504,17 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async-listen@3.0.0: + resolution: {integrity: sha512-V+SsTpDqkrWTimiotsyl33ePSjA5/KrithwupuvJ6ztsqPvGv6ge4OredFhPffVXiLN/QUWvE0XcqJaYgt6fOg==} + engines: {node: '>= 14'} + + async-listen@3.0.1: + resolution: {integrity: sha512-cWMaNwUJnf37C/S5TfCkk/15MwbPRwVYALA2jtjkbHjCmAPiDXyNJy2q3p1KAZzDLHAWyarUWSujUoHR4pEgrA==} + engines: {node: '>= 14'} + + async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -1405,6 +1544,9 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -1484,6 +1626,10 @@ packages: charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-launcher@0.14.2: resolution: {integrity: sha512-Nk8DUCIfPR6p9WClPPFeP2ztpAdkT8xueoiDS03csea1uoJjm4w0p5Oy1hjykyjT1EQ0MMrEshLD3C8gHXyiZw==} engines: {node: '>=12.13.0'} @@ -1491,6 +1637,9 @@ packages: ci-info@2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + cjs-module-lexer@1.2.3: + resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cli-boxes@2.2.1: resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} engines: {node: '>=6'} @@ -1529,6 +1678,9 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + code-block-writer@10.1.1: + resolution: {integrity: sha512-67ueh2IRGst/51p0n6FvPrnRjAGHY5F8xdjkgrYE7DDzpJe6qA07RYQ9VcoUeo5ATOjSOiWpSL3SWBRRbempMw==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1553,6 +1705,10 @@ packages: resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} engines: {node: '>=8'} + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + conventional-changelog-angular@7.0.0: resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} engines: {node: '>=16'} @@ -1566,6 +1722,10 @@ packages: engines: {node: '>=16'} hasBin: true + convert-hrtime@3.0.0: + resolution: {integrity: sha512-7V+KqSvMiHp8yWDuwfww06XleMWVVB9b9tURBx+G7UTADuo5hYPuowKloz4OzOqbPezxgo+fdQ1522WzPG4OeA==} + engines: {node: '>=8'} + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -1601,6 +1761,9 @@ packages: typescript: optional: true + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1705,6 +1868,14 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1727,6 +1898,14 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + edge-runtime@2.5.9: + resolution: {integrity: sha512-pk+k0oK0PVXdlT4oRp4lwh+unuKB7Ng4iZ2HB+EZ7QCEQizX360Rp/F4aRpgpRgdP2ufB35N+1KppHmYjqIGSg==} + engines: {node: '>=16'} + hasBin: true + electron-to-chromium@1.5.172: resolution: {integrity: sha512-fnKW9dGgmBfsebbYognQSv0CGGLFH1a5iV9EDYTBwmAQn+whbzHbLFlC+3XbHc8xaNtpO0etm8LOcRXs1qMRkQ==} @@ -1773,6 +1952,9 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} + es-module-lexer@1.4.1: + resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -1789,6 +1971,131 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild-android-64@0.14.47: + resolution: {integrity: sha512-R13Bd9+tqLVFndncMHssZrPWe6/0Kpv2/dt4aA69soX4PRxlzsVpCvoJeFE8sOEoeVEiBkI0myjlkDodXlHa0g==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + esbuild-android-arm64@0.14.47: + resolution: {integrity: sha512-OkwOjj7ts4lBp/TL6hdd8HftIzOy/pdtbrNA4+0oVWgGG64HrdVzAF5gxtJufAPOsEjkyh1oIYvKAUinKKQRSQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + esbuild-darwin-64@0.14.47: + resolution: {integrity: sha512-R6oaW0y5/u6Eccti/TS6c/2c1xYTb1izwK3gajJwi4vIfNs1s8B1dQzI1UiC9T61YovOQVuePDcfqHLT3mUZJA==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + esbuild-darwin-arm64@0.14.47: + resolution: {integrity: sha512-seCmearlQyvdvM/noz1L9+qblC5vcBrhUaOoLEDDoLInF/VQ9IkobGiLlyTPYP5dW1YD4LXhtBgOyevoIHGGnw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + esbuild-freebsd-64@0.14.47: + resolution: {integrity: sha512-ZH8K2Q8/Ux5kXXvQMDsJcxvkIwut69KVrYQhza/ptkW50DC089bCVrJZZ3sKzIoOx+YPTrmsZvqeZERjyYrlvQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + esbuild-freebsd-arm64@0.14.47: + resolution: {integrity: sha512-ZJMQAJQsIOhn3XTm7MPQfCzEu5b9STNC+s90zMWe2afy9EwnHV7Ov7ohEMv2lyWlc2pjqLW8QJnz2r0KZmeAEQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + esbuild-linux-32@0.14.47: + resolution: {integrity: sha512-FxZOCKoEDPRYvq300lsWCTv1kcHgiiZfNrPtEhFAiqD7QZaXrad8LxyJ8fXGcWzIFzRiYZVtB3ttvITBvAFhKw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + esbuild-linux-64@0.14.47: + resolution: {integrity: sha512-nFNOk9vWVfvWYF9YNYksZptgQAdstnDCMtR6m42l5Wfugbzu11VpMCY9XrD4yFxvPo9zmzcoUL/88y0lfJZJJw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + esbuild-linux-arm64@0.14.47: + resolution: {integrity: sha512-ywfme6HVrhWcevzmsufjd4iT3PxTfCX9HOdxA7Hd+/ZM23Y9nXeb+vG6AyA6jgq/JovkcqRHcL9XwRNpWG6XRw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + esbuild-linux-arm@0.14.47: + resolution: {integrity: sha512-ZGE1Bqg/gPRXrBpgpvH81tQHpiaGxa8c9Rx/XOylkIl2ypLuOcawXEAo8ls+5DFCcRGt/o3sV+PzpAFZobOsmA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + esbuild-linux-mips64le@0.14.47: + resolution: {integrity: sha512-mg3D8YndZ1LvUiEdDYR3OsmeyAew4MA/dvaEJxvyygahWmpv1SlEEnhEZlhPokjsUMfRagzsEF/d/2XF+kTQGg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + esbuild-linux-ppc64le@0.14.47: + resolution: {integrity: sha512-WER+f3+szmnZiWoK6AsrTKGoJoErG2LlauSmk73LEZFQ/iWC+KhhDsOkn1xBUpzXWsxN9THmQFltLoaFEH8F8w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + esbuild-linux-riscv64@0.14.47: + resolution: {integrity: sha512-1fI6bP3A3rvI9BsaaXbMoaOjLE3lVkJtLxsgLHqlBhLlBVY7UqffWBvkrX/9zfPhhVMd9ZRFiaqXnB1T7BsL2g==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + esbuild-linux-s390x@0.14.47: + resolution: {integrity: sha512-eZrWzy0xFAhki1CWRGnhsHVz7IlSKX6yT2tj2Eg8lhAwlRE5E96Hsb0M1mPSE1dHGpt1QVwwVivXIAacF/G6mw==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + esbuild-netbsd-64@0.14.47: + resolution: {integrity: sha512-Qjdjr+KQQVH5Q2Q1r6HBYswFTToPpss3gqCiSw2Fpq/ua8+eXSQyAMG+UvULPqXceOwpnPo4smyZyHdlkcPppQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + esbuild-openbsd-64@0.14.47: + resolution: {integrity: sha512-QpgN8ofL7B9z8g5zZqJE+eFvD1LehRlxr25PBkjyyasakm4599iroUpaj96rdqRlO2ShuyqwJdr+oNqWwTUmQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + esbuild-sunos-64@0.14.47: + resolution: {integrity: sha512-uOeSgLUwukLioAJOiGYm3kNl+1wJjgJA8R671GYgcPgCx7QR73zfvYqXFFcIO93/nBdIbt5hd8RItqbbf3HtAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + esbuild-windows-32@0.14.47: + resolution: {integrity: sha512-H0fWsLTp2WBfKLBgwYT4OTfFly4Im/8B5f3ojDv1Kx//kiubVY0IQunP2Koc/fr/0wI7hj3IiBDbSrmKlrNgLQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + esbuild-windows-64@0.14.47: + resolution: {integrity: sha512-/Pk5jIEH34T68r8PweKRi77W49KwanZ8X6lr3vDAtOlH5EumPE4pBHqkCUdELanvsT14yMXLQ/C/8XPi1pAtkQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + esbuild-windows-arm64@0.14.47: + resolution: {integrity: sha512-HFSW2lnp62fl86/qPQlqw6asIwCnEsEoNIL1h2uVMgakddf+vUuMcCbtUY1i8sst7KkgHrVKCJQB33YhhOweCQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + esbuild@0.14.47: + resolution: {integrity: sha512-wI4ZiIfFxpkuxB8ju4MHrGwGLyp1+awEHAHVpx6w7a+1pmYIq8T9FGEVVwFo0iFierDoMj++Xq69GXWYn2EiwA==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.25.5: resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} @@ -1952,10 +2259,17 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -1994,6 +2308,9 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -2027,6 +2344,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2093,6 +2414,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -2193,6 +2518,10 @@ packages: http-parser-js@0.5.10: resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -2460,6 +2789,9 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.4.2: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true @@ -2492,6 +2824,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-to-ts@1.6.4: + resolution: {integrity: sha512-pR4yQ9DHz6itqswtHCm26mw45FSNfQ9rEQjosaZErhn5J3J2sIViQiz8rDaezjKAhFGpmsoczYVBgGHzFw/stA==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -2654,6 +2989,9 @@ packages: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -2665,6 +3003,9 @@ packages: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -2732,11 +3073,28 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -2760,9 +3118,27 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-fetch@2.6.9: + resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -2873,6 +3249,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-json@6.5.0: resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} engines: {node: '>=8'} @@ -2888,6 +3267,13 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@2.1.0: + resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} + engines: {node: '>=6'} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2907,10 +3293,23 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@6.1.0: + resolution: {integrity: sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw==} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2948,6 +3347,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-ms@7.0.1: + resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} + engines: {node: '>=10'} + prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} @@ -3213,6 +3616,10 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.0.2: + resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==} + engines: {node: '>=14'} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -3276,6 +3683,10 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -3341,6 +3752,10 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -3358,6 +3773,10 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + time-span@4.0.0: + resolution: {integrity: sha512-MyqZCTGLDZ77u4k+jqg4UlrzPTPZ49NDlaekU6uuFaJLzPIN1woaRXCbGeqOfxwc3Y37ZROGAJ614Rdv7Olt+g==} + engines: {node: '>=10'} + timed-out@4.0.1: resolution: {integrity: sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==} engines: {node: '>=0.10.0'} @@ -3377,6 +3796,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -3387,6 +3809,26 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-morph@12.0.0: + resolution: {integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==} + + ts-node@10.9.1: + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + ts-toolbelt@6.15.5: + resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -3444,6 +3886,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -3456,6 +3903,10 @@ packages: undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -3496,6 +3947,9 @@ packages: deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -3545,6 +3999,9 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + websocket-driver@0.7.4: resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} engines: {node: '>=0.8.0'} @@ -3553,6 +4010,9 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -3590,6 +4050,10 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} @@ -3626,6 +4090,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -3651,6 +4119,10 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3906,6 +4378,24 @@ snapshots: '@types/conventional-commits-parser': 5.0.1 chalk: 5.4.1 + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@edge-runtime/format@2.2.1': {} + + '@edge-runtime/node-utils@2.3.0': {} + + '@edge-runtime/ponyfill@2.4.2': {} + + '@edge-runtime/primitives@4.1.0': {} + + '@edge-runtime/vm@3.2.0': + dependencies: + '@edge-runtime/primitives': 4.1.0 + + '@emailjs/browser@4.4.1': {} + '@emnapi/core@1.4.3': dependencies: '@emnapi/wasi-threads': 1.0.2 @@ -4105,6 +4595,8 @@ snapshots: '@eslint/js@9.29.0': {} + '@fastify/busboy@2.1.1': {} + '@feature-sliced/eslint-config@0.1.1(eslint-plugin-boundaries@5.0.1(@typescript-eslint/parser@8.35.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.3)(eslint@8.57.1))(eslint-plugin-import@2.32.0)': dependencies: eslint-plugin-boundaries: 5.0.1(@typescript-eslint/parser@8.35.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@4.4.3)(eslint@8.57.1) @@ -4452,6 +4944,19 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -4469,6 +4974,24 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@mapbox/node-pre-gyp@2.0.0': + dependencies: + consola: 3.4.2 + detect-libc: 2.0.4 + https-proxy-agent: 7.0.6 + node-fetch: 2.6.9 + nopt: 8.1.0 + semver: 7.7.2 + tar: 7.4.3 + transitivePeerDependencies: + - encoding + - supports-color + '@mui/core-downloads-tracker@7.1.2': {} '@mui/icons-material@7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)': @@ -4575,6 +5098,9 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@pkgjs/parseargs@0.11.0': + optional: true + '@popperjs/core@2.11.8': {} '@protobufjs/aspromise@1.1.2': {} @@ -4602,6 +5128,14 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.19': {} + '@rollup/pluginutils@5.2.0(rollup@4.44.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.44.0 + '@rollup/rollup-android-arm-eabi@4.44.0': optional: true @@ -4677,6 +5211,21 @@ snapshots: '@tanstack/query-core': 5.81.2 react: 19.1.0 + '@ts-morph/common@0.11.1': + dependencies: + fast-glob: 3.3.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + path-browserify: 1.0.1 + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 @@ -4711,6 +5260,8 @@ snapshots: '@types/history@4.7.11': {} + '@types/json-schema@7.0.15': {} + '@types/json5@0.0.29': {} '@types/keyv@3.1.4': @@ -4719,6 +5270,8 @@ snapshots: '@types/minimist@1.2.5': {} + '@types/node@16.18.11': {} + '@types/node@24.0.3': dependencies: undici-types: 7.8.0 @@ -4909,6 +5462,65 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.9.1': optional: true + '@vercel/build-utils@10.6.1': {} + + '@vercel/error-utils@2.0.3': {} + + '@vercel/nft@0.29.2(rollup@4.44.0)': + dependencies: + '@mapbox/node-pre-gyp': 2.0.0 + '@rollup/pluginutils': 5.2.0(rollup@4.44.0) + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 10.4.5 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.2 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + + '@vercel/node@5.3.0(rollup@4.44.0)': + dependencies: + '@edge-runtime/node-utils': 2.3.0 + '@edge-runtime/primitives': 4.1.0 + '@edge-runtime/vm': 3.2.0 + '@types/node': 16.18.11 + '@vercel/build-utils': 10.6.1 + '@vercel/error-utils': 2.0.3 + '@vercel/nft': 0.29.2(rollup@4.44.0) + '@vercel/static-config': 3.1.1 + async-listen: 3.0.0 + cjs-module-lexer: 1.2.3 + edge-runtime: 2.5.9 + es-module-lexer: 1.4.1 + esbuild: 0.14.47 + etag: 1.8.1 + node-fetch: 2.6.9 + path-to-regexp: 6.1.0 + path-to-regexp-updated: path-to-regexp@6.3.0 + ts-morph: 12.0.0 + ts-node: 10.9.1(@types/node@16.18.11)(typescript@4.9.5) + typescript: 4.9.5 + undici: 5.28.4 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - encoding + - rollup + - supports-color + + '@vercel/static-config@3.1.1': + dependencies: + ajv: 8.6.3 + json-schema-to-ts: 1.6.4 + ts-morph: 12.0.0 + '@vitejs/plugin-react@4.6.0(vite@6.3.5(@types/node@24.0.3)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@babel/core': 7.27.4 @@ -4926,12 +5538,24 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 + abbrev@3.0.1: {} + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + acorn@8.15.0: {} + agent-base@7.1.3: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -4946,6 +5570,13 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ajv@8.6.3: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -4966,6 +5597,8 @@ snapshots: ansi-styles@6.2.1: {} + arg@4.1.3: {} + argparse@2.0.1: {} aria-query@5.3.2: {} @@ -5045,6 +5678,12 @@ snapshots: async-function@1.0.0: {} + async-listen@3.0.0: {} + + async-listen@3.0.1: {} + + async-sema@3.1.1: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -5071,6 +5710,10 @@ snapshots: base64-js@1.5.1: {} + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -5179,6 +5822,8 @@ snapshots: charenc@0.0.2: {} + chownr@3.0.0: {} + chrome-launcher@0.14.2: dependencies: '@types/node': 24.0.3 @@ -5190,6 +5835,8 @@ snapshots: ci-info@2.0.0: {} + cjs-module-lexer@1.2.3: {} + cli-boxes@2.2.1: {} cli-cursor@3.1.0: @@ -5227,6 +5874,8 @@ snapshots: clsx@2.1.1: {} + code-block-writer@10.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -5253,6 +5902,8 @@ snapshots: write-file-atomic: 3.0.3 xdg-basedir: 4.0.0 + consola@3.4.2: {} + conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 @@ -5268,6 +5919,8 @@ snapshots: meow: 12.1.1 split2: 4.2.0 + convert-hrtime@3.0.0: {} + convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} @@ -5300,6 +5953,8 @@ snapshots: optionalDependencies: typescript: 5.8.3 + create-require@1.1.1: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5389,6 +6044,10 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + detect-libc@2.0.4: {} + + diff@4.0.2: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -5414,6 +6073,20 @@ snapshots: duplexer3@0.1.5: {} + eastasianwidth@0.2.0: {} + + edge-runtime@2.5.9: + dependencies: + '@edge-runtime/format': 2.2.1 + '@edge-runtime/ponyfill': 2.4.2 + '@edge-runtime/vm': 3.2.0 + async-listen: 3.0.1 + mri: 1.2.0 + picocolors: 1.0.0 + pretty-ms: 7.0.1 + signal-exit: 4.0.2 + time-span: 4.0.0 + electron-to-chromium@1.5.172: {} emoji-regex@10.4.0: {} @@ -5519,6 +6192,8 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 + es-module-lexer@1.4.1: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -5540,6 +6215,89 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild-android-64@0.14.47: + optional: true + + esbuild-android-arm64@0.14.47: + optional: true + + esbuild-darwin-64@0.14.47: + optional: true + + esbuild-darwin-arm64@0.14.47: + optional: true + + esbuild-freebsd-64@0.14.47: + optional: true + + esbuild-freebsd-arm64@0.14.47: + optional: true + + esbuild-linux-32@0.14.47: + optional: true + + esbuild-linux-64@0.14.47: + optional: true + + esbuild-linux-arm64@0.14.47: + optional: true + + esbuild-linux-arm@0.14.47: + optional: true + + esbuild-linux-mips64le@0.14.47: + optional: true + + esbuild-linux-ppc64le@0.14.47: + optional: true + + esbuild-linux-riscv64@0.14.47: + optional: true + + esbuild-linux-s390x@0.14.47: + optional: true + + esbuild-netbsd-64@0.14.47: + optional: true + + esbuild-openbsd-64@0.14.47: + optional: true + + esbuild-sunos-64@0.14.47: + optional: true + + esbuild-windows-32@0.14.47: + optional: true + + esbuild-windows-64@0.14.47: + optional: true + + esbuild-windows-arm64@0.14.47: + optional: true + + esbuild@0.14.47: + optionalDependencies: + esbuild-android-64: 0.14.47 + esbuild-android-arm64: 0.14.47 + esbuild-darwin-64: 0.14.47 + esbuild-darwin-arm64: 0.14.47 + esbuild-freebsd-64: 0.14.47 + esbuild-freebsd-arm64: 0.14.47 + esbuild-linux-32: 0.14.47 + esbuild-linux-64: 0.14.47 + esbuild-linux-arm: 0.14.47 + esbuild-linux-arm64: 0.14.47 + esbuild-linux-mips64le: 0.14.47 + esbuild-linux-ppc64le: 0.14.47 + esbuild-linux-riscv64: 0.14.47 + esbuild-linux-s390x: 0.14.47 + esbuild-netbsd-64: 0.14.47 + esbuild-openbsd-64: 0.14.47 + esbuild-sunos-64: 0.14.47 + esbuild-windows-32: 0.14.47 + esbuild-windows-64: 0.14.47 + esbuild-windows-arm64: 0.14.47 + esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -5789,8 +6547,12 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + esutils@2.0.3: {} + etag@1.8.1: {} + eventemitter3@5.0.1: {} fast-deep-equal@3.1.3: {} @@ -5825,6 +6587,8 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-uri-to-path@1.0.0: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -5892,6 +6656,11 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -5966,6 +6735,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -6064,6 +6842,13 @@ snapshots: http-parser-js@0.5.10: {} + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + husky@9.1.7: {} idb@7.1.1: {} @@ -6296,6 +7081,12 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jiti@2.4.2: {} jpeg-js@0.4.4: {} @@ -6316,6 +7107,11 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-schema-to-ts@1.6.4: + dependencies: + '@types/json-schema': 7.0.15 + ts-toolbelt: 6.15.5 + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -6513,6 +7309,8 @@ snapshots: lowercase-keys@2.0.0: {} + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -6525,6 +7323,8 @@ snapshots: dependencies: semver: 6.3.1 + make-error@1.3.6: {} + map-obj@1.0.1: {} map-obj@4.3.0: {} @@ -6589,8 +7389,18 @@ snapshots: minimist@1.2.8: {} + minipass@7.1.2: {} + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + mkdirp@1.0.4: {} + mkdirp@3.0.1: {} + + mri@1.2.0: {} + ms@2.0.0: {} ms@2.1.3: {} @@ -6603,8 +7413,18 @@ snapshots: natural-compare@1.4.0: {} + node-fetch@2.6.9: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build@4.8.4: {} + node-releases@2.0.19: {} + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 @@ -6744,6 +7564,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + package-json@6.5.0: dependencies: got: 9.6.0 @@ -6764,6 +7586,10 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@2.1.0: {} + + path-browserify@1.0.1: {} + path-exists@4.0.0: {} path-exists@5.0.0: {} @@ -6774,8 +7600,19 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-to-regexp@6.1.0: {} + + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} + picocolors@1.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -6798,6 +7635,10 @@ snapshots: prettier@3.6.0: {} + pretty-ms@7.0.1: + dependencies: + parse-ms: 2.1.0 + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 @@ -7117,6 +7958,8 @@ snapshots: signal-exit@3.0.7: {} + signal-exit@4.0.2: {} + signal-exit@4.1.0: {} slice-ansi@5.0.0: @@ -7174,6 +8017,12 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + string-width@7.2.0: dependencies: emoji-regex: 10.4.0 @@ -7260,6 +8109,15 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + term-size@2.2.1: {} text-extensions@2.4.0: {} @@ -7270,6 +8128,10 @@ snapshots: through@2.3.8: {} + time-span@4.0.0: + dependencies: + convert-hrtime: 3.0.0 + timed-out@4.0.1: {} tinyexec@1.0.1: {} @@ -7285,12 +8147,39 @@ snapshots: dependencies: is-number: 7.0.0 + tr46@0.0.3: {} + trim-newlines@3.0.1: {} ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 + ts-morph@12.0.0: + dependencies: + '@ts-morph/common': 0.11.1 + code-block-writer: 10.1.1 + + ts-node@10.9.1(@types/node@16.18.11)(typescript@4.9.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 16.18.11 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 4.9.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + ts-toolbelt@6.15.5: {} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -7366,6 +8255,8 @@ snapshots: transitivePeerDependencies: - supports-color + typescript@4.9.5: {} + typescript@5.8.3: {} unbox-primitive@1.1.0: @@ -7377,6 +8268,10 @@ snapshots: undici-types@7.8.0: {} + undici@5.28.4: + dependencies: + '@fastify/busboy': 2.1.1 + unicorn-magic@0.1.0: {} unique-string@2.0.0: @@ -7458,6 +8353,8 @@ snapshots: uuid@3.3.2: {} + v8-compile-cache-lib@3.0.1: {} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -7484,6 +8381,8 @@ snapshots: web-vitals@4.2.4: {} + webidl-conversions@3.0.1: {} + websocket-driver@0.7.4: dependencies: http-parser-js: 0.5.10 @@ -7492,6 +8391,11 @@ snapshots: websocket-extensions@0.1.4: {} + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -7551,6 +8455,12 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 @@ -7576,6 +8486,8 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + yaml@1.10.2: {} yaml@2.8.0: {} @@ -7604,6 +8516,8 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yn@3.1.1: {} + yocto-queue@0.1.0: {} yocto-queue@1.2.1: {} diff --git a/src/entities/projects/ui/post-info/ProjectLeader.tsx b/src/entities/projects/ui/post-info/ProjectLeader.tsx index 0adff85..e242086 100644 --- a/src/entities/projects/ui/post-info/ProjectLeader.tsx +++ b/src/entities/projects/ui/post-info/ProjectLeader.tsx @@ -2,15 +2,22 @@ import MailOutlineIcon from "@mui/icons-material/MailOutline"; import ThumbUpOffAltIcon from "@mui/icons-material/ThumbUpOffAlt"; import { Box, styled, Typography } from "@mui/material"; import type { JSX } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; +import { useAuthStore } from "@shared/stores/authStore"; import type { User } from "@shared/types/user"; import TitleWithIcon from "@shared/ui/project-detail/TitleWithIcon"; const ProjectLeader = ({ projectOwner, + onEmailClick, }: { projectOwner: User | undefined; + onEmailClick?: () => void; }): JSX.Element | null => { + const navigate = useNavigate(); + const location = useLocation(); + const { user } = useAuthStore(); if (!projectOwner) return null; return ( @@ -40,8 +47,18 @@ const ProjectLeader = ({ {projectOwner.introduceMyself || "아직 등록한 소개가 없어요! 🚀"} - + { + if (!user) { + navigate( + `/login?redirect=${encodeURIComponent(location.pathname)}` + ); + } + onEmailClick?.(); + }} + > + {} 연락하기 diff --git a/src/entities/projects/ui/projects-card/ProjectCard.tsx b/src/entities/projects/ui/projects-card/ProjectCard.tsx index 733f02c..6ca6268 100644 --- a/src/entities/projects/ui/projects-card/ProjectCard.tsx +++ b/src/entities/projects/ui/projects-card/ProjectCard.tsx @@ -1,3 +1,4 @@ +import { Favorite as FavoriteIcon } from "@mui/icons-material"; import { Box, Button, @@ -14,7 +15,10 @@ import { memo } from "react"; import { useNavigate } from "react-router-dom"; import { useGetProjectApplicationUsers } from "@entities/projects/queries/useGetProjectApplications"; -import { useGetProjectLikedUsers } from "@entities/projects/queries/useGetProjectLike"; +import { + useGetProjectLikedUsers, + useGetMyLikedProjectsIds, +} from "@entities/projects/queries/useGetProjectLike"; import { RecruitmentStatus, type ProjectListRes } from "@shared/types/project"; import DragScrollContainer from "@shared/ui/DragScrollContainer"; @@ -39,10 +43,13 @@ const ProjectCard = ({ const isRecruiting = project.status === RecruitmentStatus.recruiting; const { data: likedUsers } = useGetProjectLikedUsers(project.id); const { data: appliedUsers } = useGetProjectApplicationUsers(project.id); + const { data: myLikedProjectIds } = useGetMyLikedProjectsIds(); const likedUserCnt = likedUsers?.length || 0; const appliedUsersCnt = appliedUsers?.length || 0; + const isLikedByCurrentUser = myLikedProjectIds?.includes(project.id) || false; + return ( - + {isLikedByCurrentUser ? ( + + ) : ( + + )} {likedUserCnt} diff --git a/src/entities/search/hooks/useProjectSearch.ts b/src/entities/search/hooks/useProjectSearch.ts index 37c6fe2..1f58eae 100644 --- a/src/entities/search/hooks/useProjectSearch.ts +++ b/src/entities/search/hooks/useProjectSearch.ts @@ -1,4 +1,4 @@ -import { useState, useCallback, type RefObject } from "react"; +import { useState, useCallback, useEffect, type RefObject } from "react"; import { useProjectsByPage, @@ -40,6 +40,8 @@ const useProjectSearch = ( } ); + const [shouldScrollAfterLoad, setShouldScrollAfterLoad] = useState(false); + const { data: totalCount = 0, isLoading: isCountLoading, @@ -61,6 +63,17 @@ const useProjectSearch = ( const isLoading = isProjectsLoading || isCountLoading; const isError = isProjectsError || isCountError; + useEffect(() => { + if (!isLoading && shouldScrollAfterLoad && resultsRef?.current) { + setTimeout(() => { + if (resultsRef?.current) { + scrollToElement(resultsRef.current, "smooth", 80); + } + }, 200); + setShouldScrollAfterLoad(false); + } + }, [isLoading, shouldScrollAfterLoad, resultsRef]); + const handleSearch = useCallback( (filter: ProjectSearchFilterOption): void => { setCurrentFilter(filter); @@ -70,15 +83,14 @@ const useProjectSearch = ( ); const handlePageChange = (page: number): void => { - const isSamePage = page === currentPage; - - if (isSamePage || isLoading) return; - - setPage(page); - if (resultsRef?.current) { scrollToElement(resultsRef.current, "smooth", 80); } + + if (page !== currentPage) { + setPage(page); + setShouldScrollAfterLoad(true); + } }; return { diff --git a/src/entities/search/ui/SearchForm.tsx b/src/entities/search/ui/SearchForm.tsx index 63372d2..e0b2e91 100644 --- a/src/entities/search/ui/SearchForm.tsx +++ b/src/entities/search/ui/SearchForm.tsx @@ -125,6 +125,7 @@ const HeaderSection = styled(Box)(({ theme }) => ({ flexDirection: "column", gap: theme.spacing(3), alignItems: "stretch", + padding: theme.spacing(2), }, })); @@ -143,6 +144,10 @@ const StatusArea = styled(Box, { const SearchSection = styled(Box)(({ theme }) => ({ padding: `${theme.spacing(2)} ${theme.spacing(4)}`, backgroundColor: theme.palette.background.paper, + + [theme.breakpoints.down("md")]: { + padding: theme.spacing(1, 2), + }, })); const SearchContainer = styled(Box)(() => ({ @@ -152,6 +157,9 @@ const SearchContainer = styled(Box)(() => ({ const FiltersSection = styled(Box)(({ theme }) => ({ padding: `0 ${theme.spacing(4)} ${theme.spacing(3)}`, backgroundColor: theme.palette.background.paper, + [theme.breakpoints.down("md")]: { + padding: theme.spacing(1, 2), + }, })); const ActionSection = styled(Box)(({ theme }) => ({ diff --git a/src/entities/search/ui/SearchInputHistory.tsx b/src/entities/search/ui/SearchInputHistory.tsx index b492e23..dda91fc 100644 --- a/src/entities/search/ui/SearchInputHistory.tsx +++ b/src/entities/search/ui/SearchInputHistory.tsx @@ -34,18 +34,18 @@ interface SearchInputHistoryProps { const HistoryDisabledMessage = (): JSX.Element => ( - + 검색 히스토리가 비활성화되어 있습니다 - + ); const HistoryEmptyMessage = (): JSX.Element => ( - + 검색 히스토리가 없습니다 - + ); @@ -79,7 +79,7 @@ const HistoryListContent = ({ - - + 검색 히스토리 - + ({ "&:hover": { color: theme.palette.primary.main, }, + [theme.breakpoints.down("sm")]: { + fontSize: "0.875rem", + }, })); const StyledDeleteButton = styled(IconButton)(({ theme }) => ({ @@ -341,4 +344,30 @@ const StyledDeleteButton = styled(IconButton)(({ theme }) => ({ }, })); +const DisabledMessageText = styled(Typography)(({ theme }) => ({ + [theme.breakpoints.down("sm")]: { + fontSize: "1.3rem", + }, +})); + +const EmptyMessageText = styled(Typography)(({ theme }) => ({ + [theme.breakpoints.down("sm")]: { + fontSize: "1.3rem", + }, +})); + +const StyledListItemText = styled(ListItemText)(({ theme }) => ({ + "& .MuiListItemText-primary": { + [theme.breakpoints.down("sm")]: { + fontSize: "1.4rem", + }, + }, +})); + +const HeaderTitle = styled(Typography)(({ theme }) => ({ + [theme.breakpoints.down("sm")]: { + fontSize: "1.3rem", + }, +})); + export default SearchInputHistory; diff --git a/src/features/email/api/emailApi.ts b/src/features/email/api/emailApi.ts new file mode 100644 index 0000000..7733797 --- /dev/null +++ b/src/features/email/api/emailApi.ts @@ -0,0 +1,66 @@ +import emailjs from "@emailjs/browser"; + +import type { + SendEmailRequest, + SendEmailResponse, +} from "@features/email/types/email"; + +const EMAIL_SERVICE_ID = import.meta.env.VITE_EMAIL_SERVICE_ID || ""; +const EMAIL_TEMPLATE_ID = import.meta.env.VITE_EMAIL_TEMPLATE_ID || ""; +const EMAIL_PUBLIC_KEY = import.meta.env.VITE_EMAIL_PUBLIC_KEY || ""; + +emailjs.init(EMAIL_PUBLIC_KEY); + +export const sendEmailApi = async ({ + actualSenderEmail, + receiverEmail, + projectId, + projectTitle, + emailData, +}: SendEmailRequest): Promise => { + try { + const templateParams = { + to_name: receiverEmail.split("@")[0], + to_email: receiverEmail, + + from_name: actualSenderEmail.split("@")[0], + from_email: actualSenderEmail, + + subject: emailData.subject, + message: emailData.message, + + project_title: projectTitle, + project_id: projectId, + + reply_to: actualSenderEmail, + }; + + const result = await emailjs.send( + EMAIL_SERVICE_ID, + EMAIL_TEMPLATE_ID, + templateParams + ); + + return { + success: true, + message: result.text, + }; + } catch (error) { + let errorMessage = "이메일 전송에 실패했습니다. 다시 시도해주세요."; + + if (error instanceof Error) { + if (error.message.includes("template")) { + errorMessage = "이메일 템플릿 설정에 문제가 있습니다."; + } else if (error.message.includes("service")) { + errorMessage = "이메일 서비스 설정에 문제가 있습니다."; + } else if (error.message.includes("user")) { + errorMessage = "이메일 서비스 인증에 실패했습니다."; + } + } + + return { + success: false, + message: errorMessage, + }; + } +}; diff --git a/src/features/email/hooks/useEmailForm.ts b/src/features/email/hooks/useEmailForm.ts new file mode 100644 index 0000000..da9ddd1 --- /dev/null +++ b/src/features/email/hooks/useEmailForm.ts @@ -0,0 +1,98 @@ +import { useState, useCallback, type ChangeEvent } from "react"; + +import useSendEmail from "@features/email/queries/useSendEmail"; +import type { + UseEmailFormProps, + UseEmailFormReturn, +} from "@features/email/types/email"; + +const useEmailForm = ({ + senderEmail, + receiverEmail, + project, + onClose, +}: UseEmailFormProps): UseEmailFormReturn => { + const [isOpen, setIsOpen] = useState(false); + const [subject, setSubject] = useState(""); + const [message, setMessage] = useState(""); + + const sendEmailMutation = useSendEmail(); + + const projectId = project?.id || ""; + const projectTitle = project?.title || ""; + + const openModal = useCallback((): void => { + setIsOpen(true); + }, []); + + const closeModal = useCallback((): void => { + setIsOpen(false); + onClose?.(); + }, [onClose]); + + const handleSubjectChange = useCallback( + (e: ChangeEvent): void => { + setSubject(e.target.value); + }, + [] + ); + + const handleMessageChange = useCallback( + (e: ChangeEvent): void => { + setMessage(e.target.value); + }, + [] + ); + + const resetForm = useCallback((): void => { + setSubject(""); + setMessage(""); + }, []); + + const handleSend = async (): Promise => { + const isEmpty = !subject.trim() || !message.trim(); + if (isEmpty) return; + + sendEmailMutation.mutate( + { + actualSenderEmail: senderEmail, + receiverEmail, + projectId, + projectTitle, + emailData: { + subject, + message, + }, + }, + { + onSuccess: (data) => { + if (data.success) { + resetForm(); + closeModal(); + } + }, + } + ); + }; + + const handleCancel = useCallback((): void => { + resetForm(); + closeModal(); + }, [resetForm, closeModal]); + + return { + isOpen, + isLoading: sendEmailMutation.isPending, + subject, + message, + openModal, + closeModal, + handleSubjectChange, + handleMessageChange, + handleSend, + handleCancel, + resetForm, + }; +}; + +export default useEmailForm; diff --git a/src/features/email/queries/useSendEmail.ts b/src/features/email/queries/useSendEmail.ts new file mode 100644 index 0000000..770f6b6 --- /dev/null +++ b/src/features/email/queries/useSendEmail.ts @@ -0,0 +1,33 @@ +import { useMutation, type UseMutationResult } from "@tanstack/react-query"; + +import { sendEmailApi } from "@features/email/api/emailApi"; +import type { + SendEmailRequest, + SendEmailResponse, +} from "@features/email/types/email"; + +import { useSnackbarStore } from "@shared/stores/snackbarStore"; + +const useSendEmail = (): UseMutationResult< + SendEmailResponse, + Error, + SendEmailRequest +> => { + const { showSuccess, showError } = useSnackbarStore(); + + return useMutation({ + mutationFn: sendEmailApi, + onSuccess: (data) => { + if (data.success) { + showSuccess("이메일이 성공적으로 전송되었습니다."); + } else { + showError(data.message); + } + }, + onError: (error) => { + showError(error.message || "이메일 전송 중 오류가 발생했습니다."); + }, + }); +}; + +export default useSendEmail; diff --git a/src/features/email/types/email.ts b/src/features/email/types/email.ts new file mode 100644 index 0000000..2455bc3 --- /dev/null +++ b/src/features/email/types/email.ts @@ -0,0 +1,42 @@ +import type { ChangeEvent } from "react"; + +import type { ProjectListRes } from "@shared/types/project"; + +export interface EmailData { + subject: string; + message: string; +} + +export interface UseEmailFormProps { + senderEmail: string; + receiverEmail: string; + project: ProjectListRes | null; + onClose?: () => void; +} + +export interface UseEmailFormReturn { + isOpen: boolean; + isLoading: boolean; + subject: string; + message: string; + openModal: () => void; + closeModal: () => void; + handleSubjectChange: (e: ChangeEvent) => void; + handleMessageChange: (e: ChangeEvent) => void; + handleSend: () => Promise; + handleCancel: () => void; + resetForm: () => void; +} + +export interface SendEmailRequest { + actualSenderEmail: string; + receiverEmail: string; + projectId: string; + projectTitle: string; + emailData: EmailData; +} + +export interface SendEmailResponse { + success: boolean; + message: string; +} diff --git a/src/features/email/ui/EmailField.tsx b/src/features/email/ui/EmailField.tsx new file mode 100644 index 0000000..fa04a61 --- /dev/null +++ b/src/features/email/ui/EmailField.tsx @@ -0,0 +1,25 @@ +import { TextField } from "@mui/material"; +import { memo, type JSX } from "react"; + +interface EmailFieldProps { + label: string; + value: string; +} + +const EmailFieldComponent = ({ + label, + value, +}: EmailFieldProps): JSX.Element => ( + +); + +const EmailField = memo(EmailFieldComponent); + +export default EmailField; diff --git a/src/features/email/ui/EmailModal.tsx b/src/features/email/ui/EmailModal.tsx new file mode 100644 index 0000000..6df88de --- /dev/null +++ b/src/features/email/ui/EmailModal.tsx @@ -0,0 +1,143 @@ +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, +} from "@mui/material"; +import { styled } from "@mui/material/styles"; +import { type JSX } from "react"; + +import useEmailForm from "@features/email/hooks/useEmailForm"; +import EmailField from "@features/email/ui/EmailField"; +import MessageField from "@features/email/ui/MessageField"; +import SubjectField from "@features/email/ui/SubjectField"; + +import type { ProjectListRes } from "@shared/types/project"; + +interface EmailModalProps { + open: boolean; + onClose: () => void; + senderEmail: string; + receiverEmail: string; + project: ProjectListRes | null; +} + +const EmailModal = ({ + open, + onClose, + senderEmail, + receiverEmail, + project, +}: EmailModalProps): JSX.Element => { + const { + isLoading, + subject, + message, + handleSubjectChange, + handleMessageChange, + handleSend, + handleCancel, + } = useEmailForm({ + senderEmail, + receiverEmail, + project, + onClose, + }); + + const isFormValid = subject.trim().length > 0 && message.trim().length > 0; + + return ( + + + 📧 이메일 보내기 + + + + + + + + + + + + + 취소 + + + {isLoading ? "전송 중..." : "전송"} + + + + + ); +}; + +export default EmailModal; + +const StyledDialog = styled(Dialog)({ + zIndex: 9999, + "& .MuiDialog-paper": { + width: "600px", + maxWidth: "90vw", + }, +}); + +const Title = styled(Typography)({ + textAlign: "center", + fontSize: "1.8rem", + fontWeight: 600, + marginBottom: "8px", +}); + +const FormContainer = styled(Box)({ + display: "flex", + flexDirection: "column", + gap: "20px", + paddingTop: "16px", +}); + +const ButtonContainer = styled(Box)({ + display: "flex", + gap: "12px", + justifyContent: "flex-end", + width: "100%", +}); + +const CancelButton = styled(Button)({ + padding: "12px 24px", + border: "1px solid #ccc", + backgroundColor: "#fff", + color: "#666", + fontSize: "1.4rem", + fontWeight: 500, + borderRadius: "8px", + transition: "all 0.2s ease", + "&:hover": { + backgroundColor: "#f5f5f5", + borderColor: "#999", + }, +}); + +const SendButton = styled(Button)({ + padding: "12px 24px", + border: "1px solid #1976d2", + backgroundColor: "#1976d2", + color: "#fff", + fontSize: "1.4rem", + fontWeight: 500, + borderRadius: "8px", + transition: "all 0.2s ease", + "&:hover": { + backgroundColor: "#1565c0", + borderColor: "#1565c0", + }, + "&:disabled": { + backgroundColor: "#e0e0e0", + borderColor: "#e0e0e0", + color: "#9e9e9e", + }, +}); diff --git a/src/features/email/ui/MessageField.tsx b/src/features/email/ui/MessageField.tsx new file mode 100644 index 0000000..f1107d6 --- /dev/null +++ b/src/features/email/ui/MessageField.tsx @@ -0,0 +1,31 @@ +import { TextField } from "@mui/material"; +import { memo, type JSX, type ChangeEvent } from "react"; + +interface MessageFieldProps { + value: string; + onChange: (e: ChangeEvent) => void; +} + +const MessageFieldComponent = ({ + value, + onChange, +}: MessageFieldProps): JSX.Element => ( + +); + +const MessageField = memo(MessageFieldComponent); + +export default MessageField; diff --git a/src/features/email/ui/PositionSelect.tsx b/src/features/email/ui/PositionSelect.tsx new file mode 100644 index 0000000..3b61ea7 --- /dev/null +++ b/src/features/email/ui/PositionSelect.tsx @@ -0,0 +1,76 @@ +import { FormControl, InputLabel, Select, MenuItem } from "@mui/material"; +import { styled } from "@mui/material/styles"; +import { memo, type JSX } from "react"; + +import { RecruitmentStatus, type Positions } from "@shared/types/project"; +import type { UserRole } from "@shared/types/user"; + +interface PositionSelectProps { + value: string; + onChange: (value: string) => void; + projectPositions: Positions[]; +} + +// UserRole에 따른 한글 라벨 매핑 +const POSITION_LABELS: Record = { + frontend: "프론트엔드 개발자", + backend: "백엔드 개발자", + fullstack: "풀스택 개발자", + designer: "UI/UX 디자이너", + pm: "프로젝트 매니저", +}; + +const PositionSelectComponent = ({ + value, + onChange, + projectPositions, +}: PositionSelectProps): JSX.Element => { + // 모집중인 포지션들만 필터링 (status가 undefined이거나 recruiting인 것들) + const availablePositions = (projectPositions || []).filter( + (pos) => !pos.status || pos.status === RecruitmentStatus.recruiting + ); + + return ( + + 💼 지원 포지션 + onChange(e.target.value as UserRole | "")} + label="💼 지원 포지션" + disabled={availablePositions.length === 0} + > + {availablePositions.length === 0 ? ( + + 현재 모집중인 포지션이 없습니다 + + ) : ( + availablePositions.map((position, index) => { + const uniqueValue = `${position.position}-${index}`; + return ( + + {POSITION_LABELS[position.position]} ({position.count}명 모집) + {position.experience && ` - ${position.experience}`} + + ); + }) + )} + + + ); +}; + +const PositionSelect = memo(PositionSelectComponent); + +export default PositionSelect; + +const StyledSelect = styled(Select)({ + fontSize: "1.4rem", + fontFamily: "inherit", + "&:hover .MuiOutlinedInput-notchedOutline": { + borderColor: "#222", + }, + "&.Mui-focused .MuiOutlinedInput-notchedOutline": { + borderColor: "#1976d2", + borderWidth: "2px", + }, +}); diff --git a/src/features/email/ui/SubjectField.tsx b/src/features/email/ui/SubjectField.tsx new file mode 100644 index 0000000..4f811a2 --- /dev/null +++ b/src/features/email/ui/SubjectField.tsx @@ -0,0 +1,27 @@ +import { TextField } from "@mui/material"; +import { memo, type JSX, type ChangeEvent } from "react"; + +interface SubjectFieldProps { + value: string; + onChange: (e: ChangeEvent) => void; +} + +const SubjectFieldComponent = ({ + value, + onChange, +}: SubjectFieldProps): JSX.Element => ( + +); + +const SubjectField = memo(SubjectFieldComponent); + +export default SubjectField; diff --git a/src/pages/project-detail/ui/ProjectDetailPage.tsx b/src/pages/project-detail/ui/ProjectDetailPage.tsx index a0a986b..4be0234 100644 --- a/src/pages/project-detail/ui/ProjectDetailPage.tsx +++ b/src/pages/project-detail/ui/ProjectDetailPage.tsx @@ -1,7 +1,9 @@ import { Box, Card, Container, styled } from "@mui/material"; -import { type JSX } from "react"; +import { useEffect, type JSX } from "react"; import { useNavigate, useParams } from "react-router-dom"; +import useEmailForm from "@features/email/hooks/useEmailForm"; +import EmailModal from "@features/email/ui/EmailModal"; import ProjectApplyForm from "@features/projects/ui/ProjectApplyForm"; import { ProjectDone, ProjectDones } from "@features/projects/ui/ProjectDelete"; import ProjectLike from "@features/projects/ui/ProjectLike"; @@ -32,6 +34,19 @@ const ProjectDetailPage = (): JSX.Element | null => { isError, } = useProjectsItem({ id: id || null }); + const { isOpen, openModal, closeModal } = useEmailForm({ + senderEmail: user?.email || "", + receiverEmail: project?.projectOwner.email || "", + project: project || null, + onClose: () => {}, + }); + + useEffect(() => { + if (project) { + console.log(project); + } + }, [project]); + const projectInfoValues = !project ? null : { @@ -90,6 +105,15 @@ const ProjectDetailPage = (): JSX.Element | null => { } return ( + {project && ( + + )} @@ -117,7 +141,10 @@ const ProjectDetailPage = (): JSX.Element | null => { - + diff --git a/src/shared/ui/pagination/Pagination.tsx b/src/shared/ui/pagination/Pagination.tsx index 9468faa..84f95d2 100644 --- a/src/shared/ui/pagination/Pagination.tsx +++ b/src/shared/ui/pagination/Pagination.tsx @@ -43,6 +43,20 @@ const Pagination = ({ return null; } + const handlePrevPage = (): void => { + const newPage = currentPage - 1; + if (newPage >= 1) { + onPageChange(newPage); + } + }; + + const handleNextPage = (): void => { + const newPage = currentPage + 1; + if (newPage <= totalPages) { + onPageChange(newPage); + } + }; + const handleFastPrev = (): void => { const currentBlock = Math.floor((currentPage - 1) / 5); const prevBlockLastPage = currentBlock * 5; @@ -74,7 +88,7 @@ const Pagination = ({ )} onPageChange(currentPage - 1)} + onClick={handlePrevPage} disabled={!canGoPrev || disabled} size={isMobile ? "large" : "medium"} title="이전 페이지" @@ -106,7 +120,7 @@ const Pagination = ({ onPageChange(currentPage + 1)} + onClick={handleNextPage} disabled={!canGoNext || disabled} size={isMobile ? "large" : "medium"} title="다음 페이지"