commit bd83b83f259173cc8f41bc596df534a46390b6eb Author: Emmanuel Rizky Date: Fri Jul 25 11:25:35 2025 +0700 feat(app): add initial Berlayar Sinema event landing page This commit introduces the complete initial version of the "Berlayar Sinema" event landing page application. It includes the core HTML structure, CSS for styling, and JavaScript for interactivity. Key features implemented: - Responsive layout with Hero, Sponsors, and Film Screening sections. - A registration form that submits data to a backend webhook. - An interactive chat assistant powered by a separate webhook. - Modals for successful registration and a promotional flyer popup. - All necessary static assets, including images, logos, and posters. diff --git a/berlayar-sinema-app/assets/flyer1.jpeg b/berlayar-sinema-app/assets/flyer1.jpeg new file mode 100644 index 0000000..4312db6 Binary files /dev/null and b/berlayar-sinema-app/assets/flyer1.jpeg differ diff --git a/berlayar-sinema-app/assets/hero-cover.png b/berlayar-sinema-app/assets/hero-cover.png new file mode 100644 index 0000000..66658e3 Binary files /dev/null and b/berlayar-sinema-app/assets/hero-cover.png differ diff --git a/berlayar-sinema-app/assets/poster1.jpeg b/berlayar-sinema-app/assets/poster1.jpeg new file mode 100644 index 0000000..1529254 Binary files /dev/null and b/berlayar-sinema-app/assets/poster1.jpeg differ diff --git a/berlayar-sinema-app/assets/poster2.jpg b/berlayar-sinema-app/assets/poster2.jpg new file mode 100644 index 0000000..5e9b5c6 Binary files /dev/null and b/berlayar-sinema-app/assets/poster2.jpg differ diff --git a/berlayar-sinema-app/assets/poster3.jpeg b/berlayar-sinema-app/assets/poster3.jpeg new file mode 100644 index 0000000..2242213 Binary files /dev/null and b/berlayar-sinema-app/assets/poster3.jpeg differ diff --git a/berlayar-sinema-app/assets/sponsor1.png b/berlayar-sinema-app/assets/sponsor1.png new file mode 100644 index 0000000..dc1bb22 Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor1.png differ diff --git a/berlayar-sinema-app/assets/sponsor2.png b/berlayar-sinema-app/assets/sponsor2.png new file mode 100644 index 0000000..1f0a57b Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor2.png differ diff --git a/berlayar-sinema-app/assets/sponsor3.png b/berlayar-sinema-app/assets/sponsor3.png new file mode 100644 index 0000000..d40b5ed Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor3.png differ diff --git a/berlayar-sinema-app/assets/sponsor4.png b/berlayar-sinema-app/assets/sponsor4.png new file mode 100644 index 0000000..1a62120 Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor4.png differ diff --git a/berlayar-sinema-app/assets/sponsor5.png b/berlayar-sinema-app/assets/sponsor5.png new file mode 100644 index 0000000..7a920c2 Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor5.png differ diff --git a/berlayar-sinema-app/assets/sponsor6.png b/berlayar-sinema-app/assets/sponsor6.png new file mode 100644 index 0000000..57b4dd1 Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor6.png differ diff --git a/berlayar-sinema-app/assets/sponsor6.webp b/berlayar-sinema-app/assets/sponsor6.webp new file mode 100644 index 0000000..1cea761 Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor6.webp differ diff --git a/berlayar-sinema-app/assets/sponsor7.png b/berlayar-sinema-app/assets/sponsor7.png new file mode 100755 index 0000000..1fdde77 Binary files /dev/null and b/berlayar-sinema-app/assets/sponsor7.png differ diff --git a/berlayar-sinema-app/index.html b/berlayar-sinema-app/index.html new file mode 100644 index 0000000..449203f --- /dev/null +++ b/berlayar-sinema-app/index.html @@ -0,0 +1,321 @@ + + + + + + Berlayar Sinema: Registration + + + + + + +
+
+

BERLAYAR SINEMA

+

SILATURASA DAN MENENUN KOMUNITAS FILM INDONESIA

+

+ + Daftar Sekarang! + +
+
+ + +
+
+

Didukung Oleh

+ + +
+
+ + +
+
+

Special Screening

+ + +
+
+ + +
+
+

Daftar Sekarang!

+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ + +62 + + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+
+ + + + + + + + + + + + +
+ + + +
+ + + + \ No newline at end of file diff --git a/berlayar-sinema-app/script.js b/berlayar-sinema-app/script.js new file mode 100644 index 0000000..ad02d23 --- /dev/null +++ b/berlayar-sinema-app/script.js @@ -0,0 +1,358 @@ +document.addEventListener('DOMContentLoaded', () => { + const registrationForm = document.getElementById('eventRegistration'); + const successModal = document.getElementById('successModal'); + const closeModal = document.getElementById('closeModal'); + const chatButton = document.getElementById('chatButton'); + const chatWindow = document.getElementById('chatWindow'); + const closeChat = document.getElementById('closeChat'); + const imagePopup = document.getElementById('imagePopup'); + const closeImagePopup = document.getElementById('closeImagePopup'); + + // Form submission handling + if (registrationForm) { + registrationForm.addEventListener('submit', function(event) { + event.preventDefault(); + let isValid = true; + + // Simple validation example + const fullName = document.getElementById('fullName'); + const email = document.getElementById('email'); + const phone = document.getElementById('phone'); + const socialMedia = document.getElementById('socialMedia'); + const origin = document.getElementById('origin'); + const category = document.getElementById('category'); + const discovery = document.getElementById('discovery'); + const expectation = document.getElementById('expectation'); + + if (fullName.value.trim() === '') { + isValid = false; + alert('Please enter your full name.'); + } else if (email.value.trim() === '' || !email.value.includes('@')) { + isValid = false; + alert('Please enter a valid email address.'); + } else if (phone.value.trim() === '') { + isValid = false; + alert('Please enter your phone number.'); + } else if (socialMedia.value.trim() === '') { + isValid = false; + alert('Please enter your social media account.'); + } else if (origin.value.trim() === '') { + isValid = false; + alert('Please enter your origin.'); + } else if (category.value === '') { + isValid = false; + alert('Please select a category.'); + } else if (discovery.value.trim() === '') { + isValid = false; + alert('Please let us know how you discovered this event.'); + } else if (expectation.value.trim() === '') { + isValid = false; + alert('Please enter your expectation.'); + } + + if (isValid) { + let phoneNumber = phone.value.trim(); + if (phoneNumber.startsWith('0')) { + phoneNumber = '62' + phoneNumber.substring(1); + } else if (!phoneNumber.startsWith('62')) { + phoneNumber = '62' + phoneNumber; + } + + const formData = { + Nama: fullName.value, + Email: email.value, + WhatsApp: phoneNumber, + Akun_Sosial_Media: socialMedia.value, + Asal_Komunitas: origin.value, + Kategori_Peserta: category.value, + Informasi_Event: discovery.value, + Harapan: expectation.value, + Pertanyaan_Diskusi: document.getElementById('discussionTopic').value + }; + + fetch('https://bot.kediritechnopark.com/webhook/form-berlayar-sinema', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData), + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + console.log('Form submission successful:', data); + // Show success modal + if (successModal) { + successModal.classList.remove('hidden'); + successModal.classList.add('flex'); + } + // Reset form + registrationForm.reset(); + }) + .catch(error => { + console.error('Form submission failed:', error); + alert('Pendaftaran gagal. Silakan coba lagi nanti.'); + }); + + // Show success modal + if (successModal) { + successModal.classList.remove('hidden'); + successModal.classList.add('flex'); + } + + // Reset form + registrationForm.reset(); + } + }); + } + + // Close success modal + if (closeModal) { + closeModal.addEventListener('click', () => { + if (successModal) { + successModal.classList.add('hidden'); + successModal.classList.remove('flex'); + } + }); + } + + // Image popup on page load + if (imagePopup) { + // Show the popup after a short delay + setTimeout(() => { + imagePopup.classList.remove('hidden'); + imagePopup.classList.add('flex'); + }, 1000); // Show after 1 second + + // Close image popup + if (closeImagePopup) { + closeImagePopup.addEventListener('click', () => { + imagePopup.classList.add('hidden'); + imagePopup.classList.remove('flex'); + }); + } + } + + // Chat window functionality + if (chatButton && chatWindow && closeChat) { +// Film carousel functionality + const filmCarousel = document.getElementById('film-carousel'); + const prevFilmButton = document.getElementById('prev-film'); + const nextFilmButton = document.getElementById('next-film'); + + if (filmCarousel && prevFilmButton && nextFilmButton) { + let filmInterval; + + const startFilmCarousel = () => { + if (window.innerWidth < 768) { + filmInterval = setInterval(() => { + if (filmCarousel.scrollLeft + filmCarousel.clientWidth >= filmCarousel.scrollWidth) { + filmCarousel.scrollTo({ left: 0, behavior: 'smooth' }); + } else { + filmCarousel.scrollBy({ left: filmCarousel.clientWidth, behavior: 'smooth' }); + } + }, 3000); + } + }; + + const stopFilmCarousel = () => { + clearInterval(filmInterval); + }; + + const setupFilmCarousel = () => { + stopFilmCarousel(); + startFilmCarousel(); + updateCarouselButtons(); + }; + + const updateCarouselButtons = () => { + if (window.innerWidth < 768) { + prevFilmButton.classList.toggle('film-carousel-btn-hidden', filmCarousel.scrollLeft === 0); + nextFilmButton.classList.toggle('film-carousel-btn-hidden', filmCarousel.scrollLeft + filmCarousel.clientWidth >= filmCarousel.scrollWidth - 1); + } else { + prevFilmButton.classList.add('film-carousel-btn-hidden'); + nextFilmButton.classList.add('film-carousel-btn-hidden'); + } + }; + + filmCarousel.addEventListener('scroll', () => { + updateCarouselButtons(); + }); + + nextFilmButton.addEventListener('click', () => { + stopFilmCarousel(); + filmCarousel.scrollBy({ left: filmCarousel.clientWidth, behavior: 'smooth' }); + startFilmCarousel(); + }); + + prevFilmButton.addEventListener('click', () => { + stopFilmCarousel(); + filmCarousel.scrollBy({ left: -filmCarousel.clientWidth, behavior: 'smooth' }); + startFilmCarousel(); + }); + + window.addEventListener('resize', setupFilmCarousel); + const equalizeCardHeights = () => { + if (window.innerWidth < 768) { + const filmCards = filmCarousel.querySelectorAll('.film-card'); + let maxHeight = 0; + filmCards.forEach(card => { + card.style.height = 'auto'; + if (card.offsetHeight > maxHeight) { + maxHeight = card.offsetHeight; + } + }); + filmCards.forEach(card => { + card.style.height = `${maxHeight}px`; + }); + } else { + const filmCards = filmCarousel.querySelectorAll('.film-card'); + filmCards.forEach(card => { + card.style.height = '100%'; + }); + } + }; + + window.addEventListener('resize', () => { + setupFilmCarousel(); + equalizeCardHeights(); + }); + + setupFilmCarousel(); + equalizeCardHeights(); + } + const chatInput = chatWindow.querySelector('input[type="text"]'); + const sendButton = chatWindow.querySelector('button.bg-yellow-500'); + const chatMessagesContainer = chatWindow.querySelector('.p-4.h-64.overflow-y-auto'); + let sessionId = null; + + function generateUUID() { // Public Domain/MIT + var d = new Date().getTime();//Timestamp + var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16;//random number between 0 and 16 + if(d > 0){//Use timestamp until depleted + r = (d + r)%16 | 0; + d = Math.floor(d/16); + } else {//Use microseconds since page-load if supported + r = (d2 + r)%16 | 0; + d2 = Math.floor(d2/16); + } + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + } + + chatButton.addEventListener('click', () => { + chatWindow.classList.toggle('hidden'); + if (!sessionId) { + sessionId = generateUUID(); + console.log('Chat session started with ID:', sessionId); + } + }); + + closeChat.addEventListener('click', () => { + chatWindow.classList.add('hidden'); + }); + + sendButton.addEventListener('click', () => { + sendMessage(); + }); + + chatInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + sendMessage(); + } + }); + + function sendMessage() { + const messageText = chatInput.value.trim(); + if (messageText === '') return; + + // Display sent message + const sentMessageDiv = document.createElement('div'); + sentMessageDiv.className = 'mb-4 text-right'; + sentMessageDiv.innerHTML = `

${messageText}

`; + chatMessagesContainer.appendChild(sentMessageDiv); + chatInput.value = ''; // Clear input + + // Show typing indicator + const typingIndicator = document.createElement('div'); + typingIndicator.className = 'mb-4 text-left typing-indicator'; + typingIndicator.innerHTML = ''; + chatMessagesContainer.appendChild(typingIndicator); + + // Scroll to bottom + chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight; + + // Send message to webhook + fetch('https://bot.kediritechnopark.com/webhook/chat-berlayar-sinema', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ message: messageText, sessionId: sessionId }), + }) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + console.log('Chat message sent successfully:', data); + // Remove typing indicator + chatMessagesContainer.removeChild(typingIndicator); + + // Display response from the webhook + if (data && data.output) { + const receivedMessageDiv = document.createElement('div'); + receivedMessageDiv.className = 'mb-4 text-left'; + receivedMessageDiv.innerHTML = `

${data.output}

`; + chatMessagesContainer.appendChild(receivedMessageDiv); + chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight; + } + }) + .catch(error => { + console.error('Chat message failed to send:', error); + // Remove typing indicator + chatMessagesContainer.removeChild(typingIndicator); + + // Optionally, display an error message in the chat + const errorMessageDiv = document.createElement('div'); + errorMessageDiv.className = 'mb-4 text-left'; + errorMessageDiv.innerHTML = `

Error: Could not send message.

`; + chatMessagesContainer.appendChild(errorMessageDiv); + chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight; + }); + } + } + + // Observer to disable chat button when image popup is active + if (imagePopup && chatButton) { + const observer = new MutationObserver((mutationsList) => { + for (const mutation of mutationsList) { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + if (!imagePopup.classList.contains('hidden')) { + chatButton.classList.add('opacity-50', 'cursor-not-allowed'); + chatButton.disabled = true; + } else { + chatButton.classList.remove('opacity-50', 'cursor-not-allowed'); + chatButton.disabled = false; + } + } + } + }); + + observer.observe(imagePopup, { attributes: true }); + + // Initial check + if (!imagePopup.classList.contains('hidden')) { + chatButton.classList.add('opacity-50', 'cursor-not-allowed'); + chatButton.disabled = true; + } + } +}); \ No newline at end of file diff --git a/berlayar-sinema-app/style.css b/berlayar-sinema-app/style.css new file mode 100644 index 0000000..25eec28 --- /dev/null +++ b/berlayar-sinema-app/style.css @@ -0,0 +1,282 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&display=swap'); + +body { + font-family: 'Roboto', sans-serif; + background-color: #f8f9fa; +} + +.hero-section { + background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), + url('assets/hero-cover.png'); + background-size: cover; + background-position: center; + backdrop-filter: blur(10px); +} + +.glass-card { + background: rgba(255, 255, 255, 0.2); + backdrop-filter: blur(10px); + border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2); +} + +.film-card { + transition: transform 0.3s ease, box-shadow 0.3s ease; + display: flex; + flex-direction: column; + height: 100%; +} + +.film-card .p-6 { + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.film-card .p-6 .flex-wrap { + margin-top: auto; +} + +.film-card:hover { + transform: translateY(-10px); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} + +.sponsor-logo { + filter: grayscale(100%); + opacity: 0.7; + transition: all 0.3s ease; +} + +.sponsor-logo:hover { + filter: grayscale(0%); + opacity: 1; +} + +.form-input { + transition: all 0.3s ease; +} + +.form-input:focus { + border-color: #6b46c1; + box-shadow: 0 0 0 3px rgba(107, 70, 193, 0.2); +} + +.title-font { + font-family: 'Poppins', sans-serif; + font-weight: 700; +} + +/* Form styling */ +#registration-form { + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +#registration-form input, +#registration-form textarea, +#registration-form select { + transition: all 0.3s ease; +} + +#registration-form input:focus, +#registration-form textarea:focus, +#registration-form select:focus { + box-shadow: 0 0 0 3px rgba(192, 132, 252, 0.5); +} + + +#registration-form input::placeholder, +#registration-form textarea::placeholder { + color: #A9A9A9; /* Solid light gray */ +} + +#registration-form input:not(:placeholder-shown), +#registration-form textarea:not(:placeholder-shown), +#registration-form select:not([value=""]) { + color: #4B0082; /* Solid dark purple for active input */ +} + +@keyframes scroll { + 0% { transform: translateX(0); } + 100% { transform: translateX(-50%); } +} + +.animate-scroll { + display: inline-block; + animation: scroll 20s linear infinite; +} + +.sponsor-carousel:hover .animate-scroll { + animation-play-state: paused; +} + +.sponsor-container { + position: relative; + overflow: hidden; +} + + +.sponsor-container::before, .sponsor-container::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: 100px; + pointer-events: none; + z-index: 2; +} + +@media (max-width: 640px) { + .sponsor-container::before, .sponsor-container::after { + width: 50px; + } +} + +.sponsor-container::before { + left: 0; + background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0) 100%); +} + +.sponsor-container::after { + right: 0; + background: linear-gradient(270deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0) 100%); +} + +@media (max-width: 640px) { + .sponsor-container::before { + background: linear-gradient(90deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0) 100%); + } + .sponsor-container::after { + background: linear-gradient(270deg, rgba(255,255,255,0.9) 0%, rgba(255,255,255,0) 100%); + } +} + +.sponsor-track { + display: flex; + width: max-content; + animation: scroll 20s linear infinite; +} + +.sponsor-track:hover { + animation-play-state: paused; +} + +.sponsor-slide { + display: flex; + align-items: center; + justify-content: center; + padding: 0 2rem; +} + +/* Improved dropdown styles */ +#registration-form select { + border: 1px solid rgba(255, 255, 255, 0.3); +} + +#registration-form select option { + background-color: #ffffff; + color: #6b46c1; +} + +/* Chat window styles */ +#chatWindow { + background: rgba(255, 255, 255, 0.15); /* Slightly more opaque than glass-card for better readability */ + backdrop-filter: blur(15px); /* Stronger blur for frosted effect */ + border-radius: 20px; + border: 1px solid rgba(255, 255, 255, 0.3); /* Slightly more visible border */ + box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); /* Stronger shadow */ + transition: all 0.3s ease; + transform-origin: bottom right; +} + +#chatWindow:not(.hidden) { + animation: chatFadeIn 0.3s ease; +} + +@keyframes chatFadeIn { + from { + opacity: 0; + transform: scale(0.8) translateY(10px); + } + + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} +/* Chat bubble styles */ +.chat-message-received { + background-color: #e9d5ff; /* Purple pastel for received messages */ + color: #4b0082; /* Dark purple text */ + border-radius: 1.25rem; /* Rounded corners */ + padding: 0.75rem 1rem; + display: inline-block; + max-width: 75%; /* Limit width */ + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + margin-bottom: 0.5rem; +} + +.chat-message-sent { + background-color: #6b46c1; /* Purple for sent messages */ + color: #ffffff; /* White text */ + border-radius: 1.25rem; + padding: 0.75rem 1rem; + display: inline-block; + max-width: 75%; + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + margin-bottom: 0.5rem; +} + +/* Typing indicator styles */ +.typing-indicator { + display: flex; + align-items: center; + padding: 0.75rem 1rem; +} + +.typing-indicator span { + height: 8px; + width: 8px; + background-color: #4b0082; + border-radius: 50%; + display: inline-block; + margin: 0 2px; + animation: bounce 1.4s infinite both; +} + +.typing-indicator span:nth-child(1) { + animation-delay: -0.32s; +} + +.typing-indicator span:nth-child(2) { + animation-delay: -0.16s; +} + +@keyframes bounce { + 0%, 80%, 100% { + transform: scale(0); + } + 40% { + transform: scale(1.0); + } + +} + +.film-carousel-btn-hidden { + opacity: 0 !important; + pointer-events: none !important; +} + +/* Hide scrollbar for film carousel */ +#film-carousel::-webkit-scrollbar { + display: none; +} + +#film-carousel { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + gap: 1rem; + align-items: stretch; + padding-bottom: 1rem; +} \ No newline at end of file