기능 추가 및 버그 수정 — 처리시간 지표, 대시보드 차트, 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>
This commit is contained in:
byun
2026-05-31 06:52:56 +09:00
parent 05b478372a
commit 2e8751ea6c
35 changed files with 5541 additions and 353 deletions

View File

@@ -9,6 +9,8 @@ class ChargerType(Base):
description = Column(Text)
created_at = Column(TIMESTAMP, server_default=func.now())
chargers = relationship("Charger", back_populates="charger_type")
errors = relationship("ChargerTypeError", back_populates="charger_type",
cascade="all, delete-orphan", order_by="ChargerTypeError.display_order")
class User(Base):
__tablename__ = "users"
@@ -21,6 +23,7 @@ class User(Base):
phone = Column(String(20))
email = Column(String(100))
is_active = Column(Boolean, default=True)
is_pending = Column(Boolean, default=False)
created_at = Column(TIMESTAMP, server_default=func.now())
class Charger(Base):
@@ -52,10 +55,14 @@ class Report(Base):
gps_lat = Column(Float)
gps_lng = Column(Float)
status = Column(String(30), default="pending")
ocpp_log = Column(Text)
source = Column(String(20), default="qr") # qr | admin
reported_by = Column(Integer, ForeignKey("users.id"), nullable=True)
reported_at = Column(TIMESTAMP, server_default=func.now())
charger = relationship("Charger", back_populates="reports")
photos = relationship("ReportPhoto", back_populates="report", cascade="all, delete-orphan")
repair_links = relationship("RepairReport", back_populates="report")
reporter = relationship("User", foreign_keys=[reported_by])
class ReportPhoto(Base):
__tablename__ = "report_photos"
@@ -74,7 +81,12 @@ class Repair(Base):
started_at = Column(TIMESTAMP, nullable=False)
completed_at = Column(TIMESTAMP)
result_status = Column(String(20), default="done")
mechanic_lat = Column(Float)
mechanic_lng = Column(Float)
approved_at = Column(TIMESTAMP)
approved_by = Column(Integer, ForeignKey("users.id"))
mechanic = relationship("User", foreign_keys=[mechanic_id])
approver = relationship("User", foreign_keys=[approved_by])
report_links = relationship("RepairReport", back_populates="repair", cascade="all, delete-orphan")
photos = relationship("RepairPhoto", back_populates="repair", cascade="all, delete-orphan")
cost = relationship("RepairCost", back_populates="repair", uselist=False)
@@ -161,8 +173,36 @@ class ImprovementLog(Base):
improvement = relationship("Improvement", back_populates="logs")
changer = relationship("User")
class ChargerTypeError(Base):
__tablename__ = "charger_type_errors"
id = Column(Integer, primary_key=True)
charger_type_id = Column(Integer, ForeignKey("charger_types.id", ondelete="CASCADE"), nullable=False)
error_code = Column(String(20), nullable=False)
error_name = Column(String(100), nullable=False)
range_condition = Column(String(200))
description = Column(Text)
auto_recovery = Column(Boolean, default=True)
display_order = Column(Integer, default=0)
charger_type = relationship("ChargerType", back_populates="errors")
class Manufacturer(Base):
__tablename__ = "manufacturers"
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
representative_name = Column(String(100))
business_number = Column(String(50))
phone = Column(String(30))
address = Column(Text)
is_active = Column(Boolean, default=True)
created_at = Column(TIMESTAMP, server_default=func.now())
class SystemSetting(Base):
__tablename__ = "system_settings"
key = Column(String(100), primary_key=True)
value = Column(Text, nullable=False)
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
class Holiday(Base):
__tablename__ = "holidays"
holiday_date = Column(Date, primary_key=True)
name = Column(String(100), nullable=False)