summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/knox/approval/ApprovalCancel.tsx127
-rw-r--r--components/knox/approval/ApprovalDetail.tsx2
-rw-r--r--components/knox/approval/ApprovalList.tsx2
-rw-r--r--lib/knox-api/approval/approval.ts12
-rw-r--r--lib/material/table/material-detail-dialog.tsx112
5 files changed, 202 insertions, 53 deletions
diff --git a/components/knox/approval/ApprovalCancel.tsx b/components/knox/approval/ApprovalCancel.tsx
index b40b43df..62afce94 100644
--- a/components/knox/approval/ApprovalCancel.tsx
+++ b/components/knox/approval/ApprovalCancel.tsx
@@ -7,6 +7,7 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator';
+import { Textarea } from '@/components/ui/textarea';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
import { toast } from 'sonner';
import { Loader2, XCircle, AlertTriangle, CheckCircle } from 'lucide-react';
@@ -47,6 +48,7 @@ export default function ApprovalCancel({
const [isCancelling, setIsCancelling] = useState(false);
const [error, setError] = useState<string | null>(null);
const [cancelResult, setCancelResult] = useState<{ apInfId: string } | null>(null);
+ const [cancelOpinion, setCancelOpinion] = useState('');
const fetchApprovalDetail = async () => {
if (!apInfId.trim()) {
@@ -58,11 +60,12 @@ export default function ApprovalCancel({
setError(null);
setApprovalDetail(null);
setCancelResult(null);
+ setCancelOpinion('');
try {
const response = await getApprovalDetail(apInfId);
- if (response.result === 'SUCCESS') {
+ if (response.result === 'success') {
setApprovalDetail(response.data);
} else {
setError('결재 정보를 가져오는데 실패했습니다.');
@@ -80,12 +83,17 @@ export default function ApprovalCancel({
const handleCancelApproval = async () => {
if (!approvalDetail) return;
+ if (!cancelOpinion.trim()) {
+ toast.error('상신취소 의견을 입력해주세요.');
+ return;
+ }
+
setIsCancelling(true);
try {
- const response = await cancelApproval(approvalDetail.apInfId);
+ const response = await cancelApproval(approvalDetail.apInfId, cancelOpinion);
- if (response.result === 'SUCCESS') {
+ if (response.result === 'success') {
setCancelResult({ apInfId: response.data.apInfId });
toast.success('결재가 성공적으로 취소되었습니다.');
onCancelSuccess?.(response.data.apInfId);
@@ -95,6 +103,9 @@ export default function ApprovalCancel({
...approvalDetail,
status: '4' // 상신취소
});
+
+ // 의견 초기화
+ setCancelOpinion('');
} else {
toast.error('결재 취소에 실패했습니다.');
}
@@ -288,54 +299,78 @@ export default function ApprovalCancel({
</div>
</div>
- {/* 취소 버튼 */}
+ {/* 취소 의견 및 버튼 */}
{canCancelApproval(approvalDetail.status) && (
<>
<Separator />
- <div className="flex justify-end">
- <AlertDialog>
- <AlertDialogTrigger asChild>
- <Button variant="destructive" disabled={isCancelling}>
- {isCancelling ? (
- <>
- <Loader2 className="w-4 h-4 mr-2 animate-spin" />
- 취소 중...
- </>
- ) : (
- <>
- <XCircle className="w-4 h-4 mr-2" />
- 결재 취소
- </>
- )}
- </Button>
- </AlertDialogTrigger>
- <AlertDialogContent>
- <AlertDialogHeader>
- <AlertDialogTitle>결재 취소 확인</AlertDialogTitle>
- <AlertDialogDescription>
- 정말로 이 결재를 취소하시겠습니까?
- <br />
- <br />
- <strong>결재 ID:</strong> {approvalDetail.apInfId}
- <br />
- <strong>제목:</strong> {approvalDetail.subject}
- <br />
- <br />
- 이 작업은 되돌릴 수 없습니다.
- </AlertDialogDescription>
- </AlertDialogHeader>
- <AlertDialogFooter>
- <AlertDialogCancel>취소</AlertDialogCancel>
- <AlertDialogAction
- onClick={handleCancelApproval}
- className="bg-red-600 hover:bg-red-700"
+ <div className="space-y-4">
+ <div>
+ <Label htmlFor="cancelOpinion" className="text-sm font-medium">
+ 상신취소 의견 <span className="text-red-500">*</span>
+ </Label>
+ <Textarea
+ id="cancelOpinion"
+ placeholder="상신취소 사유를 입력해주세요"
+ value={cancelOpinion}
+ onChange={(e) => setCancelOpinion(e.target.value)}
+ className="mt-1"
+ rows={3}
+ />
+ <p className="text-xs text-gray-500 mt-1">
+ 상신취소 의견은 필수 입력 항목입니다.
+ </p>
+ </div>
+
+ <div className="flex justify-end">
+ <AlertDialog>
+ <AlertDialogTrigger asChild>
+ <Button
+ variant="destructive"
+ disabled={isCancelling || !cancelOpinion.trim()}
>
- 결재 취소
- </AlertDialogAction>
- </AlertDialogFooter>
- </AlertDialogContent>
- </AlertDialog>
+ {isCancelling ? (
+ <>
+ <Loader2 className="w-4 h-4 mr-2 animate-spin" />
+ 취소 중...
+ </>
+ ) : (
+ <>
+ <XCircle className="w-4 h-4 mr-2" />
+ 결재 취소
+ </>
+ )}
+ </Button>
+ </AlertDialogTrigger>
+ <AlertDialogContent>
+ <AlertDialogHeader>
+ <AlertDialogTitle>결재 취소 확인</AlertDialogTitle>
+ <AlertDialogDescription>
+ 정말로 이 결재를 취소하시겠습니까?
+ <br />
+ <br />
+ <strong>결재 ID:</strong> {approvalDetail.apInfId}
+ <br />
+ <strong>제목:</strong> {approvalDetail.subject}
+ <br />
+ <strong>취소 의견:</strong> {cancelOpinion}
+ <br />
+ <br />
+ 이 작업은 되돌릴 수 없습니다.
+ </AlertDialogDescription>
+ </AlertDialogHeader>
+ <AlertDialogFooter>
+ <AlertDialogCancel>취소</AlertDialogCancel>
+ <AlertDialogAction
+ onClick={handleCancelApproval}
+ className="bg-red-600 hover:bg-red-700"
+ >
+ 결재 취소
+ </AlertDialogAction>
+ </AlertDialogFooter>
+ </AlertDialogContent>
+ </AlertDialog>
+ </div>
</div>
</>
)}
diff --git a/components/knox/approval/ApprovalDetail.tsx b/components/knox/approval/ApprovalDetail.tsx
index d6c883cb..c36137b4 100644
--- a/components/knox/approval/ApprovalDetail.tsx
+++ b/components/knox/approval/ApprovalDetail.tsx
@@ -86,7 +86,7 @@ export default function ApprovalDetail({
getApprovalContent(id)
]);
- if (detailResponse.result === 'SUCCESS' && contentResponse.result === 'SUCCESS') {
+ if (detailResponse.result.toLowerCase() === 'success' && contentResponse.result.toLowerCase() === 'success') {
setApprovalData({
detail: detailResponse.data,
content: contentResponse.data
diff --git a/components/knox/approval/ApprovalList.tsx b/components/knox/approval/ApprovalList.tsx
index 3a766901..13a13936 100644
--- a/components/knox/approval/ApprovalList.tsx
+++ b/components/knox/approval/ApprovalList.tsx
@@ -67,7 +67,7 @@ export default function ApprovalList({
response = await getApprovalHistory();
}
- if (response.result === 'SUCCESS') {
+ if (response.result === 'success') {
setListData(response.data as unknown as ListItem[]);
} else {
setError('목록을 가져오는데 실패했습니다.');
diff --git a/lib/knox-api/approval/approval.ts b/lib/knox-api/approval/approval.ts
index 080867cd..fe78c8be 100644
--- a/lib/knox-api/approval/approval.ts
+++ b/lib/knox-api/approval/approval.ts
@@ -199,7 +199,7 @@ export async function submitApproval(
}
// Knox API 성공 시 데이터베이스에 저장
- if (result.result === 'SUCCESS') {
+ if (result.result === 'success') {
try {
await saveApprovalToDatabase(
request.apInfId,
@@ -473,14 +473,16 @@ export async function getApprovalHistory(
/**
* 상신 취소
- * POST /approval/api/v2.0/approvals/{apInfId}/cancel
+ * POST /approval/api/v2.0/approvals/{apInfId}/cancel?opinion={opinion}
*/
export async function cancelApproval(
- apInfId: string
+ apInfId: string,
+ opinion: string
): Promise<CancelApprovalResponse> {
try {
const config = await getKnoxConfig();
- const response = await fetch(`${config.baseUrl}/approval/api/v2.0/approvals/${apInfId}/cancel`, {
+ const encodedOpinion = encodeURIComponent(opinion);
+ const response = await fetch(`${config.baseUrl}/approval/api/v2.0/approvals/${apInfId}/cancel?opinion=${encodedOpinion}`, {
method: 'POST',
headers: await createJsonHeaders(),
});
@@ -492,7 +494,7 @@ export async function cancelApproval(
const result = await response.json();
// Knox API 성공 시 데이터베이스에서 삭제
- if (result.result === 'SUCCESS') {
+ if (result.result === 'success') {
try {
await deleteApprovalFromDatabase(apInfId);
} catch (dbError) {
diff --git a/lib/material/table/material-detail-dialog.tsx b/lib/material/table/material-detail-dialog.tsx
index aed0485c..80ec91f9 100644
--- a/lib/material/table/material-detail-dialog.tsx
+++ b/lib/material/table/material-detail-dialog.tsx
@@ -166,6 +166,118 @@ export function MaterialDetailDialog({
<TableCell>{data.material.GROES || "-"}</TableCell>
</TableRow>
<TableRow>
+ <TableCell className="font-medium">포장재료그룹 (MAGRV)</TableCell>
+ <TableCell>{data.material.MAGRV || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">치수단위 (MEABM)</TableCell>
+ <TableCell>{data.material.MEABM || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">자재상태일자 (MSTDE)</TableCell>
+ <TableCell>{data.material.MSTDE || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">제품계층 (PRDHA)</TableCell>
+ <TableCell>{data.material.PRDHA || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">포장재료유형 (VHART)</TableCell>
+ <TableCell>{data.material.VHART || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">부피단위 (VOLEH)</TableCell>
+ <TableCell>{data.material.VOLEH || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">활동코드 (ZZACT)</TableCell>
+ <TableCell>{data.material.ZZACT || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">승인일자 (ZZAPPDT)</TableCell>
+ <TableCell>{data.material.ZZAPPDT || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">승인시간 (ZZAPPTM)</TableCell>
+ <TableCell>{data.material.ZZAPPTM || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">승인사용자 (ZZAPPUS)</TableCell>
+ <TableCell>{data.material.ZZAPPUS || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">인증코드 (ZZCERT)</TableCell>
+ <TableCell>{data.material.ZZCERT || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">검사 (ZZINSP)</TableCell>
+ <TableCell>{data.material.ZZINSP || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">최종수정일자 (ZZLAMDT)</TableCell>
+ <TableCell>{data.material.ZZLAMDT || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">최종수정시간 (ZZLAMTM)</TableCell>
+ <TableCell>{data.material.ZZLAMTM || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">최종수정사용자 (ZZLAMUS)</TableCell>
+ <TableCell>{data.material.ZZLAMUS || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">자재마스터유형 (ZZMMTYP)</TableCell>
+ <TableCell>{data.material.ZZMMTYP || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">MRC (ZZMRC)</TableCell>
+ <TableCell>{data.material.ZZMRC || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">선행후행조정관 (ZZPRCD_SCV_CTLP)</TableCell>
+ <TableCell>{data.material.ZZPRCD_SCV_CTLP || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">CRUD 상태 (ZZPRFLG)</TableCell>
+ <TableCell>{data.material.ZZPRFLG || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">등록일자 (ZZREGDT)</TableCell>
+ <TableCell>{data.material.ZZREGDT || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">등록시간 (ZZREGTM)</TableCell>
+ <TableCell>{data.material.ZZREGTM || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">등록사용자 (ZZREGUS)</TableCell>
+ <TableCell>{data.material.ZZREGUS || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">REP 자재 (ZZREPMAT)</TableCell>
+ <TableCell>{data.material.ZZREPMAT || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">대표지름 (ZZREP_DIA)</TableCell>
+ <TableCell>{data.material.ZZREP_DIA || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">대표지름단위 (ZZREP_DIA_UOM)</TableCell>
+ <TableCell>{data.material.ZZREP_DIA_UOM || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">대표품명재질 (ZZREP_ITM_MATL)</TableCell>
+ <TableCell>{data.material.ZZREP_ITM_MATL || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">SM ID (ZZSMID)</TableCell>
+ <TableCell>{data.material.ZZSMID || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
+ <TableCell className="font-medium">철강재료 (ZZSTL)</TableCell>
+ <TableCell>{data.material.ZZSTL || "-"}</TableCell>
+ </TableRow>
+ <TableRow>
<TableCell className="font-medium">생성일시</TableCell>
<TableCell>{formatDate(data.material.createdAt, "KR")}</TableCell>
</TableRow>