Files
ev-charger-as/backend/routers/settings.py
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

128 lines
4.9 KiB
Python

from fastapi import APIRouter, Depends, Form, Request
from sqlalchemy.orm import Session
from datetime import datetime
from typing import Optional
from database import get_db
import models, json
from auth import require_admin
router = APIRouter(prefix="/api/settings", tags=["settings"])
# 이미지 설정 기본값
IMAGE_DEFAULTS = {
"image_compress_enabled": "true",
"image_max_px": "1024",
"image_quality": "85",
}
def upsert(db, key, value):
s = db.query(models.SystemSetting).filter_by(key=key).first()
if s:
s.value = value
s.updated_at = datetime.now()
else:
db.add(models.SystemSetting(key=key, value=value))
# ── 공개 엔드포인트: 이미지 설정만 반환 (인증 불필요 — 신고 페이지에서 사용)
@router.get("/public")
def get_public_settings(db: Session = Depends(get_db)):
rows = db.query(models.SystemSetting).filter(
models.SystemSetting.key.in_(IMAGE_DEFAULTS.keys())
).all()
result = dict(IMAGE_DEFAULTS) # 기본값으로 채운 뒤
for r in rows:
result[r.key] = r.value # DB 값으로 덮어쓰기
return {
"image_compress_enabled": result["image_compress_enabled"] == "true",
"image_max_px": int(result["image_max_px"]),
"image_quality": int(result["image_quality"]),
}
# ── 관리자 전체 설정 조회
@router.get("")
def get_settings(db: Session = Depends(get_db), _=Depends(require_admin)):
rows = db.query(models.SystemSetting).all()
result = dict(IMAGE_DEFAULTS)
for r in rows:
result[r.key] = r.value
return result
DEFAULT_ISSUE_TYPES = [
{"key": "충전불가", "label": "⚡ 충전 불가"},
{"key": "화면오류", "label": "🖥 화면 오류"},
{"key": "케이블불량", "label": "🔌 케이블 불량"},
{"key": "결제오류", "label": "💳 결제 오류"},
{"key": "외관손상", "label": "🔨 외관 손상"},
{"key": "에러발생", "label": "⚠️ 에러 발생"},
{"key": "기타", "label": "📋 기타"},
]
@router.get("/issue-types")
def get_issue_types(db: Session = Depends(get_db)):
row = db.query(models.SystemSetting).filter_by(key="issue_types").first()
if row:
return json.loads(row.value)
return DEFAULT_ISSUE_TYPES
@router.put("/issue-types")
async def update_issue_types(request: Request, db: Session = Depends(get_db), _=Depends(require_admin)):
items = await request.json()
if not isinstance(items, list):
from fastapi import HTTPException
raise HTTPException(400, "배열 형식이어야 합니다.")
upsert(db, "issue_types", json.dumps(items, ensure_ascii=False))
db.commit()
return {"ok": True}
DEFAULT_REPAIR_TYPES = [
{"key": "부품교체", "label": "🔩 부품 교체"},
{"key": "재시작", "label": "🔄 재시작"},
{"key": "설정변경", "label": "⚙️ 설정 변경"},
{"key": "청소", "label": "🧹 청소"},
{"key": "배선정리", "label": "🔌 배선 정리"},
{"key": "펌웨어", "label": "💾 펌웨어 업데이트"},
{"key": "기타", "label": "📋 기타"},
]
@router.get("/repair-types")
def get_repair_types(db: Session = Depends(get_db)):
row = db.query(models.SystemSetting).filter_by(key="repair_types").first()
if row:
return json.loads(row.value)
return DEFAULT_REPAIR_TYPES
@router.put("/repair-types")
async def update_repair_types(request: Request, db: Session = Depends(get_db), _=Depends(require_admin)):
items = await request.json()
if not isinstance(items, list):
from fastapi import HTTPException
raise HTTPException(400, "배열 형식이어야 합니다.")
upsert(db, "repair_types", json.dumps(items, ensure_ascii=False))
db.commit()
return {"ok": True}
# ── 관리자 설정 저장 (신고공개정책 + 이미지설정 + 처리시간기준 통합)
@router.put("")
def update_settings(
report_visibility_policy: str = Form(...),
image_compress_enabled: str = Form("true"),
image_max_px: str = Form("1024"),
image_quality: str = Form("85"),
time_metric_base: str = Form("occurred"),
time_metric_worktime: str = Form("false"),
db: Session = Depends(get_db),
_ = Depends(require_admin)
):
pairs = [
("report_visibility_policy", report_visibility_policy),
("image_compress_enabled", image_compress_enabled),
("image_max_px", image_max_px),
("image_quality", image_quality),
("time_metric_base", time_metric_base),
("time_metric_worktime", time_metric_worktime),
]
for key, val in pairs:
upsert(db, key, val)
db.commit()
return {"ok": True}