-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #61 from Nico1eKim/feat/setting-design
💄 design: setting 페이지들 디자인 완료
- Loading branch information
Showing
12 changed files
with
228 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,39 @@ | ||
"use client"; | ||
|
||
import { MOCK } from "app/_constants/mock"; | ||
import classNames from "classnames"; | ||
import { useEffect, useState } from "react"; | ||
import BottomButton from "@/components/button/BottomButton"; | ||
import useInfScroll from "@/hooks/useInfScroll"; | ||
|
||
const INITIAL = ["정우"]; | ||
import { useForm } from "react-hook-form"; | ||
import MyArtistList from "@/components/MyArtistList"; | ||
import AlertModal from "@/components/modal/AlertModal"; | ||
import InputModal from "@/components/modal/InputModal"; | ||
import { useModal } from "@/hooks/useModal"; | ||
|
||
const FavoritePage = () => { | ||
const [favorite, setFavorite] = useState<string[]>(INITIAL); | ||
const handleClick = (name: string) => () => { | ||
if (favorite.includes(name)) { | ||
setFavorite((prev) => prev.filter((item) => item !== name)); | ||
return; | ||
} | ||
setFavorite((prev) => [...prev, name]); | ||
}; | ||
|
||
const { isVisible, infRef } = useInfScroll(); | ||
const [cursorId, setCursorId] = useState(12); | ||
|
||
useEffect(() => { | ||
if (cursorId > MOCK.length) return; | ||
if (isVisible) { | ||
setCursorId((prev) => prev + 6); | ||
} | ||
}, [isVisible]); | ||
const { modal, openModal, closeModal } = useModal(); | ||
const { control } = useForm({ defaultValues: { search: "" } }); | ||
|
||
return ( | ||
<div className="flex flex-col gap-24 overflow-auto py-24"> | ||
<button className="w-max text-14 font-600 text-gray-500 underline underline-offset-2">{"찾으시는 아티스트가 없나요? >"}</button> | ||
<div className="grid h-5/6 snap-y snap-mandatory grid-cols-3 gap-20 self-start overflow-auto px-16"> | ||
{MOCK.slice(0, cursorId).map((entity) => ( | ||
<button onClick={handleClick(entity.name)} key={entity.name} className="flex w-max snap-start flex-col items-center gap-8"> | ||
<div className={classNames("h-80 w-80 rounded-full bg-gray-300", { "border-[1px] border-solid border-black": favorite.includes(entity.name) })} /> | ||
{/* <Image src={entity.profileImage} alt={`${entity.name} 사진`} /> */} | ||
<p className="text-16 font-600">{entity.name}</p> | ||
<> | ||
<div className="flex flex-col gap-24 px-20 py-36"> | ||
<section className="flex flex-col gap-12"> | ||
<h2 className="text-20 font-700 text-gray-900">좋아하는 아티스트를 알려주세요!</h2> | ||
<button onClick={() => openModal("noArtist")} className="w-188 text-14 text-gray-500 underline underline-offset-2"> | ||
찾으시는 아티스트가 없으신가요? | ||
</button> | ||
))} | ||
<div ref={infRef} /> | ||
</section> | ||
<MyArtistList data={MOCK} /> | ||
</div> | ||
{/* <button className={classNames("rounded-sm px-16 py-12 text-16", { "bg-black text-white": favorite.length }, { "bg-gray-300 text-black": !favorite.length })}>변경하기</button> */} | ||
<BottomButton isDisabled={!favorite.length}>변경 내용 저장</BottomButton> | ||
</div> | ||
{modal === "noArtist" && ( | ||
<InputModal | ||
title="아티스트 등록 요청" | ||
label="" | ||
btnText="요청하기" | ||
handleBtnClick={() => openModal("confirm")} | ||
closeModal={closeModal} | ||
{...{ placeholder: "찾으시는 아티스트를 알려주세요.", rules: { required: "내용을 입력하세요." }, control: control }} | ||
/> | ||
)} | ||
{modal === "confirm" && <AlertModal closeModal={closeModal}>등록 요청이 제출되었습니다.</AlertModal>} | ||
</> | ||
); | ||
}; | ||
export default FavoritePage; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { useState } from "react"; | ||
import { ArtistType } from "../_types"; | ||
import ArtistCard from "./ArtistCard"; | ||
import ChipButton from "./chip/ChipButton"; | ||
import SearchInput from "./input/SearchInput"; | ||
|
||
interface Props { | ||
data: ArtistType[]; | ||
} | ||
|
||
const MyArtistList = ({ data }: Props) => { | ||
const [keyword, setKeyword] = useState(""); | ||
const [selected, setSelected] = useState<string[]>([]); | ||
|
||
const handleArtistClick = (name: string) => { | ||
if (selected.includes(name)) { | ||
setSelected((prevSelected) => prevSelected.filter((item) => item !== name)); | ||
} else { | ||
setSelected((prevSelected) => [...prevSelected, name]); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="flex flex-col gap-24 "> | ||
<section className="flex flex-col gap-16"> | ||
<SearchInput setKeyword={setKeyword} placeholder="최애의 행사를 찾아보세요!" /> | ||
<div className="flex w-full gap-12 overflow-auto"> | ||
{selected.map((artist) => ( | ||
<ChipButton label={artist} key={artist} onDelete={() => handleArtistClick(artist)} /> | ||
))} | ||
</div> | ||
</section> | ||
<section className="m-auto w-320"> | ||
<ul className="grid grid-cols-3 gap-x-16 gap-y-20 px-8"> | ||
{data.map((artist, index) => ( | ||
<li key={index}> | ||
<ArtistCard onClick={() => handleArtistClick(artist.name)} isChecked={selected.includes(artist.name)} profileImage={artist.profileImage}> | ||
{artist.name} | ||
</ArtistCard> | ||
</li> | ||
))} | ||
</ul> | ||
</section> | ||
</div> | ||
); | ||
}; | ||
|
||
export default MyArtistList; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import classNames from "classnames"; | ||
import { ChangeEvent, KeyboardEvent, ReactNode } from "react"; | ||
import { FieldPath, FieldValues, UseControllerProps, useController } from "react-hook-form"; | ||
import EditIcon from "@/public/icon/pencil.svg"; | ||
|
||
interface Props { | ||
children?: ReactNode; | ||
hasProfile?: boolean; | ||
} | ||
|
||
type Function = <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>( | ||
prop: UseControllerProps<TFieldValues, TName> & Props, | ||
) => ReactNode; | ||
|
||
const InputProfileImg: Function = ({ children, hasProfile, ...props }) => { | ||
const { field, fieldState } = useController(props); | ||
|
||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
field.onChange({ target: { value: e.target.files, name: field.name } }); | ||
}; | ||
|
||
const handleKeyDown = (e: KeyboardEvent) => { | ||
if (e.key === "Enter" || e.key === " ") { | ||
e.preventDefault(); | ||
const target = e.target as HTMLElement; | ||
const input = target.nextElementSibling as HTMLElement; | ||
if (input) { | ||
input.click(); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="flex-center flex-col gap-8"> | ||
<h2 className="w-full text-16 text-gray-900">프로필 사진</h2> | ||
<div className="relative flex flex-col gap-8"> | ||
{children} | ||
<label> | ||
{hasProfile ? ( | ||
<div | ||
onKeyDown={handleKeyDown} | ||
tabIndex={0} | ||
className="flex-center absolute right-0 top-0 h-28 w-28 cursor-pointer rounded-md border border-white-black bg-main-pink-500" | ||
> | ||
<EditIcon /> | ||
</div> | ||
) : ( | ||
<div | ||
onKeyDown={handleKeyDown} | ||
tabIndex={0} | ||
className="flex-center h-32 w-100 cursor-pointer rounded-sm border border-gray-400 bg-gray-50 px-16 text-14 font-600 text-gray-700" | ||
> | ||
이미지 등록 | ||
</div> | ||
)} | ||
<input type="file" name={field.name} ref={field.ref} multiple onChange={handleChange} className="hidden" accept="image/*" /> | ||
</label> | ||
</div> | ||
{fieldState.error && ( | ||
<p className={classNames(`font-normal mt-4 h-8 text-12`, { "text-red-500": fieldState.error, "text-gray-400": !fieldState.error })}>{fieldState?.error?.message}</p> | ||
)} | ||
</div> | ||
); | ||
}; | ||
export default InputProfileImg; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.