js 의존성 제거

This commit is contained in:
Lectom C Han
2025-08-03 00:40:25 +09:00
parent 8db8ce668c
commit a53340e3c1
40 changed files with 96 additions and 48 deletions

View File

@@ -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`;

View File

@@ -1,5 +1,5 @@
// src/services/issue.ts
import { handleApiError } from "./error";
import { handleApiError } from "../../../src/services/error";
/**
* 특정 프로젝트의 모든 이슈를 검색합니다.
* @param projectId 프로젝트 ID

View File

@@ -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}

View File

@@ -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>

View File

@@ -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="제출하기"
/>

View File

@@ -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>
);
}

View File

@@ -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>
);

View File

@@ -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();
};

View File

@@ -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()(
},
),
);

View File

@@ -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"]
}

View File

@@ -18,6 +18,7 @@ export default defineConfig(({ mode }) => {
alias: {
"@": path.resolve(__dirname, "./src"),
},
extensions: [".ts", ".tsx", ".mjs", ".mts", ".json"],
},
server: {
proxy: {