diff --git a/index.html b/index.html index 438c9fe03..61d0a93e2 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,8 @@ - Lama Dev Portfolio + + Chinying - Portfolio
diff --git a/package-lock.json b/package-lock.json index 09f389b3c..a8738bff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,18 +8,28 @@ "name": "starter", "version": "0.0.0", "dependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" + "@emailjs/browser": "^3.11.0", + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", + "framer-motion": "^10.16.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-icons": "^4.11.0", + "react-slick": "^0.29.0", + "sass": "^1.68.0", + "slick-carousel": "^1.8.1", + "swiper": "^11.0.5" }, "devDependencies": { - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@vitejs/plugin-react": "^4.0.3", - "eslint": "^8.45.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^4.4.5" + "@types/react": "18.2.15", + "@types/react-dom": "18.2.7", + "@vitejs/plugin-react": "4.0.3", + "eslint": "8.45.0", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "vite": "4.4.5" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -371,6 +381,29 @@ "node": ">=6.9.0" } }, + "node_modules/@emailjs/browser": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-3.11.0.tgz", + "integrity": "sha512-RkY3FKZ3fPdK++OeF46mRTFpmmQWCHUVHZH209P3NE4D5sg2Atg7S2wa3gw5062Gl4clt4Wn5SyC4WhlVZC5pA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -786,14 +819,59 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", + "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", @@ -910,47 +988,6 @@ "node": ">= 8" } }, - "node_modules/@types/babel__core": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", - "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/prop-types": { "version": "15.7.8", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", @@ -958,9 +995,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.2.25", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.25.tgz", - "integrity": "sha512-24xqse6+VByVLIr+xWaQ9muX1B4bXJKXBbjszbld/UEDslGLY53+ZucF44HCmLbMPejTzGG9XgR+3m2/Wqu1kw==", + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz", + "integrity": "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==", "dev": true, "dependencies": { "@types/prop-types": "*", @@ -969,9 +1006,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.11.tgz", - "integrity": "sha512-zq6Dy0EiCuF9pWFW6I6k6W2LdpUixLE4P6XjXU1QHLfak3GPACQfLwEuHzY5pOYa4hzj1d0GxX/P141aFjZsyg==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "dev": true, "dependencies": { "@types/react": "*" @@ -984,15 +1021,14 @@ "dev": true }, "node_modules/@vitejs/plugin-react": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.0.tgz", - "integrity": "sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.3.tgz", + "integrity": "sha512-pwXDog5nwwvSIzwrvYYmA2Ljcd/ZNlcsSG2Q9CNDBwnsd55UGAyr2doXtB5j+2uymRCnCfExlznzzSFbBRcoCg==", "dev": true, "dependencies": { - "@babel/core": "^7.22.20", + "@babel/core": "^7.22.5", "@babel/plugin-transform-react-jsx-self": "^7.22.5", "@babel/plugin-transform-react-jsx-source": "^7.22.5", - "@types/babel__core": "^7.20.2", "react-refresh": "^0.14.0" }, "engines": { @@ -1060,6 +1096,18 @@ "node": ">=4" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1168,15 +1216,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -1195,6 +1234,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1205,6 +1252,17 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -1293,6 +1351,48 @@ "node": ">=4" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1412,6 +1512,11 @@ "integrity": "sha512-54z7squS1FyFRSUqq/knOFSptjjogLZXbKcYk3B0qkE1KZzvqASwRZnY2KzZQJqIYLVD38XZeoiMRflYSwyO4w==", "dev": true }, + "node_modules/enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==" + }, "node_modules/es-abstract": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", @@ -1465,28 +1570,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", - "dev": true, - "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" - } - }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -1583,27 +1666,27 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", + "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1637,16 +1720,15 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", "dev": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.flatmap": "^1.3.1", "array.prototype.tosorted": "^1.1.1", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", "estraverse": "^5.3.0", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", @@ -1656,7 +1738,7 @@ "object.values": "^1.1.6", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.4", - "semver": "^6.3.1", + "semver": "^6.3.0", "string.prototype.matchall": "^4.0.8" }, "engines": { @@ -1922,6 +2004,17 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1967,6 +2060,29 @@ "is-callable": "^1.1.3" } }, + "node_modules/framer-motion": { + "version": "10.16.4", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.16.4.tgz", + "integrity": "sha512-p9V9nGomS3m6/CALXqv6nFGMuFOxbWsmaOrdmhyQimMIlLl3LC7h7l86wge/Js/8cRu5ktutS/zlzgR7eBOtFA==", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1977,7 +2093,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -2221,6 +2336,11 @@ "node": ">= 4" } }, + "node_modules/immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2290,21 +2410,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -2317,6 +2422,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -2376,43 +2492,14 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2420,15 +2507,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -2441,6 +2519,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -2481,15 +2567,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -2547,15 +2624,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -2568,19 +2636,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -2593,18 +2648,11 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -2653,6 +2701,14 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -2717,6 +2773,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2791,11 +2852,18 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3004,6 +3072,17 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -3045,7 +3124,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3104,11 +3182,18 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", + "integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-refresh": { "version": "0.14.0", @@ -3119,24 +3204,31 @@ "node": ">=0.10.0" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", - "dev": true, + "node_modules/react-slick": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz", + "integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8.10.0" } }, "node_modules/regexp.prototype.flags": { @@ -3156,6 +3248,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "2.0.0-next.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", @@ -3278,6 +3375,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sass": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.68.0.tgz", + "integrity": "sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -3344,15 +3457,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/slick-carousel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz", + "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==", + "peerDependencies": { + "jquery": ">=1.8.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -3466,6 +3591,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swiper": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.0.5.tgz", + "integrity": "sha512-rhCwupqSyRnWrtNzWzemnBLMoyYuoDgGgspAm/8iBD3jCvAWycPLH4Z3TB0O5520DHLzMx94yUMH/B9Efpa48w==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "engines": { + "node": ">= 4.7.0" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3481,6 +3624,22 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3625,14 +3784,14 @@ } }, "node_modules/vite": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", - "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.5.tgz", + "integrity": "sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==", "dev": true, "dependencies": { "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "postcss": "^8.4.26", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" @@ -3710,47 +3869,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/which-typed-array": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", diff --git a/package.json b/package.json index 2e6793c46..62d7d3290 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,18 @@ "preview": "vite preview" }, "dependencies": { + "@emailjs/browser": "^3.11.0", + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", + "framer-motion": "^10.16.4", "react": "18.2.0", - "react-dom": "18.2.0" + "react-dom": "18.2.0", + "react-icons": "^4.11.0", + "react-slick": "^0.29.0", + "sass": "^1.68.0", + "slick-carousel": "^1.8.1", + "swiper": "^11.0.5" }, "devDependencies": { "@types/react": "18.2.15", diff --git a/public/71.png b/public/71.png new file mode 100644 index 000000000..3df405373 Binary files /dev/null and b/public/71.png differ diff --git a/public/Lenexa-logo.png b/public/Lenexa-logo.png new file mode 100644 index 000000000..c64b190b5 Binary files /dev/null and b/public/Lenexa-logo.png differ diff --git a/public/bg_3d.png b/public/bg_3d.png new file mode 100644 index 000000000..d763d6ee0 Binary files /dev/null and b/public/bg_3d.png differ diff --git a/public/bg_3d_2.png b/public/bg_3d_2.png new file mode 100644 index 000000000..875f5fee7 Binary files /dev/null and b/public/bg_3d_2.png differ diff --git a/public/bubble.png b/public/bubble.png new file mode 100644 index 000000000..f8dd3b031 Binary files /dev/null and b/public/bubble.png differ diff --git a/public/cert_acc.png b/public/cert_acc.png new file mode 100644 index 000000000..249cae349 Binary files /dev/null and b/public/cert_acc.png differ diff --git a/public/cert_ai.png b/public/cert_ai.png new file mode 100644 index 000000000..98a67c80f Binary files /dev/null and b/public/cert_ai.png differ diff --git a/public/cert_aws.png b/public/cert_aws.png new file mode 100644 index 000000000..d3552d06d Binary files /dev/null and b/public/cert_aws.png differ diff --git a/public/cert_awsome.png b/public/cert_awsome.png new file mode 100644 index 000000000..524c00a7f Binary files /dev/null and b/public/cert_awsome.png differ diff --git a/public/cert_python.png b/public/cert_python.png new file mode 100644 index 000000000..5dd780252 Binary files /dev/null and b/public/cert_python.png differ diff --git a/public/css.png b/public/css.png new file mode 100644 index 000000000..1a3c5a0fd Binary files /dev/null and b/public/css.png differ diff --git a/public/cyclist.png b/public/cyclist.png new file mode 100644 index 000000000..39cb51b41 Binary files /dev/null and b/public/cyclist.png differ diff --git a/public/docker.png b/public/docker.png new file mode 100644 index 000000000..5a562ccda Binary files /dev/null and b/public/docker.png differ diff --git a/public/edu_fju.png b/public/edu_fju.png new file mode 100644 index 000000000..020b15818 Binary files /dev/null and b/public/edu_fju.png differ diff --git a/public/edu_monash.png b/public/edu_monash.png new file mode 100644 index 000000000..6f1073c9d Binary files /dev/null and b/public/edu_monash.png differ diff --git a/public/exp_airoha.webp b/public/exp_airoha.webp new file mode 100644 index 000000000..aa37299dd Binary files /dev/null and b/public/exp_airoha.webp differ diff --git a/public/exp_formosa.webp b/public/exp_formosa.webp new file mode 100644 index 000000000..baf207881 Binary files /dev/null and b/public/exp_formosa.webp differ diff --git a/public/exp_nia.png b/public/exp_nia.png new file mode 100644 index 000000000..fc07eab3c Binary files /dev/null and b/public/exp_nia.png differ diff --git a/public/expo_cert.png b/public/expo_cert.png new file mode 100644 index 000000000..f87ee9dc8 Binary files /dev/null and b/public/expo_cert.png differ diff --git a/public/expo_win.png b/public/expo_win.png new file mode 100644 index 000000000..12a53f7d6 Binary files /dev/null and b/public/expo_win.png differ diff --git a/public/github.png b/public/github.png new file mode 100644 index 000000000..c8c441dbb Binary files /dev/null and b/public/github.png differ diff --git a/public/hero.png b/public/hero.png index e806437f3..e4820f3f4 100644 Binary files a/public/hero.png and b/public/hero.png differ diff --git a/public/honor_s1.png b/public/honor_s1.png new file mode 100644 index 000000000..cbf6a1520 Binary files /dev/null and b/public/honor_s1.png differ diff --git a/public/html.png b/public/html.png new file mode 100644 index 000000000..f90600c1d Binary files /dev/null and b/public/html.png differ diff --git a/public/javascript.png b/public/javascript.png new file mode 100644 index 000000000..2962f0e5c Binary files /dev/null and b/public/javascript.png differ diff --git a/public/linkedin.png b/public/linkedin.png new file mode 100644 index 000000000..429b2a2c0 Binary files /dev/null and b/public/linkedin.png differ diff --git a/public/mongo.png b/public/mongo.png new file mode 100644 index 000000000..7ab421394 Binary files /dev/null and b/public/mongo.png differ diff --git a/public/node.png b/public/node.png new file mode 100644 index 000000000..145726981 Binary files /dev/null and b/public/node.png differ diff --git a/public/person.png b/public/person.png new file mode 100644 index 000000000..8ee7b80f5 Binary files /dev/null and b/public/person.png differ diff --git a/public/pj_dw.png b/public/pj_dw.png new file mode 100644 index 000000000..63eb8b102 Binary files /dev/null and b/public/pj_dw.png differ diff --git a/public/pj_flight.png b/public/pj_flight.png new file mode 100644 index 000000000..44adb7acd Binary files /dev/null and b/public/pj_flight.png differ diff --git a/public/pj_lda.png b/public/pj_lda.png new file mode 100644 index 000000000..ca7c9ac61 Binary files /dev/null and b/public/pj_lda.png differ diff --git a/public/pj_lga.png b/public/pj_lga.png new file mode 100644 index 000000000..642e9b593 Binary files /dev/null and b/public/pj_lga.png differ diff --git a/public/pj_ml.png b/public/pj_ml.png new file mode 100644 index 000000000..54b877803 Binary files /dev/null and b/public/pj_ml.png differ diff --git a/public/plants_3d.png b/public/plants_3d.png new file mode 100644 index 000000000..d64043e8b Binary files /dev/null and b/public/plants_3d.png differ diff --git a/public/port_exp.png b/public/port_exp.png new file mode 100644 index 000000000..4084493f4 Binary files /dev/null and b/public/port_exp.png differ diff --git a/public/port_landing.png b/public/port_landing.png new file mode 100644 index 000000000..c85c11503 Binary files /dev/null and b/public/port_landing.png differ diff --git a/public/port_skill.png b/public/port_skill.png new file mode 100644 index 000000000..cde5e8457 Binary files /dev/null and b/public/port_skill.png differ diff --git a/public/postgregis.png b/public/postgregis.png new file mode 100644 index 000000000..cf2e91b5a Binary files /dev/null and b/public/postgregis.png differ diff --git a/public/ptv_plan.png b/public/ptv_plan.png new file mode 100644 index 000000000..acc3dd5df Binary files /dev/null and b/public/ptv_plan.png differ diff --git a/public/ptv_tech.png b/public/ptv_tech.png new file mode 100644 index 000000000..b89ff90a2 Binary files /dev/null and b/public/ptv_tech.png differ diff --git a/public/python.png b/public/python.png new file mode 100644 index 000000000..8b4e112a4 Binary files /dev/null and b/public/python.png differ diff --git a/public/qgis.png b/public/qgis.png new file mode 100644 index 000000000..463e45a4e Binary files /dev/null and b/public/qgis.png differ diff --git a/public/r.png b/public/r.png new file mode 100644 index 000000000..f85174aab Binary files /dev/null and b/public/r.png differ diff --git a/public/react.png b/public/react.png new file mode 100644 index 000000000..64bd382b1 Binary files /dev/null and b/public/react.png differ diff --git a/public/rfds_1.png b/public/rfds_1.png new file mode 100644 index 000000000..d4b36565e Binary files /dev/null and b/public/rfds_1.png differ diff --git a/public/rfds_2.png b/public/rfds_2.png new file mode 100644 index 000000000..d52b61cc8 Binary files /dev/null and b/public/rfds_2.png differ diff --git a/public/rfds_3.png b/public/rfds_3.png new file mode 100644 index 000000000..005eed18a Binary files /dev/null and b/public/rfds_3.png differ diff --git a/public/rfds_4.png b/public/rfds_4.png new file mode 100644 index 000000000..71f8b1385 Binary files /dev/null and b/public/rfds_4.png differ diff --git a/public/rfds_5.png b/public/rfds_5.png new file mode 100644 index 000000000..500aa243d Binary files /dev/null and b/public/rfds_5.png differ diff --git a/public/rocket_3d.png b/public/rocket_3d.png new file mode 100644 index 000000000..0b82e175d Binary files /dev/null and b/public/rocket_3d.png differ diff --git a/public/sc_bar.png b/public/sc_bar.png new file mode 100644 index 000000000..b6a0861ae Binary files /dev/null and b/public/sc_bar.png differ diff --git a/public/sc_bar2.png b/public/sc_bar2.png new file mode 100644 index 000000000..e757e1166 Binary files /dev/null and b/public/sc_bar2.png differ diff --git a/public/sc_donut.png b/public/sc_donut.png new file mode 100644 index 000000000..a32ab5011 Binary files /dev/null and b/public/sc_donut.png differ diff --git a/public/sc_hazard.png b/public/sc_hazard.png new file mode 100644 index 000000000..a0c1a84fb Binary files /dev/null and b/public/sc_hazard.png differ diff --git a/public/sc_line.png b/public/sc_line.png new file mode 100644 index 000000000..2f2d46061 Binary files /dev/null and b/public/sc_line.png differ diff --git a/public/sc_map.png b/public/sc_map.png new file mode 100644 index 000000000..d5d808745 Binary files /dev/null and b/public/sc_map.png differ diff --git a/public/sc_news.png b/public/sc_news.png new file mode 100644 index 000000000..857bc49cc Binary files /dev/null and b/public/sc_news.png differ diff --git a/public/sc_spinner.png b/public/sc_spinner.png new file mode 100644 index 000000000..fb0d94757 Binary files /dev/null and b/public/sc_spinner.png differ diff --git a/public/seek.png b/public/seek.png new file mode 100644 index 000000000..bdfe4286a Binary files /dev/null and b/public/seek.png differ diff --git a/public/shooting_3d.png b/public/shooting_3d.png new file mode 100644 index 000000000..0b41c76c6 Binary files /dev/null and b/public/shooting_3d.png differ diff --git a/public/spark.png b/public/spark.png new file mode 100644 index 000000000..b32b8998d Binary files /dev/null and b/public/spark.png differ diff --git a/public/sql.png b/public/sql.png new file mode 100644 index 000000000..a501af1f3 Binary files /dev/null and b/public/sql.png differ diff --git a/public/stackover.jpeg b/public/stackover.jpeg new file mode 100644 index 000000000..42349f89e Binary files /dev/null and b/public/stackover.jpeg differ diff --git a/public/stk_bar.png b/public/stk_bar.png new file mode 100644 index 000000000..217d075f9 Binary files /dev/null and b/public/stk_bar.png differ diff --git a/public/stk_heatmap.png b/public/stk_heatmap.png new file mode 100644 index 000000000..7dceffd7b Binary files /dev/null and b/public/stk_heatmap.png differ diff --git a/public/stk_landing.png b/public/stk_landing.png new file mode 100644 index 000000000..d8fdf20bc Binary files /dev/null and b/public/stk_landing.png differ diff --git a/public/stk_line.png b/public/stk_line.png new file mode 100644 index 000000000..548217058 Binary files /dev/null and b/public/stk_line.png differ diff --git a/public/ta11.JPG b/public/ta11.JPG new file mode 100644 index 000000000..2c62d4587 Binary files /dev/null and b/public/ta11.JPG differ diff --git a/public/tableau.png b/public/tableau.png new file mode 100644 index 000000000..0a9c6047b Binary files /dev/null and b/public/tableau.png differ diff --git a/public/taiwan.jpeg b/public/taiwan.jpeg new file mode 100644 index 000000000..14115a1ea Binary files /dev/null and b/public/taiwan.jpeg differ diff --git a/public/tensorflow.png b/public/tensorflow.png new file mode 100644 index 000000000..acf3a47e7 Binary files /dev/null and b/public/tensorflow.png differ diff --git a/public/tw_cluster.png b/public/tw_cluster.png new file mode 100644 index 000000000..73c615fcb Binary files /dev/null and b/public/tw_cluster.png differ diff --git a/public/tw_filter.png b/public/tw_filter.png new file mode 100644 index 000000000..2a3395f19 Binary files /dev/null and b/public/tw_filter.png differ diff --git a/public/tw_landing.png b/public/tw_landing.png new file mode 100644 index 000000000..63447ac2f Binary files /dev/null and b/public/tw_landing.png differ diff --git a/public/tw_list.png b/public/tw_list.png new file mode 100644 index 000000000..12e121912 Binary files /dev/null and b/public/tw_list.png differ diff --git a/public/tw_list_pg.png b/public/tw_list_pg.png new file mode 100644 index 000000000..919c4af29 Binary files /dev/null and b/public/tw_list_pg.png differ diff --git a/public/tw_popup.png b/public/tw_popup.png new file mode 100644 index 000000000..7fa963840 Binary files /dev/null and b/public/tw_popup.png differ diff --git a/src/App.jsx b/src/App.jsx index cb7b6da73..3223a1f22 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,60 @@ +import "./app.scss" +import Navbar from "./components/Narbar/Navbar"; +import Hero from "./components/hero/Hero"; +import Parallax from "./components/parallax/Parallax"; +import Services from "./components/services/Services"; +import Portfolio from "./components/portfolio/Portfolio"; +import Contact from "./components/contact/Contact"; +import Cursor from "./components/cursor/Cursor"; +import Skill from "./components/skill/Skill"; +import Gallery from "./components/gallery/Gallery"; +import Project from "./components/project/Project"; +import Card from "./components/card/Card"; +import ProfilesSwiper from "./components/Swiper/Swiper"; + const App = () => { - return
Hello World
; + return (
+ +
+ + + {/* */} +
+ {/*
+ +
*/} +
+ + + +
+ + {/*
+ +
*/} + {/* */} +
+ + +
+
+ + +
+ {/* */} + {/*
+ + +
*/} +
+ +
+ + +
); }; export default App; diff --git a/src/app.scss b/src/app.scss new file mode 100644 index 000000000..ddd68bf16 --- /dev/null +++ b/src/app.scss @@ -0,0 +1,51 @@ +html{ + scroll-snap-type: y mandatory; + scroll-behavior: smooth; +} + +*{ + padding: 0; + margin: 0; + box-sizing: border-box; + font-family: "DM Sans", sans-serif; + //font-family: "roboto", sans-serif; + letter-spacing: 0.05rem; +} + +body{ + // background: linear-gradient(270deg, rgb(1, 197, 168), rgb(14, 10, 136)); + background: #090a0c; + color: #dbd4b8; + overflow-x: hidden; + // background-image: url("/stars.png"); +} + +a{ + text-decoration: none; + color:inherit; +} + +section { + height: 100vh; + width: 100vw; + overflow: hidden; + scroll-snap-align: center; /*start*/ +} + +@mixin mobile { + @media (max-width: 768px) { + @content; + } +} + +@mixin tablet { + @media (max-width: 1024px) { + @content; + } +} + +@mixin desktop { + @media (max-width: 1366px) { + @content; + } +} \ No newline at end of file diff --git a/src/components/Narbar/Navbar.jsx b/src/components/Narbar/Navbar.jsx new file mode 100644 index 000000000..47adddfa6 --- /dev/null +++ b/src/components/Narbar/Navbar.jsx @@ -0,0 +1,24 @@ +import Sidebar from "../Sidebar/Sidebar"; +import "./Navbar.scss"; +import { motion } from "framer-motion"; + +const Navbar = () => { + return ( +
+ +
+ chinying +
+ + + {/* + */} +
+
+
+ ) +}; + +export default Navbar; \ No newline at end of file diff --git a/src/components/Narbar/Navbar.scss b/src/components/Narbar/Navbar.scss new file mode 100644 index 000000000..9197318d5 --- /dev/null +++ b/src/components/Narbar/Navbar.scss @@ -0,0 +1,38 @@ +@import "../../app.scss"; + +.navbar{ + height: 50px; + background-color: rgba(112, 248, 228, 0); + + .wrapper{ + max-width: 1366px; + margin: auto; + display: flex; + align-items: center; + justify-content: space-start; + height: 100%; + + @include mobile{ + justify-content: flex-start; + } + + span{ + font-weight: bold; + font-size: 40px; + + } + .social{ + display: flex; + gap: 5px; + + img{ + width: 30px; + height: 30px; + object-fit: cover; + margin-top: 5px; + } + + + } + } +} \ No newline at end of file diff --git a/src/components/Sidebar/Sidebar.jsx b/src/components/Sidebar/Sidebar.jsx new file mode 100644 index 000000000..3c74c9f44 --- /dev/null +++ b/src/components/Sidebar/Sidebar.jsx @@ -0,0 +1,41 @@ +import "./Sidebar.scss"; +import Links from "./links/links"; +import ToggleButton from "./toggleButton/toggleButton"; +import { useState } from "react"; +import { motion } from "framer-motion"; + + + +const variants = { + open:{ + clipPath: "circle(1200px at 50px 50px)", + transition:{ + type: "spring", + stiffness: 20, + },}, + closed:{ + clipPath: "circle(22px at 50px 50px)", + backgroundColor: "transparent", + transition:{ + delay: 0.5, + type: "spring", + stiffness: 400, + damping: 40, + }, + }, +}; + +const Sidebar = () => { + const [open, setOpen] = useState(false); + + return ( + + + + + + + ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/components/Sidebar/Sidebar.scss b/src/components/Sidebar/Sidebar.scss new file mode 100644 index 000000000..460700961 --- /dev/null +++ b/src/components/Sidebar/Sidebar.scss @@ -0,0 +1,56 @@ +@import "../../app.scss"; +.sidebar{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #a9927d; + color: #0f0f0e; + + .bg{ + z-index: 999; + position: fixed; + top: 0px; + right: 0; + bottom: 0; + width: 225px; + background: rgb(244, 174, 61); + + @include mobile{ + width: 180px; + right: 0; + } + + .links{ + position: absolute; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 20px; + } + + a{ + font-size: 1em; + + } + + } + + button{ + z-index: 999; + width: 50px; + height: 50px; + border-radius: 50%; + position: fixed; + top:0px; + right:5px; + color: #FFE8D6; + background-color: transparent; + border: none; + cursor: pointer; + + } +} \ No newline at end of file diff --git a/src/components/Sidebar/links/links.jsx b/src/components/Sidebar/links/links.jsx new file mode 100644 index 000000000..93d9e3899 --- /dev/null +++ b/src/components/Sidebar/links/links.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { motion } from 'framer-motion'; + +const variants = { + open: { + transition: { + staggerChildren: 0.1, + }, + }, + closed: { + transition: { + staggerChildren: 0.05, + staggerDirection: -1, + }, + }, + }; +const itemVariants = { + open: { + y: 0, + opacity: 1, + }, + closed: { + y: 50, + opacity: 0, + }, + }; + + +const Links = () => { + const items =["Home","Experience","Skills","Projects","Contact"] + return ( + + {items.map(item=>( + + {item} + + ))} + + ) + } + +export default Links; \ No newline at end of file diff --git a/src/components/Sidebar/toggleButton/toggleButton.jsx b/src/components/Sidebar/toggleButton/toggleButton.jsx new file mode 100644 index 000000000..5dbf9fee6 --- /dev/null +++ b/src/components/Sidebar/toggleButton/toggleButton.jsx @@ -0,0 +1,41 @@ +import { motion } from "framer-motion"; + +const ToggleButton = ({setOpen}) => { + return ( + + ) +}; + +export default ToggleButton; \ No newline at end of file diff --git a/src/components/Swiper/Swiper.jsx b/src/components/Swiper/Swiper.jsx new file mode 100644 index 000000000..7693395d2 --- /dev/null +++ b/src/components/Swiper/Swiper.jsx @@ -0,0 +1,173 @@ +import { Swiper, SwiperSlide } from 'swiper/react'; +import React, { useState, useEffect } from 'react'; +import 'swiper/css'; +import 'swiper/css/pagination'; +import 'swiper/css/navigation'; +import 'swiper/css/grid'; +import "./Swiper.scss"; +import { Navigation, Pagination } from 'swiper/modules'; + +const projects = [ + { + id: 1, + images: ["/Lenexa-logo.png"], + images_desc: ["ongoing project..."], + title: 'Ongoing: Lenexa Medical Dashboard with tableau', + type: 'python', + language: 'SQL', + technology: 'Tableau', + description: 'Building an interactive dashboard to replace current reports. Dashboard will be used by clients to monitor their performance and make data-driven decisions to avoid pressure injury.', + link: 'https://lenexamedical.com/' + }, + { + id: 2, + images: ["/seek.png"], + images_desc: ["Automation web scraping with Selenium"], + title: 'Ongoing: SEEK Web Scraping', + type: 'python', + language: 'LDA', + technology: 'Semi-structured Data', + description: 'This project presents a thorough approach to extracting and analysing job posting data from SEEK. Employing Python for robust web scraping, it enables to collect data needed for deep insights into job trends and requirements. ', + link: 'https://github.com/chinnnying/seek_webscraping' + }, + { + id: 8, + images: ["/port_landing.png", "/port_exp.png", "/port_skill.png"], + images_desc: ["Landing page", "Experience", "Skills"], + title: 'Personal portfolio', + type: 'javascript', + language: 'React', + technology: 'framer-motion', + description: 'An interactive portfolio website to practice and showcase my projects and skills.', + link: 'https://github.com/chinnnying/animated-portfolio/tree/chinnnying' + }, + { + id: 3, + images: ["/cyclist.png", "/sc_hazard.png", "/sc_spinner.png", "/sc_map.png", "/sc_news.png", "/sc_line.png", "/sc_bar.png", "/sc_bar2.png", "/sc_donut.png"], + images_desc: ["Worked as a data scientist and frontend developer in this project (Landing page for the app)", "Showing past 20 years bike accidents on the suggested route", "Interactive information display design", "Converted data format, concatenated over 10 datasets and showed bike trail on Leaflet Map with customised markers", "UI/UX design", "Pointed out the importance of bike safety with increasing number of bikers with chart.js", "Highlight current time to easily compare with history data", "Highlight why bike safety should be aware of", "Showing the severity of past bike related accidents"], + title: 'Secure Cyclist Web App', + type: 'Chart.js', + language: 'SQL', + technology: 'Azure', + description: 'Secure Cyclist is designed to ensure the safety and well-being of international students who commute via bicycle in Victoria. Leveraging the power of open data on car accidents, our project provides valuable insights into the dynamics of bicycling in Victoria, ultimately facilitating safer and more informed trips for cyclists.', + link: 'https://github.com/chinnnying/secure_cyclist' + }, + { + id: 4, + images: ["/tw_landing.png", "/tw_filter.png", "/tw_cluster.png", "/tw_list.png", "/tw_popup.png"], + images_desc: ["Taiwan Travel Map Dashboard Landing page (build with R shiny)", "Interactive filter feature", "Easy to explore with clustering nearby tourist spots", "Allow search by keywords", "Providing detailed information of the location"], + title: 'Taiwan Travel Map Dashboard', + type: 'Dashboard', + language: 'R Shiny', + technology: 'Interative', + description: 'A dynamic and interactive dashboard crafted to empower travelers with the essential information needed to explore and plan their journeys across island of Taiwan. This platform offers information of restaurants, accommodations, events, and scenic spots.', + link: 'https://github.com/chinnnying/taiwan_travel_map' + }, + { + id: 5, + images: ["/stk_landing.png", "/stk_bar.png","/stk_line.png", "/stk_heatmap.png"], + images_desc: ["Show respondants distribution by joining extra dataset (country name and its coordinate)", "Investigate if education background affect salary", "Explore how developers learn to code nowaways by age group", "Heatmap of most popular technology and salary of developers used it at work"], + title: 'Stack Overflow Survey Insights', + type: 'Data Exploration', + language: 'R', + technology: 'Data Vis', + description: 'This project explores the demographics, earnings, and learning journeys of contributors. Beyond Stack Overflow\'s own analysis, this endeavor provides a unique perspective, enriching our understanding of the global coding community.', + link: 'https://github.com/chinnnying/stack_overflow_analysis' + }, + { + id: 6, + images: ["/rfds_1.png", "/rfds_2.png", "/rfds_3.png", "/rfds_4.png", "/rfds_5.png"], + images_desc: ["Identify outliers (extreme and unusal value)", "Remove duplicated data (same aircraft shouldn\'t have 2 depature time)", "Aggregate with different granularity (by month & week)", "Aggregate with different granularity (by sum & average)", "Geo visualisation for flight distribution by aircraft type"], + title: 'RDBS Visualisation with Tableau', + type: 'Data cleaning', + language: 'Tableau', + technology: 'Data Vis', + description: 'The goal of the project is to understand the operation patterns of Royal Flying Doctor Service (RFDS flights), active 24/7 in providing emergency medical services across rural and remote Australia with Tableau. Anomalies (i.e. outliers, duplicates, null) were identified and handled before visualisation.', + link: '/project-one' + }, + { + id: 7, + images: ["/ptv_plan.png" , "ptv_tech.png", "/pj_lga.png"], + images_desc: ["Project plan", "Tools used in this project", "Insights: Data visualisation of blankspot areas of Melbourne"], + title: 'PTV Blankspot Spatial Data Analysis', + type: 'Spatial Data', + language: 'PostgisSQL', + technology: 'Dbeaver', + description: 'This project analyses spatial data to assess PTV\'s coverage and pinpoints any underserved areas, providing stakeholders with insights to improve public transport accessibility, reduce commute times, and support the evolving workstyle needs of Victoria\'s residents.', + link: 'https://github.com/chinnnying/ptv_blankspot' + }, + ]; + + +const ProfilesSwiper = () => { + const [slidesPerView, setSlidesPerView] = useState(3); + + // Effect to update slidesPerView on window resize + useEffect(() => { + const handleResize = () => { + // Set slidesPerView to 1 if window width is less than or equal to 768px + if (window.innerWidth <= 768) { + setSlidesPerView(1); + } + // Set slidesPerView to 2 if window width is greater than 768px and less than or equal to 1024px + else if (window.innerWidth > 768 && window.innerWidth <= 1400) { + setSlidesPerView(2); + } + // Set slidesPerView to 3 for widths greater than 1024px + else { + setSlidesPerView(3); + } + }; + + // Add event listener + window.addEventListener('resize', handleResize); + + // Call handleResize initially in case the initial window size is small + handleResize(); + + // Clean up the event listener + return () => window.removeEventListener('resize', handleResize); + }, []); + + return ( +
+
+

Projects. + + +

+
+ + {projects.map((profile, index) => ( + +
+

{profile.type}

+

{profile.language}

+

{profile.technology}

+
+
+ {profile.title} +
+
+

{profile.title}

+

{profile.description}

+
+
+ +
+ +
+ ))} +
+
+ ); +}; + +export default ProfilesSwiper; diff --git a/src/components/Swiper/Swiper.scss b/src/components/Swiper/Swiper.scss new file mode 100644 index 000000000..a9d6a510d --- /dev/null +++ b/src/components/Swiper/Swiper.scss @@ -0,0 +1,177 @@ +@import "../../app.scss"; +.swiper-container{ + justify-content: center; + text-align: center; + width: 95%; + margin-left: 50px; +} + +.mySwiper { + display: flex; + justify-content: center; + align-items: center; + height: 72vh; + padding: 20px 60px; + +} + +.project-header{ + height: 14vh; +} + +.swiper-head{ + font-size: 4rem; + span{ + color: #fa9a2c; + font-size: 5rem; + + + } + button { + padding: 15px 20px; + border: none; + font-size: 1rem; + margin-left: 50px; + border-radius: 25px; + background-color: orange; + color: black; + cursor: pointer; + &:hover { + background-color: rgb(10, 120, 237); + color: white; + } + } +} +.swiper-slide { + padding: 20px; + border-radius: 10px; + border: #dedede 1px solid; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + transition: transform 0.3s ease; + + &:hover { + transform: translateY(-5px); + } + + .swiper-img { + padding: 10px 5px; + height: 25vh; + margin: 5px; + img { + width: 100%; + height: 100%; + } + } + + .swiper-content { + padding: 0px 20px; + display: flex; + flex-direction: column; + height: 25vh; + overflow: scroll; + + h3{ + font-size: 1.1rem; + margin-bottom: 10px; + font-weight: 900; + color:#e7e7e4; + + } + p { + font-size: 0.9rem; + color: rgb(197, 197, 197); + text-align: justify; + } + } + + .swiper-footer { + text-align: center; + //height: 8vh; + button { + padding: 12px 12px; + border: none; + font-size: 0.7rem; + border-radius: 25px; + background-color: orange; + color: black; + cursor: pointer; + &:hover { + background-color: rgb(10, 120, 237); + color: white; + } + } + } +} + +.swiper-button-next, .swiper-button-prev { + color: #fa9a2c; + } + +.swiper-pagination-bullet { + background-color: #fa9a2c; +} + +.features{ + display: flex; + justify-content: right; + flex-direction: row; + gap: 10px; + } + .feature{ + background-color: rgb(69, 125, 52); + color: rgb(232, 223, 223); + padding: 5px; + border-radius: 5px; + font-size: 0.8rem; + + } + + + @media (max-width: 768px) { + .swiper-container { + margin-left: 20px; + } + + .mySwiper { + height: 100%; + padding: 1rem; + } + + .project-header { + height: 10vh; + } + + .swiper-head { + margin-top: 20px; + font-size: 3rem; + font-weight: 900; + span { + font-size: 4rem; + } + button { + padding: 0.8rem 1rem; + font-size: 0.7rem; + margin-left: 20px; + border-radius: 1.5rem; + + } + } + + .swiper-slide { + margin-top: 20px; + height: 70vh; + //min-height: 480px; + .swiper-img { + height: 25vh; + } + .swiper-content { + height: 20vh; + h3 { + font-size: 0.9rem; + } + p { + font-size: 0.8rem; + } + } + } + } \ No newline at end of file diff --git a/src/components/card/Card.jsx b/src/components/card/Card.jsx new file mode 100644 index 000000000..c605f1ad1 --- /dev/null +++ b/src/components/card/Card.jsx @@ -0,0 +1,242 @@ +import React, { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import "./Card.scss"; + +const projects = [ + { + id: 1, + images: ["/exp_formosa.webp"], + images_desc: ["ongoing project..."], + title: 'Ongoing: Formosa Massage Data Pineline', + type: 'python', + language: 'SQL', + technology: 'Visualisation', + description: 'Formosa Massage is a massage therapy located in TAS. The project is to build a data pipeline to collect data from the shop\'s booking system, and then analyse the data to provide insights for the shop owner to make business decisions.', + link: '/project-one' + }, + { + id: 2, + images: ["/seek.png"], + images_desc: ["Automation web scraping with Selenium"], + title: 'Ongoing: SEEK Web Scraping', + type: 'python', + language: 'LDA', + technology: 'Semi-structured Data Analysis', + description: 'This project presents a thorough approach to extracting and analysing job posting data from SEEK. Employing Python for robust web scraping, it enables to collect data needed for deep insights into job trends and requirements. ', + link: '/project-one' + }, + { + id: 8, + images: ["/port_landing.png", "/port_exp.png", "/port_skill.png"], + images_desc: ["Landing page", "Experience", "Skills"], + title: 'Personal portfolio', + type: 'javascript', + language: 'React', + technology: 'framer-motion', + description: 'An interactive portfolio website to practice and showcase my projects and skills.', + link: '/project-one' + }, + { + id: 3, + images: ["/cyclist.png", "/sc_hazard.png", "/sc_spinner.png", "/sc_map.png", "/sc_news.png", "/sc_line.png", "/sc_bar.png", "/sc_bar2.png", "/sc_donut.png"], + images_desc: ["Worked as a data scientist and frontend developer in this project (Landing page for the app)", "Showing past 20 years bike accidents on the suggested route", "Interactive information display design", "Converted data format, concatenated over 10 datasets and showed bike trail on Leaflet Map with customised markers", "UI/UX design", "Pointed out the importance of bike safety with increasing number of bikers with chart.js", "Highlight current time to easily compare with history data", "Highlight why bike safety should be aware of", "Showing the severity of past bike related accidents"], + title: 'Secure Cyclist Web App', + type: 'Chart.js', + language: 'SQL', + technology: 'Azure', + description: 'Secure Cyclist is a comprehensive initiative designed to ensure the safety and well-being of international students who commute via bicycle in the vibrant region of Victoria. Leveraging the power of open data on car accidents, our project seeks to provide valuable insights into the dynamics of bicycling in Victoria, ultimately facilitating safer and more informed trips for cyclists.', + link: '/project-one' + }, + { + id: 4, + images: ["/tw_landing.png", "/tw_filter.png", "/tw_cluster.png", "/tw_list.png", "/tw_popup.png"], + images_desc: ["Taiwan Travel Map Dashboard Landing page (build with R shiny)", "Interactive filter feature", "Easy to explore with clustering nearby tourist spots", "Allow search by keywords", "Providing detailed information of the location"], + title: 'Taiwan Travel Map Dashboard', + type: 'Dashboard', + language: 'R Shiny', + technology: 'Interative', + description: 'Taiwan Travel Map is a dynamic and interactive dashboard meticulously crafted to empower travelers with the essential information needed to explore and plan their journeys across the captivating island of Taiwan. This platform offers insights into a wide array of establishments and attractions, including restaurants, accommodations, events, and scenic spots.', + link: '/project-one' + }, + { + id: 5, + images: ["/stk_landing.png", "/stk_bar.png","/stk_line.png", "/stk_heatmap.png"], + images_desc: ["Show respondants distribution by joining extra dataset (country name and its coordinate)", "Investigate if education background affect salary", "Explore how developers learn to code nowaways by age group", "Heatmap of most popular technology and salary of developers used it at work"], + title: 'Stack Overflow Survey Insights', + type: 'Data Exploration', + language: 'R', + technology: 'Data Vis', + description: 'This project explores the demographics, earnings, and learning journeys of contributors to the coding community, aiming to uncover who these contributors are, their income levels, and how they acquire coding skills. Beyond Stack Overflow\'s own analysis, this endeavor provides a unique perspective, enriching our understanding of the global coding community.', + link: '/project-one' + }, + { + id: 6, + images: ["/rfds_1.png", "/rfds_2.png", "/rfds_3.png", "/rfds_4.png", "/rfds_5.png"], + images_desc: ["Identify outliers (extreme and unusal value)", "Remove duplicated data (same aircraft shouldn\'t have 2 depature time)", "Aggregate with different granularity (by month & week)", "Aggregate with different granularity (by sum & average)", "Geo visualisation for flight distribution by aircraft type"], + title: 'RDBS Visualisation with Tableau', + type: 'Data cleaning', + language: 'Tableau', + technology: 'Data Vis', + description: 'The goal of the project is to understand the operation patterns of Royal Flying Doctor Service (RFDS flights), active 24/7 in providing emergency medical services across rural and remote Australia with Tableau. Anomalies (i.e. outliers, duplicates, null) were identified and handled before visualisation.', + link: '/project-one' + }, + { + id: 7, + images: ["/ptv_plan.png" , "ptv_tech.png", "/pj_lga.png"], + images_desc: ["Project plan", "Tools used in this project", "Insights: Data visualisation of blankspot areas of Melbourne"], + title: 'PTV Blankspot Spatial Data Analysis', + type: 'Spatial Data', + language: 'PostgisSQL', + technology: 'Dbeaver', + description: 'This project analyzes spatial data to assess PTV\'s coverage, pinpoint any underserved areas, and identify regions with the best transport options, providing stakeholders with insights to improve public transport accessibility, reduce commute times, and support the evolving workstyle needs of Victoria\'s residents.', + link: '/project-one' + }, + ]; + + + + + const Card = () => { + const [selectedId, setSelectedId] = useState(null); + const [currentImageIndex, setCurrentImageIndex] = useState([{}]); + const [isExpanded, setIsExpanded] = useState(true); + const toggleExpansion = () => { + setIsExpanded(!isExpanded); + }; + + const handleSelect = () => { + if (isMobile) { + setIsExpanded(false); + } + }; + + useEffect(() => { + const imageIndices = projects.reduce((acc, project) => { + acc[project.id] = 0; + return acc; + }, {}); + setCurrentImageIndex(imageIndices); + }, []); + + const nextImage = (projectId, imageCount) => { + setCurrentImageIndex(prev => ({ + ...prev, + [projectId]: (prev[projectId] + 1) % imageCount + })); + }; + + const prevImage = (projectId, imageCount) => { + setCurrentImageIndex(prev => ({ + ...prev, + [projectId]: (prev[projectId] - 1 + imageCount) % imageCount + })); + }; + + const goToImage = (projectId, index) => { + setCurrentImageIndex(prev => ({ + ...prev, + [projectId]: index + })); + }; + const itemVariants = { + hidden: { opacity: 0, y: 0 }, + visible: i => ({ + opacity: 1, + y: 0, + transition: { delay: i * 0.08 } + }) + }; + const variants = { + hidden: { opacity: 0, x: -100 }, + visible: { opacity: 1, x: 0, transition: { type: 'spring', stiffness: 120 } } + }; + + const [isMobile, setIsMobile] = useState(window.innerWidth < 600); + + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < 600); + }; + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + const componentContainerClass = `componentContainer ${isExpanded && isMobile ? 'hiddenOnMobile' : ''}`; + //const isPhone = window.innerWidth <= 600; + const toggleButtonText = isExpanded ? 'Projects⌄' : 'Projects⌄'; + + return ( +
+
+

+ {( + + )} +

+ {isExpanded && ( + + {projects.map((project, index) => ( + { + setSelectedId(project.id); + handleSelect(); + }} className="item"> +
{project.title}
+ {/*

{project.technology}

*/} +
+ ))} +
+ )} +
+
+ {!selectedId && ( +

Please select a project
👈🏻👈🏼👈🏽👈🏾

+ )} + {selectedId && ( +
+ {projects.filter(project => project.id === selectedId).map(project => ( + + + {project.title} + + + {project.type} + {project.language} + {project.technology} + + + {project.description} + + + {typeof currentImageIndex[project.id] !== 'undefined' && ( + <> + {project.title} +

+ {project.images_desc[currentImageIndex[project.id]]} +

+ + )} +
+ {project.images.map((_, index) => ( + goToImage(project.id, index)} + > + ))} +
+ + +
+
+ ))} +
+ )} +
+
+ ); +}; + +export default Card; \ No newline at end of file diff --git a/src/components/card/Card.scss b/src/components/card/Card.scss new file mode 100644 index 000000000..4c9f49b58 --- /dev/null +++ b/src/components/card/Card.scss @@ -0,0 +1,290 @@ +@import "../../app.scss"; +.card{ + //height: calc(100vh); + position: relative; + display: flex; + flex-direction: row; + +} + +.cButton{ + font-size: 3rem; + font-weight: 900; + padding: 20px; + color: #aca764; + background-color: transparent; + border: none; + + } + +.cardContainer{ + flex-wrap: wrap; +} + +.midcontent{ + max-width: 800px; + overflow-wrap: break-word; +} +.beforeselect{ + font-size: 1.5rem; + text-align: center; + margin-top: 300px; +} + + +.componentContainer { + position: relative; + display: flex; + flex-wrap: wrap; + flex-direction: column; + margin-left: 50px; + padding: 20px; + width: 100%; + @include mobile{ + margin-left: 20px; + padding-top: 0px; + } + //height: calc(100vh - 6rem); + } + + .item { + display: flex; + justify-content: end; + align-items: center; + text-align: end; + margin: 15px; + padding: 10px; + border: 1px solid #ddd; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s ease; + width: 200px; + height: 60px; + + &:hover { + background-color: #f0f0f0; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + } + + .itemTitle { + font-size: 1.5rem; + color: #333; + } + + .itemSubtitle { + font-weight: 600; + font-size: 0.9rem; + color: #4b4a4a; + } + + .detailsContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 700px; + height: 400px; + background-color: rgba(255, 255, 255, 0); + padding: 10px; + border-radius: 10px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + z-index: 10; + } + .detailTitle{ + // position: absolute; + text-wrap: wrap; + margin-top: 60px; + font-size: 3rem; + line-height: 4rem; + //white-space: nowrap; + + } + + .detailDesc { + margin-top: 10px; + font-size: 1.1rem; + color: #b4b0b0; + font-weight: 300; + } + .detailFeaturebox{ + display: flex; + flex-direction: row; + gap: 10px; + margin-top: 10px; + } + .detailFeature{ + background-color: rgb(75, 74, 74); + color: rgb(232, 223, 223); + padding: 5px; + border-radius: 5px; + @include mobile{ + font-size: 0.9rem; + } + } + .imageDescription{ + margin-top: 5px; + color:#aca764; + } + + .cimg { + width: 100%; + height: 300px; + object-fit: contain; + + } + + .dots { + transform: translateX(-50%); + display: flex; + gap: 6px; + margin-top: 30px; + margin-left: 120px; + } + + .dot { + width: 10px; + height: 10px; + background-color: gray; + border-radius: 50%; + cursor: pointer; + transition: background-color 0.3s; + + &.active { + background-color: lightgrey; + + } + @include mobile{ + width: 7px; + height: 7px; + } + } + + .navButton { + position: absolute; + top: 50%; + //transform: translateY(-50%); + //margin-top: 300px; + background-color: transparent; + border: none; + padding: 10px; + cursor: pointer; + font-size: 40px; + border-radius: 10px; + color:rgb(236, 236, 236); + + } + + .prevButton { + left: 10px; + } + + .nextButton { + right: 10px; + } + + @media only screen and (max-width: 600px) { + .card { + flex-direction: column; + height: reset; + overflow: reset; + // Other adjustments for mobile + } + .cardContainer{ + display: flex; + flex-direction: column; + flex-wrap: wrap; + gap: 0px; + } + + .item { + width: 70%; + margin: 10px; + height: 40px; + } + + .itemSubTitle { + font-size: 1rem; + } + .itemTitle { + display: none; + } + .hiddenOnMobile { + display: none; + } + .detailTitle{ + // position: absolute; + margin-top: 0px; + font-size: 1.5rem; + text-wrap: wrap; + line-height: 2rem; + } + .detailDesc { + margin-top: 10px; + font-size: 0.9rem; + color: #555353; + } + .cButton{ + font-size: 1.8rem; + font-weight: 900; + padding: 20px; + color: #918f73; + background-color: transparent; + border: none; + + } + .detailsContainer { + margin-top: 0px; + display: flex; + width: 100%; + height: 250px; + } + .beforeselect{ + font-size: 1.2rem; + text-align: center; + margin-top: 200px; + } + + .midcontent{ + max-width: 300px; + overflow-wrap: break-word; + } + .navButton { + background-color: transparent; + border: none; + padding: 10px; + cursor: pointer; + font-size: 40px; + border-radius: 10px; + color:rgb(236, 236, 236); + margin-top: 250px; + + @include mobile{ + font-size:30px; + } + } + + .prevButton { + width: 50px; + } + + .nextButton { + width: 50px; + left: 250px; + } + + .dots { + margin-top: 20px; + transform: translateX(-50%); + display: flex; + gap: 6px; + } + .cimg { + width: 100%; + max-height: 180px; + margin-top: 20px; + object-fit: contain; + + } + + } \ No newline at end of file diff --git a/src/components/contact/Contact.jsx b/src/components/contact/Contact.jsx new file mode 100644 index 000000000..eb61da4d1 --- /dev/null +++ b/src/components/contact/Contact.jsx @@ -0,0 +1,187 @@ +import { useRef, useState, useEffect } from "react"; +import "./Contact.scss"; +import { motion, useInView } from "framer-motion"; +import emailjs from "@emailjs/browser"; + +const line = "Contact me" + +const sentence = { + hidden: { opacity: 1 }, + visible: { + opacity: 1, + transition: { + delay: 4, + staggerChildren: 0.15, + }, + }, +} + +const letter = { + hidden: { opacity: 0, y: 50 }, + visible: { + opacity: 1, + y: 0, + }} + +const variants = { + initial: { + y: 0, + opacity: 0, + }, + animate: { + y: 0, + opacity: 1, + transition: { + delay: 3, + duration: 3, + staggerChildren: 0.1, + }, + }, +}; + +const Toast = ({ message, onClose }) => { + useEffect(() => { + const timer = setTimeout(() => { + onClose(); + }, 3000); // The toast will disappear after 3 seconds + + return () => clearTimeout(timer); + }, [onClose]); + + return ( +
+ {message} +
+ ); +} + + + +const Contact = () => { + const ref = useRef(); + const formRef = useRef(); + const [error, setError] = useState(false); + const [success, setSuccess] = useState(false); + const [isVisible, setIsVisible] = useState(false); + const inView = useInView(ref, { threshold: 0.5 }); + const isInView = useInView(ref, { margin: "-100px" }); + const [showToast, setShowToast] = useState(false); + const [toastMessage, setToastMessage] = useState(""); + + useEffect(() => { + setIsVisible(inView); + }, [inView]); + + const sendEmail = (e) => { + e.preventDefault(); + + emailjs + .sendForm( + "service_6394gz9", + "template_5ph4f3o", + formRef.current, + "mhI641H6BlWFp30x2" + ) + .then( + (result) => { + setSuccess(true); + setToastMessage("Message sent successfully!"); + setShowToast(true); + }, + (error) => { + setError(true); + setToastMessage("Error sending message. Please try again later."); + setShowToast(true); + } + ); + }; + + return ( + + + + + {line.split("").map((char, index) => { + return ( + + {char} + + ); + + })} + + + + + + {/* +

Mail

+ hello@react.dev +
*/} + +
+ + + +
+ +
+ +
+
+ + + + + + + + +