refactor(tkqc): UI 스타일 통일 + 일일공수 제거 + 메뉴 정리
- UI: tkuser 스타일로 통일 (dark slate 헤더, flat 배경, gradient/glass 제거) - tkqc-common.css 공통 스타일시트 신규 생성 - 의견 제시 API 별도 엔드포인트 추가 (모든 사용자 접근 가능) - 일일 공수 기능 완전 제거 (라우터, 모델, 스키마, DB 테이블 DROP) - 프로젝트 관리/사용자 관리 메뉴 숨김 (통합관리 페이지로 이관) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -68,7 +68,6 @@ class User(Base):
|
||||
# Relationships
|
||||
issues = relationship("Issue", back_populates="reporter", foreign_keys="Issue.reporter_id")
|
||||
reviewed_issues = relationship("Issue", foreign_keys="Issue.reviewed_by_id")
|
||||
daily_works = relationship("DailyWork", back_populates="created_by")
|
||||
page_permissions = relationship("UserPagePermission", back_populates="user", foreign_keys="UserPagePermission.user_id")
|
||||
|
||||
class UserPagePermission(Base):
|
||||
@@ -183,37 +182,6 @@ class Project(Base):
|
||||
primaryjoin="Project.id == Issue.project_id",
|
||||
foreign_keys="[Issue.project_id]")
|
||||
|
||||
class DailyWork(Base):
|
||||
__tablename__ = "qc_daily_works"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
date = Column(DateTime, nullable=False, index=True)
|
||||
worker_count = Column(Integer, nullable=False)
|
||||
regular_hours = Column(Float, nullable=False)
|
||||
overtime_workers = Column(Integer, default=0)
|
||||
overtime_hours = Column(Float, default=0)
|
||||
overtime_total = Column(Float, default=0)
|
||||
total_hours = Column(Float, nullable=False)
|
||||
created_by_id = Column(Integer, ForeignKey("sso_users.user_id"))
|
||||
created_at = Column(DateTime, default=get_kst_now)
|
||||
|
||||
# Relationships
|
||||
created_by = relationship("User", back_populates="daily_works")
|
||||
|
||||
class ProjectDailyWork(Base):
|
||||
__tablename__ = "qc_project_daily_works"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
date = Column(DateTime, nullable=False, index=True)
|
||||
project_id = Column(Integer, ForeignKey("projects.project_id"), nullable=False)
|
||||
hours = Column(Float, nullable=False)
|
||||
created_by_id = Column(Integer, ForeignKey("sso_users.user_id"))
|
||||
created_at = Column(DateTime, default=get_kst_now)
|
||||
|
||||
# Relationships
|
||||
project = relationship("Project")
|
||||
created_by = relationship("User")
|
||||
|
||||
class DeletionLog(Base):
|
||||
__tablename__ = "qc_deletion_logs"
|
||||
|
||||
|
||||
@@ -250,6 +250,10 @@ class ManagementUpdateRequest(BaseModel):
|
||||
completion_photo5: Optional[str] = None # Base64 - 완료 사진 5
|
||||
review_status: Optional[ReviewStatus] = None
|
||||
|
||||
class OpinionRequest(BaseModel):
|
||||
"""의견 제시 요청 (로그인한 모든 사용자 가능)"""
|
||||
opinion: str # 의견 내용
|
||||
|
||||
class InboxIssue(BaseModel):
|
||||
"""수신함용 부적합 정보 (간소화된 버전)"""
|
||||
id: int
|
||||
@@ -296,33 +300,6 @@ class Project(ProjectBase):
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# Daily Work schemas
|
||||
class DailyWorkBase(BaseModel):
|
||||
date: datetime
|
||||
worker_count: int = Field(gt=0)
|
||||
overtime_workers: Optional[int] = 0
|
||||
overtime_hours: Optional[float] = 0
|
||||
|
||||
class DailyWorkCreate(DailyWorkBase):
|
||||
pass
|
||||
|
||||
class DailyWorkUpdate(BaseModel):
|
||||
worker_count: Optional[int] = Field(None, gt=0)
|
||||
overtime_workers: Optional[int] = None
|
||||
overtime_hours: Optional[float] = None
|
||||
|
||||
class DailyWork(DailyWorkBase):
|
||||
id: int
|
||||
regular_hours: float
|
||||
overtime_total: float
|
||||
total_hours: float
|
||||
created_by_id: int
|
||||
created_by: User
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# Report schemas
|
||||
class ReportRequest(BaseModel):
|
||||
start_date: datetime
|
||||
@@ -342,24 +319,6 @@ class ReportSummary(BaseModel):
|
||||
completed_issues: int
|
||||
average_resolution_time: float
|
||||
|
||||
# Project Daily Work schemas
|
||||
class ProjectDailyWorkBase(BaseModel):
|
||||
date: datetime
|
||||
project_id: int
|
||||
hours: float
|
||||
|
||||
class ProjectDailyWorkCreate(ProjectDailyWorkBase):
|
||||
pass
|
||||
|
||||
class ProjectDailyWork(ProjectDailyWorkBase):
|
||||
id: int
|
||||
created_by_id: int
|
||||
created_at: datetime
|
||||
project: Project
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
# 일일보고서 관련 스키마
|
||||
class DailyReportRequest(BaseModel):
|
||||
project_id: int
|
||||
|
||||
@@ -5,7 +5,7 @@ import uvicorn
|
||||
|
||||
from database.database import engine, get_db
|
||||
from database.models import Base
|
||||
from routers import auth, issues, daily_work, reports, projects, page_permissions, inbox, management
|
||||
from routers import auth, issues, reports, projects, page_permissions, inbox, management
|
||||
from services.auth_service import create_admin_user
|
||||
|
||||
# 데이터베이스 테이블 생성 (sso_users, projects는 이미 존재하므로 제외)
|
||||
@@ -36,7 +36,6 @@ app.add_middleware(
|
||||
app.include_router(auth.router)
|
||||
app.include_router(issues.router)
|
||||
app.include_router(inbox.router) # 수신함 라우터 추가
|
||||
app.include_router(daily_work.router)
|
||||
app.include_router(reports.router)
|
||||
app.include_router(projects.router)
|
||||
app.include_router(page_permissions.router)
|
||||
|
||||
@@ -246,6 +246,42 @@ async def delete_issue(
|
||||
db.commit()
|
||||
return {"detail": "Issue deleted successfully", "logged": True}
|
||||
|
||||
@router.post("/{issue_id}/opinion")
|
||||
async def add_opinion(
|
||||
issue_id: int,
|
||||
opinion_request: schemas.OpinionRequest,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
의견 제시 — 로그인한 모든 사용자가 사용 가능 (권한 체크 없음)
|
||||
solution 필드에 의견을 추가합니다.
|
||||
"""
|
||||
issue = db.query(Issue).filter(Issue.id == issue_id).first()
|
||||
if not issue:
|
||||
raise HTTPException(status_code=404, detail="부적합을 찾을 수 없습니다.")
|
||||
|
||||
# 새 의견 형식: [작성자] (날짜시간)\n내용
|
||||
now = datetime.now()
|
||||
date_str = now.strftime("%Y. %m. %d. %H:%M")
|
||||
author = current_user.full_name or current_user.username
|
||||
new_opinion = f"[{author}] ({date_str})\n{opinion_request.opinion}"
|
||||
|
||||
# 기존 solution에 추가 (최신이 위로)
|
||||
separator = "─" * 50
|
||||
if issue.solution:
|
||||
issue.solution = f"{new_opinion}\n{separator}\n{issue.solution}"
|
||||
else:
|
||||
issue.solution = new_opinion
|
||||
|
||||
db.commit()
|
||||
db.refresh(issue)
|
||||
|
||||
return {
|
||||
"message": "의견이 추가되었습니다.",
|
||||
"issue_id": issue.id
|
||||
}
|
||||
|
||||
@router.get("/stats/summary")
|
||||
async def get_issue_stats(
|
||||
current_user: User = Depends(get_current_user),
|
||||
|
||||
@@ -50,10 +50,7 @@ DEFAULT_PAGES = {
|
||||
'issues_management': {'title': '관리함', 'default_access': False},
|
||||
'issues_archive': {'title': '폐기함', 'default_access': False},
|
||||
'issues_dashboard': {'title': '현황판', 'default_access': True},
|
||||
'projects_manage': {'title': '프로젝트 관리', 'default_access': False},
|
||||
'daily_work': {'title': '일일 공수', 'default_access': False},
|
||||
'reports': {'title': '보고서', 'default_access': False},
|
||||
'users_manage': {'title': '사용자 관리', 'default_access': False}
|
||||
'reports': {'title': '보고서', 'default_access': False}
|
||||
}
|
||||
|
||||
@router.post("/page-permissions/grant")
|
||||
|
||||
@@ -13,7 +13,7 @@ from openpyxl.drawing.image import Image as XLImage
|
||||
import os
|
||||
|
||||
from database.database import get_db
|
||||
from database.models import Issue, DailyWork, IssueStatus, IssueCategory, User, UserRole, ReviewStatus
|
||||
from database.models import Issue, IssueStatus, IssueCategory, User, UserRole, ReviewStatus
|
||||
from database import schemas
|
||||
from routers.auth import get_current_user
|
||||
from utils.tkuser_client import get_token_from_request
|
||||
@@ -30,14 +30,9 @@ async def generate_report_summary(
|
||||
"""보고서 요약 생성"""
|
||||
start_date = report_request.start_date
|
||||
end_date = report_request.end_date
|
||||
|
||||
# 일일 공수 합계
|
||||
daily_works = db.query(DailyWork).filter(
|
||||
DailyWork.date >= start_date.date(),
|
||||
DailyWork.date <= end_date.date()
|
||||
).all()
|
||||
total_hours = sum(w.total_hours for w in daily_works)
|
||||
|
||||
|
||||
total_hours = 0
|
||||
|
||||
# 이슈 통계
|
||||
issues_query = db.query(Issue).filter(
|
||||
Issue.report_date >= start_date,
|
||||
@@ -116,29 +111,6 @@ async def get_report_issues(
|
||||
"detail_notes": issue.detail_notes
|
||||
} for issue in issues]
|
||||
|
||||
@router.get("/daily-works")
|
||||
async def get_report_daily_works(
|
||||
start_date: datetime,
|
||||
end_date: datetime,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""보고서용 일일 공수 목록"""
|
||||
works = db.query(DailyWork).filter(
|
||||
DailyWork.date >= start_date.date(),
|
||||
DailyWork.date <= end_date.date()
|
||||
).order_by(DailyWork.date).all()
|
||||
|
||||
return [{
|
||||
"date": work.date,
|
||||
"worker_count": work.worker_count,
|
||||
"regular_hours": work.regular_hours,
|
||||
"overtime_workers": work.overtime_workers,
|
||||
"overtime_hours": work.overtime_hours,
|
||||
"overtime_total": work.overtime_total,
|
||||
"total_hours": work.total_hours
|
||||
} for work in works]
|
||||
|
||||
@router.get("/daily-preview")
|
||||
async def preview_daily_report(
|
||||
project_id: int,
|
||||
|
||||
Reference in New Issue
Block a user