기능 추가 — 재조치 요청 (조치 기록 유지 재출동)
정비사 조치 완료 후 동일 문제 재발 시 관리자가 기존 기록을 보존한 채
재조치를 요청할 수 있는 기능 추가.
- DB: repairs.re_dispatch_requested/at, reports.re_dispatch_count 컬럼 추가
- 재조치 요청 엔드포인트 (POST /repairs/{id}/re-dispatch): 기존 repair에 플래그,
연결 신고를 pending으로 복원, re_dispatch_count 증가
- pending 상태 신고는 새 조치 생성으로 분기 (in_progress만 기존 수정 모드)
- report-detail: 조치승인·취소 사이에 "🔁 재조치 요청" 버튼, 이전 조치 이력 카드
- 정비사 대시보드: 재조치 건에 🔁 뱃지 및 강조 버튼색 표시
- 엑셀 export: 재조치횟수 컬럼 추가
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,7 @@ class Report(Base):
|
|||||||
closure_note = Column(Text)
|
closure_note = Column(Text)
|
||||||
closed_at = Column(TIMESTAMP)
|
closed_at = Column(TIMESTAMP)
|
||||||
closed_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
closed_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||||
|
re_dispatch_count = Column(Integer, default=0)
|
||||||
charger = relationship("Charger", back_populates="reports")
|
charger = relationship("Charger", back_populates="reports")
|
||||||
photos = relationship("ReportPhoto", back_populates="report", cascade="all, delete-orphan")
|
photos = relationship("ReportPhoto", back_populates="report", cascade="all, delete-orphan")
|
||||||
repair_links = relationship("RepairReport", back_populates="report")
|
repair_links = relationship("RepairReport", back_populates="report")
|
||||||
@@ -90,6 +91,8 @@ class Repair(Base):
|
|||||||
mechanic_lng = Column(Float)
|
mechanic_lng = Column(Float)
|
||||||
approved_at = Column(TIMESTAMP)
|
approved_at = Column(TIMESTAMP)
|
||||||
approved_by = Column(Integer, ForeignKey("users.id"))
|
approved_by = Column(Integer, ForeignKey("users.id"))
|
||||||
|
re_dispatch_requested = Column(Boolean, default=False)
|
||||||
|
re_dispatch_requested_at = Column(TIMESTAMP)
|
||||||
mechanic = relationship("User", foreign_keys=[mechanic_id])
|
mechanic = relationship("User", foreign_keys=[mechanic_id])
|
||||||
approver = relationship("User", foreign_keys=[approved_by])
|
approver = relationship("User", foreign_keys=[approved_by])
|
||||||
report_links = relationship("RepairReport", back_populates="repair", cascade="all, delete-orphan")
|
report_links = relationship("RepairReport", back_populates="repair", cascade="all, delete-orphan")
|
||||||
|
|||||||
@@ -88,13 +88,14 @@ def export_reports(db: Session = Depends(get_db), _=Depends(require_admin)):
|
|||||||
"조치시작","조치완료","작업소요시간","신고→완료소요시간",
|
"조치시작","조치완료","작업소요시간","신고→완료소요시간",
|
||||||
"문제원인(관리자)","비고","출장비부담주체","출장비금액(원)","출장비상태",
|
"문제원인(관리자)","비고","출장비부담주체","출장비금액(원)","출장비상태",
|
||||||
"처리담당자","처리일시","연결개선항목번호",
|
"처리담당자","처리일시","연결개선항목번호",
|
||||||
"상황종료사유","상황종료메모","상황종료일시","상황종료처리자"
|
"상황종료사유","상황종료메모","상황종료일시","상황종료처리자",
|
||||||
|
"재조치횟수"
|
||||||
]
|
]
|
||||||
style_header(ws, headers)
|
style_header(ws, headers)
|
||||||
|
|
||||||
col_widths = [10,14,14,14,18,14,12,12,12,22,12,24,14,16,16,10,16,12,
|
col_widths = [10,14,14,14,18,14,12,12,12,22,12,24,14,16,16,10,16,12,
|
||||||
12,14,16,24,16,16,12,18,24,24,16,12,12,12,16,18,
|
12,14,16,24,16,16,12,18,24,24,16,12,12,12,16,18,
|
||||||
18,24,16,14]
|
18,24,16,14,10]
|
||||||
for i, w in enumerate(col_widths, 1):
|
for i, w in enumerate(col_widths, 1):
|
||||||
ws.column_dimensions[ws.cell(1, i).column_letter].width = w
|
ws.column_dimensions[ws.cell(1, i).column_letter].width = w
|
||||||
|
|
||||||
@@ -148,6 +149,7 @@ def export_reports(db: Session = Depends(get_db), _=Depends(require_admin)):
|
|||||||
r.closure_note or "",
|
r.closure_note or "",
|
||||||
fmt_dt(r.closed_at),
|
fmt_dt(r.closed_at),
|
||||||
r.closer.name if r.closer else "",
|
r.closer.name if r.closer else "",
|
||||||
|
r.re_dispatch_count or 0,
|
||||||
]
|
]
|
||||||
for col, val in enumerate(row_data, 1):
|
for col, val in enumerate(row_data, 1):
|
||||||
ws.cell(row=row_num, column=col, value=val)
|
ws.cell(row=row_num, column=col, value=val)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ def _fmt_repair(repair: models.Repair) -> dict:
|
|||||||
"completed_at": repair.completed_at.isoformat() if repair.completed_at else None,
|
"completed_at": repair.completed_at.isoformat() if repair.completed_at else None,
|
||||||
"approved_at": repair.approved_at.isoformat() if repair.approved_at else None,
|
"approved_at": repair.approved_at.isoformat() if repair.approved_at else None,
|
||||||
"approved_by_name": repair.approver.name if repair.approved_by and repair.approver else None,
|
"approved_by_name": repair.approver.name if repair.approved_by and repair.approver else None,
|
||||||
|
"re_dispatch_requested": repair.re_dispatch_requested or False,
|
||||||
|
"re_dispatch_requested_at": repair.re_dispatch_requested_at.isoformat() if repair.re_dispatch_requested_at else None,
|
||||||
"photos_before": [{"id": p.id, "path": p.file_path} for p in repair.photos if p.photo_type == "before"],
|
"photos_before": [{"id": p.id, "path": p.file_path} for p in repair.photos if p.photo_type == "before"],
|
||||||
"photos_after": [{"id": p.id, "path": p.file_path} for p in repair.photos if p.photo_type == "after"],
|
"photos_after": [{"id": p.id, "path": p.file_path} for p in repair.photos if p.photo_type == "after"],
|
||||||
"reports": reports,
|
"reports": reports,
|
||||||
@@ -67,9 +69,9 @@ def pending_reports(db: Session = Depends(get_db),
|
|||||||
result = []
|
result = []
|
||||||
for r in q.all():
|
for r in q.all():
|
||||||
c = r.charger
|
c = r.charger
|
||||||
# in_progress 신고는 연결된 repair_id 포함 → 편집 모드로 연결
|
# in_progress 신고만 기존 repair 편집 모드; pending은 재조치 포함 새 조치 생성
|
||||||
repair_id = None
|
repair_id = None
|
||||||
if r.repair_links:
|
if r.status == "in_progress" and r.repair_links:
|
||||||
repair_id = r.repair_links[0].repair_id
|
repair_id = r.repair_links[0].repair_id
|
||||||
result.append({
|
result.append({
|
||||||
"id": r.id, "charger_id": r.charger_id,
|
"id": r.id, "charger_id": r.charger_id,
|
||||||
@@ -80,6 +82,7 @@ def pending_reports(db: Session = Depends(get_db),
|
|||||||
"reported_at": r.reported_at.isoformat(),
|
"reported_at": r.reported_at.isoformat(),
|
||||||
"occurred_at": r.occurred_at.isoformat() if r.occurred_at else None,
|
"occurred_at": r.occurred_at.isoformat() if r.occurred_at else None,
|
||||||
"repair_id": repair_id,
|
"repair_id": repair_id,
|
||||||
|
"re_dispatch_count": r.re_dispatch_count or 0,
|
||||||
"gps_lat": c.gps_lat if c else None,
|
"gps_lat": c.gps_lat if c else None,
|
||||||
"gps_lng": c.gps_lng if c else None,
|
"gps_lng": c.gps_lng if c else None,
|
||||||
"location_detail": c.location_detail if c else None,
|
"location_detail": c.location_detail if c else None,
|
||||||
@@ -263,6 +266,27 @@ def approve_repair(
|
|||||||
return {"ok": True, "improvement_id": target_imp_id}
|
return {"ok": True, "improvement_id": target_imp_id}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{repair_id}/re-dispatch")
|
||||||
|
def re_dispatch_repair(
|
||||||
|
repair_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: models.User = Depends(require_admin)
|
||||||
|
):
|
||||||
|
"""기존 조치 기록을 유지하며 재조치 요청 — 신고를 pending으로 되돌림"""
|
||||||
|
repair = db.query(models.Repair).filter_by(id=repair_id).first()
|
||||||
|
if not repair: raise HTTPException(404)
|
||||||
|
if repair.approved_at:
|
||||||
|
raise HTTPException(400, "이미 승인된 조치는 재조치 요청할 수 없습니다.")
|
||||||
|
repair.re_dispatch_requested = True
|
||||||
|
repair.re_dispatch_requested_at = datetime.now()
|
||||||
|
for link in repair.report_links:
|
||||||
|
if link.report:
|
||||||
|
link.report.status = "pending"
|
||||||
|
link.report.re_dispatch_count = (link.report.re_dispatch_count or 0) + 1
|
||||||
|
db.commit()
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{repair_id}")
|
@router.delete("/{repair_id}")
|
||||||
def cancel_repair(
|
def cancel_repair(
|
||||||
repair_id: int,
|
repair_id: int,
|
||||||
|
|||||||
@@ -203,11 +203,11 @@ def get_report(report_id: int, db: Session = Depends(get_db),
|
|||||||
r = db.query(models.Report).filter_by(id=report_id).first()
|
r = db.query(models.Report).filter_by(id=report_id).first()
|
||||||
if not r: raise HTTPException(404)
|
if not r: raise HTTPException(404)
|
||||||
result = _fmt_report(r, db)
|
result = _fmt_report(r, db)
|
||||||
# 수리 정보 포함
|
result["re_dispatch_count"] = r.re_dispatch_count or 0
|
||||||
if r.repair_links:
|
# 수리 정보 포함 — repair_links를 id 내림차순(최신 우선)으로 정렬
|
||||||
repair = r.repair_links[0].repair
|
def _fmt_one_repair(repair, include_cost=True):
|
||||||
cost = repair.cost
|
cost = repair.cost
|
||||||
result["repair"] = {
|
return {
|
||||||
"id": repair.id,
|
"id": repair.id,
|
||||||
"mechanic_name": repair.mechanic.name if repair.mechanic else None,
|
"mechanic_name": repair.mechanic.name if repair.mechanic else None,
|
||||||
"mechanic_company": repair.mechanic.company if repair.mechanic else None,
|
"mechanic_company": repair.mechanic.company if repair.mechanic else None,
|
||||||
@@ -222,6 +222,8 @@ def get_report(report_id: int, db: Session = Depends(get_db),
|
|||||||
"charger_lng": r.charger.gps_lng if r.charger else None,
|
"charger_lng": r.charger.gps_lng if r.charger else None,
|
||||||
"approved_at": repair.approved_at.isoformat() if repair.approved_at else None,
|
"approved_at": repair.approved_at.isoformat() if repair.approved_at else None,
|
||||||
"approved_by_name": repair.approver.name if repair.approved_by and repair.approver else None,
|
"approved_by_name": repair.approver.name if repair.approved_by and repair.approver else None,
|
||||||
|
"re_dispatch_requested": repair.re_dispatch_requested or False,
|
||||||
|
"re_dispatch_requested_at": repair.re_dispatch_requested_at.isoformat() if repair.re_dispatch_requested_at else None,
|
||||||
"photos_before": [p.file_path for p in repair.photos if p.photo_type == "before"],
|
"photos_before": [p.file_path for p in repair.photos if p.photo_type == "before"],
|
||||||
"photos_after": [p.file_path for p in repair.photos if p.photo_type == "after"],
|
"photos_after": [p.file_path for p in repair.photos if p.photo_type == "after"],
|
||||||
"cost": {
|
"cost": {
|
||||||
@@ -232,9 +234,19 @@ def get_report(report_id: int, db: Session = Depends(get_db),
|
|||||||
"cost_amount": cost.cost_amount,
|
"cost_amount": cost.cost_amount,
|
||||||
"cost_status": cost.cost_status,
|
"cost_status": cost.cost_status,
|
||||||
"manufacturer_name": cost.manufacturer.name if cost.manufacturer else None,
|
"manufacturer_name": cost.manufacturer.name if cost.manufacturer else None,
|
||||||
} if cost else None,
|
} if (cost and include_cost) else None,
|
||||||
"linked_improvements": _get_linked_improvements(repair, db),
|
"linked_improvements": _get_linked_improvements(repair, db) if include_cost else [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.repair_links:
|
||||||
|
sorted_links = sorted(r.repair_links, key=lambda l: l.repair_id, reverse=True)
|
||||||
|
result["repair"] = _fmt_one_repair(sorted_links[0].repair)
|
||||||
|
# 재조치로 인한 이전 조치 이력 (최신 제외, re_dispatch_requested=True인 것)
|
||||||
|
result["prev_repairs"] = [
|
||||||
|
_fmt_one_repair(link.repair, include_cost=False)
|
||||||
|
for link in sorted_links[1:]
|
||||||
|
if link.repair
|
||||||
|
]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _get_linked_improvements(repair, db):
|
def _get_linked_improvements(repair, db):
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ async function load() {
|
|||||||
]);
|
]);
|
||||||
const repair = r.repair;
|
const repair = r.repair;
|
||||||
const cost = repair?.cost;
|
const cost = repair?.cost;
|
||||||
|
const prevRepairs = r.prev_repairs || [];
|
||||||
|
|
||||||
document.getElementById('pageTitle').textContent = `신고 #${r.id} 상세`;
|
document.getElementById('pageTitle').textContent = `신고 #${r.id} 상세`;
|
||||||
|
|
||||||
@@ -529,8 +530,13 @@ async function load() {
|
|||||||
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
|
||||||
${repair.approved_at
|
${repair.approved_at
|
||||||
? `<span style="font-size:12px;background:#D1FAE5;color:#065F46;padding:3px 12px;border-radius:10px;font-weight:700;">✅ 승인완료 · ${repair.approved_by_name||''}</span>`
|
? `<span style="font-size:12px;background:#D1FAE5;color:#065F46;padding:3px 12px;border-radius:10px;font-weight:700;">✅ 승인완료 · ${repair.approved_by_name||''}</span>`
|
||||||
: `<button onclick="toggleApprovePanel()" id="approvePanelBtn" style="padding:5px 14px;border:none;border-radius:7px;background:var(--green);color:white;font-size:12px;font-weight:700;cursor:pointer;">✅ 조치 승인</button>`
|
: repair.re_dispatch_requested
|
||||||
|
? `<span style="font-size:12px;background:#FEF3C7;color:#92400E;padding:3px 12px;border-radius:10px;font-weight:700;">🔁 재조치 요청됨 · ${Auth.fmtDt(repair.re_dispatch_requested_at)}</span>`
|
||||||
|
: `<button onclick="toggleApprovePanel()" id="approvePanelBtn" style="padding:5px 14px;border:none;border-radius:7px;background:var(--green);color:white;font-size:12px;font-weight:700;cursor:pointer;">✅ 조치 승인</button>`
|
||||||
}
|
}
|
||||||
|
${!repair.approved_at && !repair.re_dispatch_requested
|
||||||
|
? `<button onclick="requestRedispatch(${repair.id})" style="padding:5px 14px;border:none;border-radius:7px;background:#F59E0B;color:white;font-size:12px;font-weight:700;cursor:pointer;">🔁 재조치 요청</button>`
|
||||||
|
: ''}
|
||||||
<button onclick="cancelRepair(${repair.id}, ${!!repair.approved_at})" style="padding:5px 14px;border:none;border-radius:7px;background:#FEE2E2;color:#991B1B;font-size:12px;font-weight:700;cursor:pointer;">🔄 조치취소</button>
|
<button onclick="cancelRepair(${repair.id}, ${!!repair.approved_at})" style="padding:5px 14px;border:none;border-radius:7px;background:#FEE2E2;color:#991B1B;font-size:12px;font-weight:700;cursor:pointer;">🔄 조치취소</button>
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
@@ -543,6 +549,8 @@ async function load() {
|
|||||||
<tr><td style="color:var(--gray4)">시작시각</td><td>${Auth.fmtDt(repair.started_at)}</td></tr>
|
<tr><td style="color:var(--gray4)">시작시각</td><td>${Auth.fmtDt(repair.started_at)}</td></tr>
|
||||||
<tr><td style="color:var(--gray4)">완료시각</td><td>${Auth.fmtDt(repair.completed_at)}</td></tr>
|
<tr><td style="color:var(--gray4)">완료시각</td><td>${Auth.fmtDt(repair.completed_at)}</td></tr>
|
||||||
<tr><td style="color:var(--gray4)">처리결과</td><td>${Auth.statusBadge(repair.result_status)}</td></tr>
|
<tr><td style="color:var(--gray4)">처리결과</td><td>${Auth.statusBadge(repair.result_status)}</td></tr>
|
||||||
|
${repair.re_dispatch_requested ? `
|
||||||
|
<tr><td style="color:var(--gray4)">재조치요청</td><td style="color:#92400E;font-weight:700;">🔁 재조치 요청됨 (${Auth.fmtDt(repair.re_dispatch_requested_at)})</td></tr>` : ''}
|
||||||
</table>
|
</table>
|
||||||
<div style="margin-top:12px">
|
<div style="margin-top:12px">
|
||||||
<label style="font-size:12px;font-weight:700;color:var(--navy2)">조치 전 사진</label>
|
<label style="font-size:12px;font-weight:700;color:var(--navy2)">조치 전 사진</label>
|
||||||
@@ -661,6 +669,35 @@ async function load() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${prevRepairs.length ? `
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">📋 이전 조치 이력 (재조치 전 기록 ${prevRepairs.length}건)</div>
|
||||||
|
${prevRepairs.map((pr, idx) => `
|
||||||
|
<details style="margin-bottom:10px;border:1px solid var(--gray2);border-radius:8px;overflow:hidden;">
|
||||||
|
<summary style="padding:10px 14px;cursor:pointer;font-size:13px;font-weight:700;color:var(--navy2);background:var(--gray1);list-style:none;display:flex;justify-content:space-between;align-items:center;">
|
||||||
|
<span>#${pr.id} · ${pr.mechanic_name||'?'} · ${Auth.fmtDt(pr.completed_at)}</span>
|
||||||
|
<span style="font-size:11px;background:#FEF3C7;color:#92400E;padding:2px 8px;border-radius:8px;">🔁 재조치 요청됨</span>
|
||||||
|
</summary>
|
||||||
|
<div style="padding:12px 14px;font-size:13px;">
|
||||||
|
<table class="no-hover" style="font-size:13px;">
|
||||||
|
<tr><td style="color:var(--gray4);width:100px">정비사</td><td>${pr.mechanic_name||'-'} (${pr.mechanic_company||'-'})</td></tr>
|
||||||
|
<tr><td style="color:var(--gray4)">조치유형</td><td>${(pr.repair_types||[]).join(', ')||'-'}</td></tr>
|
||||||
|
<tr><td style="color:var(--gray4)">조치내용</td><td>${pr.description||'-'}</td></tr>
|
||||||
|
<tr><td style="color:var(--gray4)">완료시각</td><td>${Auth.fmtDt(pr.completed_at)}</td></tr>
|
||||||
|
<tr><td style="color:var(--gray4)">처리결과</td><td>${Auth.statusBadge(pr.result_status)}</td></tr>
|
||||||
|
<tr><td style="color:var(--gray4)">재조치요청</td><td style="color:#92400E;font-weight:700;">${Auth.fmtDt(pr.re_dispatch_requested_at)}</td></tr>
|
||||||
|
</table>
|
||||||
|
${(pr.photos_before||[]).length || (pr.photos_after||[]).length ? `
|
||||||
|
<div style="margin-top:10px;">
|
||||||
|
${(pr.photos_before||[]).length ? `<div style="font-size:11px;font-weight:700;color:var(--gray4);margin-bottom:4px;">조치 전</div>
|
||||||
|
<div class="photo-preview">${(pr.photos_before||[]).map(p=>`<img src="${p}" onclick="window.open('${p}')" style="cursor:zoom-in">`).join('')}</div>` : ''}
|
||||||
|
${(pr.photos_after||[]).length ? `<div style="font-size:11px;font-weight:700;color:var(--gray4);margin:6px 0 4px;">조치 후</div>
|
||||||
|
<div class="photo-preview">${(pr.photos_after||[]).map(p=>`<img src="${p}" onclick="window.open('${p}')" style="cursor:zoom-in">`).join('')}</div>` : ''}
|
||||||
|
</div>` : ''}
|
||||||
|
</div>
|
||||||
|
</details>`).join('')}
|
||||||
|
</div>` : ''}
|
||||||
|
|
||||||
${costHtml}
|
${costHtml}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -914,6 +951,14 @@ async function cancelRepair(repairId, isApproved) {
|
|||||||
} catch(e) { alert('조치취소 오류: ' + e.message); }
|
} catch(e) { alert('조치취소 오류: ' + e.message); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function requestRedispatch(repairId) {
|
||||||
|
if (!confirm('재조치를 요청합니다.\n\n기존 조치 기록은 유지되며,\n연결된 신고가 정비사 목록에 다시 표시됩니다.\n\n계속하시겠습니까?')) return;
|
||||||
|
try {
|
||||||
|
await API.post('/repairs/' + repairId + '/re-dispatch', new FormData());
|
||||||
|
load();
|
||||||
|
} catch(e) { alert('재조치 요청 오류: ' + e.message); }
|
||||||
|
}
|
||||||
|
|
||||||
async function approveReport(id) {
|
async function approveReport(id) {
|
||||||
if (!confirm('신고를 승인하여 정비사에게 공개하시겠습니까?')) return;
|
if (!confirm('신고를 승인하여 정비사에게 공개하시겠습니까?')) return;
|
||||||
await API.patch(`/reports/${id}/approve`);
|
await API.patch(`/reports/${id}/approve`);
|
||||||
|
|||||||
@@ -157,14 +157,14 @@ function renderList() {
|
|||||||
: `/pages/mechanic/repair.html?charger_id=${r.charger_id}&report_id=${r.id}`;
|
: `/pages/mechanic/repair.html?charger_id=${r.charger_id}&report_id=${r.id}`;
|
||||||
return `
|
return `
|
||||||
<tr onclick="location.href='${href}'">
|
<tr onclick="location.href='${href}'">
|
||||||
<td>#${r.id}</td>
|
<td>#${r.id}${r.re_dispatch_count > 0 ? ' <span style="font-size:10px;background:#FEF3C7;color:#92400E;padding:1px 6px;border-radius:8px;font-weight:700;vertical-align:middle;">🔁재조치</span>' : ''}</td>
|
||||||
<td><strong>${r.charger_id}</strong><br><small>${r.charger_name||''}</small></td>
|
<td><strong>${r.charger_id}</strong><br><small>${r.charger_name||''}</small></td>
|
||||||
<td>${r.station_name||'-'}</td>
|
<td>${r.station_name||'-'}</td>
|
||||||
<td>${r.charger_type||'-'}</td>
|
<td>${r.charger_type||'-'}</td>
|
||||||
<td>${(r.issue_types||[]).join(', ')}</td>
|
<td>${(r.issue_types||[]).join(', ')}</td>
|
||||||
<td>${Auth.fmtDt(r.reported_at)}</td>
|
<td>${Auth.fmtDt(r.reported_at)}</td>
|
||||||
<td>${Auth.statusBadge(r.status)}</td>
|
<td>${Auth.statusBadge(r.status)}</td>
|
||||||
<td><a class="btn btn-primary btn-sm" href="${href}" onclick="event.stopPropagation()">조치</a></td>
|
<td><a class="btn ${r.re_dispatch_count > 0 ? 'btn-accent' : 'btn-primary'} btn-sm" href="${href}" onclick="event.stopPropagation()">조치</a></td>
|
||||||
</tr>`;
|
</tr>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user