From 2244fe891a9c01db14cfa686cda0761b97acb279 Mon Sep 17 00:00:00 2001
From: Jungu Lee <1zzangjun@gmail.com>
Date: Mon, 13 Nov 2023 09:27:04 +0900
Subject: [PATCH 1/3] =?UTF-8?q?Refactor=20:=20=EC=88=A0=EB=84=A4=EC=9E=84?=
=?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20var?=
=?UTF-8?q?iant=20=EB=B6=84=EA=B8=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
client/src/assets/icons/XIcon.svg | 11 ++++++++++
client/src/components/post/AlcoleNameTag.tsx | 22 ++++++++++++++++----
2 files changed, 29 insertions(+), 4 deletions(-)
create mode 100644 client/src/assets/icons/XIcon.svg
diff --git a/client/src/assets/icons/XIcon.svg b/client/src/assets/icons/XIcon.svg
new file mode 100644
index 0000000..f4e6bc0
--- /dev/null
+++ b/client/src/assets/icons/XIcon.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/client/src/components/post/AlcoleNameTag.tsx b/client/src/components/post/AlcoleNameTag.tsx
index 8394976..50c3d41 100644
--- a/client/src/components/post/AlcoleNameTag.tsx
+++ b/client/src/components/post/AlcoleNameTag.tsx
@@ -1,13 +1,21 @@
import { Box, Chip, IconButton, Typography } from "@mui/material";
import PostSeeMoreIcon from "@/assets/icons/PostSeeMoreIcon.svg";
import { PostInterface } from "@/types/post/PostInterface";
+import XIcon from "@/assets/icons/XIcon.svg";
type Props = {
alcoholName: PostInterface["alcoholName"];
alcoholType: PostInterface["alcoholType"];
+ removable?: boolean;
+ onClickRemove?: () => void;
};
-const AlcoleNameTag = ({ alcoholName, alcoholType }: Props) => {
+const AlcoleNameTag = ({
+ alcoholName,
+ alcoholType,
+ removable = false,
+ onClickRemove,
+}: Props) => {
return (
{
{alcoholName}
-
-
-
+ {removable ? (
+ onClickRemove && onClickRemove()}>
+
+
+ ) : (
+
+
+
+ )}
);
};
From b476e463eaea0e73ee2369c3baf547b4fad52645 Mon Sep 17 00:00:00 2001
From: Jungu Lee <1zzangjun@gmail.com>
Date: Mon, 13 Nov 2023 09:27:45 +0900
Subject: [PATCH 2/3] =?UTF-8?q?New=20:=20=EC=88=A0=EB=A6=AC=EC=8A=A4?=
=?UTF-8?q?=ED=8A=B8=20=ED=8C=A8=EC=B9=AD=20=EC=BF=BC=EB=A6=AC=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
client/src/const/serverPath.ts | 5 +++
.../alcohol/useGetAlcoholListQuery.tsx | 32 +++++++++++++++++++
client/src/types/alcohol/AlcoholInterface.ts | 2 +-
3 files changed, 38 insertions(+), 1 deletion(-)
create mode 100644 client/src/queries/alcohol/useGetAlcoholListQuery.tsx
diff --git a/client/src/const/serverPath.ts b/client/src/const/serverPath.ts
index fe9c809..15f5897 100644
--- a/client/src/const/serverPath.ts
+++ b/client/src/const/serverPath.ts
@@ -35,3 +35,8 @@ export const REMOVE_POST = (pk:number)=>`${POST_LIST}/${pk}` as const
export type ATTACH_FILE_ResourceType = "POST" | "PROFILE" | "ALCOHOL";
export const ATTACH_FILE = (type: ATTACH_FILE_ResourceType, resourcePk: number) =>
`/attach/resources/${type}/${resourcePk}` as const;
+
+/**
+ * 알콜리스트를 받아오는 URL
+ */
+export const GET_ALCOHOL_LIST = '/alcohols' as const
\ No newline at end of file
diff --git a/client/src/queries/alcohol/useGetAlcoholListQuery.tsx b/client/src/queries/alcohol/useGetAlcoholListQuery.tsx
new file mode 100644
index 0000000..8a9f434
--- /dev/null
+++ b/client/src/queries/alcohol/useGetAlcoholListQuery.tsx
@@ -0,0 +1,32 @@
+import { GET_ALCOHOL_LIST } from "@/const/serverPath";
+import axios from "@/libs/axios";
+import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface";
+import { useQuery } from "@tanstack/react-query";
+
+const useGetAlcoholListQuery = (keyword: string) => {
+ return useQuery({
+ queryKey: AlcohilListQueryKey.byKeyword(keyword),
+ queryFn: async () => await getAlcoholListByKeyword(keyword),
+ });
+};
+
+export const getAlcoholListByKeyword = async (keyword: string) => {
+ const { data } = await axios.get<{
+ list: AlcoholDetailInterface[];
+ totalCount: number;
+ }>(GET_ALCOHOL_LIST, {
+ params: {
+ page: 0,
+ size: 10,
+ searchKeyword: keyword,
+ },
+ });
+ return data;
+};
+
+export const AlcohilListQueryKey = {
+ all: ["alcohol"] as const,
+ byKeyword: (keyword: string) => ["alcohol", keyword] as const,
+};
+
+export default useGetAlcoholListQuery;
diff --git a/client/src/types/alcohol/AlcoholInterface.ts b/client/src/types/alcohol/AlcoholInterface.ts
index 74b4ded..c2a5f2a 100644
--- a/client/src/types/alcohol/AlcoholInterface.ts
+++ b/client/src/types/alcohol/AlcoholInterface.ts
@@ -1,4 +1,4 @@
-export interface AlcoholDetailResponseInterface {
+export interface AlcoholDetailInterface {
alcoholNo: number;
alcoholTypeNo: number;
alcoholAttachUrls: AlcoholAttachUrlsInterface[];
From f7539352b8be61a1c96f34a4c0f77cd75b72c4ac Mon Sep 17 00:00:00 2001
From: Jungu Lee <1zzangjun@gmail.com>
Date: Mon, 13 Nov 2023 09:28:00 +0900
Subject: [PATCH 3/3] =?UTF-8?q?New=20:=20=EC=88=A0=20=EA=B2=80=EC=83=89=20?=
=?UTF-8?q?=ED=9B=84=20=EC=B2=A8=EB=B6=80=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../app/(protectedRoute)/new-post/page.tsx | 56 +++----
.../components/newpost/SearchAlcoholInput.tsx | 141 ++++++++++++++++++
2 files changed, 172 insertions(+), 25 deletions(-)
create mode 100644 client/src/components/newpost/SearchAlcoholInput.tsx
diff --git a/client/src/app/(protectedRoute)/new-post/page.tsx b/client/src/app/(protectedRoute)/new-post/page.tsx
index 3e76d8b..786dda9 100644
--- a/client/src/app/(protectedRoute)/new-post/page.tsx
+++ b/client/src/app/(protectedRoute)/new-post/page.tsx
@@ -15,10 +15,9 @@ import {
} from "@mui/material";
import GoBackIcon from "@/assets/icons/GoBackIcon.svg";
-import InputSearchIcon from "@/assets/icons/InputSearchIcon.svg";
-import AlcholeSearchIcon from "@/assets/icons/AlcholeSearchIcon.svg";
+
import { useRouter } from "next/navigation";
-import { ChangeEvent, useEffect, useState } from "react";
+import { ChangeEvent, useCallback, useEffect, useState } from "react";
import HOME from "@/const/clientPath";
import CameraIcon from "@/assets/icons/CameraIcon.svg";
import PinIcon from "@/assets/icons/PinIcon.svg";
@@ -27,23 +26,33 @@ import useNewPostMutation from "@/queries/newPost/useNewPostMutation";
import useNewAttachMutation from "@/queries/attach/useNewAttachMutation";
import { useInvalidatePostList } from "@/queries/post/useGetPostListInfiniteQuery";
import { useDeletePostMutation } from "@/queries/post/useDeletePostMutation";
+import {
+ NewPostRequestInterface,
+ NewPostRequest_AlCohol,
+} from "@/types/newPost/NewPostInterface";
+import SearchAlcoholInput from "@/components/newpost/SearchAlcoholInput";
export default function NewpostPage() {
const { setLoading } = useGlobalLoadingStore();
const router = useRouter();
const invalidatePreviousPost = useInvalidatePostList();
- const [formValue, setFormValue] = useState({
+ const [formValue, setFormValue] = useState({
postContent: "",
postType: "BASIC",
positionInfo: "",
tagList: [] as string[],
});
+ const [alcoholInfo, setAlcoholInfo] = useState();
+ useEffect(() => {
+ console.log(alcoholInfo);
+ }, [alcoholInfo]);
const [userTypedTag, setUserTypedTag] = useState("");
const [file, setFile] = useState();
const [fileUrl, setFileUrl] = useState();
const [isSuccess, SetIsSuccess] = useState(false);
+
useEffect(() => {
if (!file) {
return;
@@ -63,11 +72,14 @@ export default function NewpostPage() {
const { mutateAsync: attachFileHandler } = useNewAttachMutation();
const { mutateAsync: deletePostHandler } = useDeletePostMutation();
- const submitHandler = async () => {
+ const submitHandler = useCallback(async () => {
setLoading(true);
let postNo;
try {
- const { postNo: res } = await newPostHandler(formValue);
+ const { postNo: res } = await newPostHandler({
+ ...formValue,
+ ...alcoholInfo,
+ });
postNo = res;
if (file) {
try {
@@ -88,7 +100,8 @@ export default function NewpostPage() {
} finally {
setLoading(false);
}
- };
+ }, [formValue, alcoholInfo, router, router, file]);
+
return (
{/* 최상단 앱바 */}
@@ -115,25 +128,15 @@ export default function NewpostPage() {
{/* 검색창 */}
- ,
- endAdornment: ,
- }}
- onChange={changeHadler}
- sx={{ px: 0 }}
- />
-
+
+ {/* 내용 */}
- {formValue.postContent.length} /{" "}
+ {formValue.postContent!.length} /{" "}
200자
-
- {formValue.tagList.map((tag) => {
+
+ {formValue.tagList!.map((tag) => {
return (
#{tag}
@@ -166,11 +169,14 @@ export default function NewpostPage() {
onSubmit={(e) => {
e.preventDefault();
setFormValue((prev) => {
- if (!userTypedTag || prev.tagList.includes(userTypedTag)) {
+ if (!userTypedTag || prev.tagList?.includes(userTypedTag)) {
setUserTypedTag("");
return prev;
}
- return { ...prev, tagList: [...prev.tagList, userTypedTag] };
+ return {
+ ...prev,
+ tagList: [...(prev?.tagList ?? []), userTypedTag],
+ };
});
setUserTypedTag("");
}}
diff --git a/client/src/components/newpost/SearchAlcoholInput.tsx b/client/src/components/newpost/SearchAlcoholInput.tsx
new file mode 100644
index 0000000..ac2867a
--- /dev/null
+++ b/client/src/components/newpost/SearchAlcoholInput.tsx
@@ -0,0 +1,141 @@
+"use client";
+
+import {
+ Box,
+ Chip,
+ CircularProgress,
+ List,
+ ListItemButton,
+ TextField,
+ Typography,
+} from "@mui/material";
+import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react";
+import AlcholeSearchIcon from "@/assets/icons/AlcholeSearchIcon.svg";
+import InputSearchIcon from "@/assets/icons/InputSearchIcon.svg";
+import useGetAlcoholListQuery from "@/queries/alcohol/useGetAlcoholListQuery";
+import { AlcoholDetailInterface } from "@/types/alcohol/AlcoholInterface";
+import AlcoleNameTag from "./../post/AlcoleNameTag";
+import useDebounce from "@/hooks/useDebounce";
+import { NewPostRequest_AlCohol } from "@/types/newPost/NewPostInterface";
+import React from "react";
+
+interface SearchAlcoholInputInterface {
+ setAlcoholInfo: Dispatch>;
+}
+const SearchAlcoholInput = ({
+ setAlcoholInfo,
+}: SearchAlcoholInputInterface) => {
+ const [searchKeyword, setSearchKeyword] = useState("");
+ const debouncedValue = useDebounce(searchKeyword, 300);
+
+ const [selectedAlcohol, setSelectedAlcohol] =
+ useState();
+
+ const { data, isLoading, isSuccess } = useGetAlcoholListQuery(debouncedValue);
+ const [isSearchingAlcohol, setIsSearchingAlCohol] = useState(false);
+
+ const parsedDTO = useMemo(() => {
+ if (!selectedAlcohol) {
+ return;
+ }
+ const { alcoholNo, alcoholName, alcoholType, ...others } = selectedAlcohol;
+ return {
+ alcoholNo,
+ alcoholName,
+ alcoholType,
+ };
+ }, [selectedAlcohol]);
+
+ useEffect(() => {
+ setSearchKeyword(selectedAlcohol?.alcoholName ?? "");
+ setAlcoholInfo(parsedDTO);
+ }, [selectedAlcohol]);
+
+ return (
+ <>
+ ,
+ endAdornment: ,
+ }}
+ onChange={({ target }) => setSearchKeyword(target.value)}
+ value={searchKeyword}
+ onFocus={() => setIsSearchingAlCohol(true)}
+ onBlur={() => setIsSearchingAlCohol(false)}
+ sx={{ px: 0 }}
+ autoComplete="off"
+ />
+ {isSearchingAlcohol && (
+
+
+ {isSuccess &&
+ data?.list.map((alcoholData) => (
+ e.preventDefault()}
+ onClick={() => {
+ setSelectedAlcohol(alcoholData);
+ setSearchKeyword(alcoholData.alcoholName);
+ setIsSearchingAlCohol(false);
+ }}
+ sx={ListItemButtonStyle}
+ >
+
+
+
+ {alcoholData.alcoholName}
+
+
+
+
+ ))}
+ {isLoading && }
+
+
+ )}
+ {selectedAlcohol && (
+ setSelectedAlcohol(undefined)}
+ removable
+ />
+ )}
+ >
+ );
+};
+
+const WrapperStyle = {
+ width: "calc(100% - 32px)",
+ minHeight: "50px",
+ backgroundColor: "#F5F5F5",
+ border: "1px solid #E6E6E6",
+ borderRadius: 1.5,
+ position: "absolute",
+ top: "64px",
+ zIndex: 1,
+};
+const ListStyle = {
+ display: "flex",
+ flexDirection: "column",
+ py: 1,
+ px: 2,
+ gap: 0.5,
+};
+const ListItemButtonStyle = {
+ p: "1px",
+ borderRadius: 12,
+ justifyContent: "space-between",
+};
+const FlexboxStyle = {
+ display: "flex",
+ flexDirection: "row",
+ alignItems: "center",
+ gap: 1,
+};
+
+export default React.memo(SearchAlcoholInput);