fix(WEB): 토큰 만료 시 무한 로딩 대신 로그인 리다이렉트 처리
- 52개 이상의 컴포넌트에 isNextRedirectError 처리 추가 - Server Action의 redirect() 에러가 catch 블록에서 삼켜지는 문제 해결 - access_token + refresh_token 모두 만료 시 정상적으로 로그인 페이지로 리다이렉트 수정된 영역: - accounting: 10개 컴포넌트 - production: 12개 컴포넌트 - hr: 5개 컴포넌트 - settings: 8개 컴포넌트 - approval: 5개 컴포넌트 - items: 20개+ 컴포넌트 - board: 5개 컴포넌트 - quality: 4개 컴포넌트 - material, outbound, quotes 등 기타 컴포넌트 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -30,6 +31,7 @@ import {
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { toast } from 'sonner';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { getMaterialsForWorkOrder, registerMaterialInput, type MaterialForInput } from './actions';
|
||||
import type { WorkOrder } from '../ProductionDashboard/types';
|
||||
import type { MaterialInput } from './types';
|
||||
@@ -70,6 +72,7 @@ export function MaterialInputModal({
|
||||
toast.error(result.error || '자재 목록 조회에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[MaterialInputModal] loadMaterials error:', error);
|
||||
toast.error('자재 목록 로드 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
@@ -136,6 +139,7 @@ export function MaterialInputModal({
|
||||
toast.error(result.error || '자재 투입 등록에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[MaterialInputModal] handleSubmit error:', error);
|
||||
toast.error('자재 투입 등록 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
@@ -196,9 +200,7 @@ export function MaterialInputModal({
|
||||
</h3>
|
||||
|
||||
{isLoading ? (
|
||||
<div className="border rounded-lg py-12 flex items-center justify-center">
|
||||
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
<ContentLoadingSpinner text="자재 목록을 불러오는 중..." />
|
||||
) : materials.length === 0 ? (
|
||||
<div className="border rounded-lg">
|
||||
<Table>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { ChevronDown, Loader2 } from 'lucide-react';
|
||||
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { getProcessSteps, requestInspection, type ProcessStep, type ProcessStepItem } from './actions';
|
||||
|
||||
interface ProcessDetailSectionProps {
|
||||
@@ -67,6 +69,7 @@ export function ProcessDetailSection({
|
||||
toast.error(result.error || '공정 단계 조회에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[ProcessDetailSection] loadProcessSteps error:', error);
|
||||
toast.error('공정 단계 로드 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
@@ -133,6 +136,7 @@ export function ProcessDetailSection({
|
||||
toast.error(result.error || '검사 요청에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[ProcessDetailSection] handleInspectionConfirm error:', error);
|
||||
toast.error('검사 요청 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
@@ -172,9 +176,7 @@ export function ProcessDetailSection({
|
||||
{/* 공정 단계 목록 */}
|
||||
<div className="space-y-2">
|
||||
{isLoading ? (
|
||||
<div className="py-8 flex items-center justify-center">
|
||||
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
<ContentLoadingSpinner text="공정 단계를 불러오는 중..." />
|
||||
) : steps.length === 0 ? (
|
||||
<div className="py-8 text-center text-gray-500">
|
||||
등록된 공정 단계가 없습니다.
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
*/
|
||||
|
||||
import { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import { ClipboardList, PlayCircle, CheckCircle2, AlertTriangle, Loader2 } from 'lucide-react';
|
||||
import { ClipboardList, PlayCircle, CheckCircle2, AlertTriangle } from 'lucide-react';
|
||||
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {
|
||||
Select,
|
||||
@@ -24,6 +25,7 @@ import { PageLayout } from '@/components/organisms/PageLayout';
|
||||
import { toast } from 'sonner';
|
||||
import { getMyWorkOrders, completeWorkOrder } from './actions';
|
||||
import type { WorkOrder } from '../ProductionDashboard/types';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import type { WorkerStats, CompletionToastInfo, MaterialInput } from './types';
|
||||
import { WorkCard } from './WorkCard';
|
||||
import { CompletionConfirmDialog } from './CompletionConfirmDialog';
|
||||
@@ -49,6 +51,7 @@ export default function WorkerScreen() {
|
||||
toast.error(result.error || '작업 목록 조회에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[WorkerScreen] loadData error:', error);
|
||||
toast.error('데이터 로드 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
@@ -188,6 +191,7 @@ export default function WorkerScreen() {
|
||||
toast.error(result.error || '작업 완료 처리에 실패했습니다.');
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[WorkerScreen] handleCompletionResultConfirm error:', error);
|
||||
toast.error('작업 완료 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
@@ -277,11 +281,7 @@ export default function WorkerScreen() {
|
||||
</Select>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<Card>
|
||||
<CardContent className="py-12 flex items-center justify-center">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-muted-foreground" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<ContentLoadingSpinner text="작업 목록을 불러오는 중..." />
|
||||
) : sortedWorkOrders.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="py-12 text-center text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user