diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
new file mode 100644
index 0000000..1c0b8e2
--- /dev/null
+++ b/.github/workflows/frontend.yml
@@ -0,0 +1,21 @@
+name: GCP CI/CD
+on:
+ push:
+ branches: [ main, develop ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: executing remote ssh commands using ssh key
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.HOST }}
+ username: ${{ secrets.USERNAME }}
+ key: ${{ secrets.SSH_KEY }}
+ passphrase: ${{ secrets.PASSPHRASE }}
+ port: 22
+ script: |
+ cd ${{ secrets.PWD }}
+ git pull https://${{ secrets.GIT_TOKEN }}:x-oauth-basic@github.com/Tekken-Supporter/Frontend.git develop
+ pm2 reload tekken7
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..2ba986f
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "chrome",
+ "request": "launch",
+ "name": "Launch Chrome against localhost",
+ "url": "http://localhost:8080",
+ "webRoot": "${workspaceFolder}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/20231214_143319381.mp4 b/20231214_143319381.mp4
new file mode 100644
index 0000000..1ed4ce1
Binary files /dev/null and b/20231214_143319381.mp4 differ
diff --git a/challenge.css b/challenge.css
new file mode 100644
index 0000000..385cb89
--- /dev/null
+++ b/challenge.css
@@ -0,0 +1,842 @@
+@import url('https://fonts.googleapis.com/css2?family=Poppins');
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: 'Poppins', sans-serif;
+}
+
+.container {
+ width: 100%;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.background-clip {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ z-index: -1;
+}
+
+@media (min-aspect-ratio:16/9) {
+ .background-clip {
+ width: 100%;
+ height: auto;
+ }
+}
+
+@media (max-aspect-ratio:16/9) {
+ .background-clip {
+ width: auto;
+ height: 100%;
+ }
+}
+
+header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ padding: 20px 100px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ z-index: 99;
+}
+
+.logo {
+ font-size: 2em;
+ color: #fff;
+ user-select: none;
+}
+
+.navigation a {
+ position: relative;
+ font-size: 1.1em;
+ color: #fff;
+ text-decoration: none;
+ font-weight: 500;
+ margin-left: 40px;
+}
+
+.navigation a::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ bottom: -6px;
+ width: 100%;
+ height: 3px;
+ background: #fff;
+ border-radius: 5px;
+ transform-origin: right;
+ transform: scaleX(0);
+ transition: transform .5s;
+}
+
+.navigation a:hover::after {
+ transform-origin: left;
+ transform: scaleX(1);
+}
+
+.navigation .btnLogout {
+ width: 100px;
+ height: 40px;
+ background: transparent;
+ border: 2px solid #fff;
+ outline: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 1.1em;
+ color: #fff;
+ font-weight: 500;
+ margin-left: 40px;
+ transition: .5s;
+}
+
+.btnQuest-popup {
+ width: 200px;
+ height: 50px;
+ top: 50%;
+ left: 10%;
+ position: absolute;
+ background: transparent;
+ backdrop-filter: blur(20px);
+ border: 2px solid #fff;
+ outline: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 2em;
+ color: #fff;
+ font-weight: 500;
+ /* margin-left: 40px; */
+ transition: .5s;
+ box-shadow: 0 0 10px rgba(0, 0, 0, .5);
+}
+
+/* 대결 신청 팝업 */
+
+.wrapper {
+ position: absolute;
+ left: 8%;
+ width: 400px;
+ height: 600px;
+ background: transparent;
+ background-color: rgba(255, 255, 255, .3);
+ border: 2px solid rgba(255, 255, 255, .5);
+ border-radius: 20px;
+ backdrop-filter: blur(20px);
+ box-shadow: 0 0 30px rgba(0, 0, 0, .5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ transform: scale(0);
+ transition: transform .5s ease, height .2s ease;
+}
+
+.wrapper.active-popup {
+ transform: scale(1);
+}
+
+.wrapper.active {
+ height: 520px;
+}
+
+.wrapper .icon-close {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 45px;
+ height: 45px;
+ background: #272727;
+ font-size: 2em;
+ color: #fff;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-bottom-left-radius: 20px;
+ cursor: pointer;
+ z-index: 1;
+}
+
+.wrapper .form-box {
+ width: 100%;
+ padding: 40px;
+}
+
+.wrapper .form-box h2 {
+ font-size: 2em;
+ color: #272727;
+ text-align: center;
+}
+
+.wrapper form {
+ width: 100%;
+ max-width: 600px;
+}
+
+.wrapper .input-box {
+ position: relative;
+ width: 100%;
+ height: 50px;
+ margin-bottom: 30px;
+}
+
+.wrapper .input-box .icon {
+ position: absolute;
+ left: 10px;
+ font-size: 1.2em;
+ color: #272727;
+ /*line-height: 30px;*/
+}
+
+.wrapper .input-box input, output{
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+}
+
+.wrapper .input-box select {
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+ cursor: pointer;
+}
+
+.wrapper .input-box select option {
+ color: black;
+ background: white;
+}
+
+.wrapper .input-box output {
+ display: block;
+}
+
+.wrapper .input-box textarea {
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+}
+
+.wrapper .input-box label {
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: -37px;
+ padding: 10px;
+ color: black;
+ cursor: text;
+ transition: 0.2s;
+}
+
+.wrapper .quest_btn {
+ padding: 10px 0;
+ width: 100%;
+ height: 45px;
+ background: #272727;
+ border: none;
+ outline: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 1em;
+ color: #fff;
+ font-weight: 500;
+}
+
+/* 대결 수락 팝업*/
+
+.wrapper_unread {
+ position: absolute;
+ left: 8%;
+ width: 400px;
+ height: 600px;
+ background: transparent;
+ background-color: rgba(255, 255, 255, .3);
+ border: 2px solid rgba(255, 255, 255, .5);
+ border-radius: 20px;
+ backdrop-filter: blur(20px);
+ box-shadow: 0 0 30px rgba(0, 0, 0, .5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ transform: scale(0);
+ transition: transform .5s ease, height .2s ease;
+}
+
+.wrapper_unread.active-popup {
+ transform: scale(1);
+}
+
+.wrapper_unread.active {
+ height: 520px;
+}
+
+.wrapper_unread .icon-close2 {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 45px;
+ height: 45px;
+ background: #272727;
+ font-size: 2em;
+ color: #fff;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-bottom-left-radius: 20px;
+ cursor: pointer;
+ z-index: 1;
+}
+
+.wrapper_unread .form-box {
+ width: 100%;
+ padding: 40px;
+}
+
+.wrapper_unread .form-box h2 {
+ font-size: 2em;
+ color: #272727;
+ text-align: center;
+}
+
+.wrapper_unread form {
+ width: 100%;
+ max-width: 600px;
+}
+
+.wrapper_unread .input-box {
+ position: relative;
+ width: 100%;
+ height: 50px;
+ margin-bottom: 30px;
+}
+
+.wrapper_unread .input-box .icon {
+ position: absolute;
+ left: 10px;
+ font-size: 1.2em;
+ color: #272727;
+ /*line-height: 30px;*/
+}
+
+.wrapper_unread .input-box input, output{
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+}
+
+.wrapper_unread .input-box output {
+ display: block;
+}
+
+.wrapper_unread .input-box textarea {
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+}
+
+.wrapper_unread .input-box label {
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: -37px;
+ padding: 10px;
+ color: black;
+ cursor: text;
+ transition: 0.2s;
+}
+
+.wrapper_unread .quest_btn {
+ padding: 10px 0;
+ width: 45%;
+ height: 45px;
+ background: #272727;
+ border: none;
+ outline: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 1em;
+ color: #fff;
+ font-weight: 500;
+ margin: 0 6.8px;
+}
+
+/* 대결 결과 팝업*/
+
+.wrapper_result {
+ position: absolute;
+ left: 8%;
+ width: 400px;
+ height: 600px;
+ background: transparent;
+ background-color: rgba(255, 255, 255, .3);
+ border: 2px solid rgba(255, 255, 255, .5);
+ border-radius: 20px;
+ backdrop-filter: blur(20px);
+ box-shadow: 0 0 30px rgba(0, 0, 0, .5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ transform: scale(0);
+ transition: transform .5s ease, height .2s ease;
+}
+
+.wrapper_result.active-popup {
+ transform: scale(1);
+}
+
+.wrapper_result.active {
+ height: 520px;
+}
+
+.wrapper_result .icon-close3 {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 45px;
+ height: 45px;
+ background: #272727;
+ font-size: 2em;
+ color: #fff;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-bottom-left-radius: 20px;
+ cursor: pointer;
+ z-index: 1;
+}
+
+.wrapper_result .form-box {
+ width: 100%;
+ padding: 40px;
+}
+
+.wrapper_result .form-box h2 {
+ font-size: 2em;
+ color: #272727;
+ text-align: center;
+}
+
+.wrapper_result form {
+ width: 100%;
+ max-width: 600px;
+}
+
+.wrapper_result .input-box {
+ position: relative;
+ width: 100%;
+ height: 50px;
+ margin-bottom: 30px;
+}
+
+.wrapper_result .input-box .icon {
+ position: absolute;
+ left: 10px;
+ font-size: 1.2em;
+ color: #272727;
+ /*line-height: 30px;*/
+}
+
+.wrapper_result .input-box output{
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+ display: block;
+}
+
+.wrapper_result .input-box input {
+ width: 20%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+}
+
+.wrapper_result .input-box label {
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: -37px;
+ padding: 10px;
+ color: black;
+ cursor: text;
+ transition: 0.2s;
+}
+
+.wrapper_result .quest_btn {
+ padding: 10px 0;
+ width: 100%;
+ height: 45px;
+ background: #272727;
+ border: none;
+ outline: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 1em;
+ color: #fff;
+ font-weight: 500;
+ margin: 0 6.8px;
+}
+
+/* 대결 결과 팝업*/
+
+.wrapper_record {
+ position: absolute;
+ left: 8%;
+ width: 400px;
+ height: 600px;
+ background: transparent;
+ background-color: rgba(255, 255, 255, .3);
+ border: 2px solid rgba(255, 255, 255, .5);
+ border-radius: 20px;
+ backdrop-filter: blur(20px);
+ box-shadow: 0 0 30px rgba(0, 0, 0, .5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ overflow: hidden;
+ transform: scale(0);
+ transition: transform .5s ease, height .2s ease;
+}
+
+.wrapper_record.active-popup {
+ transform: scale(1);
+}
+
+.wrapper_record.active {
+ height: 520px;
+}
+
+.wrapper_record .icon-close4 {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 45px;
+ height: 45px;
+ background: #272727;
+ font-size: 2em;
+ color: #fff;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-bottom-left-radius: 20px;
+ cursor: pointer;
+ z-index: 1;
+}
+
+.wrapper_record .form-box {
+ width: 100%;
+ padding: 40px;
+}
+
+.wrapper_record .form-box h2 {
+ font-size: 2em;
+ color: #272727;
+ text-align: center;
+}
+
+.wrapper_record form {
+ width: 100%;
+ max-width: 600px;
+}
+
+.wrapper_record .input-box {
+ position: relative;
+ width: 100%;
+ height: 50px;
+ margin-bottom: 30px;
+}
+
+.wrapper_record .input-box .icon {
+ position: absolute;
+ left: 10px;
+ font-size: 1.2em;
+ color: #272727;
+ /*line-height: 30px;*/
+}
+
+.wrapper_record .input-box output{
+ width: 100%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+ display: block;
+}
+
+.wrapper_record .input-box input {
+ width: 20%;
+ padding: 10px;
+ outline: 0;
+ border: 1px solid #fff;
+ color: black;
+ background: transparent;
+ font-size: 15px;
+ box-sizing: border-box;
+}
+
+.wrapper_record .input-box label {
+ height: 100%;
+ position: absolute;
+ left: 0;
+ top: -37px;
+ padding: 10px;
+ color: black;
+ cursor: text;
+ transition: 0.2s;
+}
+
+/* 리스트 */
+
+.wrapper_list {
+ position: absolute;
+ right: 8%;
+ display: flex;
+ width: 1000px;
+ height: 600px;
+ background: transparent;
+ background-color: rgba(255, 255, 255, .3);
+ border: 2px solid rgba(255, 255, 255, .5);
+ border-radius: 20px;
+ backdrop-filter: blur(20px);
+ box-shadow: 0 0 30px rgba(0, 0, 0, .5);
+ overflow: hidden;
+ transform: scale(1);
+}
+
+.unread_list {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 70px 30px 30px 30px;
+ border-right: 1px solid #ddd;
+}
+
+.past_list {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 70px 30px 30px 30px;
+}
+
+.unread_list h2,
+.past_list h2 {
+ margin-bottom: 20px;
+ text-align: center;
+}
+
+.board_list_ur,
+.board_list_p,
+.board_page {
+ width: 100%;
+ text-align: center;
+}
+
+/*미확인 보드 스타일링*/
+
+.board_list_ur {
+ width: 100%;
+ border-top: 2px solid #000;
+}
+
+.board_list_ur>div {
+ border-bottom: 1px solid #ddd;
+ font-size: 0;
+}
+
+.board_list_ur>div.top {
+ border-bottom: 1.5px solid #000;
+}
+
+.board_list_ur>div:last-child {
+ border-bottom: 1.5px solid #000;
+}
+
+.board_list_ur>div>div {
+ display: inline-block;
+ padding: 15px 0;
+ text-align: center;
+ font-size: 1rem;
+}
+
+.board_list_ur>div.top>div {
+ font-weight: 600;
+}
+
+.board_list_ur .num {
+ width: 10%;
+}
+
+.board_list_ur .name {
+ width: 70%;
+}
+
+.btnAccept-popup {
+ background: none;
+ border: none;
+ color: inherit;
+ font: inherit;
+ cursor: pointer;
+ padding: 0;
+ margin: 0;
+ text-align: left;
+ display: inline;
+ line-height: normal;
+}
+
+.board_list_ur .date {
+ width: 20%;
+}
+
+/* 전적 보드 스타일링 */
+
+.board_list_p {
+ width: 100%;
+ border-top: 2px solid #000;
+}
+
+.board_list_p .top {
+ font-size: 0;
+}
+
+.board_list_p .top > div {
+ display: inline-block;
+ padding: 15px 0;
+ text-align: center;
+ font-size: 1rem;
+}
+
+.board_list_p>div.top>div {
+ font-weight: 600;
+}
+
+.board_list_p .num, .numT {
+ width: 10%;
+}
+
+.board_list_p .name, .nameT {
+ width: 30%;
+}
+
+.board_list_p .result, .resultT {
+ width: 30%;
+}
+
+.board_list_p .date, .dateT {
+ width: 30%;
+}
+
+.list {
+ width: 100%;
+ border-top: 2px solid #000;
+}
+
+.list > div {
+ border-bottom: 1px solid #ddd;
+ font-size: 0;
+}
+
+.list > div:last-child {
+ border-bottom: 1.5px solid #000;
+}
+
+.list > div > div {
+ display: inline-block;
+ padding: 15px 0;
+ text-align: center;
+ font-size: 1rem;
+}
+
+.btnResult-popup {
+ background: none;
+ border: none;
+ color: inherit;
+ font: inherit;
+ cursor: pointer;
+ padding: 0;
+ margin: 0;
+ text-align: left;
+ display: inline;
+ line-height: normal;
+}
+
+/* 페이지 */
+
+.board_page {
+ margin-top: 30px;
+ text-align: center;
+ font-size: 0;
+}
+
+.board_page a {
+ display: inline-block;
+ width: 32px;
+ height: 32px;
+ box-sizing: border-box;
+ vertical-align: middle;
+ border: 1px solid #ddd;
+ border-left: 0;
+ line-height: 100%;
+}
+
+.board_page a.bt {
+ padding-top: 5px;
+ font-size: 1.2rem;
+ letter-spacing: -1px;
+ color: #272727;
+}
+
+.board_page a.num {
+ padding-top: 7px;
+ font-size: 1rem;
+ color: #272727;
+}
+
+.board_page a.num.on {
+ color: #fff;
+}
+
+.board_page a:first-child {
+ border-left: 1px solid #ddd;
+}
\ No newline at end of file
diff --git a/challenge.html b/challenge.html
new file mode 100644
index 0000000..b33f37b
--- /dev/null
+++ b/challenge.html
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tekken Supporter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/challenge.js b/challenge.js
new file mode 100644
index 0000000..5e9839f
--- /dev/null
+++ b/challenge.js
@@ -0,0 +1,8 @@
+const btnLogout = document.querySelector(".btnLogout");
+
+btnLogout.addEventListener("click", function (event) {
+ event.preventDefault();
+ window.location.href = "main.html";
+ console.log("Redirecting to:", window.location.href);
+ localStorage.removeItem("jwt");
+ });
\ No newline at end of file
diff --git a/challenge_accept.js b/challenge_accept.js
new file mode 100644
index 0000000..e80750b
--- /dev/null
+++ b/challenge_accept.js
@@ -0,0 +1,206 @@
+const wrapper2 = document.querySelector('.wrapper_unread')
+const btnPopup2 = document.querySelector('.btnAccept-popup');
+const iconClose2 = document.querySelector('.icon-close2');
+
+var jwt = localStorage.getItem("jwt");
+
+// challenges 배열 초기화 (서버에서 불러온 데이터를 여기에 저장)
+let challenges = [];
+
+// 현재 선택된 challenge 객체를 저장할 변수
+let currentSelectedChallenge = null;
+
+// 대결 수락/거절 응답을 서버에 보내는 함수
+function sendChallengeResponse(challenge_id, responseType) {
+
+ const parsedChallengeId = parseInt(challenge_id, 10);
+
+ console.log("Sending request with challenge_id:", parsedChallengeId, "and message:", responseType);
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("POST", "http://34.127.90.191:3000/challenge/accept");
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send(
+ JSON.stringify({
+ challenge_id: parsedChallengeId,
+ message: responseType
+ })
+ );
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ console.log("Response from server:", this.responseText);
+ if (responseType === 'accept') {
+ Swal.fire({
+ text: "대결이 성사되었습니다!",
+ icon: "success",
+ confirmButtonText: "OK"
+ }).then((result) => { //확인 버튼 누르면
+ if (result.isConfirmed) {
+ window.location.href = "./challenge.html"; //해당 페이지로 리다이렉션
+ }
+ });
+ } else if (responseType === 'deny') {
+ Swal.fire({
+ text: "대결을 거부했습니다!",
+ icon: "success",
+ confirmButtonText: "OK"
+ }).then((result) => { //확인 버튼 누르면
+ if (result.isConfirmed) {
+ window.location.href = "./challenge.html"; //해당 페이지로 리다이렉션
+ }
+ });
+ }
+ } else {
+ console.error("Server returned status code " + this.status);
+ Swal.fire({
+ text: "정보 전송에 실패했습니다",
+ icon: "error",
+ confirmButtonText: "OK",
+ });
+ }
+ }
+ };
+}
+
+// 대결 수락 정보 연결
+document.addEventListener("DOMContentLoaded", loadName);
+document.addEventListener("DOMContentLoaded", loadChallengeInfo);
+
+function loadName() {
+ const userId = localStorage.getItem("userId");
+ jwt = localStorage.getItem("jwt");
+
+ console.log("UserID from localStorage:", userId);
+ console.log("JWT from localStorage:", jwt);
+
+ if (!userId || !jwt) {
+ console.error("No userID or JWT found in localStorage");
+ return;
+ }
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("GET", `http://34.127.90.191:3000/user/info/${userId}`); // 본인 이름 띄우기
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send();
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ try {
+ const objects = JSON.parse(this.responseText);
+ console.log("Response about user:", objects);
+
+ if (objects["status"] == "ok") {
+ document.getElementById("contender_accept").textContent = objects["name"];
+ }
+ } catch (e) {
+ console.error("Error parsing response:", e);
+ }
+ } else {
+ console.error("Server responded with status:", this.status);
+ }
+ }
+ };
+}
+
+function loadChallengeInfo() {
+ const userId = localStorage.getItem("userId");
+ jwt = localStorage.getItem("jwt");
+
+ if (!userId || !jwt) {
+ console.error("No userID or JWT found in localStorage");
+ return;
+ }
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("GET", `http://34.127.90.191:3000/challenge/check/${userId}`); // 신청 정보 띄우기
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send();
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ try {
+ const response = JSON.parse(this.responseText);
+ console.log("Response from challenge check:", response);
+
+ // challengeList가 존재하는지 확인
+ if (response.challengeList && Array.isArray(response.challengeList)) {
+ challenges = response.challengeList; // 서버로부터 받은 데이터를 challenges 배열에 저장
+ const listElement = document.querySelector(".board_list_ur");
+ response.challengeList.forEach((challenge, index) => {
+ const challengeRow = document.createElement("div");
+ challengeRow.innerHTML = `
+ ${index + 1}
+
+ ${new Date(challenge.creationDate).toLocaleDateString()}
+ `;
+ listElement.appendChild(challengeRow);
+ });
+ } else {
+ console.error("challengeList is not an array or undefined");
+ }
+
+ } catch (e) {
+ console.error("Error parsing response", e);
+ }
+ } else {
+ console.error("Server returned status code " + this.status);
+ }
+ }
+ };
+}
+
+// 대결 수락 함수
+function accept() {
+ if (!currentSelectedChallenge) {
+ console.error("No challenge selected");
+ return;
+ }
+ sendChallengeResponse(currentSelectedChallenge.challenge_id, 'accept');
+ console.log("대결 수락");
+}
+
+// 대결 거절 함수
+function deny() {
+ if (!currentSelectedChallenge) {
+ console.error("No challenge selected");
+ return;
+ }
+ sendChallengeResponse(currentSelectedChallenge.challenge_id, 'deny');
+ console.log("대결 거절");
+}
+
+
+document.addEventListener('DOMContentLoaded', function () {
+ document.addEventListener('click', function (event) {
+ // 클릭된 요소가 btnAccept-popup 클래스를 가지고 있는지 확인
+ if (event.target && event.target.classList.contains('btnAccept-popup')) {
+ // 해당 버튼과 관련된 challenge 객체 찾기
+ const matchId = event.target.getAttribute('data-challenge-id');
+ // 문자열로 가져온 matchId를 숫자로 변환 (타입 불일치 방지)
+ const numericChallengeId = parseInt(matchId, 10);
+ // 타입 변환된 ID를 사용하여 객체 찾기
+ const challenge = challenges.find(ch => ch.challenge_id === numericChallengeId);
+ console.log(challenge);
+ if (challenge) {
+ // challenge 객체에서 정보 가져와서 설정하기
+ document.getElementById("challenger_accept").textContent = challenge.challenger;
+ document.getElementById("date_accept").textContent = new Date(challenge.matchDate).toLocaleDateString();
+ document.getElementById("message_accept").textContent = challenge.applymessage; // applymessage를 message_accept에 설정
+ }
+ wrapper2.classList.add('active-popup');
+ currentSelectedChallenge = challenge;
+ }
+ });
+
+ const iconClose2 = document.querySelector('.icon-close2');
+ iconClose2.addEventListener('click', () => {
+ wrapper2.classList.remove('active-popup');
+ });
+});
diff --git a/challenge_apply.js b/challenge_apply.js
new file mode 100644
index 0000000..69f94a9
--- /dev/null
+++ b/challenge_apply.js
@@ -0,0 +1,148 @@
+const wrapper = document.querySelector('.wrapper');
+const btnPopup = document.querySelector('.btnQuest-popup');
+const iconClose = document.querySelector('.icon-close');
+
+btnPopup.addEventListener('click', () => { wrapper.classList.add('active-popup'); });
+iconClose.addEventListener('click', () => { wrapper.classList.remove('active-popup'); });
+
+var jwt = localStorage.getItem("jwt");
+
+/* 대결 신청에 본인 이름 뜨기*/
+document.addEventListener("DOMContentLoaded", loadUser);
+document.addEventListener("DOMContentLoaded", loadContenders);
+
+function loadUser() {
+ const userId = localStorage.getItem("userId");
+ jwt = localStorage.getItem("jwt");
+
+ console.log("UserID from localStorage:", userId);
+ console.log("JWT from localStorage:", jwt);
+
+ if (!userId || !jwt) {
+ console.error("No userID or JWT found in localStorage");
+ return;
+ }
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("GET", `http://34.127.90.191:3000/user/info/${userId}`);
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send();
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ try {
+ const objects = JSON.parse(this.responseText);
+ console.log("Response about user:", objects);
+
+ if (objects["status"] == "ok") {
+ document.getElementById("challenger_apply").textContent = objects["name"];
+ document.getElementById("contender_accept").textContent = objects["name"];
+ }
+ } catch (e) {
+ console.error("Error parsing response:", e);
+ }
+ } else {
+ console.error("Server responded with status:", this.status);
+ }
+ }
+ };
+}
+
+function loadContenders() {
+ const userId = localStorage.getItem("userId"); // 현재 사용자 ID 가져오기
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("GET", `http://34.127.90.191:3000/challenge/name/${userId}`);
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send();
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE && this.status == 200) {
+ const response = JSON.parse(this.responseText); // 전체 응답 파싱
+ console.log("Response about Contenders:", response); // 파싱된 응답 출력
+
+ if (response.status === "ok" && Array.isArray(response.namelist)) {
+ const namelist = response.namelist; // 'namelist'에서 이름 배열을 가져옵니다.
+
+ const selectElement = document.getElementById("contender_apply");
+ namelist.forEach(function (contenderName) {
+ const option = document.createElement("option");
+ option.value = contenderName; // 옵션의 값 설정
+ option.text = contenderName; // 옵션의 텍스트 설정
+ selectElement.appendChild(option);
+ });
+ } else {
+ console.error("No namelist found in the response or response format is incorrect");
+ }
+ }
+ };
+}
+
+/* 대결 신청 */
+function quest() {
+ const challenger = document.getElementById("challenger_apply").value;
+ const contender = document.getElementById("contender_apply").value;
+ const date = document.getElementById("date_apply").value;
+ const message = document.getElementById("message_apply").value;
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("POST", "http://34.127.90.191:3000/challenge/apply");
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+
+ xhttp.send(
+ JSON.stringify({
+ challenger: challenger,
+ contender: contender,
+ matchdate: date,
+ applymessage: message,
+ })
+ );
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState === XMLHttpRequest.DONE) {
+ try {
+ if (this.status >= 200 && this.status < 300) {
+ const objects = JSON.parse(this.responseText);
+ console.log(objects);
+
+ if (objects["status"] === "ok") {
+ localStorage.setItem("jwt", objects["token"]);
+ Swal.fire({
+ text: "대결을 신청했습니다!",
+ icon: "success",
+ confirmButtonText: "OK",
+ }).then((result) => {
+ if (result.isConfirmed) {
+ window.location.href = "./challenge.html";
+ }
+ });
+ } else {
+ Swal.fire({
+ text: "신청에 실패했습니다.",
+ icon: "error",
+ confirmButtonText: "OK",
+ });
+ console.error("Empty response received from the server");
+ }
+ } else {
+ Swal.fire({
+ text: "서버 오류 발생. 잠시 후 다시 시도해주세요.",
+ icon: "error",
+ confirmButtonText: "OK",
+ });
+ console.error("Server responded with status:", this.status);
+ }
+ } catch (e) {
+ Swal.fire({
+ text: "응답 처리 중 오류가 발생했습니다.",
+ icon: "error",
+ confirmButtonText: "OK",
+ });
+ console.error("Error parsing response:", e);
+ }
+ }
+ };
+ return false;
+}
\ No newline at end of file
diff --git a/challenge_result.js b/challenge_result.js
new file mode 100644
index 0000000..0d67133
--- /dev/null
+++ b/challenge_result.js
@@ -0,0 +1,269 @@
+const wrapper3 = document.querySelector('.wrapper_result')
+const btnPopup3 = document.querySelector('.btnResult-popup');
+const wrapper4 = document.querySelector('.wrapper_record')
+
+var jwt = localStorage.getItem("jwt");
+
+// challengess 배열 초기화 (서버에서 불러온 데이터를 여기에 저장)
+let challengess = [];
+
+// 현재 선택된 challengess 객체를 저장할 변수
+let currentSelectedChallenges = null;
+let userName = null;
+
+// 페이지당 항목 수
+const itemsPerPage = 5;
+
+// 대결 전적 정보 연결
+document.addEventListener("DOMContentLoaded", loadName);
+
+function loadName() {
+ const userId = localStorage.getItem("userId");
+ jwt = localStorage.getItem("jwt");
+
+ console.log("UserID from localStorage:", userId);
+ console.log("JWT from localStorage:", jwt);
+
+ if (!userId || !jwt) {
+ console.error("No userID or JWT found in localStorage");
+ return;
+ }
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("GET", `http://34.127.90.191:3000/user/info/${userId}`); // 본인 이름 띄우기
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send();
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ try {
+ const objects = JSON.parse(this.responseText);
+ console.log("Response about user:", objects);
+
+ if (objects["status"] == "ok") {
+ userName = objects["name"];
+ console.log("계정주 이름:" + userName);
+ loadChallengeInfo();
+ updateList(1);
+ }
+ } catch (e) {
+ console.error("Error parsing response:", e);
+ }
+ } else {
+ console.error("Server responded with status:", this.status);
+ }
+ }
+ };
+}
+
+
+function loadChallengeInfo() {
+ const userId = localStorage.getItem("userId");
+ jwt = localStorage.getItem("jwt");
+
+ if (!userId || !jwt) {
+ console.error("No userID or JWT found in localStorage");
+ return;
+ }
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("GET", `http://34.127.90.191:3000/user/match/${userId}`); // 신청 정보 띄우기
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send();
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState == XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ try {
+ const response = JSON.parse(this.responseText);
+ console.log("Response from challenge check:", response);
+
+ if (response.matchlist && Array.isArray(response.matchlist)) {
+ challengess = response.matchlist;
+ updateList(1); // 첫 페이지로 초기화
+ createPaginationButtons(); // 페이지네이션 버튼 생성
+ } else {
+ console.error("matchlist is not an array or undefined");
+ }
+ } catch (e) {
+ console.error("Error parsing response", e);
+ }
+ } else {
+ console.error("Server returned status code " + this.status);
+ }
+ }
+ };
+}
+
+function updateList(pageNumber) {
+ if (!userName) {
+ console.error("userName is not loaded yet");
+ return;
+ }
+ console.log("UserName in another function:", userName);
+
+ const startIndex = (pageNumber - 1) * itemsPerPage;
+ const endIndex = startIndex + itemsPerPage;
+ const paginatedItems = challengess.slice(startIndex, endIndex);
+
+ const listElement = document.querySelector(".list");
+ listElement.innerHTML = "";
+
+ paginatedItems.forEach((challenge, index) => {
+ let nameToShow;
+ if (userName === challenge.challenger)
+ nameToShow = challenge.contender;
+ if (userName === challenge.contender)
+ nameToShow = challenge.challenger;
+ console.log(nameToShow);
+ const challengeRow = document.createElement("div");
+ challengeRow.innerHTML = `
+ ${index + 1}
+
+ ${challenge.winner}
+ ${new Date(challenge.matchDate).toLocaleDateString()}
+ `;
+ console.log(challenge.match_id);
+ listElement.appendChild(challengeRow);
+ });
+}
+
+function createPaginationButtons() {
+ const pageCount = Math.ceil(challengess.length / itemsPerPage);
+ const paginationElement = document.querySelector(".board_page");
+ paginationElement.innerHTML = ""; // 이전 페이지 버튼들을 지우고 새로 시작
+
+ for (let i = 1; i <= pageCount; i++) {
+ const pageButton = document.createElement("a");
+ pageButton.href = "#";
+ pageButton.textContent = i;
+ pageButton.className = "num";
+ pageButton.addEventListener("click", function (e) {
+ e.preventDefault();
+ updateList(i);
+ });
+
+ paginationElement.appendChild(pageButton);
+ }
+}
+
+// 대결 결과 전송
+function result() {
+ if (!currentSelectedChallenges) {
+ console.error("No challenge selected");
+ return;
+ }
+ const challengeId = currentSelectedChallenges.match_id;
+ const challengerName = document.getElementById("challenger_result").textContent;
+ const contenderName = document.getElementById("contender_result").textContent;
+ const scoreChallenger = parseInt(document.getElementById("score_challenger").value, 10);
+ const scoreContender = parseInt(document.getElementById("score_contender").value, 10);
+
+ if (!challengeId || !challengerName || !contenderName || isNaN(scoreChallenger) || isNaN(scoreContender)) {
+ alert("모든 필드를 올바르게 입력해주세요.");
+ return false;
+ }
+
+ const xhttp = new XMLHttpRequest();
+ xhttp.open("POST", "http://34.127.90.191:3000/challenge/result");
+ xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhttp.setRequestHeader("Authorization", "Bearer " + jwt);
+ xhttp.send(
+ JSON.stringify({
+ challenge_id: challengeId,
+ challenger: challengerName,
+ contender: contenderName,
+ score_challenger: scoreChallenger,
+ score_contender: scoreContender
+ }));
+
+ xhttp.onreadystatechange = function () {
+ if (this.readyState === XMLHttpRequest.DONE) {
+ if (this.status >= 200 && this.status < 300) {
+ // 요청이 성공적으로 처리되었을 때의 로직
+ console.log("대결 결과가 성공적으로 제출되었습니다.");
+ if (scoreContender > scoreChallenger) {
+ Swal.fire({
+ text: contenderName + "님이 대결에서 승리했습니다!",
+ icon: "success",
+ confirmButtonText: "OK"
+ }).then((result) => { //확인 버튼 누르면
+ if (result.isConfirmed) {
+ window.location.href = "./challenge.html"; //해당 페이지로 리다이렉션
+ }
+ });
+ } else if (scoreContender == scoreChallenger) {
+ Swal.fire({
+ text: "대결에서 비겼습니다!",
+ icon: "success",
+ confirmButtonText: "OK"
+ }).then((result) => { //확인 버튼 누르면
+ if (result.isConfirmed) {
+ window.location.href = "./challenge.html"; //해당 페이지로 리다이렉션
+ }
+ });
+ } else {
+ Swal.fire({
+ text: challengerName + "님이 대결에서 승리했습니다!",
+ icon: "success",
+ confirmButtonText: "OK"
+ }).then((result) => { //확인 버튼 누르면
+ if (result.isConfirmed) {
+ window.location.href = "./challenge.html"; //해당 페이지로 리다이렉션
+ }
+ });
+ }
+ } else {
+ // 요청 처리 중 오류가 발생했을 때의 로직
+ console.error("오류 발생:", this.status, this.responseText);
+ }
+ }
+ };
+
+ return false;
+}
+
+
+document.addEventListener('click', function (event) {
+ // 클릭된 요소가 btnResult-popup 클래스를 가지고 있는지 확인
+ if (event.target && event.target.classList.contains('btnResult-popup')) {
+ // 해당 버튼과 관련된 challenge 객체 찾기
+ const matchId = event.target.getAttribute('data-match-id');
+ // 문자열로 가져온 matchId를 숫자로 변환 (타입 불일치 방지)
+ const numericMatchId = parseInt(matchId, 10);
+ // 타입 변환된 ID를 사용하여 객체 찾기
+ const challenge = challengess.find(ch => ch.match_id === numericMatchId);
+ console.log(challenge);
+ if (challenge) {
+ // winscore와 losescore 값 확인
+ if (challenge.winscore === "0" && challenge.losescore === "0") {
+ // challenge 객체에서 정보 가져와서 설정하기 (result 팝업)
+ document.getElementById("challenger_result").textContent = challenge.challenger;
+ document.getElementById("contender_result").textContent = challenge.contender;
+ document.getElementById("date_result").textContent = new Date(challenge.matchDate).toLocaleDateString();
+ wrapper3.classList.add('active-popup');
+ } else {
+ // challenge 객체에서 정보 가져와서 설정하기 (record 팝업)
+ document.getElementById("winner_record").textContent = challenge.winscore > challenge.losescore ? challenge.winner : challenge.loser;
+ document.getElementById("loser_record").textContent = challenge.winscore < challenge.losescore ? challenge.winner : challenge.loser;
+ document.getElementById("winscore").textContent = Math.max(challenge.winscore, challenge.losescore);
+ document.getElementById("losescore").textContent = Math.min(challenge.winscore, challenge.losescore);
+ wrapper4.classList.add('active-popup');
+ }
+ currentSelectedChallenges = challenge;
+ }
+ }
+ const iconClose3 = document.querySelector('.icon-close3');
+ iconClose3.addEventListener('click', () => {
+ wrapper3.classList.remove('active-popup');
+ });
+ const iconClose4 = document.querySelector('.icon-close4');
+ iconClose4.addEventListener('click', () => {
+ wrapper4.classList.remove('active-popup');
+ });
+
+});
+
diff --git a/charinfo.css b/charinfo.css
new file mode 100644
index 0000000..2047a2b
--- /dev/null
+++ b/charinfo.css
@@ -0,0 +1,280 @@
+@import url('https://fonts.googleapis.com/css2?family=Poppins');
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: 'Poppins', sans-serif;
+}
+
+.container {
+ width: 100%;
+ height: 100vh;
+ background-color: rgba(0, 0, 0, 0);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.background-clip {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ z-index: -1;
+}
+
+@media (min-aspect-ratio:16/9) {
+ .background-clip {
+ width: 100%;
+ height: auto;
+ }
+}
+
+@media (max-aspect-ratio:16/9) {
+ .background-clip {
+ width: auto;
+ height: 100%;
+ }
+}
+
+body {
+ display: flex;
+ align-items: center;
+ background-position: center;
+ padding: 10px;
+}
+
+header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ padding: 20px 100px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ z-index: 99;
+}
+
+.logo {
+ font-size: 2em;
+ color: #fff;
+ user-select: none;
+}
+
+.navigation a {
+ position: relative;
+ font-size: 1.1em;
+ color: #fff;
+ text-decoration: none;
+ font-weight: 500;
+ margin-left: 40px;
+}
+
+.navigation a::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ bottom: -6px;
+ width: 100%;
+ height: 3px;
+ background: #fff;
+ border-radius: 5px;
+ transform-origin: right;
+ transform: scaleX(0);
+ transition: transform .5s;
+}
+
+.navigation a:hover::after {
+ transform-origin: left;
+ transform: scaleX(1);
+}
+
+.navigation .btnLogout {
+ width: 100px;
+ height: 40px;
+ background: transparent;
+ border: 2px solid #fff;
+ outline: none;
+ border-radius: 6px;
+ cursor: pointer;
+ font-size: 1.1em;
+ color: #fff;
+ font-weight: 500;
+ margin-left: 40px;
+ transition: .5s;
+}
+
+.reviews-container {
+ position: static;
+ width: calc(100%/4 - 20px);
+ top: 50px;
+ padding: 20px;
+ margin: 20px;
+ height: flex;
+ font-weight: 170;
+ font-size: 15px;
+ line-height: 20px;
+ text-transform: uppercase;
+ color: black;
+ background: linear-gradient(180deg, #D9D9D9 0%, rgba(217, 217, 217, 0.51) 0%);
+}
+
+.reviews-container>div {
+ margin-bottom: 15px;
+ color: #333;
+ font-size: 14px;
+ width: 100%;
+ height: 50px;
+}
+
+.reviews-container select,
+.reviews-container input[type="text"],
+.reviews-container textarea {
+ width: 100%;
+ height: 35px;
+ padding: 5px;
+ margin-bottom: 10px;
+}
+
+.reviews-container textarea {
+ height: 80px;
+}
+
+button {
+ padding: 5px;
+}
+
+.pagination-container {
+ position: static;
+ width: calc(100%/4 - 20px);
+ margin: 20px;
+ padding: 20px;
+}
+
+.pagination-container .review-list {
+ width: 100%;
+}
+
+
+.review-list .review {
+ width: 100%;
+ border: 1px solid #ccc;
+ padding: 10px;
+ margin-bottom: 20px;
+ background: linear-gradient(180deg, #D9D9D9 0%, rgba(217, 217, 217, 0.51) 0%);
+}
+
+.content {
+ position: static;
+ width: calc(100%/4 - 20px);
+ margin: 20px;
+ padding: 20px;
+ height: flex;
+ text-align: center;
+ font-family: 'Inter';
+ font-style: italic;
+ font-weight: 170;
+ font-size: 20px;
+ line-height: 30px;
+ text-transform: uppercase;
+ color: black;
+ background: linear-gradient(180deg, #D9D9D9 0%, rgba(217, 217, 217, 0.51) 0%);
+}
+
+.characterlist {
+ width: 100%;
+ text-align: center;
+}
+
+.infoandpic_char {
+ position: static;
+ width: calc(100%/4 - 20px);
+ margin: 20px;
+ padding: 20px;
+ height: flex;
+ text-align: center;
+ font-family: 'Inter';
+ font-style: italic;
+ font-weight: 170;
+ font-size: 20px;
+ line-height: 30px;
+ text-transform: uppercase;
+ color: black;
+}
+
+.character-container {
+ display: none;
+ background: linear-gradient(180deg, #D9D9D9 0%, rgba(217, 217, 217, 0.51) 0%);
+ border: 1px solid #ccc;
+ padding: 10px;
+}
+
+.character-info {
+ font-weight: bold;
+ color: #333;
+}
+
+.character-image {
+ width: 300px;
+ height: auto;
+ margin-top: 10px;
+}
+
+.linkposition {
+ position: relative;
+ width: 100%;
+ height: flex;
+
+ font-size: 12px;
+ display: inline-block;
+ background: linear-gradient(180deg, #d9d9d9e7 0%, rgba(167, 212, 227, 0.56) 0%);
+ font-family: 'Noto+Serif+KR', sans-serif;
+}
+
+img {
+ width: 100%;
+}
+
+.contitle {
+ position: absolute;
+ left: 100px;
+ height: 60px;
+ top: 64px;
+ font-family: 'Inter';
+ font-style: normal;
+ line-height: 20px;
+ text-transform: uppercase;
+ color: white;
+}
+
+h3 {
+ color: black;
+ text-align: center;
+ font-family: 'Noto+Serif+KR', sans-serif;
+}
+
+#btn-all-close {
+ width: 100%;
+ background-color: #b1a5dd;
+ font-size: 13px;
+ border: none;
+ color: #fff;
+ cursor: pointer;
+ float: right;
+}
+
+#btn-all-close:hover {
+ background-color: yellow;
+ color: #000;
+ transition: all.35s;
+}
+
+.page-link {
+ cursor: pointer;
+ padding: 5px;
+
+ margin-left: 5px;
+ border: 1px solid #ccc;
+ background: linear-gradient(180deg, #D9D9D9 0%, rgba(217, 217, 217, 0.51) 0%);
+}
\ No newline at end of file
diff --git a/charinfo.html b/charinfo.html
new file mode 100644
index 0000000..09596fd
--- /dev/null
+++ b/charinfo.html
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+ character_info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
캐릭터 리뷰
+
+
+
+
+
+
+
+
+
+
+
AKUMA
+
ALISA
+
ARMOR KING
+
ASUKA
+
BOB
+
BRYAN
+
CLAUDIO
+
DEVIL JIN
+
EDDY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/charinfo.js b/charinfo.js
new file mode 100644
index 0000000..d33913a
--- /dev/null
+++ b/charinfo.js
@@ -0,0 +1,405 @@
+var jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCIsImlkIjoidGVzdCIsImlhdCI6MTY5OTYzMjA1NywiZXhwIjoxNzAyMjI0MDU3fQ.P0AX2jyS18tjHFKBQRy6Wi6OWG9AlDQ4hJtpZp3woB0";
+var reviews = [];
+const reviewsPerPage = 3;
+let currentPage = 1;
+
+document.addEventListener("DOMContentLoaded", loadReviews(currentPage));
+
+var submitButton = document.getElementById("submit");
+// 이벤트 리스너를 추가하기 전에 엘리먼트가 존재하는지 확인
+
+// [리뷰창 보이게 만들기]
+//loadReviews > 리뷰 편집기능과는 별개로 사용자에게 리뷰공개
+loadReviews(currentPage);
+loadPageNumbers();//index of lists
+
+function loadPageNumbers() {
+ const paginationContainer = document.getElementById("pagination-container");
+ const totalPages = Math.ceil(20 / reviewsPerPage);
+
+ for (let i = 0; i < totalPages; i++) {
+ const pageNumber = i + 1;
+ const pageLink = document.createElement("span");
+ pageLink.textContent = pageNumber;
+ pageLink.classList.add("page-link");
+ pageLink.addEventListener("click", () => {
+ currentPage = i + 1;
+ loadReviews(currentPage);
+ const pageLinks = document.querySelectorAll(".page-link");
+
+ pageLinks.forEach((link, index) => {
+ if (index + 1 === currentPage) {
+ link.classList.add("active");
+ } else {
+ link.classList.remove("active");
+ }
+ });
+ });
+ paginationContainer.appendChild(pageLink);
+ }
+}
+
+function loadReviews(page) {
+ var xhr = new XMLHttpRequest();
+ var url = "http://34.127.90.191:3000/character/review?c_name=&id=&reviewData=&creationTime=";
+
+ xhr.open("GET", url, true);
+ xhr.onreadystatechange = function () {
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ // 서버 응답에 따른 추가 작업 수행
+ reviews = JSON.parse(xhr.responseText);
+ console.log(reviews);
+ const startIndex = (page - 1) * reviewsPerPage;
+ const endIndex = startIndex + reviewsPerPage;
+ const currentPageReviews = reviews.slice(startIndex, endIndex);
+ displayReviews(currentPageReviews);
+ } else {
+ console.error("서버 에러:", xhr.status);
+ }
+ }
+ };
+ xhr.send();
+}
+
+function displayReviews(reviews) {//loadReviews 관련
+ const reviewListContainer = document.getElementById("reviewList");
+ reviewListContainer.innerHTML = "";
+
+ reviews.forEach((review) => {
+ const reviewElement = document.createElement("div");
+ reviewElement.classList.add("review");
+ reviewElement.innerHTML = `
+ 캐릭터 이름: ${review.c_name || "알 수 없음"}
+ 사용자 ID: ${review.id || "알 수 없음"}
+ 리뷰 내용: ${review.reviewData || "리뷰 없음"}
+ 작성 시간: ${review.Creationtime || "알 수 없음"}
+
+
+ `;
+ reviewListContainer.appendChild(reviewElement);
+ });
+}
+
+// [ 캐릭터 목록들 정리버튼 ]
+const btnAllClose = document.getElementById('btn-all-close');
+btnAllClose.addEventListener('click', resetCharacterContainers);
+console.log('all close button click');
+function resetCharacterContainers() {
+ const characterContainers = document.querySelectorAll('.character-container');
+ characterContainers.forEach(container => {
+ container.style.display = 'none';
+ });
+}
+// 각 캐릭터 링크에 이벤트 리스너 추가
+const characterLinks = document.querySelectorAll('.character-link');
+characterLinks.forEach(link => {
+ link.addEventListener('click', showCharacterInfo);
+});
+// 클릭한 캐릭터의 데이터 얻기
+function showCharacterInfo(event) {
+ const characterId = event.target.getAttribute('data-character');
+ const characterContainers = document.querySelectorAll('.character-container');
+ characterContainers.forEach(container => {
+ container.style.display = 'none';
+ });
+ // 클릭한 캐릭터에 해당하는 컨테이너 보이기
+ const selectedContainer = document.getElementById(characterId);
+ if (selectedContainer) {
+ selectedContainer.style.display = 'block';
+ }
+}
+
+///////////////// 캐릭터 설명창입니다 //////////////
+var xhr7 = new XMLHttpRequest();
+var url = "http://34.127.90.191:3000/character/main";
+
+xhr7.open("GET", url, true);
+xhr7.onreadystatechange = function () {
+ if (xhr7.readyState == 4) {
+ if (xhr7.status == 200) {
+ // 서버 응답에 따른 추가 작업 수행
+ tips = JSON.parse(xhr7.responseText);
+ console.log(tips);
+ var akuma =tips[0];
+ var alisa = tips[1];
+ var armor = tips[2];
+ var asuka = tips[3];
+ var bob =tips[4];
+ var bryan =tips[5];
+ var claudio =tips[6];
+ var devil =tips[7];
+ var eddy =tips[9];
+
+ checkLevel1(akuma);
+ checkLevel2(alisa);
+ checkLevel3(armor);
+ checkLevel4(asuka);
+ checkLevel5(bob);
+ checkLevel6(bryan);
+ checkLevel7(claudio);
+ checkLevel8(devil);
+ checkLevel9(eddy);
+ } else {
+ console.error("서버 에러:", xhr7.status);
+ }
+ }
+ };
+ xhr7.send();
+
+ function getChacImageDynamic(chacName) { //모두 대문자로 받음
+ return `https://github.com/Tekken-Supporter/Frontend/blob/main/IMAGES/${chacName}`+ `.png?raw=true`;
+ }
+ ///~295 같은형식의 코드들
+function checkLevel1(tips) {
+ var characterinfo = document.getElementById('character1');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel2(tips) {
+ var characterinfo = document.getElementById('character2');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel3(tips) {
+ var characterinfo = document.getElementById('character3');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel4(tips) {
+ var characterinfo = document.getElementById('character4');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel5(tips) {
+ var characterinfo = document.getElementById('character5');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+
+
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel6(tips) {
+ var characterinfo = document.getElementById('character6');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+
+
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel7(tips) {
+ var characterinfo = document.getElementById('character7');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+
+
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel8(tips) {
+ var characterinfo = document.getElementById('character8');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+function checkLevel9(tips) {
+ var characterinfo = document.getElementById('character9');
+ var tip = document.createElement('div');
+ tip.classList.add('tips');
+ console.log(tips.difficulty);
+ tip.innerHTML = `${tips.difficulty}
`;
+ characterinfo.appendChild(tip);
+
+ var pic = document.createElement('div');
+ pic.innerHTML = `
+
+ `;
+ characterinfo.appendChild(pic);
+}
+
+//여기서부터 수정필요한 REVIEW 코드들:put, delete, post
+// 1 GET, reviewsend. js에 따로 있음.
+// 2 PUT, update 리뷰 수정 함수 > 미루기
+function updateReview(reviewId) {
+ var modifiedReviewContent = prompt("수정된 리뷰 내용을 입력하세요:");
+ if (modifiedReviewContent !== null) {
+ var modifiedTime = new Date().toISOString();
+
+ var updatedData = {
+ reviewId: reviewId,
+ modifiedTime: modifiedTime,
+ reviewData: modifiedReviewContent,
+ };
+
+ // XMLHttpRequest 객체 생성
+ var xhr3 = new XMLHttpRequest();
+ var url = "http://34.127.90.191:3000/character/review?c_name=&id=&reviewData=&creationTime=";
+
+ xhr3.open("PUT", url, true);
+ xhr3.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
+ xhr3.setRequestHeader("Authorization", "Bearer " + jwt);
+
+ xhr3.onreadystatechange = function () {
+ if (xhr3.readyState == 4) {
+ if (xhr3.status == 200) {
+ console.log("서버 응답:", xhr3.responseText);
+ // 서버 응답에 따른 추가 작업 수행
+ var response = JSON.parse(xhr3.responseText);
+ if (response.status === "success") {
+ // 리뷰가 성공적으로 수정된 경우에 처리할 내용 추가
+ // 예: 수정된 리뷰를 웹에 보여주기
+ displayReview(response.data);
+ } else {
+ // 리뷰 수정이 실패한 경우에 처리할 내용 추가
+ console.error("리뷰 수정 실패:", response.message);
+ }
+ } else {
+ console.error("서버 에러:", xhr3.status);
+ }
+ }
+ };
+ xhr3.send(JSON.stringify(updatedData));
+ }
+}
+
+//3 delete, Review
+var deleteButtons = document.querySelectorAll(".deleteButton");
+deleteButtons.forEach(function (deleteButton) {
+ deleteButton.addEventListener("click", function () {
+ var reviewId = deleteButton.getAttribute("data-reviewid");
+ console.log(reviewId);
+ deleteReviewFunction(reviewId);
+ });
+});
+
+function deleteReview(reviewId) {
+ var xhr4 = new XMLHttpRequest();
+ var url = "http://34.127.90.191:3000/character/review";
+
+ xhr4.open("DELETE", url + "?number" + reviewId, true);
+
+ xhr4.onreadystatechange = function () {
+ if (xhr4.readyState == 4) {
+ if (xhr4.status == 200) {
+ console.log("서버 응답:", xhr4.responseText);
+ var response = JSON.parse(xhr4.responseText);
+ if (response.status === "success") {
+ removeReviewFromUI(reviewId);
+ } else {
+ console.error("리뷰 삭제 실패:", response.message);
+ }
+ } else {
+ console.error("서버 에러:", xhr4.status);
+ }
+ }
+ };
+ xhr4.send();
+}
+
+// 4. 리뷰를 웹에 보여주는 함수 (UI 업데이트)
+function displayReview(reviewData) {
+ reviews.push(reviewData);// 리뷰 배열에 추가
+ // 리뷰를 웹에 표시
+ updateReviewList();
+}
+
+// 리뷰를 웹에서 제거하는 함수 (UI 업데이트)
+function removeReviewFromUI(reviewId) {
+ // 리뷰 목록에서 해당 리뷰를 찾아서 제거
+ reviews = reviews.filter(review => review.id !== reviewId);
+ // 리뷰를 웹에 표시
+ updateReviewList();
+}
+
+// 2,4 related: 리뷰 목록을 업데이트하는 함수
+function updateReviewList() {
+
+ var reviewListContainer = document.getElementById("reviewList");
+ // 이전 리뷰 목록을 지우기
+ reviewListContainer.innerHTML = "";
+
+ // 각 리뷰에 대해 HTML 엘리먼트를 생성하여 추가
+ reviews.forEach(function (review) {
+ var reviewElement = document.createElement("div");
+ reviewElement.classList.add("review");
+ reviewElement.innerHTML = `
+ 캐릭터 이름: ${review.c_name}
+ 사용자 ID: ${review.id}
+ 리뷰 내용: ${review.reviewData}
+ 작성 시간: ${review.CreationTime}
+ 수정 시간: ${review.modifiedTime}
+
+
+ `;
+ reviewListContainer.appendChild(reviewElement);
+ });
+}
+
+
+
+
+
+
+
+
+
diff --git a/login.js b/login.js
index 8180855..fd5a6a6 100644
--- a/login.js
+++ b/login.js
@@ -1,6 +1,6 @@
var jwt = localStorage.getItem("jwt");
if (jwt != null) {
- window.location.href = "./index.html";
+ window.location.href = "./myinfo.html";
}
function login() {
@@ -8,7 +8,7 @@ function login() {
const password = document.getElementById("password").value;
const xhttp = new XMLHttpRequest();
- xhttp.open("POST", "http://34.168.80.42:3000/auth/login");
+ xhttp.open("POST", "http://34.127.90.191:3000/auth/login");
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhttp.send(
JSON.stringify({
@@ -16,29 +16,52 @@ function login() {
password: password,
})
);
+
xhttp.onreadystatechange = function () {
- if (this.readyState == 4) {
- const objects = JSON.parse(this.responseText);
- console.log(objects);
- if (objects["status"] == "ok") {
- localStorage.setItem("jwt", objects["token"]);
- Swal.fire({
- text: objects["message"],
- icon: "success",
- confirmButtonText: "OK",
- }).then((result) => {
- if (result.isConfirmed) {
- window.location.href = "./index.html";
+ if (this.readyState === XMLHttpRequest.DONE) {
+ try {
+ if (this.status >= 200 && this.status < 300) {
+ const objects = JSON.parse(this.responseText); //서버로부터 받은 응답텍스트를 JSON 객체로 변환
+ console.log(objects); //서버로부터 받은 응답 콘솔에 출력
+
+ if (objects["status"] === "ok") { //서버로부터 받은 응답의 상태가 ok일 때
+ localStorage.setItem("jwt", objects["token"]); //jwt라는 이름으로 서버에서 받은 토큰을 로컬스토리지에 저장
+ localStorage.setItem("userId", id); // 사용자 ID를 로컬 스토리지에 저장
+ Swal.fire({
+ text: "로그인 성공!",
+ icon: "success",
+ confirmButtonText: "OK", //성공 메시지를 사용자에게 보여줌
+ }).then((result) => { //확인 버튼 누르면
+ if (result.isConfirmed) {
+ window.location.href = "./myinfo.html"; //해당 페이지로 리다이렉션
+ }
+ });
+ } else {
+ Swal.fire({
+ text: "로그인에 실패했습니다",
+ icon: "error",
+ confirmButtonText: "OK",
+ });
+ console.error("Empty response received from the server");
}
- });
- } else {
+ } else {
+ Swal.fire({
+ text: "서버 오류 발생. 잠시 후 다시 시도해주세요.",
+ icon: "error",
+ confirmButtonText: "OK",
+ });
+ console.error("Server responded with status:", this.status);
+ }
+ } catch (e) {
Swal.fire({
- text: objects["message"],
+ text: "응답 처리 중 오류가 발생했습니다.",
icon: "error",
confirmButtonText: "OK",
});
+ console.error("Error parsing response:", e);
}
}
};
return false;
-}
\ No newline at end of file
+}
+
diff --git a/style.css b/main.css
similarity index 88%
rename from style.css
rename to main.css
index 669f880..c66c155 100644
--- a/style.css
+++ b/main.css
@@ -93,26 +93,29 @@ header {
transform: scaleX(1);
}
-.navigation .btnLogin-popup {
- width: 130px;
- height: 50px;
+.btnLogin-popup {
+ width: 310px;
+ height: 100px;
+ position: absolute;
background: transparent;
+ backdrop-filter: blur(20px);
border: 2px solid #fff;
outline: none;
border-radius: 6px;
cursor: pointer;
- font-size: 1.1em;
+ font-size: 4em;
color: #fff;
font-weight: 500;
- margin-left: 40px;
+ /* margin-left: 40px; */
transition: .5s;
+ box-shadow: 0 0 10px rgba(0, 0, 0, .5);
}
.wrapper {
position: relative;
width: 400px;
height: 440px;
- background: transparent;
+ background-color: rgba(255, 255, 255, 0.3);
border: 2px solid rgba(255, 255, 255, .5);
border-radius: 20px;
backdrop-filter: blur(20px);
@@ -202,6 +205,7 @@ header {
transition: .5s;
}
+
.input-box input:focus~label,
.input-box input:valid~label {
top: -5px;
@@ -227,6 +231,14 @@ header {
line-height: 57px;
}
+.input-box .icon-eyeoff {
+ position: absolute;
+ right: 8px;
+ font-size: 1.2em;
+ color: #272727;
+ line-height: 57px;
+}
+
.remember-forgot {
font-size: .9em;
color: #272727;
@@ -250,7 +262,20 @@ header {
text-decoration: underline;
}
-.btn {
+.register_btn {
+ width: 100%;
+ height: 45px;
+ background: #272727;
+ border: none;
+ outline: none;
+ border-radius: 6px;
+ font-size: 1em;
+ color: #fff;
+ cursor: pointer;
+ font-weight: 500;
+}
+
+.login_btn {
width: 100%;
height: 45px;
background: #272727;
diff --git a/main.html b/main.html
index 2453569..b262124 100644
--- a/main.html
+++ b/main.html
@@ -5,98 +5,125 @@
-
- 철권 로그인과 회원가입
-
+
+
+
+
+
+
+
+
+ Tekken Supporter
+
-