duuinh's picture
I want it to be a one-page application. It's designed for mobile first so we don't need a footer. I want is to be more visual, not only text.
ae84176 verified
document.addEventListener('DOMContentLoaded', () => {
// Initialize app
createSnowflakes();
initializeStory();
setupNavigation();
// Event listeners
document.getElementById('continue-story')?.addEventListener('click', continueStory);
document.getElementById('ask-question')?.addEventListener('click', askQuestion);
// Location buttons
document.querySelectorAll('.location-btn').forEach(btn => {
btn.addEventListener('click', () => {
const location = btn.dataset.location;
showLocation(location);
});
});
// Back buttons
document.querySelectorAll('[data-back]').forEach(btn => {
btn.addEventListener('click', showHomeView);
});
});
function setupNavigation() {
// Handle bottom nav clicks
document.querySelectorAll('.nav-btn').forEach(btn => {
btn.addEventListener('click', () => {
const view = btn.dataset.view;
showView(view);
// Update active state
document.querySelectorAll('.nav-btn').forEach(navBtn => {
navBtn.classList.remove('active');
});
btn.classList.add('active');
});
});
}
function showView(viewName) {
// Hide all views
document.querySelectorAll('.view').forEach(view => {
view.classList.add('hidden');
view.classList.remove('active');
});
// Show selected view
const view = document.getElementById(`${viewName}-view`);
if (view) {
view.classList.remove('hidden');
view.classList.add('active');
}
}
function showHomeView() {
showView('home');
document.querySelector('[data-view="home"]').classList.add('active');
}
function showLocation(location) {
showView(location);
}
function initializeStory() {
const currentDay = 1;
document.getElementById('current-day').textContent = currentDay;
const chapterTitles = [
"Chapter 1: The Arrival",
"Chapter 2: The Baker's Secret",
"Chapter 3: Market Mystery",
"Chapter 4: Getting Around",
"Chapter 5: Hotel Check-in",
"Chapter 6: Café Culture",
"Chapter 7: Weekly Review",
"Chapter 8: Grocery Shopping",
"Chapter 9: Post Office Visit",
"Chapter 10: Doctor's Visit",
"Chapter 11: Public Transport",
"Chapter 12: Restaurant Night",
"Chapter 13: Shopping Spree",
"Chapter 14: Weekly Review",
"Chapter 15: Carol Singing",
"Chapter 16: Tree Decorating",
"Chapter 17: Gift Wrapping",
"Chapter 18: Winter Sports",
"Chapter 19: Holiday Cooking",
"Chapter 20: Town Celebration",
"Chapter 21: Weekly Review",
"Chapter 22: Storytelling",
"Chapter 23: Local Legends",
"Chapter 24: Christmas Eve",
"Chapter 25: Farewell"
];
if (document.getElementById('chapter-title')) {
document.getElementById('chapter-title').textContent = chapterTitles[currentDay - 1] || "Chapter 1: The Arrival";
}
}
function generateCalendar() {
const calendarDays = document.getElementById('calendar-days');
if (!calendarDays) return;
calendarDays.innerHTML = '';
for (let day = 1; day <= 25; day++) {
const dayElement = document.createElement('div');
dayElement.className = `text-center p-3 rounded-lg cursor-pointer transition ${day === 1 ? 'bg-primary text-white' : day <= 1 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-400'}`;
dayElement.innerHTML = `
<div class="font-bold text-lg">${day}</div>
<div class="text-xs">${day === 1 ? 'Today' : 'Day ' + day}</div>
`;
if (day <= 1) {
dayElement.addEventListener('click', () => {
alert(`You're on Day ${day}! ${day === 1 ? 'Continue your adventure.' : 'This day will unlock soon.'}`);
});
}
calendarDays.appendChild(dayElement);
}
}
function loadCharacters() {
const characterContainer = document.getElementById('character-profiles');
if (!characterContainer) return;
const characters = [
{
name: "Pierre",
role: "The Baker",
description: "Friendly local who loves teaching phrases about food.",
img: "http://static.photos/people/200x200/10"
},
{
name: "Claire",
role: "Market Vendor",
description: "Sells Christmas ornaments and knows all the holiday vocabulary.",
img: "http://static.photos/people/200x200/11"
},
{
name: "Luc",
role: "Town Musician",
description: "Teaches French through Christmas carols.",
img: "http://static.photos/people/200x200/12"
},
{
name: "Monsieur Lefevre",
role: "Mayor",
description: "Speaks formal French - great for learning polite expressions.",
img: "http://static.photos/people/200x200/13"
}
];
characterContainer.innerHTML = '';
characters.forEach(char => {
const charElement = document.createElement('div');
charElement.className = 'text-center';
charElement.innerHTML = `
<img src="${char.img}" alt="${char.name}" class="w-20 h-20 rounded-full mx-auto mb-3 border-4 border-white shadow">
<h4 class="font-bold text-primary">${char.name}</h4>
<p class="text-sm text-secondary font-medium">${char.role}</p>
<p class="text-xs text-gray-600 mt-1">${char.description}</p>
`;
characterContainer.appendChild(charElement);
});
}
function continueStory() {
const storyChat = document.getElementById('story-chat');
if (!storyChat) return;
const storySteps = [
{ speaker: "ai", text: "You walk toward the town square and see a beautiful Christmas market. A vendor calls out: 'Venez voir nos décorations!' (Come see our decorations!)", character: "Market Vendor" },
{ speaker: "user", text: "Je voudrais acheter une décoration. (I would like to buy a decoration.)", character: "You" },
{ speaker: "ai", text: "'Très bien! Celle-ci coûte cinq euros,' she says with a smile. You've just completed your first transaction in French!", character: "Claire" }
];
storySteps.forEach((step, index) => {
setTimeout(() => {
const messageDiv = document.createElement('div');
messageDiv.className = 'flex items-start space-x-3 mb-4';
if (step.speaker === 'user') {
messageDiv.innerHTML = `
<div class="bg-primary rounded-full p-2">
<i data-feather="user" class="text-white"></i>
</div>
<div class="bg-gray-100 rounded-2xl px-4 py-2 max-w-xs">
<p class="font-medium">${step.text}</p>
<p class="text-xs text-gray-500 mt-1">${step.character}</p>
</div>
`;
} else {
messageDiv.innerHTML = `
<div class="bg-secondary rounded-full p-2">
<i data-feather="user-check" class="text-white"></i>
</div>
<div class="bg-blue-50 rounded-2xl px-4 py-2 max-w-xs">
<p class="font-medium">${step.text}</p>
<p class="text-xs text-gray-500 mt-1">${step.character}</p>
</div>
`;
}
storyChat.appendChild(messageDiv);
storyChat.scrollTop = storyChat.scrollHeight;
feather.replace();
}, (index + 1) * 1500);
});
// Update mission text after story progresses
setTimeout(() => {
const missionElement = document.getElementById('daily-mission');
if (missionElement) {
missionElement.textContent = "Visit the Christmas market and buy a decoration using French.";
}
}, 5000);
}
function askQuestion() {
const storyChat = document.getElementById('story-chat');
if (!storyChat) return;
const questionDiv = document.createElement('div');
questionDiv.className = 'flex items-start space-x-3 mb-4';
questionDiv.innerHTML = `
<div class="bg-accent rounded-full p-2">
<i data-feather="help-circle" class="text-white"></i>
</div>
<div class="bg-yellow-50 rounded-2xl px-4 py-2 max-w-xs">
<p class="font-medium">How do I say 'How much does this cost?' in French?</p>
<p class="text-xs text-gray-500 mt-1">You asked</p>
</div>
`;
storyChat.appendChild(questionDiv);
setTimeout(() => {
const answerDiv = document.createElement('div');
answerDiv.className = 'flex items-start space-x-3 mb-4';
answerDiv.innerHTML = `
<div class="bg-secondary rounded-full p-2">
<i data-feather="user-check" class="text-white"></i>
</div>
<div class="bg-green-50 rounded-2xl px-4 py-2 max-w-xs">
<p class="font-medium">You say: 'Combien ça coûte?' (kɔm.bjɛ̃ sa kut). Try repeating it!</p>
<p class="text-xs text-gray-500 mt-1">Pierre helps</p>
</div>
`;
storyChat.appendChild(answerDiv);
storyChat.scrollTop = storyChat.scrollHeight;
feather.replace();
}, 1000);
}
function createSnowflakes() {
const snowContainer = document.getElementById('snow-overlay');
if (!snowContainer) return;
for (let i = 0; i < 100; i++) {
const snowflake = document.createElement('div');
snowflake.className = 'snowflake';
snowflake.innerHTML = '❄';
snowflake.style.left = Math.random() * 100 + 'vw';
snowflake.style.top = -20 + 'px';
snowflake.style.fontSize = (Math.random() * 10 + 8) + 'px';
snowflake.style.opacity = Math.random();
snowflake.style.animationDuration = `${Math.random() * 5 + 5}s, ${Math.random() * 5 + 5}s`;
snowflake.style.animationDelay = `${Math.random() * 5}s`;
snowContainer.appendChild(snowflake);
}
}
function visitLocation(location) {
const locations = {
bakery: {
title: "Pierre's Bakery",
description: "The sweet smell of fresh pastries fills the air as you enter the cozy bakery. Pierre greets you warmly."
},
market: {
title: "Christmas Market",
description: "Colorful stalls line the square, selling handmade ornaments and holiday treats. Claire waves as you approach."
},
hotel: {
title: "Town Hotel",
description: "The grand hotel entrance welcomes you with twinkling lights. The concierge stands ready to assist."
}
};
const storyPanel = document.querySelector('.story-panel');
if (storyPanel) {
storyPanel.querySelector('#chapter-title').textContent = locations[location].title;
storyPanel.querySelector('#story-text').textContent = locations[location].description;
}
}
});