joy01 / index.html
nkjoy's picture
<?php // config + ์ผ์ • ์ €์žฅ ์ฒ˜๋ฆฌ $pdo = new PDO("mysql:host=localhost;dbname=calendar_db;charset=utf8mb4", "root", "wjs3603825!", [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]); // ์ผ์ • ์ €์žฅ ์ฒ˜๋ฆฌ if ($_SERVER['REQUEST_METHOD'] === 'POST') { header('Content-Type: application/json'); $data = json_decode(file_get_contents('php://input'), true); if (!$data || empty($data['event-title']) || empty($data['event-date'])) { echo json_encode(['success' => false, 'error' => '์ œ๋ชฉ ๋˜๋Š” ๋‚ ์งœ ๋ˆ„๋ฝ']); exit; } // ๊ธˆ์•ก ์ฒ˜๋ฆฌ: ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ฉด 0 $amount = isset($data['event-amount']) && is_numeric($data['event-amount']) ? (int)$data['event-amount'] : 0; // ๊ธˆ์•ก๊นŒ์ง€ ํฌํ•จํ•ด์„œ INSERT $stmt = $pdo->prepare("INSERT INTO events (title, event_date, org, description, amount) VALUES (?, ?, ?, ?, ?)"); $success = $stmt->execute([ $data['event-title'], $data['event-date'], $data['event-org'], $data['event-desc'], $amount ]); echo json_encode(['success' => $success]); exit; } // ์˜ค๋Š˜ ์ผ์ • ๋ถˆ๋Ÿฌ์˜ค๊ธฐ $today = date('Y-m-d'); $stmt = $pdo->prepare("SELECT title, org, description, amount FROM events WHERE event_date = ?"); $stmt->execute([$today]); $today_events = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($today_events as &$event) { $event['icon'] = match ($event['org']) { 'uniedu' => '๐ŸŽ“', 'mod' => '๐Ÿ›ก๏ธ', 'peacemaker' => '๐Ÿ•Š๏ธ', 'hyundai' => '๐Ÿญ', 'bus' => '๐ŸšŒ', 'camp' => '๐Ÿ•๏ธ', default => '' }; } ?> <!-- HTML ์‹œ์ž‘ --> <!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>์ด๋‹ค์€ ์บ˜๋ฆฐ๋”</title> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet"> <style> :root { --uniedu: #2b7de9; --mod: #e63946; --peacemaker: #2a9d8f; --hyundai: #f77f00; --bus: #00b4d8; --camp: #7209b7; --shadow: 0 10px 30px rgba(0,0,0,0.1); --transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); --radius: 12px; --sunday: #ff5a5f; --monday: #4a6bdf; --tuesday: #2a9d8f; --wednesday: #f77f00; --thursday: #7209b7; --friday: #2b7de9; --saturday: #9c4dcc; } * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif; } body { background-color: #f8fafc; color: #1e293b; line-height: 1.6; padding: 0.5rem; font-size: 14px; } .calendar-container { max-width: 1400px; margin: 2rem auto; padding: 2.5rem; background: white; border-radius: var(--radius); box-shadow: var(--shadow); transition: var(--transition); backdrop-filter: blur(10px); background: rgba(255, 255, 255, 0.9); } .calendar-container:hover { box-shadow: 0 15px 35px rgba(0,0,0,0.15); transform: translateY(-2px); } .calendar-header { display: flex; flex-direction: column; gap: 1.5rem; margin-bottom: 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid #f1f5f9; } @media (min-width: 768px) { .calendar-header { flex-direction: row; justify-content: space-between; align-items: center; } } .calendar-title { font-size: 2.2rem; font-weight: 900; background: linear-gradient(90deg, #2b7de9, #00b4d8); -webkit-background-clip: text; background-clip: text; color: transparent; display: inline-block; letter-spacing: -0.5px; text-shadow: 0 2px 4px rgba(0,0,0,0.05); } .calendar-nav { display: flex; gap: 1.2rem; align-items: center; } .nav-button { padding: 0.8rem 1.6rem; background: white; border: none; border-radius: 12px; cursor: pointer; transition: var(--transition); font-weight: 600; color: white; box-shadow: 0 4px 6px rgba(0,0,0,0.1); background: linear-gradient(135deg, #2b7de9, #00b4d8); } .nav-button:hover { transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0,0,0,0.15); opacity: 0.9; } .current-month { font-size: 1.6rem; font-weight: 700; color: #334155; min-width: 160px; text-align: center; text-shadow: 0 2px 4px rgba(0,0,0,0.05); } .calendar-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 12px; } .day-header { text-align: center; padding: 0.8rem 0.5rem; font-weight: 700; color: #64748b; background: white; border-radius: var(--radius); text-transform: uppercase; font-size: 0.85rem; letter-spacing: 0.3px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); position: relative; overflow: hidden; border-bottom: 2px solid #f1f5f9; } .day-header:nth-child(1) { /* ์ผ */ color: var(--sunday); border-bottom: 3px solid var(--sunday); } .day-header:nth-child(2) { /* ์›” */ color: var(--monday); border-bottom: 3px solid var(--monday); } .day-header:nth-child(3) { /* ํ™” */ color: var(--tuesday); border-bottom: 3px solid var(--tuesday); } .day-header:nth-child(4) { /* ์ˆ˜ */ color: var(--wednesday); border-bottom: 3px solid var(--wednesday); } .day-header:nth-child(5) { /* ๋ชฉ */ color: var(--thursday); border-bottom: 3px solid var(--thursday); } .day-header:nth-child(6) { /* ๊ธˆ */ color: var(--friday); border-bottom: 3px solid var(--friday); } .day-header:nth-child(7) { /* ํ†  */ color: var(--saturday); border-bottom: 3px solid var(--saturday); } .day-cell { min-height: 100px; padding: 0.5rem; background: white; border-radius: var(--radius); transition: var(--transition); box-shadow: 0 2px 4px rgba(0,0,0,0.05); position: relative; overflow: hidden; } .day-cell.sunday { background-color: rgba(255, 90, 95, 0.1); } .day-cell.saturday { background-color: rgba(74, 107, 223, 0.1); } .day-cell:hover { box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08); transform: translateY(-2px); } .day-number { font-weight: 700; margin-bottom: 0.5rem; color: #334155; font-size: 1.1rem; display: inline-block; padding: 2px 6px; border-radius: 6px; background: rgba(0,0,0,0.02); } .day-today .day-number { background: linear-gradient(135deg, #2b7de9, #00b4d8); color: white; font-weight: 800; padding: 4px 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .event-card { padding: 0.4rem; margin-bottom: 0.4rem; font-size: 0.8rem; border-radius: 6px; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; transition: var(--transition); display: flex; align-items: center; gap: 4px; box-shadow: 0 1px 2px rgba(0,0,0,0.05); } .event-card:hover { transform: translateY(-3px); box-shadow: 0 2px 8px rgba(0,0,0,0.1); } .event-uniedu { border-left: 3px solid var(--uniedu); background-color: rgba(43, 125, 233, 0.08); } .event-mod { border-left: 3px solid var(--mod); background-color: rgba(230, 57, 70, 0.08); } .event-peacemaker { border-left: 3px solid var(--peacemaker); background-color: rgba(42, 157, 143, 0.08); } .event-hyundai { border-left: 3px solid var(--hyundai); background-color: rgba(247, 127, 0, 0.08); } .event-bus { border-left: 3px solid var(--bus); background-color: rgba(0, 180, 216, 0.08); } .event-camp { border-left: 3px solid var(--camp); background-color: rgba(114, 9, 183, 0.08); } .calendar-actions { display: flex; justify-content: flex-end; gap: 1rem; margin-top: 1.5rem; } .action-button { padding: 0.7rem 1.5rem; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: all 0.2s; } .print-button { background: #2b7de9; color: white; } .print-button:hover { background: #1a5fb4; } .add-button { background: #2a9d8f; color: white; } .add-button:hover { background: #21867a; } /* ๋ชจ๋‹ฌ ์Šคํƒ€์ผ */ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 1000; justify-content: center; align-items: center; } .modal-content { background: white; padding: 1.5rem; border-radius: 8px; width: 95%; max-width: 100%; margin: 0 10px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); } @media (max-width: 768px) { .calendar-container { padding: 1rem; margin: 1rem auto; } .calendar-grid { gap: 6px; } .day-cell { min-height: 70px; padding: 0.3rem; } .event-card { font-size: 0.7rem; padding: 0.3rem; } .calendar-title { font-size: 1.3rem; } .current-month { font-size: 1.1rem; } .nav-button { padding: 0.4rem 0.8rem; font-size: 0.8rem; } .calendar-actions { margin-top: 1rem; } .action-button { padding: 0.5rem 1rem; font-size: 0.8rem; } } /* ์˜ค๋Š˜ ์ผ์ • ์„น์…˜ ์Šคํƒ€์ผ */ .today-events-container { max-width: 1400px; margin: 0 auto 2rem; padding: 1.5rem; background: white; border-radius: var(--radius); box-shadow: var(--shadow); } .today-events-title { font-size: 1.5rem; font-weight: 700; margin-bottom: 1rem; color: #334155; padding-bottom: 0.5rem; border-bottom: 1px solid #f1f5f9; } .today-events-list { display: grid; gap: 0.8rem; } .today-event { padding: 1rem; border-radius: 8px; display: flex; align-items: center; gap: 1rem; transition: var(--transition); } .today-event:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); } .today-event-icon { font-size: 1.5rem; flex-shrink: 0; } .today-event-content { flex-grow: 1; } .today-event-title { font-weight: 600; margin-bottom: 0.3rem; } .today-event-org { font-size: 0.8rem; color: #64748b; } .today-event-desc { font-size: 0.9rem; margin-top: 0.5rem; color: #475569; } .no-events { text-align: center; color: #94a3b8; padding: 1rem; } @media (max-width: 768px) { .today-events-container { padding: 1rem; } .today-events-title { font-size: 1.2rem; } .today-event { padding: 0.8rem; } } @media print { body * { visibility: hidden; } .print-area, .print-area * { visibility: visible; } .print-area { position: absolute; left: 0; top: 0; width: 100%; } } </style> </head> <body> <div class="calendar-container"> <div class="calendar-header"> <div class="calendar-title"> ์ผ์ • ์บ˜๋ฆฐ๋”</div> <div class="calendar-nav"> <button class="nav-button" id="prev-month">์ด์ „ ๋‹ฌ</button> <div class="current-month" id="current-month">2025๋…„ 6์›”</div> <button class="nav-button" id="next-month">๋‹ค์Œ ๋‹ฌ</button> </div> </div> <div class="calendar-grid" id="calendar-grid"> <!-- ์ผ์š”์ผ๋ถ€ํ„ฐ ํ† ์š”์ผ๊นŒ์ง€ ์š”์ผ ํ—ค๋” --> <div class="day-header">์ผ</div> <div class="day-header">์›”</div> <div class="day-header">ํ™”</div> <div class="day-header">์ˆ˜</div> <div class="day-header">๋ชฉ</div> <div class="day-header">๊ธˆ</div> <div class="day-header">ํ† </div> <!-- ๋‹ฌ๋ ฅ ์ผ์ž๋Š” JavaScript๋กœ ๋™์  ์ƒ์„ฑ --> </div> <div class="calendar-actions"> <button class="action-button add-button" id="add-event">์ผ์ • ์ถ”๊ฐ€</button> <button class="action-button print-button" id="print-calendar">์ธ์‡„ํ•˜๊ธฐ</button> </div> </div> <div class="today-events-container"> <h3 class="today-events-title">์˜ค๋Š˜์˜ ์ผ์ •</h3> <div class="today-events-list" id="today-events"> <!-- ์˜ค๋Š˜ ์ผ์ •์€ JavaScript๋กœ ๋™์  ์ƒ์„ฑ --> <div class="no-events">์˜ค๋Š˜ ์˜ˆ์ •๋œ ์ผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค</div> </div> </div> <!-- ์ด๋ฒคํŠธ ์ถ”๊ฐ€/์ˆ˜์ • ๋ชจ๋‹ฌ --> <div class="modal" id="event-modal"> <div class="modal-content"> <h3>์ผ์ • ์ถ”๊ฐ€</h3> <form id="event-form"> <div style="margin-bottom: 1rem;"> <label for="event-title">์ œ๋ชฉ</label> <input type="text" id="event-title" required style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;"> </div> <div style="margin-bottom: 1rem;"> <label for="event-date">๋‚ ์งœ</label> <input type="date" id="event-date" required style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;"> </div> <div style="margin-bottom: 1rem;"> <label for="event-org">๊ธฐ๊ด€</label> <select id="event-org" style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;"> <option value="uniedu">๐ŸŽ“ ํ†ต์ผ๊ต์œก์›</option> <option value="mod">๐Ÿ›ก๏ธ ๊ตญ๋ฐฉ๋ถ€</option> <option value="peacemaker">๐Ÿ•Š๏ธ ํ”ผ์Šค๋ฉ”์ดํฌ</option> <option value="hyundai">๐Ÿ•๏ธ ํ•™์บ </option> <!-- ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„ --> <option value="bus">๐ŸšŒ ๋ฒ„์Šค๊ฐ•์˜</option> <option value="camp">๐Ÿ“Œ ๊ธฐํƒ€</option> <!-- ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„ --> </select> </div> <div style="margin-bottom: 1.5rem;"> <label for="event-desc">์„ค๋ช…</label> <textarea id="event-desc" rows="3" style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;"></textarea> </div> <div style="margin-bottom: 1rem;"> <label for="event-amount">๊ธˆ์•ก (์›)</label> <input type="number" id="event-amount" style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;" min="0" /> </div> <div style="display: flex; justify-content: flex-end; gap: 0.5rem;"> <button type="button" id="cancel-event" style="padding: 0.5rem 1rem; background: #f8f9fa; border: 1px solid #ddd; border-radius: 4px;">์ทจ์†Œ</button> <button type="submit" style="padding: 0.5rem 1rem; background: #2b7de9; color: white; border: none; border-radius: 4px;">์ €์žฅ</button> </div> </form> </div> </div> <script> const todayEventData = <?php echo json_encode($today_events); ?>; </script> <script> // ํ˜„์žฌ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ๋‹ฌ๋ ฅ ์ƒ์„ฑ document.addEventListener('DOMContentLoaded', function() { const currentDate = new Date(); renderCalendar(currentDate); showDayEvents(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()); // ๋ชจ๋‹ฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ const modal = document.getElementById('event-modal'); document.getElementById('add-event').addEventListener('click', () => { modal.style.display = 'flex'; }); document.getElementById('cancel-event').addEventListener('click', () => { modal.style.display = 'none'; document.getElementById('event-form').reset(); }); // Handle form submission document.getElementById('event-form').addEventListener('submit', function(e) { e.preventDefault(); const formData = { 'event-title': document.getElementById('event-title').value, 'event-date': document.getElementById('event-date').value, 'event-org': document.getElementById('event-org').value, 'event-desc': document.getElementById('event-desc').value }; fetch('<?php echo $_SERVER['PHP_SELF']; ?>', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData) }) .then(response => response.json()) .then(data => { if (data.success) { modal.style.display = 'none'; this.reset(); renderCalendar(currentDate); renderTodayEvents(currentDate); } }); }); // ๊ธˆ์•ก ์ž…๋ ฅ ๋ถ€๋ถ„ const formData = { 'event-title': document.getElementById('event-title').value.trim(), 'event-date': document.getElementById('event-date').value, 'event-org': document.getElementById('event-org').value, 'event-desc': document.getElementById('event-desc').value.trim(), 'event-amount': parseInt(document.getElementById('event-amount').value || '0', 10) }; // ์ธ์‡„ ๋ฒ„ํŠผ document.getElementById('print-calendar').addEventListener('click', () => { window.print(); }); // ์›” ์ด๋™ ๋ฒ„ํŠผ document.getElementById('prev-month').addEventListener('click', () => { currentDate.setMonth(currentDate.getMonth() - 1); renderCalendar(currentDate); }); document.getElementById('next-month').addEventListener('click', () => { currentDate.setMonth(currentDate.getMonth() + 1); renderCalendar(currentDate); renderTodayEvents(currentDate); }); }); function showDayEvents(year, month, day) { const formattedDate = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; fetch(`get_events.php?date=${formattedDate}`) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.json(); }) .then(events => { const todayEvents = document.getElementById('today-events'); todayEvents.innerHTML = ''; // Update title to show selected date document.querySelector('.today-events-title').textContent = `${year}๋…„ ${month + 1}์›” ${day}์ผ ์ผ์ •`; if (events.length === 0) { todayEvents.innerHTML = '<div class="no-events">์˜ˆ์ •๋œ ์ผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค</div>'; return; } events.forEach(event => { const eventEl = document.createElement('div'); eventEl.className = `today-event event-${event.org}`; eventEl.innerHTML = ` <div class="today-event-icon">${getOrgIcon(event.org)}</div> <div class="today-event-content"> <div class="today-event-title">${event.title}</div> <div class="today-event-org">${getOrgName(event.org)}</div> <div class="today-event-desc"> ${event.description} ${event.amount && parseInt(event.amount) > 0 ? `<br><strong>๊ธˆ์•ก:</strong> ${parseInt(event.amount).toLocaleString()}์›` : ''} </div> </div> `; todayEvents.appendChild(eventEl); }); }) .catch(error => { console.error('Error fetching events:', error); }); } function renderTodayEvents(date) { const todayEvents = document.getElementById('today-events'); todayEvents.innerHTML = ''; // PHP์—์„œ ๊ฐ€์ ธ์˜จ ์˜ค๋Š˜์˜ ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ const events = <?php echo json_encode($today_events); ?>; if (events.length === 0) { todayEvents.innerHTML = '<div class="no-events">์˜ค๋Š˜ ์˜ˆ์ •๋œ ์ผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค</div>'; return; } events.forEach(event => { const eventEl = document.createElement('div'); eventEl.className = `today-event event-${event.org}`; eventEl.innerHTML = ` <div class="today-event-icon">${event.icon}</div> <div class="today-event-content"> <div class="today-event-title">${event.title}</div> <div class="today-event-org">${getOrgName(event.org)}</div> <div class="today-event-desc">${event.desc}</div> </div> `; todayEvents.appendChild(eventEl); }); } function getOrgIcon(org) { const orgIcons = { 'uniedu': '๐ŸŽ“', 'mod': '๐Ÿ›ก๏ธ', 'peacemaker': '๐Ÿ•Š๏ธ', 'hyundai': '๐Ÿ•๏ธ', // ํ˜„๋Œ€์•„์‚ฐ โ†’ ํ•™์บ  (์•„์ด์ฝ˜ ๋ณ€๊ฒฝ) 'bus': '๐ŸšŒ', 'camp': '๐Ÿ“Œ' // ํ•™์บ  โ†’ ๊ธฐํƒ€ }; return orgIcons[org] || ''; } function getOrgName(org) { const orgNames = { 'uniedu': 'ํ†ต์ผ๊ต์œก์›', 'mod': '๊ตญ๋ฐฉ๋ถ€', 'peacemaker': 'ํ”ผ์Šค๋ฉ”์ดํฌ', 'hyundai': 'ํ•™์บ ', // ํ˜„๋Œ€์•„์‚ฐ โ†’ ํ•™์บ  'bus': '๋ฒ„์Šค๊ฐ•์˜', 'camp': '๊ธฐํƒ€' // ํ•™์บ  โ†’ ๊ธฐํƒ€ }; return orgNames[org] || ''; } function renderCalendar(date) { const year = date.getFullYear(); const month = date.getMonth(); // ํ˜„์žฌ ์›” ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ document.getElementById('current-month').textContent = `${year}๋…„ ${month + 1}์›”`; // ๋‹ฌ๋ ฅ ๊ทธ๋ฆฌ๋“œ ์ดˆ๊ธฐํ™” const calendarGrid = document.getElementById('calendar-grid'); // ์š”์ผ ํ—ค๋”๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋“  ์ž์‹ ์ œ๊ฑฐ while (calendarGrid.children.length > 7) { calendarGrid.removeChild(calendarGrid.lastChild); } // ํ˜„์žฌ ์›”์˜ ์ฒซ์งธ ๋‚ ๊ณผ ๋งˆ์ง€๋ง‰ ๋‚  const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); // ์ฒซ์งธ ๋‚ ์˜ ์š”์ผ (0: ์ผ์š”์ผ) const firstDayOfWeek = firstDay.getDay(); // ๋‹ฌ๋ ฅ ์‹œ์ž‘ ์ „ ๋นˆ ์นธ ์ถ”๊ฐ€ for (let i = 0; i < firstDayOfWeek; i++) { const emptyCell = document.createElement('div'); emptyCell.className = 'day-cell'; calendarGrid.appendChild(emptyCell); } // ๋‚ ์งœ ์นธ ์ถ”๊ฐ€ for (let day = 1; day <= lastDay.getDate(); day++) { const dayCell = document.createElement('div'); dayCell.className = 'day-cell'; // Add Sunday/Saturday classes const dayOfWeek = new Date(year, month, day).getDay(); if (dayOfWeek === 0) { dayCell.classList.add('sunday'); } else if (dayOfWeek === 6) { dayCell.classList.add('saturday'); } const dayNumber = document.createElement('div'); dayNumber.className = 'day-number'; dayNumber.textContent = day; // ์˜ค๋Š˜ ๋‚ ์งœ ์ฒดํฌ const today = new Date(); if (today.getFullYear() === year && today.getMonth() === month && today.getDate() === day) { dayCell.classList.add('day-today'); } dayCell.appendChild(dayNumber); dayCell.addEventListener('click', () => { showDayEvents(year, month, day); }); // ๋‚ ์งœ ํ˜•์‹ ๋งž์ถ”๊ธฐ (YYYY-MM-DD) const formattedDate = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; // ํ•ด๋‹น ๋‚ ์งœ์˜ ์ด๋ฒคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ fetch(`get_events.php?date=${formattedDate}`) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.json(); }) .then(events => { if (events && events.length > 0) { events.forEach(event => { const eventEl = document.createElement('div'); eventEl.className = `event-card event-${event.org}`; eventEl.textContent = `${getOrgIcon(event.org)} ${event.title}`; dayCell.appendChild(eventEl); }); } }) .catch(error => { console.error('Error fetching events:', error); }); calendarGrid.appendChild(dayCell); } } </script> </body> </html> - Follow Up Deployment
55ea541 verified
<?php
$pdo = new PDO("mysql:host=localhost;dbname=calendar_db;charset=utf8mb4", "root", "wjs3603825!", [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || !isset($data['eventId'])) {
echo json_encode(['success' => false, 'error' => 'Invalid request']);
exit;
}
$stmt = $pdo->prepare("UPDATE events SET paid = 1, paid_date = NOW() WHERE id = ?");
$success = $stmt->execute([$data['eventId']]);
echo json_encode(['success' => $success]);
?>
<?php
$pdo = new PDO("mysql:host=localhost;dbname=calendar_db;charset=utf8mb4", "root", "wjs3603825!", [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
// Get all events with payment amounts
$stmt = $pdo->query("SELECT * FROM events WHERE amount > 0 ORDER BY event_date DESC");
$events = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Calculate total amounts
$totalAmount = 0;
$paidAmount = 0;
$unpaidAmount = 0;
foreach ($events as $event) {
$totalAmount += $event['amount'];
if ($event['paid']) {
$paidAmount += $event['amount'];
} else {
$unpaidAmount += $event['amount'];
}
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>๊ฐ•์˜๋ฃŒ ์ž…๊ธˆ ํ™•์ธ ์‹œ์Šคํ…œ</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Noto Sans KR', sans-serif;
}
body {
background-color: #f8fafc;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
h1 {
color: #2b7de9;
margin-bottom: 20px;
font-size: 2rem;
}
.summary-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.summary-card {
padding: 20px;
border-radius: 8px;
text-align: center;
color: white;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.summary-card.total {
background: linear-gradient(135deg, #2b7de9, #00b4d8);
}
.summary-card.paid {
background: linear-gradient(135deg, #2a9d8f, #21867a);
}
.summary-card.unpaid {
background: linear-gradient(135deg, #e63946, #d90429);
}
.summary-card h3 {
font-size: 1.2rem;
margin-bottom: 10px;
}
.summary-card p {
font-size: 1.8rem;
font-weight: 700;
}
.search-box {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
.search-box input {
flex: 1;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
.search-box button {
padding: 12px 20px;
background: #2b7de9;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.payment-table {
width: 100%;
border-collapse: collapse;
}
.payment-table th, .payment-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.payment-table th {
background: #f5f9ff;
font-weight: 600;
color: #2b7de9;
}
.payment-status {
padding: 6px 12px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
}
.status-paid {
background: #e6f7f0;
color: #2a9d8f;
}
.status-unpaid {
background: #ffebee;
color: #e63946;
}
.action-btn {
padding: 8px 12px;
border-radius: 6px;
border: none;
cursor: pointer;
font-weight: 500;
}
.confirm-btn {
background: #2b7de9;
color: white;
}
.receipt-btn {
background: #2a9d8f;
color: white;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 30px;
gap: 10px;
}
.pagination button {
padding: 10px 15px;
border: 1px solid #ddd;
background: white;
border-radius: 6px;
cursor: pointer;
}
.pagination button.active {
background: #2b7de9;
color: white;
border-color: #2b7de9;
}
</style>
</head>
<body>
<div class="container">
<h1>๊ฐ•์˜๋ฃŒ ์ž…๊ธˆ ํ™•์ธ ์‹œ์Šคํ…œ</h1>
<div class="summary-cards">
<div class="summary-card total">
<h3>์ด ๊ฐ•์˜๋ฃŒ</h3>
<p><?= number_format($totalAmount) ?>์›</p>
</div>
<div class="summary-card paid">
<h3>์ž…๊ธˆ ์™„๋ฃŒ</h3>
<p><?= number_format($paidAmount) ?>์›</p>
</div>
<div class="summary-card unpaid">
<h3>๋ฏธ์ž…๊ธˆ</h3>
<p><?= number_format($unpaidAmount) ?>์›</p>
</div>
</div>
<div class="search-box">
<input type="text" id="search-input" placeholder="๊ฐ•์‚ฌ๋ช… ๋˜๋Š” ๊ฐ•์˜๋ช…์œผ๋กœ ๊ฒ€์ƒ‰">
<button id="search-btn">๊ฒ€์ƒ‰</button>
</div>
<table class="payment-table">
<thead>
<tr>
<th>๊ฐ•์˜์ผ์ž</th>
<th>๊ฐ•์‚ฌ๋ช…</th>
<th>๊ฐ•์˜๋ช…</th>
<th>๊ธฐ๊ด€</th>
<th>๊ฐ•์˜๋ฃŒ</th>
<th>์ž…๊ธˆ์ƒํƒœ</th>
<th>๊ด€๋ฆฌ</th>
</tr>
</thead>
<tbody id="payment-list">
<?php foreach ($events as $event): ?>
<tr>
<td><?= $event['event_date'] ?></td>
<td><?= $event['lecturer'] ?? 'N/A' ?></td>
<td><?= $event['title'] ?></td>
<td><?= $event['org'] ?></td>
<td><?= number_format($event['amount']) ?>์›</td>
<td>
<span class="payment-status <?= $event['paid'] ? 'status-paid' : 'status-unpaid' ?>">
<?= $event['paid'] ? '์ž…๊ธˆ์™„๋ฃŒ' : '๋ฏธ์ž…๊ธˆ' ?>
</span>
</td>
<td>
<?php if (!$event['paid']): ?>
<button class="action-btn confirm-btn" onclick="confirmPayment(<?= $event['id'] ?>)">์ž…๊ธˆํ™•์ธ</button>
<?php else: ?>
<button class="action-btn receipt-btn" onclick="viewReceipt(<?= $event['id'] ?>)">์˜์ˆ˜์ฆ</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
function confirmPayment(eventId) {
if (confirm('์ •๋ง๋กœ ์ž…๊ธˆ ํ™•์ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?')) {
fetch('confirm_payment.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ eventId: eventId })
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('์ž…๊ธˆ ํ™•์ธ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
location.reload();
} else {
alert('์ž…๊ธˆ ํ™•์ธ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.');
}
});
}
}
function viewReceipt(eventId) {
// ์˜์ˆ˜์ฆ ๋ณด๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„
alert('์˜์ˆ˜์ฆ ๋ณด๊ธฐ ๊ธฐ๋Šฅ: ' + eventId);
}
// ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ
document.getElementById('search-btn').addEventListener('click', function() {
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const rows = document.querySelectorAll('#payment-list tr');
rows.forEach(row => {
const text = row.textContent.toLowerCase();
if (text.includes(searchTerm)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - ๐Ÿงฌ <a href="https://enzostvs-deepsite.hf.space?remix=nkjoy/joy01" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>๊ฐ•์˜๋ฃŒ ์ž…๊ธˆ ํ™•์ธ ์‹œ์Šคํ…œ</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Noto Sans KR', sans-serif;
}
body {
background-color: #f8fafc;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
h1 {
color: #2b7de9;
margin-bottom: 20px;
font-size: 2rem;
}
.search-box {
display: flex;
gap: 10px;
margin-bottom: 30px;
}
.search-box input {
flex: 1;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
.search-box button {
padding: 12px 20px;
background: #2b7de9;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
}
.payment-table {
width: 100%;
border-collapse: collapse;
}
.payment-table th, .payment-table td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
.payment-table th {
background: #f5f9ff;
font-weight: 600;
color: #2b7de9;
}
.payment-status {
padding: 6px 12px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
}
.status-paid {
background: #e6f7f0;
color: #2a9d8f;
}
.status-unpaid {
background: #ffebee;
color: #e63946;
}
.action-btn {
padding: 8px 12px;
border-radius: 6px;
border: none;
cursor: pointer;
font-weight: 500;
}
.confirm-btn {
background: #2b7de9;
color: white;
}
.receipt-btn {
background: #2a9d8f;
color: white;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 30px;
gap: 10px;
}
.pagination button {
padding: 10px 15px;
border: 1px solid #ddd;
background: white;
border-radius: 6px;
cursor: pointer;
}
.pagination button.active {
background: #2b7de9;
color: white;
border-color: #2b7de9;
}
</style>
</head>
<body>
<div class="container">
<h1>๊ฐ•์˜๋ฃŒ ์ž…๊ธˆ ํ™•์ธ ์‹œ์Šคํ…œ</h1>
<div class="search-box">
<input type="text" id="search-input" placeholder="๊ฐ•์‚ฌ๋ช… ๋˜๋Š” ๊ฐ•์˜๋ช…์œผ๋กœ ๊ฒ€์ƒ‰">
<button id="search-btn">๊ฒ€์ƒ‰</button>
</div>
<table class="payment-table">
<thead>
<tr>
<th>๊ฐ•์˜์ผ์ž</th>
<th>๊ฐ•์‚ฌ๋ช…</th>
<th>๊ฐ•์˜๋ช…</th>
<th>๊ธฐ๊ด€</th>
<th>๊ฐ•์˜๋ฃŒ</th>
<th>์ž…๊ธˆ์ƒํƒœ</th>
<th>๊ด€๋ฆฌ</th>
</tr>
</thead>
<tbody id="payment-list">
<!-- ๋ฐ์ดํ„ฐ๊ฐ€ ์—ฌ๊ธฐ์— ๋™์ ์œผ๋กœ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค -->
<tr>
<td colspan="7" style="text-align: center;">๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค</td>
</tr>
</tbody>
</table>
<div class="pagination">
<button>&lt;</button>
<button class="active">1</button>
<button>2</button>
<button>3</button>
<button>&gt;</button>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// ์—ฌ๊ธฐ์— ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
// ์˜ˆ์‹œ ๋ฐ์ดํ„ฐ
const sampleData = [
{
date: '2023-11-15',
lecturer: '๊น€๊ฐ•์‚ฌ',
title: 'ํ†ต์ผ๊ต์œก ๊ธฐ๋ณธ๊ณผ์ •',
org: 'ํ†ต์ผ๊ต์œก์›',
amount: '300,000์›',
status: 'paid'
},
{
date: '2023-11-20',
lecturer: '์ด๊ฐ•์‚ฌ',
title: 'ํ‰ํ™”ํ†ต์ผ ๊ฐ•์˜',
org: 'ํ”ผ์Šค๋ฉ”์ดํฌ',
amount: '250,000์›',
status: 'unpaid'
},
{
date: '2023-11-25',
lecturer: '๋ฐ•๊ฐ•์‚ฌ',
title: '์•ˆ๋ณด๊ต์œก',
org: '๊ตญ๋ฐฉ๋ถ€',
amount: '350,000์›',
status: 'paid'
}
];
renderPayments(sampleData);
// ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ
document.getElementById('search-btn').addEventListener('click', function() {
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const filteredData = sampleData.filter(item =>
item.lecturer.toLowerCase().includes(searchTerm) ||
item.title.toLowerCase().includes(searchTerm)
);
renderPayments(filteredData);
});
});
function renderPayments(data) {
const paymentList = document.getElementById('payment-list');
if (data.length === 0) {
paymentList.innerHTML = '<tr><td colspan="7" style="text-align: center;">๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค</td></tr>';
return;
}
paymentList.innerHTML = '';
data.forEach(item => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${item.date}</td>
<td>${item.lecturer}</td>
<td>${item.title}</td>
<td>${item.org}</td>
<td>${item.amount}</td>
<td><span class="payment-status status-${item.status}">${
item.status === 'paid' ? '์ž…๊ธˆ์™„๋ฃŒ' : '๋ฏธ์ž…๊ธˆ'
}</span></td>
<td>
${item.status === 'unpaid' ?
'<button class="action-btn confirm-btn">์ž…๊ธˆํ™•์ธ</button>' :
'<button class="action-btn receipt-btn">์˜์ˆ˜์ฆ</button>'}
</td>
`;
paymentList.appendChild(row);
});
}
</script>
</body>
</html>
<?php
// config + ์ผ์ • ์ €์žฅ ์ฒ˜๋ฆฌ
$pdo = new PDO("mysql:host=localhost;dbname=calendar_db;charset=utf8mb4", "root", "wjs3603825!", [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
// ์ผ์ • ์ €์žฅ ์ฒ˜๋ฆฌ
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json');
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || empty($data['event-title']) || empty($data['event-date'])) {
echo json_encode(['success' => false, 'error' => '์ œ๋ชฉ ๋˜๋Š” ๋‚ ์งœ ๋ˆ„๋ฝ']);
exit;
}
$stmt = $pdo->prepare("INSERT INTO events (title, event_date, org, description) VALUES (?, ?, ?, ?)");
$success = $stmt->execute([
$data['event-title'],
$data['event-date'],
$data['event-org'],
$data['event-desc']
]);
echo json_encode(['success' => $success]);
exit;
}
// ์˜ค๋Š˜ ์ผ์ • ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
$today = date('Y-m-d');
$stmt = $pdo->prepare("SELECT title, org, description FROM events WHERE event_date = ?");
$stmt->execute([$today]);
$today_events = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($today_events as &$event) {
$event['icon'] = match ($event['org']) {
'uniedu' => '๐ŸŽ“',
'mod' => '๐Ÿ›ก๏ธ',
'peacemaker' => '๐Ÿ•Š๏ธ',
'hyundai' => '๐Ÿญ',
'bus' => '๐ŸšŒ',
'camp' => '๐Ÿ•๏ธ',
default => ''
};
}
?>
<!-- HTML ์‹œ์ž‘ -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>๊ธฐ๊ด€๋ณ„ ์ผ์ • ์บ˜๋ฆฐ๋”</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--uniedu: #2b7de9;
--mod: #e63946;
--peacemaker: #2a9d8f;
--hyundai: #f77f00;
--bus: #00b4d8;
--camp: #7209b7;
--shadow: 0 10px 30px rgba(0,0,0,0.1);
--transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
--radius: 12px;
--sunday: #ff5a5f;
--monday: #4a6bdf;
--tuesday: #2a9d8f;
--wednesday: #f77f00;
--thursday: #7209b7;
--friday: #2b7de9;
--saturday: #9c4dcc;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
}
body {
background-color: #f8fafc;
color: #1e293b;
line-height: 1.6;
padding: 0.5rem;
font-size: 14px;
}
.calendar-container {
max-width: 1400px;
margin: 2rem auto;
padding: 2.5rem;
background: white;
border-radius: var(--radius);
box-shadow: var(--shadow);
transition: var(--transition);
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.9);
}
.calendar-container:hover {
box-shadow: 0 15px 35px rgba(0,0,0,0.15);
transform: translateY(-2px);
}
.calendar-header {
display: flex;
flex-direction: column;
gap: 1.5rem;
margin-bottom: 2rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid #f1f5f9;
}
@media (min-width: 768px) {
.calendar-header {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
}
.calendar-title {
font-size: 2.2rem;
font-weight: 900;
background: linear-gradient(90deg, #2b7de9, #00b4d8);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
display: inline-block;
letter-spacing: -0.5px;
text-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.calendar-nav {
display: flex;
gap: 1.2rem;
align-items: center;
}
.nav-button {
padding: 0.8rem 1.6rem;
background: white;
border: none;
border-radius: 12px;
cursor: pointer;
transition: var(--transition);
font-weight: 600;
color: white;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
background: linear-gradient(135deg, #2b7de9, #00b4d8);
}
.nav-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0,0,0,0.15);
opacity: 0.9;
}
.current-month {
font-size: 1.6rem;
font-weight: 700;
color: #334155;
min-width: 160px;
text-align: center;
text-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 12px;
}
.day-header {
text-align: center;
padding: 0.8rem 0.5rem;
font-weight: 700;
color: #64748b;
background: white;
border-radius: var(--radius);
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 0.3px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: relative;
overflow: hidden;
border-bottom: 2px solid #f1f5f9;
}
.day-header:nth-child(1) { /* ์ผ */
color: var(--sunday);
border-bottom: 3px solid var(--sunday);
}
.day-header:nth-child(2) { /* ์›” */
color: var(--monday);
border-bottom: 3px solid var(--monday);
}
.day-header:nth-child(3) { /* ํ™” */
color: var(--tuesday);
border-bottom: 3px solid var(--tuesday);
}
.day-header:nth-child(4) { /* ์ˆ˜ */
color: var(--wednesday);
border-bottom: 3px solid var(--wednesday);
}
.day-header:nth-child(5) { /* ๋ชฉ */
color: var(--thursday);
border-bottom: 3px solid var(--thursday);
}
.day-header:nth-child(6) { /* ๊ธˆ */
color: var(--friday);
border-bottom: 3px solid var(--friday);
}
.day-header:nth-child(7) { /* ํ†  */
color: var(--saturday);
border-bottom: 3px solid var(--saturday);
}
.day-cell {
min-height: 100px;
padding: 0.5rem;
background: white;
border-radius: var(--radius);
transition: var(--transition);
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: relative;
overflow: hidden;
}
.day-cell.sunday {
background-color: rgba(255, 90, 95, 0.1);
}
.day-cell.saturday {
background-color: rgba(74, 107, 223, 0.1);
}
.day-cell:hover {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
transform: translateY(-2px);
}
.day-number {
font-weight: 700;
margin-bottom: 0.5rem;
color: #334155;
font-size: 1.1rem;
display: inline-block;
padding: 2px 6px;
border-radius: 6px;
background: rgba(0,0,0,0.02);
}
.day-today .day-number {
background: linear-gradient(135deg, #2b7de9, #00b4d8);
color: white;
font-weight: 800;
padding: 4px 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.event-card {
padding: 0.4rem;
margin-bottom: 0.4rem;
font-size: 0.8rem;
border-radius: 6px;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: var(--transition);
display: flex;
align-items: center;
gap: 4px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
.event-card:hover {
transform: translateY(-3px);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.event-uniedu { border-left: 3px solid var(--uniedu); background-color: rgba(43, 125, 233, 0.08); }
.event-mod { border-left: 3px solid var(--mod); background-color: rgba(230, 57, 70, 0.08); }
.event-peacemaker { border-left: 3px solid var(--peacemaker); background-color: rgba(42, 157, 143, 0.08); }
.event-hyundai { border-left: 3px solid var(--hyundai); background-color: rgba(247, 127, 0, 0.08); }
.event-bus { border-left: 3px solid var(--bus); background-color: rgba(0, 180, 216, 0.08); }
.event-camp { border-left: 3px solid var(--camp); background-color: rgba(114, 9, 183, 0.08); }
.calendar-actions {
display: flex;
justify-content: flex-end;
gap: 1rem;
margin-top: 1.5rem;
}
.action-button {
padding: 0.7rem 1.5rem;
border: none;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.print-button {
background: #2b7de9;
color: white;
}
.print-button:hover {
background: #1a5fb4;
}
.add-button {
background: #2a9d8f;
color: white;
}
.add-button:hover {
background: #21867a;
}
/* ๋ชจ๋‹ฌ ์Šคํƒ€์ผ */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 1.5rem;
border-radius: 8px;
width: 95%;
max-width: 100%;
margin: 0 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
@media (max-width: 768px) {
.calendar-container {
padding: 1rem;
margin: 1rem auto;
}
.calendar-grid {
gap: 6px;
}
.day-cell {
min-height: 70px;
padding: 0.3rem;
}
.event-card {
font-size: 0.7rem;
padding: 0.3rem;
}
.calendar-title {
font-size: 1.3rem;
}
.current-month {
font-size: 1.1rem;
}
.nav-button {
padding: 0.4rem 0.8rem;
font-size: 0.8rem;
}
.calendar-actions {
margin-top: 1rem;
}
.action-button {
padding: 0.5rem 1rem;
font-size: 0.8rem;
}
}
/* ์˜ค๋Š˜ ์ผ์ • ์„น์…˜ ์Šคํƒ€์ผ */
.today-events-container {
max-width: 1400px;
margin: 0 auto 2rem;
padding: 1.5rem;
background: white;
border-radius: var(--radius);
box-shadow: var(--shadow);
}
.today-events-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: #334155;
padding-bottom: 0.5rem;
border-bottom: 1px solid #f1f5f9;
}
.today-events-list {
display: grid;
gap: 0.8rem;
}
.today-event {
padding: 1rem;
border-radius: 8px;
display: flex;
align-items: center;
gap: 1rem;
transition: var(--transition);
}
.today-event:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.today-event-icon {
font-size: 1.5rem;
flex-shrink: 0;
}
.today-event-content {
flex-grow: 1;
}
.today-event-title {
font-weight: 600;
margin-bottom: 0.3rem;
}
.today-event-org {
font-size: 0.8rem;
color: #64748b;
}
.today-event-desc {
font-size: 0.9rem;
margin-top: 0.5rem;
color: #475569;
}
.no-events {
text-align: center;
color: #94a3b8;
padding: 1rem;
}
@media (max-width: 768px) {
.today-events-container {
padding: 1rem;
}
.today-events-title {
font-size: 1.2rem;
}
.today-event {
padding: 0.8rem;
}
}
@media print {
body * {
visibility: hidden;
}
.print-area, .print-area * {
visibility: visible;
}
.print-area {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
}
</style>
</head>
<body>
<div class="calendar-container">
<div class="calendar-header">
<div class="calendar-title">๊ธฐ๊ด€๋ณ„ ์ผ์ • ์บ˜๋ฆฐ๋”</div>
<div class="calendar-nav">
<button class="nav-button" id="prev-month">์ด์ „ ๋‹ฌ</button>
<div class="current-month" id="current-month">2025๋…„ 6์›”</div>
<button class="nav-button" id="next-month">๋‹ค์Œ ๋‹ฌ</button>
</div>
</div>
<div class="calendar-grid" id="calendar-grid">
<!-- ์ผ์š”์ผ๋ถ€ํ„ฐ ํ† ์š”์ผ๊นŒ์ง€ ์š”์ผ ํ—ค๋” -->
<div class="day-header">์ผ</div>
<div class="day-header">์›”</div>
<div class="day-header">ํ™”</div>
<div class="day-header">์ˆ˜</div>
<div class="day-header">๋ชฉ</div>
<div class="day-header">๊ธˆ</div>
<div class="day-header">ํ† </div>
<!-- ๋‹ฌ๋ ฅ ์ผ์ž๋Š” JavaScript๋กœ ๋™์  ์ƒ์„ฑ -->
</div>
<div class="calendar-actions">
<button class="action-button add-button" id="add-event">์ผ์ • ์ถ”๊ฐ€</button>
<button class="action-button print-button" id="print-calendar">์ธ์‡„ํ•˜๊ธฐ</button>
<a href="payment_confirmation.php" class="action-button" style="background:#7209b7; color:white; text-decoration:none;">๊ฐ•์˜๋ฃŒ ํ™•์ธ</a>
</div>
</div>
<div class="today-events-container">
<h3 class="today-events-title">์˜ค๋Š˜์˜ ์ผ์ •</h3>
<div class="today-events-list" id="today-events">
<!-- ์˜ค๋Š˜ ์ผ์ •์€ JavaScript๋กœ ๋™์  ์ƒ์„ฑ -->
<div class="no-events">์˜ค๋Š˜ ์˜ˆ์ •๋œ ์ผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค</div>
</div>
</div>
<!-- ์ด๋ฒคํŠธ ์ถ”๊ฐ€/์ˆ˜์ • ๋ชจ๋‹ฌ -->
<div class="modal" id="event-modal">
<div class="modal-content">
<h3>์ผ์ • ์ถ”๊ฐ€</h3>
<form id="event-form">
<div style="margin-bottom: 1rem;">
<label for="event-title">์ œ๋ชฉ</label>
<input type="text" id="event-title" required style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;">
</div>
<div style="margin-bottom: 1rem;">
<label for="event-date">๋‚ ์งœ</label>
<input type="date" id="event-date" required style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;">
</div>
<div style="margin-bottom: 1rem;">
<label for="event-org">๊ธฐ๊ด€</label>
<select id="event-org" style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;">
<option value="uniedu">๐ŸŽ“ ํ†ต์ผ๊ต์œก์›</option>
<option value="mod">๐Ÿ›ก๏ธ ๊ตญ๋ฐฉ๋ถ€</option>
<option value="peacemaker">๐Ÿ•Š๏ธ ํ”ผ์Šค๋ฉ”์ดํฌ</option>
<option value="hyundai">๐Ÿญ ํ˜„๋Œ€์•„์‚ฐ</option>
<option value="bus">๐ŸšŒ ๋ฒ„์Šค๊ฐ•์˜</option>
<option value="camp">๐Ÿ•๏ธ ํ•™์บ </option>
</select>
</div>
<div style="margin-bottom: 1.5rem;">
<label for="event-desc">์„ค๋ช…</label>
<textarea id="event-desc" rows="3" style="width: 100%; padding: 0.5rem; margin-top: 0.3rem;"></textarea>
</div>
<div style="display: flex; justify-content: flex-end; gap: 0.5rem;">
<button type="button" id="cancel-event" style="padding: 0.5rem 1rem; background: #f8f9fa; border: 1px solid #ddd; border-radius: 4px;">์ทจ์†Œ</button>
<button type="submit" style="padding: 0.5rem 1rem; background: #2b7de9; color: white; border: none; border-radius: 4px;">์ €์žฅ</button>
</div>
</form>
</div>
</div>
<script>
const todayEventData = <?php echo json_encode($today_events); ?>;
</script>
<script>
// ํ˜„์žฌ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ๋‹ฌ๋ ฅ ์ƒ์„ฑ
document.addEventListener('DOMContentLoaded', function() {
const currentDate = new Date();
renderCalendar(currentDate);
showDayEvents(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
// ๋ชจ๋‹ฌ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
const modal = document.getElementById('event-modal');
document.getElementById('add-event').addEventListener('click', () => {
modal.style.display = 'flex';
});
document.getElementById('cancel-event').addEventListener('click', () => {
modal.style.display = 'none';
document.getElementById('event-form').reset();
});
// Handle form submission
document.getElementById('event-form').addEventListener('submit', function(e) {
e.preventDefault();
const formData = {
'event-title': document.getElementById('event-title').value,
'event-date': document.getElementById('event-date').value,
'event-org': document.getElementById('event-org').value,
'event-desc': document.getElementById('event-desc').value
};
fetch('<?php echo $_SERVER['PHP_SELF']; ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
modal.style.display = 'none';
this.reset();
renderCalendar(currentDate);
renderTodayEvents(currentDate);
}
});
});
// ์ธ์‡„ ๋ฒ„ํŠผ
document.getElementById('print-calendar').addEventListener('click', () => {
window.print();
});
// ์›” ์ด๋™ ๋ฒ„ํŠผ
document.getElementById('prev-month').addEventListener('click', () => {
currentDate.setMonth(currentDate.getMonth() - 1);
renderCalendar(currentDate);
});
document.getElementById('next-month').addEventListener('click', () => {
currentDate.setMonth(currentDate.getMonth() + 1);
renderCalendar(currentDate);
renderTodayEvents(currentDate);
});
});
function showDayEvents(year, month, day) {
const formattedDate = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
fetch(`get_events.php?date=${formattedDate}`)
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(events => {
const todayEvents = document.getElementById('today-events');
todayEvents.innerHTML = '';
// Update title to show selected date
document.querySelector('.today-events-title').textContent =
`${year}๋…„ ${month + 1}์›” ${day}์ผ ์ผ์ •`;
if (events.length === 0) {
todayEvents.innerHTML = '<div class="no-events">์˜ˆ์ •๋œ ์ผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค</div>';
return;
}
events.forEach(event => {
const eventEl = document.createElement('div');
eventEl.className = `today-event event-${event.org}`;
eventEl.innerHTML = `
<div class="today-event-icon">${getOrgIcon(event.org)}</div>
<div class="today-event-content">
<div class="today-event-title">${event.title}</div>
<div class="today-event-org">${getOrgName(event.org)}</div>
<div class="today-event-desc">${event.description}</div>
</div>
`;
todayEvents.appendChild(eventEl);
});
})
.catch(error => {
console.error('Error fetching events:', error);
});
}
function renderTodayEvents(date) {
const todayEvents = document.getElementById('today-events');
todayEvents.innerHTML = '';
// PHP์—์„œ ๊ฐ€์ ธ์˜จ ์˜ค๋Š˜์˜ ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ
const events = <?php echo json_encode($today_events); ?>;
if (events.length === 0) {
todayEvents.innerHTML = '<div class="no-events">์˜ค๋Š˜ ์˜ˆ์ •๋œ ์ผ์ •์ด ์—†์Šต๋‹ˆ๋‹ค</div>';
return;
}
events.forEach(event => {
const eventEl = document.createElement('div');
eventEl.className = `today-event event-${event.org}`;
eventEl.innerHTML = `
<div class="today-event-icon">${event.icon}</div>
<div class="today-event-content">
<div class="today-event-title">${event.title}</div>
<div class="today-event-org">${getOrgName(event.org)}</div>
<div class="today-event-desc">${event.desc}</div>
</div>
`;
todayEvents.appendChild(eventEl);
});
}
function getOrgIcon(org) {
const orgIcons = {
'uniedu': '๐ŸŽ“',
'mod': '๐Ÿ›ก๏ธ',
'peacemaker': '๐Ÿ•Š๏ธ',
'hyundai': '๐Ÿญ',
'bus': '๐ŸšŒ',
'camp': '๐Ÿ•๏ธ'
};
return orgIcons[org] || '';
}
function getOrgName(org) {
const orgNames = {
'uniedu': 'ํ†ต์ผ๊ต์œก์›',
'mod': '๊ตญ๋ฐฉ๋ถ€',
'peacemaker': 'ํ”ผ์Šค๋ฉ”์ดํฌ',
'hyundai': 'ํ˜„๋Œ€์•„์‚ฐ',
'bus': '๋ฒ„์Šค๊ฐ•์˜',
'camp': 'ํ•™์บ '
};
return orgNames[org] || '';
}
function renderCalendar(date) {
const year = date.getFullYear();
const month = date.getMonth();
// ํ˜„์žฌ ์›” ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ
document.getElementById('current-month').textContent = `${year}๋…„ ${month + 1}์›”`;
// ๋‹ฌ๋ ฅ ๊ทธ๋ฆฌ๋“œ ์ดˆ๊ธฐํ™”
const calendarGrid = document.getElementById('calendar-grid');
// ์š”์ผ ํ—ค๋”๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋ชจ๋“  ์ž์‹ ์ œ๊ฑฐ
while (calendarGrid.children.length > 7) {
calendarGrid.removeChild(calendarGrid.lastChild);
}
// ํ˜„์žฌ ์›”์˜ ์ฒซ์งธ ๋‚ ๊ณผ ๋งˆ์ง€๋ง‰ ๋‚ 
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
// ์ฒซ์งธ ๋‚ ์˜ ์š”์ผ (0: ์ผ์š”์ผ)
const firstDayOfWeek = firstDay.getDay();
// ๋‹ฌ๋ ฅ ์‹œ์ž‘ ์ „ ๋นˆ ์นธ ์ถ”๊ฐ€
for (let i = 0; i < firstDayOfWeek; i++) {
const emptyCell = document.createElement('div');
emptyCell.className = 'day-cell';
calendarGrid.appendChild(emptyCell);
}
// ๋‚ ์งœ ์นธ ์ถ”๊ฐ€
for (let day = 1; day <= lastDay.getDate(); day++) {
const dayCell = document.createElement('div');
dayCell.className = 'day-cell';
// Add Sunday/Saturday classes
const dayOfWeek = new Date(year, month, day).getDay();
if (dayOfWeek === 0) {
dayCell.classList.add('sunday');
} else if (dayOfWeek === 6) {
dayCell.classList.add('saturday');
}
const dayNumber = document.createElement('div');
dayNumber.className = 'day-number';
dayNumber.textContent = day;
// ์˜ค๋Š˜ ๋‚ ์งœ ์ฒดํฌ
const today = new Date();
if (today.getFullYear() === year && today.getMonth() === month && today.getDate() === day) {
dayCell.classList.add('day-today');
}
dayCell.appendChild(dayNumber);
dayCell.addEventListener('click', () => {
showDayEvents(year, month, day);
});
// ๋‚ ์งœ ํ˜•์‹ ๋งž์ถ”๊ธฐ (YYYY-MM-DD)
const formattedDate = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
// ํ•ด๋‹น ๋‚ ์งœ์˜ ์ด๋ฒคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
fetch(`get_events.php?date=${formattedDate}`)
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(events => {
if (events && events.length > 0) {
events.forEach(event => {
const eventEl = document.createElement('div');
eventEl.className = `event-card event-${event.org}`;
eventEl.textContent = `${getOrgIcon(event.org)} ${event.title}`;
dayCell.appendChild(eventEl);
});
}
})
.catch(error => {
console.error('Error fetching events:', error);
});
calendarGrid.appendChild(dayCell);
}
}
</script>
</body>
</html>