diff --git a/eslint.config.js b/eslint.config.js index 60a7bd4..9b82e65 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -25,6 +25,30 @@ export default tseslint.config( ], }, }, - stylistic.configs.recommended, + { + plugins: { + '@stylistic': stylistic, + }, + rules: { + ...stylistic.configs.recommended.rules, + '@stylistic/jsx-one-expression-per-line': ['error', { allow: 'non-jsx' }], + }, + }, + { + files: ['**/*.test.ts?(x)'], + languageOptions: { + globals: { + ...vitest.environments.env.globals, + }, + }, + plugins: { + vitest, + 'testing-library': testingLibrary, + }, + rules: { + ...vitest.configs.recommended.rules, + ...testingLibrary.configs['flat/react'].rules, + }, + }, { ignores: ['dist'] }, ) diff --git a/package.json b/package.json index 39d950c..5457cb1 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "preview": "vite preview" }, "dependencies": { + "@mui/material": "^6.4.8", + "devicon": "^2.16.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router": "^7.4.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c0aed2..63bcd1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@mui/material': + specifier: ^6.4.8 + version: 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + devicon: + specifier: ^2.16.0 + version: 2.16.0 react: specifier: ^19.0.0 version: 19.0.0 @@ -134,6 +140,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.27.0': + resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.26.9': resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} engines: {node: '>=6.9.0'} @@ -146,6 +156,60 @@ packages: resolution: {integrity: sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==} engines: {node: '>=6.9.0'} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.1': + resolution: {integrity: sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.0': + resolution: {integrity: sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.25.1': resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} engines: {node: '>=18'} @@ -372,6 +436,86 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@mui/core-downloads-tracker@6.4.8': + resolution: {integrity: sha512-vjP4+A1ybyCRhDZC7r5EPWu/gLseFZxaGyPdDl94vzVvk6Yj6gahdaqcjbhkaCrJjdZj90m3VioltWPAnWF/zw==} + + '@mui/material@6.4.8': + resolution: {integrity: sha512-5S9UTjKZZBd9GfbcYh/nYfD9cv6OXmj5Y7NgKYfk7JcSoshp8/pW5zP4wecRiroBSZX8wcrywSgogpVNO+5W0Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.4.8 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@6.4.8': + resolution: {integrity: sha512-sWwQoNSn6elsPTAtSqCf+w5aaGoh7AASURNmpy+QTTD/zwJ0Jgwt0ZaaP6mXq2IcgHxYnYloM/+vJgHPMkRKTQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@6.4.8': + resolution: {integrity: sha512-oyjx1b1FvUCI85ZMO4trrjNxGm90eLN3Ohy0AP/SqK5gWvRQg1677UjNf7t6iETOKAleHctJjuq0B3aXO2gtmw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@6.4.8': + resolution: {integrity: sha512-gV7iBHoqlsIenU2BP0wq14BefRoZcASZ/4LeyuQglayBl+DfLX5rEd3EYR3J409V2EZpR0NOM1LATAGlNk2cyA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.24': + resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@6.4.8': + resolution: {integrity: sha512-C86gfiZ5BfZ51KqzqoHi1WuuM2QdSKoFhbkZeAfQRB+jCc4YNhhj11UXFVMMsqBgZ+Zy8IHNJW3M9Wj/LOwRXQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -384,6 +528,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@rollup/rollup-android-arm-eabi@4.36.0': resolution: {integrity: sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==} cpu: [arm] @@ -509,11 +656,22 @@ packages: '@types/node@22.13.11': resolution: {integrity: sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.14': + resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + '@types/react-dom@19.0.4': resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} peerDependencies: '@types/react': ^19.0.0 + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + '@types/react@19.0.12': resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==} @@ -626,6 +784,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -667,6 +829,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -677,6 +843,9 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -684,6 +853,10 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -723,10 +896,16 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + devicon@2.16.0: + resolution: {integrity: sha512-PE5a2HBNeN4av+Iu975OiiWEwS8LJPw5HAvlv0JUHb62jZTdYxTpz4ga+cQyvdtb3x1side2P9Sr1mmOmUkO/g==} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -734,6 +913,9 @@ packages: electron-to-chromium@1.5.122: resolution: {integrity: sha512-EML1wnwkY5MFh/xUnCvY8FrhUuKzdYhowuZExZOfwJo+Zu9OsNCI23Cgl5y7awy7HrUHSwB1Z8pZX5TI34lsUg==} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-abstract@1.23.9: resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} @@ -862,6 +1044,9 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -966,6 +1151,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -986,6 +1174,9 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -1103,6 +1294,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1125,6 +1319,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1220,6 +1417,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1231,6 +1432,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1272,6 +1477,9 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@19.0.0: + resolution: {integrity: sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==} + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -1286,6 +1494,12 @@ packages: react-dom: optional: true + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@19.0.0: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} @@ -1294,6 +1508,9 @@ packages: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} @@ -1302,6 +1519,11 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + resolve@2.0.0-next.5: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true @@ -1385,6 +1607,10 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -1408,6 +1634,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1545,6 +1774,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1643,6 +1876,10 @@ snapshots: '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.26.5 + '@babel/runtime@7.27.0': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.26.9': dependencies: '@babel/code-frame': 7.26.2 @@ -1666,6 +1903,94 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.25.9 + '@babel/runtime': 7.27.0 + '@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.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@emotion/cache@11.14.0': + 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 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.1': + dependencies: + '@emotion/memoize': 0.9.0 + optional: true + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@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(react@19.0.0) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.12 + transitivePeerDependencies: + - supports-color + optional: true + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.3.1 + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.0.0) + '@emotion/utils': 1.4.2 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.12 + transitivePeerDependencies: + - supports-color + optional: true + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.0.0)': + dependencies: + react: 19.0.0 + optional: true + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.25.1': optional: true @@ -1815,6 +2140,83 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@mui/core-downloads-tracker@6.4.8': {} + + '@mui/material@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@mui/core-downloads-tracker': 6.4.8 + '@mui/system': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.0.12) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-is: 19.0.0 + react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@types/react': 19.0.12 + + '@mui/private-theming@6.4.8(@types/react@19.0.12)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) + prop-types: 15.8.1 + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.12 + + '@mui/styled-engine@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.0.0 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + + '@mui/system@6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@mui/private-theming': 6.4.8(@types/react@19.0.12)(react@19.0.0) + '@mui/styled-engine': 6.4.8(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0))(react@19.0.0) + '@mui/types': 7.2.24(@types/react@19.0.12) + '@mui/utils': 6.4.8(@types/react@19.0.12)(react@19.0.0) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 19.0.0 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.0.12)(react@19.0.0) + '@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.12)(react@19.0.0))(@types/react@19.0.12)(react@19.0.0) + '@types/react': 19.0.12 + + '@mui/types@7.2.24(@types/react@19.0.12)': + optionalDependencies: + '@types/react': 19.0.12 + + '@mui/utils@6.4.8(@types/react@19.0.12)(react@19.0.0)': + dependencies: + '@babel/runtime': 7.27.0 + '@mui/types': 7.2.24(@types/react@19.0.12) + '@types/prop-types': 15.7.14 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 19.0.0 + react-is: 19.0.0 + optionalDependencies: + '@types/react': 19.0.12 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1827,6 +2229,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@popperjs/core@2.11.8': {} + '@rollup/rollup-android-arm-eabi@4.36.0': optional: true @@ -1927,10 +2331,19 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/parse-json@4.0.2': + optional: true + + '@types/prop-types@15.7.14': {} + '@types/react-dom@19.0.4(@types/react@19.0.12)': dependencies: '@types/react': 19.0.12 + '@types/react-transition-group@4.4.12(@types/react@19.0.12)': + dependencies: + '@types/react': 19.0.12 + '@types/react@19.0.12': dependencies: csstype: 3.1.3 @@ -2103,6 +2516,13 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.27.0 + cosmiconfig: 7.1.0 + resolve: 1.22.10 + optional: true + balanced-match@1.0.2: {} brace-expansion@1.1.11: @@ -2151,6 +2571,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2159,10 +2581,22 @@ snapshots: concat-map@0.0.1: {} + convert-source-map@1.9.0: + optional: true + convert-source-map@2.0.0: {} cookie@1.0.2: {} + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + optional: true + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2207,10 +2641,17 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + devicon@2.16.0: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.27.0 + csstype: 3.1.3 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2219,6 +2660,11 @@ snapshots: electron-to-chromium@1.5.122: {} + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + optional: true + es-abstract@1.23.9: dependencies: array-buffer-byte-length: 1.0.2 @@ -2472,6 +2918,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-root@1.1.0: + optional: true + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2575,6 +3024,11 @@ snapshots: dependencies: function-bind: 1.1.2 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + optional: true + ignore@5.3.2: {} import-fresh@3.3.1: @@ -2596,6 +3050,9 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-arrayish@0.2.1: + optional: true + is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -2718,6 +3175,9 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: + optional: true + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -2740,6 +3200,9 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: + optional: true + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -2842,12 +3305,23 @@ snapshots: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.2 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + optional: true + path-exists@4.0.0: {} path-key@3.1.1: {} path-parse@1.0.7: {} + path-type@4.0.0: + optional: true + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -2881,6 +3355,8 @@ snapshots: react-is@16.13.1: {} + react-is@19.0.0: {} + react-refresh@0.14.2: {} react-router@7.4.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): @@ -2893,6 +3369,15 @@ snapshots: optionalDependencies: react-dom: 19.0.0(react@19.0.0) + react-transition-group@4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@babel/runtime': 7.27.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react@19.0.0: {} reflect.getprototypeof@1.0.10: @@ -2906,6 +3391,8 @@ snapshots: get-proto: 1.0.1 which-builtin-type: 1.2.1 + regenerator-runtime@0.14.1: {} + regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -2917,6 +3404,13 @@ snapshots: resolve-from@4.0.0: {} + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + optional: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.16.1 @@ -3039,6 +3533,9 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.5.7: + optional: true + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -3085,6 +3582,8 @@ snapshots: strip-json-comments@3.1.1: {} + stylis@4.2.0: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3227,4 +3726,7 @@ snapshots: yallist@3.1.1: {} + yaml@1.10.2: + optional: true + yocto-queue@0.1.0: {} diff --git a/src/App.tsx b/src/App.tsx index c1a5266..84f0787 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,16 @@ import { BrowserRouter, Routes, Route } from 'react-router' -import { Game } from '@/components/Game' -import Home from '@/components/Home' +import Home from '@/pages/Home' +import { Navbar } from '@/components/layout/Navbar' +import GitClicker from '@/pages/GitClicker' export default function App() { return ( - } /> - } /> + }> + } /> + } /> + ) diff --git a/src/components/Game.css b/src/components/Game.css deleted file mode 100644 index 034820a..0000000 --- a/src/components/Game.css +++ /dev/null @@ -1,12 +0,0 @@ -.game { - display: flex; - flex-direction: row; - justify-content: space-between; - margin: 2rem 20rem; -} - -.game .left { - display: flex; - flex-direction: column; - align-items: center; -} diff --git a/src/components/Game.tsx b/src/components/Game.tsx deleted file mode 100644 index 27cafb2..0000000 --- a/src/components/Game.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { useEffect, useState } from 'react' -import './Game.css' -import items from '@/items.json' -import type { Item, OwnedItems } from '@/types' -import { Gitcoin } from './Gitcoin' -import { Office } from './Office' -import { Score } from './Score' -import { Store } from './Store' -import { Link } from 'react-router' - -export function Game() { - const [lines, setLines] = useState(0) - const [linesPerMillisecond, setLinesPerMillisecond] = useState(0) - - const [ownedItems, setOwnedItems] = useState({}) - - useEffect(() => { - const interval = setInterval(() => { - setLines(lines + linesPerMillisecond) - }, 100) - - return () => clearInterval(interval) - }, [lines, linesPerMillisecond]) - - useEffect(() => { - let count = 0 - - for (const name of Object.keys(ownedItems)) { - const item = items.find(element => element.name === name) - - if (item != null) { - count += item.linesPerMillisecond * ownedItems[name] - } - } - - setLinesPerMillisecond(count) - }, [ownedItems]) - - const handleClick = () => { - setLines(lines + 1) - } - - const handleBuy = (item: Item) => { - setLines(lines - item.price) - setOwnedItems({ - ...ownedItems, - [item.name]: (ownedItems[item.name] || 0) + 1, - }) - } - - return ( - <> - - Back to home - - - - - - - - - - - - - - - - > - ) -} diff --git a/src/components/Home.tsx b/src/components/Home.tsx deleted file mode 100644 index 6a42697..0000000 --- a/src/components/Home.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Link } from 'react-router' - -export default function Home() { - return ( - - Welcome to Gitclicker! - - Start a new game ! - - - ) -} diff --git a/src/components/Office.tsx b/src/components/Office.tsx deleted file mode 100644 index 4b0028c..0000000 --- a/src/components/Office.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { OwnedItems } from '@/types' - -type Props = { - items: OwnedItems -} - -export function Office({ items }: Props) { - return ( - <> - Office - - {Object.keys(items).map(name => ( - - - {items[name]} - {' '} - {name} - - - ))} - - > - ) -} diff --git a/src/components/Store.css b/src/components/Store.css deleted file mode 100644 index b7b0b21..0000000 --- a/src/components/Store.css +++ /dev/null @@ -1,11 +0,0 @@ -.item { - display: flex; - width: 15rem; - flex-direction: row; - justify-content: space-between; -} - -.item > span, -.item > button { - margin-bottom: 0.5rem; -} diff --git a/src/components/Store.tsx b/src/components/Store.tsx deleted file mode 100644 index 8ecf699..0000000 --- a/src/components/Store.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import type { Item } from '@/types' -import './Store.css' -import items from '@/items.json' -// import PropTypes from "prop-types"; - -type Props = { - lines: number - onBuy: (item: Item) => void -} - -export function Store({ lines, onBuy }: Props) { - const canBuy = (item: Item) => { - return lines >= item.price - } - - return ( - - {items.map(item => ( - - - {item.name} - {' '} - - - {item.price} - - onBuy(item)} - disabled={!canBuy(item)} - type="button" - > - Buy - - - ))} - - ) -} - -// Store.propTypes = { -// lines: PropTypes.number.isRequired, -// onBuy: PropTypes.func.isRequired, -// }; diff --git a/src/components/game/Game.tsx b/src/components/game/Game.tsx new file mode 100644 index 0000000..bc1b08b --- /dev/null +++ b/src/components/game/Game.tsx @@ -0,0 +1,80 @@ +import { useEffect, useState } from 'react' +import '@/styles/game/index.css' +import type { Item, OwnedItems } from '@/types' +import { Grid2 as Grid, Card, CardContent, CardHeader } from '@mui/material' +import { items } from '@/constants/items' +import { Score, Gitcoin } from '@/components/game/core' +import { Skills } from '@/components/game/skills' +import { Store } from '@/components/game/store' + +export function Game() { + const [lines, setLines] = useState(0) + const [linesPerMillisecond, setLinesPerMillisecond] = useState(0) + + const [ownedItems, setOwnedItems] = useState({}) + + useEffect(() => { + const interval = setInterval(() => { + setLines(prev => prev + linesPerMillisecond) + }, 100) + return () => clearInterval(interval) + }, [linesPerMillisecond]) + + useEffect(() => { + let count = 0 + + Object.keys(ownedItems).forEach((name) => { + const item = items.find(element => element.name === name) + + if (item != null) { + count += item.linesPerMillisecond * ownedItems[name] + } + }) + + setLinesPerMillisecond(count) + }, [ownedItems]) + + const handleClick = () => { + setLines(lines + 1) + } + + const handleBuy = (item: Item) => { + setLines(lines - item.price) + setOwnedItems({ + ...ownedItems, + [item.name]: (ownedItems[item.name] || 0) + 1, + }) + } + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + > + ) +} diff --git a/src/components/Gitcoin.tsx b/src/components/game/core/Gitcoin.tsx similarity index 87% rename from src/components/Gitcoin.tsx rename to src/components/game/core/Gitcoin.tsx index 79b3d6d..397d212 100644 --- a/src/components/Gitcoin.tsx +++ b/src/components/game/core/Gitcoin.tsx @@ -1,4 +1,4 @@ -import './Gitcoin.css' +import '@/styles/game/core/gitcoin.css' import githubIcon from '@/assets/github.svg' type Props = { diff --git a/src/components/Score.tsx b/src/components/game/core/Score.tsx similarity index 72% rename from src/components/Score.tsx rename to src/components/game/core/Score.tsx index 7743f72..26d85ec 100644 --- a/src/components/Score.tsx +++ b/src/components/game/core/Score.tsx @@ -7,13 +7,10 @@ export function Score({ lines, linesPerSecond }: Props) { return ( <> - {lines} - {' '} - lines + {Math.ceil(lines)} lines - per second: - {linesPerSecond} + per second: {Math.ceil(linesPerSecond * 10)} > ) diff --git a/src/components/game/core/index.ts b/src/components/game/core/index.ts new file mode 100644 index 0000000..9075673 --- /dev/null +++ b/src/components/game/core/index.ts @@ -0,0 +1,2 @@ +export { Gitcoin } from './Gitcoin' +export { Score } from './Score' diff --git a/src/components/game/index.ts b/src/components/game/index.ts new file mode 100644 index 0000000..c942c67 --- /dev/null +++ b/src/components/game/index.ts @@ -0,0 +1 @@ +export { Game } from './Game' diff --git a/src/components/game/skills/Section.tsx b/src/components/game/skills/Section.tsx new file mode 100644 index 0000000..2913797 --- /dev/null +++ b/src/components/game/skills/Section.tsx @@ -0,0 +1,36 @@ +import { items } from '@/constants/items' +import { Box, Grid2 as Grid, Typography } from '@mui/material' + +type Props = { + itemName: string + number: number +} + +export const Section = ({ itemName, number }: Props) => { + const item = items.find(element => element.name === itemName) + + if (item == null) { + return null + } + + return ( + + {item.name} + + {Array.from({ length: number }).map((_, index) => ( + + + + ))} + + + ) +} diff --git a/src/components/game/skills/Skills.tsx b/src/components/game/skills/Skills.tsx new file mode 100644 index 0000000..353b6d1 --- /dev/null +++ b/src/components/game/skills/Skills.tsx @@ -0,0 +1,20 @@ +import { Section } from './Section' +import { OwnedItems } from '@/types' + +type Props = { + skills: OwnedItems +} + +export const Skills = ({ skills }: Props) => { + return ( + <> + {Object.keys(skills).map((name, key) => ( + + ))} + > + ) +} diff --git a/src/components/game/skills/index.ts b/src/components/game/skills/index.ts new file mode 100644 index 0000000..3e575bb --- /dev/null +++ b/src/components/game/skills/index.ts @@ -0,0 +1 @@ +export { Skills } from './Skills' diff --git a/src/components/game/store/Item.tsx b/src/components/game/store/Item.tsx new file mode 100644 index 0000000..5aa6a99 --- /dev/null +++ b/src/components/game/store/Item.tsx @@ -0,0 +1,41 @@ +import '@/styles/game/store/item.css' +import { Typography, Button } from '@mui/material' +import { Item as ItemType } from '@/types' + +type Props = { + item: ItemType + lines: number + onBuy: (item: ItemType) => void +} + +export function Item({ item, lines, onBuy }: Props) { + const canBuy = lines >= item.price + + const linePerSecond = Math.ceil(item.linesPerMillisecond * 10) + + return ( + canBuy && onBuy(item)} + > + + + + {item.name} + + {linePerSecond} + {' '} + lines per second + + + + + {item.price} + + + ) +} diff --git a/src/components/game/store/Store.tsx b/src/components/game/store/Store.tsx new file mode 100644 index 0000000..2da3bf5 --- /dev/null +++ b/src/components/game/store/Store.tsx @@ -0,0 +1,24 @@ +import { Item as ItemType } from '@/types' +import { Item } from './Item.tsx' +import { items } from '@/constants/items.ts' +import { Grid2 as Grid } from '@mui/material' + +type Props = { + lines: number + onBuy: (item: ItemType) => void +} + +export function Store({ lines, onBuy }: Props) { + return ( + + {items.map((item, key) => ( + + ))} + + ) +} diff --git a/src/components/game/store/index.ts b/src/components/game/store/index.ts new file mode 100644 index 0000000..56d4fae --- /dev/null +++ b/src/components/game/store/index.ts @@ -0,0 +1 @@ +export { Store } from './Store' diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx new file mode 100644 index 0000000..1cd3837 --- /dev/null +++ b/src/components/layout/Navbar.tsx @@ -0,0 +1,22 @@ +import { AppBar, Toolbar, Typography } from '@mui/material' +import { Link, Outlet } from 'react-router' +import githubIcon from '@/assets/github.svg' +import '@/styles/layout/navbar.css' + +export const Navbar = () => { + return ( + <> + + + + + + Gitclicker + + + + + + > + ) +} diff --git a/src/constants/items.ts b/src/constants/items.ts new file mode 100644 index 0000000..700615a --- /dev/null +++ b/src/constants/items.ts @@ -0,0 +1,39 @@ +import { Item } from '@/types' +import BashIcon from 'devicon/icons/bash/bash-original.svg' +import GitIcon from 'devicon/icons/git/git-original.svg' +import JavascriptIcon from 'devicon/icons/javascript/javascript-original.svg' +import ReactIcon from 'devicon/icons/react/react-original.svg' +import VimIcon from 'devicon/icons/vim/vim-original.svg' + +export const items: Item[] = [ + { + name: 'Bash', + price: 10, + linesPerMillisecond: 0.1, + icon: BashIcon, + }, + { + name: 'Git', + price: 100, + linesPerMillisecond: 1.2, + icon: GitIcon, + }, + { + name: 'Javascript', + price: 10000, + linesPerMillisecond: 14.0, + icon: JavascriptIcon, + }, + { + name: 'React', + price: 50000, + linesPerMillisecond: 75.0, + icon: ReactIcon, + }, + { + name: 'Vim', + price: 999999, + linesPerMillisecond: 10000.0, + icon: VimIcon, + }, +] diff --git a/src/items.json b/src/items.json deleted file mode 100644 index f53c149..0000000 --- a/src/items.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "name": "Bash", - "price": 10, - "linesPerMillisecond": 0.1 - }, - { - "name": "Git", - "price": 100, - "linesPerMillisecond": 1.2 - }, - { - "name": "Javascript", - "price": 10000, - "linesPerMillisecond": 14.0 - }, - { - "name": "React", - "price": 50000, - "linesPerMillisecond": 75.0 - }, - { - "name": "Vim", - "price": 999999, - "linesPerMillisecond": 10000.0 - } -] \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 33ab7be..ebd1922 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,16 @@ import React from 'react' import { createRoot } from 'react-dom/client' import App from './App' +import { CssBaseline } from '@mui/material' +import { ThemeProvider } from '@mui/material/styles' +import theme from '@/styles/theme' const root = createRoot(document.getElementById('root') as HTMLElement) root.render( - + + + + , ) diff --git a/src/pages/GitClicker.tsx b/src/pages/GitClicker.tsx new file mode 100644 index 0000000..557a0d5 --- /dev/null +++ b/src/pages/GitClicker.tsx @@ -0,0 +1,10 @@ +import Grid from '@mui/material/Grid2' +import { Game } from '@/components/game' + +export default function GitClicker() { + return ( + + + + ) +} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx new file mode 100644 index 0000000..6c8fc85 --- /dev/null +++ b/src/pages/Home.tsx @@ -0,0 +1,23 @@ +import { Container, Typography, Grid2 as Grid, Button } from '@mui/material' +import { Link } from 'react-router' + +export default function Home() { + return ( + + + + Gitclicker + + + Dogs have boundless enthusiasm but no sense of shame. I should have a dog as a life coach. + + + + + Play + + + + + ) +} diff --git a/src/components/Gitcoin.css b/src/styles/game/core/gitcoin.css similarity index 95% rename from src/components/Gitcoin.css rename to src/styles/game/core/gitcoin.css index 87aa5ff..111cd23 100644 --- a/src/components/Gitcoin.css +++ b/src/styles/game/core/gitcoin.css @@ -1,5 +1,4 @@ .gitcoin { - display: block; width: 15rem; height: 15rem; border: none; @@ -8,6 +7,7 @@ cursor: pointer; & > img { + margin-top: 1rem; width: 100%; border-radius: 100%; animation: bounce-up 0.2s; diff --git a/src/styles/game/index.css b/src/styles/game/index.css new file mode 100644 index 0000000..4562a92 --- /dev/null +++ b/src/styles/game/index.css @@ -0,0 +1,13 @@ +.card { + .content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + ul { + list-style: none; + padding: 0; + } +} diff --git a/src/styles/game/store/item.css b/src/styles/game/store/item.css new file mode 100644 index 0000000..e534216 --- /dev/null +++ b/src/styles/game/store/item.css @@ -0,0 +1,25 @@ +.item { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 0.5rem; + border-radius: 0.3rem; + user-select: none; +} + +.item:hover { + background-color: #f8f9fa; + cursor: pointer; +} + +.item > .title { + display: flex; + align-items: center; + justify-content: flex-start; +} + +.item > .title > img { + width: 2rem; + margin-right: 0.8rem; +} diff --git a/src/styles/layout/navbar.css b/src/styles/layout/navbar.css new file mode 100644 index 0000000..9df76f5 --- /dev/null +++ b/src/styles/layout/navbar.css @@ -0,0 +1,12 @@ +.navbar-link { + display: flex; + align-items: center; + gap: 1rem; + color: white; + text-decoration: none; + + img { + width: 2rem; + height: 2rem; + } +} diff --git a/src/styles/theme.ts b/src/styles/theme.ts new file mode 100644 index 0000000..51a9162 --- /dev/null +++ b/src/styles/theme.ts @@ -0,0 +1,15 @@ +import { createTheme } from '@mui/material/styles' + +const theme = createTheme({ + cssVariables: true, + palette: { + primary: { + main: '#556cd6', + }, + background: { + default: '#FAFAFA', + }, + }, +}) + +export default theme diff --git a/src/types.ts b/src/types.ts index a42500c..422bef8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ export type Item = { name: string price: number linesPerMillisecond: number + icon: string } export type OwnedItems = {