[as frontend] 입력화면 콤포넌트화 처리

This commit is contained in:
2026-05-28 13:59:28 +09:00
parent a87f887635
commit 426e3ffcdf
6 changed files with 176 additions and 155 deletions
+1
View File
@@ -0,0 +1 @@
export * from './ui';
@@ -0,0 +1,71 @@
import React from "react";
type EtcFile = {
id: string;
name: string;
ext: string;
size: string;
uploader: string;
atflCd: string;
atflId: string;
};
type Props = {
etcFiles: EtcFile[];
etcUploading: boolean;
onFilesChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onDelete: (fileId: string, atflId: string) => void;
};
export function EtcAttachments({ etcFiles, etcUploading, onFilesChange, onDelete }: Props) {
return (
<div>
<div className="mb-3 flex flex-nowrap items-center gap-2">
<input
type="text"
className="cnfm-input min-w-0 flex-1"
placeholder="첨부문서를 등록해주세요"
value={
etcUploading ? "업로드 중..." : etcFiles.length > 0 ? `${etcFiles.length}개 파일 등록됨` : ""
}
readOnly
/>
<label className="cnfm-btn-search shrink-0 cursor-pointer whitespace-nowrap text-[1.2rem] flex items-center">
{etcUploading ? "업로드중" : "첨부파일"}
<input type="file" className="hidden" multiple onChange={onFilesChange} disabled={etcUploading} />
</label>
</div>
<div className="cnfm-table-wrap">
<table className="cnfm-table">
<thead>
<tr>
<th>파일명</th>
<th>확장자</th>
<th>크기</th>
<th>등록자</th>
<th>액션</th>
</tr>
</thead>
<tbody>
{etcFiles.map((f) => (
<tr key={f.id}>
<td className="pr-2">{f.name}</td>
<td className="text-center">{f.ext}</td>
<td className="text-right">{f.size}</td>
<td className="text-center">{f.uploader}</td>
<td className="text-center">
<button className="cnfm-btn-delete" onClick={() => onDelete(f.id, f.atflId)}>
삭제
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
export default EtcAttachments;
+18
View File
@@ -0,0 +1,18 @@
import React from "react";
export const NumCell = ({ value, onChange }: { value: string; onChange: (v: string) => void }) => (
<td>
<input
type="text"
className="cnfm-input w-full min-w-0 text-right"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</td>
);
export const ReadCell = ({ value }: { value: string | number }) => (
<td className="bg-gray-50 pr-2 text-right text-[1.25rem] font-semibold">{value}</td>
);
export default { NumCell, ReadCell };
+3
View File
@@ -0,0 +1,3 @@
export { NumCell, ReadCell } from './cells';
export { PrivacyConsent } from './privacy-consent';
export { EtcAttachments } from './attachments';
@@ -0,0 +1,31 @@
import React from "react";
type Props = {
value: "Y" | "N";
onChange: (v: "Y" | "N") => void;
children?: React.ReactNode;
};
export function PrivacyConsent({ value, onChange, children }: Props) {
return (
<div className="cnfm-card p-4">
<h3 className="mb-3 border-b pb-2 text-[1.5rem] font-bold text-gray-700">개인정보 동의</h3>
<div className="mb-3 rounded border bg-gray-50 p-3 text-[1.25rem] leading-relaxed text-gray-600 max-h-80 overflow-auto whitespace-pre-wrap pr-2">
{children}
</div>
<div className="flex gap-6 text-[1.3rem]">
<label className="flex cursor-pointer items-center gap-2 font-semibold">
<input type="radio" name="privacyAgree" value="Y" checked={value === "Y"} onChange={() => onChange("Y")} />
예 (동의)
</label>
<label className="flex cursor-pointer items-center gap-2">
<input type="radio" name="privacyAgree" value="N" checked={value === "N"} onChange={() => onChange("N")} />
아니오 (미동의)
</label>
</div>
</div>
);
}
export default PrivacyConsent;