diff --git a/package-lock.json b/package-lock.json index ef6c6e3..9ffa87a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,8 @@ "react-datepicker": "^6.9.0", "react-dom": "^18", "react-hook-form": "^7.51.2", + "react-query": "^3.39.3", + "react-toastify": "^10.0.5", "rsuite": "^5.58.1", "styled-components": "^6.1.8" }, @@ -7773,8 +7775,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { "version": "2.2.2", @@ -7849,7 +7850,6 @@ "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, "engines": { "node": ">=0.6" } @@ -8005,7 +8005,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8023,6 +8022,21 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -8768,8 +8782,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/consola": { "version": "3.2.3", @@ -9503,6 +9516,11 @@ "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, "node_modules/detect-package-manager": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz", @@ -11302,8 +11320,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -12086,7 +12103,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -12095,8 +12111,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "1.3.8", @@ -12754,6 +12769,11 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13155,6 +13175,15 @@ "react": ">= 0.14.0" } }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -13249,6 +13278,11 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "node_modules/miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -13345,7 +13379,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13427,6 +13460,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -13983,6 +14024,11 @@ "integrity": "sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==", "dev": true }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "node_modules/ohash": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", @@ -14014,7 +14060,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -14250,7 +14295,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -15747,6 +15791,31 @@ "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" } }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -15756,6 +15825,18 @@ "node": ">=0.10.0" } }, + "node_modules/react-toastify": { + "version": "10.0.5", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz", + "integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==", + "dependencies": { + "clsx": "^2.1.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-use-set": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/react-use-set/-/react-use-set-1.0.0.tgz", @@ -16113,6 +16194,11 @@ "node": ">= 0.10" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -16272,7 +16358,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -16287,7 +16372,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -18245,6 +18329,15 @@ "node": ">= 10.0.0" } }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -18834,8 +18927,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "2.4.3", diff --git a/package.json b/package.json index 46dbdf6..f66e0f4 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "react-datepicker": "^6.9.0", "react-dom": "^18", "react-hook-form": "^7.51.2", + "react-query": "^3.39.3", + "react-toastify": "^10.0.5", "rsuite": "^5.58.1", "styled-components": "^6.1.8" }, diff --git a/src/apis/http.tsx b/src/apis/http.tsx index eb43464..2629ec4 100644 --- a/src/apis/http.tsx +++ b/src/apis/http.tsx @@ -58,7 +58,7 @@ axiosInstance.interceptors.response.use( console.log(err); // access token 만료 시 if (response?.status && response?.status === 403) { - window.location.href = "/login"; + window.location.href = "/user/login"; localStorage.clear(); return config; } diff --git a/src/app/info/board/like/page.tsx b/src/app/info/board/like/page.tsx new file mode 100644 index 0000000..3cf9e0c --- /dev/null +++ b/src/app/info/board/like/page.tsx @@ -0,0 +1,11 @@ +import { InfoBoardLike } from "@/features/info/board/like/InfoBoardLike"; + +const InfoBoardLikePage = () => { + return ( + <> + + + ); +}; + +export default InfoBoardLikePage; diff --git a/src/app/user/club/page.tsx b/src/app/user/club/create/page.tsx similarity index 67% rename from src/app/user/club/page.tsx rename to src/app/user/club/create/page.tsx index 17c54f5..20b17db 100644 --- a/src/app/user/club/page.tsx +++ b/src/app/user/club/create/page.tsx @@ -1,8 +1,8 @@ import SetupUserClub from "@/features/user/club/SetupUserClub"; -const UserClub = () => { +const UserClubCreate = () => { // 모임 등록 페이지 return ; } -export default UserClub; \ No newline at end of file +export default UserClubCreate; \ No newline at end of file diff --git a/src/app/user/club/join/[clubId]/page.tsx b/src/app/user/club/join/[clubId]/page.tsx new file mode 100644 index 0000000..f248f88 --- /dev/null +++ b/src/app/user/club/join/[clubId]/page.tsx @@ -0,0 +1,7 @@ +import InvitationCode from "@/features/user/joinClub/InvitationCode"; + +const JoinClubInfo = () => { + return ; +}; + +export default JoinClubInfo; diff --git a/src/app/user/join/page.tsx b/src/app/user/club/join/page.tsx similarity index 100% rename from src/app/user/join/page.tsx rename to src/app/user/club/join/page.tsx diff --git a/src/app/login/page.tsx b/src/app/user/login/page.tsx similarity index 61% rename from src/app/login/page.tsx rename to src/app/user/login/page.tsx index c9abb93..30cc0dc 100644 --- a/src/app/login/page.tsx +++ b/src/app/user/login/page.tsx @@ -1,4 +1,4 @@ -import Login from "@/features/Login/Login"; +import Login from "@/features/user/Login/Login"; const UserLogin = () => { return ; diff --git a/src/assets/images/ic_header_menuToggle.png b/src/assets/images/ic_header_menuToggle.png new file mode 100644 index 0000000..7031ab2 Binary files /dev/null and b/src/assets/images/ic_header_menuToggle.png differ diff --git a/src/components/atoms/comment/Comment.css b/src/components/atoms/comment/Comment.css index 09aa3c6..a3486f3 100644 --- a/src/components/atoms/comment/Comment.css +++ b/src/components/atoms/comment/Comment.css @@ -1,10 +1,11 @@ .comment__box { display: flex; align-items: center; - width:fit-content; + width: 100%; } .comment__box__wrapper { margin-top: 0.94rem; + width: 100%; } .comment__box__is__reply img { width: 0.875rem; @@ -15,6 +16,7 @@ .comment__box__profile { display: flex; align-items: center; + width: 100%; } .comment__box__profile img { width: 1.8125rem; @@ -27,10 +29,9 @@ margin-bottom: 0.31rem; display: flex; flex-direction: column; + margin-right: auto; } -.comment__box__edit { - margin-left: auto; -} + .comment__box__margin__left { margin-left: 2.4125rem; } diff --git a/src/components/atoms/comment/Comment.tsx b/src/components/atoms/comment/Comment.tsx index 5fea591..93abe02 100644 --- a/src/components/atoms/comment/Comment.tsx +++ b/src/components/atoms/comment/Comment.tsx @@ -1,52 +1,47 @@ import replyIcon from "@/assets/images/reply.png"; -import Image, { StaticImageData } from "next/image"; +import { IMAGES } from "@/constants/images"; +import { useCommentDeleteMutation } from "@/hook/comment/useCommentDeleteMutation"; +import { CommentsProps } from "@/types/comment"; +import Image from "next/image"; +import { usePathname } from "next/navigation"; import { Text } from "../text"; import "./Comment.css"; -export interface CommentProps { - commentId: number; - createdAt: string; - isEditAllowed: boolean; //댓글 작성자와 동일일 경우 수정 - memberInfo: { - profile: StaticImageData; - name: string; - }; - content: string; - parentId?: number; //대댓글일경우 필요 - reply?: boolean; // 대댓글일 경우 true - onClick?: () => void; //대댓글 작성 핸들러 -} -export function Comment({ - commentId, - createdAt, - isEditAllowed, - memberInfo: { profile, name }, - content, - parentId, - reply, - onClick, -}: CommentProps) { +export function Comment({ item, onClick }: CommentsProps) { + const path = usePathname(); + const pathProps = path.split("/").slice(1); + const { mutate: deleteCommentMutate } = useCommentDeleteMutation({ + clubId: parseInt(pathProps[0], 10), + postId: parseInt(pathProps[3], 10), + commentId: item.commentId, + }); + const handleDeleteComment = () => { + deleteCommentMutate(); + }; + const handleReplyClick = () => { + onClick(item.commentId); + }; return (
- {reply && ( + {item.parentId && (
replyIcon
)}
- userProfile + userProfile
- {name} + 이름자리 - {createdAt} + {item.createdAt}
{/* 유저 정보 비교 후 동일하다면 */} - {isEditAllowed && ( + {item.memberId && (
수정 @@ -54,7 +49,12 @@ export function Comment({ | - + 삭제
@@ -63,12 +63,17 @@ export function Comment({
- {content} + {item.content}
- {!reply && ( + {!item.parentId && (
- + 답글 쓰기
diff --git a/src/components/atoms/comment/CommentInput.tsx b/src/components/atoms/comment/CommentInput.tsx index 1a0c6dc..edb6806 100644 --- a/src/components/atoms/comment/CommentInput.tsx +++ b/src/components/atoms/comment/CommentInput.tsx @@ -1,24 +1,66 @@ import submitIcon from "@/assets/images/ic_commentSubmit.png"; +import { useCommentChildMutation } from "@/hook/comment/useCommentChildMutation"; +import { useCommentMutation } from "@/hook/comment/useCommentMutation"; +import { CommentData } from "@/types/comment"; import Image from "next/image"; -import React, { useState } from "react"; +import { usePathname } from "next/navigation"; +import { useForm } from "react-hook-form"; import "./CommentInput.css"; -export const CommentInput = () => { - const [comment, setComment] = useState(""); - const handleChange = (event: React.ChangeEvent) => { - setComment(event.target.value); +interface props { + parentId?: number; +} +export const CommentInput = ({ parentId }: props) => { + const path = usePathname(); + const pathProps = path.split("/").slice(1); + const { + register, + formState: { errors }, + reset, + handleSubmit, + } = useForm({ + mode: "onSubmit", + defaultValues: { + postId: undefined, + content: "", + }, + }); + const { mutate: ToCommentMutate } = useCommentMutation({ + clubId: parseInt(pathProps[0], 10), + postId: parseInt(pathProps[3], 10), + }); + const { mutate: ToCommentChildMutate } = useCommentChildMutation({ + clubId: parseInt(pathProps[0], 10), + postId: parseInt(pathProps[3], 10), + }); + const onCommentSubmit = (data: CommentData) => { + const postData = { + ...data, + postId: parseInt(pathProps[3], 10), + }; + ToCommentMutate(postData); + reset(); }; - + const onCommentChildSubmit = (data: CommentData) => { + const postData = { + ...data, + postId: parseInt(pathProps[3], 10), + parentId: parentId, + }; + ToCommentChildMutate(postData); + reset(); + }; + const handleCommantFn = + parentId === undefined ? onCommentSubmit : onCommentChildSubmit; return (
-
- - submit -
+
+ + +
); }; diff --git a/src/components/atoms/heart/Heart.tsx b/src/components/atoms/heart/Heart.tsx index c68efd0..c1cb82c 100644 --- a/src/components/atoms/heart/Heart.tsx +++ b/src/components/atoms/heart/Heart.tsx @@ -16,7 +16,7 @@ export function Heart({ onClick, }: ChipProps) { return ( -
+
hearts {hearts} diff --git a/src/components/atoms/input/Input.tsx b/src/components/atoms/input/Input.tsx index effacbe..8f308ac 100644 --- a/src/components/atoms/input/Input.tsx +++ b/src/components/atoms/input/Input.tsx @@ -1,8 +1,10 @@ // Input.tsx "use client"; +import { COLORS } from "@/styles"; import { forwardRef, useState } from "react"; import { ControllerRenderProps } from "react-hook-form"; +import { Text } from "../text"; import "./input.css"; export interface InputProps { @@ -50,8 +52,14 @@ export const Input = forwardRef( /> {maxCnt !== 0 && (

- {textCnt} - /{maxCnt} + {textCnt} + /{maxCnt}

)}
diff --git a/src/components/atoms/input/input.css b/src/components/atoms/input/input.css index 62f5cf8..e474fa5 100644 --- a/src/components/atoms/input/input.css +++ b/src/components/atoms/input/input.css @@ -1,7 +1,7 @@ .input__wrapper { display: flex; flex-direction: column; - align-items: center; + align-items: flex-end; justify-content: center; gap: 0.3rem; text-align: right; @@ -21,6 +21,7 @@ cursor: pointer; border-radius: 10px; padding: 0 1rem; + font-family: "Pretendard Variable"; } input::-webkit-input-placeholder { diff --git a/src/components/atoms/search/Search.tsx b/src/components/atoms/search/Search.tsx index d6af7da..0486cee 100644 --- a/src/components/atoms/search/Search.tsx +++ b/src/components/atoms/search/Search.tsx @@ -1,8 +1,33 @@ import { ICONS } from "@/constants/images"; import Image from "next/image"; +import { useEffect, useState } from "react"; + +import { useDebounce } from "@/hook/schedule/useDebounce"; +import { ScheduleKeyword } from "@/types/schedule"; import "./Search.css"; -export const Search = () => { +export interface SearchProps { + refetchKeywordSchedule: ({ keyword }: ScheduleKeyword) => void; +} + +export const Search = ({ + refetchKeywordSchedule +}: SearchProps) => { + const [keyword, setKeyword] = useState(''); + const debounceKeyword = useDebounce({keyword}); + + const handleSearch = (event: React.ChangeEvent) => { + setKeyword(event.target.value); + }; + + useEffect(() => { + if(refetchKeywordSchedule){ + refetchKeywordSchedule({ + keyword + }) + } + }, [debounceKeyword]) + return(
{ alt="search" />
diff --git a/src/components/atoms/textarea/Textarea.tsx b/src/components/atoms/textarea/Textarea.tsx index 190ce24..1145743 100644 --- a/src/components/atoms/textarea/Textarea.tsx +++ b/src/components/atoms/textarea/Textarea.tsx @@ -3,6 +3,8 @@ import React, { useState } from "react"; import { ControllerRenderProps } from "react-hook-form"; +import { COLORS } from "@/styles"; +import { Text } from "../text/Text"; import "./textarea.css"; export interface TextareaProps { @@ -81,8 +83,14 @@ export function Textarea({ /> {maxCnt !== undefined && (

- {textCnt} - /{maxCnt} + {textCnt} + /{maxCnt}

)}
diff --git a/src/components/atoms/textarea/textarea.css b/src/components/atoms/textarea/textarea.css index 28adb5d..04aacb2 100644 --- a/src/components/atoms/textarea/textarea.css +++ b/src/components/atoms/textarea/textarea.css @@ -5,6 +5,12 @@ line-height: 100%; display: flex; - align-items: center; + flex-direction: column; + align-items: flex-end; justify-content: center; + gap: 0.5rem; +} + +.textarea__wrapper > textarea { + font-family: "Pretendard Variable"; } \ No newline at end of file diff --git a/src/components/molecules/ImageModal/ImageModal.tsx b/src/components/molecules/ImageModal/ImageModal.tsx index 3ed420c..f4f06c9 100644 --- a/src/components/molecules/ImageModal/ImageModal.tsx +++ b/src/components/molecules/ImageModal/ImageModal.tsx @@ -1,11 +1,11 @@ import { Arrow } from "@/components/atoms/arrow"; import { Text } from "@/components/atoms/text"; import _ from "lodash"; -import Image, { StaticImageData } from "next/image"; +import Image from "next/image"; import { useState } from "react"; import "./ImageModal.css"; interface ImageModalProps { - imageList: StaticImageData[]; + imageList: string[]; clickIndex: number; onClose: () => void; } @@ -62,6 +62,8 @@ export const ImageModal = ({ imageList, clickIndex, onClose }: ImageModalProps) src={imageList[currentIndex]} alt="contentImg" className="image__modal__image" + width={400} + height={400} />
handlePrevNext(-1)} isLeft={true} /> diff --git a/src/components/molecules/attendanceItem/AttendanceItem.tsx b/src/components/molecules/attendanceItem/AttendanceItem.tsx index 0db28df..937ee05 100644 --- a/src/components/molecules/attendanceItem/AttendanceItem.tsx +++ b/src/components/molecules/attendanceItem/AttendanceItem.tsx @@ -15,14 +15,6 @@ const AttendanceItem = ({ scheduleTime, onClick, }: AttendanceSchedule) => { - // if (attendanceStatus === "BEFORE") { - // attendanceStatus = "미출석"; - // } else if (attendanceStatus === "ONGOING") { - // attendanceStatus = "출석중"; - // } else if (attendanceStatus === "COMPLETE") { - // attendanceStatus = "출석완료"; - // } - return (
diff --git a/src/components/molecules/commentList/CommentList.tsx b/src/components/molecules/commentList/CommentList.tsx new file mode 100644 index 0000000..9667793 --- /dev/null +++ b/src/components/molecules/commentList/CommentList.tsx @@ -0,0 +1,17 @@ +import { Comment } from "@/components/atoms/comment"; +import { CommentsListProps } from "@/types/comment"; + +export const CommentList = ({ item, replies, onClick }: CommentsListProps) => { + return ( + <> + + {replies && + replies + .slice() + .reverse() + .map((comment, index: number) => ( + + ))} + + ); +}; diff --git a/src/components/molecules/floatingBox/FloatingBox.tsx b/src/components/molecules/floatingBox/FloatingBox.tsx index e166506..33b9d18 100644 --- a/src/components/molecules/floatingBox/FloatingBox.tsx +++ b/src/components/molecules/floatingBox/FloatingBox.tsx @@ -12,10 +12,10 @@ export function FloatingBox() { setOn((prev) => !prev); }; const handleRouteOnboarding = () => { - router.push("/user/club"); + router.push("/user/club/create"); }; const handleRouteJoin = () => { - router.push("/user/join"); + router.push("/user/club/join"); }; const inPlusStyle: React.CSSProperties = { diff --git a/src/components/molecules/scheduleBox/ScheduleBox.css b/src/components/molecules/scheduleBox/ScheduleBox.css index 3e3570f..a86edeb 100644 --- a/src/components/molecules/scheduleBox/ScheduleBox.css +++ b/src/components/molecules/scheduleBox/ScheduleBox.css @@ -17,7 +17,7 @@ .scheduleBox__content { display: flex; - width: 100%; + width: 60%; gap: 1rem; } diff --git a/src/components/organisms/Header/BoardHeader.tsx b/src/components/organisms/Header/BoardHeader.tsx new file mode 100644 index 0000000..dd03829 --- /dev/null +++ b/src/components/organisms/Header/BoardHeader.tsx @@ -0,0 +1,68 @@ +"use client"; +import { Text } from "@/components/atoms/text"; +import { ICONS, IMAGES } from "@/constants/images"; +import { usePostDeleteMutation } from "@/hook/post/usePostDeleteMutation"; +import Image from "next/image"; +import { usePathname, useRouter } from "next/navigation"; +import { useState } from "react"; +import "./Header.css"; + +interface ClubHeaderProps { + color?: boolean; +} +export function BoardHeader({ color }: ClubHeaderProps) { + const pathname = usePathname(); + const pathProps = pathname.split("/").slice(1); + const postItem = { + clubId: parseInt(pathProps[0], 10), + postId: parseInt(pathProps[3], 10), + }; + const router = useRouter(); + const [isOpenDropdown, setIsOpenDropdown] = useState(false); + const handleIsOpenDropdown = () => { + setIsOpenDropdown((prep) => !prep); + console.log(isOpenDropdown); + }; + const { mutate: postDeleteMutate } = usePostDeleteMutation(postItem); + const menuItems = [ + { text: "수정" }, + { text: "삭제"}, + { text: "URL 공유"}, + ]; + return ( + + ); +} diff --git a/src/components/organisms/Header/Header.css b/src/components/organisms/Header/Header.css index 9019f7d..87b5c48 100644 --- a/src/components/organisms/Header/Header.css +++ b/src/components/organisms/Header/Header.css @@ -7,7 +7,7 @@ justify-content: space-between; height: 5.5rem; padding: 0 5vw; - background-color: #2B2B2B; + background-color: #2b2b2b; position: fixed; top: 0; @@ -47,56 +47,95 @@ .logoHeader__img__right img { margin-left: 0.69rem; } -.logoHeader__img__right img{ - margin-left: 0.69rem; +.logoHeader__img__right img { + margin-left: 0.69rem; +} + +/* boardHeader */ +.boardHeader { + background-color: #2b2b2b; + display: flex; + width: 100vw; + align-items: center; + justify-content: space-between; + height: 4.19rem; + padding-left: 5vw; + padding-right: 5vw; +} + +.boardHeader.black { + background-color: #1b1b1b; } +.boardHeader img { + object-fit: contain; + width: 0.75rem; + height: 1.375rem; +} + +.dropdown { + position: absolute; + top: 100%; + right: 0; + width: 7rem; + border-radius: 0.625rem; + background: #404040; + z-index: 10; + height: auto; +} +.underline { + border-bottom: 1px solid #fff; + padding: 0.7rem 0.5rem; +} +.underline:last-child { + border-bottom: none; +} /* clubHeader */ .clubHeader { - background-color: #2B2B2B; - display: flex; - width: 100vw; - align-items: center; - justify-content: space-between; - height: 5.5rem; - padding-left: 5vw; - padding-right: 5vw; + background-color: #2b2b2b; + display: flex; + width: 100vw; + align-items: center; + justify-content: space-between; + height: 5.5rem; + padding-left: 5vw; + padding-right: 5vw; } .clubHeader.black { - background-color: #1B1B1B; + background-color: #1b1b1b; } .clubHeader img { - object-fit: contain; - height: 100%; - width: 0.75rem; - height: 1.375rem; + object-fit: contain; + height: 100%; + width: 0.75rem; + height: 1.375rem; } .clubHeader p { width: 0.75rem; - height: 1.375rem; + height: 1.375rem; } .clubHeader__title { - display: flex; - align-items: center; - gap: 0.3rem; - cursor: pointer; + display: flex; + align-items: center; + gap: 0.3rem; + cursor: pointer; } .clubHeader__title img { - height: 0.375rem; - width: 0.6875rem; + height: 0.375rem; + width: 0.6875rem; } .clubHeader__title__img.active { - animation: rotate_img 0.3s linear forwards; + animation: rotate_img 0.3s linear forwards; } @keyframes rotate_img { - 100% { - transform: rotate(180deg); - } + 100% { + transform: rotate(180deg); + } } diff --git a/src/components/organisms/Header/index.ts b/src/components/organisms/Header/index.ts index 886f51f..2df1b49 100644 --- a/src/components/organisms/Header/index.ts +++ b/src/components/organisms/Header/index.ts @@ -1,3 +1,4 @@ +export { BoardHeader } from "./BoardHeader"; export { HistoryHeader } from "./HistoryHeader"; export { LogoHeader } from "./LogoHeader"; diff --git a/src/components/organisms/Modal/Modal.tsx b/src/components/organisms/Modal/Modal.tsx index 7c52e16..34116b6 100644 --- a/src/components/organisms/Modal/Modal.tsx +++ b/src/components/organisms/Modal/Modal.tsx @@ -12,43 +12,48 @@ import { ModalTitle } from "./ModalTitle"; import styles from "./Modal.module.css"; // children 요소 중 Header filtering -const ModalHeaderType= ().type; +const ModalDimmedType = ().type; +function getModalDimmed(children: ReactNode) { + const childrenArray = Children.toArray(children); + return childrenArray.filter( + (child) => isValidElement(child) && child.type === ModalDimmedType + ); +} + +// children 요소 중 Header filtering +const ModalHeaderType = ().type; function getModalHeader(children: ReactNode) { const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalHeaderType, - ); + return childrenArray.filter( + (child) => isValidElement(child) && child.type === ModalHeaderType + ); } // children 요소 중 Content filtering -const ModalContentType= ().type; +const ModalContentType = ().type; function getModalContents(children: ReactNode) { const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalContentType, - ); + return childrenArray.filter( + (child) => isValidElement(child) && child.type === ModalContentType + ); } // children 요소 중 Footer filtering -const ModalFooterType= ().type; +const ModalFooterType = ().type; function getModalFooter(children: ReactNode) { const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalFooterType, - ) + return childrenArray.filter( + (child) => isValidElement(child) && child.type === ModalFooterType + ); } - interface ModalProps { children?: ReactNode; isOpen: boolean; -}; +} export const Modal = ({ children, isOpen }: ModalProps) => { - if(!isOpen){ + if (!isOpen) { return null; } @@ -56,25 +61,19 @@ export const Modal = ({ children, isOpen }: ModalProps) => { const modalContents = getModalContents(children); const modalFooter = getModalFooter(children); return createPortal( -
- { - modalHeader && ( -
{modalHeader}
- ) - } - { - modalContents && ( + <> + {getModalDimmed(children)} +
+ {modalHeader &&
{modalHeader}
} + {modalContents && (
{modalContents}
- ) - } - { - modalFooter && ( -
{modalFooter}
- ) - } -
- , document.body); -} + )} + {modalFooter &&
{modalFooter}
} +
+ , + document.body + ); +}; Modal.Dimmed = ModalDimmed; Modal.Header = ModalHeader; @@ -83,4 +82,4 @@ Modal.Footer = ModalFooter; Modal.Title = ModalTitle; Modal.Subtitle = ModalSubtitle; -Modal.Button = ModalButtons; \ No newline at end of file +Modal.Button = ModalButtons; diff --git a/src/components/organisms/Modal/ModalHeader.tsx b/src/components/organisms/Modal/ModalHeader.tsx index a2ece2e..640b54f 100644 --- a/src/components/organisms/Modal/ModalHeader.tsx +++ b/src/components/organisms/Modal/ModalHeader.tsx @@ -2,16 +2,16 @@ import { IMAGES } from "@/constants/images"; import Image from "next/image"; import { ReactNode } from "react"; import styles from "./Modal.module.css"; -import { useModalContext } from "./ModalProvider"; interface ModalHeaderProps { children?: ReactNode; + closeModal?: () => void; } export const ModalHeader = ({ - children + children, + closeModal }: ModalHeaderProps) => { - const { closeModal } = useModalContext(); return (
).type; -function getModalDimmed(children: ReactNode) { - const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalDimmedType, - ) - .slice(0, 1)[0] || null; -} - -// children 요소 중 Header filtering -const ModalHeaderType= ().type; -function getModalHeader(children: ReactNode) { - const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalHeaderType, - ); -} - -// children 요소 중 Content filtering -const ModalContentType= ().type; -function getModalContents(children: ReactNode) { - const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalContentType, - ); -} - -// children 요소 중 Footer filtering -const ModalFooterType= ().type; -function getModalFooter(children: ReactNode) { - const childrenArray = Children.toArray(children); - return childrenArray - .filter( - child => isValidElement(child) && child.type === ModalFooterType, - ) -} - -interface ModalMainProps { - children?: ReactNode; - isOpen: boolean; -}; - -export const ModalMain = ({ children, isOpen }: ModalMainProps) => { - if(!isOpen){ - return null; - } - - const modalHeader = getModalHeader(children); - const modalContents = getModalContents(children); - const modalFooter = getModalFooter(children); - return createPortal( -
-
{getModalDimmed(children)}
- { - modalHeader && ( -
{modalHeader}
- ) - } - { - modalContents && ( -
{modalContents}
- ) - } - { - modalFooter && ( -
{modalFooter}
- ) - } -
- , document.body); -} diff --git a/src/constants/images.ts b/src/constants/images.ts index 64c452b..354544e 100644 --- a/src/constants/images.ts +++ b/src/constants/images.ts @@ -12,6 +12,7 @@ export const IMAGES = { }; export const ICONS = { + headerToggle: require("@/assets/images/ic_header_menuToggle.png"), down: require("@/assets/images/ic_down.png"), search: require("@/assets/images/ic_search.png"), calendar: require("@/assets/images/ic_calendar.png"), @@ -26,17 +27,17 @@ export const ICONS = { back: require("@/assets/images/ic_back.png"), filter: require("@/assets/images/ic_filter.png"), nodata: require("@/assets/images/ic_nodata.png"), - + floatingPlus: require("@/assets/images/Plus.png"), floatingLock: require("@/assets/images/ic_lock.png"), floatingMeeting: require("@/assets/images/ic_people.png"), - + newBoard: require("@/assets/images/ic_new_board.png"), comment: require("@/assets/images/ic_comment.png"), - + heartColor: require("@/assets/images/ic_heartColor.png"), heartNone: require("@/assets/images/ic_heartWhite.png"), - + edit: require("@/assets/images/ic_edit.png"), member: require("@/assets/images/ic_member.png"), copy: require("@/assets/images/ic_copy.png"), diff --git a/src/constants/keys/postKey.ts b/src/constants/keys/postKey.ts new file mode 100644 index 0000000..895a2b6 --- /dev/null +++ b/src/constants/keys/postKey.ts @@ -0,0 +1,13 @@ +import { PostDetailProps } from "@/types/post"; + +export const postKeys = { + all: ["post"] as const, + detail: ({ clubId, postId }: PostDetailProps) => + [...postKeys.all, { clubId, postId }] as const, + comment: ({ clubId, postId }: PostDetailProps) => + [...postKeys.detail({ clubId, postId }), "comment"] as const, +}; + +export const categoryKeys = { + all: ["categoty"] as const, +}; diff --git a/src/features/board/components/Board.tsx b/src/features/board/components/Board.tsx index f29e55f..c14dab7 100644 --- a/src/features/board/components/Board.tsx +++ b/src/features/board/components/Board.tsx @@ -2,16 +2,25 @@ import { Floating } from "@/components/atoms/floating"; import { Text } from "@/components/atoms/text"; import { ICONS } from "@/constants/images"; +import usePostListQuery from "@/hook/post/usePostListQuery"; import { usePathname, useRouter } from "next/navigation"; import React from "react"; -import { testArr } from "../constants/testArr/TestArr"; import "./Board.css"; import BoardItem from "./BoardItem"; const Board = () => { const router = useRouter(); const pathname = usePathname(); + const startedPath = pathname.split("/").slice(1)[0]; + const clubId = parseInt(startedPath, 10); const boardTypeAddress = pathname.split("/board/")[1]; + const boardCategory = boardTypeAddress === "notice" ? "NOTICE" : "FREE"; + const { data } = usePostListQuery({ + clubId: clubId, + category: boardCategory, + page: 0, + size: 10, + }); const boardType = boardTypeAddress === "notice" ? "공지" : "자유"; const inPlusStyle: React.CSSProperties = { width: "1.625rem", @@ -30,19 +39,19 @@ const Board = () => {
- {testArr.map((item, index) => ( + {data?.data.content.map((item, index) => ( ))}
diff --git a/src/features/board/components/BoardDetail.css b/src/features/board/components/BoardDetail.css index 952930c..fb4ec9e 100644 --- a/src/features/board/components/BoardDetail.css +++ b/src/features/board/components/BoardDetail.css @@ -49,3 +49,8 @@ display: fixed; bottom: 0; } + +.board__detail__comment { + padding-bottom: 5rem; + height: 100%; +} diff --git a/src/features/board/components/BoardDetail.tsx b/src/features/board/components/BoardDetail.tsx index ed8afb0..4530484 100644 --- a/src/features/board/components/BoardDetail.tsx +++ b/src/features/board/components/BoardDetail.tsx @@ -1,20 +1,40 @@ -import { Comment, CommentInput } from "@/components/atoms/comment/index"; +import { CommentInput } from "@/components/atoms/comment/index"; import { Heart } from "@/components/atoms/heart/Heart"; import { Text } from "@/components/atoms/text"; import { ImageModal } from "@/components/molecules/ImageModal"; -// import useGetBoard from "@/hook/board/useGetBoardDetail"; +import { CommentList } from "@/components/molecules/commentList/CommentList"; +import { BoardHeader } from "@/components/organisms/Header"; +import { useCommentSort } from "@/hook/comment/useCommentSort"; +import useCommentsQuery from "@/hook/comment/useCommentsQuery"; import usePostDetailQuery from "@/hook/post/usePostDetailQuery"; +import { + useHeartCancleMutation, + useHeartMutation, +} from "@/hook/post/useheartMutation"; import Image from "next/image"; +import { usePathname } from "next/navigation"; import { useState } from "react"; -import { BoardDetailArr, commentsArr } from "../constants/testArr/TestArr"; import "./BoardDetail.css"; export const BoardDetail = () => { - const { data, isLoading } = usePostDetailQuery({ clubId: 4, postId: 9 }); - console.log(data); + const pathname = usePathname(); + const pathProps = pathname.split("/").slice(1); + const postItem = { + clubId: parseInt(pathProps[0], 10), + postId: parseInt(pathProps[3], 10), + }; + const { data, isLoading } = usePostDetailQuery(postItem); + const { mutate: isHaertMutate } = useHeartMutation(postItem); + const { mutate: isCancelHaertMutate } = useHeartCancleMutation(postItem); const [isOpenModal, setIsOpenModal] = useState(false); + const [targetComment, setTargetComment] = useState( + undefined + ); const [currentIndex, setCurrentIndex] = useState(-1); + const handleTargetComment = (commentId: number) => { + setTargetComment(commentId); + }; const handleIsHeart = () => { - // 좋아요 요청 보내기 + data && !data.data.isLiked ? isHaertMutate() : isCancelHaertMutate(); }; const handleOpenImg = (index: number) => { // 사진 크게 보기 @@ -25,86 +45,127 @@ export const BoardDetail = () => { // 사진 모달 닫기 setIsOpenModal(false); }; + const { data: commentList } = useCommentsQuery({ + clubId: parseInt(pathProps[0], 10), + postId: parseInt(pathProps[3], 10), + size: 100, + page: 0, + }); + const structuredComments = useCommentSort(commentList?.data.content); + // 로딩 중일 때 + if (isLoading) { + return
로딩 중...
; + } + + // data가 undefined인 경우 + if (!data) { + return
데이터를 불러오는 중 오류가 발생했습니다.
; + } + console.log("dkdkdkdk", targetComment); + const DetailContent = data.data; return ( <> -
- - {BoardDetailArr.title} - -
- userImg - - {BoardDetailArr.writer.name} - + + <> +
- {BoardDetailArr.date} + {DetailContent.postTitle} -
-
-
- - {BoardDetailArr.content} +
+ userImg + + {DetailContent.writerName} + + + {DetailContent.createdAt}
-
- {BoardDetailArr.contentImgs?.map((imgItem, index) => ( -
handleOpenImg(index)} - > - contentImg -
- ))} -
- {isOpenModal && ( - - )} +
+
+ + {DetailContent.postContent} + +
+
+ {DetailContent.uploadImage?.map((imgItem, index) => ( +
handleOpenImg(index)} + > + contentImg +
+ ))} +
+ {isOpenModal && ( + + )} -
- +
+ +
+
+
+
- - 댓글 {BoardDetailArr.comment} + + 댓글 {DetailContent.commentCount}
- {commentsArr.map((comment, index: number) => ( - - ))} + {structuredComments && + structuredComments + .slice() + .reverse() + .map((comment, index: number) => ( + + ))}
-
- + + ); }; diff --git a/src/features/board/components/BoardItem.tsx b/src/features/board/components/BoardItem.tsx index aee04b9..816645e 100644 --- a/src/features/board/components/BoardItem.tsx +++ b/src/features/board/components/BoardItem.tsx @@ -1,6 +1,5 @@ "use client"; -import testimg from "@/assets/images/chiikyaw.png"; import { Heart } from "@/components/atoms/heart/Heart"; import { Text } from "@/components/atoms/text"; import { ICONS } from "@/constants/images"; @@ -14,12 +13,12 @@ const BoardItem = ({ id, title, content, - date, - comment, + commentCount, hearts, contentImgs, + createdAt, isHeart = false, - writer: { profile = testimg, name }, + writer: { profile, name }, }: BoardItemProps) => { const router = useRouter(); const pathname = usePathname(); @@ -58,20 +57,26 @@ const BoardItem = ({ {/* 아래쪽 */}
- writerProfile + writerProfile {name}
- {date} + {createdAt}
- comment + comment - {comment} + {commentCount}
@@ -84,6 +89,8 @@ const BoardItem = ({ src={contentImgs[0]} alt="contentImg" className="home__content__meeting__right_img" + width={100} + height={100} /> )}
diff --git a/src/features/board/components/BoardItemProps.ts b/src/features/board/components/BoardItemProps.ts index 06bcc18..b0cddee 100644 --- a/src/features/board/components/BoardItemProps.ts +++ b/src/features/board/components/BoardItemProps.ts @@ -1,18 +1,16 @@ -import { StaticImageData } from "next/image"; export interface BoardItemProps { boardAddress?: string; id: number; title: string; content: string; - date: string; - comment: number; + commentCount: number; hearts: number; - contentImgs?: StaticImageData[] | undefined; - + contentImgs?: string[] | undefined; isHeart: boolean; writer: { - profile: StaticImageData; + profile: string; name: string; }; + createdAt: string; } diff --git a/src/features/board/components/BoardList.tsx b/src/features/board/components/BoardList.tsx index 9c21105..4797072 100644 --- a/src/features/board/components/BoardList.tsx +++ b/src/features/board/components/BoardList.tsx @@ -1,12 +1,39 @@ +import usePostListQuery from "@/hook/post/usePostListQuery"; +import { usePathname } from "next/navigation"; import "./BoardList.css"; import BoardListBox from "./BoardListBox"; const BoardList = () => { + const pathname = usePathname(); + const startedPath = pathname.split("/").slice(1)[0]; + const clubId = parseInt(startedPath, 10); + const { data: noticeList } = usePostListQuery({ + clubId: clubId, + category: "NOTICE", + page: 0, + size: 7, + }); + const { data: freeList } = usePostListQuery({ + clubId: clubId, + category: "FREE", + page: 0, + size: 7, + }); return (
- - + +
); }; diff --git a/src/features/board/components/BoardListBox.tsx b/src/features/board/components/BoardListBox.tsx index 365d582..8e41caf 100644 --- a/src/features/board/components/BoardListBox.tsx +++ b/src/features/board/components/BoardListBox.tsx @@ -1,28 +1,33 @@ import { Text } from "@/components/atoms/text"; import { ICONS } from "@/constants/images"; +import { PostList } from "@/types/post"; import Image from "next/image"; -import { usePathname, useRouter } from "next/navigation"; -import { testArr } from "../constants/testArr/TestArr"; +import { useRouter } from "next/navigation"; import "./BoardListBox.css"; interface BoardListBoxProp { - boardType: string; + clubId: number; + boardType: "공지" | "자유"; + boardTypeAddress: "notice" | "free"; + data: PostList[]|undefined; } interface BoardListItemProp { id: number; title: string; } -const BoardListBox = ({ boardType }: BoardListBoxProp) => { - const boardTypeAddress = boardType === "공지" ? "notice" : "free"; +const BoardListBox = ({ + clubId, + boardType, + boardTypeAddress, + data, +}: BoardListBoxProp) => { const router = useRouter(); - const pathname = usePathname(); - const startedPath = pathname.split("/").slice(1)[0]; const handleRouteBoard = (boardTypeAddress: string) => { - router.push(`/${startedPath}/board/${boardTypeAddress}`); + router.push(`/${clubId}/board/${boardTypeAddress}`); }; const BoardListItem = ({ id, title }: BoardListItemProp) => { const handleRouteBoardDetail = (id: number) => { - router.push(`/${startedPath}/board/${boardTypeAddress}/${id}`); + router.push(`/${clubId}/board/${boardTypeAddress}/${id}`); }; return (
{
- {testArr.slice(0, 7).map((item, index) => ( - + {data?.map((item, index) => ( + ))}
diff --git a/src/features/home/SetHome.tsx b/src/features/home/SetHome.tsx index 51b586f..f9d9120 100644 --- a/src/features/home/SetHome.tsx +++ b/src/features/home/SetHome.tsx @@ -19,9 +19,11 @@ export const ScheduleContext = createContext<{ const SetHome = () => { const { value, setValue, filteredData, isLoading, mark } = useSchedule({ clubId: 1, - q: "", - sDate: moment().startOf('month').format("YYYY-MM-DD"), - eDate: moment().endOf('month').format("YYYY-MM-DD") + keyword: "", + startDate: moment().startOf('month').format("YYYY-MM-DD"), + endDate: moment().endOf('month').format("YYYY-MM-DD"), + page: 0, + size: 20, }); console.log(filteredData); diff --git a/src/features/home/calendar/SetCalendar.tsx b/src/features/home/calendar/SetCalendar.tsx index 55ad1b2..4138c5d 100644 --- a/src/features/home/calendar/SetCalendar.tsx +++ b/src/features/home/calendar/SetCalendar.tsx @@ -1,29 +1,36 @@ "use client"; import useSchedule from "@/hook/schedule/useSchedule"; -import moment from "moment"; -import { useSearchParams } from "next/navigation"; import CalendarList from "./components/CalendarList"; import { CalendarListHeader } from "./components/CalendarListHeader"; const SetCalendar = () => { - const params = useSearchParams(); - const { data: scheduleData, refetchSchedule, isLoading, mark } = useSchedule({ + const { data: scheduleData, refetchPeriodSchedule, refetchKeywordSchedule, isLoading, mark } = useSchedule({ clubId: 1, - q: "", - sDate: moment(params.get('month'), "YYYY-MM").startOf('month').format("YYYY-MM-DD"), - eDate: moment(params.get('month'), "YYYY-MM").endOf('month').format("YYYY-MM-DD") + keyword: "", + startDate: "", + endDate: "", + page: 0, + size: 20, }); return ( <> - {!scheduleData ? ( -
loading...
- ): - (
- - -
) - } +
+ + { + isLoading ? ( + <> + ) : ( + + ) + } +
) }; diff --git a/src/features/home/calendar/components/CalendarDetailContent.tsx b/src/features/home/calendar/components/CalendarDetailContent.tsx index 9ed0bb2..3e6e884 100644 --- a/src/features/home/calendar/components/CalendarDetailContent.tsx +++ b/src/features/home/calendar/components/CalendarDetailContent.tsx @@ -8,7 +8,6 @@ interface CalendarDetailContentProps { } const CalendarDetailContent = ({ detailData } : CalendarDetailContentProps) => { - console.log(detailData); const renderDetailBox = (label: string, value: string | undefined) => (
diff --git a/src/features/home/calendar/components/CalendarDetailHeader.tsx b/src/features/home/calendar/components/CalendarDetailHeader.tsx index df1bd99..ae2625f 100644 --- a/src/features/home/calendar/components/CalendarDetailHeader.tsx +++ b/src/features/home/calendar/components/CalendarDetailHeader.tsx @@ -10,17 +10,6 @@ interface CalendarDetailContentProps { } const CalendarDetailHeader = ({detailData}: CalendarDetailContentProps) => { - const dateDetail = - { - id: 1, - time: new Date(), - user: "문해빈", - title: "와이어 프레임 작성 회의", - location: "커피빈 홍대입구점", - content: "유저 플로우 기반으로 와이어프레임 작성하겠습니다. 테블릿 PC 지참해주세요 :) 출석은 정시에 가차없이 진행할 예정입니다. 지각 시에는 벌금 6000만원 있으니 참고하시어 늦지 마시길 ~ 모두 이따 봅시다", - attendance: 0, // 미출석 - } - return ( (detailData && (
diff --git a/src/features/home/calendar/components/CalendarFilter.tsx b/src/features/home/calendar/components/CalendarFilter.tsx index 6a47339..be2ece4 100644 --- a/src/features/home/calendar/components/CalendarFilter.tsx +++ b/src/features/home/calendar/components/CalendarFilter.tsx @@ -2,15 +2,19 @@ import { Text } from "@/components/atoms/text"; import { BottomSheet } from "@/components/organisms/BottomSheet/BottomSheet"; import { ICONS } from "@/constants/images"; import { COLORS } from "@/styles"; +import { ScheduleDate } from "@/types/schedule"; import Image from "next/image"; import { CALENDAR_FILTER_BTN } from "../constants/const"; import { useCalendarFilter } from "./CalendarFilterProvider"; -import { CalendarListHeaderProps } from "./CalendarListHeader"; import { CalendarPeriod } from "./CalendarPeriod"; +export interface CalendarFilterProps { + refetchPeriodSchedule: ({ startDate, endDate }: ScheduleDate) => void; +} + export const CalendarFilter = ({ - refetchSchedule -}: CalendarListHeaderProps) => { + refetchPeriodSchedule +}: CalendarFilterProps) => { const {openFloating, setOpenFloating, isPeriod, period} = useCalendarFilter(); return( @@ -55,7 +59,7 @@ export const CalendarFilter = ({ setOpenFloating={setOpenFloating} > )} diff --git a/src/features/home/calendar/components/CalendarFilterProvider.tsx b/src/features/home/calendar/components/CalendarFilterProvider.tsx index c483358..71a5d08 100644 --- a/src/features/home/calendar/components/CalendarFilterProvider.tsx +++ b/src/features/home/calendar/components/CalendarFilterProvider.tsx @@ -32,10 +32,10 @@ interface CalendarFilterProviderProps { } export const CalendarFilterProvider = ({ children }: CalendarFilterProviderProps) => { - const [openFloating, setOpenFloating] = useState(false); - const [selectedIdx, setSelectedIdx] = useState(0); - const [isPeriod, setIsPeriod] = useState(false); - const [period, setPeriod] = useState({ + const [openFloating, setOpenFloating] = useState(false); // 바텀시트 오픈 여부 + const [selectedIdx, setSelectedIdx] = useState(0); // 설정한 기간 idx + const [isPeriod, setIsPeriod] = useState(false); // 기간 설정 여부 + const [period, setPeriod] = useState({ // 설정한 기간 startDate: moment(new Date()).format('YYYY-MM-DD'), endDate: moment(new Date()).format('YYYY-MM-DD') }); diff --git a/src/features/home/calendar/components/CalendarListHeader.tsx b/src/features/home/calendar/components/CalendarListHeader.tsx index 9eea510..ebd9120 100644 --- a/src/features/home/calendar/components/CalendarListHeader.tsx +++ b/src/features/home/calendar/components/CalendarListHeader.tsx @@ -1,21 +1,25 @@ import { Search } from "@/components/atoms/search"; -import { FetchScheduleParams } from "@/types/schedule"; +import { ScheduleDate, ScheduleKeyword } from "@/types/schedule"; import { CalendarFilter } from "./CalendarFilter"; import { CalendarFilterProvider } from "./CalendarFilterProvider"; export interface CalendarListHeaderProps { - refetchSchedule: (params: FetchScheduleParams) => void; + refetchPeriodSchedule: ({ startDate, endDate }: ScheduleDate) => void; + refetchKeywordSchedule: ({ keyword }: ScheduleKeyword) => void; } export const CalendarListHeader = ({ - refetchSchedule + refetchPeriodSchedule, + refetchKeywordSchedule }: CalendarListHeaderProps) => { return(
- +
diff --git a/src/features/home/calendar/components/CalendarPeriod.tsx b/src/features/home/calendar/components/CalendarPeriod.tsx index 26b1f9d..94baf35 100644 --- a/src/features/home/calendar/components/CalendarPeriod.tsx +++ b/src/features/home/calendar/components/CalendarPeriod.tsx @@ -4,13 +4,13 @@ import { Text } from "@/components/atoms/text"; import moment from "moment"; import { useState } from "react"; import { CALENDAR_PERIOD_BTN, CALENDAR_PERIOD_TXT, CALENDAR_PERIOD_TXT_LIST } from "../constants/const"; +import { CalendarFilterProps } from "./CalendarFilter"; import { useCalendarFilter } from "./CalendarFilterProvider"; -import { CalendarListHeaderProps } from "./CalendarListHeader"; export const CalendarPeriod = ({ - refetchSchedule -}: CalendarListHeaderProps) => { - const { setOpenFloating, selectedIdx, setSelectedIdx, setIsPeriod, period, setPeriod } = useCalendarFilter(); + refetchPeriodSchedule +}: CalendarFilterProps) => { + const { setOpenFloating, selectedIdx, setSelectedIdx, isPeriod, setIsPeriod, period, setPeriod } = useCalendarFilter(); const [tmpPeriod, setTmpPeriod] = useState({ startDate: period.startDate, endDate: period.endDate @@ -20,10 +20,23 @@ export const CalendarPeriod = ({ setSelectedIdx(0); }; + // 기간별 일정 refetch + const refetchNewSchedule = ({ newSDate, newEDate }: { + newSDate: string; + newEDate: string; + }) => { + refetchPeriodSchedule && refetchPeriodSchedule({ + startDate: newSDate, + endDate: newEDate + }); + } + const handlePeriod = () => { if(selectedIdx === 0){ + refetchNewSchedule({ newSDate: "", newEDate: "" }); setIsPeriod(false); } else { + setIsPeriod(true); // 기간 설정했는지 여부 let newSDate = period.startDate; let newEDate = period.endDate; switch(selectedIdx) { @@ -44,17 +57,22 @@ export const CalendarPeriod = ({ setPeriod(tmpPeriod); break; } - refetchSchedule({ - clubId: 1, - q: "", - sDate: newSDate, - eDate: newEDate - }); - setIsPeriod(true); + refetchNewSchedule({ newSDate: newSDate, newEDate: newEDate }); + setPeriod({ + startDate: newSDate, + endDate: newEDate + }); // 설정한 기간 + setIsPeriod(true); // 기간 설정했는지 여부 } setOpenFloating(false); // 바텀시트 닫기 }; + // useEffect(() => { + // if (isPeriod) { + // refetchNewSchedule({ newSDate: period.startDate, newEDate: period.endDate }); + // } + // }, [period]); + return(
diff --git a/src/features/home/components/Home.css b/src/features/home/components/Home.css index 306929a..a9984c6 100644 --- a/src/features/home/components/Home.css +++ b/src/features/home/components/Home.css @@ -23,7 +23,12 @@ margin-bottom: 1rem; } -.homeDateWrapper__header span { +.homeDateWrapper__header::before { + width: 0px !important; + height: 0px !important; +} + +.homeDateWrapper__header-btn { cursor: pointer; } diff --git a/src/features/home/components/HomeDateWrapper.tsx b/src/features/home/components/HomeDateWrapper.tsx index c06bda8..8d67343 100644 --- a/src/features/home/components/HomeDateWrapper.tsx +++ b/src/features/home/components/HomeDateWrapper.tsx @@ -1,5 +1,4 @@ "use client"; -import moment from "moment"; import React, { useContext } from "react"; import "./Home.css"; @@ -28,11 +27,12 @@ const HomeDateWrapper = ({ dateList, isLoading }: { fontWeight="700" >{formatDateKor(value)} 더보기
diff --git a/src/features/home/components/HomeHeader.tsx b/src/features/home/components/HomeHeader.tsx index b0d0c69..1608e29 100644 --- a/src/features/home/components/HomeHeader.tsx +++ b/src/features/home/components/HomeHeader.tsx @@ -1,5 +1,4 @@ import { HomeCalendar } from "@/components/atoms/calendar"; -import { Search } from "@/components/atoms/search"; import { useContext } from 'react'; import { ScheduleContext } from '../SetHome'; // Adjust the path as needed import "./Home.css"; @@ -10,7 +9,7 @@ const HomeHeader = ({ mark }: { const { value, onChange } = useContext(ScheduleContext); return (
- + {/* */} console.log(date)} value={value} diff --git a/src/features/info/board/like/InfoBoardLike.tsx b/src/features/info/board/like/InfoBoardLike.tsx new file mode 100644 index 0000000..17f91e5 --- /dev/null +++ b/src/features/info/board/like/InfoBoardLike.tsx @@ -0,0 +1,52 @@ +"use client"; +import { HistoryHeader } from "@/components/organisms/Header"; +import { Nav } from "@/components/organisms/Nav"; +import { IMAGES } from "@/constants/images"; +import BoardItem from "@/features/board/components/BoardItem"; +import useMyPostLikeQuery from "@/hook/info/useMyPostLikeQuery"; + +import "./components/BoardLike.css"; + +export const InfoBoardLike = () => { + const { data: boardInfo } = useMyPostLikeQuery({ + clubId: 7, + userId: 6, + category: "FREE", + page: 0, + size: 10, + }); + console.log(boardInfo); + + return( + <> + + {/* TODO: skeleton UI 적용 */} + { + !boardInfo || !boardInfo.data ? +
Loading...
: + ( +
+ {boardInfo?.data?.content?.map((item: any) => ( + + ))} +
+ ) + } +
); }; diff --git a/src/features/member/attendance/AttendanceListPage.tsx b/src/features/member/attendance/AttendanceListPage.tsx index 2cce08c..c401ba3 100644 --- a/src/features/member/attendance/AttendanceListPage.tsx +++ b/src/features/member/attendance/AttendanceListPage.tsx @@ -1,13 +1,14 @@ import React from "react"; import { Nav } from "@/components/organisms/Nav"; import AttendanceList from "./components/AttendanceList"; +import { ToastContainer } from "react-toastify"; const AttendanceListPage: React.FC = () => { return ( <> - {/* */} + -
); diff --git a/src/features/user/joinClub/components/EnterInvitationCode.tsx b/src/features/user/joinClub/components/EnterInvitationCode.tsx index b78a69d..848aee2 100644 --- a/src/features/user/joinClub/components/EnterInvitationCode.tsx +++ b/src/features/user/joinClub/components/EnterInvitationCode.tsx @@ -1,23 +1,26 @@ "use client"; // 클라이언트 컴포넌트로 지정 +import { Button } from "@/components/atoms/button"; import { CodeInput } from "@/components/atoms/input/CodeInput"; import { Text } from "@/components/atoms/text"; -import "./EnterInvitationCode.css"; -import { Button } from "@/components/atoms/button"; import { HistoryHeader } from "@/components/organisms/Header"; -import Image from "next/image"; import { IMAGES } from "@/constants/images"; +import { fetchJoinClub } from "@/hook/clubJoin/useJoinClubQuery"; +import Image from "next/image"; +import { useRouter } from "next/navigation"; +import React, { useEffect, useRef, useState } from "react"; import { ENTER_INVITATION_CODE, JOIN_BTN, RECEIVE_INVITATION_CODE, } from "../constants/const"; -import React, { useRef, useState, useEffect } from "react"; +import "./EnterInvitationCode.css"; const EnterInvitationCode = () => { const [codeValues, setCodeValues] = useState(Array(6).fill("")); const [isComplete, setIsComplete] = useState(false); const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + const router = useRouter(); const setFocus = (index: number) => { if (index >= 0 && index < inputRefs.current.length) { @@ -46,6 +49,16 @@ const EnterInvitationCode = () => { } }, [codeValues]); + const onSubmit = async () => { + if (isComplete) { + const { + data: clubInfo, + } = await fetchJoinClub(codeValues.join('')); + console.log(clubInfo); + router.push(`/user/club/join/${clubInfo.clubId}`); + } + } + return (
@@ -81,9 +94,7 @@ const EnterInvitationCode = () => { diff --git a/src/features/user/joinClub/constants/const.ts b/src/features/user/joinClub/constants/const.ts index 33413a4..93e3979 100644 --- a/src/features/user/joinClub/constants/const.ts +++ b/src/features/user/joinClub/constants/const.ts @@ -1,4 +1,4 @@ export const ENTER_INVITATION_CODE = "초대 코드 입력"; export const RECEIVE_INVITATION_CODE = "관리자에게 받은 초대코드 6자리를 입력해주세요"; -export const JOIN_BTN = "초대코드로 가입하기"; +export const JOIN_BTN = "초대코드로 모임 조회하기"; diff --git a/src/features/user/profile/components/AccountInfo.tsx b/src/features/user/profile/components/AccountInfo.tsx index 9cd43ed..2e8ada3 100644 --- a/src/features/user/profile/components/AccountInfo.tsx +++ b/src/features/user/profile/components/AccountInfo.tsx @@ -5,12 +5,12 @@ import { ACCOUNT_INFO } from "../constants/const"; import { NAME } from "@/constants/const"; import { useRouter } from "next/navigation"; import React from "react"; +import { useGetUserInfoQuery } from "@/hook/user/useGetUserInfoQuery"; const AccountInfo: React.FC = () => { const router = useRouter(); - const name = "홍길동"; - const email = "whatshu@gamil.com"; - const phoneNum = "010-1234-5678"; + const { data } = useGetUserInfoQuery(); + const userInfo = data?.data; return (
@@ -31,9 +31,9 @@ const AccountInfo: React.FC = () => {

{ACCOUNT_INFO.phoneNum}

-

{name}

-

{email}

-

{phoneNum}

+

{userInfo?.userName}

+

{userInfo?.userEmail}

+

{userInfo?.userPhone}

diff --git a/src/features/user/profile/components/EditProfile.tsx b/src/features/user/profile/components/EditProfile.tsx index 20bb6bb..c2deea1 100644 --- a/src/features/user/profile/components/EditProfile.tsx +++ b/src/features/user/profile/components/EditProfile.tsx @@ -4,31 +4,46 @@ import { Button } from "@/components/atoms/button"; import { InputBox } from "@/components/molecules/inputBox"; import { Wrapper } from "@/components/organisms/Wrapper"; import { useRouter } from "next/navigation"; -import React from "react"; +import React, { useEffect } from "react"; import { useForm, SubmitHandler } from "react-hook-form"; import { EDIT_ACCOUNT_INFO_BTN, EDIT_ACCOUNT_INFO_INPUT_ARR, } from "../constants/const"; - -interface FormData { - userName: string; - userEmail: string; - userMobile: string; -} +import { signUpInfo } from "@/types/user/types"; +import { useGetUserInfoQuery } from "@/hook/user/useGetUserInfoQuery"; +import { useModificationUserInfoMutation } from "@/hook/user/useModificationUserInfoMutation"; +import { useQueryClient } from "@tanstack/react-query"; const EditProfile: React.FC = () => { const router = useRouter(); - const methods = useForm({ + const methods = useForm({ mode: "onChange", }); - - const { handleSubmit, control } = methods; - - const submitOnboarding: SubmitHandler = (data) => { - console.log(data); + const { handleSubmit, control, setValue } = methods; + const { data: userInfo } = useGetUserInfoQuery(); + const mutation = useModificationUserInfoMutation(); + const queryClient = useQueryClient(); + const submitOnboarding: SubmitHandler = (data) => { + mutation.mutate(data, { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["userInfo"] }); + router.push("/user/profile"); + }, + onError: (error) => { + console.error("Error updating user info:", error); + }, + }); }; + useEffect(() => { + if (userInfo) { + setValue("userName", userInfo.data.userName); + setValue("userPhone", userInfo.data.userPhone); + setValue("userEmail", userInfo.data.userEmail); + } + }); + return (
@@ -45,9 +60,7 @@ const EditProfile: React.FC = () => { ))}
- + ); diff --git a/src/features/user/profile/constants/const.ts b/src/features/user/profile/constants/const.ts index 749016d..abfac5b 100644 --- a/src/features/user/profile/constants/const.ts +++ b/src/features/user/profile/constants/const.ts @@ -14,29 +14,29 @@ export const ACCOUNT_SETTING = { export const EDIT_ACCOUNT_INFO = "계정 정보 수정"; export const EDIT_ACCOUNT_INFO_INPUT_ARR = [ - { - title: "이름", - type: "input", - essential: true, - name: "userName" - }, - { - title: "이메일", - type: "input", - essential: true, - name: "userEmail" - }, - { - title: "휴대폰 번호", - type: "btnInput", - essential: true, - name: "userMobile" - } -] + { + title: "이름", + type: "input", + essential: true, + name: "userName", + }, + { + title: "이메일", + type: "input", + essential: true, + name: "userEmail", + }, + { + title: "휴대폰 번호", + type: "btnInput", + essential: true, + name: "userPhone", + }, +]; export const EDIT_ACCOUNT_INFO_BTN = { getNum: "인증번호 받기", checkNum: "인증번호 확인", retryNum: "재전송", - complete: "수정 완료" + complete: "수정 완료", }; diff --git a/src/hook/attendance/manager/useAttendanceEndMutationQuery.ts b/src/hook/attendance/manager/useAttendanceEndMutationQuery.ts index ef9c8a7..038f7ec 100644 --- a/src/hook/attendance/manager/useAttendanceEndMutationQuery.ts +++ b/src/hook/attendance/manager/useAttendanceEndMutationQuery.ts @@ -1,11 +1,11 @@ import { http } from "@/apis/http"; -import { AttendanceInfo } from "@/types/attendance/types"; +import { ScheduleDetailProp } from "@/types/schedule"; import { useMutation } from "@tanstack/react-query"; export const useAttendanceEndMutationQuery = ({ clubId, scheduleId, -}: Omit) => { +}: ScheduleDetailProp) => { return useMutation({ mutationFn: (data: string) => http.post(`/${clubId}/schedules/${scheduleId}/attendance-end`, data), diff --git a/src/hook/attendance/manager/useAttendanceStartQuery.ts b/src/hook/attendance/manager/useAttendanceStartQuery.ts index 280fd3b..72f91a9 100644 --- a/src/hook/attendance/manager/useAttendanceStartQuery.ts +++ b/src/hook/attendance/manager/useAttendanceStartQuery.ts @@ -6,13 +6,15 @@ import { useQuery } from "@tanstack/react-query"; export const useAttendanceStartQuery = ({ clubId, scheduleId, -}: ScheduleDetailProp) => { + enabled = true, +}: ScheduleDetailProp & { enabled?: boolean }) => { return useQuery({ queryKey: [`attendanceStart`, { clubId, scheduleId }], queryFn: () => - http.get( - `/${clubId}/schedules/${scheduleId}/attendance-start`, + http.post( + `/clubs/${clubId}/schedules/${scheduleId}/attendance-start`, { headers: { accept: "*/*" } } ), + enabled, }); }; diff --git a/src/hook/attendance/manager/useTodayScheduleListQuery.ts b/src/hook/attendance/manager/useTodayScheduleListQuery.ts index 43d08cc..fc649dd 100644 --- a/src/hook/attendance/manager/useTodayScheduleListQuery.ts +++ b/src/hook/attendance/manager/useTodayScheduleListQuery.ts @@ -1,18 +1,24 @@ import { http } from "@/apis/http"; import { CommonRes } from "@/types"; -import { AttendanceSchedule, TodayScheduleItem } from "@/types/attendance/types"; +import { + AttendanceSchedule, + TodayScheduleItem, +} from "@/types/attendance/types"; import { useQuery } from "@tanstack/react-query"; export const useTodayScheduleListQuery = ({ clubId, - sDate, - eDate, + startDate, + endDate, + page, + size, + sort="", }: TodayScheduleItem) => { return useQuery>({ - queryKey: ["todayScheduleList", clubId, sDate, eDate], + queryKey: ["todayScheduleList", clubId, startDate, endDate], queryFn: async () => { const response = await http.get>( - `/clubs/${clubId}/schedules?sDate=${sDate}&eDate=${eDate}` + `/clubs/${clubId}/schedules?startDate=${startDate}&endDate=${endDate}&page=${page}&size=${size}` ); return response; }, diff --git a/src/hook/attendance/member/useAttendanceMutationQuery.ts b/src/hook/attendance/member/useAttendanceMutationQuery.ts deleted file mode 100644 index 8a83109..0000000 --- a/src/hook/attendance/member/useAttendanceMutationQuery.ts +++ /dev/null @@ -1,23 +0,0 @@ -// import { http } from "@/apis/http"; -// import { useMutation } from "@tanstack/react-query"; -// import { AttendanceData, AttendanceProp } from "@/types/attendance/types"; - -// export const useAttendanceMutationQuery = ({ -// clubId, -// scheduleId, -// memberId, -// }: AttendanceProp) => { -// return useMutation({ -// mutationFn: (data: AttendanceData) => -// http.post( -// `/api/${clubId}/schedules/${scheduleId}/attendance/${memberId}`, -// data -// ), -// onSuccess: (data) => { -// console.log("Attendance updated:", data); -// }, -// onError: (error) => { -// console.error("Error updating attendance:", error); -// }, -// }); -// }; diff --git a/src/hook/attendance/member/useAttendanceMutationReqQuery.ts b/src/hook/attendance/member/useAttendanceMutationReqQuery.ts new file mode 100644 index 0000000..7c38ed1 --- /dev/null +++ b/src/hook/attendance/member/useAttendanceMutationReqQuery.ts @@ -0,0 +1,17 @@ +import { http } from "@/apis/http"; +import { AttendanceReqData } from "@/types/attendance/types"; +import { ScheduleDetailProp } from "@/types/schedule"; +import { useMutation } from "@tanstack/react-query"; + +export const useAttendanceReqMutation = ({ + clubId, + scheduleId, +}: ScheduleDetailProp) => { + return useMutation({ + mutationFn: (data: AttendanceReqData) => + http.post(`/clubs/${clubId}/schedules/${scheduleId}/attendance/`, data), + onSuccess: (data) => { + console.log(data); + }, + }); +}; diff --git a/src/hook/attendance/useAttendanceListQuery.ts b/src/hook/attendance/useAttendanceListQuery.ts index c777d19..46b6186 100644 --- a/src/hook/attendance/useAttendanceListQuery.ts +++ b/src/hook/attendance/useAttendanceListQuery.ts @@ -1,20 +1,14 @@ import { http } from "@/apis/http"; import { CommonNoPageRes } from "@/types"; import { GetAttendanceListResponse } from "@/types/attendance/types"; -import { ScheduleProp } from "@/types/schedule"; import { useQuery } from "@tanstack/react-query"; -export const useAttendanceListQuery = ({ - clubId, -}: Omit) => { +export const useAttendanceListQuery = (clubId: number) => { return useQuery>({ - queryKey: [`attendanceList`, { clubId }], + queryKey: [`attendanceList`, clubId], queryFn: () => http.get>( - `/${clubId}/attendance-list`, - { - headers: { accept: "*/*" }, - } + `/clubs/${clubId}/schedules/attendance-ongoing` ), }); }; diff --git a/src/hook/clubJoin/useJoinCancelMutation.ts b/src/hook/clubJoin/useJoinCancelMutation.ts index 29d78ab..297aaae 100644 --- a/src/hook/clubJoin/useJoinCancelMutation.ts +++ b/src/hook/clubJoin/useJoinCancelMutation.ts @@ -1,12 +1,16 @@ import { http } from "@/apis/http"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; export const useJoinCancelMutation = (joinRequestId: number) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: () => http.post(`/join/requests/${joinRequestId}/cancel`,``), + mutationFn: () => http.post(`/clubs/join/${joinRequestId}/cancel`), onSuccess: (res) => { - alert("삭제 성공"); + alert("취소 성공"); console.log(res); + queryClient.invalidateQueries({ + queryKey: ["JoinRequest"], //신청 목록 재확인 + }); }, onError: (err) => { console.log(err); diff --git a/src/hook/clubJoin/useJoinClubQuery.ts b/src/hook/clubJoin/useJoinClubQuery.ts new file mode 100644 index 0000000..176cc32 --- /dev/null +++ b/src/hook/clubJoin/useJoinClubQuery.ts @@ -0,0 +1,18 @@ +import { http } from "@/apis/http"; +import { CommonNoPageRes } from "@/types"; +import { JoinClub } from "@/types/clubjoin"; +import { useQuery } from "@tanstack/react-query"; + +export const useJoinClubQuery = (privateCode: string) => { + return useQuery>({ + queryKey: ["JoinClub"], + queryFn: async () => + await http.get>(`/clubs?privateCode=${privateCode}`), + }); +}; + +export const fetchJoinClub = async (privateCode: string) => { + const response = await http.get>(`/clubs?privateCode=${privateCode}`); + console.log(response); + return response; +}; \ No newline at end of file diff --git a/src/hook/clubJoin/useJoinDeleteMutation.ts b/src/hook/clubJoin/useJoinDeleteMutation.ts new file mode 100644 index 0000000..4440f54 --- /dev/null +++ b/src/hook/clubJoin/useJoinDeleteMutation.ts @@ -0,0 +1,20 @@ +import { http } from "@/apis/http"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +// 모임가입 신청 삭제 +export const useJoinDeleteMutation = (joinRequestId: number) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: () => http.delete(`/clubs/join/${joinRequestId}`), + onSuccess: (res) => { + alert("삭제 성공"); + console.log(res); + queryClient.invalidateQueries({ + queryKey: ["JoinRequest"], //신청 목록 재확인 + }); + }, + onError: (err) => { + console.log(err); + }, + }); +}; diff --git a/src/hook/clubJoin/useJoinRejectedQuery.ts b/src/hook/clubJoin/useJoinRejectedQuery.ts index 5b29f6a..23152ed 100644 --- a/src/hook/clubJoin/useJoinRejectedQuery.ts +++ b/src/hook/clubJoin/useJoinRejectedQuery.ts @@ -1,14 +1,19 @@ import { http } from "@/apis/http"; +import { CommonNoPageRes } from "@/types"; import { JoinRequest } from "@/types/clubjoin"; import { useQuery } from "@tanstack/react-query"; +// 모임가입 신청 거절 사유 조회 export const useJoinRejectedQuery = (joinRequestId: number, status: string) => { - return useQuery({ + return useQuery>({ queryKey: ["JoinRejected", joinRequestId], enabled: status === "REJECTED", queryFn: async () => - await http.get(`/join/requests/${joinRequestId}`, { - headers: { accept: `*/*` }, - }), + await http.get>( + `/clubs/join/${joinRequestId}`, + { + headers: { accept: `*/*` }, + } + ), }); }; diff --git a/src/hook/clubJoin/useJoinRequestsQuery.ts b/src/hook/clubJoin/useJoinRequestsQuery.ts index 9a3f30e..9253626 100644 --- a/src/hook/clubJoin/useJoinRequestsQuery.ts +++ b/src/hook/clubJoin/useJoinRequestsQuery.ts @@ -3,11 +3,12 @@ import { CommonRes } from "@/types"; import { PageProps } from "@/types/pages"; import { useQuery } from "@tanstack/react-query"; +// 모임가입 신청내역 조회 export const useJoinRequestsQuery = ({ page, size }: PageProps) => { return useQuery>({ queryKey: ["JoinRequest", { page, size }], queryFn: () => - http.get>(`/join/requests?page=${page}&size=${size}`, { + http.get>(`/clubs/join?page=${page}&size=${size}`, { headers: { accept: `*/*` }, }), }); diff --git a/src/hook/comment/useCommentChangeMutation.ts b/src/hook/comment/useCommentChangeMutation.ts new file mode 100644 index 0000000..766af1b --- /dev/null +++ b/src/hook/comment/useCommentChangeMutation.ts @@ -0,0 +1,22 @@ +import { http } from "@/apis/http"; +import { postKeys } from "@/constants/keys/postKey"; +import { CommentData } from "@/types/comment"; +import { PostDetailProps } from "@/types/post"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +export const useCommentChangeMutation = ({ + clubId, + postId, +}: PostDetailProps) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (data: CommentData) => + http.post(`/clubs/${clubId}/comment/updata`, data), + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: [postKeys.comment({ clubId, postId })], + }); + console.log(data); + }, + }); +}; diff --git a/src/hook/comment/useCommentChildMutation.ts b/src/hook/comment/useCommentChildMutation.ts new file mode 100644 index 0000000..6699aeb --- /dev/null +++ b/src/hook/comment/useCommentChildMutation.ts @@ -0,0 +1,22 @@ +import { http } from "@/apis/http"; +import { postKeys } from "@/constants/keys/postKey"; +import { CommentData } from "@/types/comment"; +import { PostDetailProps } from "@/types/post"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +export const useCommentChildMutation = ({ + clubId, + postId, +}: PostDetailProps) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (data: CommentData) => + http.post(`/clubs/${clubId}/comment/child`, data), + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: [postKeys.comment({ clubId, postId })], + }); + console.log(data); + }, + }); +}; diff --git a/src/hook/comment/useCommentDeleteMutation.ts b/src/hook/comment/useCommentDeleteMutation.ts new file mode 100644 index 0000000..3f3f55f --- /dev/null +++ b/src/hook/comment/useCommentDeleteMutation.ts @@ -0,0 +1,22 @@ +import { http } from "@/apis/http"; +import { postKeys } from "@/constants/keys/postKey"; +import { CommantDeleteProps } from "@/types/comment"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +export const useCommentDeleteMutation = ({ + clubId, + postId, + commentId, +}: CommantDeleteProps) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: () => + http.post(`/clubs/${clubId}/comment/${commentId}/delete`), + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: [postKeys.comment({ clubId, postId })], + }); + console.log(data); + }, + }); +}; diff --git a/src/hook/comment/useCommentMutation.ts b/src/hook/comment/useCommentMutation.ts new file mode 100644 index 0000000..69570a7 --- /dev/null +++ b/src/hook/comment/useCommentMutation.ts @@ -0,0 +1,19 @@ +import { http } from "@/apis/http"; +import { postKeys } from "@/constants/keys/postKey"; +import { CommentData } from "@/types/comment"; +import { PostDetailProps } from "@/types/post"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +export const useCommentMutation = ({ clubId, postId }: PostDetailProps) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: (data: CommentData) => + http.post(`/clubs/${clubId}/comment`, data), + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: [postKeys.comment({ clubId, postId })], + }); + console.log(data); + }, + }); +}; diff --git a/src/hook/comment/useCommentSort.ts b/src/hook/comment/useCommentSort.ts new file mode 100644 index 0000000..fc3c958 --- /dev/null +++ b/src/hook/comment/useCommentSort.ts @@ -0,0 +1,34 @@ +import { Comment as CommentProps } from "@/types/comment"; + +interface CommentSortInterface extends CommentProps { + replies: CommentProps[]; +} +export const useCommentSort = (comentArr: CommentProps[] | undefined) => { + if (comentArr == undefined) { + return; + } + const structuredComments: CommentSortInterface[] = []; + const sortedComments = comentArr.sort((a, b) => { + if (a.parentId === null && b.parentId !== null) return -1; + if (a.parentId !== null && b.parentId === null) return 1; + return 0; + }); + + sortedComments.forEach((comment) => { + if (comment.parentId === null) { + structuredComments.push({ + ...comment, + replies: [], + }); + } else { + const parentIndex = structuredComments.findIndex( + (c) => c.commentId === comment.parentId + ); + if (parentIndex !== -1) { + structuredComments[parentIndex].replies.push(comment); + } + } + }); + console.log(structuredComments); + return structuredComments; +}; diff --git a/src/hook/comment/useCommentsQuery.ts b/src/hook/comment/useCommentsQuery.ts new file mode 100644 index 0000000..7551642 --- /dev/null +++ b/src/hook/comment/useCommentsQuery.ts @@ -0,0 +1,28 @@ +import { http } from "@/apis/http"; +import { postKeys } from "@/constants/keys/postKey"; +import { CommonRes } from "@/types"; +import { Comment } from "@/types/comment"; +import { PostDetailProps } from "@/types/post"; +import { useQuery } from "@tanstack/react-query"; + +interface commentListProps extends PostDetailProps { + size: number; + page: number; +} + +export const useCommentsQuery = ({ + clubId, + postId, + size, + page, +}: commentListProps) => { + return useQuery>({ + queryKey: [postKeys.comment({ clubId, postId })], + queryFn: async () => + await http.get>( + `/clubs/${clubId}/comment/${postId}?size=${size}&page=${page}` + ), + }); +}; + +export default useCommentsQuery; diff --git a/src/hook/info/useMyPostLikeQuery.ts b/src/hook/info/useMyPostLikeQuery.ts new file mode 100644 index 0000000..d30a7c8 --- /dev/null +++ b/src/hook/info/useMyPostLikeQuery.ts @@ -0,0 +1,23 @@ +import { http } from "@/apis/http"; +import { CommonRes } from "@/types"; +import { MyPostProps } from "@/types/info"; +import { PostList } from "@/types/post"; +import { useQuery } from "@tanstack/react-query"; + +export const useMyPostLikeQuery = ({ + clubId, + userId, + category, + page, + size, + sort="", +}: MyPostProps) => { + const myPostUrl = `/clubs/${clubId}/posts/my_like_posts?userId=${userId}&postCategory=${category}&page=${page}&size=${size}&sort=${sort}`; + return useQuery>({ + queryKey: ["postMyLikePost", { clubId, category, size, page, sort }], + queryFn: async () => + await http.get>(myPostUrl) + }); +}; + +export default useMyPostLikeQuery; \ No newline at end of file diff --git a/src/hook/info/useMyPostQuery.ts b/src/hook/info/useMyPostQuery.ts new file mode 100644 index 0000000..4395c23 --- /dev/null +++ b/src/hook/info/useMyPostQuery.ts @@ -0,0 +1,22 @@ +import { http } from "@/apis/http"; +import { CommonRes } from "@/types"; +import { MyPostProps } from "@/types/info"; +import { PostList } from "@/types/post"; +import { useQuery } from "@tanstack/react-query"; + +export const useMyPostQuery = ({ + clubId, + category, + page, + size, + sort="", +}: MyPostProps) => { + const myPostUrl = `/clubs/${clubId}/posts/my_posts?postCategory=${category}&page=${page}&size=${size}&sort=${sort}`; + return useQuery>({ + queryKey: ["postMyPost", { clubId, category, size, page, sort }], + queryFn: async () => + await http.get>(myPostUrl) + }); +}; + +export default useMyPostQuery; \ No newline at end of file diff --git a/src/hook/post/usePostDeleteMutation.ts b/src/hook/post/usePostDeleteMutation.ts new file mode 100644 index 0000000..e94aee7 --- /dev/null +++ b/src/hook/post/usePostDeleteMutation.ts @@ -0,0 +1,18 @@ +import { http } from "@/apis/http"; +import { PostDetailProps } from "@/types/post"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useRouter } from "next/navigation"; + +export const usePostDeleteMutation = ({ clubId, postId }: PostDetailProps) => { + const queryClient = useQueryClient(); + const router = useRouter(); + return useMutation({ + mutationFn: () => http.delete(`/clubs/${clubId}/posts/${postId}/delete`), + onSuccess: () => { + router.back(); + queryClient.invalidateQueries({ + queryKey: ["postList"], + }); + }, + }); +}; diff --git a/src/hook/post/usePostDetailQuery.ts b/src/hook/post/usePostDetailQuery.ts index be2d27e..14f68f4 100644 --- a/src/hook/post/usePostDetailQuery.ts +++ b/src/hook/post/usePostDetailQuery.ts @@ -1,18 +1,14 @@ import { http } from "@/apis/http"; -import { CommonRes } from "@/types"; -import { PostDetailProps } from "@/types/post"; +import { postKeys } from "@/constants/keys/postKey"; +import { CommonNoPageRes } from "@/types"; +import { PostDetailProps, PostList } from "@/types/post"; import { useQuery } from "@tanstack/react-query"; export const usePostDetailQuery = ({ clubId, postId }: PostDetailProps) => { - return useQuery>({ - queryKey: ["postDetail", clubId, postId], - queryFn: async () => { - const response = await http.get>( - `clubs/4/posts/9` - ); - return response; - }, - staleTime: 1000, + return useQuery>({ + queryKey: [postKeys.detail({ clubId, postId })], + queryFn: async () => + await http.get>(`/clubs/${clubId}/posts/${postId}`), }); }; diff --git a/src/hook/post/usePostListQuery.ts b/src/hook/post/usePostListQuery.ts new file mode 100644 index 0000000..aa92435 --- /dev/null +++ b/src/hook/post/usePostListQuery.ts @@ -0,0 +1,37 @@ +import { http } from "@/apis/http"; +import { CommonRes } from "@/types"; +import { PostList } from "@/types/post"; +import { useQuery } from "@tanstack/react-query"; + +interface props { + clubId: number; + keyword?: string; + category: "NOTICE" | "FREE"; + startData?: string; + endData?: string; + page: number; + size: number; + sort?: string; +} +export const usePostListQuery = ({ + clubId, + keyword = "", + category, + startData, + endData, + page, + size, + sort = "string", +}: props) => { + const keywordUrl = `/clubs/${clubId}/posts?keyword=${keyword}&sortBy=createAt&category=${category}&page=${page}&size=${size}`; + const datePullUrl = `/clubs/${clubId}/posts?keyword=${keyword}&startDate=${startData}&endData=${endData}&sortBy=createAt&page=${page}&size=${size}`; + return useQuery>({ + queryKey: ["postList", { clubId, category, size, page, sort }], + queryFn: async () => + await http.get>(keywordUrl, { + headers: { accept: "*/*" }, + }), + }); +}; + +export default usePostListQuery; diff --git a/src/hook/post/usePostMutation.ts b/src/hook/post/usePostMutation.ts index a8e9954..4fd8943 100644 --- a/src/hook/post/usePostMutation.ts +++ b/src/hook/post/usePostMutation.ts @@ -1,6 +1,6 @@ import { http } from "@/apis/http"; import { PostFormProps } from "@/types/post"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; async function registePost(postData: PostFormProps) { @@ -47,9 +47,13 @@ interface UseRegistePost { } export function usePostMutation(): UseRegistePost { const router = useRouter(); + const queryClient = useQueryClient(); const { mutate } = useMutation({ mutationFn: registePost, onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ["postList"], + }); console.log("게시글 등록 성공"); router.back(); }, diff --git a/src/hook/post/useheartMutation.ts b/src/hook/post/useheartMutation.ts new file mode 100644 index 0000000..848a9c0 --- /dev/null +++ b/src/hook/post/useheartMutation.ts @@ -0,0 +1,27 @@ +import { http } from "@/apis/http"; +import { postKeys } from "@/constants/keys/postKey"; +import { PostDetailProps } from "@/types/post"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +export const useHeartMutation = ({ clubId, postId }: PostDetailProps) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: () => http.post(`/clubs/${clubId}/posts/${postId}/like`), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [postKeys.detail({ postId, clubId })], + }); + }, + }); +}; +export const useHeartCancleMutation = ({ clubId, postId }: PostDetailProps) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: () => http.delete(`/clubs/${clubId}/posts/${postId}/like`), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [postKeys.detail({ postId, clubId })], + }); + }, + }); +}; diff --git a/src/hook/schedule/useDebounce.ts b/src/hook/schedule/useDebounce.ts new file mode 100644 index 0000000..f673a39 --- /dev/null +++ b/src/hook/schedule/useDebounce.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; + +export const useDebounce = ({keyword, delay = 500}: { + keyword: string; + delay?: number; +}) => { + const [debouncedValue, setDebouncedValue] = useState(keyword); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(keyword); + }, delay); + + return () => clearTimeout(handler); + }, [keyword]); + + return debouncedValue; +} \ No newline at end of file diff --git a/src/hook/schedule/useSchedule.ts b/src/hook/schedule/useSchedule.ts index bbe9eeb..2ac60fe 100644 --- a/src/hook/schedule/useSchedule.ts +++ b/src/hook/schedule/useSchedule.ts @@ -1,38 +1,44 @@ import { changeMonth, filterDate } from "@/features/home/utils"; import { getScheduleExist } from "@/features/home/utils/util"; -import { FetchScheduleParams } from "@/types/schedule"; +import { FetchScheduleParams, ScheduleDate, ScheduleKeyword } from "@/types/schedule"; import moment from "moment"; import { useState } from "react"; import { useScheduleQuery } from "./useScheduleQuery"; const useSchedule = ( { - clubId, q, sDate, eDate + clubId, keyword, startDate, endDate, page, size }: FetchScheduleParams ) => { const [value, setValue] = useState(new Date()); const [params, setParams] = useState({ clubId: clubId, - q: q, - sDate: sDate, - eDate: eDate + keyword: keyword, + startDate: startDate, + endDate: endDate, + page: page, + size: size }) // params에 맞는 일정 fetch const { data, isLoading } = useScheduleQuery(params); // params 변경 - const refetchSchedule = ({ - clubId, - q, - sDate, - eDate - }: FetchScheduleParams) => setParams({ - clubId: clubId, - q: q, - sDate: sDate, - eDate: eDate - }) + const refetchPeriodSchedule = ({ + startDate, + endDate + }: ScheduleDate) => setParams((prev) => ({ + ...prev, + startDate: startDate, + endDate: endDate + })) + + const refetchKeywordSchedule = ({ + keyword + }: ScheduleKeyword) => setParams((prev) => ({ + ...prev, + keyword: keyword + })) // 일정 변경 시 실행 function const handleChange = (newValue: Date) => { @@ -46,8 +52,8 @@ const useSchedule = ( // 달 변경 시 해당 달 일정 새로 fetch isChn && setParams({ ...params, - sDate: moment(newValue).startOf('month').format("YYYY-MM-DD"), - eDate: moment(newValue).endOf('month').format("YYYY-MM-DD") + startDate: moment(newValue).startOf('month').format("YYYY-MM-DD"), + endDate: moment(newValue).endOf('month').format("YYYY-MM-DD") }) }; @@ -65,7 +71,8 @@ const useSchedule = ( return { value, setValue: handleChange, - refetchSchedule, + refetchPeriodSchedule, + refetchKeywordSchedule, filteredData, data, isLoading, diff --git a/src/hook/schedule/useScheduleQuery.ts b/src/hook/schedule/useScheduleQuery.ts index b4258c1..e2d4f74 100644 --- a/src/hook/schedule/useScheduleQuery.ts +++ b/src/hook/schedule/useScheduleQuery.ts @@ -4,12 +4,20 @@ import { FetchScheduleParams, ScheduleContent } from "@/types/schedule"; import { useQuery } from "@tanstack/react-query"; // 일정 조회 (/clubs/{clubs}/schedules) -export const useScheduleQuery = ({ clubId, q, sDate, eDate }: FetchScheduleParams) => { +export const useScheduleQuery = ({ + clubId, + keyword, + startDate, + endDate, + page, + size, + sort = "string", +}: FetchScheduleParams) => { return useQuery>({ - queryKey: ['schedule', clubId, q, sDate, eDate], + queryKey: ['schedule', {clubId, keyword, startDate, endDate, page, size, sort}], queryFn: async () => { const response = await http.get>( - `/clubs/${clubId}/schedules?q=${q}&sDate=${sDate}&eDate=${eDate}` + `/clubs/${clubId}/schedules?keyword=${keyword}&startDate=${startDate}&endDate=${endDate}&page=${page}&size=${size}` ); return response; }, diff --git a/src/hook/user/useClubListQuery.ts b/src/hook/user/useClubListQuery.ts index 75ffc8b..915fb5e 100644 --- a/src/hook/user/useClubListQuery.ts +++ b/src/hook/user/useClubListQuery.ts @@ -4,12 +4,13 @@ import { Club } from "@/types/club"; import { PageProps } from "@/types/pages"; import { useQuery } from "@tanstack/react-query"; +// 가입한 모임 조회 export const useClubListQuery = ({ page, size }: PageProps) => { return useQuery>({ queryKey: [`clubList`, { page, size }], queryFn: () => http.get>( - `/clubs?page=${page}&size=${size}`, + `/clubs/my?page=${page}&size=${size}`, { headers: { accept: "*/*" } } ), }); diff --git a/src/hook/user/useGetUserInfoQuery.ts b/src/hook/user/useGetUserInfoQuery.ts new file mode 100644 index 0000000..7b74a7f --- /dev/null +++ b/src/hook/user/useGetUserInfoQuery.ts @@ -0,0 +1,16 @@ +import { http } from "@/apis/http"; +import { CommonNoPageRes } from "@/types"; +import { signUpInfo, userInfo } from "@/types/user/types"; +import { useQuery } from "@tanstack/react-query"; + +export const useGetUserInfoQuery = () => { + return useQuery>({ + queryKey: ["userInfo"], + queryFn: async () => { + const response = + await http.get>(`/user/getInfo`); + return response; + }, + staleTime: 1000, + }); +}; diff --git a/src/hook/user/useModificationUserInfoMutation.ts b/src/hook/user/useModificationUserInfoMutation.ts new file mode 100644 index 0000000..f8ba8e5 --- /dev/null +++ b/src/hook/user/useModificationUserInfoMutation.ts @@ -0,0 +1,12 @@ +import { http } from "@/apis/http"; +import { signUpInfo } from "@/types/user/types"; +import { useMutation } from "@tanstack/react-query"; + +export const useModificationUserInfoMutation = () => { + return useMutation({ + mutationFn: (data: signUpInfo) => http.post(`/user/modification`, data), + onSuccess: (data) => { + console.log(data); + }, + }); +}; diff --git a/src/hook/user/useUserMutation.ts b/src/hook/user/useUserMutation.ts index 6491c6b..7663483 100644 --- a/src/hook/user/useUserMutation.ts +++ b/src/hook/user/useUserMutation.ts @@ -1,25 +1,20 @@ import { http } from "@/apis/http"; +import { signUpInfo } from "@/types/user/types"; import { useMutation } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; -export interface CreateUserProps { - userName: string; - userPhone: string; - userEmail: string; -} - // 추가 회원가입 (/user/signUp) export const useUserMutation = () => { const router = useRouter(); return useMutation({ - mutationFn: (data: CreateUserProps) => http.post('/user/signUp', data), + mutationFn: (data: signUpInfo) => http.post("/user/signUp", data), onSuccess: (res) => { - alert('회원가입 성공!'); + alert("회원가입 성공!"); console.log(res); router.push("/user/signup/complete"); }, onError: (err) => { console.log(err); - } - }) -} \ No newline at end of file + }, + }); +}; diff --git a/src/types/attendance/types.ts b/src/types/attendance/types.ts index 6e1efec..a24534b 100644 --- a/src/types/attendance/types.ts +++ b/src/types/attendance/types.ts @@ -20,13 +20,20 @@ export type GetAttendanceListResponse = AttendanceListItem[]; // 오늘의 일정 export interface TodayScheduleItem - extends Omit { - sDate: string; - eDate: string; + extends Omit { + clubId: number; + startDate: string; + endDate: string; } // 출석 아이템 export interface AttendanceSchedule extends ScheduleContent { + isSuccess?: boolean; attendanceAddress: string; onClick: () => void; } + +// 출석하기 +export interface AttendanceReqData { + attendanceNum: number; +} diff --git a/src/types/club/types.ts b/src/types/club/types.ts index c1bfb7c..eac91f0 100644 --- a/src/types/club/types.ts +++ b/src/types/club/types.ts @@ -1,4 +1,3 @@ - // 클럽 기본 구조 export interface Club { clubId: number; @@ -20,6 +19,7 @@ export enum ApprovalStatus { Approved = "ACCEPTED", Waiting = "WAITING", Rejected = "REJECTED", + Cancel = "CANCELED", } // 클럽 생성 폼 @@ -61,9 +61,9 @@ export interface ClubProfiles { } export interface ClubInfos { - clubIntro?: string + clubIntro?: string; isPrivate?: boolean; contactMeans?: string | string[]; namePolicy?: "NICK_NAME" | "REAL_NAME"; memberCount?: number; -} \ No newline at end of file +} diff --git a/src/types/comment/index.ts b/src/types/comment/index.ts new file mode 100644 index 0000000..516c191 --- /dev/null +++ b/src/types/comment/index.ts @@ -0,0 +1,2 @@ +export * from "./types"; + diff --git a/src/types/comment/types.ts b/src/types/comment/types.ts new file mode 100644 index 0000000..ea84526 --- /dev/null +++ b/src/types/comment/types.ts @@ -0,0 +1,31 @@ +import { PostDetailProps } from "../post"; + +// 클럽 조회 페이지 +export interface Comment { + commentId: number; + memberId: number; + postId: number; + parentId: null | number; + content: string; + createdAt: string; + updateAt: string; +} + +export interface CommentData { + postId: number; + content: string; + parentId?: number; +} +export interface CommantDeleteProps extends PostDetailProps{ + commentId: number; +} +export interface CommentsProps { + item: Comment; + onClick: (commentId: number) => void; +} +export interface CommentsListProps { + item: Comment; + replies?: Comment[]; + onClick: (commentId: number) => void; +} + diff --git a/src/types/info/index.ts b/src/types/info/index.ts new file mode 100644 index 0000000..eea524d --- /dev/null +++ b/src/types/info/index.ts @@ -0,0 +1 @@ +export * from "./types"; diff --git a/src/types/info/types.ts b/src/types/info/types.ts new file mode 100644 index 0000000..c5801b1 --- /dev/null +++ b/src/types/info/types.ts @@ -0,0 +1,22 @@ +export interface MyPostProps { + clubId: number; + userId?: number; + category?: "NOTICE" | "FREE"; + page: number; + size: number; + sort?: string; +} + +export interface MyPostContent { + postId: number; + writerProfileImage: string; + writerName: string; + postTitle: string; + postContent: string; + uploadImage: string[] | null; + postCategory: "NOTICE" | "FREE"; + postLikeCount: number; + commentCount: number; + isLiked: boolean; + createdAt: string; +} \ No newline at end of file diff --git a/src/types/post/types.ts b/src/types/post/types.ts index 948add0..326ebe2 100644 --- a/src/types/post/types.ts +++ b/src/types/post/types.ts @@ -12,3 +12,17 @@ export interface PostDetailProps { clubId: number; postId: number; } + +export interface PostList { + postId: number; + writerProfileImage: string; + writerName: string; + postTitle: string; + postContent: string; + uploadImage: [string]; + postCategory: "NOTICE" | "FREE"; + postLikeCount: number; + isLiked: boolean; + commentCount: number; + createdAt: string; +} diff --git a/src/types/schedule/types.ts b/src/types/schedule/types.ts index c288ccb..733a4cb 100644 --- a/src/types/schedule/types.ts +++ b/src/types/schedule/types.ts @@ -1,8 +1,8 @@ export enum Attendance { - BEFORE = "BEFORE", + BEFORE = "BEFORE", ONGOING = "ONGOING", - COMPLETE = "COMPLETE" -}; + COMPLETE = "COMPLETE", +} // 일정 목록 data export interface ScheduleContent { @@ -13,11 +13,23 @@ export interface ScheduleContent { attendanceStatus: Attendance; } +export interface ScheduleDate { + startDate?: string; + endDate?: string; +} + +export interface ScheduleKeyword { + keyword?: string; +} + export interface FetchScheduleParams { clubId: number; - q?: string; - sDate?: string; - eDate?: string; + keyword?: string; + startDate?: string; + endDate?: string; + page: number; + size: number; + sort?: string; } // 일정 박스 @@ -44,10 +56,9 @@ export interface ScheduleData { } // 일정 상세 data - export interface ScheduleDetailProp { clubId: number; - scheduleId: number + scheduleId: number; } export interface ScheduleDetailContent { @@ -61,4 +72,4 @@ export interface ScheduleDetailContent { registerProfileImage: string; registrationDate: string; attendanceStatus: Attendance; -} \ No newline at end of file +} diff --git a/src/types/user/types.ts b/src/types/user/types.ts new file mode 100644 index 0000000..ad93209 --- /dev/null +++ b/src/types/user/types.ts @@ -0,0 +1,10 @@ +export interface signUpInfo { + userName: string; + userPhone: string; + userEmail: string; +} + +export interface userInfo extends signUpInfo { + userId: number; + oauth2Id: string; +}