초기 커밋 - EV AS 관리 시스템
This commit is contained in:
214
frontend/static/pages/report.html.bak
Normal file
214
frontend/static/pages/report.html.bak
Normal file
@@ -0,0 +1,214 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>고장 신고</title>
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<style>
|
||||
body{background:var(--gray1);}
|
||||
.report-wrap{max-width:480px;margin:0 auto;padding:20px 16px 40px;}
|
||||
.charger-info{background:var(--navy);color:white;border-radius:10px;padding:16px 18px;margin-bottom:18px;}
|
||||
.charger-info h2{font-size:16px;margin-bottom:8px;color:var(--accent);}
|
||||
.charger-info .row{display:flex;justify-content:space-between;font-size:12px;color:rgba(255,255,255,.75);margin-top:4px;}
|
||||
.section{background:white;border-radius:10px;padding:18px;margin-bottom:14px;box-shadow:0 2px 6px rgba(0,0,0,.06);}
|
||||
.section h3{font-size:14px;font-weight:700;color:var(--navy);border-left:3px solid var(--accent);padding-left:9px;margin-bottom:12px;}
|
||||
.issue-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px;}
|
||||
.issue-btn{padding:10px;border:1px solid var(--gray3);border-radius:7px;background:white;cursor:pointer;font-size:13px;text-align:center;transition:all .15s;}
|
||||
.issue-btn.sel{background:#E3EDFF;border-color:var(--accent);font-weight:700;color:var(--blue);}
|
||||
#submitBtn{margin-top:4px;}
|
||||
#resultBox{background:var(--navy);color:white;border-radius:10px;padding:24px;text-align:center;display:none;}
|
||||
#resultBox h2{color:var(--green);font-size:20px;margin-bottom:10px;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="report-wrap">
|
||||
<div id="chargerInfo" class="charger-info">
|
||||
<h2>⚡ 충전기 정보 로딩 중...</h2>
|
||||
</div>
|
||||
|
||||
<div id="mainForm">
|
||||
<div class="section">
|
||||
<h3>📍 신고 위치</h3>
|
||||
<div id="gpsStatus" class="alert alert-info">위치 정보 수집 중...</div>
|
||||
<input type="hidden" id="gpsLat">
|
||||
<input type="hidden" id="gpsLng">
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>🔴 문제 유형 <span style="color:var(--red);font-size:11px">* 1개 이상 선택</span></h3>
|
||||
<div class="issue-grid" id="issueGrid"></div>
|
||||
<div id="errorCodeWrap" style="margin-top:10px;display:none;">
|
||||
<input type="text" id="errorCode" placeholder="에러 코드 입력 (예: E001)">
|
||||
</div>
|
||||
<div id="etcWrap" style="margin-top:10px;display:none;">
|
||||
<input type="text" id="etcText" placeholder="기타 문제 내용 입력">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>🕐 문제 발생 시각</h3>
|
||||
<input type="datetime-local" id="occurredAt">
|
||||
<div style="font-size:11px;color:var(--gray4);margin-top:4px">언제부터 문제가 발생했나요? (선택)</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>📷 사진 첨부</h3>
|
||||
<div class="form-group">
|
||||
<label>충전기 사진 <span style="color:var(--red)">*필수</span></label>
|
||||
<input type="file" id="chargerPhoto" accept="image/*" capture="environment" multiple>
|
||||
<div class="photo-preview" id="chargerPreview"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>차량 사진 (선택)</label>
|
||||
<input type="file" id="carPhoto" accept="image/*" capture="environment" multiple>
|
||||
<div class="photo-preview" id="carPreview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>📝 상세 설명 (선택)</h3>
|
||||
<textarea id="detail" placeholder="문제 상황을 자세히 설명해 주세요." rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>📞 연락처 (선택)</h3>
|
||||
<input type="tel" id="contact" placeholder="010-0000-0000">
|
||||
<div style="margin-top:10px;display:flex;align-items:flex-start;gap:8px;">
|
||||
<input type="checkbox" id="consent" style="width:auto;margin-top:2px;accent-color:var(--accent)">
|
||||
<label for="consent" style="font-size:12px;color:var(--gray4);cursor:pointer">
|
||||
개인정보(연락처)를 AS 처리 목적으로 수집·이용하는 것에 동의합니다.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="formErr" class="alert alert-danger" style="display:none"></div>
|
||||
<button class="btn btn-primary btn-lg" id="submitBtn">신고 접수하기</button>
|
||||
</div>
|
||||
|
||||
<div id="resultBox">
|
||||
<h2>✅ 신고 접수 완료</h2>
|
||||
<p id="resultMsg"></p>
|
||||
<p style="margin-top:12px;font-size:13px;color:rgba(255,255,255,.6)">
|
||||
빠른 시간 내에 처리하겠습니다.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/api.js"></script>
|
||||
<script>
|
||||
const ISSUES = [
|
||||
{key:'충전불가',label:'⚡ 충전 불가'},
|
||||
{key:'화면오류',label:'🖥 화면 오류'},
|
||||
{key:'케이블불량',label:'🔌 케이블 불량'},
|
||||
{key:'결제오류',label:'💳 결제 오류'},
|
||||
{key:'외관손상',label:'🔨 외관 손상'},
|
||||
{key:'에러발생',label:'⚠️ 에러 발생'},
|
||||
{key:'기타',label:'📋 기타'},
|
||||
];
|
||||
const selected = new Set();
|
||||
const chargerId = location.pathname.split('/').pop();
|
||||
|
||||
// 충전기 정보 로드
|
||||
async function loadCharger() {
|
||||
try {
|
||||
const c = await fetch('/api/chargers/' + chargerId).then(r => r.json());
|
||||
document.getElementById('chargerInfo').innerHTML = `
|
||||
<h2>⚡ ${c.name}</h2>
|
||||
<div class="row"><span>충전소</span><span>${c.station_name}</span></div>
|
||||
<div class="row"><span>종류</span><span>${c.charger_type || '-'}</span></div>
|
||||
<div class="row"><span>CPO</span><span>${c.cpo_name || '-'}</span></div>
|
||||
<div class="row"><span>설치일</span><span>${c.installed_at || '-'}</span></div>
|
||||
`;
|
||||
} catch { document.getElementById('chargerInfo').innerHTML = '<h2 style="color:#ff8888">충전기 정보를 불러올 수 없습니다.</h2>'; }
|
||||
}
|
||||
|
||||
// GPS 수집
|
||||
navigator.geolocation?.getCurrentPosition(pos => {
|
||||
document.getElementById('gpsLat').value = pos.coords.latitude;
|
||||
document.getElementById('gpsLng').value = pos.coords.longitude;
|
||||
document.getElementById('gpsStatus').textContent = `📍 위치 수집 완료 (${pos.coords.latitude.toFixed(5)}, ${pos.coords.longitude.toFixed(5)})`;
|
||||
document.getElementById('gpsStatus').className = 'alert alert-success';
|
||||
}, () => {
|
||||
document.getElementById('gpsStatus').textContent = '위치 정보를 가져올 수 없습니다. (수동 신고로 진행)';
|
||||
document.getElementById('gpsStatus').className = 'alert alert-warn';
|
||||
});
|
||||
|
||||
// 문제 유형 버튼
|
||||
const grid = document.getElementById('issueGrid');
|
||||
ISSUES.forEach(issue => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'issue-btn';
|
||||
btn.textContent = issue.label;
|
||||
btn.onclick = () => {
|
||||
if (selected.has(issue.key)) { selected.delete(issue.key); btn.classList.remove('sel'); }
|
||||
else { selected.add(issue.key); btn.classList.add('sel'); }
|
||||
document.getElementById('errorCodeWrap').style.display = selected.has('에러발생') ? 'block' : 'none';
|
||||
document.getElementById('etcWrap').style.display = selected.has('기타') ? 'block' : 'none';
|
||||
};
|
||||
grid.appendChild(btn);
|
||||
});
|
||||
|
||||
// 사진 미리보기
|
||||
function setupPreview(inputId, previewId) {
|
||||
document.getElementById(inputId).addEventListener('change', function() {
|
||||
const preview = document.getElementById(previewId);
|
||||
preview.innerHTML = '';
|
||||
Array.from(this.files).forEach(f => {
|
||||
const img = document.createElement('img');
|
||||
img.src = URL.createObjectURL(f);
|
||||
preview.appendChild(img);
|
||||
});
|
||||
});
|
||||
}
|
||||
setupPreview('chargerPhoto', 'chargerPreview');
|
||||
setupPreview('carPhoto', 'carPreview');
|
||||
|
||||
// 제출
|
||||
document.getElementById('submitBtn').addEventListener('click', async () => {
|
||||
const issues = [...selected];
|
||||
if (issues.length === 0) { showErr('문제 유형을 1개 이상 선택해 주세요.'); return; }
|
||||
const chargerPhotos = document.getElementById('chargerPhoto').files;
|
||||
if (chargerPhotos.length === 0) { showErr('충전기 사진을 첨부해 주세요.'); return; }
|
||||
const consent = document.getElementById('consent').checked;
|
||||
const contact = document.getElementById('contact').value.trim();
|
||||
if (contact && !consent) { showErr('연락처를 입력한 경우 개인정보 수집 동의가 필요합니다.'); return; }
|
||||
|
||||
document.getElementById('submitBtn').disabled = true;
|
||||
document.getElementById('submitBtn').textContent = '접수 중...';
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('charger_id', chargerId);
|
||||
fd.append('issue_types', JSON.stringify(issues));
|
||||
fd.append('issue_detail', document.getElementById('detail').value);
|
||||
fd.append('error_code', document.getElementById('errorCode').value);
|
||||
fd.append('occurred_at', document.getElementById('occurredAt').value || '');
|
||||
fd.append('contact', contact);
|
||||
fd.append('consent', consent);
|
||||
fd.append('gps_lat', document.getElementById('gpsLat').value || '');
|
||||
fd.append('gps_lng', document.getElementById('gpsLng').value || '');
|
||||
Array.from(chargerPhotos).forEach(f => fd.append('photos', f));
|
||||
Array.from(document.getElementById('carPhoto').files).forEach(f => fd.append('photos', f));
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/reports', { method:'POST', body:fd });
|
||||
if (!res.ok) { const e = await res.json(); throw new Error(e.detail); }
|
||||
const data = await res.json();
|
||||
document.getElementById('mainForm').style.display = 'none';
|
||||
document.getElementById('resultBox').style.display = 'block';
|
||||
document.getElementById('resultMsg').textContent = `접수번호: #${data.id}`;
|
||||
} catch(e) {
|
||||
showErr(e.message);
|
||||
document.getElementById('submitBtn').disabled = false;
|
||||
document.getElementById('submitBtn').textContent = '신고 접수하기';
|
||||
}
|
||||
});
|
||||
|
||||
function showErr(msg) {
|
||||
const el = document.getElementById('formErr');
|
||||
el.textContent = msg; el.style.display = 'block';
|
||||
el.scrollIntoView({behavior:'smooth'});
|
||||
}
|
||||
|
||||
loadCharger();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user