diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..dc27f9b --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + { + "associatedIndex": 3 +} + + + + { + "keyToString": { + "RunOnceActivity.ShowReadmeOnStart": "true", + "codeWithMe.voiceChat.enabledByDefault": "false", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.standard": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.standard": "", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "prettierjs.PrettierConfiguration.Package": "/Users/seoseong-won/Desktop/WABI/WABI-FE/client/node_modules/prettier", + "settings.editor.selected.configurable": "settings.javascript.prettier", + "ts.external.directory.path": "/Users/seoseong-won/Desktop/WABI/WABI-FE/client/node_modules/typescript/lib", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + 1725688472767 + + + + + + \ No newline at end of file diff --git a/client/.prettierrc b/client/.prettierrc new file mode 100644 index 0000000..3f8fc2c --- /dev/null +++ b/client/.prettierrc @@ -0,0 +1,25 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": false, + "endOfLine": "auto", + "htmlWhitespaceSensitivity": "css", + "jsxBracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false, + "vueIndentScriptAndStyle": true, + "overrides": [ + { + "files": "*.json", + "options": { + "printWidth": 200 + } + } + ] +} diff --git a/client/eslint.config.mjs b/client/eslint.config.mjs new file mode 100644 index 0000000..c692093 --- /dev/null +++ b/client/eslint.config.mjs @@ -0,0 +1,13 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; +import pluginReact from "eslint-plugin-react"; + + +export default [ + {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]}, + {languageOptions: { globals: globals.browser }}, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + pluginReact.configs.flat.recommended, +]; \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 1e79994..e73a580 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -15,11 +15,25 @@ "@types/node": "^16.18.104", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "axios": "^1.7.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-qr-scanner": "^1.0.0-alpha.11", + "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "recoil": "^0.7.7", + "styled-components": "^6.1.12", "typescript": "^4.9.5", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/styled-components": "^5.1.34", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.35.0", + "globals": "^15.9.0", + "prettier": "3.3.3", + "typescript-eslint": "^8.3.0" } }, "node_modules/@adobe/css-tools": { @@ -1051,6 +1065,14 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", @@ -1997,6 +2019,14 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { "version": "7.25.2", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", @@ -2285,6 +2315,24 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2371,11 +2419,12 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz", + "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==", + "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@humanwhocodes/config-array": { @@ -3287,6 +3336,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.2.tgz", + "integrity": "sha512-baiMx18+IMuD1yyvOGaHM9QrVUPGGG0jC+z+IPHnRJWUAUvaKuWKyE8gjDj2rzv3sz9zOGoRSPgeBVHRhZnBlA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -4044,6 +4101,16 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dev": true, + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -4225,6 +4292,22 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/styled-components": { + "version": "5.1.34", + "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", + "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", + "dev": true, + "dependencies": { + "@types/hoist-non-react-statics": "*", + "@types/react": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/stylis": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -4623,6 +4706,26 @@ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" }, + "node_modules/@zxing/library": { + "version": "0.19.3", + "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.19.3.tgz", + "integrity": "sha512-RUv5svewpDoD0ymXleOP8yVTO5BLkR0zn5coGC/Vs1671u0OBJ4xdtR8WVWf08OcvrieEMHdSfQY3ZKtqII/hg==", + "dependencies": { + "ts-custom-error": "^3.2.1" + }, + "engines": { + "node": ">= 10.4.0" + }, + "optionalDependencies": { + "@zxing/text-encoding": "~0.9.0" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "optional": true + }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -5140,6 +5243,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -5688,6 +5814,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6115,6 +6249,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6256,6 +6398,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -7304,9 +7456,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.2.tgz", + "integrity": "sha512-3XnC5fDyc8M4J2E8pt8pmSVRX2M+5yWMCfI/kDZwauQeFgzQOuhcRBFKjTeJagqgk4sFKxe1mvNVnaWwImx/Tg==", "dependencies": { "debug": "^3.2.7" }, @@ -7629,6 +7781,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -8699,11 +8859,15 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", + "integrity": "sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==", + "dev": true, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -8775,6 +8939,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -8867,6 +9036,21 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/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 + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -14257,6 +14441,21 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -14361,6 +14560,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -14707,6 +14911,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-qr-scanner": { + "version": "1.0.0-alpha.11", + "resolved": "https://registry.npmjs.org/react-qr-scanner/-/react-qr-scanner-1.0.0-alpha.11.tgz", + "integrity": "sha512-TdaygL+4U9iapskJgK5Ilb1Cw4wwzQ3bVpIfzzbrEsxPaEJzmjQ7VuMz8E9hBQGCU4Ee+YtgglE3byEtgtRpkA==", + "dependencies": { + "@zxing/library": "^0.19.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -14715,6 +14932,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.2.tgz", + "integrity": "sha512-tvN1iuT03kHgOFnLPfLJ8V95eijteveqdOSk+srqfePtQvqCExB8eHOYnlilbOcyJyKnYkr1vJvf7YqotAJu1A==", + "dependencies": { + "@remix-run/router": "1.19.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.2.tgz", + "integrity": "sha512-z7YkaEW0Dy35T3/QKPYB1LjMK2R1fxnHO8kWpUMTBdfVzZrWOiY9a7CtN8HqdWtDUWd5FY6Dl8HFsqVwH4uOtQ==", + "dependencies": { + "@remix-run/router": "1.19.2", + "react-router": "6.26.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -14819,6 +15066,25 @@ "node": ">=8.10.0" } }, + "node_modules/recoil": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", + "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -15584,6 +15850,11 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16143,6 +16414,65 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.12.tgz", + "integrity": "sha512-n/O4PzRPhbYI0k1vKKayfti3C/IGcPf+DqcrOB7O/ab9x4u/zjqraneT5N45+sIe87cxrCApXM8Bna7NYxwoTA==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", + "css-to-react-native": "3.2.0", + "csstype": "3.1.3", + "postcss": "8.4.38", + "shallowequal": "1.1.0", + "stylis": "4.3.2", + "tslib": "2.6.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/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/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16158,6 +16488,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -16655,6 +16990,26 @@ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-custom-error": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", + "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -16845,6 +17200,235 @@ "node": ">=4.2.0" } }, + "node_modules/typescript-eslint": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.3.0.tgz", + "integrity": "sha512-EvWjwWLwwKDIJuBjk2I6UkV8KEQcwZ0VM10nR1rIunRDIP67QJTZAHBXTX0HW/oI1H10YESF8yWie8fRQxjvFA==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.3.0", + "@typescript-eslint/parser": "8.3.0", + "@typescript-eslint/utils": "8.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.3.0.tgz", + "integrity": "sha512-FLAIn63G5KH+adZosDYiutqkOkYEx0nvcwNNfJAf+c7Ae/H35qWwTYvPZUKFj5AS+WfHG/WJJfWnDnyNUlp8UA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.3.0", + "@typescript-eslint/type-utils": "8.3.0", + "@typescript-eslint/utils": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.3.0.tgz", + "integrity": "sha512-h53RhVyLu6AtpUzVCYLPhZGL5jzTD9fZL+SYf/+hYOx2bDkyQXztXSc4tbvKYHzfMXExMLiL9CWqJmVz6+78IQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.3.0", + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/typescript-estree": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.3.0.tgz", + "integrity": "sha512-mz2X8WcN2nVu5Hodku+IR8GgCOl4C0G/Z1ruaWN4dgec64kDBabuXyPAr+/RgJtumv8EEkqIzf3X2U5DUKB2eg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.3.0.tgz", + "integrity": "sha512-wrV6qh//nLbfXZQoj32EXKmwHf4b7L+xXLrP3FZ0GOUU72gSvLjeWUl5J5Ue5IwRxIV1TfF73j/eaBapxx99Lg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "8.3.0", + "@typescript-eslint/utils": "8.3.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.3.0.tgz", + "integrity": "sha512-y6sSEeK+facMaAyixM36dQ5NVXTnKWunfD1Ft4xraYqxP0lC0POJmIaL/mw72CUMqjY9qfyVfXafMeaUj0noWw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.3.0.tgz", + "integrity": "sha512-Mq7FTHl0R36EmWlCJWojIC1qn/ZWo2YiWYc1XVtasJ7FIgjo0MVv9rZWXEE7IK2CGrtwe1dVOxWwqXUdNgfRCA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/visitor-keys": "8.3.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.3.0.tgz", + "integrity": "sha512-F77WwqxIi/qGkIGOGXNBLV7nykwfjLsdauRB/DOFPdv6LTF3BHHkBpq81/b5iMPSF055oO2BiivDJV4ChvNtXA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.3.0", + "@typescript-eslint/types": "8.3.0", + "@typescript-eslint/typescript-estree": "8.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.3.0.tgz", + "integrity": "sha512-RmZwrTbQ9QveF15m/Cl28n0LXD6ea2CjkhH5rQ55ewz3H24w+AMCJHPVYaZ8/0HoG8Z3cLLFFycRXxeO2tz9FA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.3.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/client/package.json b/client/package.json index 873e1e7..edf63fc 100644 --- a/client/package.json +++ b/client/package.json @@ -10,9 +10,14 @@ "@types/node": "^16.18.104", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", + "axios": "^1.7.4", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-qr-scanner": "^1.0.0-alpha.11", + "react-router-dom": "^6.26.2", "react-scripts": "5.0.1", + "recoil": "^0.7.7", + "styled-components": "^6.1.12", "typescript": "^4.9.5", "web-vitals": "^2.1.4" }, @@ -39,5 +44,14 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "@eslint/js": "^9.9.1", + "@types/styled-components": "^5.1.34", + "eslint": "^8.57.0", + "eslint-plugin-react": "^7.35.0", + "globals": "^15.9.0", + "prettier": "3.3.3", + "typescript-eslint": "^8.3.0" } } diff --git a/client/public/images/checkInDot.png b/client/public/images/checkInDot.png new file mode 100644 index 0000000..422b11c Binary files /dev/null and b/client/public/images/checkInDot.png differ diff --git a/client/public/images/notCheckInDot.png b/client/public/images/notCheckInDot.png new file mode 100644 index 0000000..6e3ab81 Binary files /dev/null and b/client/public/images/notCheckInDot.png differ diff --git a/client/public/images/qrCheckInButton.png b/client/public/images/qrCheckInButton.png new file mode 100644 index 0000000..a6aac5f Binary files /dev/null and b/client/public/images/qrCheckInButton.png differ diff --git a/client/src/App.css b/client/src/App.css index 74b5e05..2a0949a 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -1,5 +1,6 @@ .App { text-align: center; + height: 100vh; } .App-logo { diff --git a/client/src/App.tsx b/client/src/App.tsx index a53698a..20fcfc0 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,26 +1,17 @@ import React from 'react'; -import logo from './logo.svg'; +import {BrowserRouter, Routes, Route} from 'react-router-dom'; import './App.css'; +import MainQrCheckIn from './pages/QrCheckIn/MainQrCheckIn'; +import EventDetail from './pages/EventDetail/EventDetail'; function App() { return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
+ + + } /> + } /> + + ); } - export default App; diff --git a/client/src/api/fileUpload.ts b/client/src/api/fileUpload.ts new file mode 100644 index 0000000..be97b9f --- /dev/null +++ b/client/src/api/fileUpload.ts @@ -0,0 +1,13 @@ +import axios from 'axios'; + +export const fileUpload = (fileFormData : FormData) => { + axios.post('https://13f9350d-c685-4634-9131-e118c99027d6.mock.pstmn.io/api/bands/1/members/enrollments/file',fileFormData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + .then((res) => { + }) + .catch((error) => { + }) +} diff --git a/client/src/api/loadGroupMemberList.ts b/client/src/api/loadGroupMemberList.ts new file mode 100644 index 0000000..c790af8 --- /dev/null +++ b/client/src/api/loadGroupMemberList.ts @@ -0,0 +1,11 @@ +import axios from 'axios'; + +export const loadGroupMemberList = ( + setGroupMembers: React.Dispatch>, +) => { + axios + .get( + `https://13f9350d-c685-4634-9131-e118c99027d6.mock.pstmn.io/api/bands/1/students`, + ) + .then(res => setGroupMembers(res.data.data.students)); +}; diff --git a/client/src/api/manualUpload.ts b/client/src/api/manualUpload.ts new file mode 100644 index 0000000..8cbceee --- /dev/null +++ b/client/src/api/manualUpload.ts @@ -0,0 +1,10 @@ +import axios from 'axios'; +import {students} from "../types/studentTypes"; + +export const manualUpload = (students : students) => { + axios.post('https://13f9350d-c685-4634-9131-e118c99027d6.mock.pstmn.io/api/bands/1/members/enrollments/manual',students) + .then((res) => { + }) + .catch((error) => { + }) +} \ No newline at end of file diff --git a/client/src/api/sendToServer.ts b/client/src/api/sendToServer.ts new file mode 100644 index 0000000..b533399 --- /dev/null +++ b/client/src/api/sendToServer.ts @@ -0,0 +1,19 @@ +import axios, {AxiosResponse} from "axios"; +import {studentQr} from "../types/QrType/StudentQr"; + +interface ServerResponse { + message: string | null; +} + +export const sendToServer = (data: studentQr): Promise> => { + return axios + .post("http://34.47.97.81:8080/api/events/check-in",data) + .then((res) => { + console.log(res); + return res; + }) + .catch((error) => { + console.log(error); + throw error; + }) +} \ No newline at end of file diff --git a/client/src/components/GroupDetail/FileUploadModal/FileUploadModal.tsx b/client/src/components/GroupDetail/FileUploadModal/FileUploadModal.tsx new file mode 100644 index 0000000..b537732 --- /dev/null +++ b/client/src/components/GroupDetail/FileUploadModal/FileUploadModal.tsx @@ -0,0 +1,39 @@ +import React, {useState} from 'react'; +import MemberUpdateButton from '../MemberUpdateButton/MemberUpdateButton'; +import {fileUpload} from '../../../api/fileUpload'; +import ModalFrame from '../../common/ModalFrame/ModalFrame'; + +interface FileUploadModalProps { + modalStateValue: boolean; +} + +const FileUploadModal: React.FC = ({modalStateValue}) => { + const [file, setFile] = useState(null); + + const fileChange = (fileElement: React.ChangeEvent) => { + const files = fileElement.target.files; + if (files) setFile(files[0]); + }; + + const fileUploadClick = () => { + const fileToUpload = file || new Blob(); + const fileFormData = new FormData(); + fileFormData.append('file', fileToUpload); + fileUpload(fileFormData); + }; + + if (modalStateValue === false) return null; + + return ( + +

This is a FileUploadModal!

+
+ +
+ + +
+ ); +}; + +export default FileUploadModal; diff --git a/client/src/components/GroupDetail/ManualUploadModal/ManualUploadModal.tsx b/client/src/components/GroupDetail/ManualUploadModal/ManualUploadModal.tsx new file mode 100644 index 0000000..22c1540 --- /dev/null +++ b/client/src/components/GroupDetail/ManualUploadModal/ManualUploadModal.tsx @@ -0,0 +1,52 @@ +import React, {useState} from 'react'; +import Steps from './steps/Steps'; +import {student} from '../../../types/studentTypes'; +import ModalFrame from '../../common/ModalFrame/ModalFrame'; + +interface FileUploadModalProps { + modalStateValue: boolean; +} + +const ManualUploadModal: React.FC = ({ + modalStateValue, +}) => { + const [step, setStep] = useState(1); + const nextStep = () => setStep(step + 1); + const prevStep = () => setStep(step - 1); + const [student, setStudent] = useState({ + studentId: '', + name: '', + club: '', + position: '', + joinDate: '', + college: '', + major: '', + tel: '', + status: '', + }); + + const handleChange = ( + studentInfoInput: React.ChangeEvent, + ) => { + const {name, value} = studentInfoInput.target; + setStudent(prevStudent => ({ + ...prevStudent, + [name]: value, + })); + }; + if (modalStateValue === false) return null; + + return ( + + + + ); +}; + +export default ManualUploadModal; diff --git a/client/src/components/GroupDetail/ManualUploadModal/steps/OptionalStep.tsx b/client/src/components/GroupDetail/ManualUploadModal/steps/OptionalStep.tsx new file mode 100644 index 0000000..0cee0c8 --- /dev/null +++ b/client/src/components/GroupDetail/ManualUploadModal/steps/OptionalStep.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import {stepProps} from '../../../../types/stepTypes'; +import {manualUpload} from "../../../../api/manualUpload"; + +const OptionalStep = ({prevStep, student, handleChange}: stepProps) => { + return ( +
+

선택 항목 입력

+ 단과대학 + + 학과 + + 소속 동아리 + + 직책 + + 가입일자 + + 재학 유무 + + 휴대폰 번호 + + + + +
+ ) +} + +export default OptionalStep; \ No newline at end of file diff --git a/client/src/components/GroupDetail/ManualUploadModal/steps/RequiredStep.tsx b/client/src/components/GroupDetail/ManualUploadModal/steps/RequiredStep.tsx new file mode 100644 index 0000000..76ac687 --- /dev/null +++ b/client/src/components/GroupDetail/ManualUploadModal/steps/RequiredStep.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import {stepProps} from "../../../../types/stepTypes"; + +const RequiredStep = ({nextStep, student, handleChange}: stepProps) => { + + return ( +
+

필수 항목 입력

+ 학번 + + + 이름 + + +
+ ) +} + +export default RequiredStep; \ No newline at end of file diff --git a/client/src/components/GroupDetail/ManualUploadModal/steps/Steps.tsx b/client/src/components/GroupDetail/ManualUploadModal/steps/Steps.tsx new file mode 100644 index 0000000..222f797 --- /dev/null +++ b/client/src/components/GroupDetail/ManualUploadModal/steps/Steps.tsx @@ -0,0 +1,19 @@ +import {stepProps} from "../../../../types/stepTypes"; +import RequiredStep from "./RequiredStep"; +import OptionalStep from "./OptionalStep"; +import React from "react"; + +const Steps = ({step, nextStep, prevStep, student, handleChange} : stepProps) => { + + + if (step == 1) { + return ( + + ) + } + return ( + + ) +} + +export default Steps; \ No newline at end of file diff --git a/client/src/components/GroupDetail/MemberUpdateButton/MemberUpdateButton.tsx b/client/src/components/GroupDetail/MemberUpdateButton/MemberUpdateButton.tsx new file mode 100644 index 0000000..8615110 --- /dev/null +++ b/client/src/components/GroupDetail/MemberUpdateButton/MemberUpdateButton.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +//함수타입으로 정의 : () => + +const MemberUpdateButton = ({onClick}: {onClick?: () => void}) => { + return ( + //나중에 src/components/common/Button 컴포넌트로 보내주는 방식으로 확장예정 + + ); +}; +export default MemberUpdateButton; diff --git a/client/src/components/QrCheckIn/QrScan.tsx b/client/src/components/QrCheckIn/QrScan.tsx new file mode 100644 index 0000000..2adb6c9 --- /dev/null +++ b/client/src/components/QrCheckIn/QrScan.tsx @@ -0,0 +1,67 @@ +import React, {useState} from 'react'; +import QrScanner from 'react-qr-scanner'; +import {sendToServer} from '../../api/sendToServer'; +import {studentQr} from '../../types/QrType/StudentQr'; +import {scanData} from '../../types/QrType/ScanData'; +import * as Styled from './qrCode.styles'; + +const Student: studentQr = { + studentId: '', + eventId: 8, +}; + +const handleError = (err: Error) => { + console.error(err); +}; + +interface QrScanProps { + onScanResult: (message: string, messageColor:string, qrColor:string) => void; +} + +const QrScan = ({ onScanResult }: QrScanProps) => { + const [scanned, setQrScanned] = useState(false); + const [nextScanned, setNextScanned] = useState(0); + + const handleScan = (data: scanData) => { + if (data && !scanned) { + const ScannedQrArray = data.text; + const studentIdStartPoint = 8; + const studentIdEndPoint = 17; + const ExtractedStudentId = ScannedQrArray.substring( + studentIdStartPoint, + studentIdEndPoint, + ); + Student.studentId = ExtractedStudentId; + + sendToServer(Student) + .then((res) => { + if (!res.data.message || res.data.message == "이미 체크인 했습니다.") { + onScanResult("정상적으로 참석되었습니다.",'#4E54F5','#4E54F5'); + } else { + onScanResult("이벤트 해당그룹이 아닙니다.",'red','red'); + } + setQrScanned(true); + }) + .catch((error) => { + console.log(error); + }); + + setTimeout(() => { + setQrScanned(false); + onScanResult("QR CODE를 화면의 사각형 안에 맞춰주세요.",'black','lightgray'); + setNextScanned(prevKey => prevKey + 1); + }, 1000); + + } + }; + + return ( +
+ + + +
+ ); +}; + +export default QrScan; diff --git a/client/src/components/QrCheckIn/qrCode.styles.ts b/client/src/components/QrCheckIn/qrCode.styles.ts new file mode 100644 index 0000000..b7964e2 --- /dev/null +++ b/client/src/components/QrCheckIn/qrCode.styles.ts @@ -0,0 +1,7 @@ +import styled from "styled-components"; + +export const QrCameraReveral = styled.div` + width: 100%; + height: 100%; + transform: scaleX(-1); +` diff --git a/client/src/components/common/Button/Button.styles.ts b/client/src/components/common/Button/Button.styles.ts new file mode 100644 index 0000000..2bffa3c --- /dev/null +++ b/client/src/components/common/Button/Button.styles.ts @@ -0,0 +1,40 @@ +import styled,{css} from "styled-components"; + +//TODO +// hover 처리도 추가하기 + +export interface ButtonStyle { + width?: string; + height?: string; + buttonColor?: string; + hasBorder?: string; + borderColor?: string; + borderRadius?: string; + fontColor?: string; + fontSize?: string; +} + +export const ButtonStyled = styled.button` + cursor: pointer; + display: inline-flex; + justify-content: center; + align-items: center; + ${({ + width = 'auto', + height = 'auto', + buttonColor = 'auto', + hasBorder = false, + borderColor = 'white', + borderRadius = 'auto', + fontColor = 'auto', + fontSize = 'auto', + }) => css` + width: ${width}; + height: ${height}; + background-color: ${buttonColor}; + border: ${hasBorder ? `1px solid ${borderColor}` : 'none'}; + border-radius: ${borderRadius}; + color: ${fontColor}; + font-size: ${fontSize}; + `} +` diff --git a/client/src/components/common/Button/Button.tsx b/client/src/components/common/Button/Button.tsx new file mode 100644 index 0000000..d7b78e7 --- /dev/null +++ b/client/src/components/common/Button/Button.tsx @@ -0,0 +1,21 @@ +import React, {ReactElement, ReactNode} from 'react'; +import {ButtonStyle, ButtonStyled} from 'components/common/Button/Button.styles'; +interface LogoProps { + src: string; + text: string; +} + +interface ButtonProps extends React.ButtonHTMLAttributes, ButtonStyle { + children:ReactNode; + onClick?: () => void; +} + +const Button = ({children, ...buttonProps}: ButtonProps): ReactElement => { + return ( + + {children} + + ) +} + +export default Button; \ No newline at end of file diff --git a/client/src/components/common/GroupMemberList/GroupMember.tsx b/client/src/components/common/GroupMemberList/GroupMember.tsx new file mode 100644 index 0000000..5696acf --- /dev/null +++ b/client/src/components/common/GroupMemberList/GroupMember.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import * as Styled from 'components/common/GroupMemberList/GroupMemberList.styles'; +import {student} from '../../../types/studentTypes'; + +const GroupMember = ({member}: {member: student}) => { + return ( + <> + + + {member.studentId} + {member.name} {/*여기에 실선 하나*/} + {member.status} + {member.tel} + {member.major} + {member.club} + {member.position} + {member.joinDate} + {member.college} + + + + + ); +}; + +export default GroupMember; diff --git a/client/src/components/common/GroupMemberList/GroupMemberList.styles.ts b/client/src/components/common/GroupMemberList/GroupMemberList.styles.ts new file mode 100644 index 0000000..794f930 --- /dev/null +++ b/client/src/components/common/GroupMemberList/GroupMemberList.styles.ts @@ -0,0 +1,31 @@ +import styled from 'styled-components'; +export const Table = styled.table` + border: 1px solid; + border-collapse: separate; + border-spacing: 0; +`; + +export const TBody = styled.tbody` + tr:last-child td { + border-bottom: none; + } +` + +export const ThData = styled.th` + padding: 10px; + border-bottom: 1px solid; +` + +export const ThBorder = styled(ThData)` + border-left: 1px solid #AEB5BC; +`; + +export const TdData = styled.td` + padding: 10px; + border-bottom: 1px solid #AEB5BC; + +` + +export const TdBorder = styled(TdData)` + border-left: 1px solid #AEB5BC; +` diff --git a/client/src/components/common/GroupMemberList/GroupMemberList.tsx b/client/src/components/common/GroupMemberList/GroupMemberList.tsx new file mode 100644 index 0000000..2326f88 --- /dev/null +++ b/client/src/components/common/GroupMemberList/GroupMemberList.tsx @@ -0,0 +1,36 @@ +import React, {useEffect, useState} from 'react'; +import * as Styled from 'components/common/GroupMemberList/GroupMemberList.styles'; +import {loadGroupMemberList} from '../../../api/loadGroupMemberList'; +import GroupMembers from './GroupMembers'; + +// 학번, 이름 재학유무, 휴대폰 번호, 학과.전공, 체크 +const GroupMemberList = () => { + const [groupMembers, setGroupMembers] = useState([]); + useEffect(() => { + loadGroupMemberList(setGroupMembers); + }, []); + + return ( +
+ + + + 학번 + 이름 {/*여기에 실선 하나*/} + 재학 유무 + 휴대폰 번호 + 학과.전공 + 동아리 + 직책 + 가입일자 + 소속대학 + 체크 + + + + +
+ ); +}; + +export default GroupMemberList; diff --git a/client/src/components/common/GroupMemberList/GroupMembers.tsx b/client/src/components/common/GroupMemberList/GroupMembers.tsx new file mode 100644 index 0000000..df81ffd --- /dev/null +++ b/client/src/components/common/GroupMemberList/GroupMembers.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import {students, student} from '../../../types/studentTypes'; +import GroupMember from './GroupMember'; + +const GroupMembers = ({members}: {members: students}) => { + return ( + <> + {members.map((member: student, index: number) => ( + + ))} + + ); +}; + +export default GroupMembers; diff --git a/client/src/components/common/Header/Header.styles.ts b/client/src/components/common/Header/Header.styles.ts new file mode 100644 index 0000000..381ac32 --- /dev/null +++ b/client/src/components/common/Header/Header.styles.ts @@ -0,0 +1,17 @@ +import styled from 'styled-components'; +export const HeaderStyles = styled.header` + width: 100%; + height: 55px; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 20px; + box-sizing: border-box; + background-color: #ffffff; +`; + +export const LogoGroup = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; diff --git a/client/src/components/common/Header/Header.tsx b/client/src/components/common/Header/Header.tsx new file mode 100644 index 0000000..3cb9715 --- /dev/null +++ b/client/src/components/common/Header/Header.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import Logo from 'components/common/Logo/Logo'; +import {HeaderStyles, LogoGroup} from 'components/common/Header/Header.styles'; +import icon from 'icon.png'; +import Button from 'components/common/Button/Button'; +const Header = () => { + return ( + + + + + + + + ); +}; + +export default Header; diff --git a/client/src/components/common/Logo/Logo.styles.ts b/client/src/components/common/Logo/Logo.styles.ts new file mode 100644 index 0000000..74123a2 --- /dev/null +++ b/client/src/components/common/Logo/Logo.styles.ts @@ -0,0 +1,17 @@ +import styled from 'styled-components'; +export const LogoImage = styled.img` + width: 40px; + height: 40px; + background-color: #AEB5BC; +` + +export const LogoText = styled.span` + font-size: 24px; + margin-left: 10px; +` + +export const LogoContainer = styled.div` + display: flex; + align-items: center; + margin-right: 30px; +` \ No newline at end of file diff --git a/client/src/components/common/Logo/Logo.tsx b/client/src/components/common/Logo/Logo.tsx new file mode 100644 index 0000000..42746cd --- /dev/null +++ b/client/src/components/common/Logo/Logo.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { + LogoContainer, + LogoImage, + LogoText, +} from 'components/common/Logo/Logo.styles'; + +interface LogoProps { + className?: string; + src: string; + alt: string; + text: string; +} + +const Logo = ({className, src, alt, text}: LogoProps) => { + return ( +
+ + + {text} + +
+ ); +}; + +export default Logo; diff --git a/client/src/components/common/ModalFrame/ModalFrame.styles.ts b/client/src/components/common/ModalFrame/ModalFrame.styles.ts new file mode 100644 index 0000000..9265ca1 --- /dev/null +++ b/client/src/components/common/ModalFrame/ModalFrame.styles.ts @@ -0,0 +1,48 @@ +import styled from 'styled-components'; +import Logo from '../Logo/Logo'; +export const ModalContainer = styled.div` + display: flex; + align-items: center; +`; + +export const ModalBackdrop = styled.div` + z-index: 1; + width: 100%; + height: 100%; + position: fixed; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + justify-content: center; + align-items: center; +`; + +export const ModalView = styled.button` + width: 500px; + height: 400px; + background-color: white; + border: none; + opacity: 1; +`; + +export const ModalExitButton = styled.button` + position: absolute; + right: 0; + border: 0; + background-color: transparent; + cursor: pointer; +`; + +export const ModalHeader = styled.div` + width: 490px; + height: 120px; + position: relative; + bottom: 70px; +`; + +export const ModalLogo = styled(Logo)` + position: absolute; + left: 30px; + bottom: 10px; +`; + +export const ModalContent = styled.div``; diff --git a/client/src/components/common/ModalFrame/ModalFrame.tsx b/client/src/components/common/ModalFrame/ModalFrame.tsx new file mode 100644 index 0000000..7967fa0 --- /dev/null +++ b/client/src/components/common/ModalFrame/ModalFrame.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import {useRecoilState} from 'recoil'; +import {fileUploadModalState, manualUploadModalState} from 'recoil/modalState'; + +import { + ModalBackdrop, + ModalContent, + ModalExitButton, + ModalHeader, + ModalLogo, + ModalView, +} from './ModalFrame.styles'; +import icon from '../../../icon.png'; + +//인원 자동추가를 누르면 modalframe의 상태를 true로 만들어줘야한다. + +const ModalFrame: React.FC<{children: React.ReactNode}> = ({children}) => { + const [, setFileUploadModalState] = useRecoilState(fileUploadModalState); + const [, setManualUploadModalState] = useRecoilState(manualUploadModalState); + + // TODO: 모달 상태 관리에 대한 리팩토링 필요 😒 + const modalClose = () => { + setFileUploadModalState(false); + setManualUploadModalState(false); + }; + + return ( + + + + X + + + + {children} + + + ); +}; + +export default ModalFrame; diff --git a/client/src/components/common/SearchAndButtonFrame/SearchAndButtonFrame.styles.ts b/client/src/components/common/SearchAndButtonFrame/SearchAndButtonFrame.styles.ts new file mode 100644 index 0000000..8eee189 --- /dev/null +++ b/client/src/components/common/SearchAndButtonFrame/SearchAndButtonFrame.styles.ts @@ -0,0 +1,12 @@ +import styled from 'styled-components'; +export const SearchAndButtonFrameStyles = styled.div` + width: 100%; + height: 55px; + display: flex; + padding-top: 50px; + padding-bottom: 50px; + justify-content: space-between; + align-items: center; + box-sizing: border-box; + background-color: #f0f4f7; +`; diff --git a/client/src/components/common/SearchAndButtonFrame/SearchAndButtonFrame.tsx b/client/src/components/common/SearchAndButtonFrame/SearchAndButtonFrame.tsx new file mode 100644 index 0000000..4e032a6 --- /dev/null +++ b/client/src/components/common/SearchAndButtonFrame/SearchAndButtonFrame.tsx @@ -0,0 +1,17 @@ +import React, {ReactNode} from 'react'; + +import * as Styles from './SearchAndButtonFrame.styles'; + +interface FrameProps { + children: ReactNode; +} + +const SearchAndButtonFrame = ({children}: FrameProps) => { + return ( + + {children} + + ); +}; + +export default SearchAndButtonFrame; diff --git a/client/src/components/event_detail/CheckInTable/CheckInTable.styles.ts b/client/src/components/event_detail/CheckInTable/CheckInTable.styles.ts new file mode 100644 index 0000000..f17402d --- /dev/null +++ b/client/src/components/event_detail/CheckInTable/CheckInTable.styles.ts @@ -0,0 +1,35 @@ +import styled from 'styled-components'; + +export const Table = styled.table` + //border: 1px solid; + border-radius: 10px; + border-collapse: separate; + border-spacing: 0; + width: 70vw; + background-color: #ffffff; + color: #697077; +`; + +export const TBody = styled.tbody` + tr:last-child td { + border-bottom: none; + } +`; + +export const ThData = styled.th` + padding: 10px; + border-bottom: 1px solid #aeb5bc; +`; + +export const ThBorder = styled(ThData)` + border-left: 1px solid #aeb5bc; +`; + +export const TdData = styled.td` + padding: 10px; + border-bottom: 1px solid #aeb5bc; +`; + +export const TdBorder = styled(TdData)` + border-left: 1px solid #aeb5bc; +`; diff --git a/client/src/components/event_detail/CheckInTable/CheckInTable.tsx b/client/src/components/event_detail/CheckInTable/CheckInTable.tsx new file mode 100644 index 0000000..56a8705 --- /dev/null +++ b/client/src/components/event_detail/CheckInTable/CheckInTable.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import * as Styled from './CheckInTable.styles'; +import useCheckInStudent from '../../../hooks/useCheckInStudent'; + +const CheckInTable = () => { + const {students} = useCheckInStudent('8'); + + const eventStudentStatusImage = (rowEventStudentStatus: string) => { + if (rowEventStudentStatus === 'CHECK_IN') { + return Check In; + } + return ( + Not Check In + ); + }; + return ( + <> + + + + 학번 + 이름 + 그룹 + 체크인 상태 + 체크인 시간 + + + + {students.length > 0 ? ( + students.map((row, index) => ( + + {row.id} + {row.name} + {row.group} + + {eventStudentStatusImage(row.eventStudentStatus)} + + {row.checkInTime} + + )) + ) : ( + + 데이터가 없습니다. + + )} + + + + ); +}; + +export default CheckInTable; diff --git a/client/src/constants/config.txt b/client/src/constants/config.txt new file mode 100644 index 0000000..d3f5a12 --- /dev/null +++ b/client/src/constants/config.txt @@ -0,0 +1 @@ + diff --git a/client/src/hooks/config.txt b/client/src/hooks/config.txt new file mode 100644 index 0000000..d3f5a12 --- /dev/null +++ b/client/src/hooks/config.txt @@ -0,0 +1 @@ + diff --git a/client/src/hooks/useCheckInStudent.tsx b/client/src/hooks/useCheckInStudent.tsx new file mode 100644 index 0000000..31f30c1 --- /dev/null +++ b/client/src/hooks/useCheckInStudent.tsx @@ -0,0 +1,49 @@ +import { useState, useEffect } from "react"; +import axios from "axios"; + +interface CheckInStudent { + id: string; + name: string; + group: string; + eventStudentStatus: string; + checkInTime: string; +} + +const useCheckInStudent = (eventId: string) => { + const [students, setStudents] = useState([]); + + const LoadCheckInStudent = () => { + axios + .get(`http://34.47.97.81:8080/api/events/check-in/${eventId}?filter=ALL`) + .then(res => { + const CleanedData = res.data.data; + + const CheckInStudent = CleanedData.map((student: any) => ({ + id: student.id, + name: student.name, + group: student.band, + eventStudentStatus: student.eventStudentStatus, + checkInTime: student.checkInTime, + })); + + setStudents(CheckInStudent); + + }) + .catch(error => { + console.log("체크인 명단 불러오기를 실패했습니다",error); + }); + }; + + useEffect(() => { + const interval = setInterval(() => { + LoadCheckInStudent(); + console.log("로드"); + }, 2000); + + return () => clearInterval(interval); + }, []); + + return {students}; +}; + +export default useCheckInStudent; diff --git a/client/src/icon.png b/client/src/icon.png new file mode 100644 index 0000000..9650e11 Binary files /dev/null and b/client/src/icon.png differ diff --git a/client/src/index.tsx b/client/src/index.tsx index 032464f..6a5d56b 100644 --- a/client/src/index.tsx +++ b/client/src/index.tsx @@ -3,14 +3,15 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import {RecoilRoot} from 'recoil'; const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById('root') as HTMLElement, ); root.render( - + - + , ); // If you want to start measuring performance in your app, pass a function diff --git a/client/src/pages/EventDetail/EventDetail.styles.ts b/client/src/pages/EventDetail/EventDetail.styles.ts new file mode 100644 index 0000000..c8cf362 --- /dev/null +++ b/client/src/pages/EventDetail/EventDetail.styles.ts @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + padding: 0; + margin: 0; + background-color: #f0f4f7; +`; + +export {Wrapper}; diff --git a/client/src/pages/EventDetail/EventDetail.tsx b/client/src/pages/EventDetail/EventDetail.tsx new file mode 100644 index 0000000..5193162 --- /dev/null +++ b/client/src/pages/EventDetail/EventDetail.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import * as Styled from './EventDetail.styles'; +import {useNavigate} from 'react-router-dom'; +import Button from '../../components/common/Button/Button'; +import CheckInTable from '../../components/event_detail/CheckInTable/CheckInTable'; +import Header from '../../components/common/Header/Header'; +import SearchAndButtonFrame from '../../components/common/SearchAndButtonFrame/SearchAndButtonFrame'; + +const EventDetail = () => { + const navigate = useNavigate(); + const navigateToQrScanner = () => { + navigate('/qrScan'); + }; + return ( + +
+
+ +
검색 박스 준비 중... 🙇🏻‍♂️
+ + {'qrCheckIn'} +
+ +
+ + ); +}; + +export default EventDetail; diff --git a/client/src/pages/GroupDetail/GroupDetail.tsx b/client/src/pages/GroupDetail/GroupDetail.tsx new file mode 100644 index 0000000..f293415 --- /dev/null +++ b/client/src/pages/GroupDetail/GroupDetail.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import {useRecoilState} from 'recoil'; +import { + fileUploadModalState, + manualUploadModalState, +} from '../../recoil/modalState'; + +import MemberUpdateButton from 'components/GroupDetail/MemberUpdateButton/MemberUpdateButton'; +import Header from 'components/common/Header/Header'; +import FileUploadModal from '../../components/GroupDetail/FileUploadModal/FileUploadModal'; +import ManualUploadModal from '../../components/GroupDetail/ManualUploadModal/ManualUploadModal'; +import GroupMemberList from "../../components/common/GroupMemberList/GroupMemberList"; + +// 파일 업로드 기능 컴포넌트 + +//Todo +//1. title, serchbox 컴포넌트 제작 +//2. title, serchbox랑 button, GroupMemberList 맞춰 정렬하기 +//3. group detail 페이지 css 마무리하기 + +const GroupDetailPage = () => { + const [fileUploadModalStateValue, setFileUploadModalStateValue] = + useRecoilState(fileUploadModalState); + const [manualUploadModalStateValue, setManualUploadModalStateValue] = + useRecoilState(manualUploadModalState); + const openFileUploadModal = () => { + setFileUploadModalStateValue(true); + }; + + const openManualUploadModal = () => { + setManualUploadModalStateValue(true); + }; + + return ( +
+ + +
+ {/**/} + + <div> + {/*<SerchBox/>*/} + <div> + {/*Todo 자동추가 / 수동추가 버튼을 Button 컴포넌트 활용해서 나누기*/} + <MemberUpdateButton onClick={openFileUploadModal} /> + <MemberUpdateButton onClick={openManualUploadModal} /> + {/*<ManualUpdateButton />*/} + </div> + <GroupMemberList /> + </div> + {/*<FileUploadModal/>*/} + </div> + ); +}; + +export default GroupDetailPage; diff --git a/client/src/pages/QrCheckIn/MainQrCheckIn.styles.ts b/client/src/pages/QrCheckIn/MainQrCheckIn.styles.ts new file mode 100644 index 0000000..ae930e8 --- /dev/null +++ b/client/src/pages/QrCheckIn/MainQrCheckIn.styles.ts @@ -0,0 +1,75 @@ +import styled from "styled-components"; + +const Text = styled.div` + font-family: Noto Sans; + text-align: left; +`; + +const Wrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-around; + width: 100%; + height: 100%; + padding:0; + margin:0; + background-color: #F0F4F7; + overflow-x: hidden; +`; + +const Event = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + background-color: white; + height: 417px; + margin-top: 40px; + padding: 20px 55px 60px 40px; + border-radius: 10px; + gap:5px; +` +const EventName = styled(Text)` + display: flex; + flex-direction: row; + align-items: center; + max-width: 850px; + font-size: 40px; + line-height: 55px; +` +const Line = styled("div")` + height:1px; + background-color:#F0F4F7; + border:0; +` + +const EventTime = styled(Text)` + display: flex; + flex-direction: row; + font-size: 20px; + line-height: 20px; +` + +const GroupBox = styled(Text)` + font-size: 20px; + color: #697077; + line-height: 27.24px; +` + +const QrBox = styled.div<{ qrColor: string }>` + margin-top:40px; + background-color: ${({ qrColor }) => qrColor}; + border: 25px solid ${({ qrColor }) => qrColor}; + border-bottom: 20px solid ${({ qrColor }) => qrColor}; + border-radius: 20px; +` + +const QrMessage = styled(Text)<{ messageColor: string }>` + color: ${({ messageColor }) => messageColor}; + font-size: 32px; + font-weight: 400; + line-height: 43.58px; + margin-top: 20px; + white-space: normal; +` + +export {Wrapper, Event, GroupBox, EventName, Line, EventTime, QrBox, QrMessage}; \ No newline at end of file diff --git a/client/src/pages/QrCheckIn/MainQrCheckIn.tsx b/client/src/pages/QrCheckIn/MainQrCheckIn.tsx new file mode 100644 index 0000000..5893df7 --- /dev/null +++ b/client/src/pages/QrCheckIn/MainQrCheckIn.tsx @@ -0,0 +1,54 @@ +import Header from '../../components/common/Header/Header'; + +import React, {useState} from "react"; +import * as Styled from './MainQrCheckIn.styles'; +import QrScan from '../../components/QrCheckIn/QrScan'; +import icon from "../../icon.png"; +import Logo from "../../components/common/Logo/Logo"; + +const MainQrCheckIn = () => { + const [message, setMessage] = useState("QR CODE를 화면의 사각형 안에 맞춰주세요."); + const [messageColor, setMessageColor] = useState('black'); + const [qrColor,setQrColor] = useState<string>('lightgray'); + + const handleScanResult = (newMessage: string, newMessageColor:string,newQrColor:string) => { + setMessage(newMessage); + setMessageColor(newMessageColor); + setQrColor(newQrColor); + }; + + return ( + <> + <Header /> + <Styled.Wrapper> + <Styled.Event> + <Styled.EventName> + <Logo src={icon} alt={'logo1'} text={''} /> + <h2>WAP 2024 2학기 개강총회</h2> + </Styled.EventName> + <Styled.Line></Styled.Line> + <Styled.EventTime> + <h3>2024.09.13 18:30</h3> + <h3> ~ </h3> + <h3>2024.09.13 20:00</h3> + </Styled.EventTime> + <Styled.Line></Styled.Line> + <Styled.GroupBox> + <p>참가 가능한 그룹</p> + <p>WAP 2024</p> + </Styled.GroupBox> + </Styled.Event> + <div className="Qr"> + <Styled.QrBox qrColor={qrColor}> + <QrScan onScanResult={handleScanResult}/> + </Styled.QrBox> + <Styled.QrMessage messageColor={messageColor}> + <div>{message}</div> + </Styled.QrMessage> + </div> + </Styled.Wrapper> + </> + ) +} + +export default MainQrCheckIn; diff --git a/client/src/react-qr-scanner.d.ts b/client/src/react-qr-scanner.d.ts new file mode 100644 index 0000000..99755be --- /dev/null +++ b/client/src/react-qr-scanner.d.ts @@ -0,0 +1,4 @@ +declare module 'react-qr-scanner' { + const content: any; + export default content; +} \ No newline at end of file diff --git a/client/src/recoil/modalState.ts b/client/src/recoil/modalState.ts new file mode 100644 index 0000000..096b485 --- /dev/null +++ b/client/src/recoil/modalState.ts @@ -0,0 +1,11 @@ +import {atom} from 'recoil'; + +export const fileUploadModalState = atom<boolean>({ + key: 'fileUploadModalState', + default: false, +}); + +export const manualUploadModalState = atom<boolean>({ + key: 'manualUploadModalState', + default: false, +}); diff --git a/client/src/types/QrType/ScanData.ts b/client/src/types/QrType/ScanData.ts new file mode 100644 index 0000000..9b5a469 --- /dev/null +++ b/client/src/types/QrType/ScanData.ts @@ -0,0 +1,3 @@ +export type scanData = { + text: string; +} | null; diff --git a/client/src/types/QrType/StudentQr.ts b/client/src/types/QrType/StudentQr.ts new file mode 100644 index 0000000..eb1cd6d --- /dev/null +++ b/client/src/types/QrType/StudentQr.ts @@ -0,0 +1,12 @@ +export interface studentQr { + studentId: string, + eventId: number, +} + +export interface studentCheckedIn { + id: string, + name: string, + group: string, + eventStudentStatus: string, + checkInTime: string, +} \ No newline at end of file diff --git a/client/src/types/stepTypes.ts b/client/src/types/stepTypes.ts new file mode 100644 index 0000000..be18da8 --- /dev/null +++ b/client/src/types/stepTypes.ts @@ -0,0 +1,11 @@ +import React from "react"; +import {student} from "./studentTypes"; + +export interface stepProps { + step?: number; + nextStep ?: () => void; + prevStep ?: () => void; + student : student; + handleChange ?: (e: React.ChangeEvent<HTMLInputElement>) => void; +} + diff --git a/client/src/types/studentTypes.ts b/client/src/types/studentTypes.ts new file mode 100644 index 0000000..26c53c0 --- /dev/null +++ b/client/src/types/studentTypes.ts @@ -0,0 +1,13 @@ +export interface student { + studentId: string, + name: string, + club: string, + position: string, + joinDate: string, + college: string, + major: string, + tel: string, + status: string, +} + +export type students = student[]; \ No newline at end of file diff --git a/client/tsconfig.json b/client/tsconfig.json index a273b0c..9aa2001 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -6,6 +6,7 @@ "dom.iterable", "esnext" ], + "baseUrl": "src", "allowJs": true, "skipLibCheck": true, "esModuleInterop": true,