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
+
+
+ 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 (
-
+
+
+ } />
+ } />
+
+
);
}
-
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 ;
+ }
+ return (
+
+ );
+ };
+ 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 (
+
+
+
+
+ 검색 박스 준비 중... 🙇🏻♂️
+
+
+
+
+
+
+ );
+};
+
+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 (
+
+
+
+
+ {/*
*/}
+
+
+ {/*
*/}
+
+ {/*Todo 자동추가 / 수동추가 버튼을 Button 컴포넌트 활용해서 나누기*/}
+
+
+ {/**/}
+
+
+
+ {/*
*/}
+
+ );
+};
+
+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('lightgray');
+
+ const handleScanResult = (newMessage: string, newMessageColor:string,newQrColor:string) => {
+ setMessage(newMessage);
+ setMessageColor(newMessageColor);
+ setQrColor(newQrColor);
+ };
+
+ return (
+ <>
+
+
+
+
+
+ WAP 2024 2학기 개강총회
+
+
+
+ 2024.09.13 18:30
+ ~
+ 2024.09.13 20:00
+
+
+
+ 참가 가능한 그룹
+ WAP 2024
+
+
+
+
+ >
+ )
+}
+
+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({
+ key: 'fileUploadModalState',
+ default: false,
+});
+
+export const manualUploadModalState = atom({
+ 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) => 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,