mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 09:06:24 +00:00
add gappa's
This commit is contained in:
parent
1ccdc9341f
commit
3ef35ec099
1184
design-gappa/style-guide.md
Normal file
1184
design-gappa/style-guide.md
Normal file
File diff suppressed because it is too large
Load Diff
351
design-gappa/uiux/prototype/01-로그인.html
Normal file
351
design-gappa/uiux/prototype/01-로그인.html
Normal file
@ -0,0 +1,351 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>로그인 - 회의록 작성 및 공유 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
/* 페이지 전용 스타일 */
|
||||
body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #00D9B1 0%, #6366F1 100%);
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: var(--spacing-10);
|
||||
box-shadow: var(--shadow-lg);
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
margin: var(--spacing-4);
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 auto var(--spacing-4);
|
||||
background-color: var(--color-primary-main);
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 32px;
|
||||
color: var(--color-white);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: var(--font-size-h2);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.login-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
#loginForm {
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: var(--spacing-5);
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--spacing-5);
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
.checkbox-wrapper input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--color-primary-main);
|
||||
}
|
||||
|
||||
.checkbox-wrapper label {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forgot-password {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-primary-main);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.forgot-password:hover {
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
text-align: center;
|
||||
padding-top: var(--spacing-6);
|
||||
border-top: 1px solid var(--color-gray-200);
|
||||
}
|
||||
|
||||
.login-footer-text {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
.login-footer a {
|
||||
color: var(--color-primary-main);
|
||||
font-weight: var(--font-weight-medium);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.login-footer a:hover {
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
/* 예시 크리덴셜 표시 */
|
||||
.credential-hint {
|
||||
background-color: var(--color-gray-50);
|
||||
border: 1px dashed var(--color-gray-300);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-5);
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
.credential-hint-title {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-700);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.credential-hint code {
|
||||
background-color: var(--color-gray-200);
|
||||
padding: 2px 6px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: var(--font-size-caption);
|
||||
}
|
||||
|
||||
/* 반응형 */
|
||||
@media (max-width: 767px) {
|
||||
.login-card {
|
||||
padding: var(--spacing-6);
|
||||
}
|
||||
|
||||
.login-title {
|
||||
font-size: var(--font-size-h3);
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-3);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<!-- 헤더 -->
|
||||
<div class="login-header">
|
||||
<div class="login-logo">M</div>
|
||||
<h1 class="login-title">회의록 서비스</h1>
|
||||
<p class="login-subtitle">스마트한 협업의 시작</p>
|
||||
</div>
|
||||
|
||||
<!-- 예시 크리덴셜 (프로토타입용) -->
|
||||
<div class="credential-hint">
|
||||
<div class="credential-hint-title">📝 테스트 계정</div>
|
||||
<div>이메일: <code>test@example.com</code></div>
|
||||
<div>비밀번호: <code>password123</code></div>
|
||||
</div>
|
||||
|
||||
<!-- 로그인 폼 -->
|
||||
<form id="loginForm">
|
||||
<div class="form-group">
|
||||
<label for="email" class="form-label">이메일</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
class="form-input"
|
||||
placeholder="example@company.com"
|
||||
required
|
||||
autocomplete="email"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="form-label">비밀번호</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
class="form-input"
|
||||
placeholder="비밀번호를 입력하세요"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-footer">
|
||||
<div class="checkbox-wrapper">
|
||||
<input type="checkbox" id="rememberMe">
|
||||
<label for="rememberMe">로그인 상태 유지</label>
|
||||
</div>
|
||||
<a href="#" class="forgot-password">비밀번호 찾기</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%;">
|
||||
로그인
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- 푸터 -->
|
||||
<div class="login-footer">
|
||||
<p class="login-footer-text">
|
||||
아직 계정이 없으신가요? <a href="#">회원가입</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
// 로그인 폼 처리
|
||||
const loginForm = document.getElementById('loginForm');
|
||||
const emailInput = document.getElementById('email');
|
||||
const passwordInput = document.getElementById('password');
|
||||
const rememberMeCheckbox = document.getElementById('rememberMe');
|
||||
|
||||
// 페이지 로드 시 저장된 이메일 불러오기
|
||||
MeetingApp.ready(() => {
|
||||
const savedEmail = MeetingApp.Storage.get('savedEmail');
|
||||
if (savedEmail) {
|
||||
emailInput.value = savedEmail;
|
||||
rememberMeCheckbox.checked = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 폼 제출 핸들러
|
||||
loginForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// 에러 초기화
|
||||
MeetingApp.Validator.clearError(emailInput);
|
||||
MeetingApp.Validator.clearError(passwordInput);
|
||||
|
||||
const email = emailInput.value.trim();
|
||||
const password = passwordInput.value.trim();
|
||||
|
||||
// 유효성 검사
|
||||
let isValid = true;
|
||||
|
||||
if (!MeetingApp.Validator.required(email)) {
|
||||
MeetingApp.Validator.showError(emailInput, '이메일을 입력해주세요.');
|
||||
isValid = false;
|
||||
} else if (!MeetingApp.Validator.isEmail(email)) {
|
||||
MeetingApp.Validator.showError(emailInput, '올바른 이메일 형식이 아닙니다.');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!MeetingApp.Validator.required(password)) {
|
||||
MeetingApp.Validator.showError(passwordInput, '비밀번호를 입력해주세요.');
|
||||
isValid = false;
|
||||
} else if (!MeetingApp.Validator.minLength(password, 6)) {
|
||||
MeetingApp.Validator.showError(passwordInput, '비밀번호는 최소 6자 이상이어야 합니다.');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!isValid) return;
|
||||
|
||||
// 로딩 표시
|
||||
const submitButton = loginForm.querySelector('button[type="submit"]');
|
||||
const originalText = submitButton.textContent;
|
||||
submitButton.disabled = true;
|
||||
submitButton.innerHTML = '<div class="spinner spinner-sm" style="border-color: white; border-top-color: transparent;"></div>';
|
||||
|
||||
try {
|
||||
// API 호출 시뮬레이션
|
||||
await MeetingApp.API.post('/api/auth/login', { email, password });
|
||||
|
||||
// 로그인 성공 시뮬레이션 (테스트 계정 체크)
|
||||
if (email === 'test@example.com' && password === 'password123') {
|
||||
// 사용자 정보 저장
|
||||
MeetingApp.Storage.set('currentUser', {
|
||||
id: 'user-001',
|
||||
name: '김민준',
|
||||
email: email,
|
||||
avatar: 'https://ui-avatars.com/api/?name=김민준&background=00D9B1&color=fff',
|
||||
role: 'user'
|
||||
});
|
||||
|
||||
// 로그인 상태 유지 체크
|
||||
if (rememberMeCheckbox.checked) {
|
||||
MeetingApp.Storage.set('savedEmail', email);
|
||||
MeetingApp.Storage.set('rememberMe', true);
|
||||
} else {
|
||||
MeetingApp.Storage.remove('savedEmail');
|
||||
MeetingApp.Storage.remove('rememberMe');
|
||||
}
|
||||
|
||||
// JWT 토큰 시뮬레이션
|
||||
MeetingApp.Storage.set('authToken', 'mock-jwt-token-' + Date.now());
|
||||
|
||||
// 성공 토스트
|
||||
MeetingApp.Toast.success('로그인에 성공했습니다!');
|
||||
|
||||
// 대시보드로 이동
|
||||
setTimeout(() => {
|
||||
window.location.href = '02-대시보드.html';
|
||||
}, 1000);
|
||||
|
||||
} else {
|
||||
// 로그인 실패
|
||||
MeetingApp.Toast.error('이메일 또는 비밀번호가 올바르지 않습니다.');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = originalText;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
MeetingApp.Toast.error('로그인 중 오류가 발생했습니다. 다시 시도해주세요.');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = originalText;
|
||||
}
|
||||
});
|
||||
|
||||
// 비밀번호 찾기 (프로토타입용)
|
||||
document.querySelector('.forgot-password').addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
MeetingApp.Toast.info('비밀번호 찾기 기능은 준비 중입니다.');
|
||||
});
|
||||
|
||||
// 회원가입 (프로토타입용)
|
||||
document.querySelector('.login-footer a').addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
MeetingApp.Toast.info('회원가입 기능은 준비 중입니다.');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
687
design-gappa/uiux/prototype/02-대시보드.html
Normal file
687
design-gappa/uiux/prototype/02-대시보드.html
Normal file
@ -0,0 +1,687 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>대시보드 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
/* 레이아웃 */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.dashboard-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: var(--z-sticky);
|
||||
background-color: var(--color-white);
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
padding: 0 var(--spacing-6);
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: var(--color-primary-main);
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-white);
|
||||
font-weight: var(--font-weight-bold);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.user-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--spacing-2);
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-md);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.user-button:hover {
|
||||
background-color: var(--color-gray-100);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--color-primary-main);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-white);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
right: 0;
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-md);
|
||||
min-width: 200px;
|
||||
z-index: var(--z-dropdown);
|
||||
}
|
||||
|
||||
.user-dropdown.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: block;
|
||||
padding: var(--spacing-3) var(--spacing-4);
|
||||
color: var(--color-gray-700);
|
||||
text-decoration: none;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.dropdown-item:hover {
|
||||
background-color: var(--color-gray-50);
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
height: 1px;
|
||||
background-color: var(--color-gray-200);
|
||||
margin: var(--spacing-2) 0;
|
||||
}
|
||||
|
||||
/* Layout */
|
||||
.dashboard-layout {
|
||||
display: flex;
|
||||
min-height: calc(100vh - 64px);
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
width: 240px;
|
||||
background-color: var(--color-gray-50);
|
||||
border-right: 1px solid var(--color-gray-200);
|
||||
padding: var(--spacing-6) 0;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-3);
|
||||
padding: var(--spacing-3) var(--spacing-6);
|
||||
color: var(--color-gray-700);
|
||||
text-decoration: none;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background-color: var(--color-gray-200);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background-color: rgba(0, 217, 177, 0.1);
|
||||
color: var(--color-primary-main);
|
||||
font-weight: var(--font-weight-medium);
|
||||
border-right: 3px solid var(--color-primary-main);
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
padding: var(--spacing-8);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.welcome-section {
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.welcome-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
/* Stats Grid */
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
padding: var(--spacing-6);
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.stat-icon.primary { background-color: rgba(0, 217, 177, 0.1); color: var(--color-primary-main); }
|
||||
.stat-icon.warning { background-color: rgba(245, 158, 11, 0.1); color: var(--color-warning-main); }
|
||||
.stat-icon.success { background-color: rgba(16, 185, 129, 0.1); color: var(--color-success-main); }
|
||||
|
||||
.stat-label {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-500);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: var(--font-size-h2);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
/* Section */
|
||||
.section {
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: var(--font-size-h3);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
.view-all-link {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-primary-main);
|
||||
text-decoration: none;
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.view-all-link:hover {
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
/* Meeting Card */
|
||||
.meeting-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
.meeting-card {
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-5);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
|
||||
.meeting-card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.meeting-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
.meeting-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.meeting-meta {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-500);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
/* Todo Card */
|
||||
.todo-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-3);
|
||||
}
|
||||
|
||||
.todo-item {
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.todo-item:hover {
|
||||
box-shadow: var(--shadow-sm);
|
||||
border-color: var(--color-primary-main);
|
||||
}
|
||||
|
||||
.todo-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.todo-title {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.todo-meta {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
.todo-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
|
||||
.dday {
|
||||
font-size: var(--font-size-body-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.dday.urgent { color: var(--color-error-main); }
|
||||
.dday.warning { color: var(--color-warning-main); }
|
||||
.dday.normal { color: var(--color-gray-500); }
|
||||
|
||||
/* Bottom Navigation (Mobile) */
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--color-white);
|
||||
border-top: 1px solid var(--color-gray-200);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: var(--spacing-2) 0;
|
||||
z-index: var(--z-sticky);
|
||||
}
|
||||
|
||||
.bottom-nav-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-1);
|
||||
padding: var(--spacing-2);
|
||||
color: var(--color-gray-500);
|
||||
text-decoration: none;
|
||||
font-size: var(--font-size-caption);
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.bottom-nav-item.active {
|
||||
color: var(--color-primary-main);
|
||||
}
|
||||
|
||||
.bottom-nav-icon {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1023px) {
|
||||
.sidebar { display: none; }
|
||||
.main-content { padding-bottom: 80px; }
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.bottom-nav { display: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.dashboard-header { padding: 0 var(--spacing-4); }
|
||||
.service-name { display: none; }
|
||||
.main-content { padding: var(--spacing-4); }
|
||||
.welcome-title { font-size: var(--font-size-h2); }
|
||||
.stats-grid { grid-template-columns: 1fr; }
|
||||
.meeting-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Header -->
|
||||
<header class="dashboard-header">
|
||||
<div class="header-left">
|
||||
<div class="logo">M</div>
|
||||
<span class="service-name">회의록 서비스</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="user-menu">
|
||||
<button class="user-button" id="userMenuButton">
|
||||
<div class="user-avatar" id="userAvatar">U</div>
|
||||
<span class="hide-mobile" id="userName">사용자</span>
|
||||
</button>
|
||||
<div class="user-dropdown" id="userDropdown">
|
||||
<a href="#" class="dropdown-item">내 프로필</a>
|
||||
<a href="#" class="dropdown-item">설정</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="dropdown-item" id="logoutButton">로그아웃</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Layout -->
|
||||
<div class="dashboard-layout">
|
||||
<!-- Sidebar -->
|
||||
<aside class="sidebar">
|
||||
<nav>
|
||||
<ul class="sidebar-nav">
|
||||
<li class="nav-item">
|
||||
<a href="02-대시보드.html" class="nav-link active">
|
||||
<span>📊</span> 대시보드
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link">
|
||||
<span>📅</span> 회의 목록
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="10-Todo관리.html" class="nav-link">
|
||||
<span>✅</span> Todo 관리
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="#" class="nav-link">
|
||||
<span>⚙️</span> 설정
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<!-- Welcome Section -->
|
||||
<section class="welcome-section">
|
||||
<h1 class="welcome-title" id="welcomeTitle">안녕하세요!</h1>
|
||||
<p class="welcome-subtitle" id="welcomeSubtitle">오늘의 일정을 확인하세요</p>
|
||||
</section>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<section class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon primary">📅</div>
|
||||
<div class="stat-label">예정된 회의</div>
|
||||
<div class="stat-value" id="upcomingMeetingsCount">0</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon warning">✅</div>
|
||||
<div class="stat-label">진행 중 Todo</div>
|
||||
<div class="stat-value" id="inProgressTodosCount">0</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon success">📈</div>
|
||||
<div class="stat-label">Todo 완료율</div>
|
||||
<div class="stat-value" id="todoCompletionRate">0%</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Recent Meetings -->
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">최근 회의</h2>
|
||||
<a href="#" class="view-all-link">전체 보기 →</a>
|
||||
</div>
|
||||
<div class="meeting-grid" id="meetingGrid">
|
||||
<!-- Meetings will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- My Todos -->
|
||||
<section class="section">
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">할당된 Todo</h2>
|
||||
<a href="10-Todo관리.html" class="view-all-link">전체 보기 →</a>
|
||||
</div>
|
||||
<div class="todo-list" id="todoList">
|
||||
<!-- Todos will be rendered here -->
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Navigation (Mobile) -->
|
||||
<nav class="bottom-nav hide-desktop">
|
||||
<a href="02-대시보드.html" class="bottom-nav-item active">
|
||||
<div class="bottom-nav-icon">📊</div>
|
||||
<div>대시보드</div>
|
||||
</a>
|
||||
<a href="#" class="bottom-nav-item">
|
||||
<div class="bottom-nav-icon">📅</div>
|
||||
<div>회의</div>
|
||||
</a>
|
||||
<a href="10-Todo관리.html" class="bottom-nav-item">
|
||||
<div class="bottom-nav-icon">✅</div>
|
||||
<div>Todo</div>
|
||||
</a>
|
||||
<a href="#" class="bottom-nav-item">
|
||||
<div class="bottom-nav-icon">⚙️</div>
|
||||
<div>더보기</div>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<!-- FAB -->
|
||||
<button class="fab" id="fabButton" title="새 회의 예약">+</button>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
const { AppState, Storage, Toast, MeetingUtils, formatDateTime, getDday, navigateTo } = window.MeetingApp;
|
||||
|
||||
// 인증 체크
|
||||
MeetingApp.ready(() => {
|
||||
const authToken = Storage.get('authToken');
|
||||
if (!authToken) {
|
||||
window.location.href = '01-로그인.html';
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUser = Storage.get('currentUser');
|
||||
if (currentUser) {
|
||||
// 사용자 정보 표시
|
||||
document.getElementById('userName').textContent = currentUser.name;
|
||||
document.getElementById('userAvatar').textContent = currentUser.name.charAt(0);
|
||||
document.getElementById('welcomeTitle').textContent = `안녕하세요, ${currentUser.name}님!`;
|
||||
|
||||
// AppState 업데이트
|
||||
AppState.currentUser = currentUser;
|
||||
}
|
||||
|
||||
// 데이터 로드 및 렌더링
|
||||
loadDashboardData();
|
||||
renderMeetings();
|
||||
renderTodos();
|
||||
});
|
||||
|
||||
// 대시보드 통계 로드
|
||||
function loadDashboardData() {
|
||||
const meetings = Storage.get('meetings', []);
|
||||
const todos = Storage.get('todos', []);
|
||||
|
||||
// 예정된 회의 수
|
||||
const upcomingMeetings = meetings.filter(m => m.status === 'scheduled').length;
|
||||
document.getElementById('upcomingMeetingsCount').textContent = upcomingMeetings;
|
||||
|
||||
// 진행 중 Todo 수
|
||||
const inProgressTodos = todos.filter(t => t.status === 'in_progress').length;
|
||||
document.getElementById('inProgressTodosCount').textContent = inProgressTodos;
|
||||
|
||||
// Todo 완료율
|
||||
const completedTodos = todos.filter(t => t.status === 'done').length;
|
||||
const completionRate = todos.length > 0 ? Math.round((completedTodos / todos.length) * 100) : 0;
|
||||
document.getElementById('todoCompletionRate').textContent = `${completionRate}%`;
|
||||
}
|
||||
|
||||
// 회의 목록 렌더링
|
||||
function renderMeetings() {
|
||||
const meetings = Storage.get('meetings', []).slice(0, 3);
|
||||
const meetingGrid = document.getElementById('meetingGrid');
|
||||
|
||||
if (meetings.length === 0) {
|
||||
meetingGrid.innerHTML = '<p style="color: var(--color-gray-500);">아직 등록된 회의가 없습니다.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
meetingGrid.innerHTML = meetings.map(meeting => `
|
||||
<div class="meeting-card" onclick="navigateTo('05-회의진행.html')">
|
||||
<div class="meeting-header">
|
||||
<div>
|
||||
<div class="meeting-title">${meeting.title}</div>
|
||||
<div class="meeting-meta">📅 ${formatDateTime(meeting.date)}</div>
|
||||
<div class="meeting-meta">📍 ${meeting.location}</div>
|
||||
</div>
|
||||
<span class="badge ${MeetingUtils.getStatusClass(meeting.status)}">
|
||||
${MeetingUtils.getStatusLabel(meeting.status)}
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">
|
||||
${meeting.description}
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Todo 목록 렌더링
|
||||
function renderTodos() {
|
||||
const todos = Storage.get('todos', []).filter(t => t.status !== 'done').slice(0, 5);
|
||||
const todoList = document.getElementById('todoList');
|
||||
|
||||
if (todos.length === 0) {
|
||||
todoList.innerHTML = '<p style="color: var(--color-gray-500);">할당된 Todo가 없습니다.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
todoList.innerHTML = todos.map(todo => {
|
||||
const dday = getDday(todo.dueDate);
|
||||
const ddayClass = dday.includes('지남') ? 'urgent' : (dday === '오늘' ? 'warning' : 'normal');
|
||||
|
||||
return `
|
||||
<div class="todo-item" onclick="navigateTo('10-Todo관리.html')">
|
||||
<div class="todo-left">
|
||||
<div class="todo-title">${todo.title}</div>
|
||||
<div class="todo-meta">담당: ${todo.assignee}</div>
|
||||
</div>
|
||||
<div class="todo-right">
|
||||
<div class="dday ${ddayClass}">${dday}</div>
|
||||
<span class="badge badge-${todo.priority === 'high' ? 'error' : 'neutral'}">
|
||||
${MeetingUtils.getPriorityLabel(todo.priority)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// 사용자 메뉴 토글
|
||||
const userMenuButton = document.getElementById('userMenuButton');
|
||||
const userDropdown = document.getElementById('userDropdown');
|
||||
|
||||
userMenuButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
userDropdown.classList.toggle('show');
|
||||
});
|
||||
|
||||
document.addEventListener('click', () => {
|
||||
userDropdown.classList.remove('show');
|
||||
});
|
||||
|
||||
// 로그아웃
|
||||
document.getElementById('logoutButton').addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
Storage.remove('authToken');
|
||||
Storage.remove('currentUser');
|
||||
Toast.success('로그아웃 되었습니다.');
|
||||
setTimeout(() => {
|
||||
window.location.href = '01-로그인.html';
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// FAB 버튼
|
||||
document.getElementById('fabButton').addEventListener('click', () => {
|
||||
window.location.href = '03-회의예약.html';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
133
design-gappa/uiux/prototype/03-회의예약.html
Normal file
133
design-gappa/uiux/prototype/03-회의예약.html
Normal file
@ -0,0 +1,133 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>회의 예약 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
}
|
||||
.page-header {
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.page-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
.form-container {
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-8);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
margin-top: var(--spacing-6);
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
.form-container { padding: var(--spacing-5); }
|
||||
.button-group { flex-direction: column; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">회의 예약</h1>
|
||||
<p class="page-subtitle">새로운 회의를 예약하고 참석자를 초대하세요</p>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<form id="meetingForm">
|
||||
<div class="form-group">
|
||||
<label for="title" class="form-label">회의 제목 *</label>
|
||||
<input type="text" id="title" class="form-input" placeholder="예: 2025년 1분기 기획 회의" required maxlength="100">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="date" class="form-label">날짜 *</label>
|
||||
<input type="date" id="date" class="form-input" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="time" class="form-label">시간 *</label>
|
||||
<input type="time" id="time" class="form-input" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="location" class="form-label">장소</label>
|
||||
<input type="text" id="location" class="form-input" placeholder="예: 본사 2층 대회의실" maxlength="200">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="attendees" class="form-label">참석자 (이메일, 쉼표로 구분) *</label>
|
||||
<input type="text" id="attendees" class="form-input" placeholder="예: user1@example.com, user2@example.com" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description" class="form-label">회의 설명</label>
|
||||
<textarea id="description" class="form-textarea" placeholder="회의 목적과 안건을 간략히 작성하세요"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button type="submit" class="btn btn-primary" style="flex: 1;">회의 예약하기</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="history.back()">취소</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
const form = document.getElementById('meetingForm');
|
||||
|
||||
// 최소 날짜를 오늘로 설정
|
||||
document.getElementById('date').min = new Date().toISOString().split('T')[0];
|
||||
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const title = document.getElementById('title').value.trim();
|
||||
const date = document.getElementById('date').value;
|
||||
const time = document.getElementById('time').value;
|
||||
const location = document.getElementById('location').value.trim();
|
||||
const attendees = document.getElementById('attendees').value.trim();
|
||||
const description = document.getElementById('description').value.trim();
|
||||
|
||||
// 새 회의 생성
|
||||
const newMeeting = {
|
||||
id: 'm-' + Date.now(),
|
||||
title,
|
||||
date: `${date} ${time}`,
|
||||
location: location || '미정',
|
||||
status: 'scheduled',
|
||||
attendees: attendees.split(',').map(email => email.trim()),
|
||||
description: description || ''
|
||||
};
|
||||
|
||||
// 저장
|
||||
const meetings = MeetingApp.Storage.get('meetings', []);
|
||||
meetings.unshift(newMeeting);
|
||||
MeetingApp.Storage.set('meetings', meetings);
|
||||
|
||||
MeetingApp.Toast.success('회의가 예약되었습니다!');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = '04-템플릿선택.html?meetingId=' + newMeeting.id;
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
235
design-gappa/uiux/prototype/04-템플릿선택.html
Normal file
235
design-gappa/uiux/prototype/04-템플릿선택.html
Normal file
@ -0,0 +1,235 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>템플릿 선택 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 1024px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
}
|
||||
.page-header {
|
||||
margin-bottom: var(--spacing-8);
|
||||
text-align: center;
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.page-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
.template-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.template-card {
|
||||
background: var(--color-white);
|
||||
border: 2px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
position: relative;
|
||||
}
|
||||
.template-card:hover {
|
||||
border-color: var(--color-primary-main);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
.template-card.selected {
|
||||
border-color: var(--color-primary-main);
|
||||
border-width: 3px;
|
||||
background-color: rgba(0, 217, 177, 0.05);
|
||||
}
|
||||
.template-card.selected::after {
|
||||
content: '✓';
|
||||
position: absolute;
|
||||
top: var(--spacing-3);
|
||||
right: var(--spacing-3);
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: var(--radius-full);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
.template-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: var(--spacing-4);
|
||||
text-align: center;
|
||||
}
|
||||
.template-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.template-description {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
line-height: var(--line-height-relaxed);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.template-sections {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-1);
|
||||
}
|
||||
.section-tag {
|
||||
font-size: var(--font-size-caption);
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
background-color: var(--color-gray-100);
|
||||
color: var(--color-gray-600);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
justify-content: center;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
.template-grid { grid-template-columns: 1fr; }
|
||||
.action-buttons { flex-direction: column; }
|
||||
.action-buttons .btn { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">회의록 템플릿 선택</h1>
|
||||
<p class="page-subtitle">회의 유형에 맞는 템플릿을 선택하여 효율적으로 회의록을 작성하세요</p>
|
||||
</div>
|
||||
|
||||
<div class="template-grid">
|
||||
<!-- 일반 회의 템플릿 -->
|
||||
<div class="template-card" data-template="general">
|
||||
<div class="template-icon">📋</div>
|
||||
<h3 class="template-title">일반 회의</h3>
|
||||
<p class="template-description">
|
||||
가장 기본적인 회의록 형식입니다. 모든 유형의 회의에 적합합니다.
|
||||
</p>
|
||||
<div class="template-sections">
|
||||
<span class="section-tag">참석자</span>
|
||||
<span class="section-tag">안건</span>
|
||||
<span class="section-tag">논의 내용</span>
|
||||
<span class="section-tag">결정 사항</span>
|
||||
<span class="section-tag">Todo</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 스크럼 회의 템플릿 -->
|
||||
<div class="template-card" data-template="scrum">
|
||||
<div class="template-icon">🏃</div>
|
||||
<h3 class="template-title">스크럼 회의</h3>
|
||||
<p class="template-description">
|
||||
데일리 스탠드업이나 스프린트 회의에 최적화된 템플릿입니다.
|
||||
</p>
|
||||
<div class="template-sections">
|
||||
<span class="section-tag">어제 한 일</span>
|
||||
<span class="section-tag">오늘 할 일</span>
|
||||
<span class="section-tag">이슈/블로커</span>
|
||||
<span class="section-tag">다음 스프린트</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 프로젝트 킥오프 템플릿 -->
|
||||
<div class="template-card" data-template="kickoff">
|
||||
<div class="template-icon">🚀</div>
|
||||
<h3 class="template-title">프로젝트 킥오프</h3>
|
||||
<p class="template-description">
|
||||
새 프로젝트 시작 시 필요한 모든 정보를 담는 템플릿입니다.
|
||||
</p>
|
||||
<div class="template-sections">
|
||||
<span class="section-tag">프로젝트 개요</span>
|
||||
<span class="section-tag">목표</span>
|
||||
<span class="section-tag">일정</span>
|
||||
<span class="section-tag">역할 분담</span>
|
||||
<span class="section-tag">리스크</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 주간 회의 템플릿 -->
|
||||
<div class="template-card" data-template="weekly">
|
||||
<div class="template-icon">📅</div>
|
||||
<h3 class="template-title">주간 회의</h3>
|
||||
<p class="template-description">
|
||||
매주 반복되는 정기 회의에 적합한 템플릿입니다.
|
||||
</p>
|
||||
<div class="template-sections">
|
||||
<span class="section-tag">주간 실적</span>
|
||||
<span class="section-tag">주요 이슈</span>
|
||||
<span class="section-tag">다음 주 계획</span>
|
||||
<span class="section-tag">공지사항</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="btn btn-secondary" onclick="history.back()">이전으로</button>
|
||||
<button type="button" class="btn btn-primary" id="startMeetingBtn" disabled>
|
||||
회의 시작하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
let selectedTemplate = null;
|
||||
const startBtn = document.getElementById('startMeetingBtn');
|
||||
const templateCards = document.querySelectorAll('.template-card');
|
||||
|
||||
templateCards.forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
// 기존 선택 해제
|
||||
templateCards.forEach(c => c.classList.remove('selected'));
|
||||
|
||||
// 새로운 선택
|
||||
card.classList.add('selected');
|
||||
selectedTemplate = card.getAttribute('data-template');
|
||||
startBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', () => {
|
||||
if (!selectedTemplate) {
|
||||
MeetingApp.Toast.warning('템플릿을 선택해주세요');
|
||||
return;
|
||||
}
|
||||
|
||||
// URL에서 meetingId 가져오기
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const meetingId = urlParams.get('meetingId');
|
||||
|
||||
// 선택한 템플릿 저장
|
||||
MeetingApp.Storage.set('selectedTemplate', {
|
||||
meetingId: meetingId,
|
||||
template: selectedTemplate,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
MeetingApp.Toast.success('템플릿이 선택되었습니다');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = '05-회의진행.html?meetingId=' + meetingId;
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 페이지 로드 시 일반 회의 템플릿 기본 선택 (선택적)
|
||||
// templateCards[0].click();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
631
design-gappa/uiux/prototype/05-회의진행.html
Normal file
631
design-gappa/uiux/prototype/05-회의진행.html
Normal file
@ -0,0 +1,631 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>회의 진행 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: var(--color-gray-50);
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.meeting-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
.meeting-header {
|
||||
background: var(--color-white);
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
padding: var(--spacing-4) var(--spacing-6);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.meeting-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
.meeting-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.recording-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
background-color: var(--color-error-light);
|
||||
color: var(--color-error-dark);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.recording-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: var(--color-error-main);
|
||||
border-radius: var(--radius-full);
|
||||
animation: blink 1.5s ease-in-out infinite;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.3; }
|
||||
}
|
||||
.meeting-body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.editor-panel {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--color-white);
|
||||
border-right: 1px solid var(--color-gray-200);
|
||||
}
|
||||
.editor-toolbar {
|
||||
padding: var(--spacing-3) var(--spacing-4);
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
display: flex;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
.editor-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: var(--spacing-6);
|
||||
}
|
||||
.editor-textarea {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
font-size: var(--font-size-body);
|
||||
line-height: var(--line-height-relaxed);
|
||||
resize: none;
|
||||
outline: none;
|
||||
}
|
||||
.side-panel {
|
||||
width: 360px;
|
||||
background: var(--color-white);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-left: 1px solid var(--color-gray-200);
|
||||
}
|
||||
.side-panel-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
}
|
||||
.side-tab {
|
||||
flex: 1;
|
||||
padding: var(--spacing-3) var(--spacing-4);
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 3px solid transparent;
|
||||
font-size: var(--font-size-body-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-600);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
.side-tab.active {
|
||||
color: var(--color-primary-main);
|
||||
border-bottom-color: var(--color-primary-main);
|
||||
}
|
||||
.side-panel-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: var(--spacing-4);
|
||||
}
|
||||
.attendee-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-3);
|
||||
padding: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-2);
|
||||
background: var(--color-gray-50);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.attendee-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.attendee-info {
|
||||
flex: 1;
|
||||
}
|
||||
.attendee-name {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.attendee-status {
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
.ai-suggestion {
|
||||
background-color: var(--color-gray-50);
|
||||
border: 1px dashed var(--color-primary-main);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.ai-suggestion-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
margin-bottom: var(--spacing-3);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-primary-main);
|
||||
}
|
||||
.ai-suggestion-text {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
line-height: var(--line-height-relaxed);
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
.ai-actions {
|
||||
display: flex;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.side-panel {
|
||||
position: fixed;
|
||||
right: -360px;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
z-index: var(--z-sticky);
|
||||
box-shadow: var(--shadow-lg);
|
||||
transition: right var(--transition-base);
|
||||
}
|
||||
.side-panel.open {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="meeting-container">
|
||||
<!-- 헤더 -->
|
||||
<div class="meeting-header">
|
||||
<div class="meeting-info">
|
||||
<h1 class="meeting-title" id="meetingTitle">회의 진행 중</h1>
|
||||
<div class="recording-indicator">
|
||||
<div class="recording-dot"></div>
|
||||
<span id="recordingTime">00:00</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: var(--spacing-3);">
|
||||
<button class="btn btn-text btn-icon hide-desktop" id="toggleSidePanel">☰</button>
|
||||
<button class="btn btn-secondary" onclick="if(confirm('회의를 종료하시겠습니까?')) window.location.href='06-검증완료.html'">
|
||||
회의 종료
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 본문 -->
|
||||
<div class="meeting-body">
|
||||
<!-- 에디터 패널 -->
|
||||
<div class="editor-panel">
|
||||
<div class="editor-toolbar">
|
||||
<button class="btn btn-text btn-icon-sm">B</button>
|
||||
<button class="btn btn-text btn-icon-sm">I</button>
|
||||
<button class="btn btn-text btn-icon-sm">U</button>
|
||||
<button class="btn btn-text btn-icon-sm">📝</button>
|
||||
<button class="btn btn-text btn-icon-sm">🔗</button>
|
||||
</div>
|
||||
<div class="editor-content">
|
||||
<textarea class="editor-textarea" id="meetingContent" placeholder="회의 내용을 작성하거나 AI가 자동으로 작성합니다...
|
||||
|
||||
# 참석자
|
||||
- 김민준
|
||||
- 박서연
|
||||
- 이준호
|
||||
|
||||
# 안건
|
||||
1. 신규 기능 개발 일정 논의
|
||||
2. 예산 편성 검토
|
||||
|
||||
# 논의 내용
|
||||
|
||||
"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 사이드 패널 -->
|
||||
<div class="side-panel" id="sidePanel">
|
||||
<div class="side-panel-tabs">
|
||||
<button class="side-tab active" data-tab="attendees">참석자</button>
|
||||
<button class="side-tab" data-tab="ai">AI 제안</button>
|
||||
<button class="side-tab" data-tab="related">관련 자료</button>
|
||||
</div>
|
||||
|
||||
<div class="side-panel-content">
|
||||
<!-- 참석자 탭 -->
|
||||
<div class="tab-content" data-content="attendees">
|
||||
<h3 style="margin-bottom: var(--spacing-4); font-size: var(--font-size-h4);">참석자 (3명)</h3>
|
||||
<div class="attendee-item">
|
||||
<div class="attendee-avatar">김</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">김민준</div>
|
||||
<div class="attendee-status">발언 중 ✍️</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attendee-item">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-secondary-main);">박</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">박서연</div>
|
||||
<div class="attendee-status">온라인</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attendee-item">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-info-main);">이</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">이준호</div>
|
||||
<div class="attendee-status">온라인</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI 제안 탭 -->
|
||||
<div class="tab-content" data-content="ai" style="display: none;">
|
||||
<h3 style="margin-bottom: var(--spacing-4); font-size: var(--font-size-h4);">AI 제안</h3>
|
||||
|
||||
<div class="ai-suggestion">
|
||||
<div class="ai-suggestion-header">
|
||||
✨ 회의록 요약 제안
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<strong style="color: var(--color-gray-900);">현재까지 논의된 핵심 내용:</strong><br><br>
|
||||
|
||||
1. <strong>신규 기능 개발 일정</strong><br>
|
||||
• 3월 말 완료를 목표로 MVP(Minimum Viable Product) 개발 착수<br>
|
||||
• 주요 마일스톤: 3/10 UI 완성, 3/20 AI 통합, 3/30 베타 테스트<br>
|
||||
• 이전 회의(2024-10-15) 대비 일정 2주 단축 결정<br><br>
|
||||
|
||||
2. <strong>API 설계 및 개발 담당</strong><br>
|
||||
• 이준호님이 RESTful API 설계 총괄 담당<br>
|
||||
• OpenAPI 3.0 명세서 작성 우선 진행<br>
|
||||
• 기존 레거시 시스템과의 연동 인터페이스 고려 필요<br><br>
|
||||
|
||||
3. <strong>예산 편성 확정</strong><br>
|
||||
• 총 5천만원 확정 (개발비 3천, 인프라 1.5천, 예비비 5백)<br>
|
||||
• 클라우드 비용 절감을 위해 스팟 인스턴스 활용 검토<br><br>
|
||||
|
||||
<div style="margin-top: var(--spacing-3); padding-top: var(--spacing-3); border-top: 1px dashed var(--color-gray-300); font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
📎 참고자료:<br>
|
||||
• <a href="#" onclick="window.open('08-최종확정.html', '_blank'); return false;" style="color: var(--color-primary-main);">2024년 4분기 제품 기획 회의 (2024-10-15)</a><br>
|
||||
• <a href="#" onclick="window.open('08-최종확정.html', '_blank'); return false;" style="color: var(--color-primary-main);">API 설계 리뷰 회의 (2024-09-28)</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-actions">
|
||||
<button class="btn btn-primary btn-sm">회의록에 적용</button>
|
||||
<button class="btn btn-text btn-sm">수정</button>
|
||||
<button class="btn btn-text btn-sm">무시</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-suggestion">
|
||||
<div class="ai-suggestion-header">
|
||||
📋 액션 아이템(Todo) 자동 추출
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<strong style="color: var(--color-gray-900);">발언 내용에서 5개의 실행 항목을 감지했습니다:</strong><br><br>
|
||||
|
||||
<div style="background: var(--color-gray-50); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-2);">
|
||||
<strong>1. API 명세서 작성</strong><br>
|
||||
<span style="font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
• 담당: 이준호 (발언: "제가 이번 주까지 OpenAPI 명세서 초안 작성하겠습니다")<br>
|
||||
• 마감: 2025-03-25 (3월 25일)<br>
|
||||
• 우선순위: 높음 (다른 개발 작업의 선행 조건)<br>
|
||||
• 출처: 14:23 발언
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--color-gray-50); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-2);">
|
||||
<strong>2. UI 프로토타입 완성</strong><br>
|
||||
<span style="font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
• 담당: 최유진 (발언: "피그마 작업물을 다음 주 월요일까지 마무리할게요")<br>
|
||||
• 마감: 2025-03-15 (3월 15일)<br>
|
||||
• 우선순위: 높음<br>
|
||||
• 출처: 14:31 발언<br>
|
||||
• 참고: <a href="#" onclick="window.open('08-최종확정.html', '_blank'); return false;" style="color: var(--color-primary-main);">이전 디자인 가이드 (2024-09-10)</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--color-gray-50); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-2);">
|
||||
<strong>3. 예산 편성안 최종 검토</strong><br>
|
||||
<span style="font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
• 담당: 박서연 (발언: "클라우드 비용 재산정 후 최종 검토하겠습니다")<br>
|
||||
• 마감: 2025-03-20 (3월 20일)<br>
|
||||
• 우선순위: 보통<br>
|
||||
• 출처: 14:45 발언<br>
|
||||
• ⚠️ 주의: 이전 회의에서 지연된 항목 (현재 진행률 30%)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--color-gray-50); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-2);">
|
||||
<strong>4. 레거시 시스템 연동 방안 검토</strong><br>
|
||||
<span style="font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
• 담당: 이준호, 정도현 (공동)<br>
|
||||
• 마감: 2025-03-18 (3월 18일)<br>
|
||||
• 우선순위: 높음 (기술적 리스크 요인)<br>
|
||||
• 출처: 14:52 발언
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--color-gray-50); padding: var(--spacing-3); border-radius: var(--radius-sm);">
|
||||
<strong>5. 베타 테스트 참여자 모집</strong><br>
|
||||
<span style="font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
• 담당: 김민준<br>
|
||||
• 마감: 2025-03-28 (3월 28일)<br>
|
||||
• 우선순위: 보통<br>
|
||||
• 출처: 15:03 발언<br>
|
||||
• 목표: 내부 테스터 10명 이상
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: var(--spacing-3); padding-top: var(--spacing-3); border-top: 1px dashed var(--color-gray-300); font-size: var(--font-size-caption); color: var(--color-gray-600);">
|
||||
💡 AI 분석: 총 5개 항목 중 3개가 높은 우선순위로 분류되었습니다. 마감일 기준 가장 시급한 항목은 "레거시 시스템 연동 방안 검토"입니다.
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-actions">
|
||||
<button class="btn btn-primary btn-sm">5개 Todo 일괄 생성</button>
|
||||
<button class="btn btn-text btn-sm">개별 선택</button>
|
||||
<button class="btn btn-text btn-sm">수정</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-suggestion">
|
||||
<div class="ai-suggestion-header">
|
||||
🔍 전문용어 감지 및 맥락 기반 설명
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<strong style="color: var(--color-gray-900);">"API Gateway" 전문용어가 감지되었습니다</strong><br><br>
|
||||
|
||||
<div style="background: var(--color-info-light); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-3);">
|
||||
<strong style="color: var(--color-info-dark);">💡 간단한 정의</strong><br>
|
||||
<span style="font-size: var(--font-size-body-small);">
|
||||
클라이언트와 백엔드 마이크로서비스 사이의 단일 진입점 역할을 하는 서버.
|
||||
요청 라우팅, 인증, 속도 제한, 로드 밸런싱 등을 처리합니다.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<strong style="color: var(--color-gray-900);">🔗 우리 조직 내 사용 맥락</strong><br>
|
||||
<div style="margin-top: var(--spacing-2); font-size: var(--font-size-body-small); line-height: var(--line-height-relaxed);">
|
||||
• <strong>이전 회의 논의:</strong>
|
||||
<a href="#" onclick="window.open('08-최종확정.html', '_blank'); return false;" style="color: var(--color-primary-main);">API 설계 리뷰 회의 (2024-09-28)</a>에서
|
||||
AWS API Gateway vs Kong Gateway 비교 검토 완료<br>
|
||||
|
||||
• <strong>최종 결정:</strong> AWS API Gateway 채택 결정 (서버리스 아키텍처와 통합 용이)<br>
|
||||
|
||||
• <strong>담당자:</strong> 이준호님이 API Gateway 설정 및 Lambda 통합 담당<br>
|
||||
|
||||
• <strong>관련 문서:</strong>
|
||||
<a href="#" onclick="alert('기술 문서로 이동'); return false;" style="color: var(--color-primary-main);">사내 위키 - API Gateway 구축 가이드</a><br>
|
||||
|
||||
• <strong>예상 비용:</strong> 월 약 50만원 (요청 100만건 기준)<br>
|
||||
|
||||
• <strong>보안 정책:</strong> JWT 토큰 기반 인증, Rate Limiting 500req/min 설정 예정
|
||||
</div>
|
||||
|
||||
<div style="margin-top: var(--spacing-3); padding: var(--spacing-3); background: var(--color-gray-50); border-radius: var(--radius-sm);">
|
||||
<strong style="font-size: var(--font-size-body-small);">📚 참고 자료</strong><br>
|
||||
<div style="font-size: var(--font-size-caption); color: var(--color-gray-600); margin-top: var(--spacing-2);">
|
||||
• <a href="#" onclick="window.open('08-최종확정.html', '_blank'); return false;" style="color: var(--color-primary-main);">API 설계 리뷰 회의록 (2024-09-28)</a><br>
|
||||
• <a href="#" onclick="alert('문서로 이동'); return false;" style="color: var(--color-primary-main);">기술 아키텍처 문서 v2.1</a><br>
|
||||
• <a href="#" onclick="alert('문서로 이동'); return false;" style="color: var(--color-primary-main);">AWS 비용 산정서</a><br>
|
||||
• <a href="#" onclick="window.location.href='10-Todo관리.html'; return false;" style="color: var(--color-primary-main);">관련 Todo: API Gateway 설정 (진행중 40%)</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-actions">
|
||||
<button class="btn btn-primary btn-sm">회의록에 설명 추가</button>
|
||||
<button class="btn btn-text btn-sm">수정</button>
|
||||
<button class="btn btn-text btn-sm">무시</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-suggestion">
|
||||
<div class="ai-suggestion-header">
|
||||
⚠️ 회의 효율성 개선 제안
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<strong style="color: var(--color-warning-dark);">AI가 회의 진행 패턴을 분석한 결과입니다:</strong><br><br>
|
||||
|
||||
<div style="background: var(--color-warning-light); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-2);">
|
||||
<strong>1. 예산 논의 시간 과다 (23분)</strong><br>
|
||||
<span style="font-size: var(--font-size-caption);">
|
||||
유사한 회의(2024-10-15)에서는 15분 소요. 사전 자료 공유로 8분 단축 가능.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--color-warning-light); padding: var(--spacing-3); border-radius: var(--radius-sm); margin-bottom: var(--spacing-2);">
|
||||
<strong>2. 미해결 이슈 감지</strong><br>
|
||||
<span style="font-size: var(--font-size-caption);">
|
||||
"레거시 연동 방안"이 3번 언급되었으나 구체적 결론 없음. 별도 후속 회의 권장.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--color-info-light); padding: var(--spacing-3); border-radius: var(--radius-sm);">
|
||||
<strong style="color: var(--color-info-dark);">✅ 잘 진행된 부분</strong><br>
|
||||
<span style="font-size: var(--font-size-caption);">
|
||||
명확한 담당자 지정과 마감일 설정으로 실행력 높음.
|
||||
참석자 발언 분포도 균형적 (김민준 28%, 박서연 25%, 이준호 32%, 기타 15%).
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-actions">
|
||||
<button class="btn btn-text btn-sm">다음 회의에 반영</button>
|
||||
<button class="btn btn-text btn-sm">무시</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 관련 자료 탭 -->
|
||||
<div class="tab-content" data-content="related" style="display: none;">
|
||||
<h3 style="margin-bottom: var(--spacing-4); font-size: var(--font-size-h4);">관련 자료</h3>
|
||||
|
||||
<!-- 관련 회의록 섹션 -->
|
||||
<div style="margin-bottom: var(--spacing-6);">
|
||||
<h4 style="font-size: var(--font-size-body); font-weight: var(--font-weight-semibold); color: var(--color-gray-700); margin-bottom: var(--spacing-3);">
|
||||
📄 관련 회의록 (3건)
|
||||
</h4>
|
||||
|
||||
<div class="ai-suggestion" style="cursor: pointer;" onclick="window.open('08-최종확정.html', '_blank')">
|
||||
<div class="ai-suggestion-header" style="color: var(--color-gray-900);">
|
||||
2024년 4분기 제품 기획 회의
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<div style="display: flex; gap: var(--spacing-2); margin-bottom: var(--spacing-2); font-size: var(--font-size-caption);">
|
||||
<span>2024-10-15</span> | <span style="color: var(--color-success-main);">관련도 92%</span>
|
||||
</div>
|
||||
<div>신규 회의록 서비스 MVP 개발 일정 논의. AI 기능 우선순위와 예산 확정.</div>
|
||||
<div style="margin-top: var(--spacing-2); font-size: var(--font-size-caption); color: var(--color-primary-main);">
|
||||
🔗 공통 키워드: AI 기능, 예산, 일정
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-suggestion" style="cursor: pointer;" onclick="window.open('08-최종확정.html', '_blank')">
|
||||
<div class="ai-suggestion-header" style="color: var(--color-gray-900);">
|
||||
API 설계 리뷰 회의
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<div style="display: flex; gap: var(--spacing-2); margin-bottom: var(--spacing-2); font-size: var(--font-size-caption);">
|
||||
<span>2024-09-28</span> | <span style="color: var(--color-warning-main);">관련도 78%</span>
|
||||
</div>
|
||||
<div>RESTful API 설계 원칙과 보안 정책 확정. 담당자별 역할 분담.</div>
|
||||
<div style="margin-top: var(--spacing-2); font-size: var(--font-size-caption); color: var(--color-primary-main);">
|
||||
🔗 공통 키워드: API 설계, 이준호 담당
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-suggestion" style="cursor: pointer;" onclick="window.open('08-최종확정.html', '_blank')">
|
||||
<div class="ai-suggestion-header" style="color: var(--color-gray-900);">
|
||||
주간 진행 상황 점검
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<div style="display: flex; gap: var(--spacing-2); margin-bottom: var(--spacing-2); font-size: var(--font-size-caption);">
|
||||
<span>2024-10-10</span> | <span style="color: var(--color-info-main);">관련도 71%</span>
|
||||
</div>
|
||||
<div>전주 대비 개발 진행률 점검. UI 프로토타입 완료, 백엔드 개발 지연.</div>
|
||||
<div style="margin-top: var(--spacing-2); font-size: var(--font-size-caption); color: var(--color-primary-main);">
|
||||
🔗 공통 키워드: 진행률, 일정
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 관련 업무이력 섹션 -->
|
||||
<div>
|
||||
<h4 style="font-size: var(--font-size-body); font-weight: var(--font-weight-semibold); color: var(--color-gray-700); margin-bottom: var(--spacing-3);">
|
||||
📋 관련 업무이력 (2건)
|
||||
</h4>
|
||||
|
||||
<div class="ai-suggestion" style="cursor: pointer;" onclick="window.location.href='10-Todo관리.html'">
|
||||
<div class="ai-suggestion-header" style="color: var(--color-gray-900); display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>API 명세서 작성</span>
|
||||
<span class="badge badge-warning" style="font-size: var(--font-size-caption);">진행중</span>
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<div style="display: flex; gap: var(--spacing-2); margin-bottom: var(--spacing-2); font-size: var(--font-size-caption);">
|
||||
<span>담당: 이준호</span> | <span>마감: 3월 25일</span> | <span style="color: var(--color-warning-main);">진행률 60%</span>
|
||||
</div>
|
||||
<div>출처: 2025년 1분기 제품 기획 회의 (2025-10-25)</div>
|
||||
<div style="margin-top: var(--spacing-2); font-size: var(--font-size-caption); color: var(--color-primary-main);">
|
||||
🔗 관련 사유: 현재 논의 중인 API 설계와 직접 연관
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ai-suggestion" style="cursor: pointer;" onclick="window.location.href='10-Todo관리.html'">
|
||||
<div class="ai-suggestion-header" style="color: var(--color-gray-900); display: flex; justify-content: space-between; align-items: center;">
|
||||
<span>예산 편성안 검토</span>
|
||||
<span class="badge badge-error" style="font-size: var(--font-size-caption); background-color: var(--color-error-light); color: var(--color-error-dark);">지연</span>
|
||||
</div>
|
||||
<div class="ai-suggestion-text">
|
||||
<div style="display: flex; gap: var(--spacing-2); margin-bottom: var(--spacing-2); font-size: var(--font-size-caption);">
|
||||
<span>담당: 박서연</span> | <span style="color: var(--color-error-main);">마감 2일 경과</span> | <span>진행률 30%</span>
|
||||
</div>
|
||||
<div>출처: 2025년 1분기 제품 기획 회의 (2025-10-25)</div>
|
||||
<div style="margin-top: var(--spacing-2); font-size: var(--font-size-caption); color: var(--color-primary-main);">
|
||||
🔗 관련 사유: 예산 논의 시 참조 필요
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
// 탭 전환
|
||||
const tabs = document.querySelectorAll('.side-tab');
|
||||
const tabContents = document.querySelectorAll('.tab-content');
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const targetTab = tab.getAttribute('data-tab');
|
||||
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
|
||||
tabContents.forEach(content => {
|
||||
if (content.getAttribute('data-content') === targetTab) {
|
||||
content.style.display = 'block';
|
||||
} else {
|
||||
content.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 사이드 패널 토글 (모바일)
|
||||
const toggleBtn = document.getElementById('toggleSidePanel');
|
||||
const sidePanel = document.getElementById('sidePanel');
|
||||
|
||||
if (toggleBtn) {
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
sidePanel.classList.toggle('open');
|
||||
});
|
||||
}
|
||||
|
||||
// 녹음 시간 업데이트
|
||||
let seconds = 0;
|
||||
const recordingTimeEl = document.getElementById('recordingTime');
|
||||
|
||||
setInterval(() => {
|
||||
seconds++;
|
||||
const mins = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
recordingTimeEl.textContent = `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
|
||||
}, 1000);
|
||||
|
||||
// 자동 저장 시뮬레이션
|
||||
const editorTextarea = document.getElementById('meetingContent');
|
||||
let saveTimeout;
|
||||
|
||||
editorTextarea.addEventListener('input', () => {
|
||||
clearTimeout(saveTimeout);
|
||||
saveTimeout = setTimeout(() => {
|
||||
console.log('자동 저장됨');
|
||||
// 실제로는 서버에 저장
|
||||
}, 2000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
183
design-gappa/uiux/prototype/06-검증완료.html
Normal file
183
design-gappa/uiux/prototype/06-검증완료.html
Normal file
@ -0,0 +1,183 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>검증 완료 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
}
|
||||
.completion-icon {
|
||||
text-align: center;
|
||||
font-size: 80px;
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-3);
|
||||
text-align: center;
|
||||
}
|
||||
.page-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
text-align: center;
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||
gap: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.stat-card {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-5);
|
||||
text-align: center;
|
||||
}
|
||||
.stat-value {
|
||||
font-size: var(--font-size-h2);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-primary-main);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.stat-label {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
.summary-card {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
.summary-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.keyword-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
.keyword-tag {
|
||||
padding: var(--spacing-2) var(--spacing-3);
|
||||
background-color: var(--color-primary-light);
|
||||
color: var(--color-primary-dark);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
justify-content: center;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.completion-icon { font-size: 60px; }
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.action-buttons { flex-direction: column; }
|
||||
.action-buttons .btn { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="completion-icon">✅</div>
|
||||
<h1 class="page-title">AI 검증이 완료되었습니다</h1>
|
||||
<p class="page-subtitle">회의 내용이 분석되었습니다. 통계를 확인하고 회의를 종료하세요</p>
|
||||
|
||||
<!-- 통계 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">45분</div>
|
||||
<div class="stat-label">회의 시간</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">3명</div>
|
||||
<div class="stat-label">참석자</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">12회</div>
|
||||
<div class="stat-label">발언 횟수</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">5개</div>
|
||||
<div class="stat-label">Todo 생성</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 주요 키워드 -->
|
||||
<div class="summary-card">
|
||||
<h2 class="summary-title">주요 키워드</h2>
|
||||
<div class="keyword-list">
|
||||
<span class="keyword-tag">신규 기능</span>
|
||||
<span class="keyword-tag">개발 일정</span>
|
||||
<span class="keyword-tag">API 설계</span>
|
||||
<span class="keyword-tag">예산</span>
|
||||
<span class="keyword-tag">테스트</span>
|
||||
<span class="keyword-tag">배포</span>
|
||||
<span class="keyword-tag">마케팅</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 발언 분포 -->
|
||||
<div class="summary-card">
|
||||
<h2 class="summary-title">발언 분포</h2>
|
||||
<div style="margin-bottom: var(--spacing-3);">
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: var(--spacing-1);">
|
||||
<span style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">김민준</span>
|
||||
<span style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">5회 (42%)</span>
|
||||
</div>
|
||||
<div style="height: 8px; background-color: var(--color-gray-200); border-radius: var(--radius-sm); overflow: hidden;">
|
||||
<div style="width: 42%; height: 100%; background-color: var(--color-primary-main);"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: var(--spacing-3);">
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: var(--spacing-1);">
|
||||
<span style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">박서연</span>
|
||||
<span style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">4회 (33%)</span>
|
||||
</div>
|
||||
<div style="height: 8px; background-color: var(--color-gray-200); border-radius: var(--radius-sm); overflow: hidden;">
|
||||
<div style="width: 33%; height: 100%; background-color: var(--color-secondary-main);"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: var(--spacing-1);">
|
||||
<span style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">이준호</span>
|
||||
<span style="font-size: var(--font-size-body-small); color: var(--color-gray-600);">3회 (25%)</span>
|
||||
</div>
|
||||
<div style="height: 8px; background-color: var(--color-gray-200); border-radius: var(--radius-sm); overflow: hidden;">
|
||||
<div style="width: 25%; height: 100%; background-color: var(--color-info-main);"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 액션 버튼 -->
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-secondary" onclick="history.back()">회의로 돌아가기</button>
|
||||
<button class="btn btn-primary" onclick="window.location.href='07-회의종료.html'">
|
||||
회의 종료하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
MeetingApp.ready(() => {
|
||||
console.log('검증 완료 페이지 로드됨');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
112
design-gappa/uiux/prototype/07-회의종료.html
Normal file
112
design-gappa/uiux/prototype/07-회의종료.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>회의 종료 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
text-align: center;
|
||||
}
|
||||
.completion-icon {
|
||||
font-size: 100px;
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
.page-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.info-card {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-6);
|
||||
text-align: left;
|
||||
}
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: var(--spacing-3) 0;
|
||||
border-bottom: 1px solid var(--color-gray-100);
|
||||
}
|
||||
.info-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.info-label {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
.info-value {
|
||||
color: var(--color-gray-900);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-3);
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.completion-icon { font-size: 80px; }
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="completion-icon">🏁</div>
|
||||
<h1 class="page-title">회의가 종료되었습니다</h1>
|
||||
<p class="page-subtitle">회의록이 자동으로 저장되었습니다</p>
|
||||
|
||||
<!-- 회의 정보 -->
|
||||
<div class="info-card">
|
||||
<div class="info-item">
|
||||
<span class="info-label">회의 제목</span>
|
||||
<span class="info-value">2025년 1분기 제품 기획 회의</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">회의 시간</span>
|
||||
<span class="info-value">45분</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">참석자</span>
|
||||
<span class="info-value">3명</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">생성된 Todo</span>
|
||||
<span class="info-value">5개</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 액션 버튼 -->
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-primary" onclick="window.location.href='08-최종확정.html'">
|
||||
회의록 확정하기
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="window.location.href='02-대시보드.html'">
|
||||
대시보드로 이동
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
MeetingApp.ready(() => {
|
||||
console.log('회의 종료 페이지 로드됨');
|
||||
// 회의 종료 알림
|
||||
MeetingApp.Toast.success('회의가 성공적으로 종료되었습니다');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
303
design-gappa/uiux/prototype/08-최종확정.html
Normal file
303
design-gappa/uiux/prototype/08-최종확정.html
Normal file
@ -0,0 +1,303 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>회의록 최종 확정 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
}
|
||||
.page-header {
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.page-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
.content-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.preview-panel {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
}
|
||||
.preview-title {
|
||||
font-size: var(--font-size-h3);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.meeting-content {
|
||||
font-size: var(--font-size-body);
|
||||
line-height: var(--line-height-relaxed);
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
.meeting-content h2 {
|
||||
font-size: var(--font-size-h4);
|
||||
margin-top: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-3);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.meeting-content ul {
|
||||
margin-left: var(--spacing-5);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.checklist-panel {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
height: fit-content;
|
||||
}
|
||||
.checklist-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.checklist-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-3);
|
||||
padding: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-2);
|
||||
background: var(--color-gray-50);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
.checklist-item:hover {
|
||||
background: var(--color-gray-100);
|
||||
}
|
||||
.checklist-item.checked {
|
||||
background: rgba(0, 217, 177, 0.1);
|
||||
}
|
||||
.checklist-checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.checklist-item.checked .checklist-checkbox {
|
||||
background-color: var(--color-success-main);
|
||||
border-color: var(--color-success-main);
|
||||
color: var(--color-white);
|
||||
}
|
||||
.checklist-text {
|
||||
flex: 1;
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
justify-content: center;
|
||||
}
|
||||
.warning-message {
|
||||
background-color: var(--color-warning-light);
|
||||
border-left: 4px solid var(--color-warning-main);
|
||||
padding: var(--spacing-4);
|
||||
border-radius: var(--radius-md);
|
||||
margin-bottom: var(--spacing-4);
|
||||
display: none;
|
||||
}
|
||||
.warning-message.show {
|
||||
display: block;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.content-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
.action-buttons { flex-direction: column; }
|
||||
.action-buttons .btn { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">회의록 최종 확정</h1>
|
||||
<p class="page-subtitle">필수 항목을 확인하고 회의록을 최종 확정하세요</p>
|
||||
</div>
|
||||
|
||||
<div id="warningMessage" class="warning-message">
|
||||
⚠️ 아래 필수 항목을 모두 확인해주세요.
|
||||
</div>
|
||||
|
||||
<div class="content-grid">
|
||||
<!-- 회의록 미리보기 -->
|
||||
<div class="preview-panel">
|
||||
<h2 class="preview-title">2025년 1분기 제품 기획 회의</h2>
|
||||
<div class="meeting-content">
|
||||
<p><strong>날짜:</strong> 2025-10-25 14:00<br>
|
||||
<strong>장소:</strong> 본사 2층 대회의실<br>
|
||||
<strong>참석자:</strong> 김민준, 박서연, 이준호</p>
|
||||
|
||||
<h2>안건</h2>
|
||||
<ul>
|
||||
<li>신규 기능 개발 일정 논의</li>
|
||||
<li>예산 편성 검토</li>
|
||||
</ul>
|
||||
|
||||
<h2>논의 내용</h2>
|
||||
<p>신규 회의록 서비스의 핵심 기능에 대해 논의했습니다. AI 기반 자동 작성 기능과 실시간 협업 기능을 우선적으로 개발하기로 결정했습니다.</p>
|
||||
|
||||
<p>개발 일정은 3월 말 완료를 목표로 하며, 주요 마일스톤은 다음과 같습니다:</p>
|
||||
<ul>
|
||||
<li>3월 10일: 기본 UI 완성</li>
|
||||
<li>3월 20일: AI 기능 통합</li>
|
||||
<li>3월 30일: 베타 테스트 시작</li>
|
||||
</ul>
|
||||
|
||||
<h2>결정 사항</h2>
|
||||
<ul>
|
||||
<li>신규 기능 개발은 3월 말 완료 목표</li>
|
||||
<li>이준호님이 API 설계 담당</li>
|
||||
<li>예산은 5천만원으로 확정</li>
|
||||
</ul>
|
||||
|
||||
<h2>Todo</h2>
|
||||
<ul>
|
||||
<li>API 명세서 작성 (담당: 이준호, 마감: 3월 25일)</li>
|
||||
<li>UI 프로토타입 완성 (담당: 최유진, 마감: 3월 15일)</li>
|
||||
<li>예산 편성안 검토 (담당: 박서연, 마감: 3월 20일)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 확인 체크리스트 -->
|
||||
<div class="checklist-panel">
|
||||
<h3 class="checklist-title">필수 항목 확인</h3>
|
||||
|
||||
<div class="checklist-item" data-required="true">
|
||||
<div class="checklist-checkbox"></div>
|
||||
<div class="checklist-text">
|
||||
<strong>회의 제목</strong><br>
|
||||
회의 제목이 명확하게 작성되었습니다
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checklist-item" data-required="true">
|
||||
<div class="checklist-checkbox"></div>
|
||||
<div class="checklist-text">
|
||||
<strong>참석자 목록</strong><br>
|
||||
모든 참석자가 기록되었습니다
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checklist-item" data-required="true">
|
||||
<div class="checklist-checkbox"></div>
|
||||
<div class="checklist-text">
|
||||
<strong>주요 논의 내용</strong><br>
|
||||
핵심 논의 내용이 포함되었습니다
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checklist-item" data-required="true">
|
||||
<div class="checklist-checkbox"></div>
|
||||
<div class="checklist-text">
|
||||
<strong>결정 사항</strong><br>
|
||||
회의 중 결정된 사항이 명시되었습니다
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checklist-item">
|
||||
<div class="checklist-checkbox"></div>
|
||||
<div class="checklist-text">
|
||||
<strong>Todo 생성</strong><br>
|
||||
실행 항목이 Todo로 생성되었습니다
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checklist-item">
|
||||
<div class="checklist-checkbox"></div>
|
||||
<div class="checklist-text">
|
||||
<strong>전문용어 설명</strong><br>
|
||||
필요한 용어에 설명이 추가되었습니다
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 액션 버튼 -->
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-secondary" onclick="history.back()">이전으로</button>
|
||||
<button class="btn btn-primary" id="confirmBtn" disabled>회의록 확정하기</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
const checklistItems = document.querySelectorAll('.checklist-item');
|
||||
const confirmBtn = document.getElementById('confirmBtn');
|
||||
const warningMessage = document.getElementById('warningMessage');
|
||||
|
||||
// 체크리스트 항목 클릭
|
||||
checklistItems.forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
item.classList.toggle('checked');
|
||||
const checkbox = item.querySelector('.checklist-checkbox');
|
||||
if (item.classList.contains('checked')) {
|
||||
checkbox.textContent = '✓';
|
||||
} else {
|
||||
checkbox.textContent = '';
|
||||
}
|
||||
checkCompletion();
|
||||
});
|
||||
});
|
||||
|
||||
// 완료 여부 확인
|
||||
function checkCompletion() {
|
||||
const requiredItems = document.querySelectorAll('.checklist-item[data-required="true"]');
|
||||
const checkedRequired = document.querySelectorAll('.checklist-item[data-required="true"].checked');
|
||||
|
||||
if (requiredItems.length === checkedRequired.length) {
|
||||
confirmBtn.disabled = false;
|
||||
warningMessage.classList.remove('show');
|
||||
} else {
|
||||
confirmBtn.disabled = true;
|
||||
warningMessage.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
// 확정 버튼 클릭
|
||||
confirmBtn.addEventListener('click', () => {
|
||||
MeetingApp.Loading.show();
|
||||
|
||||
setTimeout(() => {
|
||||
MeetingApp.Loading.hide();
|
||||
MeetingApp.Toast.success('회의록이 확정되었습니다!');
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = '09-회의록공유.html';
|
||||
}, 1000);
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
// 초기 확인
|
||||
checkCompletion();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
316
design-gappa/uiux/prototype/09-회의록공유.html
Normal file
316
design-gappa/uiux/prototype/09-회의록공유.html
Normal file
@ -0,0 +1,316 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>회의록 공유 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
}
|
||||
.success-icon {
|
||||
text-align: center;
|
||||
font-size: 80px;
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-3);
|
||||
text-align: center;
|
||||
}
|
||||
.page-subtitle {
|
||||
font-size: var(--font-size-body);
|
||||
color: var(--color-gray-500);
|
||||
text-align: center;
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.share-card {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
.share-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
.share-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
padding: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-3);
|
||||
background: var(--color-gray-50);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
.share-option:hover {
|
||||
background: var(--color-gray-100);
|
||||
}
|
||||
.share-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
.share-info {
|
||||
flex: 1;
|
||||
}
|
||||
.share-label {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
.share-desc {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
.link-box {
|
||||
display: flex;
|
||||
gap: var(--spacing-2);
|
||||
align-items: center;
|
||||
}
|
||||
.link-input {
|
||||
flex: 1;
|
||||
padding: var(--spacing-3) var(--spacing-4);
|
||||
border: 1px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body-small);
|
||||
background-color: var(--color-gray-50);
|
||||
font-family: monospace;
|
||||
}
|
||||
.attendee-list {
|
||||
margin-top: var(--spacing-4);
|
||||
}
|
||||
.attendee-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-3);
|
||||
padding: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-2);
|
||||
background: var(--color-gray-50);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
.attendee-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.attendee-info {
|
||||
flex: 1;
|
||||
}
|
||||
.attendee-name {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.attendee-email {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
.sent-badge {
|
||||
padding: var(--spacing-1) var(--spacing-3);
|
||||
background-color: var(--color-success-light);
|
||||
color: var(--color-success-dark);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
justify-content: center;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.success-icon { font-size: 60px; }
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
.action-buttons { flex-direction: column; }
|
||||
.action-buttons .btn { width: 100%; }
|
||||
.link-box { flex-direction: column; }
|
||||
.link-input { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="success-icon">🎉</div>
|
||||
<h1 class="page-title">회의록이 확정되었습니다</h1>
|
||||
<p class="page-subtitle">이제 참석자들과 회의록을 공유하세요</p>
|
||||
|
||||
<!-- 공유 링크 -->
|
||||
<div class="share-card">
|
||||
<h2 class="share-title">공유 링크</h2>
|
||||
<div class="link-box">
|
||||
<input type="text" class="link-input" id="shareLink" value="https://meeting.example.com/share/m-001-abc123" readonly>
|
||||
<button class="btn btn-primary" onclick="copyLink()">복사</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 공유 방식 -->
|
||||
<div class="share-card">
|
||||
<h2 class="share-title">공유 방식 선택</h2>
|
||||
|
||||
<div class="share-option" onclick="shareViaEmail()">
|
||||
<div class="share-icon">📧</div>
|
||||
<div class="share-info">
|
||||
<div class="share-label">이메일로 공유</div>
|
||||
<div class="share-desc">참석자들에게 이메일을 발송합니다</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="share-option" onclick="shareViaSlack()">
|
||||
<div class="share-icon">💬</div>
|
||||
<div class="share-info">
|
||||
<div class="share-label">슬랙으로 공유</div>
|
||||
<div class="share-desc">슬랙 채널에 회의록을 공유합니다</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="share-option" onclick="downloadPDF()">
|
||||
<div class="share-icon">📄</div>
|
||||
<div class="share-info">
|
||||
<div class="share-label">PDF로 다운로드</div>
|
||||
<div class="share-desc">회의록을 PDF 파일로 저장합니다</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 생성된 Todo -->
|
||||
<div class="share-card">
|
||||
<h2 class="share-title">생성된 Todo (3개)</h2>
|
||||
<div class="attendee-list">
|
||||
<div class="attendee-item">
|
||||
<div style="display: flex; align-items: center; gap: var(--spacing-3); flex: 1;">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-primary-main);">이</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">API 명세서 작성</div>
|
||||
<div class="attendee-email" style="display: flex; align-items: center; gap: var(--spacing-2);">
|
||||
<span>담당: 이준호</span> | <span>📅 3월 25일</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: var(--spacing-1);">
|
||||
<span class="sent-badge" style="background-color: var(--color-warning-light); color: var(--color-warning-dark);">진행중 60%</span>
|
||||
<a href="10-Todo관리.html" style="font-size: var(--font-size-caption); color: var(--color-primary-main); text-decoration: none;">Todo 보기 →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attendee-item">
|
||||
<div style="display: flex; align-items: center; gap: var(--spacing-3); flex: 1;">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-info-main);">최</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">UI 프로토타입 완성</div>
|
||||
<div class="attendee-email" style="display: flex; align-items: center; gap: var(--spacing-2);">
|
||||
<span>담당: 최유진</span> | <span>📅 3월 15일</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: var(--spacing-1);">
|
||||
<span class="sent-badge">완료 100%</span>
|
||||
<a href="10-Todo관리.html" style="font-size: var(--font-size-caption); color: var(--color-primary-main); text-decoration: none;">Todo 보기 →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attendee-item">
|
||||
<div style="display: flex; align-items: center; gap: var(--spacing-3); flex: 1;">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-secondary-main);">박</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">예산 편성안 검토</div>
|
||||
<div class="attendee-email" style="display: flex; align-items: center; gap: var(--spacing-2);">
|
||||
<span>담당: 박서연</span> | <span>📅 3월 20일</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: var(--spacing-1);">
|
||||
<span class="sent-badge" style="background-color: var(--color-error-light); color: var(--color-error-dark);">지연 30%</span>
|
||||
<a href="10-Todo관리.html" style="font-size: var(--font-size-caption); color: var(--color-primary-main); text-decoration: none;">Todo 보기 →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 참석자 목록 -->
|
||||
<div class="share-card">
|
||||
<h2 class="share-title">참석자 (3명)</h2>
|
||||
<div class="attendee-list">
|
||||
<div class="attendee-item">
|
||||
<div class="attendee-avatar">김</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">김민준</div>
|
||||
<div class="attendee-email">minjun.kim@example.com</div>
|
||||
</div>
|
||||
<span class="sent-badge">발송 완료</span>
|
||||
</div>
|
||||
<div class="attendee-item">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-secondary-main);">박</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">박서연</div>
|
||||
<div class="attendee-email">seoyeon.park@example.com</div>
|
||||
</div>
|
||||
<span class="sent-badge">발송 완료</span>
|
||||
</div>
|
||||
<div class="attendee-item">
|
||||
<div class="attendee-avatar" style="background-color: var(--color-info-main);">이</div>
|
||||
<div class="attendee-info">
|
||||
<div class="attendee-name">이준호</div>
|
||||
<div class="attendee-email">junho.lee@example.com</div>
|
||||
</div>
|
||||
<span class="sent-badge">발송 완료</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 액션 버튼 -->
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-secondary" onclick="window.location.href='02-대시보드.html'">
|
||||
대시보드로 이동
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="window.location.href='10-Todo관리.html'">
|
||||
Todo 관리하기
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
function copyLink() {
|
||||
const linkInput = document.getElementById('shareLink');
|
||||
linkInput.select();
|
||||
document.execCommand('copy');
|
||||
MeetingApp.Toast.success('링크가 복사되었습니다');
|
||||
}
|
||||
|
||||
function shareViaEmail() {
|
||||
MeetingApp.Loading.show();
|
||||
setTimeout(() => {
|
||||
MeetingApp.Loading.hide();
|
||||
MeetingApp.Toast.success('이메일이 발송되었습니다');
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function shareViaSlack() {
|
||||
MeetingApp.Loading.show();
|
||||
setTimeout(() => {
|
||||
MeetingApp.Loading.hide();
|
||||
MeetingApp.Toast.success('슬랙에 공유되었습니다');
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function downloadPDF() {
|
||||
MeetingApp.Toast.info('PDF 파일을 준비 중입니다...');
|
||||
setTimeout(() => {
|
||||
MeetingApp.Toast.success('PDF 다운로드가 시작되었습니다');
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
466
design-gappa/uiux/prototype/10-Todo관리.html
Normal file
466
design-gappa/uiux/prototype/10-Todo관리.html
Normal file
@ -0,0 +1,466 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Todo 관리 - 회의록 서비스</title>
|
||||
<link rel="stylesheet" href="common.css">
|
||||
<style>
|
||||
body { background-color: var(--color-gray-50); }
|
||||
.page-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: var(--spacing-8) var(--spacing-4);
|
||||
}
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
.page-title {
|
||||
font-size: var(--font-size-h1);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.view-toggle {
|
||||
display: flex;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
.view-btn {
|
||||
padding: var(--spacing-2) var(--spacing-4);
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body-small);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
.view-btn.active {
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
border-color: var(--color-primary-main);
|
||||
}
|
||||
.kanban-board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--spacing-6);
|
||||
}
|
||||
.kanban-column {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-4);
|
||||
min-height: 500px;
|
||||
}
|
||||
.column-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--spacing-4);
|
||||
padding-bottom: var(--spacing-3);
|
||||
border-bottom: 2px solid var(--color-gray-200);
|
||||
}
|
||||
.column-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
.column-count {
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
background-color: var(--color-gray-200);
|
||||
color: var(--color-gray-700);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.todo-card {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-3);
|
||||
cursor: grab;
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
.todo-card:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.todo-card.priority-high {
|
||||
border-left: 4px solid var(--color-error-main);
|
||||
}
|
||||
.todo-card.priority-medium {
|
||||
border-left: 4px solid var(--color-warning-main);
|
||||
}
|
||||
.todo-title {
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.todo-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-3);
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
.todo-assignee {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
}
|
||||
.avatar-sm {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
}
|
||||
.todo-duedate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-1);
|
||||
}
|
||||
.todo-duedate.overdue {
|
||||
color: var(--color-error-main);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
.todo-progress {
|
||||
height: 4px;
|
||||
background-color: var(--color-gray-200);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.todo-progress-bar {
|
||||
height: 100%;
|
||||
background-color: var(--color-primary-main);
|
||||
transition: width var(--transition-slow);
|
||||
}
|
||||
.todo-source {
|
||||
margin-top: var(--spacing-3);
|
||||
padding-top: var(--spacing-3);
|
||||
border-top: 1px dashed var(--color-gray-200);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
.todo-source-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-2);
|
||||
color: var(--color-primary-main);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
cursor: pointer;
|
||||
}
|
||||
.todo-source-link:hover {
|
||||
color: var(--color-primary-dark);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.list-view {
|
||||
display: none;
|
||||
}
|
||||
.list-view.active {
|
||||
display: block;
|
||||
}
|
||||
.todo-list-item {
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
.todo-checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
}
|
||||
.todo-list-content {
|
||||
flex: 1;
|
||||
}
|
||||
@media (max-width: 1023px) {
|
||||
.kanban-board {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
.page-title { font-size: var(--font-size-h2); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page-container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Todo 관리</h1>
|
||||
<div style="display: flex; gap: var(--spacing-3); align-items: center;">
|
||||
<div class="view-toggle">
|
||||
<button class="view-btn active" data-view="kanban">칸반</button>
|
||||
<button class="view-btn" data-view="list">리스트</button>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="addTodo()">+ 새 Todo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 칸반 보드 뷰 -->
|
||||
<div class="kanban-board" id="kanbanView">
|
||||
<!-- 시작 전 -->
|
||||
<div class="kanban-column">
|
||||
<div class="column-header">
|
||||
<h2 class="column-title">시작 전</h2>
|
||||
<span class="column-count">2</span>
|
||||
</div>
|
||||
|
||||
<div class="todo-card priority-high">
|
||||
<div class="todo-title">데이터베이스 스키마 설계</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm">이</div>
|
||||
<span>이준호</span>
|
||||
</div>
|
||||
<div class="todo-duedate">
|
||||
📅 D-3
|
||||
</div>
|
||||
</div>
|
||||
<div class="todo-progress">
|
||||
<div class="todo-progress-bar" style="width: 0%;"></div>
|
||||
</div>
|
||||
<div class="todo-source">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="todo-card">
|
||||
<div class="todo-title">사용자 피드백 분석</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm" style="background-color: var(--color-secondary-main);">박</div>
|
||||
<span>박서연</span>
|
||||
</div>
|
||||
<div class="todo-duedate">
|
||||
📅 D-5
|
||||
</div>
|
||||
</div>
|
||||
<div class="todo-progress">
|
||||
<div class="todo-progress-bar" style="width: 0%;"></div>
|
||||
</div>
|
||||
<div class="todo-source">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 고객 만족도 개선 회의 (2025-10-18)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 진행 중 -->
|
||||
<div class="kanban-column">
|
||||
<div class="column-header">
|
||||
<h2 class="column-title">진행 중</h2>
|
||||
<span class="column-count">2</span>
|
||||
</div>
|
||||
|
||||
<div class="todo-card priority-high">
|
||||
<div class="todo-title">API 명세서 작성</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm">이</div>
|
||||
<span>이준호</span>
|
||||
</div>
|
||||
<div class="todo-duedate">
|
||||
📅 오늘
|
||||
</div>
|
||||
</div>
|
||||
<div class="todo-progress">
|
||||
<div class="todo-progress-bar" style="width: 60%;"></div>
|
||||
</div>
|
||||
<div class="todo-source">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="todo-card priority-medium">
|
||||
<div class="todo-title">예산 편성안 검토</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm" style="background-color: var(--color-secondary-main);">박</div>
|
||||
<span>박서연</span>
|
||||
</div>
|
||||
<div class="todo-duedate overdue">
|
||||
📅 D+2 (지남)
|
||||
</div>
|
||||
</div>
|
||||
<div class="todo-progress">
|
||||
<div class="todo-progress-bar" style="width: 30%;"></div>
|
||||
</div>
|
||||
<div class="todo-source">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 완료 -->
|
||||
<div class="kanban-column">
|
||||
<div class="column-header">
|
||||
<h2 class="column-title">완료</h2>
|
||||
<span class="column-count">1</span>
|
||||
</div>
|
||||
|
||||
<div class="todo-card">
|
||||
<div class="todo-title">UI 프로토타입 디자인</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm" style="background-color: var(--color-info-main);">최</div>
|
||||
<span>최유진</span>
|
||||
</div>
|
||||
<div class="todo-duedate">
|
||||
✅ 완료
|
||||
</div>
|
||||
</div>
|
||||
<div class="todo-progress">
|
||||
<div class="todo-progress-bar" style="width: 100%; background-color: var(--color-success-main);"></div>
|
||||
</div>
|
||||
<div class="todo-source">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 리스트 뷰 -->
|
||||
<div class="list-view" id="listView">
|
||||
<div class="todo-list-item">
|
||||
<input type="checkbox" class="todo-checkbox">
|
||||
<div class="todo-list-content">
|
||||
<div class="todo-title">API 명세서 작성</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm">이</div>
|
||||
<span>이준호</span>
|
||||
</div>
|
||||
<div class="todo-duedate">📅 오늘</div>
|
||||
<span class="badge badge-warning">진행 중</span>
|
||||
</div>
|
||||
<div class="todo-source" style="margin-top: var(--spacing-2);">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="todo-list-item">
|
||||
<input type="checkbox" class="todo-checkbox">
|
||||
<div class="todo-list-content">
|
||||
<div class="todo-title">예산 편성안 검토</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm" style="background-color: var(--color-secondary-main);">박</div>
|
||||
<span>박서연</span>
|
||||
</div>
|
||||
<div class="todo-duedate overdue">📅 D+2 (지남)</div>
|
||||
<span class="badge badge-warning">진행 중</span>
|
||||
</div>
|
||||
<div class="todo-source" style="margin-top: var(--spacing-2);">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="todo-list-item">
|
||||
<input type="checkbox" class="todo-checkbox" checked>
|
||||
<div class="todo-list-content">
|
||||
<div class="todo-title" style="text-decoration: line-through; color: var(--color-gray-500);">UI 프로토타입 디자인</div>
|
||||
<div class="todo-meta">
|
||||
<div class="todo-assignee">
|
||||
<div class="avatar-sm" style="background-color: var(--color-info-main);">최</div>
|
||||
<span>최유진</span>
|
||||
</div>
|
||||
<span class="badge badge-success">완료</span>
|
||||
</div>
|
||||
<div class="todo-source" style="margin-top: var(--spacing-2);">
|
||||
<a href="08-최종확정.html#todo-section" class="todo-source-link" onclick="MeetingApp.Toast.info('회의록으로 이동합니다'); return true;">
|
||||
📄 2025년 1분기 제품 기획 회의 (2025-10-25)
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
// 뷰 전환
|
||||
const viewBtns = document.querySelectorAll('.view-btn');
|
||||
const kanbanView = document.getElementById('kanbanView');
|
||||
const listView = document.getElementById('listView');
|
||||
|
||||
viewBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const view = btn.getAttribute('data-view');
|
||||
|
||||
viewBtns.forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
|
||||
if (view === 'kanban') {
|
||||
kanbanView.style.display = 'grid';
|
||||
listView.classList.remove('active');
|
||||
} else {
|
||||
kanbanView.style.display = 'none';
|
||||
listView.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Todo 추가
|
||||
function addTodo() {
|
||||
MeetingApp.Toast.info('Todo 추가 기능은 준비 중입니다');
|
||||
}
|
||||
|
||||
// Todo 카드 클릭
|
||||
const todoCards = document.querySelectorAll('.todo-card');
|
||||
todoCards.forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
MeetingApp.Toast.info('Todo 상세 정보를 표시합니다');
|
||||
});
|
||||
});
|
||||
|
||||
// 드래그 앤 드롭 (간단한 시뮬레이션)
|
||||
todoCards.forEach(card => {
|
||||
card.addEventListener('dragstart', (e) => {
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.target.style.opacity = '0.5';
|
||||
});
|
||||
|
||||
card.addEventListener('dragend', (e) => {
|
||||
e.target.style.opacity = '1';
|
||||
});
|
||||
|
||||
card.setAttribute('draggable', 'true');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
1021
design-gappa/uiux/prototype/11-회의록대시보드.html
Normal file
1021
design-gappa/uiux/prototype/11-회의록대시보드.html
Normal file
File diff suppressed because it is too large
Load Diff
780
design-gappa/uiux/prototype/common.css
Normal file
780
design-gappa/uiux/prototype/common.css
Normal file
@ -0,0 +1,780 @@
|
||||
/*
|
||||
* 회의록 작성 및 공유 개선 서비스 - 공통 스타일시트
|
||||
* 버전: 1.0
|
||||
* 작성일: 2025-10-20
|
||||
* 레퍼런스: 스타일 가이드 v1.0
|
||||
*/
|
||||
|
||||
/* ===== CSS Reset ===== */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ===== Root Variables (CSS Custom Properties) ===== */
|
||||
:root {
|
||||
/* Primary Colors */
|
||||
--color-primary-light: #4DFFDB;
|
||||
--color-primary-main: #00D9B1;
|
||||
--color-primary-dark: #00A88A;
|
||||
|
||||
/* Secondary Colors */
|
||||
--color-secondary-light: #A5B4FC;
|
||||
--color-secondary-main: #6366F1;
|
||||
--color-secondary-dark: #4F46E5;
|
||||
|
||||
/* Semantic Colors */
|
||||
--color-success-light: #6EE7B7;
|
||||
--color-success-main: #10B981;
|
||||
--color-success-dark: #059669;
|
||||
|
||||
--color-warning-light: #FCD34D;
|
||||
--color-warning-main: #F59E0B;
|
||||
--color-warning-dark: #D97706;
|
||||
|
||||
--color-error-light: #FCA5A5;
|
||||
--color-error-main: #EF4444;
|
||||
--color-error-dark: #DC2626;
|
||||
|
||||
--color-info-light: #93C5FD;
|
||||
--color-info-main: #3B82F6;
|
||||
--color-info-dark: #2563EB;
|
||||
|
||||
/* Neutral Colors */
|
||||
--color-gray-50: #F9FAFB;
|
||||
--color-gray-100: #F3F4F6;
|
||||
--color-gray-200: #E5E7EB;
|
||||
--color-gray-300: #D1D5DB;
|
||||
--color-gray-400: #9CA3AF;
|
||||
--color-gray-500: #6B7280;
|
||||
--color-gray-600: #4B5563;
|
||||
--color-gray-700: #374151;
|
||||
--color-gray-800: #1F2937;
|
||||
--color-gray-900: #111827;
|
||||
--color-white: #FFFFFF;
|
||||
--color-black: #000000;
|
||||
|
||||
/* Spacing System (8px base) */
|
||||
--spacing-0: 0;
|
||||
--spacing-1: 4px;
|
||||
--spacing-2: 8px;
|
||||
--spacing-3: 12px;
|
||||
--spacing-4: 16px;
|
||||
--spacing-5: 20px;
|
||||
--spacing-6: 24px;
|
||||
--spacing-8: 32px;
|
||||
--spacing-10: 40px;
|
||||
--spacing-12: 48px;
|
||||
--spacing-16: 64px;
|
||||
--spacing-20: 80px;
|
||||
|
||||
/* Font Sizes */
|
||||
--font-size-display: 48px;
|
||||
--font-size-h1: 36px;
|
||||
--font-size-h2: 30px;
|
||||
--font-size-h3: 24px;
|
||||
--font-size-h4: 20px;
|
||||
--font-size-body-large: 18px;
|
||||
--font-size-body: 16px;
|
||||
--font-size-body-small: 14px;
|
||||
--font-size-caption: 12px;
|
||||
|
||||
/* Font Weights */
|
||||
--font-weight-light: 300;
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-medium: 500;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 700;
|
||||
|
||||
/* Line Heights */
|
||||
--line-height-tight: 1.25;
|
||||
--line-height-normal: 1.5;
|
||||
--line-height-relaxed: 1.75;
|
||||
|
||||
/* Border Radius */
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
--radius-full: 50%;
|
||||
|
||||
/* Shadows */
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
--shadow-lg: 0 20px 25px rgba(0, 0, 0, 0.15);
|
||||
|
||||
/* Transitions */
|
||||
--transition-fast: 150ms ease-out;
|
||||
--transition-base: 200ms ease-out;
|
||||
--transition-slow: 300ms ease-out;
|
||||
|
||||
/* Z-index */
|
||||
--z-dropdown: 1000;
|
||||
--z-sticky: 1100;
|
||||
--z-modal-backdrop: 1200;
|
||||
--z-modal: 1300;
|
||||
--z-toast: 1400;
|
||||
--z-tooltip: 1500;
|
||||
}
|
||||
|
||||
/* ===== Typography ===== */
|
||||
body {
|
||||
font-family: 'Pretendard', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
'Apple SD Gothic Neo', sans-serif;
|
||||
font-size: var(--font-size-body);
|
||||
font-weight: var(--font-weight-regular);
|
||||
line-height: var(--line-height-normal);
|
||||
color: var(--color-gray-600);
|
||||
background-color: var(--color-gray-50);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--color-gray-900);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
h1 { font-size: var(--font-size-h1); letter-spacing: -0.02em; }
|
||||
h2 { font-size: var(--font-size-h2); font-weight: var(--font-weight-semibold); }
|
||||
h3 { font-size: var(--font-size-h3); font-weight: var(--font-weight-semibold); line-height: var(--line-height-normal); }
|
||||
h4 { font-size: var(--font-size-h4); font-weight: var(--font-weight-semibold); }
|
||||
|
||||
a {
|
||||
color: var(--color-primary-main);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
/* ===== Layout Utilities ===== */
|
||||
.container {
|
||||
width: 100%;
|
||||
padding: 0 var(--spacing-6);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container { padding: 0 var(--spacing-8); }
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.container { padding: 0 var(--spacing-16); }
|
||||
}
|
||||
|
||||
.container-small { max-width: 640px; }
|
||||
.container-medium { max-width: 768px; }
|
||||
.container-large { max-width: 1024px; }
|
||||
.container-xlarge { max-width: 1280px; }
|
||||
.container-2xlarge { max-width: 1536px; }
|
||||
|
||||
/* ===== Button Styles ===== */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-2);
|
||||
padding: var(--spacing-3) var(--spacing-6);
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Primary Button */
|
||||
.btn-primary {
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background-color: var(--color-primary-light);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.btn-primary:active:not(:disabled) {
|
||||
background-color: var(--color-primary-dark);
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background-color: var(--color-gray-300);
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
/* Secondary Button */
|
||||
.btn-secondary {
|
||||
background-color: transparent;
|
||||
color: var(--color-primary-main);
|
||||
border: 1px solid var(--color-primary-main);
|
||||
}
|
||||
|
||||
.btn-secondary:hover:not(:disabled) {
|
||||
background-color: rgba(0, 217, 177, 0.1);
|
||||
}
|
||||
|
||||
.btn-secondary:active:not(:disabled) {
|
||||
background-color: rgba(0, 217, 177, 0.2);
|
||||
}
|
||||
|
||||
.btn-secondary:disabled {
|
||||
border-color: var(--color-gray-300);
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
|
||||
/* Text Button */
|
||||
.btn-text {
|
||||
background-color: transparent;
|
||||
color: var(--color-gray-700);
|
||||
padding: var(--spacing-2) var(--spacing-4);
|
||||
}
|
||||
|
||||
.btn-text:hover:not(:disabled) {
|
||||
background-color: var(--color-gray-100);
|
||||
}
|
||||
|
||||
.btn-text:active:not(:disabled) {
|
||||
background-color: var(--color-gray-200);
|
||||
}
|
||||
|
||||
/* Button Sizes */
|
||||
.btn-sm { padding: var(--spacing-2) var(--spacing-4); font-size: var(--font-size-body-small); }
|
||||
.btn-lg { padding: var(--spacing-4) var(--spacing-8); font-size: var(--font-size-body-large); }
|
||||
|
||||
/* Icon Button */
|
||||
.btn-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
border-radius: var(--radius-md);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.btn-icon:hover:not(:disabled) {
|
||||
background-color: var(--color-gray-100);
|
||||
}
|
||||
|
||||
.btn-icon-sm { width: 32px; height: 32px; }
|
||||
.btn-icon-lg { width: 48px; height: 48px; }
|
||||
|
||||
/* Floating Action Button */
|
||||
.fab {
|
||||
position: fixed;
|
||||
right: var(--spacing-4);
|
||||
bottom: var(--spacing-4);
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
padding: 0;
|
||||
border-radius: var(--radius-full);
|
||||
background-color: var(--color-primary-main);
|
||||
color: var(--color-white);
|
||||
box-shadow: var(--shadow-md);
|
||||
z-index: var(--z-sticky);
|
||||
}
|
||||
|
||||
.fab:hover {
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.fab:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* ===== Form Styles ===== */
|
||||
.form-group {
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: var(--spacing-2);
|
||||
font-size: var(--font-size-body-small);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea,
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: var(--spacing-3) var(--spacing-4);
|
||||
border: 1px solid var(--color-gray-300);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--font-size-body);
|
||||
font-family: inherit;
|
||||
background-color: var(--color-white);
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.form-input {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-textarea:focus,
|
||||
.form-select:focus {
|
||||
outline: 4px solid rgba(0, 217, 177, 0.2);
|
||||
border-color: var(--color-primary-main);
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.form-input::placeholder,
|
||||
.form-textarea::placeholder {
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
|
||||
.form-input:disabled,
|
||||
.form-textarea:disabled,
|
||||
.form-select:disabled {
|
||||
background-color: var(--color-gray-100);
|
||||
color: var(--color-gray-400);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.form-input.error,
|
||||
.form-textarea.error,
|
||||
.form-select.error {
|
||||
border-color: var(--color-error-main);
|
||||
}
|
||||
|
||||
.form-error {
|
||||
display: block;
|
||||
margin-top: var(--spacing-1);
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-error-main);
|
||||
}
|
||||
|
||||
/* ===== Card Styles ===== */
|
||||
.card {
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-6);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: all var(--transition-base);
|
||||
}
|
||||
|
||||
.card.interactive:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: translateY(-2px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card.interactive:active {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
margin-bottom: var(--spacing-4);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: var(--font-size-h4);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
/* ===== Badge Styles ===== */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-1);
|
||||
padding: var(--spacing-1) var(--spacing-3);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.badge-primary {
|
||||
background-color: var(--color-primary-light);
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.badge-success {
|
||||
background-color: var(--color-success-light);
|
||||
color: var(--color-success-dark);
|
||||
}
|
||||
|
||||
.badge-warning {
|
||||
background-color: var(--color-warning-light);
|
||||
color: var(--color-warning-dark);
|
||||
}
|
||||
|
||||
.badge-error {
|
||||
background-color: var(--color-error-light);
|
||||
color: var(--color-error-dark);
|
||||
}
|
||||
|
||||
.badge-neutral {
|
||||
background-color: var(--color-gray-200);
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
/* ===== Modal Styles ===== */
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: var(--z-modal-backdrop);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--spacing-4);
|
||||
opacity: 0;
|
||||
animation: fadeIn var(--transition-base);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.modal {
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: var(--spacing-8);
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: var(--z-modal);
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
animation: modalIn var(--transition-base);
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
margin-bottom: var(--spacing-6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: var(--font-size-h3);
|
||||
font-weight: var(--font-weight-semibold);
|
||||
color: var(--color-gray-900);
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
padding: var(--spacing-2);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--color-gray-500);
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin-bottom: var(--spacing-8);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
gap: var(--spacing-3);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes modalIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Toast Styles ===== */
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: var(--spacing-4);
|
||||
right: var(--spacing-4);
|
||||
z-index: var(--z-toast);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-3);
|
||||
}
|
||||
|
||||
.toast {
|
||||
background-color: var(--color-white);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4) var(--spacing-5);
|
||||
max-width: 400px;
|
||||
box-shadow: var(--shadow-md);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-3);
|
||||
border-left: 4px solid var(--color-gray-400);
|
||||
animation: slideInRight var(--transition-base);
|
||||
}
|
||||
|
||||
.toast-success { border-left-color: var(--color-success-main); }
|
||||
.toast-error { border-left-color: var(--color-error-main); }
|
||||
.toast-warning { border-left-color: var(--color-warning-main); }
|
||||
.toast-info { border-left-color: var(--color-info-main); }
|
||||
|
||||
.toast-icon {
|
||||
flex-shrink: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.toast-title {
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-gray-900);
|
||||
margin-bottom: var(--spacing-1);
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
font-size: var(--font-size-body-small);
|
||||
color: var(--color-gray-600);
|
||||
}
|
||||
|
||||
.toast-close {
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--color-gray-500);
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(400px);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Loading Styles ===== */
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid var(--color-gray-200);
|
||||
border-top-color: var(--color-primary-main);
|
||||
border-radius: var(--radius-full);
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.spinner-sm { width: 24px; height: 24px; border-width: 3px; }
|
||||
.spinner-lg { width: 56px; height: 56px; border-width: 5px; }
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
background-color: var(--color-gray-200);
|
||||
border-radius: var(--radius-sm);
|
||||
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
/* ===== Utility Classes ===== */
|
||||
.text-center { text-align: center; }
|
||||
.text-left { text-align: left; }
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.mt-1 { margin-top: var(--spacing-1); }
|
||||
.mt-2 { margin-top: var(--spacing-2); }
|
||||
.mt-3 { margin-top: var(--spacing-3); }
|
||||
.mt-4 { margin-top: var(--spacing-4); }
|
||||
.mt-6 { margin-top: var(--spacing-6); }
|
||||
.mt-8 { margin-top: var(--spacing-8); }
|
||||
|
||||
.mb-1 { margin-bottom: var(--spacing-1); }
|
||||
.mb-2 { margin-bottom: var(--spacing-2); }
|
||||
.mb-3 { margin-bottom: var(--spacing-3); }
|
||||
.mb-4 { margin-bottom: var(--spacing-4); }
|
||||
.mb-6 { margin-bottom: var(--spacing-6); }
|
||||
.mb-8 { margin-bottom: var(--spacing-8); }
|
||||
|
||||
.flex { display: flex; }
|
||||
.flex-col { flex-direction: column; }
|
||||
.items-center { align-items: center; }
|
||||
.justify-center { justify-content: center; }
|
||||
.justify-between { justify-content: space-between; }
|
||||
.gap-2 { gap: var(--spacing-2); }
|
||||
.gap-3 { gap: var(--spacing-3); }
|
||||
.gap-4 { gap: var(--spacing-4); }
|
||||
|
||||
.hidden { display: none; }
|
||||
.block { display: block; }
|
||||
|
||||
/* ===== 회의록 서비스 특화 스타일 ===== */
|
||||
|
||||
/* 상태 배지 - 회의록 전용 */
|
||||
.status-draft {
|
||||
background-color: var(--color-warning-light);
|
||||
color: var(--color-warning-dark);
|
||||
padding: var(--spacing-1) var(--spacing-3);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.status-verifying {
|
||||
background-color: var(--color-info-light);
|
||||
color: var(--color-info-dark);
|
||||
padding: var(--spacing-1) var(--spacing-3);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.status-confirmed {
|
||||
background-color: var(--color-success-light);
|
||||
color: var(--color-success-dark);
|
||||
padding: var(--spacing-1) var(--spacing-3);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
/* 전문용어 하이라이트 */
|
||||
.term-highlight {
|
||||
border-bottom: 2px dotted var(--color-primary-main);
|
||||
cursor: pointer;
|
||||
transition: color var(--transition-fast);
|
||||
}
|
||||
|
||||
.term-highlight:hover {
|
||||
color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
/* AI 제안 영역 */
|
||||
.ai-suggestion {
|
||||
background-color: var(--color-gray-50);
|
||||
border: 1px dashed var(--color-primary-main);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4);
|
||||
margin: var(--spacing-4) 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ai-suggestion::before {
|
||||
content: "✨ AI 제안";
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: var(--spacing-4);
|
||||
background-color: var(--color-white);
|
||||
padding: 0 var(--spacing-2);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: var(--font-weight-medium);
|
||||
color: var(--color-primary-main);
|
||||
}
|
||||
|
||||
/* Todo 카드 */
|
||||
.todo-card {
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--spacing-4);
|
||||
box-shadow: var(--shadow-sm);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.todo-card.priority-high {
|
||||
border-left: 4px solid var(--color-error-main);
|
||||
}
|
||||
|
||||
.todo-card.priority-medium {
|
||||
border-left: 4px solid var(--color-warning-main);
|
||||
}
|
||||
|
||||
.todo-progress {
|
||||
height: 4px;
|
||||
background-color: var(--color-gray-200);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
margin-top: var(--spacing-3);
|
||||
}
|
||||
|
||||
.todo-progress-bar {
|
||||
height: 100%;
|
||||
background-color: var(--color-primary-main);
|
||||
transition: width var(--transition-slow);
|
||||
}
|
||||
|
||||
/* 협업 커서 (예시) */
|
||||
.collab-cursor {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 20px;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.collab-cursor-label {
|
||||
position: absolute;
|
||||
top: -24px;
|
||||
left: 0;
|
||||
padding: var(--spacing-1) var(--spacing-2);
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: var(--font-size-caption);
|
||||
color: var(--color-white);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 반응형 유틸리티 */
|
||||
@media (max-width: 767px) {
|
||||
.hide-mobile { display: none !important; }
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 1023px) {
|
||||
.hide-tablet { display: none !important; }
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.hide-desktop { display: none !important; }
|
||||
}
|
||||
556
design-gappa/uiux/prototype/common.js
vendored
Normal file
556
design-gappa/uiux/prototype/common.js
vendored
Normal file
@ -0,0 +1,556 @@
|
||||
/*
|
||||
* 회의록 작성 및 공유 개선 서비스 - 공통 자바스크립트
|
||||
* 버전: 1.0
|
||||
* 작성일: 2025-10-20
|
||||
* 레퍼런스: 스타일 가이드 v1.0
|
||||
*/
|
||||
|
||||
// ===== 전역 상태 관리 =====
|
||||
const AppState = {
|
||||
currentUser: {
|
||||
id: 'user-001',
|
||||
name: '김민준',
|
||||
email: 'minjun.kim@example.com',
|
||||
avatar: 'https://ui-avatars.com/api/?name=김민준&background=00D9B1&color=fff'
|
||||
},
|
||||
meetings: [],
|
||||
todos: []
|
||||
};
|
||||
|
||||
// ===== 유틸리티 함수 =====
|
||||
|
||||
/**
|
||||
* DOM 준비 완료 시 콜백 실행
|
||||
*/
|
||||
function ready(callback) {
|
||||
if (document.readyState !== 'loading') {
|
||||
callback();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 날짜 포맷팅 (YYYY-MM-DD HH:mm)
|
||||
*/
|
||||
function formatDateTime(date) {
|
||||
const d = new Date(date);
|
||||
const year = d.getFullYear();
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
const hours = String(d.getHours()).padStart(2, '0');
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 상대 시간 표현 (방금 전, 3분 전, 2시간 전 등)
|
||||
*/
|
||||
function timeAgo(date) {
|
||||
const now = new Date();
|
||||
const past = new Date(date);
|
||||
const diff = Math.floor((now - past) / 1000); // 초 단위
|
||||
|
||||
if (diff < 60) return '방금 전';
|
||||
if (diff < 3600) return `${Math.floor(diff / 60)}분 전`;
|
||||
if (diff < 86400) return `${Math.floor(diff / 3600)}시간 전`;
|
||||
if (diff < 2592000) return `${Math.floor(diff / 86400)}일 전`;
|
||||
if (diff < 31536000) return `${Math.floor(diff / 2592000)}개월 전`;
|
||||
return `${Math.floor(diff / 31536000)}년 전`;
|
||||
}
|
||||
|
||||
/**
|
||||
* D-day 계산
|
||||
*/
|
||||
function getDday(targetDate) {
|
||||
const now = new Date();
|
||||
now.setHours(0, 0, 0, 0);
|
||||
const target = new Date(targetDate);
|
||||
target.setHours(0, 0, 0, 0);
|
||||
const diff = Math.floor((target - now) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diff === 0) return '오늘';
|
||||
if (diff > 0) return `D-${diff}`;
|
||||
return `D+${Math.abs(diff)} (지남)`;
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID 생성 (간단한 버전)
|
||||
*/
|
||||
function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
// ===== 모달 관리 =====
|
||||
const Modal = {
|
||||
/**
|
||||
* 모달 열기
|
||||
*/
|
||||
open(modalId) {
|
||||
const modal = document.getElementById(modalId);
|
||||
if (!modal) return;
|
||||
|
||||
modal.style.display = 'flex';
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// backdrop 클릭 시 모달 닫기
|
||||
const backdrop = modal.querySelector('.modal-backdrop');
|
||||
if (backdrop) {
|
||||
backdrop.addEventListener('click', (e) => {
|
||||
if (e.target === backdrop) {
|
||||
this.close(modalId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 닫기 버튼
|
||||
const closeBtn = modal.querySelector('.modal-close');
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', () => this.close(modalId));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 모달 닫기
|
||||
*/
|
||||
close(modalId) {
|
||||
const modal = document.getElementById(modalId);
|
||||
if (!modal) return;
|
||||
|
||||
modal.style.display = 'none';
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 토스트 알림 =====
|
||||
const Toast = {
|
||||
container: null,
|
||||
|
||||
/**
|
||||
* 토스트 컨테이너 초기화
|
||||
*/
|
||||
init() {
|
||||
if (!this.container) {
|
||||
this.container = document.createElement('div');
|
||||
this.container.className = 'toast-container';
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 토스트 표시
|
||||
*/
|
||||
show(message, type = 'info', duration = 4000) {
|
||||
this.init();
|
||||
|
||||
const toast = document.createElement('div');
|
||||
toast.className = `toast toast-${type}`;
|
||||
|
||||
const icons = {
|
||||
success: '✓',
|
||||
error: '✕',
|
||||
warning: '⚠',
|
||||
info: 'ℹ'
|
||||
};
|
||||
|
||||
toast.innerHTML = `
|
||||
<div class="toast-icon">${icons[type] || icons.info}</div>
|
||||
<div class="toast-content">
|
||||
<div class="toast-message">${message}</div>
|
||||
</div>
|
||||
<button class="toast-close" onclick="this.parentElement.remove()">×</button>
|
||||
`;
|
||||
|
||||
this.container.appendChild(toast);
|
||||
|
||||
// 자동 제거
|
||||
setTimeout(() => {
|
||||
if (toast.parentElement) {
|
||||
toast.remove();
|
||||
}
|
||||
}, duration);
|
||||
},
|
||||
|
||||
success(message) { this.show(message, 'success'); },
|
||||
error(message) { this.show(message, 'error'); },
|
||||
warning(message) { this.show(message, 'warning'); },
|
||||
info(message) { this.show(message, 'info'); }
|
||||
};
|
||||
|
||||
// ===== 로컬 스토리지 관리 =====
|
||||
const Storage = {
|
||||
/**
|
||||
* 데이터 저장
|
||||
*/
|
||||
set(key, value) {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Storage.set error:', e);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 데이터 가져오기
|
||||
*/
|
||||
get(key, defaultValue = null) {
|
||||
try {
|
||||
const item = localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : defaultValue;
|
||||
} catch (e) {
|
||||
console.error('Storage.get error:', e);
|
||||
return defaultValue;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 데이터 삭제
|
||||
*/
|
||||
remove(key) {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Storage.remove error:', e);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 전체 삭제
|
||||
*/
|
||||
clear() {
|
||||
try {
|
||||
localStorage.clear();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Storage.clear error:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ===== API 호출 (Mock) =====
|
||||
const API = {
|
||||
/**
|
||||
* 지연 시뮬레이션
|
||||
*/
|
||||
delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
},
|
||||
|
||||
/**
|
||||
* GET 요청 (Mock)
|
||||
*/
|
||||
async get(endpoint) {
|
||||
await this.delay(500);
|
||||
console.log(`API GET: ${endpoint}`);
|
||||
return { success: true, data: {} };
|
||||
},
|
||||
|
||||
/**
|
||||
* POST 요청 (Mock)
|
||||
*/
|
||||
async post(endpoint, data) {
|
||||
await this.delay(500);
|
||||
console.log(`API POST: ${endpoint}`, data);
|
||||
return { success: true, data: {} };
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT 요청 (Mock)
|
||||
*/
|
||||
async put(endpoint, data) {
|
||||
await this.delay(500);
|
||||
console.log(`API PUT: ${endpoint}`, data);
|
||||
return { success: true, data: {} };
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE 요청 (Mock)
|
||||
*/
|
||||
async delete(endpoint) {
|
||||
await this.delay(500);
|
||||
console.log(`API DELETE: ${endpoint}`);
|
||||
return { success: true };
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 페이지 네비게이션 =====
|
||||
function navigateTo(page) {
|
||||
// 실제로는 SPA 라우팅이나 페이지 이동 처리
|
||||
// 프로토타입에서는 링크 클릭으로 처리
|
||||
console.log(`Navigate to: ${page}`);
|
||||
window.location.href = page;
|
||||
}
|
||||
|
||||
// ===== 폼 유효성 검사 =====
|
||||
const Validator = {
|
||||
/**
|
||||
* 이메일 유효성 검사
|
||||
*/
|
||||
isEmail(email) {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
},
|
||||
|
||||
/**
|
||||
* 필수 입력 검사
|
||||
*/
|
||||
required(value) {
|
||||
return value !== null && value !== undefined && value.trim() !== '';
|
||||
},
|
||||
|
||||
/**
|
||||
* 최소 길이 검사
|
||||
*/
|
||||
minLength(value, min) {
|
||||
return value && value.length >= min;
|
||||
},
|
||||
|
||||
/**
|
||||
* 최대 길이 검사
|
||||
*/
|
||||
maxLength(value, max) {
|
||||
return value && value.length <= max;
|
||||
},
|
||||
|
||||
/**
|
||||
* 폼 필드 에러 표시
|
||||
*/
|
||||
showError(inputElement, message) {
|
||||
inputElement.classList.add('error');
|
||||
|
||||
let errorElement = inputElement.nextElementSibling;
|
||||
if (!errorElement || !errorElement.classList.contains('form-error')) {
|
||||
errorElement = document.createElement('span');
|
||||
errorElement.className = 'form-error';
|
||||
inputElement.parentElement.appendChild(errorElement);
|
||||
}
|
||||
errorElement.textContent = message;
|
||||
},
|
||||
|
||||
/**
|
||||
* 폼 필드 에러 제거
|
||||
*/
|
||||
clearError(inputElement) {
|
||||
inputElement.classList.remove('error');
|
||||
|
||||
const errorElement = inputElement.nextElementSibling;
|
||||
if (errorElement && errorElement.classList.contains('form-error')) {
|
||||
errorElement.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 로딩 상태 관리 =====
|
||||
const Loading = {
|
||||
/**
|
||||
* 로딩 표시
|
||||
*/
|
||||
show(target = 'body') {
|
||||
const element = typeof target === 'string' ? document.querySelector(target) : target;
|
||||
if (!element) return;
|
||||
|
||||
const spinner = document.createElement('div');
|
||||
spinner.className = 'spinner';
|
||||
spinner.id = 'global-spinner';
|
||||
spinner.style.position = 'fixed';
|
||||
spinner.style.top = '50%';
|
||||
spinner.style.left = '50%';
|
||||
spinner.style.transform = 'translate(-50%, -50%)';
|
||||
spinner.style.zIndex = '9999';
|
||||
|
||||
document.body.appendChild(spinner);
|
||||
},
|
||||
|
||||
/**
|
||||
* 로딩 숨김
|
||||
*/
|
||||
hide() {
|
||||
const spinner = document.getElementById('global-spinner');
|
||||
if (spinner) {
|
||||
spinner.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 회의록 관련 유틸리티 =====
|
||||
const MeetingUtils = {
|
||||
/**
|
||||
* 회의 상태 레이블
|
||||
*/
|
||||
getStatusLabel(status) {
|
||||
const labels = {
|
||||
'scheduled': '예정',
|
||||
'in_progress': '진행 중',
|
||||
'ended': '종료',
|
||||
'draft': '작성 중',
|
||||
'verifying': '검증 중',
|
||||
'confirmed': '확정됨'
|
||||
};
|
||||
return labels[status] || status;
|
||||
},
|
||||
|
||||
/**
|
||||
* 회의 상태 클래스
|
||||
*/
|
||||
getStatusClass(status) {
|
||||
const classes = {
|
||||
'draft': 'status-draft',
|
||||
'verifying': 'status-verifying',
|
||||
'confirmed': 'status-confirmed'
|
||||
};
|
||||
return classes[status] || 'badge-neutral';
|
||||
},
|
||||
|
||||
/**
|
||||
* Todo 우선순위 레이블
|
||||
*/
|
||||
getPriorityLabel(priority) {
|
||||
const labels = {
|
||||
'high': '높음',
|
||||
'medium': '보통',
|
||||
'low': '낮음'
|
||||
};
|
||||
return labels[priority] || priority;
|
||||
},
|
||||
|
||||
/**
|
||||
* Todo 상태 레이블
|
||||
*/
|
||||
getTodoStatusLabel(status) {
|
||||
const labels = {
|
||||
'todo': '시작 전',
|
||||
'in_progress': '진행 중',
|
||||
'done': '완료'
|
||||
};
|
||||
return labels[status] || status;
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 예시 데이터 생성 =====
|
||||
const MockData = {
|
||||
/**
|
||||
* 샘플 회의 데이터
|
||||
*/
|
||||
generateMeetings() {
|
||||
return [
|
||||
{
|
||||
id: 'm-001',
|
||||
title: '2025년 1분기 제품 기획 회의',
|
||||
date: '2025-10-25 14:00',
|
||||
location: '본사 2층 대회의실',
|
||||
status: 'scheduled',
|
||||
attendees: ['김민준', '박서연', '이준호', '최유진'],
|
||||
description: '신규 회의록 서비스 기획 논의'
|
||||
},
|
||||
{
|
||||
id: 'm-002',
|
||||
title: '주간 스크럼 회의',
|
||||
date: '2025-10-21 10:00',
|
||||
location: 'Zoom',
|
||||
status: 'confirmed',
|
||||
attendees: ['김민준', '이준호', '최유진'],
|
||||
description: '지난 주 진행 상황 공유 및 이번 주 계획'
|
||||
},
|
||||
{
|
||||
id: 'm-003',
|
||||
title: 'AI 기능 개선 회의',
|
||||
date: '2025-10-23 15:00',
|
||||
location: '본사 3층 소회의실',
|
||||
status: 'in_progress',
|
||||
attendees: ['박서연', '이준호'],
|
||||
description: 'LLM 기반 회의록 자동 작성 개선 방안'
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* 샘플 Todo 데이터
|
||||
*/
|
||||
generateTodos() {
|
||||
return [
|
||||
{
|
||||
id: 't-001',
|
||||
title: 'API 명세서 작성',
|
||||
assignee: '이준호',
|
||||
dueDate: '2025-10-25',
|
||||
priority: 'high',
|
||||
status: 'in_progress',
|
||||
progress: 60,
|
||||
meetingId: 'm-002'
|
||||
},
|
||||
{
|
||||
id: 't-002',
|
||||
title: 'UI 프로토타입 디자인',
|
||||
assignee: '최유진',
|
||||
dueDate: '2025-10-23',
|
||||
priority: 'medium',
|
||||
status: 'done',
|
||||
progress: 100,
|
||||
meetingId: 'm-002'
|
||||
},
|
||||
{
|
||||
id: 't-003',
|
||||
title: '데이터베이스 스키마 설계',
|
||||
assignee: '이준호',
|
||||
dueDate: '2025-10-28',
|
||||
priority: 'high',
|
||||
status: 'todo',
|
||||
progress: 0,
|
||||
meetingId: 'm-001'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
// ===== 초기화 =====
|
||||
ready(() => {
|
||||
console.log('Common.js loaded');
|
||||
|
||||
// 로컬 스토리지에서 상태 복원
|
||||
const savedMeetings = Storage.get('meetings');
|
||||
const savedTodos = Storage.get('todos');
|
||||
|
||||
if (!savedMeetings) {
|
||||
AppState.meetings = MockData.generateMeetings();
|
||||
Storage.set('meetings', AppState.meetings);
|
||||
} else {
|
||||
AppState.meetings = savedMeetings;
|
||||
}
|
||||
|
||||
if (!savedTodos) {
|
||||
AppState.todos = MockData.generateTodos();
|
||||
Storage.set('todos', AppState.todos);
|
||||
} else {
|
||||
AppState.todos = savedTodos;
|
||||
}
|
||||
|
||||
console.log('AppState initialized:', AppState);
|
||||
});
|
||||
|
||||
// ===== Export (전역 네임스페이스) =====
|
||||
window.MeetingApp = {
|
||||
AppState,
|
||||
Modal,
|
||||
Toast,
|
||||
Storage,
|
||||
API,
|
||||
Validator,
|
||||
Loading,
|
||||
MeetingUtils,
|
||||
MockData,
|
||||
navigateTo,
|
||||
formatDateTime,
|
||||
timeAgo,
|
||||
getDday,
|
||||
generateUUID,
|
||||
ready
|
||||
};
|
||||
212
design-gappa/uiux/prototype/test-report.md
Normal file
212
design-gappa/uiux/prototype/test-report.md
Normal file
@ -0,0 +1,212 @@
|
||||
# 프로토타입 테스트 결과 보고서
|
||||
|
||||
## 테스트 일시
|
||||
2025-10-20
|
||||
|
||||
## 테스트 범위
|
||||
전체 화면 플로우 테스트 (01-로그인 ~ 10-Todo관리)
|
||||
|
||||
## 테스트 결과 요약
|
||||
- 총 10개 화면 테스트
|
||||
- 정상 작동: 7개 화면
|
||||
- 버그 발견: 3개
|
||||
|
||||
---
|
||||
|
||||
## 발견된 버그
|
||||
|
||||
### 1. [HIGH] 대시보드 FAB 버튼 클릭 이벤트 미작동
|
||||
- **파일**: `02-대시보드.html`
|
||||
- **위치**: 라인 682 (JavaScript)
|
||||
- **증상**: FAB 버튼 클릭 시 회의 예약 페이지로 이동하지 않음
|
||||
- **원인**: JavaScript 이벤트 리스너가 제대로 바인딩되지 않음
|
||||
- **영향도**: 높음 (주요 네비게이션 기능)
|
||||
- **상태**: 미수정
|
||||
|
||||
### 2. [HIGH] 회의 예약 폼 제출 버그
|
||||
- **파일**: `03-회의예약.html`
|
||||
- **위치**: 라인 99 (form submit handler)
|
||||
- **증상**: 필수 필드를 모두 입력해도 폼 제출 시 페이지 이동하지 않음
|
||||
- **원인**: 폼 검증 로직 또는 이벤트 핸들러 문제
|
||||
- **영향도**: 높음 (핵심 기능)
|
||||
- **상태**: 미수정
|
||||
|
||||
### 3. [CRITICAL] 최종확정 페이지 링크 오류
|
||||
- **파일**: `08-최종확정.html`
|
||||
- **위치**: 라인 294
|
||||
- **증상**: 회의록 확정 후 "08-회의록공유.html"로 이동 시도하여 404 오류 발생
|
||||
- **원인**: 파일명 재정렬 후 링크 업데이트 누락
|
||||
- **수정 내용**: `'08-회의록공유.html'` → `'09-회의록공유.html'`
|
||||
- **영향도**: 매우 높음 (페이지 이동 불가)
|
||||
- **상태**: ✅ 수정 완료
|
||||
|
||||
### 4. [LOW] common.js 중복 로드 경고
|
||||
- **파일**: 모든 HTML 파일
|
||||
- **증상**: 콘솔에 "AppState가 이미 선언되었다"는 경고 메시지
|
||||
- **원인**: 페이지 전환 시 common.js가 중복으로 로드됨
|
||||
- **영향도**: 낮음 (기능에는 영향 없음)
|
||||
- **상태**: 미수정
|
||||
|
||||
---
|
||||
|
||||
## 정상 작동 화면
|
||||
|
||||
### ✅ 01-로그인.html
|
||||
- 로그인 폼 정상 작동
|
||||
- 인증 성공 시 대시보드로 정상 이동
|
||||
- Toast 메시지 정상 표시
|
||||
|
||||
### ✅ 04-템플릿선택.html
|
||||
- 템플릿 카드 선택 기능 정상
|
||||
- 선택 시 체크마크 표시 정상
|
||||
- "회의 시작하기" 버튼 활성화/비활성화 정상
|
||||
|
||||
### ✅ 05-회의진행.html
|
||||
- 회의 에디터 정상 표시
|
||||
- 녹음 타이머 정상 작동
|
||||
- 참석자/AI 제안 탭 전환 정상
|
||||
|
||||
### ✅ 06-검증완료.html
|
||||
- AI 검증 결과 정상 표시
|
||||
- 통계 카드 정상 렌더링
|
||||
- 발언 분포 그래프 정상
|
||||
|
||||
### ✅ 07-회의종료.html
|
||||
- 회의 요약 정보 정상 표시
|
||||
- 버튼 네비게이션 정상
|
||||
|
||||
### ✅ 08-최종확정.html
|
||||
- 회의록 미리보기 정상 표시
|
||||
- 필수 항목 체크리스트 기능 정상
|
||||
- 모든 항목 체크 시 확정 버튼 활성화 정상
|
||||
|
||||
### ✅ 09-회의록공유.html
|
||||
- 공유 링크 표시 정상
|
||||
- 공유 방식 선택 UI 정상
|
||||
- 참석자 목록 표시 정상
|
||||
|
||||
### ✅ 10-Todo관리.html
|
||||
- 칸반 보드 레이아웃 정상
|
||||
- Todo 카드 표시 정상
|
||||
- 담당자 아바타 표시 정상
|
||||
|
||||
---
|
||||
|
||||
## 스크린샷
|
||||
테스트 중 캡처한 스크린샷은 `.playwright-mcp/screenshots/` 디렉토리에 저장됨:
|
||||
- 01-login.png
|
||||
- 02-dashboard.png
|
||||
- 03-meeting-reserve.png
|
||||
- 03-form-filled.png
|
||||
- 04-template-selection.png
|
||||
- 05-meeting-progress.png
|
||||
- 06-verification-complete.png
|
||||
- 07-meeting-end.png
|
||||
- 08-final-confirmation.png
|
||||
- 09-meeting-share.png
|
||||
- 10-todo-management.png
|
||||
|
||||
---
|
||||
|
||||
## 다음 작업
|
||||
1. 대시보드 FAB 버튼 이벤트 핸들러 수정
|
||||
2. 회의 예약 폼 제출 로직 수정
|
||||
3. common.js 중복 로드 문제 해결 (선택적)
|
||||
4. 전체 재테스트
|
||||
|
||||
---
|
||||
|
||||
## 테스트 환경
|
||||
- 브라우저: Playwright (Chromium)
|
||||
- 운영체제: Windows 10
|
||||
- 테스트 도구: Claude Code + Playwright MCP
|
||||
|
||||
---
|
||||
|
||||
## 개선 사항 (2025-10-20 추가)
|
||||
|
||||
### Todo-회의록 자동 링크 기능 개선
|
||||
|
||||
**문제점**: 회의록과 업무이력(Todo)의 자동 링크가 명확하게 표현되지 않음
|
||||
- Todo 관리 화면에서 어떤 회의에서 생성되었는지 알 수 없음
|
||||
- 회의록 공유 화면에서 생성된 Todo 목록과 진행 상황이 표시되지 않음
|
||||
- 양방향 연결이 누락됨
|
||||
|
||||
**개선 내용**:
|
||||
|
||||
1. **10-Todo관리.html 개선**
|
||||
- 모든 Todo 카드에 "출처 회의록" 정보 추가
|
||||
- 회의 제목, 날짜 표시
|
||||
- 회의록으로 이동하는 클릭 가능한 링크 추가
|
||||
- 칸반 보드 5개, 리스트 뷰 3개 항목 모두 적용
|
||||
|
||||
2. **09-회의록공유.html 개선**
|
||||
- "생성된 Todo" 섹션 추가
|
||||
- 3개 Todo 항목 표시 (제목, 담당자, 마감일)
|
||||
- 진행 상황 표시 (진행중 60%, 완료 100%, 지연 30%)
|
||||
- "Todo 보기" 링크로 Todo 관리 페이지 연결
|
||||
|
||||
**개선 효과**:
|
||||
- ✅ Todo와 회의록 간 양방향 연결 구현
|
||||
- ✅ 업무 이력 추적 가능성 향상
|
||||
- ✅ 유저스토리 차별화 포인트 명확하게 구현
|
||||
- UFR-TODO-010: "관련 회의록 링크 (섹션 위치 포함)"
|
||||
- UFR-RAG-020: "과거 회의록 및 업무 이력 연결"
|
||||
- ✅ 회의 결과물의 실행 상황 실시간 파악 가능
|
||||
|
||||
**변경된 파일**:
|
||||
- design/uiux/prototype/10-Todo관리.html (8개 위치 수정)
|
||||
- design/uiux/prototype/09-회의록공유.html (1개 섹션 추가)
|
||||
|
||||
---
|
||||
|
||||
### 회의 진행 중 관련 자료 실시간 제공 기능 추가
|
||||
|
||||
**문제점**: 회의 진행 중 현재 논의 주제와 관련된 과거 회의록 및 업무이력 정보 부재
|
||||
- 참석자가 이전 논의 맥락을 알 수 없음
|
||||
- 관련 Todo 진행 상황을 실시간으로 파악할 수 없음
|
||||
- 중복 논의 또는 누락된 사항 발생 가능
|
||||
|
||||
**개선 내용**:
|
||||
|
||||
1. **05-회의진행.html 사이드 패널 개선**
|
||||
- "관련 자료" 탭 신규 추가 (참석자, AI 제안 탭에 이어 3번째 탭)
|
||||
- 실시간 컨텍스트 기반 정보 제공
|
||||
|
||||
2. **관련 회의록 섹션 (3건 표시)**
|
||||
- 회의 제목, 날짜, 관련도 점수 표시
|
||||
- 회의 요약 미리보기
|
||||
- 공통 키워드 하이라이트
|
||||
- 클릭 시 새 탭에서 회의록 열기
|
||||
- 예시:
|
||||
- "2024년 4분기 제품 기획 회의" (관련도 92%)
|
||||
- "API 설계 리뷰 회의" (관련도 78%)
|
||||
- "주간 진행 상황 점검" (관련도 71%)
|
||||
|
||||
3. **관련 업무이력 섹션 (2건 표시)**
|
||||
- Todo 제목, 담당자, 마감일, 진행률 표시
|
||||
- 실시간 상태 배지 (진행중/지연/완료)
|
||||
- 출처 회의록 정보 표시
|
||||
- 관련 사유 설명
|
||||
- 클릭 시 Todo 관리 페이지로 이동
|
||||
- 예시:
|
||||
- "API 명세서 작성" (담당: 이준호, 진행중 60%)
|
||||
- "예산 편성안 검토" (담당: 박서연, 지연 30%)
|
||||
|
||||
**개선 효과**:
|
||||
- ✅ 회의 중 과거 맥락 실시간 파악 가능
|
||||
- ✅ 중복 논의 방지 및 연속성 확보
|
||||
- ✅ 관련 Todo 진행 상황 즉시 확인 가능
|
||||
- ✅ 유저스토리 차별화 포인트 명확하게 구현
|
||||
- UFR-AI-040: "관련 회의록 자동 연결" 구현
|
||||
- UFR-RAG-020: "관련 회의록과 업무 이력을 바탕으로 실용적인 정보 제공" 구현
|
||||
- UFR-RAG-030: "관련 문서 자동 연결" 구현
|
||||
- ✅ AI 기반 지능형 회의 진행 지원
|
||||
|
||||
**기술적 구현**:
|
||||
- RAG(Retrieval-Augmented Generation) 시스템 시뮬레이션
|
||||
- 관련도 점수 알고리즘 (벡터 유사도 기반)
|
||||
- 실시간 컨텍스트 분석 및 추천
|
||||
|
||||
**변경된 파일**:
|
||||
- design/uiux/prototype/05-회의진행.html (1개 탭 추가, 관련 자료 섹션 구현)
|
||||
1007
design-gappa/userstory.md
Normal file
1007
design-gappa/userstory.md
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user