diff --git a/src/app/list/create/_components/CreateList.tsx b/src/app/list/create/_components/CreateList.tsx index 240330d4..7fcc0d6f 100644 --- a/src/app/list/create/_components/CreateList.tsx +++ b/src/app/list/create/_components/CreateList.tsx @@ -4,8 +4,8 @@ import { useEffect, useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { useRouter } from 'next/navigation'; import { useSearchParams } from 'next/navigation'; +import { useQuery } from '@tanstack/react-query'; -// import Header from './list/Header'; import Header from '@/components/Header/Header'; import Section from './list/Section'; import SimpleInput from './list/SimpleInput'; @@ -15,16 +15,17 @@ import MemberSelector from './list/MemberSelector'; import ColorSelector from './list/ColorSelector'; import RadioInput from './list/RadioInput'; -import * as styles from './CreateList.css'; - -import { listPlaceholder } from '@/lib/constants/placeholder'; -import { BACKGROUND_COLOR } from '@/styles/Color'; -import { CategoryType } from '@/lib/types/categoriesType'; -import { UserProfileType } from '@/lib/types/userProfileType'; +import { useUser } from '@/store/useUser'; +import { QUERY_KEYS } from '@/lib/constants/queryKeys'; import getCategories from '@/app/_api/category/getCategories'; -import getUsers from '@/app/_api/user/getUsers'; +import getFollowingList from '@/app/_api/follow/getFollowingList'; +import { CategoryType } from '@/lib/types/categoriesType'; +import { FollowingListType } from '@/lib/types/followType'; +import { BACKGROUND_COLOR } from '@/styles/Color'; +import { listPlaceholder } from '@/lib/constants/placeholder'; import { listDescriptionRules, listLabelRules, listTitleRules } from '@/lib/constants/formInputValidationRules'; -// import { listDescription } from '@/app/[userNickname]/[listId]/_components/ListDetailOuter/ListInformation.css'; + +import * as styles from './CreateList.css'; interface CreateListProps { onNextClick: () => void; @@ -40,7 +41,7 @@ interface CreateListProps { */ function CreateList({ onNextClick, type }: CreateListProps) { const [categories, setCategories] = useState([]); - const [users, setUsers] = useState([]); + const { user: me } = useUser(); const { setValue, getValues, control } = useFormContext(); const collaboIDs = useWatch({ control, name: 'collaboratorIds' }); @@ -51,12 +52,10 @@ function CreateList({ onNextClick, type }: CreateListProps) { const searchParams = useSearchParams(); const isTemplateCreation = searchParams?.has('title') && searchParams?.has('category'); - const fetchUsers = async () => { - try { - const data = await getUsers(); - setUsers(data.userInfos); - } catch (error) {} - }; + const { data: followingList } = useQuery({ + queryKey: [QUERY_KEYS.getFollowingList, me.id], + queryFn: () => getFollowingList(me.id), + }); useEffect(() => { const fetchCategories = async () => { @@ -140,8 +139,7 @@ function CreateList({ onNextClick, type }: CreateListProps) {
{ setValue('collaboratorIds', [...collaboIDs, userId]); }} @@ -157,7 +155,6 @@ function CreateList({ onNextClick, type }: CreateListProps) { errorMessage: `콜라보레이터는 최대 20명까지 지정할 수 있어요.`, }, }} - defaultValue={getValues('collaboratorIds')} />
diff --git a/src/app/list/create/_components/list/MemberSelector.tsx b/src/app/list/create/_components/list/MemberSelector.tsx index 4a1c846a..e566528d 100644 --- a/src/app/list/create/_components/list/MemberSelector.tsx +++ b/src/app/list/create/_components/list/MemberSelector.tsx @@ -1,18 +1,24 @@ 'use client'; import { useEffect, useRef, useState } from 'react'; +import { useParams } from 'next/navigation'; +import { useQuery } from '@tanstack/react-query'; import UserProfileImage from '@/components/UserProfileImage/UserProfileImage'; import SearchIcon from '/public/icons/search.svg'; import EraseButton from '/public/icons/x_circle_fill.svg'; -import * as styles from './MemberSelector.css'; - +import getListDetail from '@/app/_api/list/getListDetail'; +import getUsersByNicknameSearch from '@/app/_api/user/getUsersByNicknameSearch'; +import { QUERY_KEYS } from '@/lib/constants/queryKeys'; +import { UserSearchType } from '@/lib/types/user'; import { UserProfileType } from '@/lib/types/userProfileType'; +import { ListDetailType } from '@/lib/types/listType'; + +import * as styles from './MemberSelector.css'; interface MemberSelectorProps { placeholder: string; - members: UserProfileType[]; - fetchData: () => Promise; + followingList: UserProfileType[]; onClickAdd: (userId: number) => void; onClickDelete: (userId: number) => void; rules?: { @@ -24,7 +30,6 @@ interface MemberSelectorProps { errorMessage: string; }; }; - defaultValue?: UserProfileType[]; } /** @@ -32,28 +37,31 @@ interface MemberSelectorProps { * 사용자의 프로필 목록을 보여주고, 검색을 통해 사용자를 선택하는 기능 제공 * * @param placeholder - 검색 입력란에 보여질 placeholder - * @param members - 드롭다운 보여질 사용자 프로필 목록 - * @param fetchData - 검색어가 입력됨에따라 보여질 멤버 목록을 업데이트할 함수 + * @param followingList - 처음 드롭다운에 보여질 팔로우한 사용자 목록 * @param onClickAdd - 선택한 멤버를 추가하는 함수 * @param onClickDelete - 사용자를 선택 취소하는 함수 */ -function MemberSelector({ - placeholder, - members = [], - fetchData, - onClickAdd, - onClickDelete, - rules, - defaultValue, -}: MemberSelectorProps) { +function MemberSelector({ placeholder, followingList, onClickAdd, onClickDelete, rules }: MemberSelectorProps) { const [input, setInput] = useState(''); - const [selectedList, setSelectedList] = useState(defaultValue || []); + const [selectedList, setSelectedList] = useState([]); const [isDropDownOpen, setIsDropDownOpen] = useState(false); const inputRef = useRef(null); const dropdownRef = useRef(null); const listRef = useRef(null); + const { data: searchResult } = useQuery({ + queryKey: [QUERY_KEYS.getUsersByNicknameSearch, input], + queryFn: () => getUsersByNicknameSearch(input), + }); + + const param = useParams<{ userId: string; listId: string }>(); + + const { data: listDataBeforeEdit } = useQuery({ + queryKey: [QUERY_KEYS.getListDetail, param?.listId], + queryFn: () => getListDetail(Number(param?.listId)), + }); + useEffect(() => { const closeDropdown = (event: MouseEvent) => { if ( @@ -69,18 +77,14 @@ function MemberSelector({ }; document.addEventListener('click', closeDropdown); - fetchData(); - return () => { document.removeEventListener('click', closeDropdown); }; }, []); useEffect(() => { - if (defaultValue) { - setSelectedList(defaultValue); - } - }, [defaultValue]); + if (listDataBeforeEdit) setSelectedList(listDataBeforeEdit.collaborators); + }, [listDataBeforeEdit]); return (
@@ -102,31 +106,70 @@ function MemberSelector({ {/* 멤버 검색 드롭다운 */} {isDropDownOpen && (
- {members - .filter((user) => user.nickname.toLocaleLowerCase().includes(input.toLocaleLowerCase())) - .map((user) => ( -
{ - if (rules?.maxNum && selectedList.length >= rules.maxNum.value) { - return; - } - if (!selectedList.find((selectedUser: UserProfileType) => selectedUser.id === user.id)) { - setSelectedList([...selectedList, user]); - onClickAdd(user.id); - } - }} - > - - {user.nickname} - {selectedList.find((collaboUser: UserProfileType) => collaboUser.id === user.id) && ( - - )} -
- ))} - {members.filter((user) => user.nickname.toLocaleLowerCase().includes(input.toLocaleLowerCase())).length === - 0 &&
검색결과가 없어요.
} + {input !== '' ? ( + searchResult?.collaborators.length === 0 ? ( + <> + {searchResult?.collaborators.filter((user) => + user.nickname.toLocaleLowerCase().includes(input.toLocaleLowerCase()) + ).length === 0 &&
검색결과가 없어요.
} + + ) : ( + <> + {searchResult?.collaborators.map((user) => ( +
{ + if (rules?.maxNum && selectedList.length >= rules.maxNum.value) { + return; + } + if (!selectedList.find((selectedUser: UserProfileType) => selectedUser.id === user.id)) { + console.log(user); + setSelectedList([...selectedList, user]); + onClickAdd(user.id); + } + }} + > + + {user.nickname} + {selectedList.find((collaboUser: UserProfileType) => collaboUser.id === user.id) && ( + + )} +
+ ))} + + ) + ) : followingList.length === 0 ? ( + <> + {searchResult?.collaborators.filter((user) => + user.nickname.toLocaleLowerCase().includes(input.toLocaleLowerCase()) + ).length === 0 &&
검색결과가 없어요.
} + + ) : ( + <> + {followingList.map((user) => ( +
{ + if (rules?.maxNum && selectedList.length >= rules.maxNum.value) { + return; + } + if (!selectedList.find((selectedUser: UserProfileType) => selectedUser.id === user.id)) { + setSelectedList([...selectedList, user]); + onClickAdd(user.id); + } + }} + > + + {user.nickname} + {selectedList.find((collaboUser: UserProfileType) => collaboUser.id === user.id) && ( + + )} +
+ ))} + + )}
)} {rules?.maxNum && selectedList.length >= rules.maxNum.value && ( diff --git a/src/app/user/[userId]/list/[listId]/edit/page.tsx b/src/app/user/[userId]/list/[listId]/edit/page.tsx index 5529662a..6f178ecf 100644 --- a/src/app/user/[userId]/list/[listId]/edit/page.tsx +++ b/src/app/user/[userId]/list/[listId]/edit/page.tsx @@ -19,7 +19,7 @@ export default function EditPage() { const param = useParams<{ userId: string; listId: string }>(); const { data } = useQuery({ - queryKey: [QUERY_KEYS.getListDetail], + queryKey: [QUERY_KEYS.getListDetail, param?.listId], queryFn: () => getListDetail(Number(param?.listId)), }); diff --git a/src/lib/types/user.ts b/src/lib/types/user.ts index 888605ea..ff0055ed 100644 --- a/src/lib/types/user.ts +++ b/src/lib/types/user.ts @@ -1,3 +1,5 @@ +import { UserProfileType } from './userProfileType'; + // 로그인한 사용자 리스폰스 타입 export interface UserOnLoginType { id: number; @@ -10,3 +12,9 @@ export interface UserOnLoginType { isFirst: boolean; accessToken: string; } + +export interface UserSearchType { + collaborators: UserProfileType[]; + totalCount: number; + hasNext: boolean; +}