Files
Todo-Project/frontend/dashboard.html
Hyungi Ahn 761757c12e Initial commit: Todo Project with dashboard, classification center, and upload functionality
- 📱 PWA 지원: 홈화면 추가 가능한 Progressive Web App
- 🎨 M-Project 색상 스키마: 하늘색, 주황색, 회색, 흰색 일관된 디자인
- 📊 대시보드: 데스크톱 캘린더 뷰 + 모바일 일일 뷰 반응형 디자인
- 📥 분류 센터: Gmail 스타일 받은편지함으로 스마트 분류 시스템
- 🤖 AI 분류 제안: 키워드 기반 자동 분류 제안 및 일괄 처리
- 📷 업로드 모달: 데스크톱(파일 선택) + 모바일(카메라/갤러리) 최적화
- 🏷️ 3가지 분류: Todo(시작일), 캘린더(마감일), 체크리스트(무기한)
- 📋 체크리스트: 진행률 표시 및 완료 토글 기능
- 🔄 시놀로지 연동 준비: 메일플러스 연동을 위한 구조 설계
- 📱 반응형 UI: 모든 페이지 모바일 최적화 완료
2025-09-19 08:52:49 +09:00

938 lines
36 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>대시보드 - Todo Project</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">
<style>
:root {
--primary: #3b82f6; /* 하늘색 */
--primary-dark: #2563eb; /* 진한 하늘색 */
--success: #10b981; /* 초록색 */
--warning: #f59e0b; /* 주황색 */
--danger: #ef4444; /* 빨간색 */
--gray-50: #f9fafb; /* 연한 회색 */
--gray-100: #f3f4f6; /* 회색 */
--gray-200: #e5e7eb; /* 중간 회색 */
--gray-300: #d1d5db; /* 진한 회색 */
}
body {
background-color: var(--gray-50);
}
.btn-primary {
background-color: var(--primary);
color: white;
transition: all 0.2s;
}
.btn-primary:hover {
background-color: var(--primary-dark);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
/* 캘린더 스타일 */
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 1px;
background-color: var(--gray-200);
border-radius: 0.5rem;
overflow: hidden;
}
.calendar-day {
background-color: white;
min-height: 120px;
padding: 8px;
position: relative;
transition: all 0.2s;
}
.calendar-day:hover {
background-color: var(--gray-50);
}
.calendar-day.other-month {
background-color: #fafafa;
color: #9ca3af;
}
.calendar-day.today {
background-color: #eff6ff;
border: 2px solid var(--primary);
}
.calendar-header {
background-color: var(--gray-100);
padding: 12px 8px;
text-align: center;
font-weight: 600;
color: var(--gray-700);
}
.day-number {
font-weight: 600;
margin-bottom: 4px;
}
.day-items {
display: flex;
flex-direction: column;
gap: 2px;
}
.day-item {
font-size: 10px;
padding: 2px 4px;
border-radius: 3px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
.day-item.todo {
background-color: #dbeafe;
color: #1e40af;
border-left: 3px solid var(--primary);
}
.day-item.calendar {
background-color: #fef3c7;
color: #92400e;
border-left: 3px solid var(--warning);
}
/* 모바일 일일 뷰 */
.daily-view {
display: none;
}
.daily-item {
background: white;
border-radius: 0.75rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.2s;
margin-bottom: 12px;
}
.daily-item:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.time-indicator {
width: 4px;
height: 100%;
border-radius: 2px;
}
.time-indicator.todo {
background-color: var(--primary);
}
.time-indicator.calendar {
background-color: var(--warning);
}
/* 체크리스트 스타일 */
.checklist-item {
background: white;
border-radius: 0.5rem;
padding: 12px;
margin-bottom: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.2s;
}
.checklist-item:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.checklist-item.completed {
opacity: 0.6;
background-color: #f9fafb;
}
.checkbox-custom {
width: 18px;
height: 18px;
border: 2px solid #d1d5db;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
}
.checkbox-custom.checked {
background-color: var(--success);
border-color: var(--success);
color: white;
}
/* 반응형 디자인 */
@media (max-width: 768px) {
.desktop-view {
display: none !important;
}
.daily-view {
display: block !important;
}
.calendar-day {
min-height: 80px;
padding: 4px;
}
.day-item {
font-size: 9px;
padding: 1px 3px;
}
/* 모바일 업로드 모달 */
.desktop-upload {
display: none !important;
}
.mobile-upload {
display: block !important;
}
}
@media (min-width: 769px) {
/* 데스크톱 업로드 모달 */
.mobile-upload {
display: none !important;
}
.desktop-upload {
display: block !important;
}
}
@media (max-width: 640px) {
.calendar-day {
min-height: 60px;
padding: 2px;
}
.day-number {
font-size: 12px;
}
}
</style>
</head>
<body>
<div class="min-h-screen">
<!-- 헤더 -->
<header class="bg-white shadow-sm border-b">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center">
<button onclick="goBack()" class="mr-4 text-gray-500 hover:text-gray-700">
<i class="fas fa-arrow-left text-xl"></i>
</button>
<i class="fas fa-chart-line text-2xl text-blue-500 mr-3"></i>
<h1 class="text-xl font-semibold text-gray-800">대시보드</h1>
<span class="ml-3 text-sm text-gray-500" id="currentDate"></span>
</div>
<div class="flex items-center space-x-4">
<button onclick="openUploadModal()" class="btn-primary px-4 py-2 rounded-lg text-sm">
<i class="fas fa-plus mr-1"></i>새 항목
</button>
<button onclick="goToClassify()" class="text-purple-600 hover:text-purple-800 font-medium text-sm">
<i class="fas fa-inbox mr-1"></i>분류 센터
<span class="ml-1 px-2 py-1 bg-red-100 text-red-800 text-xs rounded-full">3</span>
</button>
<button onclick="goToToday()" class="text-sm text-blue-600 hover:text-blue-800">
<i class="fas fa-calendar-day mr-1"></i>오늘
</button>
<span class="text-sm text-gray-600" id="currentUser"></span>
<button onclick="logout()" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-sign-out-alt"></i>
</button>
</div>
</div>
</div>
</header>
<!-- 메인 컨텐츠 -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- 데스크톱 뷰 -->
<div class="desktop-view">
<!-- 캘린더 네비게이션 -->
<div class="bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="flex justify-between items-center">
<div class="flex items-center space-x-4">
<button onclick="previousMonth()" class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg">
<i class="fas fa-chevron-left"></i>
</button>
<h2 class="text-2xl font-bold text-gray-800" id="currentMonth"></h2>
<button onclick="nextMonth()" class="p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg">
<i class="fas fa-chevron-right"></i>
</button>
</div>
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-2 text-sm">
<div class="w-3 h-3 bg-blue-200 border-l-4 border-blue-500 rounded-sm"></div>
<span class="text-gray-600">Todo</span>
</div>
<div class="flex items-center space-x-2 text-sm">
<div class="w-3 h-3 bg-yellow-200 border-l-4 border-yellow-500 rounded-sm"></div>
<span class="text-gray-600">캘린더</span>
</div>
</div>
</div>
</div>
<!-- 캘린더 그리드 -->
<div class="bg-white rounded-xl shadow-sm p-6 mb-8">
<div class="calendar-grid">
<!-- 요일 헤더 -->
<div class="calendar-header"></div>
<div class="calendar-header"></div>
<div class="calendar-header"></div>
<div class="calendar-header"></div>
<div class="calendar-header"></div>
<div class="calendar-header"></div>
<div class="calendar-header"></div>
<!-- 캘린더 날짜들 -->
<div id="calendarDays"></div>
</div>
</div>
<!-- 체크리스트 섹션 -->
<div class="bg-white rounded-xl shadow-sm p-6">
<div class="flex justify-between items-center mb-6">
<h3 class="text-lg font-semibold text-gray-800">
<i class="fas fa-check-square text-green-500 mr-2"></i>체크리스트
</h3>
<div class="text-sm text-gray-600">
<span id="checklistProgress">0/0 완료</span>
</div>
</div>
<div id="checklistItems" class="max-h-96 overflow-y-auto">
<!-- 체크리스트 항목들이 여기에 추가됩니다 -->
</div>
</div>
</div>
<!-- 모바일 뷰 -->
<div class="daily-view">
<!-- 오늘 날짜 -->
<div class="bg-white rounded-xl shadow-sm p-6 mb-6">
<div class="text-center">
<h2 class="text-2xl font-bold text-gray-800" id="todayDate"></h2>
<p class="text-gray-600" id="todayWeekday"></p>
</div>
</div>
<!-- 오늘의 일정 -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-gray-800 mb-4">
<i class="fas fa-calendar-day text-blue-500 mr-2"></i>오늘의 일정
</h3>
<div id="todayItems">
<!-- 오늘의 항목들이 여기에 추가됩니다 -->
</div>
</div>
<!-- 모바일 체크리스트 -->
<div class="bg-white rounded-xl shadow-sm p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-gray-800">
<i class="fas fa-check-square text-green-500 mr-2"></i>체크리스트
</h3>
<div class="text-sm text-gray-600">
<span id="mobileChecklistProgress">0/0</span>
</div>
</div>
<div id="mobileChecklistItems">
<!-- 모바일 체크리스트 항목들 -->
</div>
</div>
</div>
</main>
</div>
<!-- 업로드 모달 -->
<div id="uploadModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full max-h-[90vh] overflow-y-auto">
<!-- 모달 헤더 -->
<div class="flex justify-between items-center p-6 border-b">
<h3 class="text-lg font-semibold text-gray-800">
<i class="fas fa-plus-circle text-blue-500 mr-2"></i>새 항목 등록
</h3>
<button onclick="closeUploadModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<!-- 모달 내용 -->
<div class="p-6">
<form id="uploadForm" class="space-y-4">
<!-- 메모 입력 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">메모</label>
<input type="text" id="uploadContent" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="메모를 입력하세요..." required>
</div>
<!-- 사진 업로드 영역 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">사진 (선택사항)</label>
<!-- 데스크톱용 파일 선택 -->
<div class="desktop-upload">
<div class="border-2 border-dashed border-gray-200 rounded-lg p-6 text-center hover:border-blue-300 transition-colors">
<i class="fas fa-cloud-upload-alt text-3xl text-gray-400 mb-3"></i>
<p class="text-gray-600 mb-2">파일을 선택하거나 드래그하여 업로드</p>
<button type="button" onclick="selectFile()" class="text-blue-600 hover:text-blue-800 font-medium">
파일 선택
</button>
<input type="file" id="desktopFileInput" accept="image/*" class="hidden">
</div>
</div>
<!-- 모바일용 카메라/갤러리 선택 -->
<div class="mobile-upload hidden">
<div class="grid grid-cols-2 gap-3">
<button type="button" onclick="openCamera()" class="border-2 border-dashed border-gray-200 rounded-lg p-4 text-center hover:border-blue-300 transition-colors">
<i class="fas fa-camera text-2xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-600">카메라</p>
</button>
<button type="button" onclick="openGallery()" class="border-2 border-dashed border-gray-200 rounded-lg p-4 text-center hover:border-blue-300 transition-colors">
<i class="fas fa-images text-2xl text-gray-400 mb-2"></i>
<p class="text-sm text-gray-600">갤러리</p>
</button>
</div>
<input type="file" id="cameraInput" accept="image/*" capture="camera" class="hidden">
<input type="file" id="galleryInput" accept="image/*" class="hidden">
</div>
<!-- 사진 미리보기 -->
<div id="photoPreview" class="hidden mt-4">
<div class="relative">
<img id="previewImage" class="w-full h-48 object-cover rounded-lg" alt="미리보기">
<button type="button" onclick="removePhoto()" class="absolute top-2 right-2 bg-red-500 text-white rounded-full w-8 h-8 flex items-center justify-center hover:bg-red-600">
<i class="fas fa-times text-sm"></i>
</button>
</div>
<div class="mt-2 text-sm text-gray-600" id="photoInfo"></div>
</div>
</div>
<!-- 버튼 -->
<div class="flex space-x-3 pt-4">
<button type="submit" class="btn-primary flex-1 py-3 px-4 rounded-lg font-medium">
<i class="fas fa-plus mr-2"></i>등록하기
</button>
<button type="button" onclick="closeUploadModal()" class="px-4 py-3 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">
취소
</button>
</div>
</form>
</div>
</div>
</div>
<!-- 로딩 오버레이 -->
<div id="loadingOverlay" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-60">
<div class="bg-white rounded-lg p-6 flex items-center space-x-3">
<div class="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
<span class="text-gray-700">처리 중...</span>
</div>
</div>
<!-- JavaScript -->
<script src="static/js/auth.js"></script>
<script src="static/js/image-utils.js"></script>
<script>
let currentDate = new Date();
let calendarData = {};
let checklistData = [];
// 페이지 초기화
document.addEventListener('DOMContentLoaded', () => {
checkAuthStatus();
initializeDashboard();
});
// 대시보드 초기화
function initializeDashboard() {
updateCurrentDate();
loadCalendarData();
loadChecklistData();
renderCalendar();
renderDailyView();
renderChecklist();
}
// 현재 날짜 업데이트
function updateCurrentDate() {
const now = new Date();
const options = { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' };
document.getElementById('currentDate').textContent = now.toLocaleDateString('ko-KR', {
month: 'long',
day: 'numeric'
});
document.getElementById('currentMonth').textContent = currentDate.toLocaleDateString('ko-KR', {
year: 'numeric',
month: 'long'
});
document.getElementById('todayDate').textContent = now.toLocaleDateString('ko-KR', {
month: 'long',
day: 'numeric'
});
document.getElementById('todayWeekday').textContent = now.toLocaleDateString('ko-KR', {
weekday: 'long'
});
}
// 캘린더 데이터 로드
function loadCalendarData() {
// 임시 데이터
calendarData = {
'2024-01-20': [
{ type: 'todo', title: '프로젝트 시작', time: '09:00' },
{ type: 'calendar', title: '회의 준비', time: '14:00' }
],
'2024-01-22': [
{ type: 'calendar', title: '보고서 제출', time: '17:00' }
],
'2024-01-25': [
{ type: 'todo', title: '문서 검토', time: '10:00' },
{ type: 'todo', title: '팀 미팅', time: '15:00' },
{ type: 'calendar', title: '월말 마감', time: '18:00' }
]
};
}
// 체크리스트 데이터 로드
function loadChecklistData() {
checklistData = [
{ id: 1, title: '책상 정리하기', completed: false },
{ id: 2, title: '운동 계획 세우기', completed: true },
{ id: 3, title: '독서 목록 만들기', completed: false },
{ id: 4, title: '이메일 정리', completed: false },
{ id: 5, title: '비타민 구매', completed: true }
];
}
// 캘린더 렌더링
function renderCalendar() {
const calendarDays = document.getElementById('calendarDays');
if (!calendarDays) return;
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
// 월의 첫 번째 날과 마지막 날
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
// 캘린더 시작일 (이전 달의 마지막 주 포함)
const startDate = new Date(firstDay);
startDate.setDate(startDate.getDate() - firstDay.getDay());
// 캘린더 종료일 (다음 달의 첫 주 포함)
const endDate = new Date(lastDay);
endDate.setDate(endDate.getDate() + (6 - lastDay.getDay()));
let html = '';
const today = new Date();
for (let date = new Date(startDate); date <= endDate; date.setDate(date.getDate() + 1)) {
const dateStr = date.toISOString().split('T')[0];
const isCurrentMonth = date.getMonth() === month;
const isToday = date.toDateString() === today.toDateString();
const dayData = calendarData[dateStr] || [];
html += `
<div class="calendar-day ${!isCurrentMonth ? 'other-month' : ''} ${isToday ? 'today' : ''}"
onclick="selectDate('${dateStr}')">
<div class="day-number">${date.getDate()}</div>
<div class="day-items">
${dayData.map(item => `
<div class="day-item ${item.type}" title="${item.title} (${item.time})">
${item.title}
</div>
`).join('')}
</div>
</div>
`;
}
calendarDays.innerHTML = html;
}
// 일일 뷰 렌더링 (모바일)
function renderDailyView() {
const todayItems = document.getElementById('todayItems');
if (!todayItems) return;
const today = new Date().toISOString().split('T')[0];
const todayData = calendarData[today] || [];
if (todayData.length === 0) {
todayItems.innerHTML = `
<div class="text-center py-8 text-gray-500">
<i class="fas fa-calendar-day text-3xl mb-3 opacity-50"></i>
<p>오늘 예정된 일정이 없습니다.</p>
</div>
`;
return;
}
todayItems.innerHTML = todayData.map(item => `
<div class="daily-item p-4">
<div class="flex items-center space-x-3">
<div class="time-indicator ${item.type}"></div>
<div class="flex-1">
<h4 class="font-medium text-gray-900">${item.title}</h4>
<p class="text-sm text-gray-600">
<i class="fas fa-clock mr-1"></i>${item.time}
</p>
</div>
<div class="text-xs px-2 py-1 rounded-full ${item.type === 'todo' ? 'bg-blue-100 text-blue-800' : 'bg-yellow-100 text-yellow-800'}">
${item.type === 'todo' ? 'Todo' : '캘린더'}
</div>
</div>
</div>
`).join('');
}
// 체크리스트 렌더링
function renderChecklist() {
const checklistItems = document.getElementById('checklistItems');
const mobileChecklistItems = document.getElementById('mobileChecklistItems');
const checklistProgress = document.getElementById('checklistProgress');
const mobileChecklistProgress = document.getElementById('mobileChecklistProgress');
const completed = checklistData.filter(item => item.completed).length;
const total = checklistData.length;
const progressText = `${completed}/${total} 완료`;
if (checklistProgress) checklistProgress.textContent = progressText;
if (mobileChecklistProgress) mobileChecklistProgress.textContent = `${completed}/${total}`;
const html = checklistData.map(item => `
<div class="checklist-item ${item.completed ? 'completed' : ''}">
<div class="flex items-center space-x-3">
<div class="checkbox-custom ${item.completed ? 'checked' : ''}"
onclick="toggleChecklistItem(${item.id})">
${item.completed ? '<i class="fas fa-check text-xs"></i>' : ''}
</div>
<span class="flex-1 ${item.completed ? 'line-through text-gray-500' : 'text-gray-900'}">${item.title}</span>
</div>
</div>
`).join('');
if (checklistItems) checklistItems.innerHTML = html;
if (mobileChecklistItems) mobileChecklistItems.innerHTML = html;
}
// 체크리스트 항목 토글
function toggleChecklistItem(id) {
const item = checklistData.find(item => item.id === id);
if (item) {
item.completed = !item.completed;
renderChecklist();
// TODO: API 호출
}
}
// 이전 달
function previousMonth() {
currentDate.setMonth(currentDate.getMonth() - 1);
updateCurrentDate();
renderCalendar();
}
// 다음 달
function nextMonth() {
currentDate.setMonth(currentDate.getMonth() + 1);
updateCurrentDate();
renderCalendar();
}
// 오늘로 이동
function goToToday() {
currentDate = new Date();
updateCurrentDate();
renderCalendar();
renderDailyView();
}
// 날짜 선택
function selectDate(dateStr) {
console.log('선택된 날짜:', dateStr);
// TODO: 선택된 날짜의 상세 정보 표시
}
// 뒤로 가기
function goBack() {
window.location.href = 'index.html';
}
// 분류 센터로 이동
function goToClassify() {
window.location.href = 'classify.html';
}
// 업로드 모달 관련 변수
let currentPhoto = null;
// 업로드 모달 열기
function openUploadModal() {
document.getElementById('uploadModal').classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
// 업로드 모달 닫기
function closeUploadModal() {
document.getElementById('uploadModal').classList.add('hidden');
document.body.style.overflow = 'auto';
clearUploadForm();
}
// 업로드 폼 초기화
function clearUploadForm() {
document.getElementById('uploadForm').reset();
removePhoto();
}
// 파일 선택 (데스크톱)
function selectFile() {
document.getElementById('desktopFileInput').click();
}
// 카메라 열기 (모바일)
function openCamera() {
document.getElementById('cameraInput').click();
}
// 갤러리 열기 (모바일)
function openGallery() {
document.getElementById('galleryInput').click();
}
// 사진 제거
function removePhoto() {
currentPhoto = null;
const previewContainer = document.getElementById('photoPreview');
const previewImage = document.getElementById('previewImage');
if (previewContainer) {
previewContainer.classList.add('hidden');
}
if (previewImage) {
previewImage.src = '';
}
// 파일 입력 초기화
const inputs = ['desktopFileInput', 'cameraInput', 'galleryInput'];
inputs.forEach(id => {
const input = document.getElementById(id);
if (input) input.value = '';
});
}
// 사진 업로드 처리
async function handlePhotoUpload(event) {
const files = event.target.files;
if (!files || files.length === 0) return;
const file = files[0];
try {
showLoading(true);
// 이미지 압축 (ImageUtils가 있는 경우)
let processedImage;
if (window.ImageUtils) {
processedImage = await ImageUtils.compressImage(file, {
maxWidth: 800,
maxHeight: 600,
quality: 0.8
});
} else {
// 기본 처리
processedImage = await fileToBase64(file);
}
currentPhoto = processedImage;
// 미리보기 표시
const previewContainer = document.getElementById('photoPreview');
const previewImage = document.getElementById('previewImage');
const photoInfo = document.getElementById('photoInfo');
if (previewContainer && previewImage) {
previewImage.src = processedImage;
previewContainer.classList.remove('hidden');
}
if (photoInfo) {
const fileSize = Math.round(file.size / 1024);
photoInfo.textContent = `${file.name} (${fileSize}KB)`;
}
} catch (error) {
console.error('이미지 처리 실패:', error);
alert('이미지 처리에 실패했습니다.');
} finally {
showLoading(false);
}
}
// 파일을 Base64로 변환
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
// 업로드 폼 제출
async function handleUploadSubmit(event) {
event.preventDefault();
const content = document.getElementById('uploadContent').value.trim();
if (!content) {
alert('메모를 입력해주세요.');
return;
}
try {
showLoading(true);
const itemData = {
content: content,
photo: currentPhoto,
created_at: new Date().toISOString()
};
// TODO: API 호출하여 항목 저장
console.log('새 항목 등록:', itemData);
// 성공 메시지
alert('항목이 등록되었습니다!');
// 모달 닫기 및 데이터 새로고침
closeUploadModal();
initializeDashboard();
} catch (error) {
console.error('항목 등록 실패:', error);
alert('항목 등록에 실패했습니다.');
} finally {
showLoading(false);
}
}
// 로딩 표시
function showLoading(show) {
const overlay = document.getElementById('loadingOverlay');
if (overlay) {
if (show) {
overlay.classList.remove('hidden');
} else {
overlay.classList.add('hidden');
}
}
}
// 이벤트 리스너 설정
document.addEventListener('DOMContentLoaded', () => {
// 파일 입력 이벤트 리스너
const fileInputs = ['desktopFileInput', 'cameraInput', 'galleryInput'];
fileInputs.forEach(id => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('change', handlePhotoUpload);
}
});
// 업로드 폼 이벤트 리스너
const uploadForm = document.getElementById('uploadForm');
if (uploadForm) {
uploadForm.addEventListener('submit', handleUploadSubmit);
}
// 드래그 앤 드롭 (데스크톱)
const desktopUpload = document.querySelector('.desktop-upload');
if (desktopUpload) {
desktopUpload.addEventListener('dragover', (e) => {
e.preventDefault();
e.currentTarget.classList.add('border-blue-300');
});
desktopUpload.addEventListener('dragleave', (e) => {
e.preventDefault();
e.currentTarget.classList.remove('border-blue-300');
});
desktopUpload.addEventListener('drop', (e) => {
e.preventDefault();
e.currentTarget.classList.remove('border-blue-300');
const files = e.dataTransfer.files;
if (files.length > 0) {
const input = document.getElementById('desktopFileInput');
if (input) {
input.files = files;
handlePhotoUpload({ target: input });
}
}
});
}
// 모달 외부 클릭 시 닫기
const modal = document.getElementById('uploadModal');
if (modal) {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeUploadModal();
}
});
}
});
// 전역 함수 등록
window.previousMonth = previousMonth;
window.nextMonth = nextMonth;
window.goToToday = goToToday;
window.selectDate = selectDate;
window.toggleChecklistItem = toggleChecklistItem;
window.goBack = goBack;
window.openUploadModal = openUploadModal;
window.closeUploadModal = closeUploadModal;
window.selectFile = selectFile;
window.openCamera = openCamera;
window.openGallery = openGallery;
window.removePhoto = removePhoto;
window.goToClassify = goToClassify;
</script>
</body>
</html>