이슈 상세페이지 가독성 개선.
This commit is contained in:
14
TODO.md
14
TODO.md
@@ -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 오류 해결.
|
||||
|
||||
@@ -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()}
|
||||
|
||||
@@ -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>
|
||||
|
||||
45
viewer/src/components/IssueDetailCard.tsx
Normal file
45
viewer/src/components/IssueDetailCard.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user