Skip to content

Commit ffe667d

Browse files
authored
[FIX] 에러상황에서의 폴백 UI 개선 (#515)
1 parent 9e426ab commit ffe667d

File tree

24 files changed

+250
-115
lines changed

24 files changed

+250
-115
lines changed
File renamed without changes.

frontend/src/api/lib/error.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { API_ERROR_MESSAGES } from "@/api/constants";
22
import { getApiErrorMessageByBackendErrorCode } from "@/api/lib/errorCode";
3-
import { STATUS_ERROR_MESSAGE_MAP, RESPONSE_PATH_ERROR_MESSAGE_MAP } from "@/api/lib/constant";
3+
import {
4+
STATUS_ERROR_MESSAGE_MAP,
5+
RESPONSE_PATH_ERROR_MESSAGE_MAP,
6+
} from "@/api/constants/errorCodeMap";
47
import type { ApiHttpError, ErrorResponse } from "@/api/lib/type";
58

69
export const toApiHttpError = async (

frontend/src/api/lib/errorCode.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { API_ERROR_MESSAGES } from "@/api/constants";
2-
import { BACKEND_ERROR_CODE_MAP, BACKEND_ERROR_CODE_TO_API_ERROR_KEY } from "@/api/lib/constant";
2+
import {
3+
BACKEND_ERROR_CODE_MAP,
4+
BACKEND_ERROR_CODE_TO_API_ERROR_KEY,
5+
} from "@/api/constants/errorCodeMap";
36
import type { BackendErrorCode } from "@/api/lib/type";
47

58
// backend/src/main/java/com/moong/exception/errorcode/ErrorCode.java 기준

frontend/src/api/lib/type.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ToastOptions } from "@/components/ui/Toast/toast.type";
2-
import type { BACKEND_ERROR_CODE_MAP } from "@/api/lib/constant";
2+
import type { BACKEND_ERROR_CODE_MAP } from "@/api/constants/errorCodeMap";
33

44
export type ErrorResponse = {
55
code?: unknown;

frontend/src/app/(sidebar)/analysis/_components/chart/AnalysisCharts.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { formatAmount } from "@/utils/amount";
33
import CategoryAnalysisChart from "./CategoryAnalysisChart";
44
import MedicalAnalysisChart from "./MedicalAnalysisChart";
55
import { formatRatio } from "@/app/(sidebar)/analysis/_utils";
6-
import ServerComponentErrorFallback from "@/components/ui/ErrorBoundary/ServerComponentErrorFallback";
7-
6+
import AnalysisChartEmpty from "./AnalysisChartEmpty";
87
import type { components } from "@/types/schema";
98
import type {
109
CategoryAnalysisChartCardProps,
@@ -16,7 +15,7 @@ export async function CategoryAnalysisChartCard({
1615
}: CategoryAnalysisChartCardProps) {
1716
const categoryData = await categoryPromise;
1817
if (categoryData instanceof Error) {
19-
return <ServerComponentErrorFallback message={categoryData.message} />;
18+
return <AnalysisChartEmpty message={categoryData.message} />;
2019
}
2120

2221
const { items: categoryItems } = categoryData;
@@ -51,12 +50,12 @@ export async function MedicalAnalysisChartCard({
5150
}: MedicalAnalysisChartCardProps) {
5251
const petInfo = await petInfoPromise;
5352
if (petInfo instanceof Error) {
54-
return <ServerComponentErrorFallback message={petInfo.message} />;
53+
return <AnalysisChartEmpty message={petInfo.message} />;
5554
}
5655

5756
const medicalData = await medicalPromise;
5857
if (medicalData instanceof Error) {
59-
return <ServerComponentErrorFallback message={medicalData.message} />;
58+
return <AnalysisChartEmpty message={medicalData.message} />;
6059
}
6160

6261
const petName = petInfo?.petName ?? "반려동물";

frontend/src/app/(sidebar)/analysis/_components/chart/test/AnalysisCharts.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ vi.mock("@/app/(sidebar)/analysis/_components/chart/CategoryAnalysisChart", () =
1818
default: () => null,
1919
}));
2020

21+
vi.mock("@/assets/icons/analysis/ic_warning.svg", () => ({ default: () => null }));
22+
2123
describe("MedicalAnalysisChartCard", () => {
2224
it("반려동물 정보가 없으면 기본 이름으로 제목을 노출한다", async () => {
2325
const ui = await MedicalAnalysisChartCard({

frontend/src/app/(sidebar)/analysis/_components/table/AnalysisTable.tsx

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,55 @@ import { formatAmount } from "@/utils/amount";
33
import { buildExpenseColumns } from "@/app/(sidebar)/analysis/_lib/ExpenseColumns";
44
import type { AnalysisTableSectionProps } from "@/app/(sidebar)/analysis/_types/componentPropsType.type";
55
import AnalysisTableEmpty from "./AnalysisTableEmpty";
6-
import ServerComponentErrorFallback from "@/components/ui/ErrorBoundary/ServerComponentErrorFallback";
6+
import AnalysisTableWrapper from "./AnalysisTableWrapper";
77

88
export default async function AnalysisTable({
99
petInfoPromise,
1010
expensesPromise,
1111
}: AnalysisTableSectionProps) {
1212
const petInfo = await petInfoPromise;
13-
if (petInfo instanceof Error) {
14-
return <ServerComponentErrorFallback message={petInfo.message} />;
15-
}
16-
1713
const rows = await expensesPromise;
18-
if (rows instanceof Error) {
19-
return <ServerComponentErrorFallback message={rows.message} />;
14+
if (petInfo instanceof Error || rows instanceof Error) {
15+
return (
16+
<AnalysisTableWrapper>
17+
<AnalysisTableEmpty
18+
message={
19+
petInfo instanceof Error
20+
? petInfo.message
21+
: rows instanceof Error
22+
? rows.message
23+
: undefined
24+
}
25+
/>
26+
</AnalysisTableWrapper>
27+
);
2028
}
2129

2230
const petName = petInfo?.petName ?? "반려동물";
2331
const totalCost = rows.reduce((sum, row) => sum + (row.cost ?? 0), 0);
2432
const totalLabel = `총 ${formatAmount(totalCost)}`;
2533

2634
return (
27-
<section className="flex flex-col gap-400">
28-
<div className="flex flex-col gap-200">
29-
<p className="typo-body-l-bold text-gray-500">우리 가족이 {petName}에게 쓴 비용</p>
30-
<p className="typo-headline-l-bold text-text-base">{totalLabel}</p>
31-
</div>
32-
<div className="relative h-[336px]">
33-
<DataTable
34-
className="h-full border-gray-50 rounded-600"
35-
columns={buildExpenseColumns()}
36-
data={rows}
37-
rowKey={(row, index) => row.expenseId ?? `empty-${index}`}
38-
/>
39-
{!rows.length ? (
40-
// 테이블 헤드 유지하면서 비어 있는 상태를 테이블 영역 위에 오버레이로 표시
41-
<div className="absolute inset-x-0 bottom-0 top-[48px] flex items-center justify-center border-t border-gray-50 bg-white-100">
42-
<AnalysisTableEmpty />
43-
</div>
44-
) : null}
45-
</div>
46-
</section>
35+
<AnalysisTableWrapper
36+
header={
37+
<>
38+
<p className="typo-body-l-bold text-gray-500">우리 가족이 {petName}에게 쓴 비용</p>
39+
<p className="typo-headline-l-bold text-text-base">{totalLabel}</p>
40+
</>
41+
}
42+
>
43+
<DataTable
44+
className="h-full border-gray-50 rounded-600"
45+
columns={buildExpenseColumns()}
46+
data={rows}
47+
rowKey={(row, index) => row.expenseId ?? `empty-${index}`}
48+
/>
49+
{!rows.length ? (
50+
// 테이블 헤드 유지하면서 비어 있는 상태를 테이블 영역 위에 오버레이로 표시
51+
<div className="absolute inset-x-0 bottom-0 top-[48px] flex items-center justify-center border-t border-gray-50 bg-white-100">
52+
<AnalysisTableEmpty />
53+
</div>
54+
) : null}
55+
</AnalysisTableWrapper>
4756
);
4857
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import WarningIcon from "@/assets/icons/analysis/ic_warning.svg";
22

3-
export default function AnalysisTableEmpty() {
3+
export default function AnalysisTableEmpty({ message }: { message?: string }) {
44
return (
55
<div className="flex flex-col items-center gap-200">
66
<WarningIcon className="h-[60px] w-[60px] text-text-sub" />
7-
<p className="typo-body-m-medium text-text-sub">지출 데이터가 아직 없어요</p>
7+
<p className="typo-body-m-medium text-text-sub">{message ?? "지출 데이터가 아직 없어요"}</p>
88
</div>
99
);
1010
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export default function AnalysisTableWrapper({
2+
header,
3+
children,
4+
}: {
5+
header?: React.ReactNode;
6+
children: React.ReactNode;
7+
}) {
8+
return (
9+
<section className="flex flex-col gap-400">
10+
<div className="flex flex-col gap-200">{header}</div>
11+
<div className="relative h-[336px] flex items-center justify-center">{children}</div>
12+
</section>
13+
);
14+
}

frontend/src/app/(sidebar)/analysis/_components/table/test/AnalysisTable.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { describe, it, expect } from "vitest";
1+
import { describe, it, expect, vi } from "vitest";
22
import { render } from "@testing-library/react";
33

44
import AnalysisTable from "@/app/(sidebar)/analysis/_components/table/AnalysisTable";
55
import type { components } from "@/types/schema";
66

7+
vi.mock("@/assets/icons/analysis/ic_warning.svg", () => ({ default: () => null }));
8+
79
type GroupExpenseResponse = components["schemas"]["GroupExpenseResponse"];
810

911
const rows: GroupExpenseResponse[] = Array.from({ length: 30 }, (_, index) => ({

0 commit comments

Comments
 (0)