기능 개선 — 조치 시작/완료 시각 직접 입력
정비사가 조치 입력 시 시작·완료 시각을 직접 수정할 수 있도록 변경. 현장 처리 후 나중에 입력하는 경우 실제 조치 시간을 정확히 기록 가능. - repair.html: 자동기록 안내문구 → datetime-local 입력 필드 2개로 교체 (페이지 로드 시 현재 시각 기본 설정, 편집 모드에서 기존 값 복원) - repairs.py: POST/PUT 엔드포인트에 started_at_input / completed_at_input Form 파라미터 추가, 미입력 시 datetime.now() 유지 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -125,12 +125,21 @@ def get_repair(repair_id: int, db: Session = Depends(get_db),
|
||||
return _fmt_repair(repair)
|
||||
|
||||
|
||||
def _parse_dt(s: Optional[str]) -> Optional[datetime]:
|
||||
if not s: return None
|
||||
try:
|
||||
return datetime.fromisoformat(s)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
@router.post("")
|
||||
async def create_repair(
|
||||
report_ids: str = Form(...),
|
||||
repair_types: str = Form(...),
|
||||
description: str = Form(...),
|
||||
result_status: str = Form("done"),
|
||||
started_at_input: Optional[str] = Form(None),
|
||||
completed_at_input: Optional[str] = Form(None),
|
||||
mechanic_lat: Optional[float] = Form(None),
|
||||
mechanic_lng: Optional[float] = Form(None),
|
||||
photos_before: List[UploadFile] = File(default=[]),
|
||||
@@ -138,13 +147,14 @@ async def create_repair(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(require_mechanic)
|
||||
):
|
||||
now = datetime.now()
|
||||
rids = json.loads(report_ids)
|
||||
repair = models.Repair(
|
||||
mechanic_id=current_user.id,
|
||||
repair_types=json.loads(repair_types),
|
||||
description=description,
|
||||
started_at=datetime.now(),
|
||||
completed_at=datetime.now(),
|
||||
started_at=_parse_dt(started_at_input) or now,
|
||||
completed_at=_parse_dt(completed_at_input) or now,
|
||||
result_status=result_status,
|
||||
mechanic_lat=mechanic_lat,
|
||||
mechanic_lng=mechanic_lng,
|
||||
@@ -175,6 +185,8 @@ async def update_repair(
|
||||
repair_types: str = Form(...),
|
||||
description: str = Form(...),
|
||||
result_status: str = Form("done"),
|
||||
started_at_input: Optional[str] = Form(None),
|
||||
completed_at_input: Optional[str] = Form(None),
|
||||
mechanic_lat: Optional[float] = Form(None),
|
||||
mechanic_lng: Optional[float] = Form(None),
|
||||
photos_before: List[UploadFile] = File(default=[]),
|
||||
@@ -192,7 +204,9 @@ async def update_repair(
|
||||
repair.repair_types = json.loads(repair_types)
|
||||
repair.description = description
|
||||
repair.result_status = result_status
|
||||
repair.completed_at = datetime.now()
|
||||
if started_at_input:
|
||||
repair.started_at = _parse_dt(started_at_input) or repair.started_at
|
||||
repair.completed_at = _parse_dt(completed_at_input) or datetime.now()
|
||||
if mechanic_lat is not None: repair.mechanic_lat = mechanic_lat
|
||||
if mechanic_lng is not None: repair.mechanic_lng = mechanic_lng
|
||||
|
||||
|
||||
@@ -81,8 +81,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" style="margin-bottom:14px;">
|
||||
🕐 조치 시작 시간: <strong id="startedAt"></strong> (자동 기록)
|
||||
<div class="form-row" style="margin-bottom:14px;">
|
||||
<div class="form-group" style="margin-bottom:0">
|
||||
<label>🕐 조치 시작 시각 <span style="font-size:11px;color:var(--gray4);font-weight:400">(직접 수정 가능)</span></label>
|
||||
<input type="datetime-local" id="startedAt" style="width:100%">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom:0">
|
||||
<label>🏁 조치 완료 시각 <span style="font-size:11px;color:var(--gray4);font-weight:400">(직접 수정 가능)</span></label>
|
||||
<input type="datetime-local" id="completedAt" style="width:100%">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="gpsStatus" class="alert alert-info" style="margin-bottom:14px;">
|
||||
@@ -139,8 +146,15 @@ const chargerId = params.get('charger_id'); // 신규 모드
|
||||
const initReportId = params.get('report_id');
|
||||
const isEditMode = !!repairId;
|
||||
|
||||
// datetime-local 입력값 포맷: "YYYY-MM-DDTHH:mm"
|
||||
function toLocalDtInput(date) {
|
||||
const d = new Date(date);
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
}
|
||||
const startTime = new Date();
|
||||
document.getElementById('startedAt').textContent = startTime.toLocaleString('ko-KR');
|
||||
document.getElementById('startedAt').value = toLocalDtInput(startTime);
|
||||
document.getElementById('completedAt').value = toLocalDtInput(startTime);
|
||||
|
||||
const selectedReports = new Set();
|
||||
if (initReportId) selectedReports.add(parseInt(initReportId));
|
||||
@@ -222,6 +236,8 @@ async function loadEdit() {
|
||||
// 폼 미리채우기 — 조치유형 동적 로드 후 체크 복원
|
||||
await loadRepairTypes(repair.repair_types || []);
|
||||
document.getElementById('description').value = repair.description || '';
|
||||
if (repair.started_at) document.getElementById('startedAt').value = toLocalDtInput(repair.started_at);
|
||||
if (repair.completed_at) document.getElementById('completedAt').value = toLocalDtInput(repair.completed_at);
|
||||
const sel = document.getElementById('resultStatus');
|
||||
if (repair.result_status && sel.querySelector(`option[value="${repair.result_status}"]`))
|
||||
sel.value = repair.result_status;
|
||||
@@ -318,6 +334,10 @@ async function submitForm(isDone) {
|
||||
fd.append('repair_types', JSON.stringify(types));
|
||||
fd.append('description', desc);
|
||||
fd.append('result_status', resultStatus);
|
||||
const startedAtVal = document.getElementById('startedAt').value;
|
||||
const completedAtVal = document.getElementById('completedAt').value;
|
||||
if (startedAtVal) fd.append('started_at_input', startedAtVal);
|
||||
if (completedAtVal) fd.append('completed_at_input', completedAtVal);
|
||||
if (lat) fd.append('mechanic_lat', lat);
|
||||
if (lng) fd.append('mechanic_lng', lng);
|
||||
Array.from(document.getElementById('photosBefore').files).forEach(f => fd.append('photos_before', f));
|
||||
|
||||
Reference in New Issue
Block a user