js 의존성 제거
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// src/services/feedback.ts
|
||||
import { handleApiError } from "./error";
|
||||
import { handleApiError } from "../../../src/services/error";
|
||||
// --- API 함수 ---
|
||||
const getFeedbacksSearchApiUrl = (projectId, channelId) =>
|
||||
`/api/v2/projects/${projectId}/channels/${channelId}/feedbacks/search`;
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/services/issue.ts
|
||||
import { handleApiError } from "./error";
|
||||
import { handleApiError } from "../../../src/services/error";
|
||||
/**
|
||||
* 특정 프로젝트의 모든 이슈를 검색합니다.
|
||||
* @param projectId 프로젝트 ID
|
||||
@@ -1,3 +1,4 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { ProjectSelectBox } from "./ProjectSelectBox";
|
||||
import { ThemeSelectBox } from "./ThemeSelectBox";
|
||||
@@ -10,7 +11,11 @@ const menuItems = [
|
||||
{ name: "Issue", path: "/issues", type: "issue" },
|
||||
];
|
||||
|
||||
export function Header() {
|
||||
interface HeaderProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Header({ className }: HeaderProps) {
|
||||
const { projectId, channelId } = useSettingsStore();
|
||||
|
||||
const getPath = (type: string, basePath: string) => {
|
||||
@@ -23,7 +28,12 @@ export function Header() {
|
||||
const homePath = `/projects/${projectId}`;
|
||||
|
||||
return (
|
||||
<header className="flex h-16 items-center justify-between border-b px-6">
|
||||
<header
|
||||
className={cn(
|
||||
"flex h-16 items-center justify-between border-b px-6",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-6">
|
||||
<NavLink
|
||||
to={homePath}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Header } from "./Header";
|
||||
export function MainLayout() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Header />
|
||||
<main className="flex-1 p-6">
|
||||
<Header className="sticky top-0 z-50 bg-background" />
|
||||
<main className="flex-1 container mx-auto p-6">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,9 @@ import { useSettingsStore } from "@/store/useSettingsStore";
|
||||
import { useSyncChannelId } from "@/hooks/useSyncChannelId";
|
||||
import { DynamicForm } from "@/components/DynamicForm";
|
||||
import {
|
||||
getFeedbackSchema,
|
||||
getFeedbackFields,
|
||||
createFeedback,
|
||||
type FeedbackSchema,
|
||||
type FeedbackField,
|
||||
type CreateFeedbackRequest,
|
||||
} from "@/services/feedback";
|
||||
import { ErrorDisplay } from "@/components/ErrorDisplay";
|
||||
@@ -17,7 +17,7 @@ export function FeedbackCreatePage() {
|
||||
const navigate = useNavigate();
|
||||
const { projectId, channelId } = useSettingsStore();
|
||||
|
||||
const [schema, setSchema] = useState<FeedbackSchema | null>(null);
|
||||
const [schema, setSchema] = useState<FeedbackField[] | null>(null);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [submitMessage, setSubmitMessage] = useState<string | null>(null);
|
||||
@@ -28,8 +28,13 @@ export function FeedbackCreatePage() {
|
||||
const fetchSchema = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const schemaData = await getFeedbackSchema(projectId, channelId);
|
||||
setSchema(schemaData);
|
||||
const schemaData = await getFeedbackFields(projectId, channelId);
|
||||
// ID, Created, Updated, Issue 필드 제외
|
||||
const filteredSchema = schemaData.filter(
|
||||
(field) =>
|
||||
!["id", "createdAt", "updatedAt", "issues"].includes(field.id),
|
||||
);
|
||||
setSchema(filteredSchema);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "폼을 불러오는 데 실패했습니다.",
|
||||
@@ -91,7 +96,7 @@ export function FeedbackCreatePage() {
|
||||
<Separator />
|
||||
{schema && (
|
||||
<DynamicForm
|
||||
schema={schema}
|
||||
fields={schema}
|
||||
onSubmit={handleSubmit}
|
||||
submitButtonText="제출하기"
|
||||
/>
|
||||
|
||||
@@ -7,6 +7,12 @@ import {
|
||||
updateFeedback,
|
||||
} from "@/services/feedback";
|
||||
import { ErrorDisplay } from "@/components/ErrorDisplay";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
export function FeedbackDetailPage() {
|
||||
@@ -128,31 +134,35 @@ export function FeedbackDetailPage() {
|
||||
</p>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className="mt-6">
|
||||
<div className="flex justify-between items-center mb-4 p-3 bg-slate-50 rounded-md">
|
||||
<span className="text-sm font-medium text-slate-600">
|
||||
ID: {feedback?.id}
|
||||
</span>
|
||||
<span className="text-sm text-slate-500">
|
||||
생성일:{" "}
|
||||
{feedback?.createdAt
|
||||
? new Date(feedback.createdAt).toLocaleString("ko-KR")
|
||||
: "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<DynamicForm
|
||||
fields={fields}
|
||||
initialData={initialData}
|
||||
onSubmit={handleSubmit}
|
||||
submitButtonText="수정하기"
|
||||
/>
|
||||
{successMessage && (
|
||||
<div className="mt-4 p-3 bg-green-100 text-green-800 rounded-md">
|
||||
{successMessage}
|
||||
<Card className="mt-6">
|
||||
<CardHeader>
|
||||
<div className="flex justify-between items-center">
|
||||
<CardTitle>피드백 정보</CardTitle>
|
||||
<div className="flex items-center gap-4 text-sm text-slate-500">
|
||||
<span>ID: {feedback?.id}</span>
|
||||
<span>
|
||||
생성일:{" "}
|
||||
{feedback?.createdAt
|
||||
? new Date(feedback.createdAt).toLocaleString("ko-KR")
|
||||
: "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DynamicForm
|
||||
fields={fields}
|
||||
initialData={initialData}
|
||||
onSubmit={handleSubmit}
|
||||
submitButtonText="수정하기"
|
||||
/>
|
||||
{successMessage && (
|
||||
<div className="mt-4 p-3 bg-green-100 text-green-800 rounded-md">
|
||||
{successMessage}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import { useSyncChannelId } from "@/hooks/useSyncChannelId";
|
||||
import { DynamicTable } from "@/components/DynamicTable";
|
||||
import {
|
||||
getFeedbacks,
|
||||
getFeedbackSchema,
|
||||
getFeedbackFields,
|
||||
type Feedback,
|
||||
type FeedbackSchema,
|
||||
type FeedbackField,
|
||||
} from "@/services/feedback";
|
||||
import { ErrorDisplay } from "@/components/ErrorDisplay";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -17,7 +17,7 @@ export function FeedbackListPage() {
|
||||
const { projectId, channelId } = useSettingsStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [schema, setSchema] = useState<FeedbackSchema | null>(null);
|
||||
const [schema, setSchema] = useState<FeedbackField[] | null>(null);
|
||||
const [feedbacks, setFeedbacks] = useState<Feedback[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -30,7 +30,7 @@ export function FeedbackListPage() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
const schemaData = await getFeedbackSchema(projectId, channelId);
|
||||
const schemaData = await getFeedbackFields(projectId, channelId);
|
||||
setSchema(schemaData);
|
||||
|
||||
const feedbacksData = await getFeedbacks(projectId, channelId);
|
||||
@@ -67,11 +67,13 @@ export function FeedbackListPage() {
|
||||
</div>
|
||||
{error && <ErrorDisplay message={error} />}
|
||||
{schema && (
|
||||
<DynamicTable
|
||||
schema={schema}
|
||||
data={feedbacks}
|
||||
onRowClick={handleRowClick}
|
||||
/>
|
||||
<div className="mt-6">
|
||||
<DynamicTable
|
||||
columns={schema}
|
||||
data={feedbacks}
|
||||
onRowClick={handleRowClick}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -188,5 +188,11 @@ export const updateFeedback = async (
|
||||
if (!response.ok) {
|
||||
await handleApiError("피드백 수정에 실패했습니다.", response);
|
||||
}
|
||||
|
||||
const contentLength = response.headers.get("Content-Length");
|
||||
if (contentLength === "0" || !contentLength) {
|
||||
return {} as Feedback; // 혹은 적절한 기본 객체
|
||||
}
|
||||
|
||||
return response.json();
|
||||
};
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
export const useSettingsStore = create()(
|
||||
|
||||
interface SettingsState {
|
||||
projectId: string;
|
||||
channelId: string;
|
||||
theme: "light" | "dark" | "system";
|
||||
setProjectId: (projectId: string) => void;
|
||||
setChannelId: (channelId: string) => void;
|
||||
setTheme: (theme: "light" | "dark" | "system") => void;
|
||||
}
|
||||
|
||||
export const useSettingsStore = create<SettingsState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
projectId: "1", // 기본 프로젝트 ID를 1로 설정
|
||||
projectId: "1", // 기본 프로젝트 ID
|
||||
channelId: "4", // 기본 채널 ID
|
||||
theme: "system",
|
||||
setProjectId: (projectId) => set({ projectId }),
|
||||
setChannelId: (channelId) => set({ channelId }),
|
||||
setTheme: (theme) => set({ theme }),
|
||||
}),
|
||||
{
|
||||
@@ -13,3 +25,4 @@ export const useSettingsStore = create()(
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowJs": false,
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
@@ -25,5 +26,5 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": ["src", "effort_js/src/services/error.js", "effort_js/src/services/feedback.js", "effort_js/src/services/issue.js", "effort_js/src/services/project.js"]
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export default defineConfig(({ mode }) => {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
extensions: [".ts", ".tsx", ".mjs", ".mts", ".json"],
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
|
||||
Reference in New Issue
Block a user