이슈 상세페이지 가독성 개선.

This commit is contained in:
2025-08-04 20:01:51 +09:00
parent 84cc83d36b
commit 0e85144cdf
5 changed files with 69 additions and 41 deletions

14
TODO.md
View File

@@ -1 +1,13 @@
2025-08-05 10:30:00 KST - 이슈 API 연동 및 UI 개선 완료
2025-08-05 10:30:00 KST - 이슈 API 연동 및 UI 개선 완료
---
### 2025-08-04 20:00:27 KST
- **레이아웃 안정성 및 반응형 개선 (완료)**
- **페이지 레이아웃 고정**: 테이블 행 확장 시 페이지 전체가 밀리는 현상 수정. `PageTitle`을 상단에 고정하고 컨텐츠 영역만 스크롤되도록 `MainLayout``PageLayout` 구조 개선.
- **일관된 페이지 너비 적용**: 목록 페이지와 상세 페이지의 너비가 다른 문제 해결. 모든 페이지가 최대 1400px 너비를 갖도록 `container` 사용법 통일.
- **반응형 헤더 구현**: 모바일 화면 크기에서 햄버거 메뉴가 나타나도록 `Header` 컴포넌트 개선.
- **컴포넌트 리팩터링**: `IssueDetailPage`의 UI를 재사용 가능한 `IssueDetailCard` 컴포넌트로 분리.
- **UI/UX 개선**:
- `DynamicTable`의 검색창과 카드 간 여백 조정.
- `IssueDetailCard`의 제목, 레이블, 컨텐츠 스타일을 개선하여 가독성 향상.
- **접근성(a11y) 수정**: `DynamicTable`의 컬럼 리사이저에 `slider` 역할을 부여하여 웹 접근성 lint 오류 해결.

View File

@@ -273,9 +273,9 @@ export function DynamicTable<TData extends BaseData>({
});
return (
<Card>
<Card className="mt-6">
<CardContent>
<div className="flex items-center justify-between py-4">
<div className="flex items-center justify-between pb-4">
<Input
placeholder="전체 데이터에서 검색..."
value={globalFilter}
@@ -373,7 +373,12 @@ export function DynamicTable<TData extends BaseData>({
header.getContext(),
)}
<div
role="presentation"
role="slider"
aria-label={`컬럼 너비 조절: ${header.id}`}
aria-valuemin={header.column.minSize}
aria-valuemax={header.column.maxSize}
aria-valuenow={header.column.getSize()}
tabIndex={0}
onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()}
onDoubleClick={() => header.column.resetSize()}

View File

@@ -50,7 +50,7 @@ export function FeedbackFormCard({
const readOnlyFields = fields.map((field) => ({ ...field, readOnly: true }));
return (
<Card className="w-full">
<Card className="w-full mt-6">
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>

View File

@@ -0,0 +1,45 @@
// src/components/IssueDetailCard.tsx
import type { Issue } from "@/services/issue";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
interface IssueDetailCardProps {
issue: Issue;
}
export function IssueDetailCard({ issue }: IssueDetailCardProps) {
return (
<Card className="mt-6">
<CardHeader>
<CardTitle className="text-2xl font-bold">{issue.name}</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div>
<h3 className="text-sm text-muted-foreground"></h3>
<p className="font-medium whitespace-pre-wrap">{issue.description}</p>
</div>
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div>
<h3 className="text-sm text-muted-foreground"></h3>
<p className="font-medium">{issue.status}</p>
</div>
<div>
<h3 className="text-sm text-muted-foreground"></h3>
<p className="font-medium">{issue.priority}</p>
</div>
<div>
<h3 className="text-sm text-muted-foreground"></h3>
<p className="font-medium">
{new Date(issue.createdAt).toLocaleString("ko-KR")}
</p>
</div>
<div>
<h3 className="text-sm text-muted-foreground"></h3>
<p className="font-medium">
{new Date(issue.updatedAt).toLocaleString("ko-KR")}
</p>
</div>
</div>
</CardContent>
</Card>
);
}

View File

@@ -3,9 +3,9 @@ import { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { getIssueById, type Issue } from "@/services/issue";
import { PageLayout } from "@/components/PageLayout";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { ErrorDisplay } from "@/components/ErrorDisplay";
import { IssueDetailCard } from "@/components/IssueDetailCard";
export function IssueDetailPage() {
const { projectId, issueId } = useParams<{
@@ -59,41 +59,7 @@ export function IssueDetailPage() {
</Button>
}
>
<Card>
<CardHeader>
<CardTitle>{issue.name}</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground whitespace-pre-wrap">
{issue.description}
</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">{issue.status}</p>
</div>
<div>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">{issue.priority}</p>
</div>
<div>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">
{new Date(issue.createdAt).toLocaleString("ko-KR")}
</p>
</div>
<div>
<h3 className="font-semibold"></h3>
<p className="text-muted-foreground">
{new Date(issue.updatedAt).toLocaleString("ko-KR")}
</p>
</div>
</div>
</CardContent>
</Card>
<IssueDetailCard issue={issue} />
</PageLayout>
);
}