프로토타입 파일 구조 개선 및 CSS 디렉토리 통합

- 중복 프로토타입 파일 삭제
- common.css/js를 css 디렉토리로 통합
- uiux.md 업데이트

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
doyeon 2025-10-20 13:50:09 +09:00
parent d1bb9d0231
commit 58ab3bac44
13 changed files with 1923 additions and 8166 deletions

View File

@ -1,307 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>메인 대시보드 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<div class="flex items-center gap-s">
<span class="material-icons text-primary" style="font-size: 28px;">store</span>
<span class="h3 text-primary">KT 이벤트 도우미</span>
</div>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Welcome Section -->
<section class="mb-l">
<h1 class="h2 mb-s">안녕하세요, <span id="userName">정우진</span>님!</h1>
<p class="body-l text-gray-700" id="businessName">우진이네 고깃집</p>
</section>
<!-- Active Events Section -->
<section class="mb-xl">
<div class="flex items-center justify-between mb-m">
<h2 class="h3">진행 중인 이벤트</h2>
<a href="#" class="body-s text-secondary" id="viewAllEvents">전체보기 &gt;</a>
</div>
<div id="activeEventsList" class="flex flex-col gap-m">
<!-- 이벤트 카드들이 JavaScript로 생성됨 -->
</div>
</section>
<!-- Quick Actions Section -->
<section class="mb-xl">
<h2 class="h3 mb-m">빠른 실행</h2>
<div class="grid gap-m">
<!-- 새 이벤트 만들기 -->
<div class="col-2 card card-clickable" onclick="NavManager.navigate('03', '이벤트목적선택')">
<div class="card-body flex flex-col items-center justify-center gap-s p-l">
<span class="material-icons text-primary" style="font-size: 40px;">add_circle</span>
<span class="body-l text-semibold">새 이벤트<br/>만들기</span>
</div>
</div>
<!-- 이벤트 관리 -->
<div class="col-2 card card-clickable" onclick="showEventManagement()">
<div class="card-body flex flex-col items-center justify-center gap-s p-l">
<span class="material-icons text-secondary" style="font-size: 40px;">event_note</span>
<span class="body-l text-semibold">이벤트<br/>관리</span>
</div>
</div>
<!-- 통계 보기 -->
<div class="col-2 card card-clickable" onclick="NavManager.navigate('21', '실시간대시보드')">
<div class="card-body flex flex-col items-center justify-center gap-s p-l">
<span class="material-icons text-success" style="font-size: 40px;">bar_chart</span>
<span class="body-l text-semibold">통계<br/>보기</span>
</div>
</div>
<!-- 도움말 -->
<div class="col-2 card card-clickable" onclick="showHelp()">
<div class="card-body flex flex-col items-center justify-center gap-s p-l">
<span class="material-icons text-warning" style="font-size: 40px;">help</span>
<span class="body-l text-semibold">도움말</span>
</div>
</div>
</div>
</section>
<!-- Tips Section -->
<section class="mb-xl">
<div class="card bg-secondary-light" style="border: none;">
<div class="card-body flex gap-m">
<span class="material-icons text-secondary" style="font-size: 32px;">lightbulb</span>
<div>
<h3 class="body-l text-semibold mb-xs text-secondary">AI 활용 팁</h3>
<p class="body-m text-gray-700">이벤트 목적을 명확히 입력하면 더 정확한 기획안을 받을 수 있어요!</p>
</div>
</div>
</div>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item active" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// 사용자 정보 로드
function loadUserInfo() {
const user = AppState.loadUser() || SampleData.user;
document.getElementById('userName').textContent = user.name;
document.getElementById('businessName').textContent = user.businessName;
}
// 진행 중인 이벤트 카드 렌더링
function renderActiveEvents() {
const events = SampleData.events.filter(e => e.status === 'in_progress');
const container = document.getElementById('activeEventsList');
if (events.length === 0) {
container.innerHTML = `
<div class="card bg-gray-100" style="border: none;">
<div class="card-body text-center p-xl">
<span class="material-icons text-gray-500" style="font-size: 48px;">event_busy</span>
<p class="body-m text-gray-600 mt-m">진행 중인 이벤트가 없습니다.</p>
<button class="btn btn-primary mt-m" onclick="NavManager.navigate('03', '이벤트목적선택')">
새 이벤트 만들기
</button>
</div>
</div>
`;
return;
}
container.innerHTML = events.slice(0, 3).map(event => {
const daysLeft = calculateDaysLeft(event.startDate);
const badgeClass = daysLeft <= 3 ? 'badge-error' : daysLeft <= 7 ? 'badge-warning' : 'badge-success';
return `
<div class="card card-clickable" onclick="showEventDetail('${event.id}')">
<div class="card-body">
<div class="flex items-center justify-between mb-m">
<h3 class="body-l text-semibold">${event.name}</h3>
<span class="badge ${badgeClass}">D-${daysLeft}</span>
</div>
<div class="flex items-center gap-m text-gray-700">
<div class="flex items-center gap-xs">
<span class="material-icons" style="font-size: 18px;">calendar_today</span>
<span class="body-s">${event.startDate}</span>
</div>
<div class="flex items-center gap-xs">
<span class="material-icons" style="font-size: 18px;">people</span>
<span class="body-s">${Utils.formatNumber(event.participants)}명</span>
</div>
</div>
<div class="mt-m">
<div class="progress-bar">
<div class="progress-fill" style="width: ${Math.min((event.participants / 200) * 100, 100)}%"></div>
</div>
<p class="body-s text-gray-600 mt-xs text-right">
목표 대비 ${Math.round((event.participants / 200) * 100)}%
</p>
</div>
</div>
</div>
`;
}).join('');
}
// D-Day 계산
function calculateDaysLeft(startDateStr) {
const parts = startDateStr.split('.');
const startDate = new Date(parts[0], parts[1] - 1, parts[2]);
const today = new Date();
const diffTime = startDate - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return Math.max(diffDays, 0);
}
// 이벤트 상세 보기
function showEventDetail(eventId) {
const event = SampleData.events.find(e => e.id === eventId);
if (!event) return;
UIManager.showModal({
title: event.name,
body: `
<div class="flex flex-col gap-m">
<div class="flex items-center gap-s">
<span class="material-icons text-gray-700">calendar_today</span>
<span class="body-m">${event.startDate} 시작</span>
</div>
<div class="flex items-center gap-s">
<span class="material-icons text-gray-700">people</span>
<span class="body-m">참여자 ${Utils.formatNumber(event.participants)}명</span>
</div>
<div class="flex items-center gap-s">
<span class="material-icons text-gray-700">info</span>
<span class="body-m">상태: ${event.status === 'in_progress' ? '진행 중' : '완료'}</span>
</div>
</div>
`,
confirmText: '이벤트 관리',
cancelText: '닫기',
onConfirm: () => {
NavManager.navigate('21', '실시간대시보드');
}
});
}
// 이벤트 관리
function showEventManagement() {
const allEvents = SampleData.events;
const eventListHTML = allEvents.map(event => {
const statusText = event.status === 'in_progress' ? '진행 중' : '완료';
const statusClass = event.status === 'in_progress' ? 'badge-success' : 'badge-gray';
return `
<div class="flex items-center justify-between p-m" style="border-bottom: 1px solid var(--color-gray-200);">
<div>
<p class="body-m text-semibold">${event.name}</p>
<p class="body-s text-gray-600">${event.startDate}</p>
</div>
<span class="badge ${statusClass}">${statusText}</span>
</div>
`;
}).join('');
UIManager.showBottomSheet({
content: `
<h2 class="h2 mb-l">이벤트 관리</h2>
<div class="mb-l">
${eventListHTML}
</div>
<button class="btn btn-primary btn-full" onclick="UIManager.closeBottomSheet(); NavManager.navigate('03', '이벤트목적선택');">
새 이벤트 만들기
</button>
`
});
}
// 도움말
function showHelp() {
UIManager.showModal({
title: 'KT 이벤트 도우미 사용 가이드',
body: `
<div class="flex flex-col gap-m">
<div>
<h3 class="body-l text-semibold mb-xs">1. 새 이벤트 만들기</h3>
<p class="body-s text-gray-700">AI가 자동으로 이벤트 기획안을 생성해드립니다.</p>
</div>
<div>
<h3 class="body-l text-semibold mb-xs">2. 이벤트 관리</h3>
<p class="body-s text-gray-700">진행 중이거나 완료된 이벤트를 확인하세요.</p>
</div>
<div>
<h3 class="body-l text-semibold mb-xs">3. 통계 보기</h3>
<p class="body-s text-gray-700">실시간 참여 현황과 성과를 분석합니다.</p>
</div>
</div>
`,
confirmText: '확인',
cancelText: null
});
}
// 페이지 초기화
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('home');
// 사용자 정보 로드
loadUserInfo();
// 진행 중인 이벤트 렌더링
renderActiveEvents();
// 전체보기 버튼
document.getElementById('viewAllEvents').addEventListener('click', (e) => {
e.preventDefault();
showEventManagement();
});
});
</script>
</body>
</html>

View File

@ -1,407 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>회원가입 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<button class="back-button" aria-label="뒤로가기">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="top-bar-title">회원가입</h1>
<div style="width: 44px;"></div> <!-- Spacer for centering -->
</header>
<!-- Page Container -->
<main class="page" style="padding-bottom: 24px;">
<div class="page-content">
<!-- Welcome Section -->
<section class="text-center mb-xl">
<span class="material-icons text-primary" style="font-size: 60px;">store</span>
<h2 class="h1 mt-m">KT 이벤트 도우미</h2>
<p class="body-l text-gray-700 mt-s">환영합니다!</p>
</section>
<!-- Sign Up Form -->
<form id="signUpForm" novalidate>
<!-- Email -->
<div class="input-group">
<label for="email">이메일 *</label>
<input
type="email"
id="email"
class="input"
placeholder="example@email.com"
required
/>
<span class="input-error">올바른 이메일 형식이 아닙니다.</span>
</div>
<!-- Password -->
<div class="input-group">
<label for="password">비밀번호 *</label>
<input
type="password"
id="password"
class="input"
placeholder="8자 이상, 영문과 숫자 조합"
required
/>
<span class="input-error">비밀번호는 8자 이상, 영문과 숫자 조합이어야 합니다.</span>
</div>
<!-- Password Confirm -->
<div class="input-group">
<label for="passwordConfirm">비밀번호 확인 *</label>
<input
type="password"
id="passwordConfirm"
class="input"
placeholder="비밀번호를 다시 입력하세요"
required
/>
<span class="input-error">비밀번호가 일치하지 않습니다.</span>
</div>
<div class="divider my-l"></div>
<!-- Business Name -->
<div class="input-group">
<label for="businessName">상호명 *</label>
<input
type="text"
id="businessName"
class="input"
placeholder="예: 우진이네 고깃집"
required
/>
<span class="input-error">상호명을 입력해주세요.</span>
</div>
<!-- Business Type -->
<div class="input-group">
<label for="businessType">업종 *</label>
<select
id="businessType"
class="input select"
required
>
<option value="">업종 선택</option>
<option value="한식당">한식당</option>
<option value="중식당">중식당</option>
<option value="일식당">일식당</option>
<option value="양식당">양식당</option>
<option value="카페/디저트">카페/디저트</option>
<option value="베이커리">베이커리</option>
<option value="의류/잡화">의류/잡화</option>
<option value="화장품/미용">화장품/미용</option>
<option value="스포츠/레저">스포츠/레저</option>
<option value="기타">기타</option>
</select>
<span class="input-error">업종을 선택해주세요.</span>
</div>
<!-- Location -->
<div class="input-group">
<label for="location">위치 *</label>
<input
type="text"
id="location"
class="input"
placeholder="예: 서울 강남구"
required
/>
<span class="input-error">위치를 입력해주세요.</span>
</div>
<!-- Phone (Optional) -->
<div class="input-group">
<label for="phone">전화번호</label>
<input
type="tel"
id="phone"
class="input"
placeholder="010-1234-5678"
/>
<span class="input-error">올바른 전화번호 형식이 아닙니다.</span>
</div>
<div class="divider my-l"></div>
<!-- Terms Agreements -->
<div class="mb-l">
<label class="checkbox mb-m">
<input type="checkbox" id="agreeAll" />
<span class="body-m text-semibold">전체 동의</span>
</label>
<label class="checkbox mb-s">
<input type="checkbox" id="agreeTerms" required />
<span class="body-m">서비스 이용약관 동의 (필수)</span>
<button type="button" class="btn-text body-s ml-xs" onclick="showTerms('terms')">보기</button>
</label>
<label class="checkbox mb-s">
<input type="checkbox" id="agreePrivacy" required />
<span class="body-m">개인정보 처리방침 (필수)</span>
<button type="button" class="btn-text body-s ml-xs" onclick="showTerms('privacy')">보기</button>
</label>
<label class="checkbox">
<input type="checkbox" id="agreeMarketing" />
<span class="body-m text-gray-700">마케팅 정보 수신 동의 (선택)</span>
</label>
</div>
<!-- Submit Button -->
<button type="submit" class="btn btn-primary btn-full">
가입하기
</button>
<!-- Login Link -->
<p class="text-center mt-m">
<span class="body-m text-gray-700">이미 계정이 있으신가요?</span>
<a href="#" class="body-m text-secondary text-semibold ml-xs" onclick="showLogin(event)">로그인</a>
</p>
</form>
</div>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// 전체 동의 체크박스
const agreeAll = document.getElementById('agreeAll');
const agreeTerms = document.getElementById('agreeTerms');
const agreePrivacy = document.getElementById('agreePrivacy');
const agreeMarketing = document.getElementById('agreeMarketing');
agreeAll.addEventListener('change', (e) => {
const checked = e.target.checked;
agreeTerms.checked = checked;
agreePrivacy.checked = checked;
agreeMarketing.checked = checked;
});
// 개별 체크박스 변경 시 전체 동의 상태 업데이트
[agreeTerms, agreePrivacy, agreeMarketing].forEach(checkbox => {
checkbox.addEventListener('change', () => {
agreeAll.checked = agreeTerms.checked && agreePrivacy.checked && agreeMarketing.checked;
});
});
// Form Submit
const form = document.getElementById('signUpForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Form validation
if (!validateSignUpForm()) {
return;
}
// Loading 표시
UIManager.showLoading('회원가입 처리 중...');
// API 호출 시뮬레이션 (1.5초 대기)
await new Promise(resolve => setTimeout(resolve, 1500));
// 사용자 정보 저장
const userData = {
email: document.getElementById('email').value,
name: document.getElementById('businessName').value.replace(/이네|네/, '').trim() || '사장님',
businessName: document.getElementById('businessName').value,
businessType: document.getElementById('businessType').value,
location: document.getElementById('location').value,
phone: document.getElementById('phone').value || ''
};
AppState.saveUser(userData);
UIManager.hideLoading();
// 성공 메시지
UIManager.showToast('회원가입이 완료되었습니다!', 'success');
// 메인 대시보드로 이동
setTimeout(() => {
NavManager.navigate('00', '메인대시보드');
}, 1000);
});
// Form Validation
function validateSignUpForm() {
let isValid = true;
// Clear all errors
document.querySelectorAll('.input-group').forEach(group => {
group.classList.remove('error');
});
// Email
const email = document.getElementById('email');
if (!FormValidator.validateRequired(email.value)) {
FormValidator.showError(email, '이메일을 입력해주세요.');
isValid = false;
} else if (!FormValidator.validateEmail(email.value)) {
FormValidator.showError(email, '올바른 이메일 형식이 아닙니다.');
isValid = false;
}
// Password
const password = document.getElementById('password');
if (!FormValidator.validateRequired(password.value)) {
FormValidator.showError(password, '비밀번호를 입력해주세요.');
isValid = false;
} else if (!FormValidator.validatePassword(password.value)) {
FormValidator.showError(password, '비밀번호는 8자 이상, 영문과 숫자 조합이어야 합니다.');
isValid = false;
}
// Password Confirm
const passwordConfirm = document.getElementById('passwordConfirm');
if (!FormValidator.validateRequired(passwordConfirm.value)) {
FormValidator.showError(passwordConfirm, '비밀번호 확인을 입력해주세요.');
isValid = false;
} else if (password.value !== passwordConfirm.value) {
FormValidator.showError(passwordConfirm, '비밀번호가 일치하지 않습니다.');
isValid = false;
}
// Business Name
const businessName = document.getElementById('businessName');
if (!FormValidator.validateRequired(businessName.value)) {
FormValidator.showError(businessName, '상호명을 입력해주세요.');
isValid = false;
}
// Business Type
const businessType = document.getElementById('businessType');
if (!FormValidator.validateRequired(businessType.value)) {
FormValidator.showError(businessType, '업종을 선택해주세요.');
isValid = false;
}
// Location
const location = document.getElementById('location');
if (!FormValidator.validateRequired(location.value)) {
FormValidator.showError(location, '위치를 입력해주세요.');
isValid = false;
}
// Phone (optional but validate if provided)
const phone = document.getElementById('phone');
if (phone.value && !FormValidator.validatePhone(phone.value)) {
FormValidator.showError(phone, '올바른 전화번호 형식이 아닙니다. (예: 010-1234-5678)');
isValid = false;
}
// Terms Agreement
if (!agreeTerms.checked) {
UIManager.showToast('서비스 이용약관에 동의해주세요.', 'error');
isValid = false;
}
if (!agreePrivacy.checked) {
UIManager.showToast('개인정보 처리방침에 동의해주세요.', 'error');
isValid = false;
}
return isValid;
}
// 약관 보기
function showTerms(type) {
const titles = {
'terms': '서비스 이용약관',
'privacy': '개인정보 처리방침'
};
const contents = {
'terms': `
<div class="body-s text-gray-700" style="max-height: 300px; overflow-y: auto;">
<p class="mb-m"><strong>제1조 (목적)</strong></p>
<p class="mb-l">본 약관은 KT 이벤트 도우미 서비스의 이용 조건 및 절차에 관한 기본적인 사항을 규정함을 목적으로 합니다.</p>
<p class="mb-m"><strong>제2조 (용어의 정의)</strong></p>
<p class="mb-l">1. "서비스"란 KT가 제공하는 AI 기반 이벤트 자동 생성 서비스를 의미합니다.<br/>
2. "회원"이란 본 약관에 동의하고 서비스를 이용하는 자를 말합니다.</p>
<p class="mb-m"><strong>제3조 (서비스의 제공)</strong></p>
<p>회사는 회원에게 다음과 같은 서비스를 제공합니다:<br/>
1. AI 기반 이벤트 기획안 생성<br/>
2. 이벤트 이미지 생성<br/>
3. 이벤트 관리 및 분석</p>
</div>
`,
'privacy': `
<div class="body-s text-gray-700" style="max-height: 300px; overflow-y: auto;">
<p class="mb-m"><strong>1. 개인정보의 수집 및 이용 목적</strong></p>
<p class="mb-l">회사는 다음의 목적을 위하여 개인정보를 수집 및 이용합니다:<br/>
- 회원 가입 및 관리<br/>
- 서비스 제공 및 개선<br/>
- 이벤트 운영 및 통계 분석</p>
<p class="mb-m"><strong>2. 수집하는 개인정보 항목</strong></p>
<p class="mb-l">- 필수항목: 이메일, 비밀번호, 상호명, 업종, 위치<br/>
- 선택항목: 전화번호</p>
<p class="mb-m"><strong>3. 개인정보의 보유 및 이용 기간</strong></p>
<p>회사는 회원 탈퇴 시까지 개인정보를 보유하며, 탈퇴 후 즉시 파기합니다.</p>
</div>
`
};
UIManager.showModal({
title: titles[type],
body: contents[type],
confirmText: '확인',
cancelText: null
});
}
// 로그인 (시뮬레이션)
function showLogin(e) {
e.preventDefault();
UIManager.showModal({
title: '로그인',
body: `
<p class="body-m text-gray-700 mb-m">로그인 화면은 프로토타입에 포함되지 않습니다.</p>
<p class="body-s text-gray-600">데모를 위해 메인 대시보드로 이동합니다.</p>
`,
confirmText: '메인으로',
cancelText: '취소',
onConfirm: () => {
NavManager.navigate('00', '메인대시보드');
}
});
}
// 입력 중 실시간 에러 제거
document.querySelectorAll('input, select').forEach(input => {
input.addEventListener('input', () => {
FormValidator.clearError(input);
});
});
</script>
</body>
</html>

View File

@ -1,291 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>이벤트 목적 선택 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<button class="back-button" aria-label="뒤로가기">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="top-bar-title">이벤트 만들기</h1>
<div style="width: 44px;"></div>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Stepper -->
<div class="stepper mb-xl">
<div class="step active">
<div class="step-number">1</div>
<div class="step-label">목적</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-label">정보</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-label">기획</div>
</div>
<div class="step">
<div class="step-number">4</div>
<div class="step-label">이미지</div>
</div>
<div class="step">
<div class="step-number">5</div>
<div class="step-label">배포</div>
</div>
</div>
<!-- Title -->
<section class="mb-xl">
<h2 class="h2 mb-s">이벤트 목적을 선택하세요</h2>
<p class="body-m text-gray-700">AI가 목적에 맞는 이벤트를 기획합니다</p>
</section>
<!-- Purpose Selection Cards -->
<section class="mb-xl">
<div class="flex flex-col gap-m">
<!-- 신규 고객 유치 -->
<div class="card card-clickable purpose-card" data-purpose="new-customer" onclick="selectPurpose('new-customer')">
<div class="card-body flex items-center gap-m p-l">
<div class="flex items-center justify-center" style="width: 60px; height: 60px; border-radius: 50%; background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 32px;">person_add</span>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">신규 고객 유치</h3>
<p class="body-s text-gray-600">처음 방문하는 고객을 늘립니다</p>
</div>
<span class="material-icons text-gray-300 check-icon">check_circle</span>
</div>
</div>
<!-- 재방문 유도 -->
<div class="card card-clickable purpose-card" data-purpose="revisit" onclick="selectPurpose('revisit')">
<div class="card-body flex items-center gap-m p-l">
<div class="flex items-center justify-center" style="width: 60px; height: 60px; border-radius: 50%; background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 32px;">replay</span>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">재방문 유도</h3>
<p class="body-s text-gray-600">기존 고객의 재방문을 늘립니다</p>
</div>
<span class="material-icons text-gray-300 check-icon">check_circle</span>
</div>
</div>
<!-- 신메뉴 홍보 -->
<div class="card card-clickable purpose-card" data-purpose="new-menu" onclick="selectPurpose('new-menu')">
<div class="card-body flex items-center gap-m p-l">
<div class="flex items-center justify-center" style="width: 60px; height: 60px; border-radius: 50%; background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 32px;">restaurant_menu</span>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">신메뉴 홍보</h3>
<p class="body-s text-gray-600">새로운 메뉴를 알립니다</p>
</div>
<span class="material-icons text-gray-300 check-icon">check_circle</span>
</div>
</div>
<!-- 기타 -->
<div class="card card-clickable purpose-card" data-purpose="other" onclick="selectPurpose('other')">
<div class="card-body flex items-center gap-m p-l">
<div class="flex items-center justify-center" style="width: 60px; height: 60px; border-radius: 50%; background-color: var(--color-gray-200);">
<span class="material-icons text-gray-700" style="font-size: 32px;">more_horiz</span>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">기타</h3>
<p class="body-s text-gray-600">직접 목적을 입력합니다</p>
</div>
<span class="material-icons text-gray-300 check-icon">check_circle</span>
</div>
</div>
</div>
</section>
<!-- Next Button -->
<section>
<button id="nextButton" class="btn btn-primary btn-full" disabled onclick="goToNextStep()">
다음
</button>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item active" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
let selectedPurpose = null;
// Purpose 선택
function selectPurpose(purpose) {
selectedPurpose = purpose;
// 모든 카드 비활성화
document.querySelectorAll('.purpose-card').forEach(card => {
card.classList.remove('selected');
card.style.borderColor = 'var(--color-gray-200)';
card.querySelector('.check-icon').style.color = 'var(--color-gray-300)';
});
// 선택한 카드 활성화
const selectedCard = document.querySelector(`[data-purpose="${purpose}"]`);
if (selectedCard) {
selectedCard.classList.add('selected');
selectedCard.style.borderColor = 'var(--color-primary)';
selectedCard.style.borderWidth = '2px';
selectedCard.querySelector('.check-icon').style.color = 'var(--color-primary)';
}
// 다음 버튼 활성화
const nextButton = document.getElementById('nextButton');
nextButton.disabled = false;
// 데이터 저장
AppState.save('eventPurpose', {
purpose: purpose,
label: selectedCard.querySelector('h3').textContent
});
}
// 다음 단계로 이동
function goToNextStep() {
if (!selectedPurpose) {
UIManager.showToast('이벤트 목적을 선택해주세요.', 'warning');
return;
}
// 기타 선택 시 입력 받기
if (selectedPurpose === 'other') {
UIManager.showModal({
title: '이벤트 목적 입력',
body: `
<div class="input-group">
<label for="customPurpose">이벤트 목적을 입력하세요</label>
<input type="text" id="customPurpose" class="input" placeholder="예: 신규 매장 오픈 홍보" />
</div>
`,
confirmText: '확인',
cancelText: '취소',
onConfirm: () => {
const customPurpose = document.getElementById('customPurpose').value;
if (!customPurpose || customPurpose.trim() === '') {
UIManager.showToast('목적을 입력해주세요.', 'error');
return;
}
AppState.save('eventPurpose', {
purpose: 'other',
label: customPurpose
});
proceedToPlanning();
}
});
} else {
proceedToPlanning();
}
}
// AI 기획 진행
function proceedToPlanning() {
// Loading 표시
UIManager.showLoading('AI가 이벤트 기획안을 생성하고 있습니다...');
// Progress bar 업데이트 시뮬레이션
let progress = 0;
const progressInterval = setInterval(() => {
progress += 10;
UIManager.updateProgress(progress);
if (progress >= 100) {
clearInterval(progressInterval);
// AI 생성 완료
setTimeout(() => {
UIManager.hideLoading();
UIManager.showToast('기획안이 생성되었습니다!', 'success');
// 08-이벤트기획안승인으로 이동
setTimeout(() => {
NavManager.navigate('08', '이벤트기획안승인');
}, 500);
}, 500);
}
}, 200);
}
// 페이지 초기화
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('event');
// 이전에 선택한 목적이 있으면 복원
const savedPurpose = AppState.load('eventPurpose');
if (savedPurpose && savedPurpose.purpose) {
selectPurpose(savedPurpose.purpose);
}
});
</script>
<style>
/* Purpose Card Transition */
.purpose-card {
transition: all 0.2s ease-out;
}
.purpose-card:hover {
transform: translateY(-2px);
}
.purpose-card:active {
transform: scale(0.98);
}
.check-icon {
transition: color 0.2s ease-out;
}
</style>
</body>
</html>

View File

@ -1,330 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>이벤트 기획안 승인 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<button class="back-button" aria-label="뒤로가기">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="top-bar-title">이벤트 기획안</h1>
<div style="width: 44px;"></div>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Stepper -->
<div class="stepper mb-xl">
<div class="step completed">
<div class="step-number">1</div>
<div class="step-label">목적</div>
</div>
<div class="step completed">
<div class="step-number">2</div>
<div class="step-label">정보</div>
</div>
<div class="step active">
<div class="step-number">3</div>
<div class="step-label">기획</div>
</div>
<div class="step">
<div class="step-number">4</div>
<div class="step-label">이미지</div>
</div>
<div class="step">
<div class="step-number">5</div>
<div class="step-label">배포</div>
</div>
</div>
<!-- Title Section -->
<section class="mb-l">
<div class="flex items-center gap-s mb-s">
<span class="material-icons text-secondary" style="font-size: 28px;">auto_awesome</span>
<h2 class="h2">AI가 생성한 기획안</h2>
</div>
<p class="body-m text-gray-700">내용을 확인하고 승인해주세요</p>
</section>
<!-- Event Plan Card -->
<section class="mb-xl">
<div class="card">
<div class="card-body p-l">
<!-- Event Title -->
<div class="mb-l">
<label class="body-s text-gray-600 mb-xs block">이벤트 제목</label>
<h3 class="h2" id="eventTitle">우진이네 여름 특별 할인 이벤트</h3>
</div>
<div class="divider mb-l"></div>
<!-- Event Description -->
<div class="mb-l">
<label class="body-s text-gray-600 mb-xs block">이벤트 내용</label>
<p class="body-l text-gray-900" id="eventDescription" style="line-height: 1.6;">
여름 시즌 신규 고객 유치를 위한 20% 할인 이벤트입니다. 점심 시간대 직장인을 타겟으로 하며, SNS 공유 시 추가 쿠폰을 제공합니다. 시원한 여름 메뉴와 함께 특별한 혜택을 경험하세요!
</p>
</div>
<div class="divider mb-l"></div>
<!-- Event Details -->
<div class="flex flex-col gap-m">
<!-- Target -->
<div class="flex items-center gap-m">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 20px;">group</span>
</div>
<div>
<div class="body-s text-gray-600">타겟 고객</div>
<div class="body-l text-semibold" id="eventTarget">20-30대 직장인</div>
</div>
</div>
<!-- Period -->
<div class="flex items-center gap-m">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 20px;">calendar_month</span>
</div>
<div>
<div class="body-s text-gray-600">이벤트 기간</div>
<div class="body-l text-semibold" id="eventPeriod">2025.06.01 ~ 2025.06.30 (30일)</div>
</div>
</div>
<!-- Discount -->
<div class="flex items-center gap-m">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 20px;">local_offer</span>
</div>
<div>
<div class="body-s text-gray-600">할인 혜택</div>
<div class="body-l text-semibold" id="eventDiscount">전 메뉴 20% 할인</div>
</div>
</div>
<!-- Additional Benefit -->
<div class="flex items-center gap-m">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-warning-light);">
<span class="material-icons text-warning" style="font-size: 20px;">card_giftcard</span>
</div>
<div>
<div class="body-s text-gray-600">추가 혜택</div>
<div class="body-l text-semibold" id="eventBenefit">SNS 공유 시 10% 쿠폰 추가 증정</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- AI Tips -->
<section class="mb-xl">
<div class="card bg-secondary-light" style="border: none;">
<div class="card-body flex gap-m">
<span class="material-icons text-secondary" style="font-size: 28px;">tips_and_updates</span>
<div>
<h3 class="body-l text-semibold mb-xs text-secondary">AI 제안</h3>
<p class="body-m text-gray-700">타겟 고객층에 맞춰 점심 시간대(11:00-14:00)에 집중 홍보하면 더 좋은 효과를 얻을 수 있습니다.</p>
</div>
</div>
</div>
</section>
<!-- Action Buttons -->
<section>
<div class="flex gap-m">
<button class="btn btn-secondary flex-1" onclick="showEditModal()">
<span class="material-icons mr-xs" style="font-size: 20px;">edit</span>
수정
</button>
<button class="btn btn-primary flex-1" onclick="approveAndContinue()">
<span class="material-icons mr-xs" style="font-size: 20px;">check_circle</span>
승인
</button>
</div>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item active" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// 이벤트 기획안 데이터
const eventPlan = {
title: '우진이네 여름 특별 할인 이벤트',
description: '여름 시즌 신규 고객 유치를 위한 20% 할인 이벤트입니다. 점심 시간대 직장인을 타겟으로 하며, SNS 공유 시 추가 쿠폰을 제공합니다. 시원한 여름 메뉴와 함께 특별한 혜택을 경험하세요!',
target: '20-30대 직장인',
startDate: '2025.06.01',
endDate: '2025.06.30',
duration: 30,
discount: '전 메뉴 20% 할인',
benefit: 'SNS 공유 시 10% 쿠폰 추가 증정'
};
// 페이지 데이터 로드
function loadEventPlan() {
document.getElementById('eventTitle').textContent = eventPlan.title;
document.getElementById('eventDescription').textContent = eventPlan.description;
document.getElementById('eventTarget').textContent = eventPlan.target;
document.getElementById('eventPeriod').textContent =
`${eventPlan.startDate} ~ ${eventPlan.endDate} (${eventPlan.duration}일)`;
document.getElementById('eventDiscount').textContent = eventPlan.discount;
document.getElementById('eventBenefit').textContent = eventPlan.benefit;
}
// 수정 모달 표시
function showEditModal() {
UIManager.showModal({
title: '기획안 수정',
body: `
<p class="body-m text-gray-700 mb-m">
기획안을 수정하시겠습니까?
</p>
<div class="card bg-info-light" style="border: none;">
<div class="card-body">
<p class="body-s text-gray-700">
💡 프로토타입에서는 수정 기능이 제한됩니다.
실제 서비스에서는 AI에게 수정 요청을 할 수 있습니다.
</p>
</div>
</div>
`,
confirmText: 'AI에게 수정 요청',
cancelText: '취소',
onConfirm: () => {
showAIEditRequest();
}
});
}
// AI 수정 요청 (시뮬레이션)
function showAIEditRequest() {
UIManager.showModal({
title: 'AI에게 수정 요청',
body: `
<div class="input-group">
<label for="editRequest">수정 요청 내용을 입력하세요</label>
<textarea
id="editRequest"
class="input textarea"
placeholder="예: 할인율을 30%로 높여주세요"
rows="3"
></textarea>
</div>
<p class="body-s text-gray-600 mt-s">
AI가 요청사항을 반영하여 기획안을 다시 생성합니다.
</p>
`,
confirmText: '수정 요청',
cancelText: '취소',
onConfirm: () => {
const request = document.getElementById('editRequest').value;
if (!request || request.trim() === '') {
UIManager.showToast('수정 요청 내용을 입력해주세요.', 'error');
return;
}
// AI 재생성 시뮬레이션
UIManager.showLoading('AI가 기획안을 수정하고 있습니다...');
setTimeout(() => {
UIManager.hideLoading();
UIManager.showToast('기획안이 수정되었습니다!', 'success');
// 실제로는 수정된 내용 반영
// 프로토타입에서는 같은 내용 유지
}, 2000);
}
});
}
// 승인 및 다음 단계
function approveAndContinue() {
UIManager.showModal({
title: '이벤트 기획안 승인',
body: `
<p class="body-m text-gray-700 mb-m">
기획안을 승인하고 다음 단계로 진행하시겠습니까?
</p>
<div class="card bg-success-light" style="border: none;">
<div class="card-body">
<h4 class="body-m text-semibold mb-xs text-success">승인 후 진행사항</h4>
<ul class="body-s text-gray-700" style="padding-left: 20px; line-height: 1.8;">
<li>AI가 이벤트 이미지를 생성합니다</li>
<li>다양한 채널에 배포할 수 있습니다</li>
<li>실시간으로 성과를 확인할 수 있습니다</li>
</ul>
</div>
</div>
`,
confirmText: '승인하고 계속',
cancelText: '취소',
onConfirm: () => {
// 기획안 데이터 저장
AppState.save('approvedEventPlan', eventPlan);
// 성공 메시지
UIManager.showToast('기획안이 승인되었습니다!', 'success');
// 다음 단계로 이동 (09-AI이미지생성)
setTimeout(() => {
NavManager.navigate('09', 'AI이미지생성');
}, 1000);
}
});
}
// 페이지 초기화
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('event');
// 이벤트 기획안 로드
loadEventPlan();
});
</script>
</body>
</html>

View File

@ -1,401 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI 이미지 생성 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
<style>
.image-card {
position: relative;
cursor: pointer;
border: 3px solid transparent;
transition: all 0.2s ease-out;
}
.image-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
}
.image-card.selected {
border-color: var(--color-primary);
}
.image-placeholder {
width: 100%;
aspect-ratio: 4 / 3;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: var(--font-size-h2);
font-weight: var(--font-weight-bold);
border-radius: var(--radius-medium);
position: relative;
overflow: hidden;
}
.image-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y="50" font-size="40" fill="white" opacity="0.2">🍖</text></svg>') center/cover;
}
.check-badge {
position: absolute;
top: 12px;
right: 12px;
width: 32px;
height: 32px;
background-color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
opacity: 0;
transform: scale(0.8);
transition: all 0.2s ease-out;
}
.image-card.selected .check-badge {
opacity: 1;
transform: scale(1);
}
.regenerating {
opacity: 0.5;
pointer-events: none;
}
</style>
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<button class="back-button" aria-label="뒤로가기">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="top-bar-title">AI 이미지 생성</h1>
<div style="width: 44px;"></div>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Stepper -->
<div class="stepper mb-xl">
<div class="step completed">
<div class="step-number">1</div>
<div class="step-label">목적</div>
</div>
<div class="step completed">
<div class="step-number">2</div>
<div class="step-label">정보</div>
</div>
<div class="step completed">
<div class="step-number">3</div>
<div class="step-label">기획</div>
</div>
<div class="step active">
<div class="step-number">4</div>
<div class="step-label">이미지</div>
</div>
<div class="step">
<div class="step-number">5</div>
<div class="step-label">배포</div>
</div>
</div>
<!-- Title Section -->
<section class="mb-l">
<div class="flex items-center gap-s mb-s">
<span class="material-icons text-secondary" style="font-size: 28px;">image</span>
<h2 class="h2">AI가 생성한 이미지</h2>
</div>
<p class="body-m text-gray-700">마음에 드는 이미지를 선택하세요 (복수 선택 가능)</p>
</section>
<!-- Images Grid -->
<section class="mb-l" id="imagesContainer">
<div class="grid gap-m">
<!-- Image 1 -->
<div class="col-4 image-card selected card" data-image-id="1" onclick="toggleImageSelection(1)">
<div class="image-placeholder">
<div style="position: relative; z-index: 1; text-align: center;">
<div style="font-size: 48px; margin-bottom: 8px;">🔥</div>
<div style="font-size: 16px; font-weight: 600;">여름 특별 이벤트</div>
<div style="font-size: 14px; opacity: 0.9;">20% 할인</div>
</div>
</div>
<div class="check-badge">
<span class="material-icons text-primary">check</span>
</div>
<div class="card-body p-m">
<p class="body-s text-gray-700">스타일: 모던, 밝은 느낌</p>
</div>
</div>
<!-- Image 2 -->
<div class="col-4 image-card card" data-image-id="2" onclick="toggleImageSelection(2)">
<div class="image-placeholder" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
<div style="position: relative; z-index: 1; text-align: center;">
<div style="font-size: 48px; margin-bottom: 8px;">🍖</div>
<div style="font-size: 16px; font-weight: 600;">우진이네 고깃집</div>
<div style="font-size: 14px; opacity: 0.9;">여름 시즌 특가</div>
</div>
</div>
<div class="check-badge">
<span class="material-icons text-primary">check</span>
</div>
<div class="card-body p-m">
<p class="body-s text-gray-700">스타일: 따뜻한 색감, 식욕 자극</p>
</div>
</div>
<!-- Image 3 -->
<div class="col-4 image-card card" data-image-id="3" onclick="toggleImageSelection(3)">
<div class="image-placeholder" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
<div style="position: relative; z-index: 1; text-align: center;">
<div style="font-size: 48px; margin-bottom: 8px;">❄️</div>
<div style="font-size: 16px; font-weight: 600;">시원한 여름</div>
<div style="font-size: 14px; opacity: 0.9;">20% OFF</div>
</div>
</div>
<div class="check-badge">
<span class="material-icons text-primary">check</span>
</div>
<div class="card-body p-m">
<p class="body-s text-gray-700">스타일: 시원한 느낌, 여름 테마</p>
</div>
</div>
</div>
</section>
<!-- Regenerate Button -->
<section class="mb-l">
<button class="btn btn-text btn-full" onclick="regenerateImages()">
<span class="material-icons mr-xs">refresh</span>
다른 이미지 생성
</button>
</section>
<!-- Tips -->
<section class="mb-xl">
<div class="card bg-info-light" style="border: none;">
<div class="card-body flex gap-m">
<span class="material-icons text-info" style="font-size: 24px;">info</span>
<div>
<p class="body-s text-gray-700">
💡 여러 이미지를 선택하여 다양한 채널에 활용할 수 있습니다.
</p>
</div>
</div>
</div>
</section>
<!-- Complete Button -->
<section>
<button id="completeButton" class="btn btn-primary btn-full" onclick="completeImageSelection()">
선택 완료
</button>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item active" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// 선택된 이미지 ID 목록
let selectedImages = [1]; // 기본으로 첫 번째 이미지 선택
// 이미지 선택 토글
function toggleImageSelection(imageId) {
const card = document.querySelector(`[data-image-id="${imageId}"]`);
if (selectedImages.includes(imageId)) {
// 선택 해제
selectedImages = selectedImages.filter(id => id !== imageId);
card.classList.remove('selected');
} else {
// 선택
selectedImages.push(imageId);
card.classList.add('selected');
}
// 완료 버튼 활성화/비활성화
updateCompleteButton();
// 데이터 저장
AppState.save('selectedImages', selectedImages);
}
// 완료 버튼 상태 업데이트
function updateCompleteButton() {
const button = document.getElementById('completeButton');
if (selectedImages.length > 0) {
button.disabled = false;
button.innerHTML = `선택 완료 (${selectedImages.length}개)`;
} else {
button.disabled = true;
button.innerHTML = '이미지를 선택하세요';
}
}
// 이미지 재생성
function regenerateImages() {
UIManager.showModal({
title: '이미지 다시 생성',
body: `
<p class="body-m text-gray-700 mb-m">
새로운 이미지를 생성하시겠습니까?
</p>
<div class="card bg-warning-light" style="border: none;">
<div class="card-body">
<p class="body-s text-gray-700">
⚠️ 현재 선택한 이미지는 유지됩니다.
새로 생성된 이미지 중에서 추가로 선택할 수 있습니다.
</p>
</div>
</div>
`,
confirmText: '새로 생성',
cancelText: '취소',
onConfirm: () => {
// AI 이미지 생성 시뮬레이션
UIManager.showLoading('AI가 새로운 이미지를 생성하고 있습니다...');
const container = document.getElementById('imagesContainer');
container.classList.add('regenerating');
// Progress 업데이트
let progress = 0;
const progressInterval = setInterval(() => {
progress += 15;
UIManager.updateProgress(progress);
if (progress >= 100) {
clearInterval(progressInterval);
setTimeout(() => {
UIManager.hideLoading();
UIManager.showToast('새로운 이미지가 생성되었습니다!', 'success');
container.classList.remove('regenerating');
// 실제로는 새 이미지로 교체
// 프로토타입에서는 같은 이미지 유지
}, 500);
}
}, 150);
}
});
}
// 선택 완료
function completeImageSelection() {
if (selectedImages.length === 0) {
UIManager.showToast('이미지를 선택해주세요.', 'warning');
return;
}
// 선택된 이미지 데이터 저장
AppState.save('selectedImages', selectedImages);
UIManager.showModal({
title: '이미지 선택 완료',
body: `
<p class="body-m text-gray-700 mb-m">
${selectedImages.length}개의 이미지를 선택하셨습니다.
</p>
<p class="body-m text-gray-700 mb-m">
다음 단계에서 이벤트를 배포할 채널을 선택합니다.
</p>
<div class="card bg-success-light" style="border: none;">
<div class="card-body">
<h4 class="body-m text-semibold mb-xs text-success">다음 단계</h4>
<ul class="body-s text-gray-700" style="padding-left: 20px; line-height: 1.8;">
<li>배포 채널 선택 (SNS, 문자, 이메일 등)</li>
<li>일정 설정</li>
<li>최종 검토 및 배포</li>
</ul>
</div>
</div>
`,
confirmText: '다음 단계로',
cancelText: '취소',
onConfirm: () => {
UIManager.showToast('이미지 선택이 완료되었습니다!', 'success');
// 다음 단계로 이동 (15-다중채널배포설정)
setTimeout(() => {
NavManager.navigate('15', '다중채널배포설정');
}, 800);
}
});
}
// 페이지 초기화
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('event');
// 이전에 선택한 이미지 복원
const savedImages = AppState.load('selectedImages');
if (savedImages && savedImages.length > 0) {
selectedImages = savedImages;
// UI 업데이트
selectedImages.forEach(imageId => {
const card = document.querySelector(`[data-image-id="${imageId}"]`);
if (card) {
card.classList.add('selected');
}
});
}
// 완료 버튼 상태 업데이트
updateCompleteButton();
});
</script>
</body>
</html>

View File

@ -1,681 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>다중 채널 배포 설정 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link
rel="stylesheet"
as="style"
crossorigin
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css"
/>
<!-- Material Icons -->
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<!-- Common CSS -->
<link rel="stylesheet" href="common.css" />
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<button class="back-button" aria-label="뒤로가기">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="top-bar-title">배포 설정</h1>
<div style="width: 44px"></div>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Stepper -->
<div class="stepper mb-xl">
<div class="step completed">
<div class="step-number">1</div>
<div class="step-label">목적</div>
</div>
<div class="step completed">
<div class="step-number">2</div>
<div class="step-label">정보</div>
</div>
<div class="step completed">
<div class="step-number">3</div>
<div class="step-label">기획</div>
</div>
<div class="step completed">
<div class="step-number">4</div>
<div class="step-label">이미지</div>
</div>
<div class="step active">
<div class="step-number">5</div>
<div class="step-label">배포</div>
</div>
</div>
<!-- Channel Selection Section -->
<section class="mb-xl">
<h2 class="h3 mb-m">배포 채널 선택</h2>
<p class="body-m text-gray-700 mb-l">
이벤트를 배포할 채널을 선택하세요 (복수 선택 가능)
</p>
<div class="flex flex-col gap-m">
<!-- 우리동네TV -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input
type="checkbox"
id="channelUriDongneTV"
value="uridongnetv"
checked
/>
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-primary-light);
"
>
<span
class="material-icons text-primary"
style="font-size: 24px"
>tv</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">우리동네TV</h3>
<p class="body-s text-gray-600">
지니 TV 기반 로컬 마케팅 플랫폼
</p>
</div>
</div>
</label>
<!-- 링고비즈 -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input
type="checkbox"
id="channelRingoBiz"
value="ringobiz"
checked
/>
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-secondary-light);
"
>
<span
class="material-icons text-secondary"
style="font-size: 24px"
>phone_in_talk</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">링고비즈</h3>
<p class="body-s text-gray-600">
특별한 통화연결음으로 매장 홍보
</p>
</div>
</div>
</label>
<!-- genie TV -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input
type="checkbox"
id="channelGenieTV"
value="genietv"
checked
/>
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-warning-light);
"
>
<span
class="material-icons text-warning"
style="font-size: 24px"
>live_tv</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">genie TV</h3>
<p class="body-s text-gray-600">
다양한 콘텐츠를 즐기는 미디어 포털
</p>
</div>
</div>
</label>
<!-- 하이오더 -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input
type="checkbox"
id="channelHighOrder"
value="highorder"
checked
/>
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-success-light);
"
>
<span
class="material-icons text-success"
style="font-size: 24px"
>restaurant_menu</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">하이오더</h3>
<p class="body-s text-gray-600">
테이블에서 주문과 결제하는 서비스
</p>
</div>
</div>
</label>
<!-- SNS -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input type="checkbox" id="channelSNS" value="sns" checked />
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-primary-light);
"
>
<span
class="material-icons text-primary"
style="font-size: 24px"
>share</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">SNS</h3>
<p class="body-s text-gray-600">
Instagram, Facebook, 네이버 플레이스
</p>
</div>
</div>
</label>
<!-- SMS -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input type="checkbox" id="channelSMS" value="sms" checked />
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-secondary-light);
"
>
<span
class="material-icons text-secondary"
style="font-size: 24px"
>sms</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">문자 메시지</h3>
<p class="body-s text-gray-600">기존 고객에게 SMS 발송</p>
</div>
</div>
</label>
<!-- Email -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input type="checkbox" id="channelEmail" value="email" />
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-success-light);
"
>
<span
class="material-icons text-success"
style="font-size: 24px"
>email</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">이메일</h3>
<p class="body-s text-gray-600">뉴스레터 및 이메일 마케팅</p>
</div>
</div>
</label>
<!-- Offline -->
<label class="checkbox card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input type="checkbox" id="channelOffline" value="offline" />
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-warning-light);
"
>
<span
class="material-icons text-warning"
style="font-size: 24px"
>print</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">오프라인 인쇄물</h3>
<p class="body-s text-gray-600">포스터, 전단지, POP</p>
</div>
</div>
</label>
</div>
</section>
<!-- Schedule Section -->
<section class="mb-xl">
<h2 class="h3 mb-m">배포 일정</h2>
<div class="flex flex-col gap-m">
<!-- Immediate -->
<label class="radio card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input
type="radio"
name="schedule"
id="scheduleImmediate"
value="immediate"
checked
/>
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-primary-light);
"
>
<span
class="material-icons text-primary"
style="font-size: 24px"
>bolt</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">즉시 배포</h3>
<p class="body-s text-gray-600">
선택한 채널에 바로 배포합니다
</p>
</div>
</div>
</label>
<!-- Scheduled -->
<label class="radio card p-l" style="cursor: pointer">
<div class="flex items-center gap-m">
<input
type="radio"
name="schedule"
id="scheduleReserved"
value="reserved"
/>
<div
class="flex items-center justify-center"
style="
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-secondary-light);
"
>
<span
class="material-icons text-secondary"
style="font-size: 24px"
>schedule</span
>
</div>
<div class="flex-1">
<h3 class="body-l text-semibold mb-xs">예약 배포</h3>
<p class="body-s text-gray-600">
원하는 날짜와 시간에 배포합니다
</p>
</div>
</div>
</label>
<!-- Date/Time Picker (hidden by default) -->
<div
id="dateTimeSection"
class="card p-l"
style="display: none; border-color: var(--color-secondary)"
>
<div class="flex flex-col gap-m">
<div class="input-group" style="margin-bottom: 0">
<label for="scheduleDate">배포 날짜</label>
<input type="date" id="scheduleDate" class="input" />
</div>
<div class="input-group" style="margin-bottom: 0">
<label for="scheduleTime">배포 시간</label>
<input
type="time"
id="scheduleTime"
class="input"
value="12:00"
/>
</div>
</div>
</div>
</div>
</section>
<!-- Summary -->
<section class="mb-xl">
<div class="card bg-gray-100" style="border: none">
<div class="card-body">
<h3 class="body-l text-semibold mb-m">배포 요약</h3>
<div class="flex flex-col gap-s body-m text-gray-700">
<div class="flex items-center gap-xs">
<span class="material-icons" style="font-size: 18px"
>check_circle</span
>
<span id="summaryChannels"
>우리동네TV, 링고비즈, genie TV, 하이오더, SNS, 문자 메시지
(6개 채널)</span
>
</div>
<div class="flex items-center gap-xs">
<span class="material-icons" style="font-size: 18px"
>schedule</span
>
<span id="summarySchedule">즉시 배포</span>
</div>
<div class="flex items-center gap-xs">
<span class="material-icons" style="font-size: 18px"
>image</span
>
<span id="summaryImages">선택된 이미지 1개</span>
</div>
</div>
</div>
</div>
</section>
<!-- Deploy Button -->
<section>
<button
id="deployButton"
class="btn btn-primary btn-full"
onclick="startDeployment()"
>
<span class="material-icons mr-xs">rocket_launch</span>
배포 시작
</button>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item active" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// 채널 체크박스 변경 시 요약 업데이트
document
.querySelectorAll('input[type="checkbox"]')
.forEach((checkbox) => {
checkbox.addEventListener("change", updateSummary);
});
// 일정 라디오 변경 시 날짜/시간 섹션 표시
document.querySelectorAll('input[name="schedule"]').forEach((radio) => {
radio.addEventListener("change", (e) => {
const dateTimeSection = document.getElementById("dateTimeSection");
if (e.target.value === "reserved") {
dateTimeSection.style.display = "block";
// 기본 날짜 설정 (내일)
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
document.getElementById("scheduleDate").value =
Utils.formatDateISO(tomorrow);
} else {
dateTimeSection.style.display = "none";
}
updateSummary();
});
});
// 요약 업데이트
function updateSummary() {
// 선택된 채널
const channels = [];
const channelLabels = {
uridongnetv: "우리동네TV",
ringobiz: "링고비즈",
genietv: "genie TV",
highorder: "하이오더",
sns: "SNS",
sms: "문자 메시지",
email: "이메일",
offline: "오프라인 인쇄물",
};
document
.querySelectorAll('input[type="checkbox"]:checked')
.forEach((checkbox) => {
channels.push(channelLabels[checkbox.value]);
});
document.getElementById("summaryChannels").textContent =
channels.length > 0
? `${channels.join(", ")} (${channels.length}개 채널)`
: "선택된 채널 없음";
// 선택된 일정
const scheduleType = document.querySelector(
'input[name="schedule"]:checked'
).value;
let scheduleText = "즉시 배포";
if (scheduleType === "reserved") {
const date = document.getElementById("scheduleDate").value;
const time = document.getElementById("scheduleTime").value;
if (date) {
const dateObj = new Date(date);
scheduleText = `${Utils.formatDate(dateObj)} ${time}`;
}
}
document.getElementById("summarySchedule").textContent = scheduleText;
// 선택된 이미지 개수
const selectedImages = AppState.load("selectedImages") || [1];
document.getElementById(
"summaryImages"
).textContent = `선택된 이미지 ${selectedImages.length}개`;
// 배포 버튼 활성화/비활성화
const deployButton = document.getElementById("deployButton");
deployButton.disabled = channels.length === 0;
}
// 배포 시작
function startDeployment() {
const channels = [];
document
.querySelectorAll('input[type="checkbox"]:checked')
.forEach((checkbox) => {
channels.push(checkbox.value);
});
if (channels.length === 0) {
UIManager.showToast("배포 채널을 선택해주세요.", "warning");
return;
}
const scheduleType = document.querySelector(
'input[name="schedule"]:checked'
).value;
let scheduleInfo = "즉시 배포";
if (scheduleType === "reserved") {
const date = document.getElementById("scheduleDate").value;
const time = document.getElementById("scheduleTime").value;
if (!date) {
UIManager.showToast("배포 날짜를 선택해주세요.", "error");
return;
}
const dateObj = new Date(date);
scheduleInfo = `${Utils.formatDate(dateObj)} ${time}`;
}
UIManager.showModal({
title: "이벤트 배포 확인",
body: `
<p class="body-m text-gray-900 mb-m">
이벤트를 배포하시겠습니까?
</p>
<div class="card bg-primary-light" style="border: none;">
<div class="card-body">
<div class="flex flex-col gap-s body-m text-gray-900">
<div><strong>채널:</strong> ${channels.length}개</div>
<div><strong>일정:</strong> ${scheduleInfo}</div>
<div><strong>이미지:</strong> ${
(AppState.load("selectedImages") || [1]).length
}개</div>
</div>
</div>
</div>
<p class="body-s text-gray-600 mt-m">
배포 후에는 실시간 대시보드에서 성과를 확인할 수 있습니다.
</p>
`,
confirmText: "배포 시작",
cancelText: "취소",
onConfirm: () => {
proceedDeployment(channels, scheduleType, scheduleInfo);
},
});
}
// 배포 진행
function proceedDeployment(channels, scheduleType, scheduleInfo) {
UIManager.showLoading("이벤트를 배포하고 있습니다...");
// Progress 업데이트
let progress = 0;
const progressInterval = setInterval(() => {
progress += 20;
UIManager.updateProgress(progress);
if (progress >= 100) {
clearInterval(progressInterval);
// 배포 데이터 저장
AppState.save("deployedEvent", {
channels: channels,
scheduleType: scheduleType,
scheduleInfo: scheduleInfo,
deployedAt: new Date().toISOString(),
});
setTimeout(() => {
UIManager.hideLoading();
UIManager.showModal({
title: "배포 완료!",
body: `
<div class="text-center mb-m">
<span class="material-icons text-success" style="font-size: 64px;">check_circle</span>
</div>
<p class="body-l text-center text-gray-900 mb-m">
이벤트가 성공적으로 배포되었습니다!
</p>
<div class="card bg-success-light" style="border: none;">
<div class="card-body">
<p class="body-m text-gray-700">
실시간 대시보드에서 이벤트 성과를 확인하세요.
</p>
</div>
</div>
`,
confirmText: "대시보드 보기",
cancelText: "닫기",
onConfirm: () => {
NavManager.navigate("21", "실시간대시보드");
},
});
}, 500);
}
}, 300);
}
// 페이지 초기화
document.addEventListener("DOMContentLoaded", () => {
// Bottom Navigation 초기화
NavManager.initBottomNav("event");
// 초기 요약 업데이트
updateSummary();
});
</script>
</body>
</html>

View File

@ -1,381 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>이벤트 참여 신청 - 우진이네 고깃집</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
<style>
.event-image {
width: 100%;
aspect-ratio: 16 / 9;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
position: relative;
overflow: hidden;
}
.event-image::before {
content: '';
position: absolute;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 1px, transparent 1px);
background-size: 20px 20px;
animation: backgroundMove 20s linear infinite;
}
@keyframes backgroundMove {
0% { transform: translate(0, 0); }
100% { transform: translate(20px, 20px); }
}
.event-content {
position: relative;
z-index: 1;
text-align: center;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.benefit-item {
display: flex;
align-items: center;
gap: var(--space-m);
padding: var(--space-m);
background-color: var(--color-gray-100);
border-radius: var(--radius-medium);
}
</style>
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<div class="flex items-center gap-s">
<span class="material-icons text-primary" style="font-size: 24px;">store</span>
<span class="h3">우진이네 고깃집</span>
</div>
</header>
<!-- Page Container (No Bottom Nav) -->
<main class="page" style="padding-bottom: 24px;">
<!-- Event Image -->
<div class="event-image">
<div class="event-content">
<div style="font-size: 64px; margin-bottom: 16px;">🔥</div>
<h1 class="h1" style="color: white; margin-bottom: 8px;">여름 특별 할인</h1>
<p class="body-l" style="color: white; opacity: 0.95;">전 메뉴 20% 할인!</p>
</div>
</div>
<div class="page-content">
<!-- Event Title -->
<section class="mb-l">
<h2 class="h2 mb-s">우진이네 여름 특별 할인 이벤트</h2>
<p class="body-l text-gray-700">
시원한 여름 메뉴와 함께 특별한 혜택을 경험하세요!
</p>
</section>
<!-- Benefits -->
<section class="mb-xl">
<h3 class="h3 mb-m">이벤트 혜택</h3>
<div class="flex flex-col gap-m">
<!-- Period -->
<div class="benefit-item">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 20px;">calendar_today</span>
</div>
<div>
<div class="body-s text-gray-600">이벤트 기간</div>
<div class="body-l text-semibold">2025.06.01 ~ 2025.06.30</div>
</div>
</div>
<!-- Discount -->
<div class="benefit-item">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 20px;">local_offer</span>
</div>
<div>
<div class="body-s text-gray-600">할인 혜택</div>
<div class="body-l text-semibold">전 메뉴 20% 할인</div>
</div>
</div>
<!-- Additional Benefit -->
<div class="benefit-item">
<div class="flex items-center justify-center" style="width: 40px; height: 40px; border-radius: 50%; background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 20px;">card_giftcard</span>
</div>
<div>
<div class="body-s text-gray-600">추가 혜택</div>
<div class="body-l text-semibold">SNS 공유 시 10% 쿠폰 추가</div>
</div>
</div>
</div>
</section>
<!-- Participation Form -->
<section class="mb-xl">
<h3 class="h3 mb-m">참여 신청</h3>
<form id="participationForm" novalidate>
<!-- Name -->
<div class="input-group">
<label for="customerName">이름 *</label>
<input
type="text"
id="customerName"
class="input"
placeholder="이름을 입력하세요"
required
/>
<span class="input-error">이름을 입력해주세요.</span>
</div>
<!-- Phone -->
<div class="input-group">
<label for="customerPhone">전화번호 *</label>
<input
type="tel"
id="customerPhone"
class="input"
placeholder="010-1234-5678"
required
/>
<span class="input-error">전화번호를 입력해주세요.</span>
</div>
<!-- Privacy Agreement -->
<div class="mb-l">
<label class="checkbox">
<input type="checkbox" id="agreePrivacy" required />
<span class="body-m">개인정보 수집 및 이용에 동의합니다 (필수)</span>
</label>
<button type="button" class="btn-text body-s mt-xs" onclick="showPrivacyPolicy()">
개인정보 처리방침 보기
</button>
</div>
<!-- Submit Button -->
<button type="submit" class="btn btn-primary btn-full">
<span class="material-icons mr-xs">check_circle</span>
신청하기
</button>
</form>
</section>
<!-- Info -->
<section class="mb-l">
<div class="card bg-info-light" style="border: none;">
<div class="card-body">
<h4 class="body-m text-semibold mb-xs text-info">유의사항</h4>
<ul class="body-s text-gray-700" style="padding-left: 20px; line-height: 1.8;">
<li>이벤트 참여는 선착순으로 마감됩니다</li>
<li>중복 신청은 불가능합니다</li>
<li>신청 후 문자로 쿠폰을 받으실 수 있습니다</li>
</ul>
</div>
</div>
</section>
<!-- Share Section -->
<section class="text-center">
<p class="body-m text-gray-700 mb-m">이 이벤트를 친구에게 공유하세요!</p>
<div class="flex gap-m justify-center">
<button class="btn btn-secondary" onclick="shareEvent('kakao')">
<span class="body-m">카카오톡</span>
</button>
<button class="btn btn-secondary" onclick="shareEvent('link')">
<span class="material-icons" style="font-size: 20px;">link</span>
</button>
</div>
</section>
</div>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
const form = document.getElementById('participationForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Validation
if (!validateForm()) {
return;
}
// Loading
UIManager.showLoading('참여 신청 처리 중...');
// API 호출 시뮬레이션
await new Promise(resolve => setTimeout(resolve, 1500));
// 신청 데이터
const participationData = {
name: document.getElementById('customerName').value,
phone: document.getElementById('customerPhone').value,
eventName: '우진이네 여름 특별 할인 이벤트',
appliedAt: new Date().toISOString()
};
// 저장 (프로토타입용)
const participants = AppState.load('participants') || [];
participants.push(participationData);
AppState.save('participants', participants);
UIManager.hideLoading();
// 성공 모달
UIManager.showModal({
title: '신청 완료!',
body: `
<div class="text-center mb-m">
<span class="material-icons text-success" style="font-size: 64px;">check_circle</span>
</div>
<p class="body-l text-center text-gray-900 mb-m">
이벤트 참여 신청이 완료되었습니다!
</p>
<div class="card bg-success-light" style="border: none;">
<div class="card-body">
<p class="body-m text-gray-700 mb-m">
<strong>${participationData.name}</strong>
</p>
<p class="body-s text-gray-700">
입력하신 전화번호로 쿠폰이 발송되었습니다.<br/>
우진이네 고깃집에서 사용하실 수 있습니다.
</p>
</div>
</div>
<p class="body-s text-gray-600 text-center mt-m">
SNS에 공유하시면 추가 10% 쿠폰을 드립니다!
</p>
`,
confirmText: '확인',
cancelText: null,
onConfirm: () => {
// 폼 초기화
form.reset();
}
});
});
// Form Validation
function validateForm() {
let isValid = true;
// Clear errors
document.querySelectorAll('.input-group').forEach(group => {
group.classList.remove('error');
});
// Name
const name = document.getElementById('customerName');
if (!FormValidator.validateRequired(name.value)) {
FormValidator.showError(name, '이름을 입력해주세요.');
isValid = false;
}
// Phone
const phone = document.getElementById('customerPhone');
if (!FormValidator.validateRequired(phone.value)) {
FormValidator.showError(phone, '전화번호를 입력해주세요.');
isValid = false;
} else if (!FormValidator.validatePhone(phone.value)) {
FormValidator.showError(phone, '올바른 전화번호 형식이 아닙니다. (예: 010-1234-5678)');
isValid = false;
}
// Privacy Agreement
const agreePrivacy = document.getElementById('agreePrivacy');
if (!agreePrivacy.checked) {
UIManager.showToast('개인정보 수집 및 이용에 동의해주세요.', 'error');
isValid = false;
}
return isValid;
}
// 개인정보 처리방침
function showPrivacyPolicy() {
UIManager.showModal({
title: '개인정보 처리방침',
body: `
<div class="body-s text-gray-700" style="max-height: 300px; overflow-y: auto; line-height: 1.8;">
<p class="mb-m"><strong>1. 개인정보의 수집 및 이용 목적</strong></p>
<p class="mb-l">
우진이네 고깃집은 이벤트 참여자에게 쿠폰을 발송하기 위해 개인정보를 수집합니다.
</p>
<p class="mb-m"><strong>2. 수집하는 개인정보 항목</strong></p>
<p class="mb-l">
- 필수항목: 이름, 전화번호<br/>
- 수집 방법: 이벤트 참여 신청 폼
</p>
<p class="mb-m"><strong>3. 개인정보의 보유 및 이용 기간</strong></p>
<p class="mb-l">
이벤트 종료 후 3개월까지 보관하며, 이후 즉시 파기합니다.
</p>
<p class="mb-m"><strong>4. 개인정보 제공 거부 권리</strong></p>
<p>
귀하는 개인정보 제공을 거부할 권리가 있으나, 거부 시 이벤트 참여가 제한됩니다.
</p>
</div>
`,
confirmText: '확인',
cancelText: null
});
}
// 이벤트 공유
function shareEvent(platform) {
const eventUrl = window.location.href;
const eventTitle = '우진이네 여름 특별 할인 이벤트';
const eventDescription = '전 메뉴 20% 할인! 지금 바로 참여하세요!';
if (platform === 'kakao') {
UIManager.showToast('카카오톡 공유 기능은 프로토타입에서 지원하지 않습니다.', 'info');
} else if (platform === 'link') {
// 링크 복사 (시뮬레이션)
UIManager.showToast('링크가 복사되었습니다!', 'success');
// 실제로는 clipboard API 사용
// navigator.clipboard.writeText(eventUrl);
}
}
// 실시간 에러 제거
document.querySelectorAll('input').forEach(input => {
input.addEventListener('input', () => {
FormValidator.clearError(input);
});
});
</script>
</body>
</html>

View File

@ -1,538 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>실시간 대시보드 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
<style>
.kpi-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-m);
margin-bottom: var(--space-xl);
}
.kpi-card {
background: white;
border-radius: var(--radius-medium);
padding: var(--space-l);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
gap: var(--space-s);
}
.kpi-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--space-xs);
}
.kpi-value {
font-size: var(--font-size-h1);
font-weight: var(--font-weight-bold);
color: var(--color-gray-900);
line-height: 1.2;
}
.kpi-label {
font-size: var(--font-size-body-s);
color: var(--color-gray-600);
}
.kpi-change {
display: flex;
align-items: center;
gap: 4px;
font-size: var(--font-size-body-s);
margin-top: var(--space-xs);
}
.kpi-change.positive {
color: var(--color-success);
}
.kpi-change.negative {
color: var(--color-error);
}
.event-item {
background: white;
border-radius: var(--radius-medium);
padding: var(--space-l);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: var(--space-m);
cursor: pointer;
transition: all 0.2s ease-out;
}
.event-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.event-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--space-m);
}
.event-status-badge {
padding: 4px 12px;
border-radius: var(--radius-small);
font-size: var(--font-size-body-s);
font-weight: var(--font-weight-semibold);
}
.event-status-badge.active {
background-color: var(--color-success-light);
color: var(--color-success);
}
.event-status-badge.completed {
background-color: var(--color-gray-200);
color: var(--color-gray-700);
}
.event-stats {
display: flex;
gap: var(--space-l);
margin-top: var(--space-m);
padding-top: var(--space-m);
border-top: 1px solid var(--color-gray-200);
}
.event-stat {
display: flex;
align-items: center;
gap: var(--space-xs);
font-size: var(--font-size-body-s);
color: var(--color-gray-600);
}
.chart-container {
background: white;
border-radius: var(--radius-medium);
padding: var(--space-l);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: var(--space-xl);
}
.chart-placeholder {
width: 100%;
height: 200px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: var(--radius-medium);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: var(--font-size-body-l);
font-weight: var(--font-weight-semibold);
position: relative;
overflow: hidden;
}
.chart-placeholder::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
background: repeating-linear-gradient(
45deg,
rgba(255, 255, 255, 0.1),
rgba(255, 255, 255, 0.1) 10px,
rgba(255, 255, 255, 0.05) 10px,
rgba(255, 255, 255, 0.05) 20px
);
}
@media (min-width: 768px) {
.kpi-grid {
grid-template-columns: repeat(4, 1fr);
}
}
</style>
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<div style="width: 44px;"></div>
<h1 class="top-bar-title">실시간 대시보드</h1>
<button class="icon-button" aria-label="새로고침" onclick="refreshDashboard()">
<span class="material-icons">refresh</span>
</button>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- KPI Cards -->
<section class="kpi-grid">
<!-- Total Events -->
<div class="kpi-card">
<div class="kpi-icon" style="background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 20px;">event</span>
</div>
<div class="kpi-value" id="totalEvents">12</div>
<div class="kpi-label">전체 이벤트</div>
<div class="kpi-change positive">
<span class="material-icons" style="font-size: 16px;">arrow_upward</span>
<span>+2 이번 달</span>
</div>
</div>
<!-- Active Events -->
<div class="kpi-card">
<div class="kpi-icon" style="background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 20px;">play_circle</span>
</div>
<div class="kpi-value" id="activeEvents">3</div>
<div class="kpi-label">진행 중</div>
<div class="kpi-change positive">
<span class="material-icons" style="font-size: 16px;">trending_up</span>
<span>진행 중</span>
</div>
</div>
<!-- Total Participants -->
<div class="kpi-card">
<div class="kpi-icon" style="background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 20px;">group</span>
</div>
<div class="kpi-value" id="totalParticipants">1,248</div>
<div class="kpi-label">총 참여자</div>
<div class="kpi-change positive">
<span class="material-icons" style="font-size: 16px;">arrow_upward</span>
<span>+156 오늘</span>
</div>
</div>
<!-- Sales Increase -->
<div class="kpi-card">
<div class="kpi-icon" style="background-color: var(--color-warning-light);">
<span class="material-icons text-warning" style="font-size: 20px;">trending_up</span>
</div>
<div class="kpi-value" id="salesIncrease">+23%</div>
<div class="kpi-label">매출 증가율</div>
<div class="kpi-change positive">
<span class="material-icons" style="font-size: 16px;">arrow_upward</span>
<span>전월 대비</span>
</div>
</div>
</section>
<!-- Chart Section -->
<section class="chart-container">
<h3 class="h3 mb-m">참여자 추이</h3>
<div class="chart-placeholder">
<div style="position: relative; z-index: 1;">
<span class="material-icons" style="font-size: 48px; margin-bottom: 8px;">show_chart</span>
<div>참여자 증가 추이 그래프</div>
</div>
</div>
<p class="body-s text-gray-600 mt-s">
최근 7일간 일평균 178명의 고객이 이벤트에 참여했습니다.
</p>
</section>
<!-- Active Events List -->
<section class="mb-xl">
<div class="flex items-center justify-between mb-m">
<h3 class="h3">진행 중인 이벤트</h3>
<button class="btn-text body-s" onclick="viewAllEvents()">
전체 보기
<span class="material-icons" style="font-size: 16px;">arrow_forward</span>
</button>
</div>
<div id="eventsList">
<!-- Events will be rendered here -->
</div>
</section>
<!-- AI Insights Section -->
<section class="mb-xl">
<div class="card bg-secondary-light" style="border: none;">
<div class="card-body flex gap-m">
<span class="material-icons text-secondary" style="font-size: 28px;">auto_awesome</span>
<div>
<h4 class="body-l text-semibold mb-xs text-secondary">AI 인사이트</h4>
<p class="body-m text-gray-700 mb-m">
현재 진행 중인 이벤트의 참여율이 예상보다 15% 높습니다.
추가 할인 혜택을 제공하면 더 큰 효과를 얻을 수 있습니다.
</p>
<button class="btn btn-secondary btn-sm" onclick="viewAIRecommendations()">
<span class="material-icons mr-xs" style="font-size: 18px;">lightbulb</span>
AI 개선안 보기
</button>
</div>
</div>
</div>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item active" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// Dashboard data (실제로는 API에서 가져옴)
const dashboardData = {
kpis: {
totalEvents: 12,
activeEvents: 3,
totalParticipants: 1248,
salesIncrease: 23
},
events: [
{
id: 1,
name: '우진이네 여름 특별 할인 이벤트',
status: 'active',
startDate: '2025.06.01',
endDate: '2025.06.30',
participants: 456,
views: 1234,
clickRate: 37
},
{
id: 2,
name: '신메뉴 출시 기념 이벤트',
status: 'active',
startDate: '2025.06.15',
endDate: '2025.07.15',
participants: 234,
views: 890,
clickRate: 26
},
{
id: 3,
name: '재방문 고객 감사 이벤트',
status: 'active',
startDate: '2025.06.10',
endDate: '2025.06.25',
participants: 558,
views: 2100,
clickRate: 27
},
{
id: 4,
name: '봄맞이 할인 이벤트',
status: 'completed',
startDate: '2025.03.01',
endDate: '2025.03.31',
participants: 892,
views: 3200,
clickRate: 28
}
]
};
// Render events list
function renderEventsList() {
const container = document.getElementById('eventsList');
const activeEvents = dashboardData.events.filter(e => e.status === 'active');
if (activeEvents.length === 0) {
container.innerHTML = `
<div class="card bg-gray-100" style="border: none;">
<div class="card-body text-center">
<span class="material-icons text-gray-400" style="font-size: 48px;">event_busy</span>
<p class="body-m text-gray-600 mt-m">진행 중인 이벤트가 없습니다.</p>
</div>
</div>
`;
return;
}
container.innerHTML = activeEvents.map(event => `
<div class="event-item" onclick="viewEventDetail(${event.id})">
<div class="event-header">
<div>
<h4 class="body-l text-semibold mb-xs">${event.name}</h4>
<p class="body-s text-gray-600">${event.startDate} ~ ${event.endDate}</p>
</div>
<span class="event-status-badge ${event.status}">
${event.status === 'active' ? '진행 중' : '완료'}
</span>
</div>
<div class="event-stats">
<div class="event-stat">
<span class="material-icons" style="font-size: 18px;">group</span>
<span>${Utils.formatNumber(event.participants)}명 참여</span>
</div>
<div class="event-stat">
<span class="material-icons" style="font-size: 18px;">visibility</span>
<span>${Utils.formatNumber(event.views)}회 조회</span>
</div>
<div class="event-stat">
<span class="material-icons" style="font-size: 18px;">ads_click</span>
<span>${event.clickRate}% 클릭률</span>
</div>
</div>
</div>
`).join('');
}
// Update KPIs
function updateKPIs() {
document.getElementById('totalEvents').textContent = dashboardData.kpis.totalEvents;
document.getElementById('activeEvents').textContent = dashboardData.kpis.activeEvents;
document.getElementById('totalParticipants').textContent =
Utils.formatNumber(dashboardData.kpis.totalParticipants);
document.getElementById('salesIncrease').textContent =
`+${dashboardData.kpis.salesIncrease}%`;
}
// Refresh dashboard
function refreshDashboard() {
UIManager.showLoading('데이터를 불러오는 중...');
// API 호출 시뮬레이션
setTimeout(() => {
// 데이터 업데이트 (실제로는 API 응답으로 업데이트)
updateKPIs();
renderEventsList();
UIManager.hideLoading();
UIManager.showToast('대시보드가 업데이트되었습니다.', 'success');
}, 1000);
}
// View event detail
function viewEventDetail(eventId) {
const event = dashboardData.events.find(e => e.id === eventId);
UIManager.showModal({
title: event.name,
body: `
<div class="flex flex-col gap-m">
<div class="flex items-center gap-s">
<span class="material-icons text-gray-600">calendar_month</span>
<span class="body-m text-gray-900">${event.startDate} ~ ${event.endDate}</span>
</div>
<div class="divider"></div>
<div class="flex flex-col gap-s">
<div class="flex justify-between body-m">
<span class="text-gray-600">참여자 수</span>
<span class="text-semibold">${Utils.formatNumber(event.participants)}명</span>
</div>
<div class="flex justify-between body-m">
<span class="text-gray-600">조회 수</span>
<span class="text-semibold">${Utils.formatNumber(event.views)}회</span>
</div>
<div class="flex justify-between body-m">
<span class="text-gray-600">클릭률</span>
<span class="text-semibold">${event.clickRate}%</span>
</div>
</div>
<div class="divider"></div>
<p class="body-s text-gray-600">
이벤트 참여율이 목표 대비 ${Math.floor(Math.random() * 30 + 10)}% 높습니다.
</p>
</div>
`,
confirmText: '확인',
cancelText: null
});
}
// View all events
function viewAllEvents() {
UIManager.showBottomSheet({
title: '전체 이벤트',
body: `
<div style="max-height: 400px; overflow-y: auto;">
${dashboardData.events.map(event => `
<div class="event-item" style="margin-bottom: var(--space-m);" onclick="viewEventDetail(${event.id})">
<div class="event-header">
<div>
<h4 class="body-l text-semibold mb-xs">${event.name}</h4>
<p class="body-s text-gray-600">${event.startDate} ~ ${event.endDate}</p>
</div>
<span class="event-status-badge ${event.status}">
${event.status === 'active' ? '진행 중' : '완료'}
</span>
</div>
<div class="event-stats">
<div class="event-stat">
<span class="material-icons" style="font-size: 18px;">group</span>
<span>${Utils.formatNumber(event.participants)}명</span>
</div>
<div class="event-stat">
<span class="material-icons" style="font-size: 18px;">visibility</span>
<span>${Utils.formatNumber(event.views)}회</span>
</div>
</div>
</div>
`).join('')}
</div>
`,
confirmText: '닫기',
cancelText: null
});
}
// View AI recommendations
function viewAIRecommendations() {
// Navigate to AI improvement suggestions page
NavManager.navigate('25', 'AI개선안제안');
}
// Page initialization
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('analytics');
// Dashboard 데이터 렌더링
updateKPIs();
renderEventsList();
// Auto-refresh every 30 seconds (프로토타입에서는 비활성화)
// setInterval(refreshDashboard, 30000);
});
</script>
</body>
</html>

View File

@ -1,467 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI 개선안 제안 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
<style>
.suggestion-card {
background: white;
border-radius: var(--radius-medium);
padding: var(--space-l);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: var(--space-m);
border-left: 4px solid var(--color-secondary);
}
.suggestion-header {
display: flex;
align-items: flex-start;
gap: var(--space-m);
margin-bottom: var(--space-m);
}
.suggestion-icon {
width: 48px;
height: 48px;
border-radius: 50%;
background-color: var(--color-secondary-light);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.suggestion-content {
flex: 1;
}
.suggestion-type {
display: inline-block;
padding: 4px 8px;
border-radius: var(--radius-small);
font-size: var(--font-size-body-s);
font-weight: var(--font-weight-semibold);
margin-bottom: var(--space-xs);
}
.suggestion-type.optimization {
background-color: var(--color-primary-light);
color: var(--color-primary);
}
.suggestion-type.targeting {
background-color: var(--color-success-light);
color: var(--color-success);
}
.suggestion-type.content {
background-color: var(--color-warning-light);
color: var(--color-warning);
}
.suggestion-impact {
display: flex;
align-items: center;
gap: var(--space-s);
margin-top: var(--space-m);
padding: var(--space-m);
background-color: var(--color-gray-100);
border-radius: var(--radius-small);
}
.impact-label {
font-size: var(--font-size-body-s);
color: var(--color-gray-600);
}
.impact-value {
font-size: var(--font-size-body-l);
font-weight: var(--font-weight-bold);
color: var(--color-success);
}
.suggestion-actions {
display: flex;
gap: var(--space-s);
margin-top: var(--space-m);
}
.suggestion-status {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-s) var(--space-m);
border-radius: var(--radius-small);
font-size: var(--font-size-body-s);
font-weight: var(--font-weight-semibold);
margin-top: var(--space-m);
}
.suggestion-status.accepted {
background-color: var(--color-success-light);
color: var(--color-success);
}
.suggestion-status.rejected {
background-color: var(--color-gray-200);
color: var(--color-gray-700);
}
.empty-state {
text-align: center;
padding: var(--space-2xl) var(--space-l);
}
.empty-state-icon {
font-size: 64px;
color: var(--color-gray-300);
margin-bottom: var(--space-m);
}
</style>
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<button class="back-button" aria-label="뒤로가기">
<span class="material-icons">arrow_back</span>
</button>
<h1 class="top-bar-title">AI 개선안 제안</h1>
<div style="width: 44px;"></div>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Header Section -->
<section class="mb-l">
<div class="flex items-center gap-s mb-s">
<span class="material-icons text-secondary" style="font-size: 28px;">auto_awesome</span>
<h2 class="h2">AI가 분석한 개선안</h2>
</div>
<p class="body-m text-gray-700">
현재 진행 중인 이벤트를 분석하여 성과를 높일 수 있는 개선안을 제안합니다.
</p>
</section>
<!-- Suggestions List -->
<section id="suggestionsContainer" class="mb-xl">
<!-- Suggestions will be rendered here -->
</section>
<!-- Generate New Suggestions Button -->
<section>
<button class="btn btn-secondary btn-full" onclick="generateNewSuggestions()">
<span class="material-icons mr-xs">refresh</span>
새로운 개선안 생성
</button>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item active" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// AI Suggestions data (실제로는 API에서 가져옴)
let suggestions = [
{
id: 1,
type: 'optimization',
typeLabel: '최적화',
eventName: '우진이네 여름 특별 할인 이벤트',
title: '할인율 조정 제안',
description: '현재 참여율이 목표 대비 15% 높습니다. 할인율을 20%에서 25%로 상향 조정하면 참여자가 30% 이상 증가할 것으로 예상됩니다.',
rationale: '경쟁 매장 분석 결과, 유사 이벤트의 평균 할인율은 22-28%입니다. 현재 할인율은 경쟁력이 다소 낮은 수준입니다.',
expectedImpact: '+30% 참여자 증가',
impactValue: '+30%',
status: null
},
{
id: 2,
type: 'targeting',
typeLabel: '타겟팅',
eventName: '신메뉴 출시 기념 이벤트',
title: '타겟 시간대 확대',
description: '점심 시간대(11:00-14:00)에 집중된 현재 전략을 저녁 시간대(18:00-21:00)로 확대하면 참여자 기반을 넓힐 수 있습니다.',
rationale: '데이터 분석 결과, 저녁 시간대 방문 고객 중 65%가 이벤트를 인지하지 못하고 있습니다.',
expectedImpact: '+45% 도달률 증가',
impactValue: '+45%',
status: null
},
{
id: 3,
type: 'content',
typeLabel: '콘텐츠',
eventName: '재방문 고객 감사 이벤트',
title: 'SNS 공유 인센티브 강화',
description: 'SNS 공유 시 추가 할인율을 10%에서 15%로 상향하고, 리뷰 작성 시 추가 쿠폰을 제공하면 바이럴 효과가 증가합니다.',
rationale: '현재 SNS 공유율은 12%로 업계 평균(18-25%)보다 낮습니다. 인센티브 강화로 공유율을 2배 이상 높일 수 있습니다.',
expectedImpact: '+120% SNS 공유 증가',
impactValue: '+120%',
status: null
}
];
// Render suggestions
function renderSuggestions() {
const container = document.getElementById('suggestionsContainer');
if (suggestions.length === 0) {
container.innerHTML = `
<div class="empty-state">
<span class="material-icons empty-state-icon">lightbulb_outline</span>
<h3 class="h3 mb-s">제안 가능한 개선안이 없습니다</h3>
<p class="body-m text-gray-600 mb-l">
진행 중인 이벤트가 최적화되어 있거나,<br/>
충분한 데이터가 수집되지 않았습니다.
</p>
<button class="btn btn-primary" onclick="generateNewSuggestions()">
새로운 개선안 생성
</button>
</div>
`;
return;
}
container.innerHTML = suggestions.map(suggestion => `
<div class="suggestion-card">
<div class="suggestion-header">
<div class="suggestion-icon">
<span class="material-icons text-secondary" style="font-size: 24px;">
${suggestion.type === 'optimization' ? 'tune' :
suggestion.type === 'targeting' ? 'my_location' : 'create'}
</span>
</div>
<div class="suggestion-content">
<span class="suggestion-type ${suggestion.type}">${suggestion.typeLabel}</span>
<h3 class="body-l text-semibold mb-xs">${suggestion.title}</h3>
<p class="body-s text-gray-600">${suggestion.eventName}</p>
</div>
</div>
<div class="mb-m">
<h4 class="body-m text-semibold mb-xs">개선안</h4>
<p class="body-m text-gray-900">${suggestion.description}</p>
</div>
<div class="mb-m">
<h4 class="body-m text-semibold mb-xs">분석 근거</h4>
<p class="body-s text-gray-700">${suggestion.rationale}</p>
</div>
<div class="suggestion-impact">
<span class="material-icons text-success">trending_up</span>
<div>
<div class="impact-label">예상 효과</div>
<div class="impact-value">${suggestion.impactValue}</div>
</div>
<div class="flex-1 text-right">
<p class="body-s text-gray-700">${suggestion.expectedImpact}</p>
</div>
</div>
${suggestion.status ? `
<div class="suggestion-status ${suggestion.status}">
<span class="material-icons" style="font-size: 18px;">
${suggestion.status === 'accepted' ? 'check_circle' : 'cancel'}
</span>
<span>${suggestion.status === 'accepted' ? '적용됨' : '거절됨'}</span>
</div>
` : `
<div class="suggestion-actions">
<button class="btn btn-secondary flex-1" onclick="rejectSuggestion(${suggestion.id})">
<span class="material-icons mr-xs" style="font-size: 18px;">close</span>
거절
</button>
<button class="btn btn-primary flex-1" onclick="acceptSuggestion(${suggestion.id})">
<span class="material-icons mr-xs" style="font-size: 18px;">check</span>
적용
</button>
</div>
`}
</div>
`).join('');
}
// Accept suggestion
function acceptSuggestion(suggestionId) {
const suggestion = suggestions.find(s => s.id === suggestionId);
UIManager.showModal({
title: '개선안 적용',
body: `
<p class="body-m text-gray-900 mb-m">
<strong>${suggestion.title}</strong>을(를) 적용하시겠습니까?
</p>
<div class="card bg-success-light" style="border: none;">
<div class="card-body">
<p class="body-s text-gray-700">
<strong>예상 효과:</strong> ${suggestion.expectedImpact}
</p>
</div>
</div>
<p class="body-s text-gray-600 mt-m">
이벤트 설정이 즉시 업데이트되며, 변경 사항은 실시간으로 반영됩니다.
</p>
`,
confirmText: '적용',
cancelText: '취소',
onConfirm: () => {
UIManager.showLoading('개선안을 적용하고 있습니다...');
// API 호출 시뮬레이션
setTimeout(() => {
suggestion.status = 'accepted';
AppState.save('acceptedSuggestions',
suggestions.filter(s => s.status === 'accepted').map(s => s.id));
UIManager.hideLoading();
UIManager.showToast('개선안이 적용되었습니다!', 'success');
renderSuggestions();
}, 1500);
}
});
}
// Reject suggestion
function rejectSuggestion(suggestionId) {
const suggestion = suggestions.find(s => s.id === suggestionId);
UIManager.showModal({
title: '개선안 거절',
body: `
<p class="body-m text-gray-900 mb-m">
<strong>${suggestion.title}</strong>을(를) 거절하시겠습니까?
</p>
<p class="body-s text-gray-600">
거절된 개선안은 목록에서 제거되며, 언제든지 새로운 개선안을 생성할 수 있습니다.
</p>
`,
confirmText: '거절',
cancelText: '취소',
onConfirm: () => {
suggestion.status = 'rejected';
AppState.save('rejectedSuggestions',
suggestions.filter(s => s.status === 'rejected').map(s => s.id));
UIManager.showToast('개선안이 거절되었습니다.', 'info');
renderSuggestions();
}
});
}
// Generate new suggestions
function generateNewSuggestions() {
UIManager.showModal({
title: '새로운 개선안 생성',
body: `
<p class="body-m text-gray-700 mb-m">
현재 이벤트 데이터를 분석하여 새로운 개선안을 생성하시겠습니까?
</p>
<div class="card bg-info-light" style="border: none;">
<div class="card-body">
<p class="body-s text-gray-700">
💡 AI가 최신 데이터를 분석하여 최적의 개선안을 제시합니다.
이전에 거절한 개선안은 제외됩니다.
</p>
</div>
</div>
`,
confirmText: '생성',
cancelText: '취소',
onConfirm: () => {
UIManager.showLoading('AI가 새로운 개선안을 분석하고 있습니다...');
// Progress 업데이트
let progress = 0;
const progressInterval = setInterval(() => {
progress += 15;
UIManager.updateProgress(progress);
if (progress >= 100) {
clearInterval(progressInterval);
setTimeout(() => {
// 새로운 개선안 추가 (시뮬레이션)
const newSuggestion = {
id: suggestions.length + 1,
type: 'targeting',
typeLabel: '타겟팅',
eventName: '우진이네 여름 특별 할인 이벤트',
title: '주말 특별 프로모션 추가',
description: '주말에 별도의 추가 할인(5%)을 제공하면 주말 방문객을 25% 증가시킬 수 있습니다.',
rationale: '주말 방문 고객의 평균 구매액이 평일 대비 40% 높으나, 현재 주말 특화 전략이 없습니다.',
expectedImpact: '+25% 주말 방문 증가',
impactValue: '+25%',
status: null
};
suggestions.unshift(newSuggestion);
UIManager.hideLoading();
UIManager.showToast('새로운 개선안이 생성되었습니다!', 'success');
renderSuggestions();
}, 500);
}
}, 200);
}
});
}
// Page initialization
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('analytics');
// 이전에 저장된 상태 복원
const acceptedIds = AppState.load('acceptedSuggestions') || [];
const rejectedIds = AppState.load('rejectedSuggestions') || [];
suggestions.forEach(suggestion => {
if (acceptedIds.includes(suggestion.id)) {
suggestion.status = 'accepted';
} else if (rejectedIds.includes(suggestion.id)) {
suggestion.status = 'rejected';
}
});
// Render suggestions
renderSuggestions();
});
</script>
</body>
</html>

View File

@ -1,646 +0,0 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>마이페이지 - KT AI 이벤트 자동 생성</title>
<!-- Pretendard Font -->
<link rel="stylesheet" as="style" crossorigin href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css" />
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Common CSS -->
<link rel="stylesheet" href="common.css">
<style>
.profile-section {
background: white;
border-radius: var(--radius-medium);
padding: var(--space-l);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
margin-bottom: var(--space-xl);
text-align: center;
}
.profile-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto var(--space-m);
color: white;
font-size: var(--font-size-h1);
font-weight: var(--font-weight-bold);
}
.menu-section {
margin-bottom: var(--space-xl);
}
.menu-title {
font-size: var(--font-size-body-m);
font-weight: var(--font-weight-semibold);
color: var(--color-gray-600);
margin-bottom: var(--space-m);
padding: 0 var(--space-s);
}
.menu-list {
background: white;
border-radius: var(--radius-medium);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
overflow: hidden;
}
.menu-item {
display: flex;
align-items: center;
padding: var(--space-l);
cursor: pointer;
transition: background-color 0.2s ease-out;
border-bottom: 1px solid var(--color-gray-100);
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:hover {
background-color: var(--color-gray-50);
}
.menu-item:active {
background-color: var(--color-gray-100);
}
.menu-icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: var(--space-m);
}
.menu-content {
flex: 1;
}
.menu-label {
font-size: var(--font-size-body-l);
font-weight: var(--font-weight-medium);
color: var(--color-gray-900);
margin-bottom: 2px;
}
.menu-description {
font-size: var(--font-size-body-s);
color: var(--color-gray-600);
}
.menu-arrow {
color: var(--color-gray-400);
}
.toggle-switch {
position: relative;
width: 44px;
height: 24px;
background-color: var(--color-gray-300);
border-radius: 12px;
cursor: pointer;
transition: background-color 0.2s ease-out;
}
.toggle-switch.active {
background-color: var(--color-primary);
}
.toggle-switch::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background-color: white;
border-radius: 50%;
transition: transform 0.2s ease-out;
}
.toggle-switch.active::after {
transform: translateX(20px);
}
.logout-button {
margin-top: var(--space-l);
margin-bottom: var(--space-2xl);
}
</style>
</head>
<body>
<!-- Top Bar -->
<header class="top-bar">
<div style="width: 44px;"></div>
<h1 class="top-bar-title">MY</h1>
<button class="icon-button" aria-label="설정" onclick="openSettings()">
<span class="material-icons">settings</span>
</button>
</header>
<!-- Page Container -->
<main class="page">
<div class="page-content">
<!-- Profile Section -->
<section class="profile-section">
<div class="profile-avatar" id="profileAvatar"></div>
<h2 class="h2 mb-xs" id="userName">정우진</h2>
<p class="body-m text-gray-600 mb-s" id="businessName">우진이네 고깃집</p>
<p class="body-s text-gray-500 mb-l" id="userEmail">woojin@example.com</p>
<button class="btn btn-secondary" onclick="editProfile()">
<span class="material-icons mr-xs" style="font-size: 18px;">edit</span>
프로필 수정
</button>
</section>
<!-- Business Settings -->
<section class="menu-section">
<h3 class="menu-title">사업장 관리</h3>
<div class="menu-list">
<div class="menu-item" onclick="openBusinessInfo()">
<div class="menu-icon" style="background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 20px;">store</span>
</div>
<div class="menu-content">
<div class="menu-label">사업장 정보</div>
<div class="menu-description">업종, 주소, 연락처 관리</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="openOperatingHours()">
<div class="menu-icon" style="background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 20px;">schedule</span>
</div>
<div class="menu-content">
<div class="menu-label">운영 시간</div>
<div class="menu-description">영업 시간 및 휴무일 설정</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
</section>
<!-- Event Settings -->
<section class="menu-section">
<h3 class="menu-title">이벤트 관리</h3>
<div class="menu-list">
<div class="menu-item" onclick="openEventHistory()">
<div class="menu-icon" style="background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 20px;">history</span>
</div>
<div class="menu-content">
<div class="menu-label">이벤트 내역</div>
<div class="menu-description">지난 이벤트 조회 및 관리</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="openTemplates()">
<div class="menu-icon" style="background-color: var(--color-warning-light);">
<span class="material-icons text-warning" style="font-size: 20px;">folder_special</span>
</div>
<div class="menu-content">
<div class="menu-label">저장된 템플릿</div>
<div class="menu-description">자주 사용하는 이벤트 템플릿</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
</section>
<!-- Account Settings -->
<section class="menu-section">
<h3 class="menu-title">계정 설정</h3>
<div class="menu-list">
<div class="menu-item">
<div class="menu-icon" style="background-color: var(--color-info-light);">
<span class="material-icons text-info" style="font-size: 20px;">notifications</span>
</div>
<div class="menu-content">
<div class="menu-label">알림 설정</div>
<div class="menu-description">이벤트 알림 수신</div>
</div>
<div class="toggle-switch active" id="notificationToggle" onclick="toggleNotification(event)"></div>
</div>
<div class="menu-item" onclick="openLanguage()">
<div class="menu-icon" style="background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 20px;">language</span>
</div>
<div class="menu-content">
<div class="menu-label">언어 설정</div>
<div class="menu-description">한국어</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="openSecurity()">
<div class="menu-icon" style="background-color: var(--color-error-light);">
<span class="material-icons text-error" style="font-size: 20px;">lock</span>
</div>
<div class="menu-content">
<div class="menu-label">보안 설정</div>
<div class="menu-description">비밀번호 변경, 2단계 인증</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
</section>
<!-- Help & Support -->
<section class="menu-section">
<h3 class="menu-title">고객 지원</h3>
<div class="menu-list">
<div class="menu-item" onclick="openGuide()">
<div class="menu-icon" style="background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 20px;">help</span>
</div>
<div class="menu-content">
<div class="menu-label">사용 가이드</div>
<div class="menu-description">서비스 이용 방법 안내</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="openFAQ()">
<div class="menu-icon" style="background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 20px;">question_answer</span>
</div>
<div class="menu-content">
<div class="menu-label">자주 묻는 질문</div>
<div class="menu-description">FAQ 및 문제 해결</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="openContact()">
<div class="menu-icon" style="background-color: var(--color-secondary-light);">
<span class="material-icons text-secondary" style="font-size: 20px;">support_agent</span>
</div>
<div class="menu-content">
<div class="menu-label">고객센터</div>
<div class="menu-description">1:1 문의 및 상담</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="openTerms()">
<div class="menu-icon" style="background-color: var(--color-gray-200);">
<span class="material-icons text-gray-700" style="font-size: 20px;">description</span>
</div>
<div class="menu-content">
<div class="menu-label">약관 및 정책</div>
<div class="menu-description">이용약관, 개인정보처리방침</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
</section>
<!-- App Info -->
<section class="menu-section">
<div class="menu-list">
<div class="menu-item" onclick="openVersionInfo()">
<div class="menu-icon" style="background-color: var(--color-info-light);">
<span class="material-icons text-info" style="font-size: 20px;">info</span>
</div>
<div class="menu-content">
<div class="menu-label">앱 정보</div>
<div class="menu-description">버전 1.0.0</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
</section>
<!-- Logout Button -->
<section class="logout-button">
<button class="btn btn-text btn-full text-error" onclick="logout()">
<span class="material-icons mr-xs">logout</span>
로그아웃
</button>
</section>
</div>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<a href="#" class="nav-item" data-nav="home">
<span class="material-icons">home</span>
<span></span>
</a>
<a href="#" class="nav-item" data-nav="event">
<span class="material-icons">event</span>
<span>이벤트</span>
</a>
<a href="#" class="nav-item" data-nav="analytics">
<span class="material-icons">analytics</span>
<span>분석</span>
</a>
<a href="#" class="nav-item active" data-nav="my">
<span class="material-icons">person</span>
<span>MY</span>
</a>
</nav>
</main>
<!-- Common JavaScript -->
<script src="common.js"></script>
<!-- Page Specific JavaScript -->
<script>
// Load user profile
function loadProfile() {
const user = AppState.loadUser() || SampleData.user;
document.getElementById('userName').textContent = user.name;
document.getElementById('businessName').textContent = user.businessName;
document.getElementById('userEmail').textContent = user.email;
document.getElementById('profileAvatar').textContent = user.name.charAt(0);
}
// Edit profile
function editProfile() {
const user = AppState.loadUser() || SampleData.user;
UIManager.showModal({
title: '프로필 수정',
body: `
<div class="flex flex-col gap-m">
<div class="input-group" style="margin-bottom: 0;">
<label for="editName">이름</label>
<input type="text" id="editName" class="input" value="${user.name}" />
</div>
<div class="input-group" style="margin-bottom: 0;">
<label for="editBusinessName">사업장명</label>
<input type="text" id="editBusinessName" class="input" value="${user.businessName}" />
</div>
<div class="input-group" style="margin-bottom: 0;">
<label for="editEmail">이메일</label>
<input type="email" id="editEmail" class="input" value="${user.email}" />
</div>
<div class="input-group" style="margin-bottom: 0;">
<label for="editPhone">전화번호</label>
<input type="tel" id="editPhone" class="input" value="${user.phone || ''}" />
</div>
</div>
`,
confirmText: '저장',
cancelText: '취소',
onConfirm: () => {
const updatedUser = {
...user,
name: document.getElementById('editName').value,
businessName: document.getElementById('editBusinessName').value,
email: document.getElementById('editEmail').value,
phone: document.getElementById('editPhone').value
};
AppState.saveUser(updatedUser);
UIManager.showToast('프로필이 수정되었습니다.', 'success');
loadProfile();
}
});
}
// Toggle notification
function toggleNotification(event) {
event.stopPropagation();
const toggle = document.getElementById('notificationToggle');
const isActive = toggle.classList.contains('active');
toggle.classList.toggle('active');
AppState.save('notificationEnabled', !isActive);
const message = !isActive ? '알림이 활성화되었습니다.' : '알림이 비활성화되었습니다.';
UIManager.showToast(message, 'info');
}
// Settings menu handlers
function openSettings() {
UIManager.showToast('설정 페이지 준비 중입니다.', 'info');
}
function openBusinessInfo() {
UIManager.showToast('사업장 정보 페이지 준비 중입니다.', 'info');
}
function openOperatingHours() {
UIManager.showToast('운영 시간 설정 페이지 준비 중입니다.', 'info');
}
function openEventHistory() {
UIManager.showToast('이벤트 내역 페이지 준비 중입니다.', 'info');
}
function openTemplates() {
UIManager.showToast('템플릿 페이지 준비 중입니다.', 'info');
}
function openLanguage() {
UIManager.showBottomSheet({
title: '언어 선택',
body: `
<div class="flex flex-col gap-s">
<label class="radio card p-m" style="cursor: pointer;">
<div class="flex items-center gap-m">
<input type="radio" name="language" value="ko" checked />
<div class="flex-1">
<div class="body-l text-semibold">한국어</div>
</div>
</div>
</label>
<label class="radio card p-m" style="cursor: pointer;">
<div class="flex items-center gap-m">
<input type="radio" name="language" value="en" />
<div class="flex-1">
<div class="body-l text-semibold">English</div>
</div>
</div>
</label>
</div>
`,
confirmText: '확인',
cancelText: '취소',
onConfirm: () => {
UIManager.showToast('언어가 변경되었습니다.', 'success');
}
});
}
function openSecurity() {
UIManager.showBottomSheet({
title: '보안 설정',
body: `
<div class="flex flex-col gap-m">
<div class="menu-item" onclick="changePassword()" style="padding: var(--space-m); cursor: pointer;">
<div class="menu-icon" style="background-color: var(--color-primary-light);">
<span class="material-icons text-primary" style="font-size: 20px;">lock</span>
</div>
<div class="menu-content">
<div class="menu-label">비밀번호 변경</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" onclick="setup2FA()" style="padding: var(--space-m); cursor: pointer;">
<div class="menu-icon" style="background-color: var(--color-success-light);">
<span class="material-icons text-success" style="font-size: 20px;">security</span>
</div>
<div class="menu-content">
<div class="menu-label">2단계 인증 설정</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
`,
confirmText: '닫기',
cancelText: null
});
}
function changePassword() {
UIManager.closeBottomSheet();
UIManager.showToast('비밀번호 변경 기능 준비 중입니다.', 'info');
}
function setup2FA() {
UIManager.closeBottomSheet();
UIManager.showToast('2단계 인증 설정 기능 준비 중입니다.', 'info');
}
function openGuide() {
UIManager.showToast('사용 가이드 페이지 준비 중입니다.', 'info');
}
function openFAQ() {
UIManager.showToast('FAQ 페이지 준비 중입니다.', 'info');
}
function openContact() {
UIManager.showToast('고객센터 페이지 준비 중입니다.', 'info');
}
function openTerms() {
UIManager.showBottomSheet({
title: '약관 및 정책',
body: `
<div class="flex flex-col gap-m">
<div class="menu-item" style="padding: var(--space-m); cursor: pointer;">
<div class="menu-content">
<div class="menu-label">이용약관</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" style="padding: var(--space-m); cursor: pointer;">
<div class="menu-content">
<div class="menu-label">개인정보 처리방침</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
<div class="menu-item" style="padding: var(--space-m); cursor: pointer;">
<div class="menu-content">
<div class="menu-label">마케팅 정보 수신 동의</div>
</div>
<span class="material-icons menu-arrow">chevron_right</span>
</div>
</div>
`,
confirmText: '닫기',
cancelText: null
});
}
function openVersionInfo() {
UIManager.showModal({
title: '앱 정보',
body: `
<div class="text-center mb-m">
<div class="profile-avatar" style="width: 64px; height: 64px; margin: 0 auto var(--space-m);">
<span class="material-icons" style="font-size: 32px;">storefront</span>
</div>
<h3 class="h3 mb-xs">KT AI 이벤트 자동 생성</h3>
<p class="body-m text-gray-600 mb-m">버전 1.0.0</p>
</div>
<div class="card bg-gray-100" style="border: none;">
<div class="card-body">
<p class="body-s text-gray-700" style="line-height: 1.8;">
© 2025 KT Corporation.<br/>
All rights reserved.<br/><br/>
AI 기반 소상공인 이벤트 자동 생성 서비스
</p>
</div>
</div>
`,
confirmText: '확인',
cancelText: null
});
}
// Logout
function logout() {
UIManager.showModal({
title: '로그아웃',
body: `
<p class="body-m text-gray-900 mb-m">
정말 로그아웃하시겠습니까?
</p>
<p class="body-s text-gray-600">
저장되지 않은 데이터는 유지되지 않을 수 있습니다.
</p>
`,
confirmText: '로그아웃',
cancelText: '취소',
onConfirm: () => {
UIManager.showLoading('로그아웃 중...');
setTimeout(() => {
// Clear user data (프로토타입에서는 일부만 삭제)
// AppState.clear(); // 전체 삭제는 프로토타입 테스트를 위해 주석 처리
UIManager.hideLoading();
UIManager.showToast('로그아웃되었습니다.', 'success');
// Redirect to login (프로토타입에서는 메인으로)
setTimeout(() => {
NavManager.navigate('00', '메인대시보드');
}, 500);
}, 1000);
}
});
}
// Page initialization
document.addEventListener('DOMContentLoaded', () => {
// Bottom Navigation 초기화
NavManager.initBottomNav('my');
// Load profile
loadProfile();
// Load notification setting
const notificationEnabled = AppState.load('notificationEnabled');
if (notificationEnabled === false) {
document.getElementById('notificationToggle').classList.remove('active');
}
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,774 +0,0 @@
/**
* KT AI 기반 소상공인 이벤트 자동 생성 서비스
* 공통 JavaScript
*
* 기능:
* - 네비게이션 관리
* - 상태 관리 (localStorage)
* - UI 유틸리티 (Toast, Modal, Bottom Sheet)
* - 검증
* - 애니메이션 헬퍼
*/
// ============================================
// 1. Navigation Manager
// ============================================
const NavManager = {
/**
* 화면 이동
* @param {string} pageNumber - 화면 번호 (00, 01, 03 )
* @param {string} pageName - 화면명 (한글)
*/
navigate: (pageNumber, pageName) => {
window.location.href = `${pageNumber}-${pageName}.html`;
},
/**
* 뒤로가기
*/
back: () => {
window.history.back();
},
/**
* Bottom Navigation 초기화
* @param {string} activeMenu - 활성 메뉴 ('home' | 'event' | 'analytics' | 'my')
*/
initBottomNav: (activeMenu = 'home') => {
const navItems = document.querySelectorAll('.nav-item');
navItems.forEach(item => {
const menu = item.getAttribute('data-nav');
// 활성 메뉴 표시
if (menu === activeMenu) {
item.classList.add('active');
}
// 클릭 이벤트
item.addEventListener('click', (e) => {
e.preventDefault();
switch (menu) {
case 'home':
NavManager.navigate('00', '메인대시보드');
break;
case 'event':
NavManager.navigate('03', '이벤트목적선택');
break;
case 'analytics':
NavManager.navigate('21', '실시간대시보드');
break;
case 'my':
NavManager.navigate('99', '마이페이지');
break;
}
});
});
}
};
// ============================================
// 2. State Management
// ============================================
const AppState = {
/**
* 데이터 저장
* @param {string} key - 저장
* @param {*} data - 저장할 데이터
*/
save: (key, data) => {
try {
localStorage.setItem(key, JSON.stringify(data));
return true;
} catch (error) {
console.error('Failed to save data:', error);
return false;
}
},
/**
* 데이터 로드
* @param {string} key - 로드
* @returns {*} 저장된 데이터 또는 null
*/
load: (key) => {
try {
const data = localStorage.getItem(key);
return data ? JSON.parse(data) : null;
} catch (error) {
console.error('Failed to load data:', error);
return null;
}
},
/**
* 데이터 삭제
* @param {string} key - 삭제
*/
remove: (key) => {
localStorage.removeItem(key);
},
/**
* 전체 데이터 삭제
*/
clear: () => {
localStorage.clear();
},
/**
* 사용자 정보 저장
*/
saveUser: (userData) => {
AppState.save('user', userData);
},
/**
* 사용자 정보 로드
*/
loadUser: () => {
return AppState.load('user');
},
/**
* 이벤트 데이터 저장
*/
saveEvent: (eventData) => {
const events = AppState.load('events') || [];
events.push(eventData);
AppState.save('events', events);
},
/**
* 이벤트 목록 로드
*/
loadEvents: () => {
return AppState.load('events') || [];
}
};
// ============================================
// 3. UI Manager
// ============================================
const UIManager = {
/**
* Toast 메시지 표시
* @param {string} message - 메시지 내용
* @param {string} type - 타입 ('success' | 'error' | 'warning' | 'info')
* @param {number} duration - 표시 시간 (ms, 기본 3000)
*/
showToast: (message, type = 'info', duration = 3000) => {
// 기존 Toast 제거
const existingToast = document.querySelector('.toast');
if (existingToast) {
existingToast.remove();
}
// Toast 생성
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.textContent = message;
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'polite');
document.body.appendChild(toast);
// 애니메이션
setTimeout(() => {
toast.classList.add('show');
}, 10);
// 자동 제거
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
toast.remove();
}, 300);
}, duration);
},
/**
* Modal 표시
* @param {Object} options - Modal 옵션
* @param {string} options.title - 제목
* @param {string} options.body - 내용
* @param {string} options.confirmText - 확인 버튼 텍스트
* @param {string} options.cancelText - 취소 버튼 텍스트
* @param {Function} options.onConfirm - 확인 콜백
* @param {Function} options.onCancel - 취소 콜백
*/
showModal: (options) => {
const {
title = '알림',
body = '',
confirmText = '확인',
cancelText = '취소',
onConfirm = null,
onCancel = null
} = options;
// Modal HTML 생성
const modalHTML = `
<div class="modal-overlay" id="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="modal-title">${title}</h2>
</div>
<div class="modal-body">
${body}
</div>
<div class="modal-footer">
${cancelText ? `<button class="btn btn-text" id="modal-cancel">${cancelText}</button>` : ''}
<button class="btn btn-primary" id="modal-confirm">${confirmText}</button>
</div>
</div>
</div>
`;
// DOM에 추가
const modalContainer = document.createElement('div');
modalContainer.innerHTML = modalHTML;
document.body.appendChild(modalContainer.firstElementChild);
const modalElement = document.getElementById('modal');
// 표시 애니메이션
setTimeout(() => {
modalElement.classList.add('show');
}, 10);
// 확인 버튼
const confirmBtn = document.getElementById('modal-confirm');
confirmBtn.addEventListener('click', () => {
if (onConfirm) onConfirm();
UIManager.closeModal();
});
// 취소 버튼
const cancelBtn = document.getElementById('modal-cancel');
if (cancelBtn) {
cancelBtn.addEventListener('click', () => {
if (onCancel) onCancel();
UIManager.closeModal();
});
}
// 배경 클릭으로 닫기
modalElement.addEventListener('click', (e) => {
if (e.target === modalElement) {
if (onCancel) onCancel();
UIManager.closeModal();
}
});
// ESC 키로 닫기
const handleEscape = (e) => {
if (e.key === 'Escape') {
if (onCancel) onCancel();
UIManager.closeModal();
document.removeEventListener('keydown', handleEscape);
}
};
document.addEventListener('keydown', handleEscape);
},
/**
* Modal 닫기
*/
closeModal: () => {
const modal = document.getElementById('modal');
if (modal) {
modal.classList.remove('show');
setTimeout(() => {
modal.remove();
}, 300);
}
},
/**
* Bottom Sheet 표시
* @param {Object} options - Bottom Sheet 옵션
* @param {string} options.content - HTML 내용
* @param {Function} options.onClose - 닫기 콜백
*/
showBottomSheet: (options) => {
const { content = '', onClose = null } = options;
// Bottom Sheet HTML 생성
const sheetHTML = `
<div class="bottom-sheet-overlay" id="bottom-sheet">
<div class="bottom-sheet">
<div class="bottom-sheet-handle"></div>
<div class="bottom-sheet-content">
${content}
</div>
</div>
</div>
`;
// DOM에 추가
const sheetContainer = document.createElement('div');
sheetContainer.innerHTML = sheetHTML;
document.body.appendChild(sheetContainer.firstElementChild);
const sheetElement = document.getElementById('bottom-sheet');
// 표시 애니메이션
setTimeout(() => {
sheetElement.classList.add('show');
}, 10);
// 배경 클릭으로 닫기
sheetElement.addEventListener('click', (e) => {
if (e.target === sheetElement) {
if (onClose) onClose();
UIManager.closeBottomSheet();
}
});
},
/**
* Bottom Sheet 닫기
*/
closeBottomSheet: () => {
const sheet = document.getElementById('bottom-sheet');
if (sheet) {
sheet.classList.remove('show');
setTimeout(() => {
sheet.remove();
}, 300);
}
},
/**
* Loading 표시
* @param {string} message - 로딩 메시지
*/
showLoading: (message = 'AI가 처리하고 있습니다...') => {
const loadingHTML = `
<div class="modal-overlay show" id="loading">
<div class="ai-loading">
<div class="spinner"></div>
<p>${message}</p>
</div>
</div>
`;
const loadingContainer = document.createElement('div');
loadingContainer.innerHTML = loadingHTML;
document.body.appendChild(loadingContainer.firstElementChild);
},
/**
* Loading 숨기기
*/
hideLoading: () => {
const loading = document.getElementById('loading');
if (loading) {
loading.remove();
}
},
/**
* Progress Bar 업데이트
* @param {number} percent - 진행률 (0-100)
*/
updateProgress: (percent) => {
const progressFill = document.querySelector('.progress-fill');
if (progressFill) {
progressFill.style.width = `${percent}%`;
}
}
};
// ============================================
// 4. Form Validator
// ============================================
const FormValidator = {
/**
* 이메일 검증
* @param {string} email - 이메일 주소
* @returns {boolean}
*/
validateEmail: (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
},
/**
* 비밀번호 검증 (8 이상, 영문+숫자 조합)
* @param {string} password - 비밀번호
* @returns {boolean}
*/
validatePassword: (password) => {
const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/;
return passwordRegex.test(password);
},
/**
* 전화번호 검증 (한국 형식)
* @param {string} phone - 전화번호
* @returns {boolean}
*/
validatePhone: (phone) => {
const phoneRegex = /^01[0-9]-?[0-9]{3,4}-?[0-9]{4}$/;
return phoneRegex.test(phone);
},
/**
* 필수 입력 검증
* @param {string} value - 입력값
* @returns {boolean}
*/
validateRequired: (value) => {
return value && value.trim().length > 0;
},
/**
* Input 요소에 에러 표시
* @param {HTMLElement} inputElement - Input 요소
* @param {string} errorMessage - 에러 메시지
*/
showError: (inputElement, errorMessage) => {
const inputGroup = inputElement.closest('.input-group');
if (inputGroup) {
inputGroup.classList.add('error');
const errorElement = inputGroup.querySelector('.input-error');
if (errorElement) {
errorElement.textContent = errorMessage;
}
}
},
/**
* Input 요소의 에러 제거
* @param {HTMLElement} inputElement - Input 요소
*/
clearError: (inputElement) => {
const inputGroup = inputElement.closest('.input-group');
if (inputGroup) {
inputGroup.classList.remove('error');
}
},
/**
* 전체 검증
* @param {HTMLFormElement} formElement - Form 요소
* @returns {boolean}
*/
validateForm: (formElement) => {
let isValid = true;
const inputs = formElement.querySelectorAll('input[required], textarea[required]');
inputs.forEach(input => {
FormValidator.clearError(input);
// 필수 입력 체크
if (!FormValidator.validateRequired(input.value)) {
FormValidator.showError(input, '필수 입력 항목입니다.');
isValid = false;
return;
}
// 타입별 검증
if (input.type === 'email' && !FormValidator.validateEmail(input.value)) {
FormValidator.showError(input, '올바른 이메일 형식이 아닙니다.');
isValid = false;
} else if (input.type === 'password' && !FormValidator.validatePassword(input.value)) {
FormValidator.showError(input, '비밀번호는 8자 이상, 영문과 숫자 조합이어야 합니다.');
isValid = false;
} else if (input.type === 'tel' && !FormValidator.validatePhone(input.value)) {
FormValidator.showError(input, '올바른 전화번호 형식이 아닙니다.');
isValid = false;
}
});
return isValid;
}
};
// ============================================
// 5. Animation Helpers
// ============================================
const AnimationHelper = {
/**
* Fade In
* @param {HTMLElement} element - 대상 요소
* @param {number} duration - 지속 시간 (ms)
*/
fadeIn: (element, duration = 300) => {
element.style.opacity = '0';
element.style.display = 'block';
let opacity = 0;
const increment = 50 / duration;
const timer = setInterval(() => {
opacity += increment;
element.style.opacity = opacity;
if (opacity >= 1) {
clearInterval(timer);
element.style.opacity = '1';
}
}, 50);
},
/**
* Fade Out
* @param {HTMLElement} element - 대상 요소
* @param {number} duration - 지속 시간 (ms)
*/
fadeOut: (element, duration = 300) => {
let opacity = 1;
const decrement = 50 / duration;
const timer = setInterval(() => {
opacity -= decrement;
element.style.opacity = opacity;
if (opacity <= 0) {
clearInterval(timer);
element.style.opacity = '0';
element.style.display = 'none';
}
}, 50);
},
/**
* Slide Down
* @param {HTMLElement} element - 대상 요소
* @param {number} duration - 지속 시간 (ms)
*/
slideDown: (element, duration = 300) => {
element.style.height = '0';
element.style.overflow = 'hidden';
element.style.display = 'block';
const targetHeight = element.scrollHeight;
let height = 0;
const increment = (targetHeight * 50) / duration;
const timer = setInterval(() => {
height += increment;
element.style.height = `${height}px`;
if (height >= targetHeight) {
clearInterval(timer);
element.style.height = 'auto';
element.style.overflow = 'visible';
}
}, 50);
}
};
// ============================================
// 6. Utility Functions
// ============================================
const Utils = {
/**
* 날짜 포맷 (YYYY.MM.DD)
* @param {Date} date - 날짜 객체
* @returns {string}
*/
formatDate: (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}.${month}.${day}`;
},
/**
* 날짜 포맷 (YYYY-MM-DD)
* @param {Date} date - 날짜 객체
* @returns {string}
*/
formatDateISO: (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
/**
* 숫자 포맷 ( 단위 콤마)
* @param {number} number - 숫자
* @returns {string}
*/
formatNumber: (number) => {
return number.toLocaleString('ko-KR');
},
/**
* 퍼센트 포맷
* @param {number} value -
* @param {number} decimals - 소수점 자릿수
* @returns {string}
*/
formatPercent: (value, decimals = 1) => {
return `${value.toFixed(decimals)}%`;
},
/**
* 디바운스 (연속 호출 방지)
* @param {Function} func - 실행할 함수
* @param {number} wait - 대기 시간 (ms)
* @returns {Function}
*/
debounce: (func, wait = 300) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
/**
* 쓰로틀 (일정 시간마다 번만 실행)
* @param {Function} func - 실행할 함수
* @param {number} limit - 제한 시간 (ms)
* @returns {Function}
*/
throttle: (func, limit = 300) => {
let inThrottle;
return function executedFunction(...args) {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
},
/**
* 랜덤 ID 생성
* @returns {string}
*/
generateId: () => {
return `id-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
},
/**
* 요소가 뷰포트에 보이는지 확인
* @param {HTMLElement} element - 대상 요소
* @returns {boolean}
*/
isInViewport: (element) => {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
};
// ============================================
// 7. 예제 데이터
// ============================================
const SampleData = {
// 사용자 정보
user: {
name: '정우진',
businessName: '우진이네 고깃집',
businessType: '한식당',
location: '서울 강남구',
email: 'woojin@example.com',
phone: '010-1234-5678'
},
// 이벤트 예제
event: {
id: 'evt-001',
name: '우진이네 여름 특별 할인 이벤트',
purpose: '신규 고객 유치',
startDate: '2025.06.01',
endDate: '2025.06.30',
discountRate: 20,
target: '20-30대 직장인',
status: 'in_progress'
},
// 이벤트 목록
events: [
{
id: 'evt-001',
name: '우진이네 여름 특별 할인 이벤트',
status: 'in_progress',
startDate: '2025.06.01',
participants: 127
},
{
id: 'evt-002',
name: '신메뉴 출시 기념 이벤트',
status: 'in_progress',
startDate: '2025.05.15',
participants: 89
},
{
id: 'evt-003',
name: '재방문 고객 감사 이벤트',
status: 'completed',
startDate: '2025.04.01',
participants: 254
}
],
// 통계 데이터
analytics: {
totalEvents: 5,
activeEvents: 2,
completedEvents: 3,
totalParticipants: 127,
salesIncrease: 23,
conversionRate: 15.4
},
// AI 생성 콘텐츠
aiContent: {
plan: '여름 시즌 신규 고객 유치를 위한 20% 할인 이벤트입니다. 점심 시간대 직장인을 타겟으로 하며, SNS 공유 시 추가 쿠폰을 제공합니다.',
images: [
{ id: 1, url: 'https://via.placeholder.com/400x300?text=여름+특별+이미지1', selected: true },
{ id: 2, url: 'https://via.placeholder.com/400x300?text=여름+특별+이미지2', selected: false },
{ id: 3, url: 'https://via.placeholder.com/400x300?text=여름+특별+이미지3', selected: false }
],
improvement: '참여율 향상을 위해 쿠폰 사용 기한을 1주일에서 2주일로 연장하고, SNS 공유 혜택을 10%에서 15%로 상향 조정하는 것을 제안합니다.'
}
};
// ============================================
// 8. 초기화
// ============================================
document.addEventListener('DOMContentLoaded', () => {
// Back 버튼 초기화
const backButtons = document.querySelectorAll('.back-button');
backButtons.forEach(btn => {
btn.addEventListener('click', () => {
NavManager.back();
});
});
// 예제 데이터 로드 (개발용)
if (!AppState.loadUser()) {
AppState.saveUser(SampleData.user);
}
});
// Export for use in other files
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
NavManager,
AppState,
UIManager,
FormValidator,
AnimationHelper,
Utils,
SampleData
};
}

File diff suppressed because it is too large Load Diff