- ai-service: Ollama 기반 AI 서비스 (분류, 시맨틱 검색, RAG Q&A, 패턴 분석) - AI 어시스턴트 페이지: 채팅형 Q&A, 시맨틱 검색, 패턴 분석, 분류 테스트 - 권한 시스템에 ai_assistant 페이지 등록 (기본 비활성) - 기존 페이지에 AI 기능 통합 (대시보드, 수신함, 관리함) - docker-compose, gateway, nginx 설정 업데이트 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
384 lines
21 KiB
HTML
384 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>수신함 - 작업보고서</title>
|
|
<script>if(window.innerWidth<=768)window.location.replace('/m/inbox.html');</script>
|
|
|
|
<!-- Tailwind CSS -->
|
|
<link rel="preload" href="https://cdn.tailwindcss.com" as="script">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
|
|
<!-- Font Awesome -->
|
|
<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/mobile-calendar.css">
|
|
|
|
<!-- 공통 스타일 -->
|
|
<link rel="stylesheet" href="/static/css/tkqc-common.css?v=20260213">
|
|
|
|
<!-- 페이지 전용 스타일 -->
|
|
<link rel="stylesheet" href="/static/css/issues-inbox.css?v=20260213">
|
|
</head>
|
|
<body>
|
|
<!-- 로딩 오버레이 -->
|
|
<div id="loadingOverlay" class="loading-overlay">
|
|
<div class="text-center">
|
|
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
|
<p class="text-gray-600">데이터를 불러오는 중...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 공통 헤더가 여기에 자동으로 삽입됩니다 -->
|
|
|
|
<!-- Main Content -->
|
|
<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 mb-4">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-gray-900 flex items-center">
|
|
<i class="fas fa-inbox text-blue-500 mr-3"></i>
|
|
수신함
|
|
</h1>
|
|
<p class="text-gray-600 mt-1">새로 등록된 신고 사항을 확인하고 처리하세요</p>
|
|
</div>
|
|
<div class="flex items-center space-x-3">
|
|
<button onclick="refreshInbox()" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600 transition-colors">
|
|
<i class="fas fa-sync-alt mr-2"></i>
|
|
새로고침
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 통계 카드 -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div class="bg-yellow-50 p-4 rounded-lg">
|
|
<div class="flex items-center">
|
|
<i class="fas fa-plus-circle text-yellow-500 text-xl mr-3"></i>
|
|
<div>
|
|
<p class="text-sm text-yellow-600">금일 신규</p>
|
|
<p class="text-2xl font-bold text-yellow-700" id="todayNewCount">0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-green-50 p-4 rounded-lg">
|
|
<div class="flex items-center">
|
|
<i class="fas fa-check-circle text-green-500 text-xl mr-3"></i>
|
|
<div>
|
|
<p class="text-sm text-green-600">금일 처리</p>
|
|
<p class="text-2xl font-bold text-green-700" id="todayProcessedCount">0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-red-50 p-4 rounded-lg">
|
|
<div class="flex items-center">
|
|
<i class="fas fa-exclamation-triangle text-red-500 text-xl mr-3"></i>
|
|
<div>
|
|
<p class="text-sm text-red-600">미해결</p>
|
|
<p class="text-2xl font-bold text-red-700" id="unresolvedCount">0</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 필터 -->
|
|
<div class="bg-white rounded-xl shadow-sm p-6 mb-6">
|
|
<div class="max-w-md">
|
|
<!-- 프로젝트 필터 -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">📁 프로젝트</label>
|
|
<select id="projectFilter" class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" onchange="filterIssues()">
|
|
<option value="">전체 프로젝트</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 신고 목록 -->
|
|
<div class="bg-white rounded-xl shadow-sm">
|
|
<div class="p-6 border-b border-gray-200">
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-lg font-semibold text-gray-800">신고 목록</h2>
|
|
<div class="flex items-center space-x-2">
|
|
<span class="text-sm text-gray-500">정렬:</span>
|
|
<select id="sortOrder" class="text-sm border border-gray-300 rounded px-2 py-1" onchange="sortIssues()">
|
|
<option value="newest">최신순</option>
|
|
<option value="oldest">오래된순</option>
|
|
<option value="priority">우선순위</option>
|
|
<option value="unread">읽지 않은 순</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="issuesList" class="divide-y divide-gray-200">
|
|
<!-- 부적합 목록이 여기에 동적으로 생성됩니다 -->
|
|
</div>
|
|
|
|
<!-- 빈 상태 -->
|
|
<div id="emptyState" class="hidden p-12 text-center">
|
|
<i class="fas fa-inbox text-6xl text-gray-300 mb-4"></i>
|
|
<h3 class="text-lg font-medium text-gray-900 mb-2">수신함이 비어있습니다</h3>
|
|
<p class="text-gray-500">새로운 신고가 등록되면 여기에 표시됩니다.</p>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- 폐기 모달 -->
|
|
<div id="disposeModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
|
|
<div class="flex items-center justify-center min-h-screen p-4">
|
|
<div class="bg-white rounded-xl max-w-md w-full p-6">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">부적합 폐기</h3>
|
|
<button onclick="closeDisposeModal()" class="text-gray-400 hover:text-gray-600">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">폐기 사유</label>
|
|
<select id="disposalReason" class="w-full px-3 py-2 border border-gray-300 rounded-lg" onchange="toggleCustomReason(); toggleDuplicateSelection();">
|
|
<option value="duplicate">중복 (기본)</option>
|
|
<option value="invalid_report">잘못된 신고</option>
|
|
<option value="not_applicable">해당 없음</option>
|
|
<option value="spam">스팸/오류</option>
|
|
<option value="custom">직접 입력</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div id="customReasonDiv" class="hidden">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">사용자 정의 사유</label>
|
|
<textarea id="customReason" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
|
placeholder="폐기 사유를 입력하세요..."></textarea>
|
|
</div>
|
|
|
|
<!-- 중복 대상 선택 -->
|
|
<div id="duplicateSelectionDiv" class="space-y-3">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">중복 대상 선택</label>
|
|
<p class="text-sm text-gray-600 mb-3">동일 프로젝트의 관리함에 있는 이슈 중 중복 대상을 선택하세요:</p>
|
|
|
|
<div id="managementIssuesList" class="max-h-48 overflow-y-auto border border-gray-200 rounded-lg">
|
|
<div class="p-4 text-center text-gray-500">
|
|
<i class="fas fa-spinner fa-spin mr-2"></i>관리함 이슈를 불러오는 중...
|
|
</div>
|
|
</div>
|
|
|
|
<input type="hidden" id="selectedDuplicateId" value="">
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-3">
|
|
<button onclick="closeDisposeModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
|
취소
|
|
</button>
|
|
<button onclick="confirmDispose()" class="px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600">
|
|
폐기
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 검토/수정 모달 -->
|
|
<div id="reviewModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
|
|
<div class="flex items-center justify-center min-h-screen p-4">
|
|
<div class="bg-white rounded-xl max-w-2xl w-full p-6 max-h-[90vh] overflow-y-auto">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">부적합 검토 및 수정</h3>
|
|
<button onclick="closeReviewModal()" class="text-gray-400 hover:text-gray-600">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<!-- 원본 정보 표시 -->
|
|
<div class="bg-gray-50 p-4 rounded-lg">
|
|
<h4 class="font-medium text-gray-700 mb-2">원본 정보</h4>
|
|
<div id="originalInfo" class="text-sm text-gray-600">
|
|
<!-- 원본 정보가 여기에 표시됩니다 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AI 분류 추천 -->
|
|
<div class="bg-purple-50 border border-purple-200 rounded-lg p-3">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm font-medium text-purple-700">
|
|
<i class="fas fa-robot mr-1"></i>AI 분류 추천
|
|
</span>
|
|
<button id="aiClassifyBtn" onclick="aiClassifyCurrentIssue()"
|
|
class="px-3 py-1 bg-purple-500 text-white text-xs rounded-lg hover:bg-purple-600 transition-colors">
|
|
<i class="fas fa-magic mr-1"></i>AI 분석
|
|
</button>
|
|
</div>
|
|
<div id="aiClassifyLoading" class="hidden mt-2 text-center">
|
|
<i class="fas fa-spinner fa-spin text-purple-500 mr-1"></i>
|
|
<span class="text-xs text-purple-600">AI 분석 중...</span>
|
|
</div>
|
|
<div id="aiClassifyResult" class="hidden mt-2 text-sm text-purple-800 space-y-1">
|
|
<!-- AI 결과가 여기에 표시됩니다 -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 수정 폼 -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">프로젝트</label>
|
|
<select id="reviewProjectId" class="w-full px-3 py-2 border border-gray-300 rounded-lg">
|
|
<option value="">프로젝트 선택</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">카테고리</label>
|
|
<select id="reviewCategory" class="w-full px-3 py-2 border border-gray-300 rounded-lg">
|
|
<option value="material_missing">자재 누락</option>
|
|
<option value="design_error">설계 오류</option>
|
|
<option value="incoming_defect">반입 불량</option>
|
|
<option value="inspection_miss">검사 누락</option>
|
|
<option value="etc">기타</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">부적합명</label>
|
|
<input type="text" id="reviewTitle" class="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
|
placeholder="부적합의 간단한 제목을 입력하세요...">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">상세 내용</label>
|
|
<textarea id="reviewDescription" rows="4" class="w-full px-3 py-2 border border-gray-300 rounded-lg"
|
|
placeholder="부적합에 대한 상세한 설명을 입력하세요..."></textarea>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-3">
|
|
<button onclick="closeReviewModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
|
취소
|
|
</button>
|
|
<button onclick="saveReview()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
|
|
검토 완료
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 상태 결정 모달 -->
|
|
<div id="statusModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
|
|
<div class="flex items-center justify-center min-h-screen p-4">
|
|
<div class="bg-white rounded-xl max-w-md w-full p-6">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-semibold text-gray-900">최종 상태 결정</h3>
|
|
<button onclick="closeStatusModal()" class="text-gray-400 hover:text-gray-600">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">상태 선택</label>
|
|
<div class="space-y-2">
|
|
<label class="flex items-center">
|
|
<input type="radio" name="finalStatus" value="in_progress" class="mr-2" onchange="toggleCompletionPhotoSection()">
|
|
<span class="text-sm">🔄 진행 중 (관리함으로 이동)</span>
|
|
</label>
|
|
<label class="flex items-center">
|
|
<input type="radio" name="finalStatus" value="completed" class="mr-2" onchange="toggleCompletionPhotoSection()">
|
|
<span class="text-sm">✅ 완료됨 (관리함으로 이동)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 완료 관련 추가 정보 (완료 상태 선택 시에만 표시) -->
|
|
<div id="completionSection" class="hidden space-y-4">
|
|
<!-- 완료 사진 업로드 -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-camera text-green-500 mr-1"></i>완료 사진 (1장, 선택사항)
|
|
</label>
|
|
<input type="file" id="completionPhotoInput" accept="image/*" capture="environment"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm"
|
|
onchange="handleCompletionPhotoSelect(event)">
|
|
<div id="completionPhotoPreview" class="mt-2 hidden">
|
|
<img id="completionPhotoImg" src="" alt="완료 사진 미리보기"
|
|
class="w-full max-h-40 object-cover rounded-lg border">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 해결방안 입력 -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-lightbulb text-yellow-500 mr-1"></i>해결방안 (선택사항)
|
|
</label>
|
|
<textarea id="solutionInput" rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm"
|
|
placeholder="어떻게 해결하였는지 입력하세요... (빈칸으로 두고 관리함에서 입력해도 됩니다)"></textarea>
|
|
</div>
|
|
|
|
<!-- 해결한 부서 -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-building text-blue-500 mr-1"></i>상기 문제를 해결한 부서 (선택사항)
|
|
</label>
|
|
<select id="responsibleDepartmentInput" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm">
|
|
<option value="">부서 선택 (관리함에서 입력 가능)</option>
|
|
<option value="production">생산</option>
|
|
<option value="quality">품질</option>
|
|
<option value="purchasing">구매</option>
|
|
<option value="design">설계</option>
|
|
<option value="sales">영업</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- 해결한 사람 -->
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">
|
|
<i class="fas fa-user text-purple-500 mr-1"></i>해결한 사람 (선택사항)
|
|
</label>
|
|
<input type="text" id="responsiblePersonInput"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm"
|
|
placeholder="담당자 이름 입력 (관리함에서 입력 가능)">
|
|
</div>
|
|
|
|
<div class="bg-blue-50 p-3 rounded-lg">
|
|
<p class="text-xs text-blue-600">
|
|
<i class="fas fa-info-circle mr-1"></i>
|
|
위 정보들은 선택사항입니다. 빈칸으로 두고 관리함에서 나중에 입력하셔도 됩니다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex justify-end space-x-3">
|
|
<button onclick="closeStatusModal()" class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
|
취소
|
|
</button>
|
|
<button onclick="confirmStatus()" class="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600">
|
|
상태 변경
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scripts -->
|
|
<script src="/static/js/date-utils.js?v=20260213"></script>
|
|
<script src="/static/js/core/permissions.js?v=20260213"></script>
|
|
<script src="/static/js/components/common-header.js?v=20260306"></script>
|
|
<script src="/static/js/core/page-manager.js?v=20260306"></script>
|
|
<script src="/static/js/components/mobile-calendar.js?v=20260306"></script>
|
|
<script src="/static/js/utils/issue-helpers.js?v=20260306"></script>
|
|
<script src="/static/js/utils/photo-modal.js?v=20260306"></script>
|
|
<script src="/static/js/utils/toast.js?v=20260306"></script>
|
|
<script src="/static/js/components/mobile-bottom-nav.js?v=20260306"></script>
|
|
<script src="/static/js/api.js?v=20260306"></script>
|
|
<script src="/static/js/pages/issues-inbox.js?v=20260306"></script>
|
|
</body>
|
|
</html>
|