fix:새 계약 생성 폼 PC 화면 UI 개선 (과대 크기 축소)
- max-w-3xl + mx-auto로 폼 너비 제한 (768px) - input/select/textarea padding 축소 (py-2 → py-1.5) - 카드 내부 패딩 축소 (p-5 → p-4) - 라벨 폰트 크기 축소 (text-sm → text-xs) - 섹션 간격 축소 (space-y-5 → space-y-4) - 버튼 크기 축소 및 rounded-lg → rounded-md 통일 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -23,20 +23,20 @@
|
||||
|
||||
const Input = ({ label, name, value, error, onChange, type = 'text', required = false, placeholder = '', style }) => (
|
||||
<div style={style}>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">{label} {required && <span className="text-red-500">*</span>}</label>
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">{label} {required && <span className="text-red-500">*</span>}</label>
|
||||
<input type={type} value={value} onChange={e => onChange(name, e.target.value)}
|
||||
placeholder={placeholder} required={required}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors" />
|
||||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors" />
|
||||
{error && <p className="text-red-500 text-xs mt-1">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
|
||||
const SignerRow = ({ prefix, title, subtitle, color, form, errors, onChange }) => (
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<span className="w-2 h-2 rounded-full flex-shrink-0" style={{ backgroundColor: color }}></span>
|
||||
<h3 className="text-sm font-semibold text-gray-800">{title}</h3>
|
||||
<span className="text-xs text-gray-400">{subtitle}</span>
|
||||
<h3 className="text-xs font-semibold text-gray-800">{title}</h3>
|
||||
<span className="text-[11px] text-gray-400">{subtitle}</span>
|
||||
</div>
|
||||
<div className="flex gap-3" style={{ flexWrap: 'wrap' }}>
|
||||
<Input label="이름" name={`${prefix}_name`} value={form[`${prefix}_name`]} error={errors[`${prefix}_name`]}
|
||||
@@ -135,8 +135,8 @@ className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 sm:p-6">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="p-4 sm:p-6 max-w-3xl mx-auto">
|
||||
<div className="flex items-center gap-3 mb-5">
|
||||
<a href="/esign" className="text-gray-400 hover:text-gray-600 text-lg" hx-boost="false">←</a>
|
||||
<h1 className="text-xl font-bold text-gray-900">새 계약 생성</h1>
|
||||
{IS_ADMIN && (
|
||||
@@ -151,22 +151,22 @@ className="w-8 h-8 flex items-center justify-center rounded-lg text-amber-500 ho
|
||||
|
||||
{errors.general && <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg mb-4 text-sm">{errors.general}</div>}
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-5">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
{/* 계약 정보 */}
|
||||
<div className="bg-white rounded-lg border p-5">
|
||||
<h2 className="text-base font-semibold text-gray-900 mb-4">계약 정보</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="bg-white rounded-lg border p-4">
|
||||
<h2 className="text-sm font-semibold text-gray-900 mb-3">계약 정보</h2>
|
||||
<div className="space-y-3">
|
||||
<Input label="계약 제목" name="title" value={form.title} error={errors.title} onChange={handleChange} required placeholder="예: 2026년 공급 계약서" />
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">설명</label>
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">설명</label>
|
||||
<textarea value={form.description} onChange={e => handleChange('description', e.target.value)}
|
||||
placeholder="계약에 대한 간단한 설명 (선택)" rows={2}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors" />
|
||||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-colors" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">PDF 파일 {!templateId && <span className="text-red-500">*</span>}</label>
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">PDF 파일 {!templateId && <span className="text-red-500">*</span>}</label>
|
||||
<input ref={fileRef} type="file" accept=".pdf" onChange={e => setFile(e.target.files[0])} required={!templateId}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm file:mr-3 file:py-1 file:px-3 file:rounded file:border-0 file:bg-blue-50 file:text-blue-700 file:text-sm file:font-medium file:cursor-pointer" />
|
||||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm file:mr-3 file:py-0.5 file:px-2.5 file:rounded file:border-0 file:bg-blue-50 file:text-blue-700 file:text-xs file:font-medium file:cursor-pointer" />
|
||||
{file && <p className="text-xs text-gray-500 mt-1">{file.name} ({(file.size / 1024 / 1024).toFixed(2)} MB)</p>}
|
||||
{templateId && !file && (() => {
|
||||
const selectedTpl = templates.find(t => t.id == templateId);
|
||||
@@ -176,11 +176,11 @@ className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm file:mr-3
|
||||
return <p className="text-xs text-amber-600 mt-1">템플릿 선택 시 PDF 파일은 선택사항입니다. 나중에 업로드할 수 있습니다.</p>;
|
||||
})()}
|
||||
</div>
|
||||
<div className="flex gap-4" style={{ flexWrap: 'wrap' }}>
|
||||
<div className="flex gap-3" style={{ flexWrap: 'wrap' }}>
|
||||
<div style={{ flex: '1 1 200px' }}>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">서명 순서</label>
|
||||
<label className="block text-xs font-medium text-gray-700 mb-1">서명 순서</label>
|
||||
<select value={form.sign_order_type} onChange={e => handleChange('sign_order_type', e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
className="w-full border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
<option value="counterpart_first">상대방 먼저 서명</option>
|
||||
<option value="creator_first">작성자 먼저 서명</option>
|
||||
</select>
|
||||
@@ -193,9 +193,9 @@ className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring
|
||||
</div>
|
||||
|
||||
{/* 서명자 정보 */}
|
||||
<div className="bg-white rounded-lg border p-5">
|
||||
<h2 className="text-base font-semibold text-gray-900 mb-4">서명자 정보</h2>
|
||||
<div className="space-y-5">
|
||||
<div className="bg-white rounded-lg border p-4">
|
||||
<h2 className="text-sm font-semibold text-gray-900 mb-3">서명자 정보</h2>
|
||||
<div className="space-y-4">
|
||||
<SignerRow prefix="creator" title="작성자 (갑)" subtitle="나" color="#3B82F6"
|
||||
form={form} errors={errors} onChange={handleChange} />
|
||||
<div className="border-t"></div>
|
||||
@@ -206,20 +206,20 @@ className="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring
|
||||
|
||||
{/* 필드 템플릿 (선택) */}
|
||||
{templates.length > 0 && (
|
||||
<div className="bg-white rounded-lg border p-5">
|
||||
<h2 className="text-base font-semibold text-gray-900 mb-1">필드 템플릿</h2>
|
||||
<div className="bg-white rounded-lg border p-4">
|
||||
<h2 className="text-sm font-semibold text-gray-900 mb-1">필드 템플릿</h2>
|
||||
<p className="text-xs text-gray-400 mb-3">계약 생성 후 필드 에디터에서 자동으로 적용됩니다 (선택사항)</p>
|
||||
|
||||
<div className="flex gap-2 mb-3">
|
||||
<select value={templateCategory} onChange={e => setTemplateCategory(e.target.value)}
|
||||
className="border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
className="border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none">
|
||||
<option value="">전체 카테고리</option>
|
||||
{[...new Set(templates.map(t => t.category).filter(Boolean))].sort().map(c =>
|
||||
<option key={c} value={c}>{c}</option>
|
||||
)}
|
||||
</select>
|
||||
<input type="text" value={templateSearch} onChange={e => setTemplateSearch(e.target.value)}
|
||||
placeholder="검색..." className="border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none flex-1" />
|
||||
placeholder="검색..." className="border border-gray-300 rounded-md px-2.5 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 outline-none flex-1" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5 max-h-[200px] overflow-y-auto">
|
||||
@@ -257,10 +257,10 @@ className="text-blue-600 focus:ring-blue-500" />
|
||||
)}
|
||||
|
||||
{/* 버튼 */}
|
||||
<div className="flex justify-end gap-3 pt-1">
|
||||
<a href="/esign" className="px-5 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 text-sm transition-colors" hx-boost="false">취소</a>
|
||||
<div className="flex justify-end gap-3">
|
||||
<a href="/esign" className="px-4 py-1.5 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 text-sm transition-colors" hx-boost="false">취소</a>
|
||||
<button type="submit" disabled={submitting}
|
||||
className="px-5 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium disabled:opacity-50 transition-colors">
|
||||
className="px-4 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 text-sm font-medium disabled:opacity-50 transition-colors">
|
||||
{submitting ? '생성 중...' : '계약 생성 및 서명 위치 설정'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user