feat: 체크리스트 이미지 미리보기 기능 구현

- 체크리스트 섹션에 이미지 썸네일 미리보기 추가 (16x16)
- 대시보드 상단 체크리스트 카드에 이미지 미리보기 기능 추가
- 이미지 클릭 시 전체 화면 모달로 확대 보기
- 백엔드 image_url 컬럼을 TEXT 타입으로 변경하여 Base64 이미지 지원
- 파일 업로드를 이미지만 지원하도록 단순화 (file_url, file_name 제거)
- 422 validation 오류 해결 및 상세 로깅 추가
- 체크리스트 렌더링 누락 문제 해결
This commit is contained in:
hyungi
2025-09-23 07:49:54 +09:00
parent 5c9ea92fb8
commit f80995c1ec
22 changed files with 2635 additions and 930 deletions

View File

@@ -162,28 +162,15 @@
}
// Todo 항목 로드
function loadTodoItems() {
// 임시 데이터 (실제로는 API에서 가져옴)
const todoItems = [
{
id: 1,
content: '프로젝트 기획서 작성',
photo: null,
start_date: '2024-01-20',
status: 'scheduled',
created_at: '2024-01-15'
},
{
id: 2,
content: '팀 미팅 준비',
photo: null,
start_date: '2024-01-18',
status: 'active',
created_at: '2024-01-16'
}
];
renderTodoItems(todoItems);
async function loadTodoItems() {
try {
// API에서 Todo 카테고리 항목들만 가져오기
const items = await TodoAPI.getTodos(null, 'todo');
renderTodoItems(items);
} catch (error) {
console.error('Todo 항목 로드 실패:', error);
renderTodoItems([]);
}
}
// Todo 항목 렌더링
@@ -218,10 +205,10 @@
<!-- 내용 -->
<div class="flex-1 min-w-0">
<h4 class="text-gray-900 font-medium mb-2">${item.content}</h4>
<h4 class="text-gray-900 font-medium mb-2">${item.title}</h4>
<div class="flex items-center space-x-4 text-sm text-gray-500">
<span>
<i class="fas fa-calendar mr-1"></i>시작일: ${formatDate(item.start_date)}
<i class="fas fa-calendar mr-1"></i>마감일: ${formatDate(item.due_date)}
</span>
<span>
<i class="fas fa-clock mr-1"></i>등록: ${formatDate(item.created_at)}
@@ -232,14 +219,17 @@
<!-- 액션 버튼 -->
<div class="flex-shrink-0 flex space-x-2">
${item.status !== 'completed' ? `
<button onclick="startTodo(${item.id})" class="text-blue-500 hover:text-blue-700" title="시작하기">
<i class="fas fa-play"></i>
<button onclick="delayTodo('${item.id}')" class="text-orange-500 hover:text-orange-700" title="지연하기">
<i class="fas fa-clock"></i>
</button>
<button onclick="completeTodo(${item.id})" class="text-green-500 hover:text-green-700" title="완료하기">
<button onclick="completeTodo('${item.id}')" class="text-green-500 hover:text-green-700" title="완료하기">
<i class="fas fa-check"></i>
</button>
<button onclick="splitTodo('${item.id}')" class="text-purple-500 hover:text-purple-700" title="분할하기">
<i class="fas fa-cut"></i>
</button>
` : ''}
<button onclick="editTodo(${item.id})" class="text-gray-400 hover:text-blue-500" title="수정하기">
<button onclick="editTodo('${item.id}')" class="text-gray-400 hover:text-blue-500" title="수정하기">
<i class="fas fa-edit"></i>
</button>
</div>
@@ -270,26 +260,68 @@
// 날짜 포맷팅
function formatDate(dateString) {
if (!dateString) return '날짜 없음';
const date = new Date(dateString);
if (isNaN(date.getTime())) return '날짜 없음';
return date.toLocaleDateString('ko-KR');
}
// Todo 시작
function startTodo(id) {
console.log('Todo 시작:', id);
// TODO: API 호출하여 상태를 'active'로 변경
// Todo 지연
function delayTodo(id) {
const newDate = prompt('새로운 시작 날짜를 입력하세요 (YYYY-MM-DD):');
if (newDate && /^\d{4}-\d{2}-\d{2}$/.test(newDate)) {
// TODO: API 호출하여 due_date 업데이트
console.log('Todo 지연:', id, '새 날짜:', newDate);
alert(`할 일이 ${newDate}로 지연되었습니다.`);
loadTodoItems(); // 목록 새로고침
} else if (newDate) {
alert('올바른 날짜 형식을 입력해주세요 (YYYY-MM-DD)');
}
}
// Todo 완료
function completeTodo(id) {
console.log('Todo 완료:', id);
// TODO: API 호출하여 상태를 'completed'로 변경
async function completeTodo(id) {
try {
// TODO: API 호출하여 상태를 'completed'로 변경
console.log('Todo 완료:', id);
alert('할 일이 완료되었습니다!');
loadTodoItems(); // 목록 새로고침
} catch (error) {
console.error('완료 처리 실패:', error);
alert('완료 처리에 실패했습니다.');
}
}
// Todo 분할
function splitTodo(id) {
const splitCount = prompt('몇 개로 분할하시겠습니까? (2-5개):');
const count = parseInt(splitCount);
if (count >= 2 && count <= 5) {
const subtasks = [];
for (let i = 1; i <= count; i++) {
const subtask = prompt(`${i}번째 세부 작업을 입력하세요:`);
if (subtask) {
subtasks.push(subtask.trim());
}
}
if (subtasks.length > 0) {
// TODO: API 호출하여 원본 삭제 후 세부 작업들 생성
console.log('Todo 분할:', id, '세부 작업들:', subtasks);
alert(`할 일이 ${subtasks.length}개의 세부 작업으로 분할되었습니다.`);
loadTodoItems(); // 목록 새로고침
}
} else if (splitCount) {
alert('2-5개 사이의 숫자를 입력해주세요.');
}
}
// Todo 편집
function editTodo(id) {
console.log('Todo 편집:', id);
// TODO: 편집 모달 또는 페이지로 이동
alert('편집 기능은 준비 중입니다.');
}
// 필터링
@@ -306,5 +338,7 @@
// 전역 함수 등록
window.goToDashboard = goToDashboard;
</script>
<script src="static/js/api.js?v=20250921110800"></script>
<script src="static/js/auth.js?v=20250921110800"></script>
</body>
</html>