Skip to content

Commit f65c701

Browse files
authored
Merge pull request #78 from KW-AUTA/refactor/76-responsive
2 parents 8458516 + a33aead commit f65c701

File tree

21 files changed

+265
-147
lines changed

21 files changed

+265
-147
lines changed

src/components/layout/page-layout/Header.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function Header({ onMenuClick }: { onMenuClick?: () => void }) {
3535
<header className="fixed top-4 left-1/2 -translate-x-1/2 z-[999] w-[95vw] max-w-2xl bg-background/90 backdrop-blur-md flex items-center justify-between px-4 py-2 rounded-full shadow-lg transition-all md:static md:top-0 md:left-0 md:translate-x-0 md:w-full md:max-w-full md:rounded-none md:shadow-none md:px-8 md:py-6">
3636
{/* 모바일 햄버거 메뉴 */}
3737
{onMenuClick && (
38-
<button className="block md:hidden mr-2 p-2 rounded-full hover:bg-gray-100 transition" onClick={onMenuClick}>
38+
<button className="block lg:hidden mr-2 p-2 rounded-full hover:bg-gray-100 transition" onClick={onMenuClick}>
3939
<svg width="28" height="28" fill="none" stroke="currentColor">
4040
<path strokeLinecap="round" strokeWidth="2" d="M4 7h20M4 14h20M4 21h20" />
4141
</svg>
@@ -63,21 +63,21 @@ export default function Header({ onMenuClick }: { onMenuClick?: () => void }) {
6363
}}
6464
/>
6565
</div>
66-
<div className="flex items-center gap-2 md:gap-4 min-w-0">
66+
<div className="flex items-center gap-2 lg:gap-4 min-w-0">
6767
{/* 알림 버튼 (모바일에서는 숨김) */}
68-
<div className="hidden md:flex items-center">
68+
<div className="hidden lg:flex items-center">
6969
<button>
7070
<img src={notificationIcon} alt="notification button" />
7171
</button>
7272
</div>
7373
{/* 프로필 버튼 */}
74-
<button className="flex items-center min-w-0 max-w-[120px] md:max-w-44 gap-2 rounded-full px-3 py-1 bg-white shadow md:shadow-none overflow-hidden">
75-
<div className="w-8 h-8 md:w-9 md:h-9 rounded-full bg-gray-300 flex items-center justify-center overflow-hidden flex-shrink-0">
74+
<button className="flex items-center min-w-0 max-w-[120px] lg:max-w-44 gap-2 rounded-full px-3 py-1 bg-white shadow lg:shadow-none overflow-hidden">
75+
<div className="w-8 h-8 lg:w-9 lg:h-9 rounded-full bg-gray-300 flex items-center justify-center overflow-hidden flex-shrink-0">
7676
<img src={profileImg} alt="profile img" className="w-full h-full object-cover" />
7777
</div>
7878
<div className="flex flex-col justify-center items-start min-w-0">
79-
<p className="font-bm text-xs md:text-sm truncate max-w-[60px] md:max-w-[100px]">{profile?.username}</p>
80-
<p className="font-medium text-[10px] md:text-7 text-typography-gray truncate max-w-[60px] md:max-w-[100px]">
79+
<p className="font-bm text-xs lg:text-sm truncate max-w-[60px] lg:max-w-[100px]">{profile?.username}</p>
80+
<p className="font-medium text-[10px] lg:text-7 text-typography-gray truncate max-w-[60px] lg:max-w-[100px]">
8181
Test Automation Developer
8282
</p>
8383
</div>

src/components/layout/page-layout/Layout.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ export default function Layout() {
5454
{!shouldHideSidebarAndHeader && (
5555
<>
5656
{/* 데스크탑 사이드바 */}
57-
<div className="hidden md:block">
57+
<div className="hidden lg:block">
5858
<Sidebar />
5959
</div>
6060
{/* 모바일 오버레이 사이드바 */}
61-
<div className="block md:hidden">
61+
<div className="block lg:hidden">
6262
<Sidebar open={sidebarOpen} onClose={() => setSidebarOpen(false)} />
6363
</div>
6464
</>
6565
)}
66-
<div className={`flex flex-col w-full ${shouldHideSidebarAndHeader ? '' : 'md:ml-[280px]'}`}>
66+
<div className={`flex flex-col w-full ${shouldHideSidebarAndHeader ? '' : 'lg:ml-[280px]'}`}>
6767
<div className="w-full">
6868
{!shouldHideSidebarAndHeader && <Header onMenuClick={() => setSidebarOpen((prev) => !prev)} />}
69-
<main className="flex-grow py-4 pt-[90px] md:pt-0">
69+
<main className="flex-grow py-4 pt-[90px] lg:pt-0">
7070
<Outlet />
7171
</main>
7272
</div>

src/components/layout/sidebar/Sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ const Sidebar: React.FC<SidebarProps> = ({ open, onClose }) => {
5858
<>
5959
{/* 모바일 오버레이 */}
6060
<div
61-
className={`fixed inset-0 bg-black/50 z-[998] transition-opacity duration-300 md:hidden ${open ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}
61+
className={`fixed inset-0 bg-black/50 z-[998] transition-opacity duration-300 lg:hidden ${open ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}
6262
onClick={onClose}
6363
/>
6464
{/* 사이드바 */}
6565
<aside
66-
className={`fixed top-0 left-0 h-full w-[80vw] max-w-[320px] min-w-[280px] min-[450px]:min-w-[450px] bg-pointColor rounded-2xl shadow-xl z-[999] px-4 pt-24 pb-6 flex flex-col transition-transform duration-300 md:hidden ${open ? 'translate-x-0' : '-translate-x-full'}`}>
66+
className={`fixed top-0 left-0 h-full w-[80vw] max-w-[320px] min-w-[280px] min-[450px]:min-w-[450px] bg-pointColor rounded-2xl shadow-xl z-[999] px-4 pt-24 pb-6 flex flex-col transition-transform duration-300 lg:hidden ${open ? 'translate-x-0' : '-translate-x-full'}`}>
6767
{/* 로고 */}
6868
<div className="w-full flex justify-center mb-6">
6969
<img src={smallLogo} alt="logo" className="h-10" />

src/components/ui/progressBar/CircleProgressBar.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,56 @@ interface CircleProgressBarProps {
66
color?: string;
77
size?: number;
88
thickness?: number;
9+
className?: string;
910
}
11+
1012
export default function CircleProgressBar({
1113
value,
1214
label = 'TOTAL',
1315
color = '#E48989',
14-
size = 150,
15-
thickness = 6
16+
size,
17+
thickness = 6,
18+
className = ''
1619
}: CircleProgressBarProps) {
20+
// size prop이 있으면 사용, 없으면 반응형 클래스 사용
21+
const sizeStyle = size ? { width: size, height: size } : {};
22+
const sizeClass = size ? '' : 'w-[100px] h-[100px] sm:w-[120px] sm:h-[120px] lg:w-[150px] lg:h-[150px]';
23+
1724
return (
18-
<div className="flex flex-col justify-center items-center gap-3">
19-
<Box position="relative" display="inline-flex">
25+
<div className={`flex flex-col justify-center items-center gap-2 lg:gap-3 ${className}`}>
26+
<Box position="relative" display="inline-flex" className={sizeClass} style={sizeStyle}>
2027
{/* 배경 회색원 */}
2128
<CircularProgress
2229
variant="determinate"
2330
value={100}
24-
size={size}
2531
thickness={thickness}
2632
sx={{
27-
color: '#DDDDDD'
33+
color: '#DDDDDD',
34+
width: '100% !important',
35+
height: '100% !important'
2836
}}
2937
/>
3038
{/* 진행률 원 */}
3139
<CircularProgress
3240
variant="determinate"
3341
value={value}
34-
size={size}
3542
thickness={thickness}
3643
sx={{
44+
color: color,
3745
position: 'absolute',
3846
left: 0,
39-
color: color
47+
top: 0,
48+
width: '100% !important',
49+
height: '100% !important'
4050
}}
4151
/>
4252
</Box>
53+
4354
{/* 테스트 이름, 진행률 text */}
44-
<Box
45-
top={0}
46-
left={0}
47-
bottom={0}
48-
right={0}
49-
display="flex"
50-
flexDirection="column"
51-
alignItems="center"
52-
justifyContent="center">
53-
<p className="font-bold text-14 text-typography-dark">{label}</p>
54-
<p className="font-bold text-14 text-typography-gray">{`${Math.round(value)}%`}</p>
55-
</Box>
55+
<div className="flex flex-col items-center justify-center">
56+
<p className="font-bold text-11 sm:text-12 lg:text-14 text-typography-dark">{label}</p>
57+
<p className="font-bold text-11 sm:text-12 lg:text-14 text-typography-gray">{`${Math.round(value)}%`}</p>
58+
</div>
5659
</div>
5760
);
5861
}

src/components/ui/progressBar/LinearProgressBar2.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ export default function LinearProgressBar2({
1515
<div className={`w-full ${className}`}>
1616
{/* 테스트 이름 넣기 */}
1717
<div className="flex justify-center mb-2">
18-
<span className="font-bold text-14 text-typography-dark">{label}</span>
18+
<span className="font-bold text-14 text-typography-dark max-sm:text-11">{label}</span>
1919
</div>
2020
{/* 진행률 나타내는 bar */}
21-
<div className="flex items-center gap-2 ">
22-
<div className="w-full bg-[#DDDDDD] rounded-20 h-8 overflow-hidden relative">
21+
<div className="flex items-center gap-2">
22+
<div className="w-full bg-[#DDDDDD] rounded-20 h-8 overflow-hidden relative max-sm:h-4">
2323
<div
2424
className="h-full rounded-full transition-all duration-300 ease-in-out"
2525
style={{ width: `${value}%`, backgroundColor: color }}

src/pages/auth/_components/AuthForm.tsx

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,37 +42,39 @@ export default function AuthForm({ type, onSubmit }: AuthFormProps) {
4242
};
4343

4444
return (
45-
<div className="flex flex-col items-center">
46-
<Link to={ROUTES.LANDING}>
47-
<img src={bigLogo} alt="AUTA big logo" className="py-20" />
48-
</Link>
49-
<form onSubmit={handleSubmitAuthForm} className="flex flex-col gap-8 max-w-[446px] w-full">
50-
<Input label="이메일" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
51-
<Input label="비밀번호" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
45+
<div className="min-h-screen flex items-center justify-center max-xl2:px-8">
46+
<div className="flex flex-col items-center">
47+
<Link to={ROUTES.LANDING}>
48+
<img src={bigLogo} alt="AUTA big logo" className="py-20" />
49+
</Link>
50+
<form onSubmit={handleSubmitAuthForm} className="flex flex-col gap-8 max-w-[446px] w-full">
51+
<Input label="이메일" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
52+
<Input label="비밀번호" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
5253

53-
{type === 'signup' && (
54-
<Input label="이름" type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
55-
)}
54+
{type === 'signup' && (
55+
<Input label="이름" type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
56+
)}
5657

57-
<Button
58-
text={type === 'login' ? '로그인' : '회원가입'}
59-
type="submit"
60-
className="w-[90px] block mx-auto font-medium text-11 my-7"
61-
/>
62-
</form>
58+
<Button
59+
text={type === 'login' ? '로그인' : '회원가입'}
60+
type="submit"
61+
className="w-[90px] mx-auto font-medium text-11 my-7"
62+
/>
63+
</form>
6364

64-
<div className="flex justify-center font-medium text-11 text-typography-dark">
65-
{type === 'login' ? (
66-
<div className="flex gap-3">
67-
<Link to={ROUTES.SIGNUP}>회원가입</Link>
68-
<span className="text-typography-gray">|</span>
69-
<Link to="#">PW 찾기</Link>
70-
</div>
71-
) : (
72-
<Link to={ROUTES.LOGIN} className="">
73-
로그인
74-
</Link>
75-
)}
65+
<div className="flex justify-center font-medium text-11 text-typography-dark">
66+
{type === 'login' ? (
67+
<div className="flex gap-3">
68+
<Link to={ROUTES.SIGNUP}>회원가입</Link>
69+
<span className="text-typography-gray">|</span>
70+
<Link to="#">PW 찾기</Link>
71+
</div>
72+
) : (
73+
<Link to={ROUTES.LOGIN} className="">
74+
로그인
75+
</Link>
76+
)}
77+
</div>
7678
</div>
7779
</div>
7880
);

src/pages/project/ProjectCreatePage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export default function ProjectCreatePage() {
99
username,
1010
handleProjectSubmit,
1111
handleCancelProject,
12-
isLoading,
12+
isRegisterLoading,
13+
isTestLoading,
1314
isCancelModalOpen,
1415
handleCloseCancelModal,
1516
handleConfirmCancelProject
@@ -29,7 +30,8 @@ export default function ProjectCreatePage() {
2930
username={username}
3031
onSubmit={handleProjectSubmit}
3132
onCancel={handleCancelProject}
32-
isLoading={isLoading}
33+
isRegisterLoading={isRegisterLoading}
34+
isTestLoading={isTestLoading}
3335
/>
3436
<CommonModal
3537
isOpen={isCancelModalOpen}

src/pages/project/ProjectMangePage.tsx

Lines changed: 71 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { ProjectListData } from '@/types/project.type';
88
import { useSelector } from 'react-redux';
99
import { RootState } from '@/store/redux/store';
1010
import { BeatLoader } from 'react-spinners';
11+
import TableListCard from '@/pages/project/_components/responsive_tableListCard/TableListCard';
12+
import ScrollToTopButton from '@/components/ui/scrollTopButton/ScrollToTopButton';
1113

1214
const columns = [
1315
{ id: 'projectName', label: '프로젝트 명' },
@@ -45,60 +47,75 @@ export default function ProjectMangePage() {
4547
if (isError) return <div>오류가 발생했습니다.</div>;
4648

4749
return (
48-
<div className="w-[90%] flex flex-col m-auto">
49-
<ProjectTitle />
50-
{projects.length === 0 ? (
51-
<div className="w-full text-center py-20 text-typography-gray text-16 font-medium">
52-
<p>검색 결과가 없습니다.</p>
53-
</div>
54-
) : (
55-
<TableItem
56-
columns={columns}
57-
items={projects}
58-
onItemClick={handleItemClick}
59-
className="w-full"
60-
renderCell={(column, item) => {
61-
if (column.id === 'projectStatus') {
62-
return <StatusBadge status={item.projectStatus} />;
63-
}
64-
if (column.id === 'projectMember') {
65-
return (
66-
<div className="flex -space-x-1 overflow-hidden">
67-
<img
68-
className="inline-block size-6 rounded-full ring-2 ring-white"
69-
src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
70-
alt=""
71-
/>
72-
<img
73-
className="inline-block size-6 rounded-full ring-2 ring-white"
74-
src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
75-
alt=""
76-
/>
77-
<img
78-
className="inline-block size-6 rounded-full ring-2 ring-white"
79-
src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80"
80-
alt=""
81-
/>
82-
<img
83-
className="inline-block size-6 rounded-full ring-2 ring-white"
84-
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
85-
alt=""
86-
/>
87-
</div>
88-
);
89-
}
90-
if (column.id === 'testRate') {
91-
if (item.testRate === null) {
92-
return <span>-</span>;
93-
} else {
94-
return <span>{item.testRate}%</span>;
95-
}
96-
}
50+
<>
51+
<div className="w-[90%] flex flex-col m-auto">
52+
<ProjectTitle />
53+
{projects.length === 0 ? (
54+
<div className="w-full text-center py-20 text-typography-gray text-16 font-medium">
55+
<p>검색 결과가 없습니다.</p>
56+
</div>
57+
) : (
58+
<>
59+
<div className="hidden md:block">
60+
<TableItem
61+
columns={columns}
62+
items={projects}
63+
onItemClick={handleItemClick}
64+
className="w-full"
65+
renderCell={(column, item) => {
66+
if (column.id === 'projectStatus') {
67+
return <StatusBadge status={item.projectStatus} />;
68+
}
69+
if (column.id === 'projectMember') {
70+
return (
71+
<div className="flex -space-x-1 overflow-hidden">
72+
<img
73+
className="inline-block size-6 rounded-full ring-2 ring-white"
74+
src="https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
75+
alt=""
76+
/>
77+
<img
78+
className="inline-block size-6 rounded-full ring-2 ring-white"
79+
src="https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
80+
alt=""
81+
/>
82+
<img
83+
className="inline-block size-6 rounded-full ring-2 ring-white"
84+
src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80"
85+
alt=""
86+
/>
87+
<img
88+
className="inline-block size-6 rounded-full ring-2 ring-white"
89+
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
90+
alt=""
91+
/>
92+
</div>
93+
);
94+
}
95+
if (column.id === 'testRate') {
96+
if (item.testRate === null) {
97+
return <span>-</span>;
98+
} else {
99+
return <span>{item.testRate}%</span>;
100+
}
101+
}
97102

98-
return item[column.id as keyof typeof item];
99-
}}
100-
/>
101-
)}
102-
</div>
103+
return item[column.id as keyof typeof item];
104+
}}
105+
/>
106+
</div>
107+
108+
{/* md(768px) 이하 사이즈일 때 카드 형태로 변경 */}
109+
<div className="md:hidden grid grid-cols-2 max-sm:grid-cols-1 gap-4">
110+
{projects.map((project: ProjectListData) => (
111+
<TableListCard key={project.projectId} project={project} onClick={() => handleItemClick(project)} />
112+
))}
113+
</div>
114+
</>
115+
)}
116+
</div>
117+
118+
<ScrollToTopButton />
119+
</>
103120
);
104121
}

0 commit comments

Comments
 (0)