diff --git a/package-lock.json b/package-lock.json index 39a5bc7..18ccbeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,10 @@ "clsx": "^2.1.1", "date-fns": "^4.1.0", "firebase": "^10.14.1", + "framer-motion": "^11.15.0", "lucide-react": "^0.436.0", "next": "14.2.6", + "ngrok": "^5.0.0-beta.2", "puppeteer": "^23.11.1", "react": "^18", "react-dom": "^18", @@ -1072,6 +1074,18 @@ "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", "dev": true }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -1086,6 +1100,18 @@ "tslib": "^2.4.0" } }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@tanstack/query-core": { "version": "5.59.17", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.17.tgz", @@ -1117,12 +1143,39 @@ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "license": "MIT" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.16.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", @@ -1156,6 +1209,15 @@ "@types/react": "*" } }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -1762,6 +1824,33 @@ "node": ">=10.16.0" } }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1949,6 +2038,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2132,6 +2233,33 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -2170,6 +2298,15 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -3166,25 +3303,39 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/framer-motion": { + "version": "11.15.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.15.0.tgz", + "integrity": "sha512-MLk8IvZntxOMg7lDBLw2qgTHHv664bYoYmnFTmE0Gm/FW67aOJk0WM3ctMcG+Xhcv+vh5uyyXwxvxhSeJzSe+w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.14.3", + "motion-utils": "^11.14.3", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3429,6 +3580,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3520,6 +3696,19 @@ "node": ">= 0.4" } }, + "node_modules/hpagent": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/hpagent/-/hpagent-0.1.2.tgz", + "integrity": "sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==", + "license": "MIT", + "optional": true + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause" + }, "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -3537,6 +3726,19 @@ "node": ">= 14" } }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -4116,8 +4318,7 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -4167,7 +4368,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -4236,6 +4436,12 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4258,6 +4464,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -4291,6 +4506,15 @@ "node": ">=8.6" } }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4325,6 +4549,18 @@ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, + "node_modules/motion-dom": { + "version": "11.14.3", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.14.3.tgz", + "integrity": "sha512-lW+D2wBy5vxLJi6aCP0xyxTxlTfiu+b+zcpVbGVFUxotwThqhdpPRSmX8xztAgtZMPMeU0WGVn/k1w4I+TbPqA==", + "license": "MIT" + }, + "node_modules/motion-utils": { + "version": "11.14.3", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.14.3.tgz", + "integrity": "sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4447,6 +4683,37 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/ngrok": { + "version": "5.0.0-beta.2", + "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-5.0.0-beta.2.tgz", + "integrity": "sha512-UzsyGiJ4yTTQLCQD11k1DQaMwq2/SsztBg2b34zAqcyjS25qjDpogMKPaCKHwe/APRTHeel3iDXcVctk5CNaCQ==", + "hasInstallScript": true, + "dependencies": { + "extract-zip": "^2.0.1", + "got": "^11.8.5", + "lodash.clonedeep": "^4.5.0", + "uuid": "^7.0.0 || ^8.0.0", + "yaml": "^2.2.2" + }, + "bin": { + "ngrok": "bin/ngrok" + }, + "engines": { + "node": ">=14.2" + }, + "optionalDependencies": { + "hpagent": "^0.1.2" + } + }, + "node_modules/ngrok/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4455,6 +4722,18 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4614,6 +4893,15 @@ "node": ">= 0.8.0" } }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5205,6 +5493,18 @@ "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5351,6 +5651,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5368,6 +5674,18 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", diff --git a/package.json b/package.json index 46e29c6..d9cd87a 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,10 @@ "clsx": "^2.1.1", "date-fns": "^4.1.0", "firebase": "^10.14.1", + "framer-motion": "^11.15.0", "lucide-react": "^0.436.0", "next": "14.2.6", + "ngrok": "^5.0.0-beta.2", "puppeteer": "^23.11.1", "react": "^18", "react-dom": "^18", diff --git a/public/landing/bg.svg b/public/landing/bg.svg new file mode 100644 index 0000000..5d9dbb5 --- /dev/null +++ b/public/landing/bg.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/landing/logos/meta.png b/public/landing/logos/meta.png new file mode 100644 index 0000000..72ffa9f Binary files /dev/null and b/public/landing/logos/meta.png differ diff --git a/public/landing/logos/microsoft.png b/public/landing/logos/microsoft.png new file mode 100644 index 0000000..aa2d5d2 Binary files /dev/null and b/public/landing/logos/microsoft.png differ diff --git a/public/landing/logos/nvidia.png b/public/landing/logos/nvidia.png new file mode 100644 index 0000000..53b8abc Binary files /dev/null and b/public/landing/logos/nvidia.png differ diff --git a/public/landing/logos/roblox.png b/public/landing/logos/roblox.png new file mode 100644 index 0000000..d30e6a5 Binary files /dev/null and b/public/landing/logos/roblox.png differ diff --git a/public/landing/logos/samsung.png b/public/landing/logos/samsung.png new file mode 100644 index 0000000..806accf Binary files /dev/null and b/public/landing/logos/samsung.png differ diff --git a/public/landing/logos/ukg.png b/public/landing/logos/ukg.png new file mode 100644 index 0000000..b3bf920 Binary files /dev/null and b/public/landing/logos/ukg.png differ diff --git a/src/app/events/page.tsx b/src/app/events/page.tsx index f4c5f0d..9b39b1f 100644 --- a/src/app/events/page.tsx +++ b/src/app/events/page.tsx @@ -1,13 +1,13 @@ "use client"; -import Calendar from "@/components/client-events/Calendar"; -import MonthlyEvents from "@/components/client-events/MonthlyEvents"; +import Calendar from "@/components/events/Calendar"; +import MonthlyEvents from "@/components/events/MonthlyEvents"; export default function Events() { return ( <> -
-
-
+
+
+
diff --git a/src/app/globals.css b/src/app/globals.css index 2dbcc1c..58c280f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,4 +1,4 @@ -@import url("https://fonts.googleapis.com/css2?family=Krub:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;1,200;1,300;1,400;1,500;1,600;1,700&family=Michroma&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Michroma&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap"); @tailwind base; @tailwind components; @tailwind utilities; @@ -15,12 +15,40 @@ textarea { } body { - font-family: "Krub", sans-serif; + font-family: "Inter", sans-serif; + background-color: #ffffff; + overflow-x: hidden; } -.bg { - background: linear-gradient(60deg, #000000, #3c4175); - inset: 0; +#hero { + background-image: url("/landing/bg.svg"); + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.noise-bg { position: fixed; - z-index: -1; + inset: 0; + z-index: 1; + pointer-events: none; + opacity: 0.02; + background: + repeating-radial-gradient(#fff 0 0.0001%, #000 0 0.0002%) 50% 0/2500px + 2500px, + repeating-conic-gradient(#fff 0 0.0001%, #000 0 0.0002%) 50% 50%/2500px + 2500px; + background-blend-mode: difference; +} + +.monthly-event { + background: radial-gradient( + 138.71% 110.11% at 93.15% -36.58%, + #4255f922 25%, + #0f1319 100% + ), + radial-gradient(61.66% 37.6% at 6.63% 95.08%, #4255f922 25%, #0f1319 100%), + rgba(26, 26, 26, 0.43); + box-shadow: 0px 0.5px 16px 0px rgba(200, 200, 255, 0.1); + border: 1px solid #4255f911; } diff --git a/src/app/page.tsx b/src/app/page.tsx index 05a3804..7de3548 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,620 +1,14 @@ -// page.tsx "use client"; - -import React from "react"; -import Image from "next/image"; -import { ChevronDown } from "lucide-react"; -import GlowingLine from "@/components/decorations/GlowingLine"; -import Link from "next/link"; -import { useState, useEffect } from "react"; -import SocialStats from "@/components/MemberCounts/SocialStats"; +import Hero from "@/components/landing/Hero"; +import LandingAbout from "@/components/landing/LandingAbout"; const Website = () => { - const [isDesktop, setIsDesktop] = useState(false); - - useEffect(() => { - const checkIfDesktop = () => { - setIsDesktop(window.innerWidth >= 768); - }; - - // Initial check - checkIfDesktop(); - - // Add event listener - window.addEventListener("resize", checkIfDesktop); - - // Cleanup - return () => window.removeEventListener("resize", checkIfDesktop); - }, []); - return ( -
- {/* Add Michroma font */} - - {/* Hero Section with Background Image */} -
- {/* Hero Content */} -
- {/* Content Container with Grid */} -
- {/* Logo Section */} -
- {/* Outer Glow */} -
- {/* Inner Glow */} -
- {/* Logo */} -
- SPCB Logo -
-
- - {/* Text Section */} -
-

- The Society of PC Building -

- - {/* Social Stats */} -
- -
- - {/* Join Button */} - -
-
- - {/* Scroll Indicator */} -
-
- -
-
-
-
- - {/* About Section */} -
- - {/* Base black background */} - - - {/* Gradient overlay for radial left */} - - - - - - - - {/* White polygon - using isDesktop state */} - {isDesktop && ( - - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- {/* Decorative Lines */} - - - - - {/* Content Box */} -
-
-
-

- About -

-

- The Society of PC Building (SPCB) at the University of Florida - is a community for students passionate about PC hardware, - building, and tech. Whether you're a beginner or an expert, - SPCB offers workshops, live demos, and events to help you - learn, collaborate, and build custom rigs. Beyond PCs, we - foster innovation, teamwork, and technical growth, connecting - students through a shared love for technology. -

- - Learn More - - - - -
-
-
-
- {/* Activities Section */} -
- {/* Main vertical line */} - - - - {/* Top right Blue Group */} - - - - {/* Middle Left Blue Group */} - - - - - {/* Content Cards */} -
- {/* Socials Card - Left */} -
-
-
- Socials -
-
-

- Socials -

-
-
-
- - {/* GBMs Card - Right */} -
-
-
- GBMs -
-
-

- GBMs -

-
-
-
- - {/* PC Builds Card - Left */} -
-
-
- PC Builds -
-
-

- PC Builds -

-
-
-
-
-
- {/* Social Links Section */} -
-
- - - -
- - {/* Content */} -
-
- {/* Left side */} -
-
- SPCB Logo -
-

- Proudly building PCs. -

- {/* Social Media Icons */} -
- - LinkedIn - - - Instagram - - - Discord - - - Linktree - -
-
- - {/* Right side - GUD section */} -
-
-
-

- Gator User Design -

- GUD Logo -
- - Check us out - - - - -
-
-
-
- {/* Footer */} -
-
-

- © {new Date().getFullYear()} The Society of PC Building. - All rights reserved. -

-
-
-
-
+
+ {/* Applied a noise bg to the page for added depth */} +
+ +
); }; diff --git a/src/components/Navbar/PublicNavbar.tsx b/src/components/Navbar/PublicNavbar.tsx index 462a3cb..3a3fa5f 100644 --- a/src/components/Navbar/PublicNavbar.tsx +++ b/src/components/Navbar/PublicNavbar.tsx @@ -1,122 +1,102 @@ -"use client"; - import Link from "next/link"; -import Image from "next/image"; import { useState } from "react"; +import { FaBars } from "react-icons/fa"; +import { FaXmark } from "react-icons/fa6"; +import { AnimatePresence, motion } from "framer-motion"; + -export default function Navbar() { - const [isOpen, setIsOpen] = useState(false); - const toggleMenu = () => setIsOpen(!isOpen); +// TODO: Fix the navbar apperance when viewing admin dashboard +export default function Nav() { + const [showMobileNav, setShowMobileNav] = useState(false); return ( - <> - {/* Add Michroma font */} - + ); } diff --git a/src/components/admin/auth/ProtectedRoute.tsx b/src/components/admin/auth/ProtectedRoute.tsx index 4615351..1045cb6 100644 --- a/src/components/admin/auth/ProtectedRoute.tsx +++ b/src/components/admin/auth/ProtectedRoute.tsx @@ -37,5 +37,5 @@ export default function ProtectedRoute({ return null; } - return
{children}
; + return
{children}
; } diff --git a/src/components/client-events/CalendarDays.tsx b/src/components/client-events/CalendarDays.tsx deleted file mode 100644 index 3d7bf4d..0000000 --- a/src/components/client-events/CalendarDays.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { FirebaseEvent } from "@/types/events"; -import { format, isSameDay } from "date-fns"; - -type CalendarDaysProps = { - daysToDisplay: Date[]; - currentDate: Date; - events: FirebaseEvent[]; -}; - -const CalendarDays: React.FC = ({ - daysToDisplay, - currentDate, - events, -}) => { - return ( -
- {daysToDisplay.map((day, i) => { - const isCurrentMonth = format(day, "MM") === format(currentDate, "MM"); - - const dayEvents = events.filter((event) => { - const eventDate = event.time.toDate(); - return isCurrentMonth && isSameDay(eventDate, day); - }); - - if (isCurrentMonth) { - return ( -
-
{format(day, "dd")}
-
- {dayEvents.map((_event,i) => ( -
{_event.title}
- ))} -
-
- ); - } else { - return ( -
- ); - } - })} -
- ); -}; - -export default CalendarDays; diff --git a/src/components/client-events/MonthlyEvent.tsx b/src/components/client-events/MonthlyEvent.tsx deleted file mode 100644 index c82abec..0000000 --- a/src/components/client-events/MonthlyEvent.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { FirebaseEvent } from "@/types/events"; - -type MonthlyEventProps = { - event: FirebaseEvent; -}; - -const MonthlyEvent: React.FC = ({ event }) => { - function parseDateTime(dateTime: Date): { - timeString: string; - dayOfMonth: string; - dayOfWeek: string; - } { - const timeString = dateTime.toLocaleString("en-US", { - hour: "numeric", - minute: "2-digit", - hour12: true, - }); - - const dayOfMonth = dateTime.getDate().toString(); - - const dayOfWeek = dateTime - .toLocaleString("en-US", { - weekday: "short", - }) - .toUpperCase(); - - return { timeString, dayOfMonth, dayOfWeek }; - } - - const { - timeString: time, - dayOfMonth, - dayOfWeek, - } = parseDateTime(event.time.toDate()); - - return ( -
-
-

{dayOfWeek}

-

{dayOfMonth}

-
-
-

{event.title}

-
-

- Location:{" "} - {event.location} -

-

- Time: {time} -

-
-

{event.description}

-
- {event.tags.map((tag, i) => ( -
- - {tag} -
- ))} -
-
-
- ); -}; - -export default MonthlyEvent; diff --git a/src/components/client-events/MonthlyEvents.tsx b/src/components/client-events/MonthlyEvents.tsx deleted file mode 100644 index 71581dc..0000000 --- a/src/components/client-events/MonthlyEvents.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useCachedEvents } from "@/hooks/useCachedEvents"; -import React, { useState } from "react"; -import MonthlyEvent from "./MonthlyEvent"; - -export default function MonthlyEvents() { - const [currentDate, setCurrentDate] = useState(new Date()); - const { - data: events, - isLoading, - isError, - error, - } = useCachedEvents(currentDate); - - if (isError) { - return ( -
- Error occured while fetching data. Please contact us on discord. -
- ); - } - if (isLoading) { - return ( -
-

December Events

-
- ); - } - - return ( -
-

December Events

-
- {events?.map((_event) => )} -
-
- ); -} - diff --git a/src/components/client-events/Calendar.tsx b/src/components/events/Calendar.tsx similarity index 61% rename from src/components/client-events/Calendar.tsx rename to src/components/events/Calendar.tsx index 8904517..ec376bc 100644 --- a/src/components/client-events/Calendar.tsx +++ b/src/components/events/Calendar.tsx @@ -12,6 +12,7 @@ import { import { FaChevronLeft, FaChevronRight } from "react-icons/fa"; import { useCachedEvents } from "@/hooks/useCachedEvents"; import CalendarDays from "./CalendarDays"; +import CalendarSkeleton from "./CalendarSkeleton"; export default function Calendar() { const [currentDate, setCurrentDate] = useState(new Date()); @@ -38,20 +39,20 @@ export default function Calendar() { }; return ( -
-
-

+

+
+

{currentMonthName} {currentYear}

- {/* Days of the Week */} -
+ {/* Desktop Days of the Week */} +
{["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => (
{day}
))}
- {/* Calendar Days */} + {/* Mobile Days of the Week */} +
+ {["Su", "M", "T", "W", "Th", "F", "Sa"].map((day) => ( +
+ {day} +
+ ))} +
+ + {/* While fetching show the calendar skeleton */} {events && } + {isLoading && } +
); } diff --git a/src/components/events/CalendarDay.tsx b/src/components/events/CalendarDay.tsx new file mode 100644 index 0000000..64b9d97 --- /dev/null +++ b/src/components/events/CalendarDay.tsx @@ -0,0 +1,67 @@ +import { FirebaseEvent } from "@/types/events"; +import { format } from "date-fns"; +import CalendarEvent from "./CalendarEvent"; +import MobileCalendarEvent from "./MobileCalendarEvent"; + +type CalendarDayProps = { + dayEvents?: FirebaseEvent[]; + daysInMonth: number; + isCurrentMonth: boolean; + index: number; + day: Date; +}; + +const CalendarDay: React.FC = ({ + dayEvents, + daysInMonth, + isCurrentMonth, + index, + day, +}) => { + if (!isCurrentMonth) { + return ( + <> + {/* Desktop Calendar Day not in current month */} +
+

{format(day, "dd")}

+
+ + {/* Mobile Calendar Day not in current month */} +
+
{format(day, "dd")}
+
+ + ); + } + return ( + <> + {/* Desktop Calendar day in current month */} +
+
{format(day, "dd")}
+ {dayEvents && ( +
+ {dayEvents.map((_event, i) => ( + + ))} +
+ )} +
+ + {/* Mobile Calendar day in current month */} + +
+ {dayEvents && dayEvents.length > 0 ? ( + + ) : ( +
{format(day, "dd")}
+ )} +
+ + ); +}; + +export default CalendarDay; diff --git a/src/components/events/CalendarDays.tsx b/src/components/events/CalendarDays.tsx new file mode 100644 index 0000000..4a3d6e3 --- /dev/null +++ b/src/components/events/CalendarDays.tsx @@ -0,0 +1,34 @@ +import { FirebaseEvent } from "@/types/events"; +import { format, isSameDay } from "date-fns"; +import CalendarDay from "./CalendarDay"; + +type CalendarDaysProps = { + daysToDisplay: Date[]; + currentDate: Date; + events: FirebaseEvent[]; +}; + +const CalendarDays: React.FC = ({ + daysToDisplay, + currentDate, + events, +}) => { + return ( +
+ {daysToDisplay.map((day, i) => { + const isCurrentMonth = format(day, "MM") === format(currentDate, "MM"); + + const dayEvents = events.filter((event) => { + const eventDate = event.time.toDate(); + return isCurrentMonth && isSameDay(eventDate, day); + }); + + return + })} +
+ ); +}; + +export default CalendarDays; + + diff --git a/src/components/events/CalendarEvent.tsx b/src/components/events/CalendarEvent.tsx new file mode 100644 index 0000000..19d927a --- /dev/null +++ b/src/components/events/CalendarEvent.tsx @@ -0,0 +1,85 @@ +import { FirebaseEvent } from "@/types/events"; +import { useEffect, useState } from "react"; +import { FaXmark } from "react-icons/fa6"; + +type CalendarEventProps = { + _event: FirebaseEvent; +}; + +const CalendarEvent: React.FC = ({ _event }) => { + const [showEventModal, setShowEventModal] = useState(false); + + const handleEventClick = () => { + setShowEventModal((prev) => !prev); + document.body.style.overflowY = + document.body.style.overflowY === "hidden" ? "auto" : "hidden"; + }; + + const time = _event.time.toDate().toLocaleString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + + function formatDateToMMDDYY(date: Date): string { + const padZero = (num: number): string => num.toString().padStart(2, "0"); + + const month = padZero(date.getMonth() + 1); // Months are 0-based + const day = padZero(date.getDate()); + const year = date.getFullYear().toString().slice(-2); // Get last 2 digits of the year + + return `${month}/${day}/${year}`; + } + + const date = formatDateToMMDDYY(_event.time.toDate()); + + useEffect(() => { + return () => { + if (document.body.style.overflowY === "hidden") { + document.body.style.overflowY = "auto"; + } + }; + }, []); + return ( + <> +
+ {_event.title} +
+ + {showEventModal && ( +
+
+ +

{_event.title}

+
+

Location: {_event.location}

+

Time: {time}

Date: {date}

+
+

{_event.description}

+
+ {_event.tags.map((tag, i) => ( +
+ + {tag} +
+ ))} +
+
+
+ )} + + ); +}; + +export default CalendarEvent; diff --git a/src/components/events/CalendarSkeleton.tsx b/src/components/events/CalendarSkeleton.tsx new file mode 100644 index 0000000..3a28e09 --- /dev/null +++ b/src/components/events/CalendarSkeleton.tsx @@ -0,0 +1,22 @@ +import { format } from "date-fns"; +import CalendarDay from "./CalendarDay"; + +type CalendarSkeletonProps = { + daysToDisplay: Date[]; + currentDate: Date; +}; + +const CalendarSkeleton: React.FC = ({ + daysToDisplay, + currentDate, +}) => { + return ( +
+ {daysToDisplay.map((day, i) => { + const isCurrentMonth = format(day, "MM") === format(currentDate, "MM"); + return + })} +
+ ); +}; +export default CalendarSkeleton; diff --git a/src/components/events/MobileCalendarEvent.tsx b/src/components/events/MobileCalendarEvent.tsx new file mode 100644 index 0000000..9fc6076 --- /dev/null +++ b/src/components/events/MobileCalendarEvent.tsx @@ -0,0 +1,61 @@ +import { FirebaseEvent } from "@/types/events"; +import { format } from "date-fns"; +import { useEffect, useState } from "react"; +import { FaXmark } from "react-icons/fa6"; +import MonthlyEvent from "./MonthlyEvent"; + +type MobileCalendarEventProps = { + day: Date; + dayEvents: FirebaseEvent[]; +}; + +const MobileCalendarEvent: React.FC = ({ + day, + dayEvents, +}) => { + const [showEventModal, setShowEventModal] = useState(false); + + const handleEventClick = () => { + setShowEventModal((prev) => !prev); + document.body.style.overflow = + document.body.style.overflowY === "hidden" ? "auto" : "hidden"; + }; + + useEffect(() => { + return () => { + if (document.body.style.overflow === "hidden") { + document.body.style.overflowY = "auto"; + } + }; + }, []); + + return ( + <> +
+ {format(day, "dd")} +
+ {showEventModal && ( +
+
+ + +
+ {dayEvents.map((_event, i) => ( + + ))} +
+ + +
+
+ )} + + ); +}; + +export default MobileCalendarEvent; diff --git a/src/components/events/MonthlyEvent.tsx b/src/components/events/MonthlyEvent.tsx new file mode 100644 index 0000000..d1d3ff4 --- /dev/null +++ b/src/components/events/MonthlyEvent.tsx @@ -0,0 +1,118 @@ +import { FirebaseEvent } from "@/types/events"; +import { AnimatePresence, motion } from "framer-motion"; +import { useState } from "react"; + +type MonthlyEventProps = { + event: FirebaseEvent; +}; + +const MonthlyEvent: React.FC = ({ event }) => { + const [expandMobileEvent, setExpandMobileEvent] = useState(false); + function parseDateTime(dateTime: Date): { + timeString: string; + dayOfMonth: string; + dayOfWeek: string; + } { + const timeString = dateTime.toLocaleString("en-US", { + hour: "numeric", + minute: "2-digit", + hour12: true, + }); + + const dayOfMonth = dateTime.getDate().toString(); + + const dayOfWeek = dateTime + .toLocaleString("en-US", { + weekday: "short", + }) + .toUpperCase(); + + return { timeString, dayOfMonth, dayOfWeek }; + } + + const { + timeString: time, + dayOfMonth, + dayOfWeek, + } = parseDateTime(event.time.toDate()); + + return ( + <> + {/* Desktop version */} +
+
+

{dayOfWeek}

+

{dayOfMonth}

+
+
+

{event.title}

+
+

+ Location: {event.location} +

+

+ Time: {time} +

+
+

{event.description}

+
+ {event.tags.map((tag, i) => ( +
+ + {tag} +
+ ))} +
+
+
+ + {/* Mobile Version */} +
setExpandMobileEvent((prev) => !prev)} + > +
+
+

{dayOfWeek}

+

{dayOfMonth}

+
+

{event.title}

+
+ + + {expandMobileEvent && ( + +
+

Location: {event.location}

+

Time: {time}

+
+

{event.description}

+
+ {event.tags.map((tag, i) => ( +
+ + {tag} +
+ ))} +
+
+ )} +
+
+ + ); +}; + +export default MonthlyEvent; diff --git a/src/components/events/MonthlyEvents.tsx b/src/components/events/MonthlyEvents.tsx new file mode 100644 index 0000000..09fbcc3 --- /dev/null +++ b/src/components/events/MonthlyEvents.tsx @@ -0,0 +1,49 @@ +import { useCachedEvents } from "@/hooks/useCachedEvents"; +import React, { useState } from "react"; +import MonthlyEvent from "./MonthlyEvent"; +import { format } from "date-fns"; +import { FaSpinner } from "react-icons/fa"; + +export default function MonthlyEvents() { + const [currentDate, setCurrentDate] = useState(new Date()); + const { + data: events, + isLoading, + isError, + error, + } = useCachedEvents(currentDate); + + if (isError) { + return ( +
+ Error occured while fetching data. Please contact us on discord. +
+ ); + } + if (isLoading) { + return ( +
+

{format(currentDate, 'MMMM')} Events

+

Click an event for more details.

+
+ +
+
+ ); + } + + + + return ( +
+ +

{format(currentDate, 'MMMM')} Events

+ +

Click an event for more details.

+
+ {events?.map((_event) => )} +
+
+ ); +} + diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx new file mode 100644 index 0000000..622a002 --- /dev/null +++ b/src/components/landing/Hero.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import LogoCarousel from "./LogoCarousel"; +import Image from "next/image"; +import { motion } from "framer-motion"; + +export default function Hero() { + return ( +
+ {/* Flex-row on desktop devices but flex-col-reverse for devices lg and smaller */} +
+
+ {/* Hidden on mobile */} + + 1000+ Active Members! + +

+ The Society
+ of PC Building +

+ + {/* On mobile devices shorten the description to limit text-length */} +

+ + Join the Society of PC Building at UF—where + + Where students passionate about + hardware and tech connect, innovate, and build custom PCs together. +

+ + + + + + {/* On mobile devices show the infinite carousel but on desktop static list */} +
+

+ Led by a team of officers with experience at: +

+
+ + + + + + +

+ More!

+
+ +
+
+ + {/* Animate SPCB logo upwards. Applied negative margin to center logo better */} + + SPCB Logo + + +
+
+ ); +} diff --git a/src/components/landing/LandingAbout.tsx b/src/components/landing/LandingAbout.tsx new file mode 100644 index 0000000..bac0dfe --- /dev/null +++ b/src/components/landing/LandingAbout.tsx @@ -0,0 +1,428 @@ +import Image from "next/image"; +import Link from "next/link"; +import React, { useEffect, useState } from "react"; +import { motion } from "framer-motion"; +import GlowingLine from "../decorations/GlowingLine"; + +export default function LandingAbout() { + const [isDesktop, setIsDesktop] = useState(false); + + useEffect(() => { + const checkIfDesktop = () => { + setIsDesktop(window.innerWidth >= 768); + }; + + // Initial check + checkIfDesktop(); + + // Add event listener + window.addEventListener("resize", checkIfDesktop); + + // Cleanup + return () => window.removeEventListener("resize", checkIfDesktop); + }, []); + return ( +
+ + {/* Gradient overlay for radial left */} + + {/* White polygon - using isDesktop state */} + {isDesktop && ( + + )} + + +
+ {/* Decorative Lines */} + + + + + {/* Content Box */} +
+ +
+

+ About +

+

+ The Society of PC Building (SPCB) at the University of Florida + is a community for students passionate about PC hardware, + building, and tech. Whether you're a beginner or an expert, SPCB + offers workshops, live demos, and events to help you learn, + collaborate, and build custom rigs. Beyond PCs, we foster + innovation, teamwork, and technical growth, connecting students + through a shared love for technology. +

+ + Learn More + + + + +
+
+
+
+ {/* Activities Section */} +
+ {/* Main vertical line */} + + + + {/* Top right Blue Group */} + + + + {/* Middle Left Blue Group */} + + + + + {/* Content Cards */} +
+ {/* Socials Card - Left */} + +
+
+ Socials +
+
+

+ Socials +

+
+
+
+ + {/* GBMs Card - Right */} + +
+
+ GBMs +
+
+

+ GBMs +

+
+
+
+ + {/* PC Builds Card - Left */} + +
+
+ PC Builds +
+
+

+ PC Builds +

+
+
+
+
+
+ {/* Social Links Section */} +
+
+ + + +
+ + {/* Content */} +
+
+ {/* Left side */} +
+
+ SPCB Logo +
+

+ Proudly building PCs. +

+ {/* Social Media Icons */} +
+ + LinkedIn + + + Instagram + + + Discord + + + Linktree + +
+
+ + {/* Right side - GUD section */} +
+
+
+

+ Gator User Design +

+ GUD Logo +
+ + Check us out + + + + +
+
+
+
+ {/* Footer */} +
+
+

+ © {new Date().getFullYear()} The Society of PC Building. All + rights reserved. +

+
+
+
+
+ ); +} diff --git a/src/components/landing/LogoCarousel.tsx b/src/components/landing/LogoCarousel.tsx new file mode 100644 index 0000000..3acc702 --- /dev/null +++ b/src/components/landing/LogoCarousel.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +interface Logo { + src: string; + alt: string; +} + +const LogoCarousel: React.FC = () => { + const logos: Logo[] = [ + { src: "/landing/logos/roblox.png", alt: "Roblox" }, + { src: "/landing/logos/nvidia.png", alt: "NVIDIA" }, + { src: "/landing/logos/microsoft.png", alt: "Microsoft" }, + { src: "/landing/logos/samsung.png", alt: "Samsung" }, + { src: "/landing/logos/ukg.png", alt: "UKG" }, + { src: "/landing/logos/meta.png", alt: "Meta" } + ]; + + // Duplicate the logos array to create a seamless infinite effect + const duplicatedLogos = [...logos, ...logos]; + + return ( +
+ + {duplicatedLogos.map((logo, index) => ( +
+ {logo.alt} +
+ ))} + + More! +
+
+ ); +}; + +export default LogoCarousel; diff --git a/tailwind.config.ts b/tailwind.config.ts index 8e167c1..74049d2 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -26,7 +26,7 @@ const config = { 'light-blue': "#8BC5F7", }, fontFamily: { - title: ["Michroma", "sans-serif"], + Michroma: ["Michroma", "sans-serif"], }, }, },