<?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