Files
ev-charger-as/frontend/static/js/api.js
byun 2e8751ea6c 기능 추가 및 버그 수정 — 처리시간 지표, 대시보드 차트, UI 개선
## 처리시간 지표
- 업무시간 기준(09-18 평일) / 공휴일 제외 24h / 달력 기준 3가지 모드 선택
- 공휴일 DB 관리 (holidays 테이블, 수동 등록·삭제·일괄 추가)
- 2026년 공휴일 등록 지원
- 설정 페이지에서 라디오 버튼으로 모드 선택

## 대시보드 차트
- 월별 평균 처리시간 막대 차트 추가
- 월별 신고 접수 건수 누적 막대 차트 추가
- 월별 → 일별 드릴다운 (막대 클릭 시 해당 월의 일별 차트로 전환)
- 일별 막대 클릭 시 처리 완료/신고 접수 상세 내역 모달
- 충전기별 누적 고장 건수 Top 10 수평 막대 차트 추가

## 신고 목록
- # 컬럼을 DB PK 대신 현재 목록 순서(1, 2, 3…)로 표시
- 엑셀 export 접수번호도 순차번호로 변경

## 모바일 네비게이션 버그 수정
- 모바일에서 가로 오버플로우 시 nav가 body 넓이로 늘어나
  햄버거 버튼이 화면 밖으로 밀리는 문제 수정
- nav를 position:fixed + body padding-top:54px 로 변경 (전체 페이지 적용)
- 충전기 관리·신고 목록 페이지 지도 컨테이너에 isolation:isolate 적용

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 06:52:56 +09:00

73 lines
2.4 KiB
JavaScript

const API = (() => {
const BASE = '/api';
function token() { return localStorage.getItem('ev_token') || ''; }
async function req(method, path, body = null, isForm = false) {
const headers = {};
if (token()) headers['Authorization'] = 'Bearer ' + token();
let fetchBody = null;
if (body) {
if (isForm) {
fetchBody = body;
} else {
headers['Content-Type'] = 'application/json';
fetchBody = JSON.stringify(body);
}
}
const res = await fetch(BASE + path, { method, headers, body: fetchBody });
if (res.status === 401) { Auth.logout(); return; }
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: '오류가 발생했습니다.' }));
throw new Error(err.detail || '오류');
}
const ct = res.headers.get('content-type') || '';
if (ct.includes('spreadsheet') || ct.includes('octet') || ct.includes('excel')) {
return res.blob();
}
return res.json().catch(() => ({}));
}
// 엑셀 다운로드 전용 함수 — 인증 토큰 포함, 에러 처리 강화
async function download(path, filename) {
try {
const headers = {};
if (token()) headers['Authorization'] = 'Bearer ' + token();
const res = await fetch(BASE + path, { method: 'GET', headers });
if (res.status === 401) { Auth.logout(); return; }
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: '다운로드 오류' }));
throw new Error(err.detail || '다운로드 오류');
}
const blob = await res.blob();
if (!blob || blob.size === 0) {
alert('데이터가 없어 엑셀 파일을 생성할 수 없습니다.');
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch(e) {
alert('엑셀 다운로드 실패: ' + e.message);
}
}
return {
get: (path) => req('GET', path),
post: (path, body) => req('POST', path, body, body instanceof FormData),
put: (path, body) => req('PUT', path, body, body instanceof FormData),
patch: (path, body) => req('PATCH', path, body, body instanceof FormData),
delete: (path, body) => req('DELETE', path, body ?? null),
download,
};
})();