- 회계: 거래처, 매입/매출, 입출금 상세 페이지 개선 - HR: 직원 관리 및 출퇴근 설정 기능 수정 - 주문관리: 상세폼 구조 분리 (cards, dialogs, hooks, tables) - 알림설정: 컴포넌트 구조 단순화 및 리팩토링 - 캘린더: 헤더 및 일정 타입 개선 - 출고관리: 액션 및 타입 정의 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
638 lines
21 KiB
TypeScript
638 lines
21 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useCallback, useMemo, useEffect, useTransition } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { format } from 'date-fns';
|
|
import {
|
|
Receipt,
|
|
Save,
|
|
Trash2,
|
|
Plus,
|
|
X,
|
|
Send,
|
|
FileText,
|
|
Loader2,
|
|
List,
|
|
} from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from '@/components/ui/alert-dialog';
|
|
import { PageLayout } from '@/components/organisms/PageLayout';
|
|
import { PageHeader } from '@/components/organisms/PageHeader';
|
|
import type { SalesRecord, SalesItem, SalesType } from './types';
|
|
import { SALES_TYPE_OPTIONS } from './types';
|
|
import { getSaleById, createSale, updateSale, deleteSale } from './actions';
|
|
import { toast } from 'sonner';
|
|
import { getClients } from '../VendorManagement/actions';
|
|
|
|
// ===== Props =====
|
|
interface SalesDetailProps {
|
|
mode: 'view' | 'edit' | 'new';
|
|
salesId?: string;
|
|
}
|
|
|
|
// ===== 거래처 타입 (간단) =====
|
|
interface ClientOption {
|
|
id: string;
|
|
name: string;
|
|
email?: string;
|
|
}
|
|
|
|
// ===== 초기 품목 데이터 =====
|
|
const createEmptyItem = (): SalesItem => ({
|
|
id: `item-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
itemName: '',
|
|
quantity: 0,
|
|
unitPrice: 0,
|
|
supplyAmount: 0,
|
|
vat: 0,
|
|
note: '',
|
|
});
|
|
|
|
export function SalesDetail({ mode, salesId }: SalesDetailProps) {
|
|
const router = useRouter();
|
|
const [isPending, startTransition] = useTransition();
|
|
const isViewMode = mode === 'view';
|
|
const isNewMode = mode === 'new';
|
|
|
|
// ===== 로딩 상태 =====
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isSaving, setIsSaving] = useState(false);
|
|
|
|
// ===== 거래처 목록 =====
|
|
const [clients, setClients] = useState<ClientOption[]>([]);
|
|
|
|
// ===== 폼 상태 =====
|
|
const [salesNo, setSalesNo] = useState('');
|
|
const [salesDate, setSalesDate] = useState(format(new Date(), 'yyyy-MM-dd'));
|
|
const [vendorId, setVendorId] = useState('');
|
|
const [vendorName, setVendorName] = useState('');
|
|
const [salesType, setSalesType] = useState<SalesType>('product');
|
|
const [items, setItems] = useState<SalesItem[]>([createEmptyItem()]);
|
|
const [taxInvoiceIssued, setTaxInvoiceIssued] = useState(false);
|
|
const [transactionStatementIssued, setTransactionStatementIssued] = useState(false);
|
|
const [note, setNote] = useState('');
|
|
|
|
// ===== 알림 다이얼로그 상태 =====
|
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
|
const [showEmailAlert, setShowEmailAlert] = useState(false);
|
|
const [emailAlertMessage, setEmailAlertMessage] = useState('');
|
|
|
|
// ===== 거래처 목록 로드 =====
|
|
useEffect(() => {
|
|
async function loadClients() {
|
|
const result = await getClients({ size: 1000, only_active: true });
|
|
if (result.success) {
|
|
setClients(result.data.map(v => ({
|
|
id: v.id,
|
|
name: v.vendorName,
|
|
email: v.email,
|
|
})));
|
|
}
|
|
}
|
|
loadClients();
|
|
}, []);
|
|
|
|
// ===== 매출 상세 데이터 로드 =====
|
|
useEffect(() => {
|
|
async function loadSaleDetail() {
|
|
if (salesId && mode !== 'new') {
|
|
setIsLoading(true);
|
|
const result = await getSaleById(salesId);
|
|
if (result.success && result.data) {
|
|
const data = result.data;
|
|
setSalesNo(data.salesNo);
|
|
setSalesDate(data.salesDate);
|
|
setVendorId(data.vendorId);
|
|
setVendorName(data.vendorName);
|
|
setSalesType(data.salesType);
|
|
setItems(data.items.length > 0 ? data.items : [createEmptyItem()]);
|
|
setTaxInvoiceIssued(data.taxInvoiceIssued);
|
|
setTransactionStatementIssued(data.transactionStatementIssued);
|
|
setNote(data.note || '');
|
|
}
|
|
setIsLoading(false);
|
|
} else if (isNewMode) {
|
|
// 신규: 매출번호는 서버에서 자동 생성
|
|
setSalesNo('(자동생성)');
|
|
setIsLoading(false);
|
|
} else {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
loadSaleDetail();
|
|
}, [salesId, mode, isNewMode]);
|
|
|
|
// ===== 선택된 거래처 정보 =====
|
|
const selectedVendor = useMemo(() => {
|
|
return clients.find(v => v.id === vendorId);
|
|
}, [clients, vendorId]);
|
|
|
|
// ===== 합계 계산 =====
|
|
const totals = useMemo(() => {
|
|
const totalSupply = items.reduce((sum, item) => sum + item.supplyAmount, 0);
|
|
const totalVat = items.reduce((sum, item) => sum + item.vat, 0);
|
|
return {
|
|
supplyAmount: totalSupply,
|
|
vat: totalVat,
|
|
total: totalSupply + totalVat,
|
|
};
|
|
}, [items]);
|
|
|
|
// ===== 품목 수정 핸들러 =====
|
|
const handleItemChange = useCallback((index: number, field: keyof SalesItem, value: string | number) => {
|
|
setItems(prev => {
|
|
const newItems = [...prev];
|
|
const item = { ...newItems[index] };
|
|
|
|
if (field === 'quantity' || field === 'unitPrice') {
|
|
const numValue = typeof value === 'string' ? parseFloat(value) || 0 : value;
|
|
item[field] = numValue;
|
|
// 자동 계산
|
|
item.supplyAmount = item.quantity * item.unitPrice;
|
|
item.vat = Math.floor(item.supplyAmount * 0.1);
|
|
} else {
|
|
(item as any)[field] = value;
|
|
}
|
|
|
|
newItems[index] = item;
|
|
return newItems;
|
|
});
|
|
}, []);
|
|
|
|
// ===== 품목 추가 =====
|
|
const handleAddItem = useCallback(() => {
|
|
setItems(prev => [...prev, createEmptyItem()]);
|
|
}, []);
|
|
|
|
// ===== 품목 삭제 =====
|
|
const handleRemoveItem = useCallback((index: number) => {
|
|
setItems(prev => {
|
|
if (prev.length <= 1) return prev; // 최소 1개 유지
|
|
return prev.filter((_, i) => i !== index);
|
|
});
|
|
}, []);
|
|
|
|
// ===== 저장 =====
|
|
const handleSave = useCallback(async () => {
|
|
if (!vendorId) {
|
|
toast.warning('거래처를 선택해주세요.');
|
|
return;
|
|
}
|
|
|
|
setIsSaving(true);
|
|
|
|
const saleData: Partial<SalesRecord> = {
|
|
salesDate,
|
|
vendorId,
|
|
salesType,
|
|
totalSupplyAmount: totals.supplyAmount,
|
|
totalVat: totals.vat,
|
|
totalAmount: totals.total,
|
|
taxInvoiceIssued,
|
|
transactionStatementIssued,
|
|
note,
|
|
};
|
|
|
|
try {
|
|
let result;
|
|
if (isNewMode) {
|
|
result = await createSale(saleData);
|
|
} else if (salesId) {
|
|
result = await updateSale(salesId, saleData);
|
|
}
|
|
|
|
if (result?.success) {
|
|
toast.success(isNewMode ? '매출이 등록되었습니다.' : '매출이 수정되었습니다.');
|
|
router.push('/ko/accounting/sales');
|
|
} else {
|
|
toast.error(result?.error || '저장에 실패했습니다.');
|
|
}
|
|
} catch {
|
|
toast.error('저장 중 오류가 발생했습니다.');
|
|
} finally {
|
|
setIsSaving(false);
|
|
}
|
|
}, [salesDate, vendorId, salesType, totals, taxInvoiceIssued, transactionStatementIssued, note, isNewMode, salesId, router]);
|
|
|
|
// ===== 삭제 =====
|
|
const handleDelete = useCallback(async () => {
|
|
if (!salesId) return;
|
|
|
|
try {
|
|
const result = await deleteSale(salesId);
|
|
setShowDeleteDialog(false);
|
|
|
|
if (result.success) {
|
|
toast.success('매출이 삭제되었습니다.');
|
|
router.push('/ko/accounting/sales');
|
|
} else {
|
|
toast.error(result.error || '삭제에 실패했습니다.');
|
|
}
|
|
} catch {
|
|
toast.error('삭제 중 오류가 발생했습니다.');
|
|
}
|
|
}, [salesId, router]);
|
|
|
|
// ===== 목록으로 이동 =====
|
|
const handleBack = useCallback(() => {
|
|
router.push('/ko/accounting/sales');
|
|
}, [router]);
|
|
|
|
// ===== 수정 모드로 이동 =====
|
|
const handleEdit = useCallback(() => {
|
|
if (salesId) {
|
|
router.push(`/ko/accounting/sales/${salesId}?mode=edit`);
|
|
}
|
|
}, [router, salesId]);
|
|
|
|
// ===== 취소 (수정/등록 모드에서) =====
|
|
const handleCancel = useCallback(() => {
|
|
if (isNewMode) {
|
|
router.push('/ko/accounting/sales');
|
|
} else if (salesId) {
|
|
router.push(`/ko/accounting/sales/${salesId}`);
|
|
}
|
|
}, [router, salesId, isNewMode]);
|
|
|
|
// ===== 거래명세서 발행 =====
|
|
const handleSendTransactionStatement = useCallback(() => {
|
|
if (selectedVendor?.email) {
|
|
setEmailAlertMessage(`거래명세서가 '${selectedVendor.email}'으로 발송되었습니다.`);
|
|
setTransactionStatementIssued(true);
|
|
setShowEmailAlert(true);
|
|
}
|
|
}, [selectedVendor]);
|
|
|
|
// ===== 금액 포맷 =====
|
|
const formatAmount = (amount: number): string => {
|
|
return amount.toLocaleString();
|
|
};
|
|
|
|
return (
|
|
<PageLayout>
|
|
{/* 페이지 헤더 */}
|
|
<PageHeader
|
|
title={isNewMode ? '매출 상세_직접 등록' : '매출 상세'}
|
|
description="매출 상세를 등록하고 관리합니다"
|
|
icon={Receipt}
|
|
/>
|
|
|
|
{/* 헤더 액션 버튼 */}
|
|
<div className="flex items-center justify-end gap-2 mb-6">
|
|
{/* view 모드: [목록] [삭제] [수정] */}
|
|
{isViewMode ? (
|
|
<>
|
|
<Button variant="outline" onClick={handleBack}>
|
|
<List className="h-4 w-4 mr-2" />
|
|
목록
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
className="text-red-500 border-red-200 hover:bg-red-50"
|
|
onClick={() => setShowDeleteDialog(true)}
|
|
>
|
|
삭제
|
|
</Button>
|
|
<Button onClick={handleEdit} className="bg-blue-500 hover:bg-blue-600">
|
|
수정
|
|
</Button>
|
|
</>
|
|
) : (
|
|
/* edit/new 모드: [취소] [저장/등록] */
|
|
<>
|
|
<Button variant="outline" onClick={handleCancel}>
|
|
취소
|
|
</Button>
|
|
<Button onClick={handleSave} className="bg-blue-500 hover:bg-blue-600">
|
|
{isNewMode ? '등록' : '저장'}
|
|
</Button>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
{/* 기본 정보 섹션 */}
|
|
<Card className="mb-6">
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">기본 정보</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
{/* 매출번호 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="salesNo">매출번호</Label>
|
|
<Input
|
|
id="salesNo"
|
|
value={salesNo}
|
|
readOnly
|
|
disabled
|
|
className="bg-gray-50"
|
|
/>
|
|
</div>
|
|
|
|
{/* 매출일 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="salesDate">매출일</Label>
|
|
<Input
|
|
id="salesDate"
|
|
type="date"
|
|
value={salesDate}
|
|
onChange={(e) => setSalesDate(e.target.value)}
|
|
disabled={isViewMode}
|
|
/>
|
|
</div>
|
|
|
|
{/* 거래처명 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="vendorId">거래처명</Label>
|
|
<Select value={vendorId} onValueChange={setVendorId} disabled={isViewMode}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="거래처명 ▼" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{clients.map((client) => (
|
|
<SelectItem key={client.id} value={client.id}>
|
|
{client.name}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 매출 유형 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="salesType">매출 유형</Label>
|
|
<Select value={salesType} onValueChange={(v) => setSalesType(v as SalesType)} disabled={isViewMode}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="선택 ▼" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{SALES_TYPE_OPTIONS.filter(o => o.value !== 'all').map((option) => (
|
|
<SelectItem key={option.value} value={option.value}>
|
|
{option.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 품목 정보 섹션 */}
|
|
<Card className="mb-6">
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">품목 정보</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="overflow-x-auto">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="w-[50px] text-center">#</TableHead>
|
|
<TableHead>품목명</TableHead>
|
|
<TableHead className="w-[100px] text-right">수량</TableHead>
|
|
<TableHead className="w-[120px] text-right">단가</TableHead>
|
|
<TableHead className="w-[120px] text-right">공급가액</TableHead>
|
|
<TableHead className="w-[100px] text-right">부가세</TableHead>
|
|
<TableHead>적요</TableHead>
|
|
<TableHead className="w-[50px]"></TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{items.map((item, index) => (
|
|
<TableRow key={item.id}>
|
|
<TableCell className="text-center">{index + 1}</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
value={item.itemName}
|
|
onChange={(e) => handleItemChange(index, 'itemName', e.target.value)}
|
|
placeholder="품목명"
|
|
disabled={isViewMode}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
type="number"
|
|
value={item.quantity || ''}
|
|
onChange={(e) => handleItemChange(index, 'quantity', e.target.value)}
|
|
className="text-right"
|
|
disabled={isViewMode}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
type="number"
|
|
value={item.unitPrice || ''}
|
|
onChange={(e) => handleItemChange(index, 'unitPrice', e.target.value)}
|
|
className="text-right"
|
|
disabled={isViewMode}
|
|
/>
|
|
</TableCell>
|
|
<TableCell className="text-right font-medium">
|
|
{formatAmount(item.supplyAmount)}
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatAmount(item.vat)}
|
|
</TableCell>
|
|
<TableCell>
|
|
<Input
|
|
value={item.note}
|
|
onChange={(e) => handleItemChange(index, 'note', e.target.value)}
|
|
placeholder="적요"
|
|
disabled={isViewMode}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
{!isViewMode && items.length > 1 && (
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-8 w-8 text-red-600 hover:text-red-700"
|
|
onClick={() => handleRemoveItem(index)}
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</Button>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{/* 합계 행 */}
|
|
<TableRow className="bg-gray-50 font-medium">
|
|
<TableCell colSpan={4} className="text-right">
|
|
합계
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatAmount(totals.supplyAmount)}
|
|
</TableCell>
|
|
<TableCell className="text-right">
|
|
{formatAmount(totals.vat)}
|
|
</TableCell>
|
|
<TableCell colSpan={2}></TableCell>
|
|
</TableRow>
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
|
|
{/* 품목 추가 버튼 */}
|
|
{!isViewMode && (
|
|
<div className="mt-4">
|
|
<Button variant="outline" onClick={handleAddItem}>
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
추가
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 세금계산서 섹션 */}
|
|
<Card className="mb-6">
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">세금계산서</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<Label htmlFor="taxInvoice">세금계산서 발행</Label>
|
|
<Switch
|
|
id="taxInvoice"
|
|
checked={taxInvoiceIssued}
|
|
onCheckedChange={setTaxInvoiceIssued}
|
|
disabled={isViewMode}
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{taxInvoiceIssued ? (
|
|
<span className="text-sm text-green-600 flex items-center gap-1">
|
|
<FileText className="h-4 w-4" />
|
|
발행완료
|
|
</span>
|
|
) : (
|
|
<span className="text-sm text-gray-500 flex items-center gap-1">
|
|
<FileText className="h-4 w-4" />
|
|
미발행
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 거래명세서 섹션 */}
|
|
<Card className="mb-6">
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">거래명세서</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center justify-between flex-wrap gap-4">
|
|
<div className="flex items-center gap-3">
|
|
<Label htmlFor="transactionStatement">거래명세서 발행</Label>
|
|
<Switch
|
|
id="transactionStatement"
|
|
checked={transactionStatementIssued}
|
|
onCheckedChange={setTransactionStatementIssued}
|
|
disabled={isViewMode}
|
|
/>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant="default"
|
|
className="bg-gray-900 hover:bg-gray-800 text-white"
|
|
onClick={() => {
|
|
// TODO: 거래명세서 조회 기능 연결
|
|
toast.info('거래명세서 조회 기능 준비 중입니다.');
|
|
}}
|
|
>
|
|
<FileText className="h-4 w-4 mr-2" />
|
|
거래명세서 조회
|
|
</Button>
|
|
{vendorId && (
|
|
<Button
|
|
variant="default"
|
|
className="bg-gray-900 hover:bg-gray-800 text-white"
|
|
onClick={handleSendTransactionStatement}
|
|
disabled={!selectedVendor?.email}
|
|
>
|
|
<Send className="h-4 w-4 mr-2" />
|
|
거래명세서 발행하기
|
|
</Button>
|
|
)}
|
|
{transactionStatementIssued ? (
|
|
<span className="text-sm text-green-600 flex items-center gap-1">
|
|
<FileText className="h-4 w-4" />
|
|
발행완료
|
|
</span>
|
|
) : (
|
|
<span className="text-sm text-gray-500 flex items-center gap-1">
|
|
<FileText className="h-4 w-4" />
|
|
미발행
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* 삭제 확인 다이얼로그 */}
|
|
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>매출 삭제</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
이 매출 내역을 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>취소</AlertDialogCancel>
|
|
<AlertDialogAction onClick={handleDelete} className="bg-red-600 hover:bg-red-700">
|
|
삭제
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
|
|
{/* 이메일 발송 알림 다이얼로그 */}
|
|
<AlertDialog open={showEmailAlert} onOpenChange={setShowEmailAlert}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>발송 완료</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
{emailAlertMessage}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogAction>확인</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</PageLayout>
|
|
);
|
|
} |