Skip to content

Commit be91640

Browse files
authored
[design] 홈화면 퍼블리싱 (#59)
* feat: 프로젝트 생성 모달 적용 * feat: 홈 화면 디자인, 기능 개선 * feat: 코드리뷰 적용
1 parent 669eced commit be91640

22 files changed

Lines changed: 734 additions & 217 deletions

index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<!doctype html>
2-
<html lang="en">
2+
<html lang="ko">
33
<head>
44
<meta charset="UTF-8" />
5-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
5+
<link rel="shortcut icon" href="/icon.png" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>Vite + React + TS</title>
7+
<title>오레오파크 5RE5PARK</title>
88
</head>
99
<body>
1010
<div id="root"></div>

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"axios": "^1.7.7",
2727
"class-variance-authority": "^0.7.0",
2828
"clsx": "^2.1.1",
29+
"dayjs": "^1.11.13",
2930
"lucide-react": "^0.454.0",
3031
"react": "^18.3.1",
3132
"react-daum-postcode": "^3.1.3",

src/assets/icon.png

3.16 KB
Loading
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from 'react';
2+
3+
interface IDivideProps {
4+
className?: string;
5+
}
6+
7+
const DividingLine = (props: IDivideProps) => {
8+
return (
9+
<div
10+
className={`border-b-[1px] border-b-gray-300 ${props.className ?? ''}`}
11+
/>
12+
);
13+
};
14+
15+
export default DividingLine;

src/components/common/ListView.tsx

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Badge } from '@/components/ui/badge';
12
import { Button } from '@/components/ui/button';
23
import { Checkbox } from '@/components/ui/checkbox';
34
import {
@@ -10,7 +11,9 @@ import {
1011
} from '@/components/ui/table';
1112
import { ROUTES } from '@/constants/route';
1213
import useChecked from '@/hooks/useChecked';
14+
import { useCheckedStore } from '@/stores/checkedStore';
1315
import { IProjectProps } from '@/types/project';
16+
import { convertDateFormat } from '@/utils/date';
1417
import { SquareArrowOutUpRightIcon } from 'lucide-react';
1518
import { useEffect, useState } from 'react';
1619
import { NavigateFunction, useNavigate } from 'react-router-dom';
@@ -37,6 +40,8 @@ const ListView = ({ option = 'list', data }: Omit<IListViewProps, 'navi'>) => {
3740
};
3841

3942
const List = ({ data, navi }: IListViewProps) => {
43+
const { addChecked, removeChecked, removeAll } = useCheckedStore();
44+
4045
const [items, setItems] = useState<IProjectProps[]>(() => {
4146
const initialData = data || [];
4247
return initialData.map((item) => ({ ...item, checked: false }));
@@ -47,10 +52,10 @@ const List = ({ data, navi }: IListViewProps) => {
4752
const handleCheckboxChange = (id: number): void => {
4853
setItems(
4954
items.map((item) => {
50-
if (item.projectId === id) {
55+
if (item.projectSeq === id) {
5156
handleCheckedList.set(id);
5257
}
53-
return item.projectId === id
58+
return item.projectSeq === id
5459
? { ...item, checked: !item.checked }
5560
: item;
5661
})
@@ -65,7 +70,7 @@ const List = ({ data, navi }: IListViewProps) => {
6570
}))
6671
);
6772
!masterChecked
68-
? handleCheckedList.addAll(items.map((item) => item.projectId))
73+
? handleCheckedList.addAll(items.map((item) => item.projectSeq))
6974
: handleCheckedList.removeAll();
7075
};
7176

@@ -74,10 +79,14 @@ const List = ({ data, navi }: IListViewProps) => {
7479
setMasterChecked(allChecked);
7580
}, [items]);
7681

77-
const onTest = () => {
78-
alert(`삭제요청 리스트: ${checkedList}`);
79-
// 삭제요청 API 호출
80-
};
82+
useEffect(() => {
83+
const currentChecked = new Set(checkedList);
84+
// 한 번의 업데이트로 처리
85+
useCheckedStore.setState((state) => ({
86+
...state,
87+
checkedList: Array.from(currentChecked),
88+
}));
89+
}, [checkedList]);
8190

8291
return (
8392
<div>
@@ -87,31 +96,53 @@ const List = ({ data, navi }: IListViewProps) => {
8796
<TableHead className='w-10'>
8897
<Checkbox onClick={handleSelectAll} checked={masterChecked} />
8998
</TableHead>
90-
<TableHead className='w-[50%]'>Name</TableHead>
91-
<TableHead>Last modified</TableHead>
92-
<TableHead>Created</TableHead>
99+
<TableHead className='w-[50%]'>프로젝트 명</TableHead>
100+
<TableHead>작업목록</TableHead>
101+
<TableHead>수정일</TableHead>
102+
<TableHead>생성일</TableHead>
93103
<TableHead>바로가기</TableHead>
94104
</TableRow>
95105
</TableHeader>
96106
<TableBody>
97107
{items.map((item, idx) => {
98108
return (
99-
<TableRow key={item.name + idx}>
109+
<TableRow key={item.projectName + idx}>
100110
<TableCell>
101111
<Checkbox
102112
onClick={() => {
103-
handleCheckboxChange(item.projectId);
113+
handleCheckboxChange(item.projectSeq);
104114
}}
105115
checked={item.checked}
106116
/>
107117
</TableCell>
108-
<TableCell className='font-medium'>{item.name}</TableCell>
109-
<TableCell>{item.modDate}</TableCell>
110-
<TableCell>{item.regDate}</TableCell>
118+
<TableCell className='font-medium'>
119+
{item.projectName}
120+
</TableCell>
121+
<TableCell className='flex gap-1'>
122+
{item.tts ? <Badge variant={'outline'}>TTS</Badge> : <></>}
123+
{item.vc ? <Badge variant={'destructive'}>VC</Badge> : <></>}
124+
{item.concat ? (
125+
<Badge variant={'default'}>Concat</Badge>
126+
) : (
127+
<></>
128+
)}
129+
</TableCell>
130+
<TableCell>
131+
{convertDateFormat(
132+
new Date(item.projectUpdateDate),
133+
'YYYY-MM-DD hh:mm:ss'
134+
)}
135+
</TableCell>
136+
<TableCell>
137+
{convertDateFormat(
138+
new Date(item.projectDate),
139+
'YYYY-MM-DD hh:mm:ss'
140+
)}
141+
</TableCell>
111142
<TableCell
112143
className='hover:cursor-pointer'
113144
onClick={() => {
114-
navi(ROUTES.PROJECT + ROUTES.TTS + `/${item.projectId}`);
145+
navi(ROUTES.PROJECT + ROUTES.TTS + `/${item.projectSeq}`);
115146
}}
116147
>
117148
<SquareArrowOutUpRightIcon />
@@ -121,13 +152,6 @@ const List = ({ data, navi }: IListViewProps) => {
121152
})}
122153
</TableBody>
123154
</Table>
124-
{checkedList.length > 0 ? (
125-
<Button variant={'destructive'} onClick={onTest}>
126-
삭제하기
127-
</Button>
128-
) : (
129-
<></>
130-
)}
131155
</div>
132156
);
133157
};
@@ -138,27 +162,27 @@ const Tile = ({ data, navi }: IListViewProps) => {
138162
};
139163
return (
140164
<div>
141-
<ul className='flex flex-wrap flex-1'>
165+
<ul className='flex flex-wrap flex-1 gap-5'>
142166
{data ? (
143167
data.map((item, idx) => (
144168
<li
145-
key={item.name + idx}
146-
className='p-5 m-5 w-[30%] border hover:cursor-pointer hover:scale-95 duration-100 rounded-lg z-10'
169+
key={item.projectName + idx}
170+
className='p-5 w-[30%] border hover:cursor-pointer hover:scale-95 duration-100 rounded-lg z-10'
147171
onClick={() => {
148-
navi(ROUTES.PROJECT + ROUTES.TTS + `/${item.projectId}`);
172+
navi(ROUTES.PROJECT + ROUTES.TTS + `/${item.projectSeq}`);
149173
}}
150174
>
151-
<Checkbox
175+
{/* <Checkbox
152176
className='relative z-0 hover:bg-neutral-100'
153177
onClick={(
154178
e: React.MouseEvent<HTMLInputElement, MouseEvent>
155179
) => {
156180
e.stopPropagation();
157181
onTest(e);
158182
}}
159-
/>
183+
/> */}
160184
<div className='content-center h-40 text-center'>썸네일</div>
161-
<div>{item.name}</div>
185+
<div>{item.projectName}</div>
162186
</li>
163187
))
164188
) : (
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Tile from '@/components/common/Tile';
2+
import { Button } from '@/components/ui/button';
3+
import {
4+
Dialog,
5+
DialogContent,
6+
DialogHeader,
7+
DialogTitle,
8+
DialogTrigger,
9+
} from '@/components/ui/dialog';
10+
import { PROJECT_TYPE, ProjectType } from '@/pages/Home';
11+
import { BookAIcon, CombineIcon, FilePlusIcon, MicIcon } from 'lucide-react';
12+
import React from 'react';
13+
14+
const NewProjectButton = () => {
15+
const handleClick = {
16+
createNewProject: (type: ProjectType) => {
17+
alert(`${type} 프로젝트 생성`);
18+
},
19+
};
20+
return (
21+
<div>
22+
<Dialog>
23+
<DialogTrigger asChild>
24+
<Button variant={'green'}>
25+
<FilePlusIcon />새 프로젝트 생성
26+
</Button>
27+
</DialogTrigger>
28+
<DialogContent className='sm:max-w-fit w-[450px]'>
29+
<DialogHeader>
30+
<DialogTitle>새 프로젝트 생성</DialogTitle>
31+
</DialogHeader>
32+
<div className='flex flex-col gap-3'>
33+
<div>
34+
<Tile
35+
title={PROJECT_TYPE.TTS}
36+
desc='텍스트를 음성파일로 생성할 수 있어요'
37+
icon={BookAIcon}
38+
onClick={() => {
39+
handleClick.createNewProject(PROJECT_TYPE.TTS);
40+
}}
41+
className='w-[100%]'
42+
/>
43+
</div>
44+
<div className='flex gap-3'>
45+
<Tile
46+
title={PROJECT_TYPE.VC}
47+
desc='내 보이스를 연예인 보이스로 바꿀 수 있어요'
48+
icon={MicIcon}
49+
onClick={() => {
50+
handleClick.createNewProject(PROJECT_TYPE.VC);
51+
}}
52+
/>
53+
<Tile
54+
title={PROJECT_TYPE.CONCAT}
55+
desc='다양한 보이스 파일을 하나로 합칠 수 있어요'
56+
icon={CombineIcon}
57+
onClick={() => {
58+
handleClick.createNewProject(PROJECT_TYPE.CONCAT);
59+
}}
60+
/>
61+
</div>
62+
</div>
63+
</DialogContent>
64+
</Dialog>
65+
</div>
66+
);
67+
};
68+
69+
export default NewProjectButton;

src/components/common/OneVc.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const OneVc = () => {
88
<div className='pb-5'>
99
<div id='iconDiv' className='flex justify-between mb-1'>
1010
<div id='leftIcons'>
11-
{/* <Button size={'sm'} variant={'aipark'}> */}
1211
<Button size={'sm'}>
1312
재생성
1413
<RefreshCcwDotIcon />

src/components/common/Tile.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Button } from '@/components/ui/button';
2+
import { LucideProps } from 'lucide-react';
3+
import React from 'react';
4+
5+
interface ITileProps {
6+
title: string;
7+
icon?: React.ComponentType<Omit<LucideProps, 'ref'>>;
8+
desc?: string;
9+
className?: string;
10+
onClick?: () => void;
11+
}
12+
13+
const Tile = (props: ITileProps) => {
14+
return (
15+
<div
16+
className={
17+
' p-3 border h-[200px] rounded-lg hover:cursor-pointer hover:bg-accent hover:text-accent-foreground ' +
18+
`${props.className ?? ''}`
19+
}
20+
onClick={props.onClick}
21+
>
22+
<div className='content-center text-center h-2/3'>
23+
{props.icon && (
24+
<props.icon className='text-center w-[100%]' size={40} />
25+
)}
26+
<p className='mt-2 font-bold'>{props.title}</p>
27+
</div>
28+
{props.desc && (
29+
<div className='mt-2 text-sm text-center text-gray-500 break-keep'>
30+
{props.desc}
31+
</div>
32+
)}
33+
</div>
34+
);
35+
};
36+
37+
export default Tile;

0 commit comments

Comments
 (0)