chore(WEB): actions.ts 에러 핸들링 및 CEO 대시보드 개선

- 전체 모듈 actions.ts redirect 에러 핸들링 추가
- CEODashboard DetailModal 추가
- MonthlyExpenseSection 개선
- fetch-wrapper redirect 에러 처리
- redirect-error 유틸 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2026-01-08 18:41:15 +09:00
parent 9885085259
commit 0d539628f3
61 changed files with 1226 additions and 359 deletions

View File

@@ -20,7 +20,7 @@
'use server';
import { isRedirectError } from 'next/dist/client/components/redirect';
import { isNextRedirectError } from '@/lib/utils/redirect-error';
import { serverFetch } from '@/lib/api/fetch-wrapper';
import type {
Quote,
@@ -133,7 +133,7 @@ export async function getQuotes(params?: QuoteListParams): Promise<{
},
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] getQuotes error:', error);
return {
success: false,
@@ -210,7 +210,7 @@ export async function getQuoteById(id: string): Promise<{
data: transformApiToFrontend(result.data),
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] getQuoteById error:', error);
return {
success: false,
@@ -267,7 +267,7 @@ export async function createQuote(
data: transformApiToFrontend(result.data),
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] createQuote error:', error);
return {
success: false,
@@ -325,7 +325,7 @@ export async function updateQuote(
data: transformApiToFrontend(result.data),
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] updateQuote error:', error);
return {
success: false,
@@ -370,7 +370,7 @@ export async function deleteQuote(id: string): Promise<{ success: boolean; error
return { success: true };
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] deleteQuote error:', error);
return {
success: false,
@@ -416,7 +416,7 @@ export async function bulkDeleteQuotes(ids: string[]): Promise<{ success: boolea
return { success: true };
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] bulkDeleteQuotes error:', error);
return {
success: false,
@@ -469,7 +469,7 @@ export async function finalizeQuote(id: string): Promise<{
data: transformApiToFrontend(result.data),
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] finalizeQuote error:', error);
return {
success: false,
@@ -522,7 +522,7 @@ export async function cancelFinalizeQuote(id: string): Promise<{
data: transformApiToFrontend(result.data),
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] cancelFinalizeQuote error:', error);
return {
success: false,
@@ -577,7 +577,7 @@ export async function convertQuoteToOrder(id: string): Promise<{
orderId: result.data?.order?.id ? String(result.data.order.id) : undefined,
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] convertQuoteToOrder error:', error);
return {
success: false,
@@ -629,7 +629,7 @@ export async function getQuoteNumberPreview(): Promise<{
data: result.data?.quote_number || result.data,
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] getQuoteNumberPreview error:', error);
return {
success: false,
@@ -681,7 +681,7 @@ export async function generateQuotePdf(id: string): Promise<{
data: blob,
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] generateQuotePdf error:', error);
return {
success: false,
@@ -730,7 +730,7 @@ export async function sendQuoteEmail(
return { success: true };
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] sendQuoteEmail error:', error);
return {
success: false,
@@ -779,7 +779,7 @@ export async function sendQuoteKakao(
return { success: true };
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] sendQuoteKakao error:', error);
return {
success: false,
@@ -871,7 +871,7 @@ export async function getFinishedGoods(category?: string): Promise<{
})),
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] getFinishedGoods error:', error);
return {
success: false,
@@ -964,7 +964,7 @@ export async function calculateBomBulk(items: BomCalculateItem[]): Promise<{
data: result.data || [],
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] calculateBomBulk error:', error);
return {
success: false,
@@ -1040,7 +1040,7 @@ export async function getQuotesSummary(params?: {
},
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] getQuotesSummary error:', error);
return {
success: false,
@@ -1085,7 +1085,7 @@ export async function getSiteNames(): Promise<{
data: siteNames,
};
} catch (error) {
if (isRedirectError(error)) throw error;
if (isNextRedirectError(error)) throw error;
console.error('[QuoteActions] getSiteNames error:', error);
return {
success: false,