Issue도 Dynamic Table 사용하도록 공유

This commit is contained in:
Lectom C Han
2025-08-03 01:23:20 +09:00
parent a53340e3c1
commit 26fbb3f4c0
42 changed files with 261 additions and 2629 deletions

View File

@@ -2,17 +2,9 @@ import { useState, useEffect, useMemo } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { DynamicForm } from "@/components/DynamicForm";
import { useSyncChannelId } from "@/hooks/useSyncChannelId";
import {
getFeedbackById,
updateFeedback,
} from "@/services/feedback";
import { getFeedbackById, updateFeedback } from "@/services/feedback";
import { ErrorDisplay } from "@/components/ErrorDisplay";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
export function FeedbackDetailPage() {

View File

@@ -11,6 +11,7 @@ import {
} from "@/services/feedback";
import { ErrorDisplay } from "@/components/ErrorDisplay";
import { Button } from "@/components/ui/button";
import type { Row } from "@tanstack/react-table";
export function FeedbackListPage() {
useSyncChannelId(); // URL의 channelId를 전역 상태와 동기화
@@ -53,6 +54,13 @@ export function FeedbackListPage() {
);
};
const renderExpandedRow = (row: Row<Feedback>) => (
<div className="p-4 bg-muted rounded-md">
<h4 className="font-bold text-lg">{row.original.title}</h4>
<p className="mt-2 whitespace-pre-wrap">{row.original.contents}</p>
</div>
);
if (loading) {
return <div className="text-center py-10"> ...</div>;
}
@@ -72,6 +80,7 @@ export function FeedbackListPage() {
columns={schema}
data={feedbacks}
onRowClick={handleRowClick}
renderExpandedRow={renderExpandedRow}
/>
</div>
)}

View File

@@ -0,0 +1,83 @@
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useSettingsStore } from "@/store/useSettingsStore";
import { DynamicTable } from "@/components/DynamicTable";
import {
getIssues,
getIssueFields,
type Issue,
type IssueField,
} from "@/services/issue";
import { ErrorDisplay } from "@/components/ErrorDisplay";
import type { Row } from "@tanstack/react-table";
export function IssueListPage() {
const { projectId } = useSettingsStore();
const _navigate = useNavigate();
const [schema, setSchema] = useState<IssueField[] | null>(null);
const [issues, setIssues] = useState<Issue[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!projectId) return;
const fetchSchemaAndIssues = async () => {
try {
setLoading(true);
setError(null);
const schemaData = await getIssueFields();
setSchema(schemaData);
const issuesData = await getIssues(projectId);
setIssues(issuesData);
} catch (err) {
setError(
err instanceof Error ? err.message : "데이터 로딩에 실패했습니다.",
);
} finally {
setLoading(false);
}
};
fetchSchemaAndIssues();
}, [projectId]);
const handleRowClick = (row: Issue) => {
// 상세 페이지 구현 시 주석 해제
// navigate(`/projects/${projectId}/issues/${row.id}`);
console.log("Clicked issue:", row);
};
const renderExpandedRow = (row: Row<Issue>) => (
<div className="p-4 bg-muted rounded-md">
<h4 className="font-bold text-lg">{row.original.name}</h4>
<p className="mt-2 whitespace-pre-wrap">{row.original.description}</p>
</div>
);
if (loading) {
return <div className="text-center py-10"> ...</div>;
}
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold"> </h1>
</div>
{error && <ErrorDisplay message={error} />}
{schema && (
<div className="mt-6">
<DynamicTable
columns={schema}
data={issues}
onRowClick={handleRowClick}
renderExpandedRow={renderExpandedRow}
/>
</div>
)}
</div>
);
}

View File

@@ -1 +0,0 @@
export declare function IssueViewerPage(): import("react/jsx-runtime").JSX.Element;

View File

@@ -1,93 +0,0 @@
// src/pages/IssueViewerPage.tsx
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { getIssues, type Issue } from "@/services/issue";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ErrorDisplay } from "@/components/ErrorDisplay";
// 테이블 헤더 정의
const issueTableHeaders = [
{ key: "title", label: "Title" },
{ key: "feedbackCount", label: "Feedback Count" },
{ key: "description", label: "Description" },
{ key: "status", label: "Status" },
{ key: "createdAt", label: "Created" },
{ key: "updatedAt", label: "Updated" },
{ key: "category", label: "Category" },
];
export function IssueViewerPage() {
const { projectId } = useParams<{ projectId: string }>();
const [issues, setIssues] = useState<Issue[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!projectId) return;
setLoading(true);
setError(null);
getIssues(projectId)
.then(setIssues)
.catch((err) => setError((err as Error).message))
.finally(() => setLoading(false));
}, [projectId]);
if (error) {
return <ErrorDisplay message={error} />;
}
return (
<Card>
<CardHeader>
<CardTitle> </CardTitle>
</CardHeader>
<CardContent>
{loading && <p className="text-center"> ...</p>}
{!loading && (
<div className="border rounded-md">
<Table>
<TableHeader>
<TableRow>
{issueTableHeaders.map((header) => (
<TableHead key={header.key}>{header.label}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{issues.length > 0 ? (
issues.map((issue) => (
<TableRow key={issue.id}>
{issueTableHeaders.map((header) => (
<TableCell key={`${issue.id}-${header.key}`}>
{String(issue[header.key] ?? "")}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={issueTableHeaders.length}
className="h-24 text-center"
>
.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)}
</CardContent>
</Card>
);
}