Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import { Cards } from '@/types/dashboardView';
import { useModal } from '@/app/(with-header-sidebar)/mydashboard/_hooks/useModal';
import Modal from '@/app/(with-header-sidebar)/mydashboard/_components/modal/Modal';
import CardInfo from './card-detail/CardInfo';
import HeaderMenu from './card-detail/HeaderMenu';
import styles from './Card.module.css';

interface Props {
item: Cards;
index: number;
columnTitle: string;
}

function Card({ item, index }: Props) {
function Card({ item, index, columnTitle }: Props) {
const { isOpen, openModal, isClosing, closeModal } = useModal();

if (!item || !item.id) {
Expand Down Expand Up @@ -39,8 +41,11 @@ function Card({ item, index }: Props) {
onClose={closeModal}
title={item.title}
hasCloseButton={true}
headerComponent={() => (
<HeaderMenu closeModal={closeModal} cardId={item.id} />
)}
>
<CardInfo card={item} />
<CardInfo card={item} columnTitle={columnTitle} />
</Modal>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ function Column({
/>
</Button>
</div>

<div className={styles.scrollContext}>
<Droppable
droppableId={`${id}`}
Expand All @@ -116,11 +115,16 @@ function Column({
style={{ minHeight }}
>
{items.map((item, index) =>
item ? <Card key={item.id} item={item} index={index} /> : null
item ? (
<Card
key={item.id}
item={item}
index={index}
columnTitle={title}
/>
) : null
)}

{provided.placeholder}

<div ref={loadMoreRef} style={{ height: '1px' }} />
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: 2px;
}

.label {
Expand All @@ -26,13 +27,14 @@
line-height: 20px;
}

.description {
.description,
.avatarContainer {
color: var(--black-100);
font-size: 12px;
font-weight: 400;
}

.assignee .description {
.avatarContainer {
display: flex;
align-items: center;
gap: 8px;
Expand All @@ -43,7 +45,7 @@
height: 26px;
}

.dueDate .description {
.description {
margin-top: 8px;
}

Expand All @@ -57,6 +59,10 @@
gap: 16px;
}

.assignee {
gap: 5px;
}

.avatar {
width: 34px;
height: 34px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ interface AssignmentProps {
export default function Assignment({ card }: AssignmentProps) {
if (!card) return null;

// const { assignee, dueDate } = {
// dueDate: '2024-11-30 12:00',
// assignee: { profileImageUrl: null, nickname: 'manta', id: 1 },
// };

const { assignee, dueDate } = card;

return (
Expand All @@ -23,7 +18,7 @@ export default function Assignment({ card }: AssignmentProps) {
{assignee && (
<div className={styles.assignee}>
<dt className={styles.label}>๋‹ด๋‹น์ž</dt>
<dd className={styles.description}>
<dd className={styles.avatarContainer}>
<Avatar
name={assignee.nickname}
profileImageUrl={assignee.profileImageUrl}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,43 @@
margin-top: 20px;
}

.infoContainer {
display: flex;
flex-direction: column;
gap: 18px;
max-width: 330px;
margin-top: 16px;
}

.labelArea {
display: flex;
align-items: center;
gap: 12px;
}

.tagContainer {
display: flex;
gap: 6px;
}

.description {
color: var(--black);
font-size: 12px;
font-weight: 400;
line-height: 18px;
}

.imageWrapper {
position: relative;
width: 325px;
height: 170px;
}

.imageWrapper img {
border-radius: 6px;
object-fit: cover;
}

@media screen and (min-width: 768px) {
.cardInfo {
display: flex;
Expand All @@ -16,11 +53,27 @@

.infoContainer {
flex: 7;
max-width: 450px;
margin-top: 0;
}

.description {
font-size: 14px;
line-height: 24px;
}

.imageWrapper {
width: 445px;
height: 260px;
}
}

@media screen and (min-width: 1200px) {
.cardInfo {
gap: 39px;
}

.labelArea {
gap: 20px;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
import { Cards } from '@/types/dashboardView';
import Assignment from './Assignment';
import Tag from '@/components/card/Tag';
import ColumnLabel from '@/components/card/ColumnLabel';
import Pipe from '@/components/svg/Pipe';
import Image from 'next/image';
import styles from './CardInfo.module.css';

export default function CardInfo({ card }: { card: Cards }) {
interface CardInfoProps {
card: Cards;
columnTitle: string;
}

export default function CardInfo({ card, columnTitle }: CardInfoProps) {
const { description, tags, imageUrl } = card;

return (
<div className={styles.cardInfo}>
<div className={styles.assignmentContainer}>
<Assignment card={card} />
</div>
<div className={styles.infoContainer}>์นด๋“œ ์ƒ์„ธ๋‚ด์šฉ + ๋Œ“๊ธ€ ์˜์—ญ</div>
<div className={styles.infoContainer}>
<div className={styles.labelArea}>
<div>
<ColumnLabel name={columnTitle} />
</div>
<Pipe />
<div className={styles.tagContainer}>
{tags.map((tag, index) => (
<Tag key={index} name={tag} />
))}
</div>
</div>
<p className={styles.description}>{description}</p>
{imageUrl && (
<div className={styles.imageWrapper}>
<Image src={imageUrl} alt="ํ• ์ผ ์ด๋ฏธ์ง€" fill />
</div>
)}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.headerMenu {
display: flex;
align-items: center;
margin-right: 16px;
position: relative;
}

.moreButton {
display: inline-flex;
align-items: center;
}

.menuContainer {
position: absolute;
z-index: 999;
top: 40px;
right: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import More from '@/components/svg/More';
import type { Menu } from '@/types/menu';
import MenuDropdown from '@/components/MenuDropdown';
import { useMenu } from '@/hooks/useMenu';
import styles from './HeaderMenu.module.css';
import { deleteCard } from '@/lib/cardService';
import { useRouter } from 'next/navigation';
import useDashboardStore from '@/store/dashboardStore';

interface HeaderMenuProps {
cardId: number;
closeModal: () => void;
}

export default function HeaderMenu({ cardId, closeModal }: HeaderMenuProps) {
const router = useRouter();
const { dashboard } = useDashboardStore();
const { isMenuVisible, toggleMenu } = useMenu();

const handelDeleteClick = async () => {
await deleteCard(cardId);
closeModal();
router.replace(`/dashboard/${dashboard?.id}`);
};

const cardMenus: Menu[] = [
{ name: '์ˆ˜์ •ํ•˜๊ธฐ', handleOnClick: () => console.log('์ˆ˜์ •ํ•˜๊ธฐ ํด๋ฆญ') },
{ name: '์‚ญ์ œํ•˜๊ธฐ', handleOnClick: handelDeleteClick },
];

return (
<div className={styles.headerMenu}>
<button type="button" onClick={toggleMenu} className={styles.moreButton}>
<More />
</button>
{isMenuVisible && (
<div className={styles.menuContainer}>
<MenuDropdown menus={cardMenus} />
</div>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const DEFAULT_MEMBERS_STATE: MemberState = {
members: [],
};

const useMember = (dashboardId: string, pageSize = 4) => {
const useMember = (dashboardId: string | null, pageSize = 4) => {
const [memberState, setMemberState] = useState<MemberState>(
DEFAULT_MEMBERS_STATE
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@
color: var(--black-100);
}

.close {
.closeButtonWrapper {
position: relative;
width: 24px;
height: 24px;
transition: 200ms;
}

.close:hover {
filter: brightness(2);
transform: rotate(90deg);
.closeButton {
display: inline-flex;
align-items: center;
}

@keyframes fadeIn {
Expand Down Expand Up @@ -107,4 +107,9 @@
flex: 1;
color: var(--black-100);
}

.closeButtonWrapper {
width: 32px;
height: 32px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ModalProps {
allowDimClose?: boolean;
title?: string;
hasCloseButton?: boolean;
headerComponent?: React.ComponentType;
headerComponent?: React.ComponentType<unknown>;
children: ReactNode;
}

Expand Down Expand Up @@ -59,18 +59,16 @@ export default function Modal({
{title && <h3 className={styles.title}>{title}</h3>}
{Component && <Component />}
{hasCloseButton && (
<button
onClick={onClose}
className={styles.close}
aria-label="Close modal"
>
<Image
src="/icons/x_lg.svg"
alt="Close icon"
width={24}
height={24}
/>
</button>
<div className={styles.closeButtonWrapper}>
<button
onClick={onClose}
aria-label="Close modal"
type="button"
className={styles.closeButton}
>
<Image src="/icons/x_lg.svg" alt="Close icon" fill />
</button>
</div>
)}
</div>
{children}
Expand Down
1 change: 0 additions & 1 deletion src/app/(with-header-sidebar)/mydashboard/_hooks/useApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export default function useApi<
const [error, setError] = useState<AxiosError | null>(null);

const fetchData = useCallback(async () => {
// TODO if (loading) return; add?
setIsLoading(true);
setError(null);

Expand Down
Loading