Merge branch 'fe/refactor/improve-design' into 'fe/develop'
Design: browse 페이지, 리뷰 페이지, 모델 페이지 디자인 개선 See merge request s11-s-project/S11P21S002!274
This commit is contained in:
commit
4f9bcd5843
@ -22,15 +22,16 @@ export default function ManageLayout({ tabTitle }: { tabTitle: string }) {
|
||||
|
||||
<div className="flex min-h-screen flex-col justify-between">
|
||||
<div className="mt-16 flex flex-1">
|
||||
<div className="flex w-[280px] flex-col border-r border-gray-200 bg-gray-100 p-2">
|
||||
<div className="flex items-center justify-center gap-5 p-2">
|
||||
<div className="flex w-[280px] flex-col gap-1 border-r border-gray-200 bg-gray-100 p-2">
|
||||
<div className="flex items-center justify-center gap-5">
|
||||
<Link
|
||||
to={`/${tabTitle}/${workspaceId}`}
|
||||
className="subheading w-full overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
className="subheading w-full overflow-hidden text-ellipsis whitespace-nowrap p-2"
|
||||
>
|
||||
{workspaceTitle}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
{projects.map((project: ProjectResponse) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
@ -44,6 +45,7 @@ export default function ManageLayout({ tabTitle }: { tabTitle: string }) {
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex w-[calc(100%-280px)] flex-col gap-24">
|
||||
<main className="h-full grow overflow-y-auto">
|
||||
|
@ -38,7 +38,7 @@ export default function MemberAddModal({ projectId, buttonClass = '' }: MemberAd
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="blue"
|
||||
className={`${buttonClass}`}
|
||||
className={buttonClass}
|
||||
onClick={handleOpen}
|
||||
>
|
||||
<span>프로젝트에 새 멤버 초대</span>
|
||||
|
@ -111,8 +111,8 @@ function ModelEvaluation({ projectId, selectedModel }: ModelEvaluationProps) {
|
||||
data={[
|
||||
{ name: 'precision', value: resultData[0]?.precision, fill: 'var(--color-precision)' },
|
||||
{ name: 'recall', value: resultData[0]?.recall, fill: 'var(--color-recall)' },
|
||||
{ name: 'mAP50', value: resultData[0]?.map50, fill: 'var(--color-map50)' },
|
||||
{ name: 'mAP50_95', value: resultData[0]?.map5095, fill: 'var(--color-map50-95)' },
|
||||
{ name: 'map50', value: resultData[0]?.map50, fill: 'var(--color-map50)' },
|
||||
{ name: 'map5095', value: resultData[0]?.map5095, fill: 'var(--color-map5095)' },
|
||||
{ name: 'fitness', value: resultData[0]?.fitness, fill: 'var(--color-fitness)' },
|
||||
]}
|
||||
className="h-full"
|
||||
|
@ -24,11 +24,11 @@ const chartConfig = {
|
||||
label: 'Recall',
|
||||
color: '#1E90FF',
|
||||
},
|
||||
mAP50: {
|
||||
map50: {
|
||||
label: 'mAP50',
|
||||
color: '#32CD3',
|
||||
color: '#32CD30',
|
||||
},
|
||||
mAP50_95: {
|
||||
map5095: {
|
||||
label: 'mAP50-95',
|
||||
color: '#BA55D3',
|
||||
},
|
||||
@ -62,21 +62,26 @@ export default function ModelBarChart({ data, className }: ModelBarChartProps) {
|
||||
<YAxis />
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={<ChartTooltipContent hideLabel />}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
hideLabel
|
||||
className="bg-white"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="value"
|
||||
strokeWidth={2}
|
||||
strokeWidth={0}
|
||||
radius={8}
|
||||
activeIndex={2}
|
||||
activeBar={({ ...props }) => {
|
||||
return (
|
||||
<Rectangle
|
||||
{...props}
|
||||
fillOpacity={0.8}
|
||||
fillOpacity={1}
|
||||
stroke={props.payload.fill}
|
||||
strokeDasharray={4}
|
||||
strokeDashoffset={4}
|
||||
strokeDasharray={0}
|
||||
strokeDashoffset={0}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@ -10,8 +10,8 @@ export default function ModelManage() {
|
||||
return (
|
||||
<div className="grid h-screen w-full">
|
||||
<div className="flex flex-col">
|
||||
<header className="bg-background sticky top-0 z-10 flex h-[57px] items-center gap-1 border-b px-4">
|
||||
<h1 className="text-xl font-semibold">모델 관리</h1>
|
||||
<header className="bg-background flex h-16 items-center gap-1 border-b border-gray-200 px-4">
|
||||
<h1 className="heading">모델 관리</h1>
|
||||
</header>
|
||||
|
||||
<main className="grid flex-1 gap-4 overflow-auto p-4">
|
||||
@ -19,9 +19,20 @@ export default function ModelManage() {
|
||||
defaultValue="train"
|
||||
className="w-full"
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger value="train">모델 학습</TabsTrigger>
|
||||
<TabsTrigger value="results">모델 평가</TabsTrigger>
|
||||
{/* TabsList랑 TabsTrigger 디자인이 shadcn 문서에서 본 것과 많이 다르고 이상함.. 그래서 일단 className을 일일이 지정함.. */}
|
||||
<TabsList className="rounded-none border-transparent bg-transparent shadow-none">
|
||||
<TabsTrigger
|
||||
value="train"
|
||||
className="rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:border-blue-500 data-[state=active]:shadow-none"
|
||||
>
|
||||
모델 학습
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="results"
|
||||
className="rounded-none border-b-2 border-transparent bg-transparent shadow-none data-[state=active]:border-blue-500 data-[state=active]:shadow-none"
|
||||
>
|
||||
모델 평가
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="train">
|
||||
|
@ -12,7 +12,7 @@ export default function ProjectCard({ title, description, imageUrl = '', to = ''
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className="relative flex w-[327px] cursor-pointer items-start gap-4 rounded-lg border border-gray-200 bg-white p-4 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50"
|
||||
className="flex w-[327px] cursor-pointer items-start gap-4 rounded-lg border border-gray-200 bg-white p-4 transition-colors hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50"
|
||||
>
|
||||
<div className="flex h-24 w-24 shrink-0 items-center justify-center rounded-lg bg-gray-100">
|
||||
{imageUrl ? (
|
||||
@ -26,8 +26,8 @@ export default function ProjectCard({ title, description, imageUrl = '', to = ''
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-1 overflow-hidden">
|
||||
<div className="font-sans text-lg leading-tight text-black dark:text-white">{title}</div>
|
||||
<div className="text-sm leading-tight text-gray-500 dark:text-gray-400">{description}</div>
|
||||
<div className="body leading-tight text-black dark:text-white">{title}</div>
|
||||
<div className="caption leading-tight text-gray-500 dark:text-gray-400">{description}</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
|
@ -24,7 +24,7 @@ export default function ProjectCreateModal({ onSubmit, buttonClass = '' }: Proje
|
||||
<Button
|
||||
variant="blue"
|
||||
className={buttonClass}
|
||||
size={'xs'}
|
||||
size="xs"
|
||||
onClick={handleOpen}
|
||||
>
|
||||
<span>새 프로젝트</span>
|
||||
|
@ -45,7 +45,7 @@ export default function ReviewItem({
|
||||
to={`/review/${workspaceId}/${projectId}/${reviewId}`}
|
||||
className="block hover:bg-gray-100"
|
||||
>
|
||||
<div className="flex h-[100px] w-full items-center justify-between border-b-[0.67px] border-[#ececef] bg-[#fbfafd] p-4">
|
||||
<div className="flex h-[100px] w-full items-center justify-between border-b border-gray-200 bg-white p-4 hover:bg-gray-100">
|
||||
<div className="flex flex-col">
|
||||
<p className="body-small-strong text-black">{title}</p>
|
||||
<p className="caption mt-1 text-gray-500">
|
||||
@ -68,7 +68,7 @@ export default function ReviewItem({
|
||||
<div className="flex flex-col items-end gap-1">
|
||||
<div
|
||||
className={cn(
|
||||
'caption flex items-center gap-1 rounded-full px-3 py-0.5',
|
||||
'caption flex items-center gap-1 rounded-md px-2 py-0.5',
|
||||
status === 'APPROVED'
|
||||
? 'bg-green-100 text-green-600'
|
||||
: status === 'REJECTED'
|
||||
|
@ -28,12 +28,12 @@ export default function ReviewList({
|
||||
{['REQUESTED', 'APPROVED', 'REJECTED', 'ALL'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
className={`flex h-12 w-[100px] items-center justify-center px-3 ${
|
||||
className={`mt-[3px] flex h-12 w-[100px] items-center justify-center px-3 ${
|
||||
activeTab === tab ? 'border-b-[3px] border-blue-500' : 'border-b-[3px] border-transparent'
|
||||
}`}
|
||||
onClick={() => setActiveTab(tab as typeof activeTab)}
|
||||
>
|
||||
<span className={`text-sm ${activeTab === tab ? 'font-semibold' : 'font-normal'} text-black`}>
|
||||
<span className={`${activeTab === tab ? 'body-small-strong' : 'body-small'} text-black`}>
|
||||
{tab === 'REQUESTED' ? '요청' : tab === 'APPROVED' ? '승인' : tab === 'REJECTED' ? '거부' : '전체'}
|
||||
</span>
|
||||
</button>
|
||||
|
@ -38,20 +38,24 @@ export default function WorkspaceBrowseLayout() {
|
||||
|
||||
<div className="flex min-h-screen flex-col justify-between">
|
||||
<div className="mt-16 flex flex-1">
|
||||
<div className="flex w-[280px] flex-col border-r border-gray-200 bg-gray-100 p-2">
|
||||
<div className="flex items-center justify-center gap-5 p-2">
|
||||
<h1 className="subheading mr-2.5 w-full overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
<div className="flex w-[280px] flex-col gap-1 border-r border-gray-200 bg-gray-100 p-2">
|
||||
<div className="flex items-center justify-center gap-5">
|
||||
<h1 className="subheading mr-2.5 w-full overflow-hidden text-ellipsis whitespace-nowrap p-2">
|
||||
내 워크스페이스
|
||||
</h1>
|
||||
<WorkSpaceCreateModal onSubmit={handleCreateWorkspace} />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
{workspaces.length > 0 ? (
|
||||
workspaces.map((workspace: WorkspaceResponse) => (
|
||||
<NavLink
|
||||
key={workspace.id}
|
||||
to={`/browse/${workspace.id}`}
|
||||
className={({ isActive }) =>
|
||||
cn('cursor-pointer rounded-lg p-3 hover:bg-gray-200', isActive ? 'body-strong bg-gray-300' : 'body')
|
||||
cn(
|
||||
'cursor-pointer rounded-lg p-3 hover:bg-gray-200',
|
||||
isActive ? 'body-strong bg-gray-300' : 'body'
|
||||
)
|
||||
}
|
||||
>
|
||||
{workspace.title}
|
||||
@ -61,6 +65,7 @@ export default function WorkspaceBrowseLayout() {
|
||||
<p className="text-gray-500">워크스페이스가 없습니다.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-[calc(100%-280px)] flex-col gap-24">
|
||||
<main className="grow">
|
||||
<Suspense fallback={<div></div>}>
|
||||
|
@ -88,14 +88,14 @@ export default function ProjectMemberManage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid h-screen w-full">
|
||||
<div className="grid w-full">
|
||||
<div className="flex flex-col">
|
||||
<header className="bg-background sticky top-0 z-10 flex h-[57px] items-center gap-4 border-b px-4">
|
||||
<h1 className="flex-1 text-xl font-semibold">프로젝트 멤버 관리</h1>
|
||||
<header className="bg-background flex h-16 items-center gap-4 px-4">
|
||||
<h1 className="heading flex-1">프로젝트 멤버 관리</h1>
|
||||
{isAdminOrManager && <MemberAddModal projectId={projectId ? Number(projectId) : 0} />}
|
||||
</header>
|
||||
|
||||
<main className="grid flex-1 gap-4 overflow-auto p-4">
|
||||
<main className="grid flex-1 gap-4 overflow-auto px-4 pb-4">
|
||||
{sortedMembers.length === 0 ? (
|
||||
<div className="py-4 text-center">프로젝트에 멤버가 없습니다.</div>
|
||||
) : (
|
||||
|
@ -60,8 +60,8 @@ export default function ProjectReviewList() {
|
||||
return (
|
||||
<Suspense fallback={<div></div>}>
|
||||
<div>
|
||||
<header className="sticky top-0 z-10 flex h-[57px] items-center gap-1 border-b bg-white px-4">
|
||||
<h1 className="text-xl font-semibold">프로젝트 리뷰</h1>
|
||||
<header className="sticky top-0 z-10 flex h-16 items-center gap-1 border-b border-gray-200 bg-white px-4">
|
||||
<h1 className="heading">프로젝트 리뷰</h1>
|
||||
<Link
|
||||
to={`/review/${workspaceId}/request`}
|
||||
className="ml-auto"
|
||||
|
@ -68,7 +68,7 @@ export default function ReviewDetail(): JSX.Element {
|
||||
<div className="mb-1 flex gap-1">
|
||||
<div
|
||||
className={cn(
|
||||
'caption mr-1 flex items-center gap-1 rounded-full px-3 py-0.5',
|
||||
'caption mr-1 flex items-center gap-1 rounded-md px-2 py-0.5',
|
||||
reviewDetail.reviewStatus === 'APPROVED'
|
||||
? 'bg-green-100 text-green-600'
|
||||
: reviewDetail.reviewStatus === 'REJECTED'
|
||||
@ -88,25 +88,31 @@ export default function ReviewDetail(): JSX.Element {
|
||||
{reviewDetail.reviewStatus === 'APPROVED' || reviewDetail.reviewStatus === 'REJECTED' ? (
|
||||
<>
|
||||
<p className="body-small text-gray-500">by</p>
|
||||
<p className="body-small-strong text-gray-500">
|
||||
{reviewDetail.reviewer.nickname} ({reviewDetail.reviewer.email})
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<p className="body-small text-gray-500">updated</p>
|
||||
)}
|
||||
|
||||
<p className="body-small-strong text-gray-500">{reviewDetail.reviewer.nickname}</p>
|
||||
<p className="body-small text-gray-500">({reviewDetail.reviewer.email})</p>
|
||||
<p className="body-small-strong text-gray-500">{timeAgo(reviewDetail.updatedAt)}</p>
|
||||
<p className="body-small text-gray-500">({formatDateTime(reviewDetail.updatedAt)})</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="body-small text-gray-500">by</p>
|
||||
<p className="body-small-strong text-gray-500">{reviewDetail.author.nickname}</p>
|
||||
<p className="body-small text-gray-500">({reviewDetail.author.email})</p>
|
||||
<p className="body-small-strong text-gray-500">{timeAgo(reviewDetail.createdAt)}</p>
|
||||
<p className="body-small text-gray-500">({formatDateTime(reviewDetail.createdAt)})</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{reviewDetail.reviewStatus === 'APPROVED' || reviewDetail.reviewStatus === 'REJECTED' ? (
|
||||
<div className="flex gap-1">
|
||||
<p className="body-small-strong text-gray-500">
|
||||
{reviewDetail.author.nickname} ({reviewDetail.author.email})
|
||||
</p>
|
||||
<p className="body-small text-gray-500">requested a review</p>
|
||||
<p className="body-small-strong text-gray-500">{reviewDetail.author.nickname}</p>
|
||||
<p className="body-small text-gray-500">({reviewDetail.author.email}) requested a review</p>
|
||||
<p className="body-small-strong text-gray-500">{timeAgo(reviewDetail.createdAt)}</p>
|
||||
<p className="body-small text-gray-500">({formatDateTime(reviewDetail.createdAt)})</p>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative w-full">
|
||||
|
@ -29,7 +29,7 @@ export default function WorkspaceBrowseDetail() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col gap-8 px-6 py-4">
|
||||
<div className="flex h-full w-full flex-col">
|
||||
<HeaderSection
|
||||
workspaceName={workspaceData?.title ?? `Workspace-${workspaceId}`}
|
||||
onCreateProject={handleCreateProject}
|
||||
@ -54,17 +54,15 @@ function HeaderSection({
|
||||
onCreateProject: (data: ProjectRequest) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<h1 className="small-title flex grow">{workspaceName}</h1>
|
||||
<div className="flex h-16 items-center justify-center px-4">
|
||||
<h1 className="heading flex grow">{workspaceName}</h1>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex gap-3">
|
||||
<ProjectCreateModal
|
||||
buttonClass="flex items-center gap-2 body-small-strong h-10 px-4 py-2"
|
||||
buttonClass="flex items-center gap-2 h-10 px-4 py-2"
|
||||
onSubmit={onCreateProject}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,7 +94,7 @@ function ProjectList({ projects, workspaceId }: { projects: ProjectResponse[]; w
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-6">
|
||||
<div className="flex flex-wrap gap-6 px-4 pb-4">
|
||||
{projects.map((project: ProjectResponse) => (
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
|
@ -11,10 +11,10 @@ export default function WorkspaceMemberManage() {
|
||||
const { data: members = [] } = useWorkspaceMembersQuery(Number(workspaceId));
|
||||
|
||||
return (
|
||||
<div className="grid h-screen w-full">
|
||||
<div className="grid w-full">
|
||||
<div className="flex flex-col">
|
||||
<header className="bg-background sticky top-0 z-10 flex h-[57px] items-center gap-4 border-b px-4">
|
||||
<h1 className="flex-1 text-xl font-semibold">워크스페이스 멤버 관리</h1>
|
||||
<header className="bg-background flex h-16 items-center gap-4 px-4">
|
||||
<h1 className="heading flex-1">워크스페이스 멤버 관리</h1>
|
||||
|
||||
<WorkspaceMemberAddModal
|
||||
workspaceId={Number(workspaceId)}
|
||||
@ -22,7 +22,7 @@ export default function WorkspaceMemberManage() {
|
||||
/>
|
||||
</header>
|
||||
|
||||
<main className="flex-1 overflow-auto p-4">
|
||||
<main className="flex-1 overflow-auto px-4 pb-4">
|
||||
{members.length === 0 ? (
|
||||
<div className="py-4 text-center">워크스페이스에 멤버가 없습니다.</div>
|
||||
) : (
|
||||
|
@ -60,8 +60,8 @@ export default function WorkspaceReviewList() {
|
||||
return (
|
||||
<Suspense fallback={<div></div>}>
|
||||
<div>
|
||||
<header className="sticky top-0 z-10 flex h-[57px] items-center gap-1 border-b bg-white px-4">
|
||||
<h1 className="text-xl font-semibold">워크스페이스 리뷰</h1>
|
||||
<header className="sticky top-0 z-10 flex h-16 items-center gap-1 border-b border-gray-200 bg-white px-4">
|
||||
<h1 className="heading">워크스페이스 리뷰</h1>
|
||||
<Link
|
||||
to={`/review/${workspaceId}/request`}
|
||||
className="ml-auto"
|
||||
|
Loading…
Reference in New Issue
Block a user