-
Notifications
You must be signed in to change notification settings - Fork 6
Feat : 링크 수정 & 삭제 기능 작업 #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
715b952
5b0e946
48dfa67
a3ab47e
ef4648a
4f7619c
a614c13
8906724
a7baf53
9616d70
c941779
b1f340f
1ea09c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import React from "react"; | ||
|
|
||
| interface DropdownProps { | ||
| isDropdownOpen: boolean; | ||
| onEdit: () => void; | ||
| openDelete: () => void; | ||
| } | ||
|
|
||
| const Dropdown = ({ isDropdownOpen, onEdit, openDelete }: DropdownProps) => { | ||
| const buttonStyle = | ||
| "block w-full py-2 text-sm hover:bg-gray200 hover:text-purple100"; | ||
|
|
||
| return ( | ||
| <div className="absolute top-[17px] right-0 flex flex-col gap-[2px] min-w-[100px] bg-white shadow-lg rounded"> | ||
| <button className={buttonStyle} onClick={onEdit}> | ||
| 수정하기 | ||
| </button> | ||
| <button className={buttonStyle} onClick={openDelete}> | ||
| 삭제하기 | ||
| </button> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Dropdown; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { useState } from "react"; | ||
| import timeAgo from "@/util/timAgo"; | ||
| import Image from "next/image"; | ||
| import Dropdown from "./Dropdown"; | ||
|
|
||
| interface linkDataType { | ||
| id: number; | ||
|
|
@@ -13,77 +14,98 @@ interface linkDataType { | |
| } | ||
|
|
||
| interface CardItemProps extends linkDataType { | ||
| onEdit: () => void; | ||
| openDelete: () => void; | ||
| isFavoritePage?: boolean; // 즐겨찾기 페이지 여부를 판별하는 flag | ||
| } | ||
|
|
||
| const LinkCard = ({ isFavoritePage, ...info }: CardItemProps) => { | ||
| const LinkCard = ({ | ||
| onEdit, | ||
| openDelete, | ||
| isFavoritePage, | ||
|
||
| ...info | ||
|
||
| }: CardItemProps) => { | ||
| const [isSubscribed, setIsSubscribed] = useState(false); | ||
| const [isOpen, setIsOpen] = useState(false); | ||
| const [isDropdownOpen, setIsDropdownOpen] = useState(false); | ||
|
|
||
| const formattedDate = info.createdAt?.slice(0, 10).replace(/-/g, "."); | ||
| const createdTime = timeAgo(info.createdAt); | ||
|
|
||
| // dropdown 버튼 | ||
| const toggleDropdown = () => setIsDropdownOpen((prev) => !prev); | ||
|
|
||
| return ( | ||
| <div className="w-[340px] h-[344px] rounded-[12px] shadow-lg overflow-hidden cursor-pointer hover:scale-105 hover:duration-300"> | ||
| <section className="relative w-full h-[60%]"> | ||
| <Image | ||
| src={info.imageSource || `/images/no-content.svg`} | ||
| className="object-cover" | ||
| alt="링크 미리보기" | ||
| fill | ||
| /> | ||
| {/* isFavoritePage가 false일 때만 즐겨찾기 버튼 렌더링 */} | ||
| {!isFavoritePage && | ||
| (isSubscribed ? ( | ||
| <div | ||
| onClick={() => setIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-fill.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| ) : ( | ||
| <div | ||
| onClick={() => setIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-empty.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| ))} | ||
| </section> | ||
| <> | ||
| <div className="w-[340px] h-[344px] rounded-[12px] shadow-lg overflow-hidden cursor-pointer hover:scale-105 hover:duration-300"> | ||
| <section className="relative w-full h-[60%]"> | ||
| <Image | ||
| src={info.imageSource || `/images/no-content.svg`} | ||
| className="object-cover" | ||
| alt="링크 미리보기" | ||
| fill | ||
| /> | ||
| {/* isFavoritePage가 false일 때만 즐겨찾기 버튼 렌더링 */} | ||
| {!isFavoritePage && | ||
| (isSubscribed ? ( | ||
| <div | ||
| onClick={() => setIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-fill.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
| ) : ( | ||
| <div | ||
| onClick={() => setIsSubscribed(!isSubscribed)} | ||
| className="absolute top-[15px] right-[15px] z-1" | ||
| > | ||
| <Image | ||
| src="/icons/star-empty.svg" | ||
| width={32} | ||
| height={32} | ||
| alt="subscripe button" | ||
| /> | ||
| </div> | ||
|
||
| ))} | ||
| </section> | ||
|
|
||
| <section className="w-full h-[40%] flex flex-col justify-between gap-[10px] pt-[15px] px-[20px] pb-[10px]"> | ||
| <div className="flex justify-between"> | ||
| <span className="text-sm text-gray-400"> | ||
| {createdTime || "1일 전"} | ||
| </span> | ||
| {/* isFavoritePage가 false일 때만 케밥 버튼 렌더링 */} | ||
| {!isFavoritePage && ( | ||
| <div | ||
| className="relative w-[21px] h-[17px]" | ||
| onClick={(state) => setIsOpen(!state)} | ||
| > | ||
| <Image src="/icons/kebab.svg" alt="kebab button" fill /> | ||
| </div> | ||
| )} | ||
| </div> | ||
| <div className="text-[black100] text-lg "> | ||
| {info.description || "설명"} | ||
| </div> | ||
| <div className="text-sm text-[black200]"> | ||
| {formattedDate || "2024.11.06"} | ||
| </div> | ||
| </section> | ||
| </div> | ||
| <section className="w-full h-[40%] flex flex-col justify-between gap-[10px] pt-[15px] px-[20px] pb-[10px]"> | ||
| <div className="flex justify-between"> | ||
| <span className="text-sm text-gray-400"> | ||
| {createdTime || "1일 전"} | ||
| </span> | ||
| {/* isFavoritePage가 false일 때만 케밥 버튼 렌더링 */} | ||
| {!isFavoritePage && ( | ||
| <div className="relative"> | ||
| <button | ||
| className="relative w-[21px] h-[17px]" | ||
| onClick={toggleDropdown} | ||
| > | ||
| <Image src="/icons/kebab.svg" alt="kebab button" fill /> | ||
| </button> | ||
| {isDropdownOpen && ( | ||
| <Dropdown | ||
| isDropdownOpen={isDropdownOpen} | ||
| onEdit={onEdit} | ||
| openDelete={openDelete} | ||
| /> | ||
| )} | ||
| </div> | ||
| )} | ||
| </div> | ||
| <div className="text-[black100] text-lg "> | ||
| {info.description || "설명"} | ||
| </div> | ||
| <div className="text-sm text-[black200]"> | ||
| {formattedDate || "2024.11.06"} | ||
| </div> | ||
| </section> | ||
| </div> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,14 +1,34 @@ | ||
| import useModalStore from "@/store/useModalStore"; | ||
| import SubmitButton from "../SubMitButton"; | ||
| import ModalContainer from "./modalComponents/ModalContainer"; | ||
| import { useLinkCardStore } from "@/store/useLinkCardStore"; | ||
|
|
||
| const DeleteLinkModal = ({ | ||
| link, | ||
| linkId, | ||
| }: { | ||
| link: string; | ||
| linkId: number; | ||
| }) => { | ||
| const { closeModal } = useModalStore(); | ||
| const { deleteLink } = useLinkCardStore(); | ||
|
|
||
| const handleDelete = async () => { | ||
| try { | ||
| await deleteLink(linkId); | ||
| closeModal(); | ||
| } catch (error) { | ||
| console.error("Failed to delete the link:", error); | ||
| } | ||
| }; | ||
|
|
||
| const DeleteLinkModal = ({ link }: { link: string }) => { | ||
| return ( | ||
| <ModalContainer title="링크 삭제" subtitle={link}> | ||
| <SubmitButton | ||
| type="button" | ||
| // onClick={handleSubmit} | ||
| onClick={handleDelete} | ||
| width="w-full" | ||
| height="h-[51px] " | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 모달 파일마다 공백이 있었더라구요 😅👍 |
||
| height="h-[51px]" | ||
| color="negative" | ||
| > | ||
| 삭제하기 | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 안그래도 요걸 제가 안만들어놨었는데 추가해주셨군요!👍 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import { ChangeEvent, useState } from "react"; | ||
| import { putLinkURL } from "@/lib/api/link"; | ||
| import { useLinkCardStore } from "@/store/useLinkCardStore"; | ||
| import ModalContainer from "./modalComponents/ModalContainer"; | ||
| import ModalInput from "./modalComponents/ModalInput"; | ||
| import useModalStore from "@/store/useModalStore"; | ||
| import SubmitButton from "../SubMitButton"; | ||
|
|
||
| const EditLink = ({ | ||
| folderName, | ||
| link, | ||
| linkId, | ||
| }: { | ||
| folderName: string; | ||
| link: string; | ||
| linkId: number; | ||
| }) => { | ||
| const [value, setValue] = useState(""); | ||
| const { closeModal } = useModalStore(); | ||
| const { updateLink } = useLinkCardStore(); | ||
|
|
||
| const handleChange = (e: ChangeEvent<HTMLInputElement>) => { | ||
| setValue(e.target.value); | ||
| }; | ||
|
|
||
| const handleSubmit = async () => { | ||
| const body = { | ||
| url: value, | ||
| }; | ||
| if (value !== "") { | ||
| await updateLink(linkId, body); | ||
| } | ||
| closeModal(); | ||
| }; | ||
| return ( | ||
| <ModalContainer title="링크 주소 변경"> | ||
| <ModalInput | ||
| placeholder={link} | ||
| name={folderName} | ||
| value={value} | ||
| onChange={handleChange} | ||
| /> | ||
| <SubmitButton | ||
| type="button" | ||
| onClick={handleSubmit} | ||
| width="w-full" | ||
| height="h-[51px] " | ||
| color="positive" | ||
| > | ||
| 변경하기 | ||
| </SubmitButton> | ||
| </ModalContainer> | ||
| ); | ||
| }; | ||
| export default EditLink; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isDropdownOpen은 어떤 용도 인가요 ?.?
따로 쓰여지고 있지는 않는거 같아서요 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
modal 열림 상태일 때 상태 변경하려고 사용했는데 useModalStore에서 isOpen 상태를 가져와 isModalOpen에 저장하도록 변경했습니다!