diff --git a/system3-nonconformance/api/database/models.py b/system3-nonconformance/api/database/models.py index 1c82d94..2c21835 100644 --- a/system3-nonconformance/api/database/models.py +++ b/system3-nonconformance/api/database/models.py @@ -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" diff --git a/system3-nonconformance/api/database/schemas.py b/system3-nonconformance/api/database/schemas.py index d466d49..aabac7c 100644 --- a/system3-nonconformance/api/database/schemas.py +++ b/system3-nonconformance/api/database/schemas.py @@ -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 diff --git a/system3-nonconformance/api/main.py b/system3-nonconformance/api/main.py index 5d1312f..a246843 100644 --- a/system3-nonconformance/api/main.py +++ b/system3-nonconformance/api/main.py @@ -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) diff --git a/system3-nonconformance/api/routers/issues.py b/system3-nonconformance/api/routers/issues.py index cfc118a..02b6b0a 100644 --- a/system3-nonconformance/api/routers/issues.py +++ b/system3-nonconformance/api/routers/issues.py @@ -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), diff --git a/system3-nonconformance/api/routers/page_permissions.py b/system3-nonconformance/api/routers/page_permissions.py index 37f3e92..18731b0 100644 --- a/system3-nonconformance/api/routers/page_permissions.py +++ b/system3-nonconformance/api/routers/page_permissions.py @@ -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") diff --git a/system3-nonconformance/api/routers/reports.py b/system3-nonconformance/api/routers/reports.py index 393af40..292a945 100644 --- a/system3-nonconformance/api/routers/reports.py +++ b/system3-nonconformance/api/routers/reports.py @@ -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, diff --git a/system3-nonconformance/web/admin.html b/system3-nonconformance/web/admin.html index 133240d..a9ddfbe 100644 --- a/system3-nonconformance/web/admin.html +++ b/system3-nonconformance/web/admin.html @@ -714,8 +714,6 @@ 'issues_inbox': { title: '수신함', defaultAccess: true }, 'issues_management': { title: '관리함', defaultAccess: false }, 'issues_archive': { title: '폐기함', defaultAccess: false }, - 'projects_manage': { title: '프로젝트 관리', defaultAccess: false }, - 'daily_work': { title: '일일 공수', defaultAccess: false }, 'reports': { title: '보고서', defaultAccess: false } }; @@ -769,10 +767,7 @@ 'issues_archive': { title: '🗃️ 폐기함', icon: 'fas fa-archive', color: 'text-gray-600' } }, '시스템 관리': { - 'projects_manage': { title: '프로젝트 관리', icon: 'fas fa-folder-open', color: 'text-indigo-600' }, - 'daily_work': { title: '일일 공수', icon: 'fas fa-calendar-check', color: 'text-blue-600' }, - 'reports': { title: '보고서', icon: 'fas fa-chart-bar', color: 'text-red-600' }, - 'users_manage': { title: '사용자 관리', icon: 'fas fa-users-cog', color: 'text-purple-600' } + 'reports': { title: '보고서', icon: 'fas fa-chart-bar', color: 'text-red-600' } } }; @@ -839,7 +834,7 @@ const allPages = [ 'issues_create', 'issues_view', 'issues_manage', 'issues_inbox', 'issues_management', 'issues_archive', - 'projects_manage', 'daily_work', 'reports', 'users_manage' + 'reports' ]; const permissions = {}; diff --git a/system3-nonconformance/web/daily-work.html b/system3-nonconformance/web/daily-work.html index 2602895..bb7bd93 100644 --- a/system3-nonconformance/web/daily-work.html +++ b/system3-nonconformance/web/daily-work.html @@ -6,6 +6,10 @@ 일일 공수 입력 + + + + -
+
-
+

diff --git a/system3-nonconformance/web/issue-view.html b/system3-nonconformance/web/issue-view.html index fe0eceb..0111aa8 100644 --- a/system3-nonconformance/web/issue-view.html +++ b/system3-nonconformance/web/issue-view.html @@ -11,42 +11,19 @@ - + + + + - + -
+
diff --git a/system3-nonconformance/web/issues-dashboard.html b/system3-nonconformance/web/issues-dashboard.html index 037b4b7..a5cb0fe 100644 --- a/system3-nonconformance/web/issues-dashboard.html +++ b/system3-nonconformance/web/issues-dashboard.html @@ -6,96 +6,55 @@ 부적합 현황판 + + + - +
@@ -139,7 +98,7 @@
-
+
@@ -155,7 +114,7 @@
-
+

@@ -167,43 +126,43 @@

- -
+ +
-

+

오늘 신규 -

+

0

- +
- -
+ +
-

+

완료 대기 -

+

0

- +
- -
+ +
-

+

지연 중 -

+

0

- +
@@ -2131,44 +2090,15 @@ } try { - // 현재 이슈 정보 가져오기 - const issueResponse = await fetch(`/api/issues/${selectedOpinionIssueId}`, { - headers: { - 'Authorization': `Bearer ${TokenManager.getToken()}` - } - }); - - if (!issueResponse.ok) { - throw new Error('이슈 정보를 가져올 수 없습니다.'); - } - - const issue = await issueResponse.json(); - - // 새 의견 형식: [작성자] (날짜시간)\n내용 - const now = new Date(); - const dateStr = now.toLocaleString('ko-KR', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit' - }); - const newOpinion = `[${currentUser.full_name || currentUser.username}] (${dateStr})\n${opinionText}`; - - // 기존 solution에 추가 (최신이 위로) - const updatedSolution = issue.solution - ? `${newOpinion}\n${'─'.repeat(50)}\n${issue.solution}` - : newOpinion; - - // 백엔드 업데이트 - const response = await fetch(`/api/issues/${selectedOpinionIssueId}/management`, { - method: 'PUT', + // 별도 의견 제시 API 사용 (권한 체크 없음, 로그인만 필요) + const response = await fetch(`/api/issues/${selectedOpinionIssueId}/opinion`, { + method: 'POST', headers: { 'Authorization': `Bearer ${TokenManager.getToken()}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ - solution: updatedSolution + opinion: opinionText }) }); diff --git a/system3-nonconformance/web/issues-inbox.html b/system3-nonconformance/web/issues-inbox.html index 22be80e..d711cf0 100644 --- a/system3-nonconformance/web/issues-inbox.html +++ b/system3-nonconformance/web/issues-inbox.html @@ -13,15 +13,12 @@ - + + + + - +
@@ -179,7 +127,7 @@ -
+
diff --git a/system3-nonconformance/web/issues-management.html b/system3-nonconformance/web/issues-management.html index 0b59032..155fdd1 100644 --- a/system3-nonconformance/web/issues-management.html +++ b/system3-nonconformance/web/issues-management.html @@ -13,69 +13,43 @@ - + + + + - + -
+
diff --git a/system3-nonconformance/web/project-management.html b/system3-nonconformance/web/project-management.html index a14ed2a..497f300 100644 --- a/system3-nonconformance/web/project-management.html +++ b/system3-nonconformance/web/project-management.html @@ -6,6 +6,10 @@ 프로젝트 관리 - 작업보고서 시스템 + + + + -
+

diff --git a/system3-nonconformance/web/reports-daily.html b/system3-nonconformance/web/reports-daily.html index 8007d6e..6756ade 100644 --- a/system3-nonconformance/web/reports-daily.html +++ b/system3-nonconformance/web/reports-daily.html @@ -11,22 +11,17 @@ + + + - + -
+
diff --git a/system3-nonconformance/web/reports-monthly.html b/system3-nonconformance/web/reports-monthly.html index d98e22b..580da68 100644 --- a/system3-nonconformance/web/reports-monthly.html +++ b/system3-nonconformance/web/reports-monthly.html @@ -10,21 +10,15 @@ - - - + + + - + -
+
diff --git a/system3-nonconformance/web/reports-weekly.html b/system3-nonconformance/web/reports-weekly.html index 76bbaed..887cbc3 100644 --- a/system3-nonconformance/web/reports-weekly.html +++ b/system3-nonconformance/web/reports-weekly.html @@ -10,21 +10,15 @@ - - - + + + - + -
+
diff --git a/system3-nonconformance/web/reports.html b/system3-nonconformance/web/reports.html index f79e1b1..173be11 100644 --- a/system3-nonconformance/web/reports.html +++ b/system3-nonconformance/web/reports.html @@ -10,49 +10,44 @@ - + + + + - +
-
+
diff --git a/system3-nonconformance/web/static/css/tkqc-common.css b/system3-nonconformance/web/static/css/tkqc-common.css new file mode 100644 index 0000000..63753a3 --- /dev/null +++ b/system3-nonconformance/web/static/css/tkqc-common.css @@ -0,0 +1,39 @@ +/* tkqc-common.css — tkuser 스타일 통일 */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + background: #f1f5f9; + min-height: 100vh; +} + +.input-field { + background: white; + border: 1px solid #e2e8f0; + transition: all 0.2s; +} +.input-field:focus { + outline: none; + border-color: #64748b; + box-shadow: 0 0 0 3px rgba(100,116,139,0.1); +} + +.issue-card { + transition: all 0.2s ease; + border-left: 4px solid transparent; +} +.issue-card:hover { + box-shadow: 0 4px 12px rgba(0,0,0,0.08); +} + +.badge { display: inline-flex; align-items: center; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 500; } +.badge-new { background: #dbeafe; color: #1e40af; } +.badge-processing { background: #fef3c7; color: #92400e; } +.badge-completed { background: #d1fae5; color: #065f46; } +.badge-archived { background: #f3f4f6; color: #374151; } +.badge-cancelled { background: #fee2e2; color: #991b1b; } + +.fade-in { opacity: 0; transform: translateY(10px); transition: opacity 0.4s, transform 0.4s; } +.fade-in.visible { opacity: 1; transform: translateY(0); } + +.toast-message { transition: all 0.3s ease; } diff --git a/system3-nonconformance/web/static/js/api.js b/system3-nonconformance/web/static/js/api.js index 60afe72..89a1d9f 100644 --- a/system3-nonconformance/web/static/js/api.js +++ b/system3-nonconformance/web/static/js/api.js @@ -273,35 +273,6 @@ const IssuesAPI = { getStats: () => apiRequest('/issues/stats/summary') }; -// Daily Work API -const DailyWorkAPI = { - create: (workData) => apiRequest('/daily-work/', { - method: 'POST', - body: JSON.stringify(workData) - }), - - getAll: (params = {}) => { - const queryString = new URLSearchParams(params).toString(); - return apiRequest(`/daily-work/${queryString ? '?' + queryString : ''}`); - }, - - get: (id) => apiRequest(`/daily-work/${id}`), - - update: (id, workData) => apiRequest(`/daily-work/${id}`, { - method: 'PUT', - body: JSON.stringify(workData) - }), - - delete: (id) => apiRequest(`/daily-work/${id}`, { - method: 'DELETE' - }), - - getStats: (params = {}) => { - const queryString = new URLSearchParams(params).toString(); - return apiRequest(`/daily-work/stats/summary${queryString ? '?' + queryString : ''}`); - } -}; - // Reports API const ReportsAPI = { getSummary: (startDate, endDate) => apiRequest('/reports/summary', { @@ -320,13 +291,6 @@ const ReportsAPI = { return apiRequest(`/reports/issues?${params}`); }, - getDailyWorks: (startDate, endDate) => { - const params = new URLSearchParams({ - start_date: startDate, - end_date: endDate - }).toString(); - return apiRequest(`/reports/daily-works?${params}`); - } }; // 권한 체크 diff --git a/system3-nonconformance/web/static/js/app.js b/system3-nonconformance/web/static/js/app.js index f78f688..b179ef9 100644 --- a/system3-nonconformance/web/static/js/app.js +++ b/system3-nonconformance/web/static/js/app.js @@ -270,10 +270,7 @@ class App { const titles = { 'dashboard': '대시보드', 'issues': '부적합 사항', - 'projects': '프로젝트', - 'daily_work': '일일 공수', - 'reports': '보고서', - 'users': '사용자 관리' + 'reports': '보고서' }; const title = titles[module] || module; diff --git a/system3-nonconformance/web/static/js/components/common-header.js b/system3-nonconformance/web/static/js/components/common-header.js index 2dc199e..72d15fa 100644 --- a/system3-nonconformance/web/static/js/components/common-header.js +++ b/system3-nonconformance/web/static/js/components/common-header.js @@ -32,8 +32,8 @@ class CommonHeader { icon: 'fas fa-chart-line', url: '/issues-dashboard.html', pageName: 'issues_dashboard', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100' + color: 'text-slate-300', + bgColor: 'text-slate-300 hover:bg-slate-700' }, { id: 'issues_inbox', @@ -41,8 +41,8 @@ class CommonHeader { icon: 'fas fa-inbox', url: '/issues-inbox.html', pageName: 'issues_inbox', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100' + color: 'text-slate-300', + bgColor: 'text-slate-300 hover:bg-slate-700' }, { id: 'issues_management', @@ -50,8 +50,8 @@ class CommonHeader { icon: 'fas fa-cog', url: '/issues-management.html', pageName: 'issues_management', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100' + color: 'text-slate-300', + bgColor: 'text-slate-300 hover:bg-slate-700' }, { id: 'issues_archive', @@ -59,17 +59,8 @@ class CommonHeader { icon: 'fas fa-archive', url: '/issues-archive.html', pageName: 'issues_archive', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100' - }, - { - id: 'daily_work', - title: '일일 공수', - icon: 'fas fa-calendar-check', - url: '/daily-work.html', - pageName: 'daily_work', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100' + color: 'text-slate-300', + bgColor: 'text-slate-300 hover:bg-slate-700' }, { id: 'reports', @@ -77,8 +68,8 @@ class CommonHeader { icon: 'fas fa-chart-bar', url: '/reports.html', pageName: 'reports', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100', + color: 'text-slate-300', + bgColor: 'text-slate-300 hover:bg-slate-700', subMenus: [ { id: 'reports_daily', @@ -86,7 +77,7 @@ class CommonHeader { icon: 'fas fa-file-excel', url: '/reports-daily.html', pageName: 'reports_daily', - color: 'text-slate-600' + color: 'text-slate-300' }, { id: 'reports_weekly', @@ -94,7 +85,7 @@ class CommonHeader { icon: 'fas fa-calendar-week', url: '/reports-weekly.html', pageName: 'reports_weekly', - color: 'text-slate-600' + color: 'text-slate-300' }, { id: 'reports_monthly', @@ -102,29 +93,10 @@ class CommonHeader { icon: 'fas fa-calendar-alt', url: '/reports-monthly.html', pageName: 'reports_monthly', - color: 'text-slate-600' + color: 'text-slate-300' } ] }, - { - id: 'projects_manage', - title: '프로젝트 관리', - icon: 'fas fa-folder-open', - url: '/project-management.html', - pageName: 'projects_manage', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100' - }, - { - id: 'users_manage', - title: '사용자 관리', - icon: 'fas fa-users-cog', - url: this._getUserManageUrl(), - pageName: 'users_manage', - color: 'text-slate-600', - bgColor: 'text-slate-600 hover:bg-slate-100', - external: true - } ]; } @@ -205,14 +177,14 @@ class CommonHeader { const userRole = this.getUserRoleDisplay(); return ` -
+
-
+
- -

부적합 관리

+ +

부적합 관리

@@ -226,8 +198,8 @@ class CommonHeader {
-
${userDisplayName}
-
${userRole}
+
${userDisplayName}
+
${userRole}
@@ -238,7 +210,7 @@ class CommonHeader {
- -
-