feat: 문의사항(Q&A) 게시판 구현 및 대시보드 활성도 분석 로직 개선
- [server.py] 문의사항 API(목록, 상세, 답변 저장/삭제) 및 라우트 추가 - [templates/inquiries.html] 문의사항 게시판 HTML 구조 구현 (Sticky 헤더, 아코디언 상세) - [js/inquiries.js] 비동기 데이터 로드, 아코디언 토글, 답변 편집 로직 구현 - [style/inquiries.css] 와이드 레이아웃, Sticky 헤더, 아코디언 및 상태 배지 스타일 적용 - [server.py, js/dashboard.js] '폴더 자동 삭제' 로그 발생 시 14일 경과 여부와 관계없이 '방치'로 분류하도록 로직 수정 - [templates/dashboard.html] 대시보드 내 문의사항 탭 활성화 및 링크 연동
This commit is contained in:
99
server.py
99
server.py
@@ -71,6 +71,86 @@ async def get_dashboard(request: Request):
|
||||
async def get_mail_test(request: Request):
|
||||
return templates.TemplateResponse("mailTest.html", {"request": request})
|
||||
|
||||
@app.get("/inquiries")
|
||||
async def get_inquiries_page(request: Request):
|
||||
return templates.TemplateResponse("inquiries.html", {"request": request})
|
||||
|
||||
class InquiryReplyRequest(BaseModel):
|
||||
reply: str
|
||||
status: str
|
||||
handler: str
|
||||
|
||||
# --- 문의사항 API ---
|
||||
@app.get("/api/inquiries")
|
||||
async def get_inquiries(pm_type: str = None, category: str = None, status: str = None, keyword: str = None):
|
||||
# ... (existing code)
|
||||
try:
|
||||
with get_db_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
sql = "SELECT * FROM inquiries WHERE 1=1"
|
||||
params = []
|
||||
if pm_type:
|
||||
sql += " AND pm_type = %s"
|
||||
params.append(pm_type)
|
||||
if category:
|
||||
sql += " AND category = %s"
|
||||
params.append(category)
|
||||
if status:
|
||||
sql += " AND status = %s"
|
||||
params.append(status)
|
||||
if keyword:
|
||||
sql += " AND (content LIKE %s OR author LIKE %s OR project_nm LIKE %s)"
|
||||
params.extend([f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"])
|
||||
|
||||
sql += " ORDER BY no DESC"
|
||||
cursor.execute(sql, params)
|
||||
return cursor.fetchall()
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@app.get("/api/inquiries/{id}")
|
||||
async def get_inquiry_detail(id: int):
|
||||
try:
|
||||
with get_db_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute("SELECT * FROM inquiries WHERE id = %s", (id,))
|
||||
return cursor.fetchone()
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@app.post("/api/inquiries/{id}/reply")
|
||||
async def update_inquiry_reply(id: int, req: InquiryReplyRequest):
|
||||
try:
|
||||
with get_db_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
handled_date = datetime.now().strftime("%Y. %m. %d")
|
||||
sql = """
|
||||
UPDATE inquiries
|
||||
SET reply = %s, status = %s, handler = %s, handled_date = %s
|
||||
WHERE id = %s
|
||||
"""
|
||||
cursor.execute(sql, (req.reply, req.status, req.handler, handled_date, id))
|
||||
conn.commit()
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
@app.delete("/api/inquiries/{id}/reply")
|
||||
async def delete_inquiry_reply(id: int):
|
||||
try:
|
||||
with get_db_connection() as conn:
|
||||
with conn.cursor() as cursor:
|
||||
sql = """
|
||||
UPDATE inquiries
|
||||
SET reply = '', status = '미확인', handled_date = ''
|
||||
WHERE id = %s
|
||||
"""
|
||||
cursor.execute(sql, (id,))
|
||||
conn.commit()
|
||||
return {"success": True}
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
|
||||
# --- 분석 및 수집 API ---
|
||||
@app.get("/available-dates")
|
||||
async def get_available_dates():
|
||||
@@ -153,14 +233,19 @@ async def get_project_activity(date: str = None):
|
||||
# [핵심] 파일이 0개면 무조건 "데이터 없음"
|
||||
status = "unknown"
|
||||
elif has_log:
|
||||
# 로그 날짜가 있는 경우 정밀 분석
|
||||
match = re.search(r'(\d{4})\.(\d{2})\.(\d{2})', log)
|
||||
if match:
|
||||
diff = (target_date_dt - datetime.strptime(match.group(0), "%Y.%m.%d")).days
|
||||
status = "active" if diff <= 7 else "warning" if diff <= 14 else "stale"
|
||||
days = diff
|
||||
else:
|
||||
if "폴더자동삭제" in log.replace(" ", ""):
|
||||
# [추가] 폴더 자동 삭제인 경우 날짜 상관없이 무조건 "방치"
|
||||
status = "stale"
|
||||
days = 999
|
||||
else:
|
||||
# 로그 날짜가 있는 경우 정밀 분석
|
||||
match = re.search(r'(\d{4})\.(\d{2})\.(\d{2})', log)
|
||||
if match:
|
||||
diff = (target_date_dt - datetime.strptime(match.group(0), "%Y.%m.%d")).days
|
||||
status = "active" if diff <= 7 else "warning" if diff <= 14 else "stale"
|
||||
days = diff
|
||||
else:
|
||||
status = "stale"
|
||||
else:
|
||||
# 파일은 있지만 로그가 없는 경우
|
||||
status = "stale"
|
||||
|
||||
Reference in New Issue
Block a user