diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 4dcb439..bb4f312 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,19 +2,20 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], + "react/prop-types": "off", }, -} +}; diff --git a/index.html b/index.html index 0644cfa..d6c8259 100644 --- a/index.html +++ b/index.html @@ -3,13 +3,14 @@ - + Intranet Five
+ diff --git a/package-lock.json b/package-lock.json index 2eb1c19..d72ae13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,15 @@ "version": "0.0.0", "dependencies": { "@sanity/client": "^6.11.3", + "@sanity/image-url": "^1.0.2", "firebase": "^10.7.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.0.1", + "react-modal": "^3.16.1", "react-router-dom": "^6.21.3", + "react-slick": "^0.30.1", + "slick-carousel": "^1.8.1", "tailwindcss": "^3.4.1" }, "devDependencies": { @@ -48,6 +53,196 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "peer": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "peer": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "peer": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -1413,6 +1608,14 @@ "eventsource": "2.0.2" } }, + "node_modules/@sanity/image-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@sanity/image-url/-/image-url-1.0.2.tgz", + "integrity": "sha512-C4+jb2ny3ZbMgEkLd7Z3C75DsxcTEoE+axXQJsQ75ou0AKWGdVsP351hqK6mJUUxn5HCSlu3vznoh7Yljye4cQ==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@swc/core": { "version": "1.3.106", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.106.tgz", @@ -1623,6 +1826,43 @@ "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", "dev": true }, + "node_modules/@testing-library/dom": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "peer": true + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -1789,11 +2029,19 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "peer": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "is-array-buffer": "^3.0.1" @@ -1941,7 +2189,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -2019,7 +2266,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2", "get-intrinsic": "^1.2.1", @@ -2070,7 +2316,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2119,6 +2364,11 @@ "node": ">= 6" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2261,6 +2511,38 @@ "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", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "peer": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2271,7 +2553,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.1", "gopd": "^1.0.1", @@ -2285,7 +2566,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -2320,6 +2600,12 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "peer": true + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2336,6 +2622,11 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "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.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -2389,6 +2680,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-iterator-helpers": { "version": "1.0.15", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", @@ -2727,6 +3038,11 @@ "node": ">=12.0.0" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2905,7 +3221,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, "dependencies": { "is-callable": "^1.1.3" } @@ -2996,7 +3311,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3013,7 +3327,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2", "has-proto": "^1.0.1", @@ -3125,7 +3438,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -3143,7 +3455,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3152,7 +3463,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -3161,7 +3471,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.2" }, @@ -3173,7 +3482,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3185,7 +3493,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3197,7 +3504,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -3282,7 +3588,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.2", "hasown": "^2.0.0", @@ -3307,11 +3612,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.0", @@ -3340,7 +3660,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, "dependencies": { "has-bigints": "^1.0.1" }, @@ -3363,7 +3682,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -3379,7 +3697,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -3402,7 +3719,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3471,7 +3787,6 @@ "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" } @@ -3500,7 +3815,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3532,7 +3846,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -3559,7 +3872,6 @@ "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" } @@ -3568,7 +3880,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2" }, @@ -3591,7 +3902,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -3606,7 +3916,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, "dependencies": { "has-symbols": "^1.0.2" }, @@ -3621,7 +3930,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, "dependencies": { "which-typed-array": "^1.1.11" }, @@ -3636,7 +3944,6 @@ "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" } @@ -3657,7 +3964,6 @@ "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" @@ -3669,8 +3975,7 @@ "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "node_modules/isexe": { "version": "2.0.0", @@ -3715,6 +4020,12 @@ "jiti": "bin/jiti.js" } }, + "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", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3750,6 +4061,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/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -3820,6 +4139,11 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "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", @@ -3850,6 +4174,15 @@ "node": "14 || >=16.14" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3982,7 +4315,22 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "peer": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3991,7 +4339,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4000,7 +4347,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", @@ -4397,6 +4743,38 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "peer": true + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4415,7 +4793,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", @@ -4496,11 +4873,41 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz", + "integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==", + "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-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } }, "node_modules/react-router": { "version": "6.21.3", @@ -4532,6 +4939,23 @@ "react-dom": ">=16.8" } }, + "node_modules/react-slick": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.1.tgz", + "integrity": "sha512-xtxWBIJH9CvcleAfcZD5A78YtqfHYRMAIkn/AQZFk6Qh/HP/DUtbd3BqHvPKS/Oz0yuINM3YMtw0xLfXvQjeMA==", + "dependencies": { + "@testing-library/user-event": "^14.3.0", + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + }, + "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/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4590,11 +5014,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "peer": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -4615,6 +5044,11 @@ "node": ">=0.10.0" } }, + "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.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -4788,7 +5222,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", - "dev": true, "dependencies": { "define-data-property": "^1.1.1", "function-bind": "^1.1.2", @@ -4804,7 +5237,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "functions-have-names": "^1.2.3", @@ -4837,7 +5269,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -4858,6 +5289,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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", @@ -4871,6 +5310,18 @@ "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.0.0.tgz", "integrity": "sha512-lgxErLl/7A5+vgIIXsh9MbeukOaCb2axgQ+bKCdIE+ibNT4XNYGNCR1qFEGq6F+YDASXK3Fh/c5FgtZchFolxw==" }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "peer": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4879,6 +5330,11 @@ "safe-buffer": "~5.1.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-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -5107,7 +5563,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -5463,6 +5918,14 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -5502,7 +5965,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -5544,7 +6006,6 @@ "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", @@ -5559,7 +6020,6 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.4", diff --git a/package.json b/package.json index f094a5e..7d2e29e 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,15 @@ }, "dependencies": { "@sanity/client": "^6.11.3", + "@sanity/image-url": "^1.0.2", "firebase": "^10.7.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-icons": "^5.0.1", + "react-modal": "^3.16.1", "react-router-dom": "^6.21.3", + "react-slick": "^0.30.1", + "slick-carousel": "^1.8.1", "tailwindcss": "^3.4.1" }, "devDependencies": { diff --git a/public/background-image.jpeg b/public/background-image.jpeg new file mode 100644 index 0000000..5f017af Binary files /dev/null and b/public/background-image.jpeg differ diff --git a/public/employeebg.jpg b/public/employeebg.jpg new file mode 100644 index 0000000..199d2a4 Binary files /dev/null and b/public/employeebg.jpg differ diff --git a/public/symbol.png b/public/symbol.png new file mode 100644 index 0000000..dd33f19 Binary files /dev/null and b/public/symbol.png differ diff --git a/src/App.jsx b/src/App.jsx index 994efa2..3782d7c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,63 +1,27 @@ import { Route, Routes } from "react-router-dom"; import "./App.css"; import HomePage from "./pages/HomePage"; -import MyPage from "./pages/MyPage"; import Header from "./components/Header"; -import Sidebar from "./components/Sidebar"; -import { useEffect, useState } from "react"; -import client from "./sanity/client"; -import { EmployeeContext } from "./context/EmployeeContext"; -import Management from "./pages/Management"; +import DataContextProvider from "./context/DataContext"; import Login from "./pages/Login"; import SignUp from "./pages/SignUp"; -import { getAuth, onAuthStateChanged } from "firebase/auth"; +import NoticePage from "./pages/NoticePage"; +import EmployeePage from "./pages/EmployeePage"; function App() { - const [employees, setEmployees] = useState([]); - const [loginUser, setLoginUser] = useState(null); - - useEffect(() => { - const unsubscribe = onAuthStateChanged(getAuth(), (user) => { - if (user) { - const loginUser = employees.find( - (employee) => employee.email === user.email - ); - setLoginUser(loginUser); - } else { - setLoginUser(null); - } - }); - - return () => unsubscribe(); - }, [employees]); - - useEffect(() => { - client - .fetch( - `*[_type == "employee"]{ - ..., - "id": _id, - "image": image.asset->url - }` - ) - .then((data) => setEmployees(data)) - .catch(console.error); - }, []); - return ( - +
-
- +
} /> - } /> - } /> } /> } /> + } /> + } />
- + ); } diff --git a/src/components/AbsenceEmployees.jsx b/src/components/AbsenceEmployees.jsx new file mode 100644 index 0000000..fa5bb28 --- /dev/null +++ b/src/components/AbsenceEmployees.jsx @@ -0,0 +1,127 @@ +import { useContext, useState } from "react"; +import { DataContext } from "../context/DataContext"; +import EmployeeCard from "./EmployeeCard"; +import { EMPLOYEE_SKELETON_ARRAY } from "../data/skeleton"; +import SkeletonEmployeeCard from "./skeleton/SkeletonEmployeeCard"; + +export default function AbsenceEmployees() { + const [reason, setReason] = useState("전체"); + const [currentPage, setCurrentPage] = useState(1); + const { employees } = useContext(DataContext); + + if (!employees) { + return
Loading...
; + } + + {/* 만약 employees가 없으면 로딩 중임을 나타내는 화면을 반환 */} + const absenceEmployees = employees.filter( + (employee) => employee.isWorking === false + ); + + function getFilteredEmployees() { + switch (reason) { + case "전체": + return absenceEmployees; + case "미기입": + return absenceEmployees.filter( + (employee) => !employee.reasonForAbsence + ); + default: + return absenceEmployees.filter( + (employee) => employee.reasonForAbsence === reason + ); + } + } + + // 페이지당 표시할 항목 수 + const itemsPerPage = 8; + + // 전체 필터된 직원 목록 + const filteredEmployees = getFilteredEmployees(); + + // 현재 페이지의 데이터 추출 + const indexOfLastEmployee = currentPage * itemsPerPage; + const indexOfFirstEmployee = indexOfLastEmployee - itemsPerPage; + const currentEmployees = filteredEmployees.slice( + indexOfFirstEmployee, + indexOfLastEmployee + ); + + // 페이지 변경 핸들러 + function handlePageChange(pageNumber) { + setCurrentPage(pageNumber); + } + + function handleChange(e) { + setReason(e.target.value); + setCurrentPage(1); // 필터 변경 시 페이지를 첫 페이지로 리셋 + } + + const isLoading = employees.length === 0; + + if (isLoading) { + return ( +
+

+ not working now ! +

+
+ +
+
    + {EMPLOYEE_SKELETON_ARRAY.map((i) => ( + + ))} +
+
+ ); + } + + return ( +
+

+ not working now ! +

+
+ +
+
    + {currentEmployees.map((employee) => ( + + ))} +
+ {/* 페이지네이션 컴포넌트 */} +
+ {Array.from({ length: Math.ceil(filteredEmployees.length / itemsPerPage) }).map((_, index) => ( + + ))} +
+
+ ); +} diff --git a/src/components/EmployeeCard.jsx b/src/components/EmployeeCard.jsx new file mode 100644 index 0000000..61f4e2a --- /dev/null +++ b/src/components/EmployeeCard.jsx @@ -0,0 +1,46 @@ +import { Link } from "react-router-dom"; + +export default function EmployeeCard({ employee }) { + const { + name, + email, + age, + department, + image, + isWorking, + workingHours, + reasonForAbsence, + id, + } = employee; + + const isReason = isWorking || reasonForAbsence; + + return ( +
  • + + +
    +

    {name}

    +

    {email}

    +

    {age}

    +

    {department}

    +

    {workingHours}

    + {!isReason ? ( +
    + ) : ( +

    + {reasonForAbsence} +

    + )} +
    + +
  • + ); +} diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 47e53d7..0de982e 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,33 +1,118 @@ -import { Link } from "react-router-dom"; -import { useContext } from "react"; -import { EmployeeContext } from "../context/EmployeeContext"; +import { Link, useNavigate } from "react-router-dom"; +import { getAuth, signOut } from "firebase/auth"; +import { useContext, useRef, useState } from "react"; +import Modal from "./Modal"; +import Timer from "./Timer"; +import { DataContext } from "../context/DataContext"; +import { updateEmployee } from "../sanity/employee"; +import Toggle from "./Toggle"; +import { buttonStyle } from "../style/button"; +import symbol from "/symbol.png"; export default function Header() { - const { loginUser } = useContext(EmployeeContext); + const { loginUser } = useContext(DataContext); + + const [dropdownIsOpen, setDropdownIsOpen] = useState(false); + + const navigate = useNavigate(); + + const modalRef = useRef(); + + function openModal() { + modalRef.current.open(); + } + + function signOutHandler() { + const auth = getAuth(); + signOut(auth) + .then(() => { + navigate("/login"); + }) + .catch((error) => { + console.log(error); + }); + } + + const handleChange = () => { + updateEmployee(loginUser.id, "isWorking", !loginUser.isWorking); + }; return ( -
    - - Logo +
    + + intranet five symbol +

    intranet five

    - - {!loginUser ? ( - + {loginUser && ( +
    + +
    setDropdownIsOpen(!dropdownIsOpen)} + > +
    +

    {loginUser?.name}

    + {`${loginUser.name}님의 +
    +
    + + 마이페이지 + + +
    +
    +
    + )} + {!loginUser && ( + Login - ) : ( -
    -

    현재 시간(컴포넌트화)

    - -

    {loginUser?.name}

    - demo-icon - -
    )} + +
    +

    + {loginUser?.department} +

    + {`${loginUser?.name}의 + +

    + {loginUser?.name}님은 현재{" "} + {loginUser?.isWorking ? "근무중" : "부재중"} + 입니다. +

    + +
    +
    ); } diff --git a/src/components/ImageUpload.jsx b/src/components/ImageUpload.jsx new file mode 100644 index 0000000..75c9496 --- /dev/null +++ b/src/components/ImageUpload.jsx @@ -0,0 +1,31 @@ +export default function ImageUpload({ message, handleChange, file }) { + return ( +
    +

    Image

    + + + {message && ( +

    + {message} +

    + )} +
    + ); +} diff --git a/src/components/Input.jsx b/src/components/Input.jsx new file mode 100644 index 0000000..0e584b6 --- /dev/null +++ b/src/components/Input.jsx @@ -0,0 +1,25 @@ +import { forwardRef } from "react"; + +const Input = forwardRef(function Input( + { message = "", label, type = "text" }, + ref +) { + return ( +
    +

    {label}

    + + {message && ( +

    + {message} +

    + )} +
    + ); +}); + +export default Input; diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx new file mode 100644 index 0000000..798e564 --- /dev/null +++ b/src/components/Modal.jsx @@ -0,0 +1,34 @@ +import { useRef, forwardRef, useImperativeHandle } from "react"; +import { createPortal } from "react-dom"; + +const Modal = forwardRef(function Modal({ children }, ref) { + useImperativeHandle(ref, () => { + return { + open() { + dialog.current.showModal(); + }, + }; + }); + + const dialog = useRef(); + + function closeModal(e) { + if (dialog.current === e.target) { + dialog.current.close(); + } + } + + return createPortal( + + {children} + , + document.getElementById("modal") + ); +}); + +export default Modal; diff --git a/src/components/NoticeCard.jsx b/src/components/NoticeCard.jsx new file mode 100644 index 0000000..0046c26 --- /dev/null +++ b/src/components/NoticeCard.jsx @@ -0,0 +1,20 @@ +import { Link } from "react-router-dom"; + +export default function NoticeCard({ notice }) { + const { title, thumbnail, id } = notice; + + return ( +
    + +
    + {`${title} +
    +

    {title}

    + +
    + ); +} diff --git a/src/components/NoticeGallery.jsx b/src/components/NoticeGallery.jsx new file mode 100644 index 0000000..b4892db --- /dev/null +++ b/src/components/NoticeGallery.jsx @@ -0,0 +1,51 @@ +import { useContext } from "react"; +import Slider from "react-slick"; +import "slick-carousel/slick/slick.css"; +import "slick-carousel/slick/slick-theme.css"; +import NoticeCard from "./NoticeCard"; +import { DataContext } from "../context/DataContext"; +import SkeletonNoticeCard from "./skeleton/SkeletonNoticeCard"; +import { NOTICE_SKELETON_ARRAY } from "../data/skeleton"; + +
    +export default function NoticeGallery() { + const { notices } = useContext(DataContext); + + const isLoading = notices.length === 0; + + const settings = { + infinite: true, + speed: 500, + slidesToShow: 4, + slidesToScroll: 1, + }; + + if (isLoading) { + return ( +
    +

    + notice gallery +

    +
    + {NOTICE_SKELETON_ARRAY.map((i) => ( + + ))} +
    +
    + ); + } + + return ( +
    +

    + notice gallery +

    + {/* slick 라이브러리 로직 */} + + {notices.map((notice) => ( + + ))} + +
    + ); +} diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 8a0592a..1a9ef16 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -26,7 +26,7 @@ export default function Sidebar() {

    페이지

    • - 직원 관리 + 직원 관
    • page2 @@ -53,4 +53,4 @@ export default function Sidebar() {
    ); -} +} \ No newline at end of file diff --git a/src/components/TimePicker.jsx b/src/components/TimePicker.jsx new file mode 100644 index 0000000..d77e84c --- /dev/null +++ b/src/components/TimePicker.jsx @@ -0,0 +1,26 @@ +import { useRef } from "react"; + +export default function TimePicker({ label, timeChangeHandler, pickTime }) { + const timeRef = useRef(); + return ( +
    +
    timeRef.current.showPicker()} + className="flex justify-between items-center border-[1px] border-slate-400/30 rounded-md h-[34px] px-4 w-[200px] cursor-pointer hover:bg-white/10" + > + +

    {pickTime}

    + {!pickTime && ( +

    시간을 선택하세요.

    + )} +
    + timeChangeHandler(label.toLowerCase(), e)} + required + /> +
    + ); +} diff --git a/src/components/Timer.jsx b/src/components/Timer.jsx new file mode 100644 index 0000000..52d9cf3 --- /dev/null +++ b/src/components/Timer.jsx @@ -0,0 +1,27 @@ +import { useState, useEffect } from "react"; +export default function Timer({ openModal, ...props }) { + const [currentDateTime, setCurrentDateTime] = useState(new Date()); + useEffect(() => { + // 1초마다 현재 날짜 및 시간을 업데이트 + const intervalId = setInterval(() => { + setCurrentDateTime(new Date()); + }, 1000); + // 컴포넌트가 언마운트될 때 clearInterval을 통해 interval 정리 + return () => clearInterval(intervalId); + }, []); // 두 번째 매개변수에 빈 배열을 전달하여 최초 렌더링 시에만 useEffect가 실행되도록 함 + const daysOfWeek = ["일", "월", "화", "수", "목", "금", "토"]; + const dayOfWeek = daysOfWeek[currentDateTime.getDay()]; // 현재 날짜의 요일을 가져옴 + const year = currentDateTime.getFullYear(); + const hours = currentDateTime.getHours(); + const minutes = currentDateTime.getMinutes(); + // const seconds = currentDateTime.getSeconds(); + const ampm = hours >= 12 ? "오후" : "오전"; + const formattedHours = hours % 12 || 12; + const formattedDateTime = `${hours}: ${minutes}`; + + return ( +

    + {formattedDateTime} ({dayOfWeek}) +

    + ); +} diff --git a/src/components/Toggle.jsx b/src/components/Toggle.jsx new file mode 100644 index 0000000..4d322a6 --- /dev/null +++ b/src/components/Toggle.jsx @@ -0,0 +1,21 @@ +function Toggle({ isChecked, onChange }) { + return ( +
    + + +
    + ); +} + +export default Toggle; diff --git a/src/components/WorkingEmployees.jsx b/src/components/WorkingEmployees.jsx new file mode 100644 index 0000000..935e36a --- /dev/null +++ b/src/components/WorkingEmployees.jsx @@ -0,0 +1,68 @@ +import { useContext, useState } from "react"; +import { DataContext } from "../context/DataContext"; +import EmployeeCard from "./EmployeeCard"; +import SkeletonEmployeeCard from "./skeleton/SkeletonEmployeeCard"; +import { EMPLOYEE_SKELETON_ARRAY } from "../data/skeleton"; + +export default function WorkingEmployees() { + const { employees } = useContext(DataContext); + + const isLoading = employees.length === 0; + + const itemsPerPage = 8; // 한 페이지에 보여질 항목 수 + const [currentPage, setCurrentPage] = useState(1); + + const workingEmployees = employees.filter( + (employee) => employee.isWorking === true + ); + + // 페이지 변경 이벤트 핸들러 + const handlePageChange = (newPage) => { + setCurrentPage(newPage); + }; + + // 현재 페이지에 표시할 항목 계산 + const indexOfLastItem = currentPage * itemsPerPage; + const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const currentItems = workingEmployees.slice(indexOfFirstItem, indexOfLastItem); + if (isLoading) { + return ( +
    +

    + working now ! +

    +
      + {EMPLOYEE_SKELETON_ARRAY.map((i) => ( + + ))} +
    +
    + ); + } + + return ( +
    +

    + working now ! +

    +
      + {currentItems.map((employee) => ( + + ))} +
    + + {/* 페이지네이션 UI */} +
    + {Array.from({ length: Math.ceil(workingEmployees.length / itemsPerPage) }, (_, index) => ( + + ))} +
    +
    + ); +} diff --git a/src/components/skeleton/SkeletonEmployeeCard.jsx b/src/components/skeleton/SkeletonEmployeeCard.jsx new file mode 100644 index 0000000..b30033f --- /dev/null +++ b/src/components/skeleton/SkeletonEmployeeCard.jsx @@ -0,0 +1,17 @@ +export default function SkeletonEmployeeCard() { + return ( +
  • +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
  • + ); +} diff --git a/src/components/skeleton/SkeletonNoticeCard.jsx b/src/components/skeleton/SkeletonNoticeCard.jsx new file mode 100644 index 0000000..6d0b62b --- /dev/null +++ b/src/components/skeleton/SkeletonNoticeCard.jsx @@ -0,0 +1,10 @@ +export default function SkeletonNoticeCard() { + return ( +
    +
    +
    +
    +
    +
    + ); +} diff --git a/src/context/DataContext.jsx b/src/context/DataContext.jsx new file mode 100644 index 0000000..a8f2496 --- /dev/null +++ b/src/context/DataContext.jsx @@ -0,0 +1,107 @@ +import { createContext } from "react"; +import { useEffect, useState } from "react"; +import client, { urlFor } from "../sanity/client"; +import { getAuth, onAuthStateChanged } from "firebase/auth"; + +export const DataContext = createContext(null); + +export default function DataContextProvider({ children }) { + const [employees, setEmployees] = useState([]); + const [notices, setNotices] = useState([]); + const [loginUser, setLoginUser] = useState(null); + + useEffect(() => { + const unsubscribe = onAuthStateChanged(getAuth(), (user) => { + if (user) { + const loginUser = employees.find( + (employee) => employee.email === user.email + ); + setLoginUser(loginUser); + } else { + setLoginUser(null); + } + }); + + return () => unsubscribe(); + }, [employees]); + + useEffect(() => { + const fetchData = async () => { + try { + const data = await client.fetch( + `*[_type == "employee"]{ + ..., + "id": _id, + "image": image.asset->url + }` + ); + setEmployees(data); + } catch (error) { + console.error("Error fetching initial data:", error); + } + }; + + fetchData(); + + const query = `*[_type == "employee"]`; + const subscription = client.listen(query).subscribe((update) => { + if (update.transition === "disappear") { + setEmployees((prevEmployees) => { + const updatedEmployees = prevEmployees.filter( + (employee) => employee.id !== update.documentId + ); + return updatedEmployees; + }); + return; + } + + const updatedEmployee = update.result; + const transformedEmployee = { + ...updatedEmployee, + id: updatedEmployee._id, + image: urlFor(updatedEmployee.image).url(), + }; + + if (update.transition === "appear") { + setEmployees((prevEmployees) => { + return [...prevEmployees, transformedEmployee]; + }); + } + + if (update.transition === "update") { + setEmployees((prevEmployees) => { + const existingEmployeeIndex = prevEmployees.findIndex( + (employee) => employee.id === transformedEmployee.id + ); + prevEmployees.splice(existingEmployeeIndex, 1, transformedEmployee); + return [...prevEmployees]; + }); + } + }); + + return () => { + subscription.unsubscribe(); + }; + }, []); + + useEffect(() => { + client + .fetch( + `*[_type == "notice"]{ + ..., + "id": _id, + "thumbnail": thumbnail.asset->url, + "createdAt": _createdAt, + "updatedAt": _updatedAt, + }` + ) + .then((data) => setNotices(data)) + .catch(console.error); + }, []); + + const dataValue = { employees, notices, loginUser }; + + return ( + {children} + ); +} diff --git a/src/context/EmployeeContext.js b/src/context/EmployeeContext.js deleted file mode 100644 index be14933..0000000 --- a/src/context/EmployeeContext.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from "react"; - -export const EmployeeContext = createContext(null); diff --git a/src/data/Introduction.js b/src/data/Introduction.js new file mode 100644 index 0000000..23c93b2 --- /dev/null +++ b/src/data/Introduction.js @@ -0,0 +1,5 @@ +export const introduction = { + developer : `"코드로 세상을 더 나은 곳으로 만들자!" 사용자 경험을 개선하고 웹 애플리케이션을 만드는 것을 사랑합니다. 기술적 역량을 활용하여 혁신적인 솔류션을 창조하고 사용자 경험을 개선하는데 노력합니다.`, + designer : `"디자인은 감동을 전달합니다!" 사용자 중심의 디자인을 통해 쉽고 효과적인 사용자 경험을 제공합니다. 사용자 연구와 디자인 시스템을 통해 제품을 최적화하고자 노력합니다.`, + planner : `"목표 달성을 위한 프로젝트 경영!" 프로젝트 일정과 예산을 관리하며 팀원 간의 원활한 협력을 조율합니다. 고객의 요구를 이해하고 프로젝트 목표를 달성하기 위해 끊임없이 노력합니다.` +} \ No newline at end of file diff --git a/src/data/skeleton.js b/src/data/skeleton.js new file mode 100644 index 0000000..e35d806 --- /dev/null +++ b/src/data/skeleton.js @@ -0,0 +1,3 @@ +export const NOTICE_SKELETON_ARRAY = [1, 2, 3, 4]; + +export const EMPLOYEE_SKELETON_ARRAY = [1, 2, 3, 4, 5, 6, 7, 8]; diff --git a/src/index.css b/src/index.css index 1864921..3ff3256 100644 --- a/src/index.css +++ b/src/index.css @@ -3,5 +3,10 @@ @tailwind utilities; body { - background-color: #eff6ff; + background-color: #030a13; + background-image: url("/background-image.jpeg"); + background-repeat: no-repeat; + background-size: cover; + background-position: center; + } diff --git a/src/pages/EmployeePage.jsx b/src/pages/EmployeePage.jsx new file mode 100644 index 0000000..031e15a --- /dev/null +++ b/src/pages/EmployeePage.jsx @@ -0,0 +1,79 @@ +import { useContext } from "react"; +import { DataContext } from "../context/DataContext"; +import { useNavigate, useParams } from "react-router-dom"; +import { deleteUser, getAuth } from "firebase/auth"; +import { deleteEmployee } from "../sanity/employee"; +import { introduction } from "../data/Introduction"; +import { deleteButtonStyle } from "../style/button"; +export default function EmployeePage() { + const { employees, loginUser } = useContext(DataContext); + const params = useParams(); + const navigater = useNavigate(); + const employee = employees.find((employee) => employee.id === params.id); + const auth = getAuth(); + const user = auth.currentUser; + + if (!employee) { + return

    loading...

    ; + } + + function employeeIntroduction() { + if (employee.department === "developer") { + return introduction.developer; + } else if (employee.department === "designer") { + return introduction.designer; + } else if (employee.department === "planner") { + return introduction.planner; + } + } + + function deleteHandler() { + deleteUser(user) + .then(() => { + deleteEmployee(loginUser.id); + navigater("/"); + }) + .catch((error) => { + console.error(error); + }); + } + + return ( +
    +
    + {`${employee.name}님의 +
    +
    +

    {employee.name}

    +

    age : {employee.age}

    +

    + {employee.department} +

    +

    {employeeIntroduction()}

    +
    +
    +
    +

    + Working hours :
    + {employee.workingHours} +

    +
    + +
    +

    {employee.email}

    +
    +
    + {employee?.id === loginUser?.id && ( + + )} +
    +
    +
    + ); +} diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx index c3b8bd4..5f79219 100644 --- a/src/pages/HomePage.jsx +++ b/src/pages/HomePage.jsx @@ -1,16 +1,14 @@ +import AbsenceEmployees from "../components/AbsenceEmployees"; +import NoticeGallery from "../components/NoticeGallery"; +import WorkingEmployees from "../components/WorkingEmployees"; + export default function HomePage() { return ( -
    -
    - 기업 공지 모음 갤러리 -
    -
    - 근무중인 전 직원 조회 -
    -
    - 부재중인 직원 중 부재 항목에 따른 카테고리 메뉴로 데이터 필터링 기능 - 구현 -
    +
    + + + + {/*
    */}
    ); } diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 25712ab..47c3ed2 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,59 +1,86 @@ -import { useRef } from "react"; +import { useContext, useRef, useState } from "react"; import { getAuth, signInWithEmailAndPassword } from "firebase/auth"; import { Link, useNavigate } from "react-router-dom"; +import Input from "../components/Input"; +import { DataContext } from "../context/DataContext"; +import { buttonStyle } from "../style/button"; export default function Login() { + const { employees } = useContext(DataContext); + + const [errorMessage, setErrorMessage] = useState({ + emailMessage: "", + passwordMessage: "", + }); + const emailRef = useRef(); const passwordRef = useRef(); const navigate = useNavigate(); - function signInHandler() { + function signInHandler(e) { + e.preventDefault(); + const auth = getAuth(); - signInWithEmailAndPassword( - auth, - emailRef.current.value, - passwordRef.current.value - ) + + const email = emailRef.current.value; + const password = passwordRef.current.value; + + if (!employees.some((employee) => employee.email === email)) { + setErrorMessage((prev) => { + return { ...prev, emailMessage: "존재하는 직원 이메일이 아닙니다." }; + }); + return; + } + + signInWithEmailAndPassword(auth, email, password) .then(() => { navigate("/"); }) .catch((error) => { + setErrorMessage({ + emailMessage: "", + passwordMessage: "비밀번호가 틀립니다.", + }); console.log(error); }); } const contentHeight = `${window.innerHeight - 80}px`; - const inputStyle = "border-[1px] border-gray-200 w-full rounded-md py-1 px-2"; - return (
    -
    -

    Welcome to Intranet Five !

    -
    -

    email

    - -
    -
    -

    password

    - -
    +
    +

    + Welcome to Intranet Five ! +

    + + 아직 직원등록을 안하셨나요 ? - -
    +
    ); } diff --git a/src/pages/Management.jsx b/src/pages/Management.jsx deleted file mode 100644 index 2650e5e..0000000 --- a/src/pages/Management.jsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Management() { - return

    management

    ; -} diff --git a/src/pages/MyPage.jsx b/src/pages/MyPage.jsx deleted file mode 100644 index a803b85..0000000 --- a/src/pages/MyPage.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useContext } from "react"; -import { EmployeeContext } from "../context/EmployeeContext"; - -export default function MyPage() { - const { loginUser } = useContext(EmployeeContext); - - return ( -
    - -
    - ); -} diff --git a/src/pages/NoticePage.jsx b/src/pages/NoticePage.jsx new file mode 100644 index 0000000..d0f006b --- /dev/null +++ b/src/pages/NoticePage.jsx @@ -0,0 +1,50 @@ +import { useContext } from "react"; +import { useParams } from "react-router"; +import { DataContext } from "../context/DataContext"; + +export default function NoticePage() { + const { id } = useParams(); + const { notices } = useContext(DataContext); + const notice = notices.find((notice) => notice.id === id); + + if (!notice) { + return
    Loading...
    ; + } + const { title, description, thumbnail, createdAt, updatedAt } = notice; + + function dateFormatter(date) { + const utcDate = new Date(date); + + const koreaDateTimeFormat = new Intl.DateTimeFormat("ko-KR", { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + timeZone: "Asia/Seoul", + }); + + return koreaDateTimeFormat.format(utcDate); + } + + return ( +
    +
    + {`${title} +
    +

    {title}

    +

    {description}

    +
    +

    작성일 : {dateFormatter(createdAt)}

    +

    수정일 : {dateFormatter(updatedAt)}

    +
    +
    +
    +
    + ); +} diff --git a/src/pages/SignUp.jsx b/src/pages/SignUp.jsx index 0f20081..70b82a0 100644 --- a/src/pages/SignUp.jsx +++ b/src/pages/SignUp.jsx @@ -1,34 +1,78 @@ -import { useRef, useState } from "react"; -import { - getAuth, - createUserWithEmailAndPassword, - signOut, -} from "firebase/auth"; +import { useContext, useRef, useState } from "react"; +import { getAuth, createUserWithEmailAndPassword } from "firebase/auth"; import { useNavigate } from "react-router-dom"; import { addEmployee } from "../sanity/employee"; +import Input from "../components/Input"; +import ImageUpload from "../components/ImageUpload"; +import { DataContext } from "../context/DataContext"; +import { buttonStyle } from "../style/button"; +import TimePicker from "../components/TimePicker"; export default function SignUp() { + const { employees } = useContext(DataContext); const [file, setFile] = useState(); + const [workingHours, setWorkingHours] = useState({}); + const [errorMessage, setErrorMessage] = useState({ + imageMessage: "", + emailMessage: "", + passwordMessage: "", + }); const emailRef = useRef(); const passwordRef = useRef(); const nameRef = useRef(); const ageRef = useRef(); const departmentRef = useRef(); - const startTimeRef = useRef(); - const endTimeRef = useRef(); const navigate = useNavigate(); - function signInHandler() { + function signInHandler(e) { + e.preventDefault(); + const auth = getAuth(); const email = emailRef.current.value; const password = passwordRef.current.value; const name = nameRef.current.value; const age = ageRef.current.value; const department = departmentRef.current.value; - const startTime = startTimeRef.current.value; - const endTime = endTimeRef.current.value; + const startTime = workingHours.start; + const endTime = workingHours.end; + + if (!file) { + setErrorMessage((prev) => { + return { ...prev, imageMessage: "사진을 추가해주세요." }; + }); + return; + } + + if (employees.some((employee) => email === employee.email)) { + setErrorMessage((prev) => { + return { ...prev, emailMessage: "이미 존재하는 이메일입니다." }; + }); + return; + } + + if (password.length < 6) { + setErrorMessage((prev) => { + return { + ...prev, + passwordMessage: "비밀번호는 6자리 이상이어야 합니다.", + }; + }); + return; + } + + const specialCharacters = /[!@#$%^&*(),.?":{}|<>]/; + + if (specialCharacters.test(password)) { + setErrorMessage((prev) => { + return { + ...prev, + passwordMessage: "비밀번호에 특수문자를 포함할 수 없습니다.", + }; + }); + return; + } const userData = { name, @@ -42,12 +86,13 @@ export default function SignUp() { createUserWithEmailAndPassword(auth, email, password) .then(() => { addEmployee(userData); - signOut(auth); // createUserWithEmailAndPassword함수가 동작하면 회원가입 후 자동으로 로그인함. 이를 막기위해 임시로 signOut함수사용 + navigate("/"); }) .catch((error) => { console.log(error); + console.log(error.code); + console.log(error.message); }); - navigate("/login"); } const handleChange = (e) => { @@ -55,84 +100,80 @@ export default function SignUp() { const files = e.target.files; if (files && files[0]) { setFile(files[0]); + setErrorMessage((prev) => { + return { ...prev, imageMessage: "" }; + }); } }; - const labelStyle = "mb-2 text-gray-400"; - const inputStyle = "border-[1px] border-gray-200 w-full rounded-md py-1 px-2"; + const timeChangeHandler = (type, e) => { + setWorkingHours((prev) => { + return { ...prev, [type]: e.target.value }; + }); + }; + + const contentHeight = `${window.innerHeight - 80}px`; return ( -
    -
    -

    직원 등록

    -
    +
    +
    +

    + 직원 등록 +

    +
    + + + + +
    -

    Image

    - -
    -
    -

    Password

    - -
    -
    -

    Name

    - -
    -
    -

    Age

    - -
    -
    -

    Department

    -
    -

    WorkingHours

    +

    WorkingHours

    -
    - - -
    -
    - - -
    + +
    - -
    +
    ); diff --git a/src/sanity/client.js b/src/sanity/client.js index e775654..2f1903e 100644 --- a/src/sanity/client.js +++ b/src/sanity/client.js @@ -1,4 +1,5 @@ import { createClient } from "@sanity/client"; +import imageUrlBuilder from "@sanity/image-url"; const client = createClient({ projectId: "y3b22irq", @@ -10,4 +11,10 @@ const client = createClient({ ignoreBrowserTokenWarning: true, }); +const builder = imageUrlBuilder(client); + +export function urlFor(source) { + return builder.image(source); +} + export default client; diff --git a/src/sanity/employee.js b/src/sanity/employee.js index d51bb1c..8f519fa 100644 --- a/src/sanity/employee.js +++ b/src/sanity/employee.js @@ -29,8 +29,21 @@ export async function addEmployee({ age, department, image: { asset: { _ref: result.document._id } }, - isWorking: false, + isWorking: true, workingHours, + reasonForAbsence: "", }); }); } + +export async function updateEmployee(id, key, value) { + return client + .patch(id) + .set({ [key]: value }) + .commit() + .catch((error) => console.log(error)); +} + +export async function deleteEmployee(id) { + return client.delete(id).catch((error) => console.log(error)); +} diff --git a/src/style/button.js b/src/style/button.js new file mode 100644 index 0000000..a7691d8 --- /dev/null +++ b/src/style/button.js @@ -0,0 +1,5 @@ +export const buttonStyle = + "text-center border-[1px] border-slate-400/30 text-slate-300 p-2 rounded-lg hover:border-slate-400/50 hover:bg-white/10 transition"; + +export const deleteButtonStyle = + "text-center border-[2px] border-slate-400/30 text-slate-300 p-2 rounded-lg hover:text-red-500 hover:border-red-500 hover:bg-red-500/30 transition"; diff --git a/toy-project-1/schemas/employee.js b/toy-project-1/schemas/employee.js index d7343fe..699ef3e 100644 --- a/toy-project-1/schemas/employee.js +++ b/toy-project-1/schemas/employee.js @@ -38,6 +38,11 @@ export default { name: 'workingHours', type: 'string', }, + { + title: 'ReasonForAbsence', + name: 'reasonForAbsence', + type: 'string', + }, ], preview: { select: {title: 'name', media: 'image'}, diff --git a/toy-project-1/schemas/index.js b/toy-project-1/schemas/index.js index bf5cfc4..66cfdf8 100644 --- a/toy-project-1/schemas/index.js +++ b/toy-project-1/schemas/index.js @@ -1,3 +1,4 @@ import employee from './employee' +import notice from './notice' -export const schemaTypes = [employee] +export const schemaTypes = [employee, notice] diff --git a/toy-project-1/schemas/notice.js b/toy-project-1/schemas/notice.js new file mode 100644 index 0000000..f6c5be5 --- /dev/null +++ b/toy-project-1/schemas/notice.js @@ -0,0 +1,25 @@ +export default { + title: 'Notice', + name: 'notice', + type: 'document', + fields: [ + { + title: 'Title', + name: 'title', + type: 'string', + }, + { + title: 'Description', + name: 'description', + type: 'text', + }, + { + title: 'Thumbnail', + name: 'thumbnail', + type: 'image', + }, + ], + preview: { + select: {title: 'title', media: 'thumbnail'}, + }, +}