Files
sam-manage/resources/views/esign/sign/done.blade.php
김보곤 3281788536 feat:E-Sign 전자계약 서명 솔루션 MNG 프론트엔드 구현
- 컨트롤러 2개 (EsignController, EsignPublicController)
- 뷰 8개 (dashboard, create, detail, fields, send, sign/auth, sign/sign, sign/done)
- React 하이브리드 방식 (기존 Finance 패턴)
- 라우트 추가 (인증 esign/* + 공개 esign/sign/*)
- PDF.js 기반 서명 위치 설정
- signature_pad 기반 전자서명 입력
- OTP 본인인증 플로우

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 07:02:48 +09:00

128 lines
6.2 KiB
PHP

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>서명 완료 - SAM E-Sign</title>
@vite(['resources/css/app.css'])
</head>
<body class="bg-gray-50 min-h-screen">
<div id="esign-done-root" data-token="{{ $token }}"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const { useState, useEffect } = React;
const TOKEN = document.getElementById('esign-done-root')?.dataset.token;
const API = window.SAM_CONFIG?.apiBaseUrl || '{{ config("services.api.base_url", "") }}';
const API_KEY = '{{ config("services.api.key", "") }}';
const App = () => {
const [contract, setContract] = useState(null);
const [signer, setSigner] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
try {
const res = await fetch(`${API}/api/v1/esign/sign/${TOKEN}`, {
headers: { 'Accept': 'application/json', 'X-API-Key': API_KEY },
});
const json = await res.json();
if (json.success) {
setContract(json.data.contract);
setSigner(json.data.signer);
}
} catch (e) { /* ignore */ }
setLoading(false);
})();
}, []);
if (loading) return <div className="flex items-center justify-center min-h-screen"><p className="text-gray-400">로딩 ...</p></div>;
const isSigned = signer?.status === 'signed';
const isRejected = signer?.status === 'rejected';
const isCompleted = contract?.status === 'completed';
return (
<div className="flex items-center justify-center min-h-screen p-4">
<div className="w-full max-w-md text-center">
<div className="mb-8">
<h1 className="text-2xl font-bold text-gray-900">SAM E-Sign</h1>
</div>
<div className="bg-white rounded-xl shadow-sm border p-8">
{isSigned && (
<>
<div className="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-6">
<svg className="w-10 h-10 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<h2 className="text-xl font-bold text-gray-900 mb-2">서명이 완료되었습니다</h2>
<p className="text-gray-500 mb-6">
{isCompleted
? '모든 서명자의 서명이 완료되어 계약이 체결되었습니다.'
: '서명이 정상적으로 접수되었습니다. 다른 서명자의 서명이 완료되면 알려드리겠습니다.'
}
</p>
</>
)}
{isRejected && (
<>
<div className="w-20 h-20 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-6">
<svg className="w-10 h-10 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h2 className="text-xl font-bold text-gray-900 mb-2">서명이 거절되었습니다</h2>
<p className="text-gray-500 mb-6">서명 거절이 접수되었습니다. 계약 담당자에게 알림이 발송됩니다.</p>
</>
)}
{!isSigned && !isRejected && (
<>
<div className="w-20 h-20 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-6">
<svg className="w-10 h-10 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</div>
<h2 className="text-xl font-bold text-gray-900 mb-2">처리 완료</h2>
<p className="text-gray-500 mb-6">요청이 처리되었습니다.</p>
</>
)}
<div className="bg-gray-50 rounded-lg p-4 text-left text-sm">
<div className="flex justify-between mb-2">
<span className="text-gray-500">계약</span>
<span className="font-medium">{contract?.title || '-'}</span>
</div>
<div className="flex justify-between mb-2">
<span className="text-gray-500">서명자</span>
<span className="font-medium">{signer?.name || '-'}</span>
</div>
{signer?.signed_at && (
<div className="flex justify-between">
<span className="text-gray-500">서명일시</span>
<span className="font-medium">{signer.signed_at?.slice(0,16).replace('T', ' ')}</span>
</div>
)}
</div>
</div>
<p className="text-xs text-gray-400 mt-6">
SAM E-Sign 전자계약 서명 시스템
</p>
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('esign-done-root'));
</script>
</body>
</html>