Compact visit request form
This commit is contained in:
@@ -28,3 +28,45 @@
|
||||
|
||||
프론트 빌드는 샌드박스 안에서 `spawn EPERM`으로 한 차례 실패했으며, 승인 권한으로 재실행하여 성공을 확인했다.
|
||||
|
||||
## 배포 확인
|
||||
|
||||
2026-07-14 운영 URL `https://acs.apps.bokdev.in/visit-requests/new`의 HTML을 확인한 결과 아직 이전 asset을 참조하고 있었다.
|
||||
|
||||
| 구분 | JS asset | CSS asset |
|
||||
|---|---|---|
|
||||
| 운영 현재 | `index-B00mdy5F.js` | `index-teRe_8AF.css` |
|
||||
| 최신 로컬 빌드 | `index-BteIhXv6.js` | `index-CLolpzdX.css` |
|
||||
|
||||
판단: UI 수정 커밋 `7a404cd Update visit request form layout`이 원격 `main`에는 push되었지만, 운영 컨테이너에는 아직 반영되지 않았다. Coolify Redeploy가 필요하다.
|
||||
|
||||
## 2차 수정
|
||||
|
||||
2026-07-14 추가 요청에 따라 한 화면 표시를 목표로 폼 밀도를 높였다.
|
||||
|
||||
| 항목 | 반영 |
|
||||
|---|---|
|
||||
| 출입 전산실/추가 구역 | 같은 행에 배치 |
|
||||
| 출입 목적/작업명 | 같은 행에 배치 |
|
||||
| 연락처 레이블 | `연락처 *`로 축약 |
|
||||
| 차량번호 안내 | 레이블에서 제거하고 placeholder로 이동 |
|
||||
| 출입 전산실 안내 | 레이블에서 제거 |
|
||||
| 작업명 안내 | 레이블에서 제거하고 placeholder를 `작업 내용을 구체적으로 입력하세요`로 변경 |
|
||||
| 퇴실예정일시 | `퇴실 예정일시`로 변경 |
|
||||
| 직원명 | `이름`으로 변경 |
|
||||
| 출입통제담당자 연락처 | 값/범례를 `내선번호/휴대폰번호`로 변경 |
|
||||
| 현장감시자1과2 | `현장감시자`로 변경 |
|
||||
| 현장감시자1 범례 | `IT센터 사무보조원 (자동 지정)`으로 변경 |
|
||||
|
||||
2차 수정 검증:
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| `npm run typecheck` | 성공 |
|
||||
| `npm --prefix frontend run build` | 성공 |
|
||||
|
||||
2차 최신 로컬 빌드 asset:
|
||||
|
||||
| 구분 | asset |
|
||||
|---|---|
|
||||
| JS | `index-Cc3Y9nv_.js` |
|
||||
| CSS | `index-V1wV5EGT.css` |
|
||||
|
||||
@@ -154,7 +154,7 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
<input value={form.company} onChange={update('company')} />
|
||||
</label>
|
||||
<label className="field">
|
||||
<span>연락처 <b className="required">*</b><em className="hint-inline">: 입력형식은 010-0000-0000 로 작성해 주세요.</em></span>
|
||||
<span>연락처 <b className="required">*</b></span>
|
||||
<input type="tel" value={form.contact} onChange={updateFormattedContact('contact')} placeholder="010-0000-0000" />
|
||||
</label>
|
||||
<label className="field">
|
||||
@@ -162,13 +162,8 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
<input className="ime-en" type="email" value={form.email} onChange={update('email')} placeholder="name@example.com" />
|
||||
</label>
|
||||
|
||||
<label className="field span-2">
|
||||
<span>차량번호 <em className="hint-inline">: 차량번호가 5부제에 해당될 경우 출입이 제한됩니다.</em></span>
|
||||
<input className="ime-ko" value={form.vehicleNo} onChange={update('vehicleNo')} />
|
||||
</label>
|
||||
|
||||
<div className="field span-2">
|
||||
<span>출입 전산실 <em className="hint-inline">: 선택한 전산실마다 출입증(QR)이 각각 발급됩니다.</em></span>
|
||||
<div className="field">
|
||||
<span>출입 전산실</span>
|
||||
<div className="checkbox-row">
|
||||
{SERVER_ROOM_OPTIONS.map((z) => (
|
||||
<label key={z} className="checkbox-inline">
|
||||
@@ -195,7 +190,12 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
<span>기타 구역 입력 <b className="required">*</b></span>
|
||||
<input value={form.roomEtc} onChange={update('roomEtc')} placeholder="추가 구역을 입력하세요" />
|
||||
</label>
|
||||
) : <div />}
|
||||
) : null}
|
||||
|
||||
<label className="field">
|
||||
<span>차량번호</span>
|
||||
<input className="ime-ko" value={form.vehicleNo} onChange={update('vehicleNo')} placeholder="차량번호가 5부제에 해당될 경우 출입이 제한됩니다." />
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
<span>출입 목적 <b className="required">*</b></span>
|
||||
@@ -209,11 +209,11 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
<span>기타 목적 입력 <b className="required">*</b></span>
|
||||
<input value={form.purposeEtc} onChange={update('purposeEtc')} placeholder="출입 목적을 입력하세요" />
|
||||
</label>
|
||||
) : <div />}
|
||||
) : null}
|
||||
|
||||
<label className="field span-2">
|
||||
<span>작업명 <em className="hint-inline">: 구체적인 작업 내용 (예: Active-Active 회의)</em></span>
|
||||
<input className="ime-ko" value={form.workName} onChange={update('workName')} placeholder="작업 내용을 입력하세요 (선택)" />
|
||||
<label className="field">
|
||||
<span>작업명</span>
|
||||
<input className="ime-ko" value={form.workName} onChange={update('workName')} placeholder="작업 내용을 구체적으로 입력하세요" />
|
||||
</label>
|
||||
|
||||
<label className="field">
|
||||
@@ -225,11 +225,11 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
/>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span>퇴실예정일시 <b className="required">*</b></span>
|
||||
<span>퇴실 예정일시 <b className="required">*</b></span>
|
||||
<DateTimePicker
|
||||
value={form.visitTo}
|
||||
onChange={(v) => setForm((f) => ({ ...f, visitTo: v }))}
|
||||
placeholder="퇴실예정일시 선택"
|
||||
placeholder="퇴실 예정일시 선택"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -239,7 +239,7 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
<legend>출입통제담당자</legend>
|
||||
<div className="group-grid">
|
||||
<label className="field">
|
||||
<span>직원명</span>
|
||||
<span>이름</span>
|
||||
<input value={user?.fullName ?? ''} readOnly />
|
||||
</label>
|
||||
<label className="field">
|
||||
@@ -247,15 +247,15 @@ export const VisitRequestFormPage: React.FC = () => {
|
||||
<input value={user?.department ?? ''} readOnly />
|
||||
</label>
|
||||
<label className="field">
|
||||
<span>연락처 <em className="hint-inline">: 내선번호/휴대폰번호</em></span>
|
||||
<input value={user?.email ?? ''} readOnly />
|
||||
<span>연락처</span>
|
||||
<input value="" placeholder="내선번호/휴대폰번호" readOnly />
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset className="form-group span-2">
|
||||
<legend>현장감시자1과2</legend>
|
||||
<div className="subsection-title">현장감시자1 <em className="hint-inline">: 고정 인원 (자동 지정)</em></div>
|
||||
<legend>현장감시자</legend>
|
||||
<div className="subsection-title">현장감시자1 <em className="hint-inline">: IT센터 사무보조원 (자동 지정)</em></div>
|
||||
<div className="group-grid">
|
||||
<label className="field">
|
||||
<span>이름</span>
|
||||
|
||||
@@ -76,7 +76,7 @@ a { color: inherit; text-decoration: none; }
|
||||
padding: 2px 6px; border-radius: 6px;
|
||||
}
|
||||
|
||||
.content { width: 100%; max-width: none; margin: 0; padding: 28px; }
|
||||
.content { width: 100%; max-width: none; margin: 0; padding: 12px 18px; }
|
||||
|
||||
/* ===== Center (auth) ===== */
|
||||
.center-screen {
|
||||
@@ -108,19 +108,19 @@ a { color: inherit; text-decoration: none; }
|
||||
.card h3 { margin-top: 0; }
|
||||
.page-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-head h2 { margin: 0; }
|
||||
.page-head h2 { margin: 0; font-size: 22px; }
|
||||
.head-actions { display: flex; gap: 8px; }
|
||||
|
||||
/* ===== Fields / forms ===== */
|
||||
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
|
||||
.field > span { font-size: 13px; font-weight: 600; color: #334155; }
|
||||
.field input, .field select, .field textarea {
|
||||
padding: 10px 12px;
|
||||
padding: 7px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
}
|
||||
.field input:focus, .field select:focus { outline: 2px solid #bfdbfe; border-color: var(--primary); }
|
||||
@@ -173,46 +173,48 @@ a { color: inherit; text-decoration: none; }
|
||||
gap: 0 18px;
|
||||
}
|
||||
.form-grid .span-2 { grid-column: 1 / -1; }
|
||||
.visit-form { gap: 16px; }
|
||||
.visit-form { gap: 8px; padding: 12px; margin-bottom: 0; }
|
||||
.form-group {
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
padding: 18px 18px 4px;
|
||||
padding: 10px 12px 0;
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
}
|
||||
.form-group > legend {
|
||||
padding: 0 8px;
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
color: #0f172a;
|
||||
}
|
||||
.group-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(360px, 1fr));
|
||||
gap: 0 18px;
|
||||
grid-template-columns: repeat(3, minmax(280px, 1fr));
|
||||
gap: 0 14px;
|
||||
}
|
||||
.group-grid .span-2 { grid-column: 1 / -1; }
|
||||
.visit-form .field {
|
||||
display: grid;
|
||||
grid-template-columns: 180px minmax(0, 1fr);
|
||||
grid-template-columns: 116px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
column-gap: 12px;
|
||||
column-gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.visit-form .field > span {
|
||||
min-width: 0;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.visit-form .field.span-2 {
|
||||
grid-template-columns: 180px minmax(0, 1fr);
|
||||
grid-template-columns: 116px minmax(0, 1fr);
|
||||
}
|
||||
.required { color: var(--red); }
|
||||
.subsection-title {
|
||||
margin: 4px 0 12px;
|
||||
padding-top: 10px;
|
||||
margin: 2px 0 8px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
font-weight: 700;
|
||||
color: #334155;
|
||||
font-size: 13px;
|
||||
}
|
||||
.form-group .subsection-title:first-of-type {
|
||||
margin-top: 0;
|
||||
@@ -237,15 +239,15 @@ a { color: inherit; text-decoration: none; }
|
||||
background: #f8fafc;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.consent-label { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; font-size: 13px; color: #334155; line-height: 1.5; }
|
||||
.consent-label { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; font-size: 12px; color: #334155; line-height: 1.35; }
|
||||
.consent-label input { margin-top: 2px; width: 16px; height: 16px; flex-shrink: 0; }
|
||||
|
||||
/* Inline checkbox group (e.g. 전산실 다중 선택) */
|
||||
.checkbox-row { display: flex; flex-wrap: wrap; gap: 18px; padding: 8px 2px; }
|
||||
.checkbox-inline { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; color: #334155; }
|
||||
.checkbox-row { display: flex; flex-wrap: wrap; gap: 10px; padding: 4px 2px; }
|
||||
.checkbox-inline { display: flex; align-items: center; gap: 6px; cursor: pointer; font-size: 13px; color: #334155; }
|
||||
.checkbox-inline input { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
|
||||
/* ===== Buttons ===== */
|
||||
|
||||
Reference in New Issue
Block a user