diff --git a/README.md b/README.md
index 74872fd..780c92d 100644
--- a/README.md
+++ b/README.md
@@ -18,11 +18,11 @@ export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ project: ["./tsconfig.node.json", "./tsconfig.app.json"],
tsconfigRootDir: import.meta.dirname,
},
},
-})
+});
```
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
@@ -31,11 +31,11 @@ export default tseslint.config({
```js
// eslint.config.js
-import react from 'eslint-plugin-react'
+import react from "eslint-plugin-react";
export default tseslint.config({
// Set the react version
- settings: { react: { version: '18.3' } },
+ settings: { react: { version: "18.3" } },
plugins: {
// Add the react plugin
react,
@@ -44,7 +44,7 @@ export default tseslint.config({
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
- ...react.configs['jsx-runtime'].rules,
+ ...react.configs["jsx-runtime"].rules,
},
-})
+});
```
diff --git a/build.sh b/build.sh
index 14fc7ed..dd61cb3 100644
--- a/build.sh
+++ b/build.sh
@@ -4,4 +4,6 @@ cd ../
mkdir -p output
-cp -R ./F4_Front/* ./output
\ No newline at end of file
+cp -R ./F4_Front/* ./output
+
+cp -R ./output ./F4_Front
\ No newline at end of file
diff --git a/index.html b/index.html
index e4b78ea..ad73b72 100644
--- a/index.html
+++ b/index.html
@@ -1,10 +1,10 @@
-
-
+
+
- Vite + React + TS
+ Sync Up
diff --git a/package-lock.json b/package-lock.json
index 0c9ff49..33ef462 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,9 +8,16 @@
"name": "syncup",
"version": "0.0.0",
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.6.0",
+ "@fortawesome/free-brands-svg-icons": "^6.6.0",
+ "@fortawesome/free-regular-svg-icons": "^6.6.0",
+ "@fortawesome/free-solid-svg-icons": "^6.6.0",
+ "@fortawesome/react-fontawesome": "^0.2.2",
+ "axios": "^1.7.7",
"path": "^0.12.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-router-dom": "^6.27.0",
"styled-components": "^6.1.13",
"url": "^0.11.4"
},
@@ -19,6 +26,7 @@
"@types/node": "^22.8.4",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
+ "@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
@@ -44,9 +52,9 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz",
- "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -59,9 +67,9 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz",
- "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz",
+ "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -100,13 +108,13 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.26.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz",
- "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz",
+ "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.26.0",
+ "@babel/parser": "^7.26.2",
"@babel/types": "^7.26.0",
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25",
@@ -220,9 +228,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.26.1",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz",
- "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==",
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
+ "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -842,9 +850,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz",
- "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==",
+ "version": "9.14.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz",
+ "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -874,6 +882,76 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz",
+ "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz",
+ "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==",
+ "license": "MIT",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-brands-svg-icons": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.6.0.tgz",
+ "integrity": "sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-regular-svg-icons": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz",
+ "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz",
+ "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/react-fontawesome": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz",
+ "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==",
+ "license": "MIT",
+ "dependencies": {
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6",
+ "react": ">=16.3"
+ }
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -898,6 +976,20 @@
"node": ">=18.18.0"
}
},
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -913,9 +1005,9 @@
}
},
"node_modules/@humanwhocodes/retry": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
- "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz",
+ "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -1017,6 +1109,15 @@
"node": ">= 8"
}
},
+ "node_modules/@remix-run/router": {
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
+ "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.24.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.3.tgz",
@@ -1321,6 +1422,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/history": {
+ "version": "4.7.11",
+ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
+ "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1329,9 +1437,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.8.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.4.tgz",
- "integrity": "sha512-SpNNxkftTJOPk0oN+y2bIqurEXHTA2AOZ3EJDDKeJ5VzkvvORSvmQXGQarcOzWV1ac7DCaPBEdMDxBsM+d8jWw==",
+ "version": "22.8.6",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz",
+ "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1366,6 +1474,29 @@
"@types/react": "*"
}
},
+ "node_modules/@types/react-router": {
+ "version": "5.1.20",
+ "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
+ "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/history": "^4.7.11",
+ "@types/react": "*"
+ }
+ },
+ "node_modules/@types/react-router-dom": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
+ "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/history": "^4.7.11",
+ "@types/react": "*",
+ "@types/react-router": "*"
+ }
+ },
"node_modules/@types/stylis": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz",
@@ -1697,6 +1828,23 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
+ "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1800,9 +1948,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001674",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001674.tgz",
- "integrity": "sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw==",
+ "version": "1.0.30001676",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001676.tgz",
+ "integrity": "sha512-Qz6zwGCiPghQXGJvgQAem79esjitvJ+CxSbSQkW9H/UX5hg8XM88d4lp2W+MEQ81j+Hip58Il+jGVdazk1z9cw==",
"dev": true,
"funding": [
{
@@ -1857,6 +2005,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1954,10 +2114,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/electron-to-chromium": {
- "version": "1.5.49",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.49.tgz",
- "integrity": "sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==",
+ "version": "1.5.50",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.50.tgz",
+ "integrity": "sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==",
"dev": true,
"license": "ISC"
},
@@ -2045,22 +2214,22 @@
}
},
"node_modules/eslint": {
- "version": "9.13.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz",
- "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==",
+ "version": "9.14.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz",
+ "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.11.0",
+ "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.18.0",
"@eslint/core": "^0.7.0",
"@eslint/eslintrc": "^3.1.0",
- "@eslint/js": "9.13.0",
+ "@eslint/js": "9.14.0",
"@eslint/plugin-kit": "^0.2.0",
- "@humanfs/node": "^0.16.5",
+ "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
- "@humanwhocodes/retry": "^0.3.1",
+ "@humanwhocodes/retry": "^0.4.0",
"@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4",
@@ -2068,9 +2237,9 @@
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.1.0",
- "eslint-visitor-keys": "^4.1.0",
- "espree": "^10.2.0",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
"esquery": "^1.5.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -2347,6 +2516,40 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2745,6 +2948,27 @@
"node": ">=8.6"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -2797,6 +3021,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/object-inspect": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
@@ -2922,10 +3155,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.47",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
- "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
- "dev": true,
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"funding": [
{
"type": "opencollective",
@@ -2943,8 +3175,8 @@
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
- "picocolors": "^1.1.0",
- "source-map-js": "^1.2.1"
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -2975,6 +3207,23 @@
"node": ">= 0.6.0"
}
},
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -3046,6 +3295,12 @@
"react": "^18.3.1"
}
},
+ "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==",
+ "license": "MIT"
+ },
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -3056,6 +3311,38 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "6.27.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
+ "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.20.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.27.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
+ "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
+ "license": "MIT",
+ "dependencies": {
+ "@remix-run/router": "1.20.0",
+ "react-router": "6.27.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -3272,34 +3559,6 @@
"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"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.2.0"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
"node_modules/stylis": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
@@ -3340,9 +3599,9 @@
}
},
"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==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz",
+ "integrity": "sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3545,6 +3804,35 @@
}
}
},
+ "node_modules/vite/node_modules/postcss": {
+ "version": "8.4.47",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "dev": true,
+ "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"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.1.0",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index 53d3b22..1a5d828 100644
--- a/package.json
+++ b/package.json
@@ -10,9 +10,16 @@
"preview": "vite preview"
},
"dependencies": {
+ "axios": "^1.7.7",
+ "@fortawesome/fontawesome-svg-core": "^6.6.0",
+ "@fortawesome/free-brands-svg-icons": "^6.6.0",
+ "@fortawesome/free-regular-svg-icons": "^6.6.0",
+ "@fortawesome/free-solid-svg-icons": "^6.6.0",
+ "@fortawesome/react-fontawesome": "^0.2.2",
"path": "^0.12.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-router-dom": "^6.27.0",
"styled-components": "^6.1.13",
"url": "^0.11.4"
},
@@ -21,6 +28,7 @@
"@types/node": "^22.8.4",
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
+ "@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
diff --git a/src/App.tsx b/src/App.tsx
index 7654d85..47ea1eb 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -2,11 +2,13 @@
import { ThemeProvider } from "styled-components";
import theme from "@styles/theme";
import GlobalStyle from "@styles/global";
+import Router from "./Router";
function App() {
return (
+
);
}
diff --git a/src/Router.tsx b/src/Router.tsx
new file mode 100644
index 0000000..4c873dc
--- /dev/null
+++ b/src/Router.tsx
@@ -0,0 +1,25 @@
+// Router.tsx
+import { BrowserRouter, Route, Routes } from "react-router-dom";
+import Login from "./pages/login/Login";
+import Create from "@pages/create/Create";
+import Test from "@pages/test/Test";
+import Invite from "@pages/invite/Invite";
+import Result from "@pages/result/Result";
+import Confirm from "@pages/confirm/Confirm";
+
+const Router: React.FC = () => {
+ return (
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+ );
+};
+
+export default Router;
diff --git a/src/apis/instance.ts b/src/apis/instance.ts
new file mode 100644
index 0000000..7f2cc99
--- /dev/null
+++ b/src/apis/instance.ts
@@ -0,0 +1,12 @@
+import axios, { AxiosRequestConfig } from "axios";
+
+const axiosConfig: AxiosRequestConfig = {
+ baseURL: "",
+ withCredentials: false,
+ // false를 통해 cross domain request 시 HTTP 인증 및 클라이언트 SSL 인증서 사용 허용
+ headers: {
+ "Content-Type": "application/json",
+ },
+};
+
+export const client = axios.create(axiosConfig);
diff --git a/src/components/common/button/Button.tsx b/src/components/common/button/Button.tsx
new file mode 100644
index 0000000..38b368b
--- /dev/null
+++ b/src/components/common/button/Button.tsx
@@ -0,0 +1,64 @@
+import React from "react";
+import styled from "styled-components";
+import { useNavigate } from "react-router-dom";
+
+interface ButtonProps {
+ link?: string; // 링크를 위한 prop
+ name: string; // 버튼의 텍스트를 위한 prop
+ type?: "button" | "submit"; // 기본 버튼 타입
+ $width?: string; // 버튼의 넓이
+ onClick?: () => void; // 클릭 핸들러
+}
+
+const Btn = styled.button`
+ width: ${({ $width }) => ($width ? $width : "350px")};
+ height: 60px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: #343a40;
+ text-align: center;
+ font-size: 20px;
+ font-weight: 500;
+ border-radius: 10px;
+ background: #99bc85;
+ cursor: pointer;
+ border: none;
+ margin: 10px auto;
+
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+`;
+
+const Button: React.FC = ({
+ link,
+ name,
+ type = "button",
+ $width,
+ onClick,
+}) => {
+ const navigate = useNavigate();
+ const handleClick = () => {
+ if (onClick) {
+ onClick(); // onClick이 존재하면 호출
+ }
+ if (link) {
+ navigate(link);
+ }
+ };
+
+ return (
+
+ {name}
+
+ );
+};
+
+export default Button;
diff --git a/src/components/common/container/style.ts b/src/components/common/container/style.ts
new file mode 100644
index 0000000..6218fb5
--- /dev/null
+++ b/src/components/common/container/style.ts
@@ -0,0 +1,12 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ margin-top: 5%;
+ width: 90%;
+ min-height: 100vh;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ flex-direction: column;
+ gap: 2rem;
+`;
diff --git a/src/components/common/hintText/HintText1.tsx b/src/components/common/hintText/HintText1.tsx
new file mode 100644
index 0000000..8b3342b
--- /dev/null
+++ b/src/components/common/hintText/HintText1.tsx
@@ -0,0 +1,49 @@
+import styled from "styled-components";
+
+const HintTextWrapper = styled.div`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+`;
+
+const HintTextHeadline2 = styled.h2<{ width?: string }>`
+ width: ${({ width }) => width || "240px"};
+ margin: 0 auto;
+ color: #343a40;
+ text-align: center;
+ font-family: "Pretendard";
+ font-size: 28px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 40px;
+ letter-spacing: -0.5px;
+`;
+
+const HintTextP1 = styled.p`
+ color: #868e96;
+ text-align: center;
+ font-family: "Pretendard";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 22px; /* 157.143% */
+ letter-spacing: -0.1px;
+`;
+
+interface TextProps {
+ headline: string;
+ paragraph: string;
+ width?: string;
+}
+
+const HintText1: React.FC = ({ headline, paragraph, width }) => {
+ return (
+
+ {headline}
+ {paragraph}
+
+ );
+};
+
+export default HintText1;
diff --git a/src/components/common/hintText/HintText2.tsx b/src/components/common/hintText/HintText2.tsx
new file mode 100644
index 0000000..ab31ad8
--- /dev/null
+++ b/src/components/common/hintText/HintText2.tsx
@@ -0,0 +1,47 @@
+import styled from "styled-components";
+
+const HintTextWrapper = styled.div`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+`;
+
+const HintTextHeadline2 = styled.h2`
+ color: #343a40;
+ font-family: "Pretendard";
+ font-size: 28px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 30px;
+ letter-spacing: -0.5px;
+`;
+
+const HintTextP1 = styled.p`
+ /* width: 300px; */
+ color: #868e96;
+ font-family: "Pretendard";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 500;
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+ white-space: pre-line;
+`;
+
+interface TextProps {
+ headline: string;
+ paragraph: string;
+}
+
+const HintText2: React.FC = ({ headline, paragraph }) => {
+ return (
+
+ {headline}
+ {paragraph}
+
+ );
+};
+
+export default HintText2;
diff --git a/src/components/common/input/DateInput.tsx b/src/components/common/input/DateInput.tsx
new file mode 100644
index 0000000..eebd0c2
--- /dev/null
+++ b/src/components/common/input/DateInput.tsx
@@ -0,0 +1,28 @@
+// DateInput.tsx
+import React from "react";
+import * as S from "./style";
+
+const DateInput: React.FC = () => {
+ return (
+
+
+ 생년월일
+ *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DateInput;
diff --git a/src/components/common/input/Input.tsx b/src/components/common/input/Input.tsx
new file mode 100644
index 0000000..076363e
--- /dev/null
+++ b/src/components/common/input/Input.tsx
@@ -0,0 +1,32 @@
+// Input.tsx
+import React from "react";
+import * as S from "./style";
+
+interface InputProps {
+ width?: string;
+ label: string;
+ essential: boolean;
+ hint: string;
+}
+
+const Input: React.FC = ({
+ width = "340px",
+ label,
+ essential = true,
+ hint,
+}) => {
+ return (
+
+
+ {label}
+ {essential && *}
+
+ {hint && {hint}}
+
+
+
+
+ );
+};
+
+export default Input;
diff --git a/src/components/common/input/style.ts b/src/components/common/input/style.ts
new file mode 100644
index 0000000..e7a8eda
--- /dev/null
+++ b/src/components/common/input/style.ts
@@ -0,0 +1,88 @@
+import styled from "styled-components";
+
+interface InputWrapProps {
+ width?: string;
+}
+
+export const InputContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
+
+export const InputTitleWrapper = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 5px;
+`;
+
+export const InputTitle = styled.label`
+ color: #343a40;
+
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 22px;
+ letter-spacing: -0.1px;
+`;
+
+export const EssentialIcon = styled.span`
+ color: #f5535e;
+
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 18px;
+ letter-spacing: -0.1px;
+`;
+
+export const InputHint = styled.p`
+ color: #868e96;
+
+ font-size: 10px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 18px;
+ letter-spacing: -0.1px;
+`;
+
+export const InputWrap = styled.div`
+ width: ${({ width }) => width || "340px"};
+ height: 40px;
+ border-radius: 8px;
+ border: 1px solid #ced4da;
+ background: #fff;
+
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+`;
+
+export const DateInputWrap = styled.div`
+ width: 102px;
+ height: 40px;
+ border-radius: 8px;
+ border: 1px solid #ced4da;
+ background: #fff;
+
+ @media (max-width: 360px) {
+ width: 82px;
+ }
+`;
+
+export const TextInput = styled.input`
+ width: 100%;
+ color: #495057;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 500;
+ letter-spacing: -0.1px;
+ outline: none;
+ padding: 10px 12px;
+ border-radius: 8px;
+`;
+
+// DateInput.tsx
+export const DateInputContainer = styled.div`
+ display: flex;
+ gap: 17px;
+`;
diff --git a/src/components/common/wrapper/style.ts b/src/components/common/wrapper/style.ts
new file mode 100644
index 0000000..f7f1dc3
--- /dev/null
+++ b/src/components/common/wrapper/style.ts
@@ -0,0 +1,10 @@
+import styled from "styled-components";
+
+export const Wrapper = styled.div`
+ display: flex;
+ width: 100%;
+ justify-content: center;
+ align-items: center;
+ gap: 2rem;
+ flex-direction: column;
+`;
diff --git a/src/components/create/TeamButton.tsx b/src/components/create/TeamButton.tsx
new file mode 100644
index 0000000..d5258bf
--- /dev/null
+++ b/src/components/create/TeamButton.tsx
@@ -0,0 +1,34 @@
+import { useState } from "react";
+import * as S from "./style";
+
+export default function TeamButton() {
+ const [count, setCount] = useState(0);
+
+ const increment = () => setCount(count + 1);
+ const decrement = () => setCount(count > 0 ? count - 1 : 0);
+
+ return (
+
+
+
+ -
+
+
+ {count} 명
+
+
+ +
+
+
+
+ );
+}
diff --git a/src/components/create/style.ts b/src/components/create/style.ts
new file mode 100644
index 0000000..ed3d1d5
--- /dev/null
+++ b/src/components/create/style.ts
@@ -0,0 +1,31 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 36px;
+`;
+
+export const Button = styled.button`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 4rem;
+ height: 4rem;
+ border-radius: 50%;
+ background: #ced4da;
+ color: #343a40;
+ text-align: center;
+ font-family: Pretendard;
+ font-size: 30px;
+ text-align: center;
+`;
+
+export const PersonNum = styled.span`
+ color: #343a40;
+ text-align: center;
+ font-family: Pretendard;
+ font-size: 36px;
+ font-weight: 600;
+`;
diff --git a/src/components/invite/profile/ActivatedProfile.tsx b/src/components/invite/profile/ActivatedProfile.tsx
new file mode 100644
index 0000000..0f34df1
--- /dev/null
+++ b/src/components/invite/profile/ActivatedProfile.tsx
@@ -0,0 +1,43 @@
+import * as S from "./style";
+
+interface ProfileProps {
+ name: string;
+ position: string;
+ part: string;
+ imgSrc?: string;
+ url?: string;
+}
+
+const ActivatedProfile: React.FC = ({
+ name,
+ position,
+ part,
+ imgSrc,
+ url,
+}) => {
+ const handleClick = () => {
+ if (url) {
+ window.location.href = url;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ {name}
+ {position}
+
+ {part}
+
+
+ );
+};
+
+export default ActivatedProfile;
diff --git a/src/components/invite/profile/DeactivatedProfile.tsx b/src/components/invite/profile/DeactivatedProfile.tsx
new file mode 100644
index 0000000..8acaee4
--- /dev/null
+++ b/src/components/invite/profile/DeactivatedProfile.tsx
@@ -0,0 +1,18 @@
+import * as S from "./style";
+import { useNavigate } from "react-router-dom";
+
+export default function DeactivatedProfile() {
+ const navigate = useNavigate();
+ const handleClick = () => {
+ navigate("/personal");
+ };
+
+ return (
+
+
+ 아직 팀원 정보가 입력되지 않았어요!
URL을 공유하여 팀원을
+ 초대해주세요.
+
+
+ );
+}
diff --git a/src/components/invite/profile/style.ts b/src/components/invite/profile/style.ts
new file mode 100644
index 0000000..76fa426
--- /dev/null
+++ b/src/components/invite/profile/style.ts
@@ -0,0 +1,88 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ width: 350px;
+ height: 80px;
+ display: flex;
+ align-items: center;
+ padding: 14px 10px;
+ gap: 10px;
+ border-radius: 20px;
+ background: #bfd8af;
+
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+`;
+
+export const Img = styled.img`
+ width: 54px;
+ height: 54px;
+ border-radius: 27px;
+`;
+
+export const InformWrap = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+`;
+
+export const InformDetailWrap = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 6px;
+`;
+
+export const Name = styled.h3`
+ color: #343a40;
+ font-family: Pretendard;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 20px;
+ letter-spacing: -0.5px;
+`;
+
+export const Position = styled.p`
+ color: #868e96;
+ text-align: center;
+ font-family: Pretendard;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 300;
+ line-height: 14px;
+ letter-spacing: -0.5px;
+`;
+
+export const Part = styled.p`
+ color: #495057;
+ font-family: Pretendard;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 14px;
+ letter-spacing: -0.5px;
+`;
+
+export const DeactivatedContainer = styled.div`
+ width: 350px;
+ height: 80px;
+ border-radius: 20px;
+ border: 2px dashed #dee2e6;
+ background: #fff;
+ padding: 18px;
+ cursor: pointer;
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+`;
+
+export const Hint = styled.p`
+ color: #868e96;
+ font-family: Pretendard;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 20px;
+ letter-spacing: -0.5px;
+`;
diff --git a/src/components/invite/urlCopy/UrlCopy.tsx b/src/components/invite/urlCopy/UrlCopy.tsx
new file mode 100644
index 0000000..7ebb42c
--- /dev/null
+++ b/src/components/invite/urlCopy/UrlCopy.tsx
@@ -0,0 +1,28 @@
+import { faCopy } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import * as S from "./style";
+
+export default function UrlCopy() {
+ const url = "https://www.syncup.com/234asdf94al4";
+
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(url);
+ alert("URL이 복사되었습니다.");
+ } catch (err) {
+ console.error("Failed to copy text: ", err);
+ }
+ };
+
+ return (
+
+ {url}
+
+
+
+
+ );
+}
diff --git a/src/components/invite/urlCopy/style.ts b/src/components/invite/urlCopy/style.ts
new file mode 100644
index 0000000..1e6a841
--- /dev/null
+++ b/src/components/invite/urlCopy/style.ts
@@ -0,0 +1,34 @@
+import styled from "styled-components";
+
+// UrlCopy.tsx
+export const Container = styled.div`
+ display: flex;
+ justify-content: space-between;
+
+ width: 350px;
+ height: 40px;
+ border-radius: 10px;
+ border: 1px solid #ced4da;
+ background: #e9ecef;
+ padding: 10px 12px;
+
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+`;
+
+export const Url = styled.p`
+ color: #495057;
+ font-family: Pretendard;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 20px; /* 116.667% */
+ letter-spacing: -0.5px;
+`;
+
+export const Icon = styled.button`
+ width: 20px;
+ height: 20px;
+ cursor: pointer;
+`;
diff --git a/src/constants/testText.ts b/src/constants/testText.ts
new file mode 100644
index 0000000..2260b8d
--- /dev/null
+++ b/src/constants/testText.ts
@@ -0,0 +1,9 @@
+export const TEST_TEXT: string =
+ "님이 이번 팀 프로젝트에서\n가장 챙겨가고 싶은 점은 무엇인가요?";
+
+export const TEST_BTN_TEXT: string[] = [
+ "팀원들과의 네트워킹",
+ "새로운 개발 기술 도전",
+ "팀원들과의 협업 경험",
+ "문제 해결 능력 강화",
+];
diff --git a/src/hooks/useUserProfile.ts b/src/hooks/useUserProfile.ts
new file mode 100644
index 0000000..de8b5ef
--- /dev/null
+++ b/src/hooks/useUserProfile.ts
@@ -0,0 +1,24 @@
+import { useEffect, useState } from "react";
+import { AxiosResponse } from "axios";
+import { client } from "@apis/instance";
+
+export const useUserProfile = () => {
+ const [response, setResponse] = useState(null);
+ const [error, setError] = useState("세호");
+
+ const fetchData = async () => {
+ try {
+ // TODO - team_id 저장, profile id 저장
+ const response = await client.get(
+ "https://test.site/api/teams/{team_id}/profiles/{profile_id}"
+ );
+ setResponse(response);
+ } catch (err) {
+ setError("박세호");
+ }
+ };
+ useEffect(() => {
+ fetchData();
+ }, []);
+ return { response, error };
+};
diff --git a/src/pages/confirm/Confirm.tsx b/src/pages/confirm/Confirm.tsx
new file mode 100644
index 0000000..cdb49af
--- /dev/null
+++ b/src/pages/confirm/Confirm.tsx
@@ -0,0 +1,37 @@
+import { Container } from "@components/common/container/style";
+import { Wrapper } from "@components/common/wrapper/style";
+import HintText2 from "@components/common/hintText/HintText2";
+import { InputContainer } from "./_components/inputContainer/InputContainer";
+import Button from "@components/common/button/Button";
+
+const Confirm: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ // TODO - Post로 최종 결과 등록 후 최종 공유 페이지 이동 로직
+};
+
+export default Confirm;
diff --git a/src/pages/confirm/_components/inputContainer/InputContainer.tsx b/src/pages/confirm/_components/inputContainer/InputContainer.tsx
new file mode 100644
index 0000000..0b0002a
--- /dev/null
+++ b/src/pages/confirm/_components/inputContainer/InputContainer.tsx
@@ -0,0 +1,24 @@
+import * as S from "./style";
+
+interface InputContainerProps {
+ label: string;
+ defaultText: string;
+ isTextArea?: boolean;
+}
+
+export const InputContainer = ({
+ label,
+ defaultText,
+ isTextArea,
+}: InputContainerProps) => {
+ return (
+
+ {label}
+ {isTextArea ? (
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/src/pages/confirm/_components/inputContainer/style.ts b/src/pages/confirm/_components/inputContainer/style.ts
new file mode 100644
index 0000000..4c4bf38
--- /dev/null
+++ b/src/pages/confirm/_components/inputContainer/style.ts
@@ -0,0 +1,33 @@
+import styled from "styled-components";
+
+export const InputWrapper = styled.div`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+`;
+
+export const Label = styled.label`
+ font-family: Pretendard;
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 500;
+ letter-spacing: -0.1px;
+ padding: 0.2rem;
+`;
+
+export const Input = styled.input`
+ width: 100%;
+ height: 3rem;
+ border-radius: 8px;
+ border: 1px solid #ced4da;
+ padding: 0 0.5rem;
+`;
+
+export const TextArea = styled.textarea`
+ width: 100%;
+ height: 10rem;
+ border-radius: 8px;
+ border: 1px solid #ced4da;
+ padding: 0.5rem;
+`;
diff --git a/src/pages/confirm/style.ts b/src/pages/confirm/style.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/create/Create.tsx b/src/pages/create/Create.tsx
new file mode 100644
index 0000000..1413def
--- /dev/null
+++ b/src/pages/create/Create.tsx
@@ -0,0 +1,39 @@
+import styled from "styled-components";
+import Button from "@components/common/button/Button";
+import HintText from "@components/common/hintText/HintText1";
+import TeamButton from "@components/create/TeamButton";
+
+const Container = styled.div`
+ height: calc(100vh - 80px);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 120px;
+`;
+
+const ButtonLayout = styled.div`
+ position: relative;
+ bottom: 10px;
+`;
+
+const Create: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Create;
diff --git a/src/pages/invite/Invite.tsx b/src/pages/invite/Invite.tsx
new file mode 100644
index 0000000..34c544a
--- /dev/null
+++ b/src/pages/invite/Invite.tsx
@@ -0,0 +1,75 @@
+import Button from "@components/common/button/Button";
+import HintText2 from "@components/common/hintText/HintText2";
+import ActivatedProfile from "@components/invite/profile/ActivatedProfile";
+import DeactivatedProfile from "@components/invite/profile/DeactivatedProfile";
+import UrlCopy from "@components/invite/urlCopy/UrlCopy";
+import styled from "styled-components";
+
+export const Container = styled.div`
+ width: 350px;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ @media (max-width: 360px) {
+ width: 280px;
+ }
+`;
+
+export const Section = styled.div`
+ margin: 40px 0 0;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+`;
+
+export const ProfileList = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ margin: 0 0 20px 0;
+`;
+
+const Invite: React.FC = () => {
+ return (
+
+
+
+
+
+
+ );
+ // TODO - 미등록 프로필 누르면 등록하는 페이지로
+ // TODO - 서버로 받은 UUID로 고유 페이지 생성
+ // TODO - 서버로 부터 등록받은 유저 받아와서 mapping
+ // TODO - 전 인원 미등록 시 disabled
+ // TODO - 전 인원 등록 후 버튼 클릭 시 GPT 관련 API요청
+};
+
+export default Invite;
diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx
new file mode 100644
index 0000000..551729c
--- /dev/null
+++ b/src/pages/login/Login.tsx
@@ -0,0 +1,50 @@
+// Login.tsx
+import Input from "@components/common/input/Input";
+import DateInput from "@components/common/input/DateInput";
+import HintText from "@components/common/hintText/HintText1";
+import React from "react";
+import styled from "styled-components";
+import Button from "@components/common/button/Button";
+
+const Container = styled.div`
+ height: calc(100vh - 80px);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 60px;
+`;
+
+const InputForm = styled.form`
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+`;
+
+const ButtonLayout = styled.div`
+ position: relative;
+ bottom: 10px;
+`;
+
+const Login: React.FC = () => {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Login;
diff --git a/src/pages/result/Result.tsx b/src/pages/result/Result.tsx
new file mode 100644
index 0000000..24090cc
--- /dev/null
+++ b/src/pages/result/Result.tsx
@@ -0,0 +1,40 @@
+import { Container } from "@components/common/container/style";
+import { Wrapper } from "@components/common/wrapper/style";
+import HintText2 from "@components/common/hintText/HintText2";
+import { ResultCard } from "./_components/ResultCard";
+import Button from "@components/common/button/Button";
+
+const FIRST_MOCK_TEXT: string[] = [
+ "모든 인원이 커뮤니케이션을 좋아하는 팀이에요. 작업할 때 매우 소통을 중요하게 여기는 팀입니다. 실시간 커뮤 툴을 병행해서 사용하는 것이 좋을 것 같아요.",
+ "대부분이 빠른 피드백을 선호하는 애자일 방식을 선호해요.",
+];
+
+const SECOND_MOCK_TEXT: string[] = [
+ "정기적으로 학습한 내용을 공유하는 시간을 가지는 게 좋을 것 같아요. 특히 A와 B는 최신 기술을 도입하는 데에, C와 D는 기존 알던 기술을 활용하는 데에 가치를 두고 있으니, 토론해보는 게 좋을 듯 해요!",
+ "A, C는 온라인을 B, D는 오프라인 만남을 주로 선호하고 있어요. 프로젝트 기간동안 서로에게 무리가 없도록 일정을 잘 조율하는 것이 매우 중요해요.",
+];
+
+const Result: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Result;
diff --git a/src/pages/result/_components/ResultCard.tsx b/src/pages/result/_components/ResultCard.tsx
new file mode 100644
index 0000000..9c6831f
--- /dev/null
+++ b/src/pages/result/_components/ResultCard.tsx
@@ -0,0 +1,19 @@
+import * as S from "./style";
+
+interface ResultCardProps {
+ title: string;
+ contents: string[];
+}
+
+export const ResultCard = ({ title, contents }: ResultCardProps) => {
+ return (
+
+ {title}
+
+ {contents.map((content, index) => (
+ {content}
+ ))}
+
+
+ );
+};
diff --git a/src/pages/result/_components/style.ts b/src/pages/result/_components/style.ts
new file mode 100644
index 0000000..7dba3f6
--- /dev/null
+++ b/src/pages/result/_components/style.ts
@@ -0,0 +1,32 @@
+import styled from "styled-components";
+
+export const ResultCard = styled.div`
+ border-radius: 20px;
+ background-color: #e9ecef;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem;
+ gap: 1rem;
+`;
+
+export const ResultTitle = styled.h1`
+ color: #99bc85;
+ font-family: Pretendard;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: 16px;
+ letter-spacing: -0.1px;
+`;
+
+export const ResultContentWrapper = styled.ul`
+ width: 100%;
+ list-style: square;
+ display: flex;
+ flex-direction: column;
+ padding: 0 1rem;
+ gap: 0.5rem;
+`;
+
+export const ResultContent = styled.li``;
diff --git a/src/pages/result/style.ts b/src/pages/result/style.ts
new file mode 100644
index 0000000..f119156
--- /dev/null
+++ b/src/pages/result/style.ts
@@ -0,0 +1,20 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ margin-top: 5%;
+ width: 90%;
+ min-height: 100vh;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ flex-direction: column;
+ gap: 2rem;
+`;
+
+export const ContentWrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 2rem;
+ flex-direction: column;
+`;
diff --git a/src/pages/test/Test.tsx b/src/pages/test/Test.tsx
new file mode 100644
index 0000000..44163a7
--- /dev/null
+++ b/src/pages/test/Test.tsx
@@ -0,0 +1,76 @@
+import * as S from "./style";
+import { CircleInfo } from "./_components/circle/CircleInfo";
+import { usePageNumber } from "./_hooks/usePageNumber";
+import { useUserProfile } from "@hooks/useUserProfile";
+import { TEST_TEXT, TEST_BTN_TEXT } from "@constants/testText";
+import { TestButton } from "./_components/button/TestButton";
+import Button from "@components/common/button/Button";
+import { useState } from "react";
+
+const Test: React.FC = () => {
+ const { page } = usePageNumber();
+ const { error } = useUserProfile();
+
+ // 각 페이지에서 선택된 버튼 인덱스를 저장하는 배열
+ const [selectedContent, setSelectedContent] = useState([
+ -1, -1, -1, -1, -1,
+ ]);
+
+ // 버튼 클릭 핸들러
+ const handleButtonClick = (index: number) => {
+ const newContent = [...selectedContent]; // 이전 상태 복사
+ newContent[page - 1] = index; // 현재 페이지에 해당하는 인덱스 업데이트
+ setSelectedContent(newContent); // 상태 업데이트
+ };
+
+ // 다음 버튼 클릭 시 선택된 내용 확인
+ const handleNext = () => {
+ console.log(selectedContent[page - 1]);
+ // 필요한 추가 로직을 여기에 작성
+ };
+
+ return (
+
+
+
+
+ {error}
+ {TEST_TEXT}
+
+
+
+ {TEST_BTN_TEXT.map((text, index) => (
+ handleButtonClick(index)} // 클릭 핸들러
+ />
+ ))}
+
+
+ {page !== 5 ? (
+
+ ) : (
+
+ );
+ // TODO - 25개 데이터 가지고 있기
+ // TODO - mapping해서 서버에 등록해주기
+};
+
+export default Test;
diff --git a/src/pages/test/_components/button/TestButton.tsx b/src/pages/test/_components/button/TestButton.tsx
new file mode 100644
index 0000000..92c3f2e
--- /dev/null
+++ b/src/pages/test/_components/button/TestButton.tsx
@@ -0,0 +1,19 @@
+import * as S from "./style";
+
+export interface TestButtonProps {
+ text: string;
+ $isActive?: boolean; // 클릭 상태에 따른 색상 적용을 위한 prop
+ onClick: () => void; // 클릭 핸들러
+}
+
+export const TestButton = ({
+ text,
+ $isActive = false,
+ onClick,
+}: TestButtonProps) => {
+ return (
+
+ {text}
+
+ );
+};
diff --git a/src/pages/test/_components/button/style.ts b/src/pages/test/_components/button/style.ts
new file mode 100644
index 0000000..7873f89
--- /dev/null
+++ b/src/pages/test/_components/button/style.ts
@@ -0,0 +1,25 @@
+import styled from "styled-components";
+import { TestButtonProps } from "./TestButton";
+
+export const TestButton = styled.button`
+ border-radius: 15px;
+ border: 1px solid #ced4da;
+ width: 100%;
+ padding: 1.5rem 0;
+ background-color: ${({ $isActive }) =>
+ $isActive ? "#b2d1a1" : "transparent"};
+ cursor: pointer;
+ &:hover {
+ background-color: #d4e7c5; // hover 효과
+ }
+`;
+
+export const ButtonText = styled.h1`
+ color: #343a40;
+ font-family: Pretendard;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 30px; /* 150% */
+ letter-spacing: -0.5px;
+`;
diff --git a/src/pages/test/_components/circle/CircleInfo.tsx b/src/pages/test/_components/circle/CircleInfo.tsx
new file mode 100644
index 0000000..e8eaee2
--- /dev/null
+++ b/src/pages/test/_components/circle/CircleInfo.tsx
@@ -0,0 +1,23 @@
+import * as S from "./style";
+import { useNavigate } from "react-router-dom";
+
+interface CircleInfoProps {
+ pageNumber: number;
+}
+
+export const CircleInfo: React.FC = ({ pageNumber }) => {
+ const navigate = useNavigate();
+ return (
+
+ {[1, 2, 3, 4, 5].map((number) => (
+ navigate(`/test/${number}`)}
+ >
+ {number}
+
+ ))}
+
+ );
+};
diff --git a/src/pages/test/_components/circle/style.ts b/src/pages/test/_components/circle/style.ts
new file mode 100644
index 0000000..c8ca366
--- /dev/null
+++ b/src/pages/test/_components/circle/style.ts
@@ -0,0 +1,25 @@
+import styled from "styled-components";
+
+interface CircleProps {
+ $activePage: boolean;
+}
+
+export const CircleContainer = styled.div`
+ width: 60%;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ gap: 5%;
+`;
+
+export const Circle = styled.div`
+ width: 30px;
+ height: 30px;
+ background-color: ${({ $activePage }) =>
+ $activePage ? "#99BC85" : "#D4E7C5"};
+ border-radius: 50%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: white;
+`;
diff --git a/src/pages/test/_hooks/usePageNumber.ts b/src/pages/test/_hooks/usePageNumber.ts
new file mode 100644
index 0000000..690a043
--- /dev/null
+++ b/src/pages/test/_hooks/usePageNumber.ts
@@ -0,0 +1,6 @@
+import { useParams } from "react-router-dom";
+
+export const usePageNumber = () => {
+ const { page } = useParams();
+ return { page: page ? Number(page) : 1 };
+};
diff --git a/src/pages/test/style.ts b/src/pages/test/style.ts
new file mode 100644
index 0000000..99ff26d
--- /dev/null
+++ b/src/pages/test/style.ts
@@ -0,0 +1,31 @@
+import styled from "styled-components";
+
+export const TestContainer = styled.div`
+ width: 90%;
+ min-height: 100vh;
+ display: flex;
+ justify-content: space-around;
+ margin-top: 5%;
+ gap: 1rem;
+ flex-direction: column;
+`;
+
+export const TextContainer = styled.div`
+ margin-top: 2.5%;
+ white-space: pre-line;
+ color: #343a40;
+ font-family: Pretendard;
+ font-size: 24px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: 30px; /* 125% */
+ letter-spacing: -0.5px;
+ margin-bottom: 1.5rem;
+`;
+
+export const Wrapper = styled.div`
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ gap: 1rem;
+`;
diff --git a/src/styles/global.ts b/src/styles/global.ts
index 4680a87..a93363b 100644
--- a/src/styles/global.ts
+++ b/src/styles/global.ts
@@ -22,9 +22,8 @@ i {font-style:normal}
display: flex;
flex-direction: column;
overflow: hidden;
-
+ align-items: center;
min-height: 100vh;
-
}
// 폰트설정
diff --git a/vercel.config b/vercel.config
new file mode 100644
index 0000000..7732282
--- /dev/null
+++ b/vercel.config
@@ -0,0 +1,9 @@
+{
+ "version": 2,
+ "rewrites": [
+ {
+ "source": "/api/(.*)",
+ "destination": "test/$1"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/vercel.json b/vercel.json
new file mode 100644
index 0000000..00e7ecc
--- /dev/null
+++ b/vercel.json
@@ -0,0 +1,3 @@
+{
+ "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }]
+}