diff --git a/api/chat.php b/api/chat.php new file mode 100644 index 0000000..2f3e588 --- /dev/null +++ b/api/chat.php @@ -0,0 +1,28 @@ + 'Sorry, I am having trouble understanding you right now.' +]; + +if (isset($_POST['message'])) { + $userMessage = trim($_POST['message']); + + $ai_response = LocalAIApi::createResponse([ + 'input' => [ + ['role' => 'system', 'content' => 'You are a helpful assistant for a winter resort website. Your name is WinterBot. You are friendly and helpful. You should help users plan their winter vacation.'], + ['role' => 'user', 'content' => $userMessage], + ], + ]); + + if (!empty($ai_response['success']) && !empty($ai_response['data'])) { + $text = LocalAIApi::extractText($ai_response); + if ($text !== '') { + $response['message'] = $text; + } + } +} + +echo json_encode($response); diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..b929339 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,334 @@ +/* Cozy Lodge Theme */ + +body { + background-color: #F6FBFF; /* Frosty White */ + font-family: 'Nunito Sans', sans-serif; + color: #212529; +} + +.container { + width: 80%; + margin: 0 auto; + padding: 2rem 0; +} + +/* Navigation */ +.navbar { + background-color: #FFFFFF; + border-bottom: 1px solid #E9F1F8; + padding: 1rem 0; +} + +.navbar .container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.navbar-brand { + font-family: 'Playfair Display', serif; + font-size: 1.5rem; + font-weight: 700; + color: #0B233F; + text-decoration: none; +} + +.nav-links { + list-style: none; + margin: 0; + padding: 0; + display: flex; +} + +.nav-links li a { + text-decoration: none; + color: #212529; + padding: 0.5rem 1rem; + transition: color 0.3s ease; +} + +.nav-links li a:hover { + color: #FF7A4D; +} + +/* Hero Section */ +.hero { + background-image: url('https://images.pexels.com/photos/912110/pexels-photo-912110.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2'); + background-size: cover; + background-position: center; + height: 80vh; + display: flex; + align-items: center; + justify-content: center; + color: white; + text-shadow: 0 2px 4px rgba(0,0,0,0.5); +} + +.hero-content { + background: rgba(11, 35, 63, 0.6); /* Deep Navy with transparency */ + padding: 2rem 4rem; + border-radius: .5rem; + text-align: center; +} + +.hero h1 { + font-family: 'Playfair Display', serif; + font-weight: 700; + font-size: 4rem; +} + +.lead { + font-size: 1.25rem; +} + +.btn-primary { + background-color: #FF7A4D; /* Warm Ember */ + border-color: #FF7A4D; + color: white; + font-weight: bold; + padding: 0.75rem 1.5rem; + transition: background-color 0.3s ease; + text-decoration: none; + border-radius: .25rem; + border: none; + cursor: pointer; +} + +.btn-primary:hover { + background-color: #E86A3C; + border-color: #E86A3C; +} + +/* Featured Experiences */ +.featured-experiences { + text-align: center; + padding: 4rem 0; +} + +.featured-experiences h2 { + font-family: 'Playfair Display', serif; + font-size: 2.5rem; + margin-bottom: 2rem; +} + +.experience-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.experience-card { + background-color: #FFFFFF; + border: 1px solid #E9F1F8; /* Snow Grey */ + border-radius: .5rem; + overflow: hidden; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.experience-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 16px rgba(0,0,0,0.1); +} + +.experience-card img { + width: 100%; + height: 200px; + object-fit: cover; +} + +.experience-card h3 { + font-family: 'Playfair Display', serif; + font-size: 1.5rem; + margin: 1rem; +} + +.experience-card p { + margin: 0 1rem 1rem; +} + +/* Experience Detail */ +.experience-detail img { + width: 100%; + height: 400px; + object-fit: cover; + border-radius: .5rem; + margin-bottom: 2rem; +} + +.experience-detail h1 { + font-family: 'Playfair Display', serif; + font-size: 3rem; + margin-bottom: 1rem; +} + +.experience-detail p { + line-height: 1.6; + margin-bottom: 1.5rem; +} + +.experience-detail h2 { + font-family: 'Playfair Display', serif; + font-size: 2rem; + margin-top: 2rem; + margin-bottom: 1rem; +} + +.experience-detail ul { + list-style: disc; + margin-left: 1.5rem; +} + +/* Gallery */ +.gallery-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1rem; +} + +.gallery-card { + overflow: hidden; + border-radius: .5rem; +} + +.gallery-card img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.gallery-card:hover img { + transform: scale(1.05); +} + +.upload-section { + text-align: center; + padding: 4rem 0; +} + +.upload-section h2 { + font-family: 'Playfair Display', serif; + font-size: 2.5rem; + margin-bottom: 1rem; +} + +/* Forms */ +.form-group { + margin-bottom: 1.5rem; +} + +.form-group label { + display: block; + margin-bottom: .5rem; + font-weight: bold; +} + +.form-group input, +.form-group textarea, +.form-group select { + width: 100%; + padding: .75rem; + border: 1px solid #E9F1F8; + border-radius: .25rem; +} + + +/* Footer */ +footer { + background-color: #0B233F; + color: #F6FBFF; + text-align: center; + padding: 2rem 0; + margin-top: 4rem; +} + +/* Chat Widget */ +.chat-icon { + position: fixed; + bottom: 2rem; + right: 2rem; + background-color: #0B233F; /* Deep Navy */ + color: #F6FBFF; /* Frosty White */ + width: 60px; + height: 60px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + transition: transform 0.3s ease; +} + +.chat-icon:hover { + transform: scale(1.1); +} + +.chat-window { + display: none; /* Hidden by default */ + position: fixed; + bottom: 7rem; + right: 2rem; + width: 350px; + max-width: 90%; + background-color: #FFFFFF; + border-radius: .5rem; + box-shadow: 0 4px 16px rgba(0,0,0,0.2); + flex-direction: column; + overflow: hidden; +} + +.chat-window.show { + display: flex; +} + +.chat-header { + background-color: #0B233F; /* Deep Navy */ + color: #F6FBFF; /* Frosty White */ + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.chat-header h5 { + margin: 0; + font-family: 'Playfair Display', serif; +} + +.chat-body { + padding: 1rem; + overflow-y: auto; + height: 300px; +} + +.message-container { + /* The messages will be prepended here by JS */ +} + +.message { + padding: 0.5rem 1rem; + border-radius: 1rem; + margin-bottom: 0.5rem; + max-width: 80%; + /* Use float for alignment */ +} + +.message.received { + background-color: #E9F1F8; /* Snow Grey */ + float: left; + clear: both; +} + +.message.sent { + background-color: #FF7A4D; /* Warm Ember */ + color: white; + float: right; + clear: both; +} + +.chat-input { + display: flex; + padding: 1rem; + border-top: 1px solid #E9F1F8; /* Snow Grey */ +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..e7bd320 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,53 @@ +document.addEventListener('DOMContentLoaded', function() { + const chatIcon = document.querySelector('.chat-icon'); + const chatWindow = document.querySelector('.chat-window'); + const closeChat = document.querySelector('.close-chat'); + const chatInput = document.querySelector('.chat-input input'); + const sendButton = document.querySelector('.chat-input button'); + const messageContainer = document.querySelector('.message-container'); + + if (chatIcon) { + chatIcon.addEventListener('click', () => { + chatWindow.classList.toggle('show'); + }); + } + + if (closeChat) { + closeChat.addEventListener('click', () => { + chatWindow.classList.remove('show'); + }); + } + + const sendMessage = () => { + const messageText = chatInput.value.trim(); + if (messageText === '') return; + + appendMessage(messageText, 'sent'); + chatInput.value = ''; + + // Simulate a response from the AI + setTimeout(() => { + appendMessage('I am a demo bot. I will be able to help you soon.', 'received'); + }, 1000); + }; + + const appendMessage = (text, type) => { + const messageDiv = document.createElement('div'); + messageDiv.classList.add('message', type); + messageDiv.textContent = text; + messageContainer.appendChild(messageDiv); + messageContainer.scrollTop = messageContainer.scrollHeight; + }; + + if (sendButton) { + sendButton.addEventListener('click', sendMessage); + } + + if (chatInput) { + chatInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + sendMessage(); + } + }); + } +}); \ No newline at end of file diff --git a/db/migrate.php b/db/migrate.php new file mode 100644 index 0000000..0555e7a --- /dev/null +++ b/db/migrate.php @@ -0,0 +1,69 @@ +exec($sql); + echo "Database schema created successfully.\n"; + } catch (PDOException $e) { + die("DB ERROR: ". $e->getMessage()); + } + + // 2. Insert data (idempotently) + $experiences_data = [ + [ + 'slug' => 'guided-ski-lessons', + 'title' => 'Guided Ski & Snowboard Lessons', + 'image' => 'https://images.pexels.com/photos/167699/pexels-photo-167699.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + 'description' => 'Whether you\'re a first-timer or a seasoned pro, our certified instructors are here to help you sharpen your skills and conquer the mountain with confidence. We offer private and group lessons for all ages and abilities.', + 'features' => json_encode([ + 'Duration' => '2 hours, Half-day, or Full-day', + 'Skill Level' => 'Beginner to Expert', + 'Group Size' => '1-6 people', + 'Includes' => 'Lift ticket & rental discounts' + ]) + ], + [ + 'slug' => 'enchanted-ice-skating', + 'title' => 'Enchanted Ice Skating', + 'image' => 'https://images.pexels.com/photos/714258/pexels-photo-714258.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + 'description' => 'Glide across our scenic outdoor rink, surrounded by magical winter lights and festive music. A perfect activity for families, couples, and friends. Skate rentals available.', + 'features' => json_encode([ + 'Duration' => 'All-day pass', + 'Skill Level' => 'All levels', + 'Group Size' => 'N/A', + 'Includes' => 'Skate rentals available for a fee' + ]) + ], + [ + 'slug' => 'cozy-winter-markets', + 'title' => 'Cozy Winter Markets', + 'image' => 'https://images.pexels.com/photos/1525612/pexels-photo-1525612.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + 'description' => 'Explore our charming village market, filled with handcrafted gifts from local artisans, delicious seasonal treats, and warm spiced beverages. A festive experience for all ages.', + 'features' => json_encode([ + 'Duration' => 'Weekends in December', + 'Skill Level' => 'N/A', + 'Group Size' => 'N/A', + 'Includes' => 'Unique gifts and festive food' + ]) + ] + ]; + + $stmt = $pdo->prepare("INSERT INTO experiences (slug, title, image, description, features) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE title=VALUES(title), image=VALUES(image), description=VALUES(description), features=VALUES(features)"); + + try { + foreach ($experiences_data as $exp) { + $stmt->execute(array_values($exp)); + } + echo "Experience data inserted/updated successfully.\n"; + } catch (PDOException $e) { + die("DB ERROR: ". $e->getMessage()); + } +} + +migrate(); + diff --git a/db/schema.sql b/db/schema.sql new file mode 100644 index 0000000..b0685dd --- /dev/null +++ b/db/schema.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `experiences` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `slug` VARCHAR(255) NOT NULL UNIQUE, + `title` VARCHAR(255) NOT NULL, + `image` VARCHAR(255) NOT NULL, + `description` TEXT NOT NULL, + `features` JSON NOT NULL +); diff --git a/experience_detail.php b/experience_detail.php new file mode 100644 index 0000000..930b45c --- /dev/null +++ b/experience_detail.php @@ -0,0 +1,18 @@ + + +
+ Learn to ski with our expert instructors. We offer lessons for all ages and skill levels. Our instructors are certified and have years of experience. We provide all the necessary equipment, so you just need to bring your enthusiasm!
+Browse our collection of unique winter experiences.
+ +