Skip to content
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

feat: 내 정보 뽀각 페이지 UI 구현 #15

Merged
merged 23 commits into from
Aug 1, 2024
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
1,048 changes: 952 additions & 96 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 2 additions & 2 deletions components.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/util"
"components": "@/system/components",
"utils": "@/utils/tailwind-util"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"node": "20.2.0"
},
"dependencies": {
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@tanstack/react-query": "^5.51.1",
"@tanstack/react-query-devtools": "^5.51.1",
"axios": "^1.7.2",
Expand Down
58 changes: 58 additions & 0 deletions src/app/(sidebar)/(my-info)/components/InfoCardItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Icon } from '@/system/components';
import { Tag } from '@/system/components/Tag/Tag';
import { formatToYYMMDD } from '@/utils/date';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/system/components/DropdownMenu/DropdownMenu';
import { InfoCard } from '@/types/info';

interface Props extends InfoCard {}

const TAG_TYPE_COLOR = {
역량: 'blue',
인성: 'purple',
} as const;

export function InfoCardItem({ title, updatedDate, cardTagList }: Props) {
const formattedDate = formatToYYMMDD(updatedDate, { separator: '.' });

return (
<div className="flex flex-col justify-between min-w-[343px] h-[140px] p-[24px] rounded-[16px] bg-white border border-neutral-5 cursor-pointer hover:border-neutral-30">
<div className="flex">
<div className="flex-1 overflow-hidden">
<div className="mb-[9px] text-[12px] text-neutral-20">{formattedDate}</div>
<div className="w-auto truncate text-[16px] font-semibold">{title}</div>
</div>
<div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 div 태그가 없어도 괜찮을 것이라고 생각하는데 어떻게 생각하시나요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스크린샷 2024-07-30 오전 1 34 34

해당 div 태그가 없다면 상단 div의 flex 속성 때문에 위 사진처럼 more 버튼의 height가 늘어나게 됩니다(원래는 정사각형).
버튼의 모양은 유지하면서 버튼의 배치만 지정하기 위해 div 태그로 한 번 감싸주게 되었습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 !

<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="rounded-[4px] hover:bg-neutral-1" aria-label="more button">
<Icon name="more" color="#1B1C1E" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem className="gap-[8px]">
<Icon name="delete" color="#FF5C5C" />
<div className="text-red-50 text-[15px] font-normal">삭제하기</div>
</DropdownMenuItem>
<DropdownMenuItem className="gap-[8px]">
<Icon name="pip" color="#70737C" />
<div className="text-neutral-95 text-[15px] font-normal">개별창으로 띄우기</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div className="flex gap-[8px]">
{cardTagList.map(({ id, type, name }) => (
<Tag key={id} color={TAG_TYPE_COLOR[type]}>
{name}
</Tag>
))}
</div>
</div>
);
}
56 changes: 56 additions & 0 deletions src/app/(sidebar)/(my-info)/components/InfoCardList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
'use client';

import { useState } from 'react';
import { mockInfoCount, mockInfoList } from '../mock';
import { cn } from '@/utils/tailwind-util';
import { Icon } from '@/system/components';
import { InfoCardItem } from './InfoCardItem';

const INFO_OPTIONS = ['경험 정리', '자기소개서', '면접 질문'] as const;

export function InfoCardList() {
const [currentOption, setCurrentOption] = useState<(typeof INFO_OPTIONS)[number]>('경험 정리');

// TODO: API 연동 시 response data로 변경
const infoCount = mockInfoCount;
const infoList = mockInfoList;
Comment on lines +14 to +16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool !


return (
<section>
<div className="mb-[28px] flex justify-between">
<div className="flex gap-[24px]">
{INFO_OPTIONS.map((option) => (
<button
key={option}
className="flex gap-[6px] items-center cursor-pointer"
onClick={() => setCurrentOption(option)}>
<div
className={cn(
'text-[18px] text-neutral-10 font-semibold',
currentOption === option && 'text-neutral-80',
)}>
{option}
</div>
<div
className={cn(
'px-[8px] py-[2px] bg-neutral-10 rounded-[6px] text-neutral-1 text-[14px] font-semibold',
currentOption === option && 'bg-neutral-80',
)}>
{infoCount[option]}
</div>
</button>
))}
</div>
<button className="flex items-center gap-[8px] bg-neutral-95 py-[6px] pl-[16px] pr-[10px] rounded-[6px]">
<div className="text-white text-[14px] font-semibold">카드 추가</div>
<Icon name="add" color="#08F29B" />
</button>
</div>
<div className="grid grid-cols-[repeat(auto-fill,minmax(343px,1fr))] gap-[16px]">
{infoList.map((info) => (
<InfoCardItem key={info.id} {...info} />
))}
</div>
</section>
);
}
61 changes: 61 additions & 0 deletions src/app/(sidebar)/(my-info)/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { InfoCard } from '@/types/info';

export const mockInfoCount = {
'경험 정리': 1,
자기소개서: 3,
Copy link
Member

@eunbeann eunbeann Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

목데이터라 큰 의미 업겟지만... 혹시.. 여기만 string으로 안묶은 이유가 따로 잇는건가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

경험 정리면접 질문은 중간에 띄어쓰기가 들어가기 때문에 객체의 key값으로 인식 못해서 따옴표로 묶어줬어용
반면에 자기소개서는 띄어쓰기가 없기 때문에 따옴표 없이도 key 값으로 인식합니다!

'면접 질문': 2,
};

export const mockInfoList: InfoCard[] = [
{
id: 1,
title: '제목',
updatedDate: '2024-07-20T20:00:00',
cardTagList: [
{
id: 1,
name: '인성태그1',
type: '인성',
},
{
id: 2,
name: '역량태그1',
type: '역량',
},
],
},
{
id: 2,
title: 'test title2',
updatedDate: '2024-07-10T20:00:00',
cardTagList: [
{
id: 1,
name: '인성태그1',
type: '인성',
},
{
id: 2,
name: '역량태그1',
type: '역량',
},
],
},
{
id: 3,
title: '제목제목제목제목제목제목제목제목제목제목제목제목제목제목제목제목제목',
updatedDate: '2024-07-15T20:00:00',
cardTagList: [
{
id: 1,
name: '인성태그1',
type: '인성',
},
{
id: 2,
name: '역량태그1',
type: '역량',
},
],
},
];
17 changes: 17 additions & 0 deletions src/app/(sidebar)/(my-info)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Icon } from '@/system/components';
import { InfoCardList } from './components/InfoCardList';

export default function MyInfo() {
return (
<div className="max-w-[1700px] py-[64px] px-[80px] mx-auto">
<div className="mb-[72px] flex justify-between">
<h1 className="text-[28px] font-bold">내 정보 뽀각</h1>
qkrdmstlr3 marked this conversation as resolved.
Show resolved Hide resolved
<button className="flex gap-[24px] p-[16px] border rounded-[8px] border-neutral-5 bg-white">
<div className="text-[16px] font-semibold">우정균님의 기본정보</div>
<Icon name="down" color="#878A93" />
</button>
</div>
<InfoCardList />
</div>
);
}
11 changes: 11 additions & 0 deletions src/app/(sidebar)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Sidebar } from '@/container/Sidebar/Sidebar';
import { PropsWithChildren } from 'react';

export default function SidebarLayout({ children }: PropsWithChildren) {
return (
<div className="flex">
<Sidebar />
<div className="flex-grow">{children}</div>
</div>
);
}
11 changes: 3 additions & 8 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { Metadata } from 'next';
import { Sidebar } from '@/container/Sidebar/Sidebar';
import { QueryProvider } from '@/lib';
import clsx from 'clsx';
import { Inter } from 'next/font/google';
import './globals.css';
import { cn } from '@/utils/tailwind-util';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 clsx 대신 쓰는 것 맞죠?? 🎉

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹 clsx와 twMerge의 기능을 같이 해주는 유틸함수입니다!


const inter = Inter({ subsets: ['latin'] });

Expand All @@ -19,12 +18,8 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={clsx(inter.className, 'flex')}>
<Sidebar />
{/* FIXME: */}
<div className="flex-grow">
<QueryProvider>{children}</QueryProvider>
</div>
<body className={cn(inter.className, 'bg-neutral-1')}>
<QueryProvider>{children}</QueryProvider>
</body>
</html>
);
Expand Down
3 changes: 0 additions & 3 deletions src/app/page.tsx

This file was deleted.

69 changes: 69 additions & 0 deletions src/system/components/DropdownMenu/DropdownMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client';

import * as React from 'react';
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';

import { cn } from '@/utils/tailwind-util';

const DropdownMenu = DropdownMenuPrimitive.Root;

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;

const DropdownMenuGroup = DropdownMenuPrimitive.Group;

const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-[12px] bg-white p-1 text-slate-950 shadow-[0px_2px_8px_0px_rgba(99,99,99,0.20)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50',
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;

const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-[12px] py-[12px] pl-[12px] pr-[16px] text-sm outline-none transition-colors focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50',
inset && 'pl-8',
className,
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;

const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn('-mx-1 my-1 h-px bg-slate-100 dark:bg-slate-800', className)}
{...props}
/>
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;

export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuGroup,
};
10 changes: 10 additions & 0 deletions src/system/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ import { Memo } from './SVG/Memo';
import { Profile } from './SVG/Profile';
import { Setting } from './SVG/Setting';
import { Logout } from './SVG/Logout';
import { Down } from './SVG/Down';
import { Add } from './SVG/Add';
import { More } from './SVG/More';
import { Division } from './SVG/Division';
import { RightChevron } from './SVG/RightChevron';
import { ProfileFill } from './SVG/ProfileFill';
import type { IconBaseType } from '@/system/components/Icon/SVG/type';
import { Delete } from './SVG/Delete';
import { Pip } from './SVG/Pip';

const iconMap = {
bell: Bell,
Expand All @@ -21,6 +26,11 @@ const iconMap = {
rightChevron: RightChevron,
search: Search,
setting: Setting,
down: Down,
add: Add,
more: More,
delete: Delete,
pip: Pip,
} as const;

export interface IconProps extends IconBaseType {
Expand Down
21 changes: 21 additions & 0 deletions src/system/components/Icon/SVG/Add.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IconBaseType } from './type';

export function Add({ size, color }: IconBaseType) {
return (
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} fill="none" xmlns="http://www.w3.org/2000/svg">
<mask
id="mask0_1054_1585"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24">
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_1054_1585)">
<path d="M11.25 12.75H5.5V11.25H11.25V5.5H12.75V11.25H18.5V12.75H12.75V18.5H11.25V12.75Z" fill={color} />
</g>
</svg>
);
}
16 changes: 16 additions & 0 deletions src/system/components/Icon/SVG/Delete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IconBaseType } from './type';

export function Delete({ size, color }: IconBaseType) {
return (
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.62598 7.40259H19.3736L18.0473 21.25H5.95222L4.62598 7.40259Z"
stroke={color}
strokeWidth="1.5"
strokeLinecap="square"
/>
<path d="M7.81177 2.75H16.1884" stroke={color} strokeWidth="1.5" strokeLinecap="square" />
<path d="M12 11.9995L12 16.6525" stroke={color} strokeWidth="1.5" strokeLinecap="square" />
</svg>
);
}
Loading
Loading