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:
@@ -6,96 +6,55 @@
|
||||
<title>부적합 현황판</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- 공통 스타일 -->
|
||||
<link rel="stylesheet" href="/static/css/tkqc-common.css">
|
||||
|
||||
<style>
|
||||
.fade-in { opacity: 0; animation: fadeIn 0.5s ease-in forwards; }
|
||||
@keyframes fadeIn { to { opacity: 1; } }
|
||||
|
||||
.header-fade-in { opacity: 0; animation: headerFadeIn 0.6s ease-out forwards; }
|
||||
@keyframes headerFadeIn { to { opacity: 1; transform: translateY(0); } from { transform: translateY(-10px); } }
|
||||
|
||||
.content-fade-in { opacity: 0; animation: contentFadeIn 0.7s ease-out 0.2s forwards; }
|
||||
@keyframes contentFadeIn { to { opacity: 1; transform: translateY(0); } from { transform: translateY(20px); } }
|
||||
|
||||
.content-fade-in { opacity: 0; animation: contentFadeIn 0.4s ease-out 0.2s forwards; }
|
||||
@keyframes contentFadeIn { to { opacity: 1; transform: translateY(0); } from { transform: translateY(10px); } }
|
||||
|
||||
/* 대시보드 카드 스타일 */
|
||||
.dashboard-card {
|
||||
transition: all 0.3s ease;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
.dashboard-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
/* 이슈 카드 스타일 (세련된 모던 스타일) */
|
||||
.issue-card {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-left: 4px solid transparent;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||
}
|
||||
|
||||
.issue-card:hover {
|
||||
transform: translateY(-8px) scale(1.02);
|
||||
border-left-color: #3b82f6;
|
||||
box-shadow:
|
||||
0 25px 50px -12px rgba(0, 0, 0, 0.15),
|
||||
0 0 0 1px rgba(59, 130, 246, 0.1),
|
||||
0 0 20px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
|
||||
.issue-card label {
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
|
||||
.issue-card .bg-gray-50 {
|
||||
background-color: #f9fafb;
|
||||
border: 1px solid #e5e7eb;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.issue-card .bg-gray-50:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
.issue-card .fas.fa-image:hover {
|
||||
transform: scale(1.2);
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
/* 진행 중 애니메이션 */
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.animate-pulse {
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
/* 날짜 그룹 스타일 */
|
||||
.date-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.date-header {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
.date-header:hover {
|
||||
background-color: #f3f4f6 !important;
|
||||
}
|
||||
|
||||
|
||||
.collapse-content {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
.progress-bar {
|
||||
background: linear-gradient(90deg, #10b981 0%, #059669 100%);
|
||||
background: #10b981;
|
||||
transition: width 0.8s ease;
|
||||
}
|
||||
|
||||
|
||||
/* 반응형 그리드 */
|
||||
.dashboard-grid {
|
||||
display: grid;
|
||||
@@ -104,7 +63,7 @@
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen">
|
||||
<body>
|
||||
<!-- 로딩 스크린 -->
|
||||
<div id="loadingScreen" class="fixed inset-0 bg-white z-50 flex items-center justify-center">
|
||||
<div class="text-center">
|
||||
@@ -139,7 +98,7 @@
|
||||
<div id="commonHeader"></div>
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="container mx-auto px-4 py-8 content-fade-in" style="padding-top: 80px;">
|
||||
<main class="container mx-auto px-4 py-8 content-fade-in" style="padding-top: 72px;">
|
||||
<!-- 페이지 헤더 -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-6 mb-6">
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -155,7 +114,7 @@
|
||||
|
||||
<!-- 전체 통계 대시보드 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
||||
<div class="dashboard-card text-white p-6 rounded-xl">
|
||||
<div class="bg-blue-500 text-white p-6 rounded-xl dashboard-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-blue-100 text-sm flex items-center space-x-1">
|
||||
@@ -167,43 +126,43 @@
|
||||
<i class="fas fa-tasks text-4xl text-blue-200"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gradient-to-br from-green-400 to-green-600 text-white p-6 rounded-xl dashboard-card">
|
||||
|
||||
<div class="bg-emerald-500 text-white p-6 rounded-xl dashboard-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-green-100 text-sm flex items-center space-x-1">
|
||||
<p class="text-emerald-100 text-sm flex items-center space-x-1">
|
||||
<span>오늘 신규</span>
|
||||
<div class="w-1.5 h-1.5 bg-green-200 rounded-full animate-pulse"></div>
|
||||
<div class="w-1.5 h-1.5 bg-emerald-200 rounded-full animate-pulse"></div>
|
||||
</p>
|
||||
<p class="text-3xl font-bold" id="todayNew">0</p>
|
||||
</div>
|
||||
<i class="fas fa-plus-circle text-4xl text-green-200"></i>
|
||||
<i class="fas fa-plus-circle text-4xl text-emerald-200"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gradient-to-br from-purple-400 to-purple-600 text-white p-6 rounded-xl dashboard-card">
|
||||
|
||||
<div class="bg-amber-500 text-white p-6 rounded-xl dashboard-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-purple-100 text-sm flex items-center space-x-1">
|
||||
<p class="text-amber-100 text-sm flex items-center space-x-1">
|
||||
<span>완료 대기</span>
|
||||
<div class="w-1.5 h-1.5 bg-purple-200 rounded-full animate-pulse"></div>
|
||||
<div class="w-1.5 h-1.5 bg-amber-200 rounded-full animate-pulse"></div>
|
||||
</p>
|
||||
<p class="text-3xl font-bold" id="pendingCompletion">0</p>
|
||||
</div>
|
||||
<i class="fas fa-hourglass-half text-4xl text-purple-200"></i>
|
||||
<i class="fas fa-hourglass-half text-4xl text-amber-200"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gradient-to-br from-red-400 to-red-600 text-white p-6 rounded-xl dashboard-card">
|
||||
|
||||
<div class="bg-slate-500 text-white p-6 rounded-xl dashboard-card">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-red-100 text-sm flex items-center space-x-1">
|
||||
<p class="text-slate-200 text-sm flex items-center space-x-1">
|
||||
<span>지연 중</span>
|
||||
<div class="w-1.5 h-1.5 bg-red-200 rounded-full animate-pulse"></div>
|
||||
<div class="w-1.5 h-1.5 bg-slate-300 rounded-full animate-pulse"></div>
|
||||
</p>
|
||||
<p class="text-3xl font-bold" id="overdue">0</p>
|
||||
</div>
|
||||
<i class="fas fa-clock text-4xl text-red-200"></i>
|
||||
<i class="fas fa-clock text-4xl text-slate-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user