From 39afff75c4482a1760ddc1568ca03184c33addd9 Mon Sep 17 00:00:00 2001 From: "emmanuel.rizky" Date: Sat, 12 Jul 2025 09:50:35 +0700 Subject: [PATCH] feat: Add initial chat page UI This commit introduces a new, self-contained chat page, including the necessary HTML, CSS, and JavaScript files. The page features a modern, responsive design with a glassmorphism effect on the main chat container, a gradient background with animated floating circles, and a clear layout for displaying sent and received messages. - `index.html`: Defines the structure for the header, message area, and input form. - `style.css`: Provides the visual styling, including the glass effect, message bubbles, and responsive adjustments. - `script.js`: Contains the initial client-side logic for adding messages to the chat. --- chat-page/index.html | 79 +++++++++ chat-page/script.js | 126 +++++++++++++ chat-page/style.css | 412 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 617 insertions(+) create mode 100644 chat-page/index.html create mode 100644 chat-page/script.js create mode 100644 chat-page/style.css diff --git a/chat-page/index.html b/chat-page/index.html new file mode 100644 index 0000000..50d7e5d --- /dev/null +++ b/chat-page/index.html @@ -0,0 +1,79 @@ + + + + + + Halaman Chat + + + +
+ + + + +
+ +
+ + +
+ +
+
+ Hai, ada kabar terbaru? +
+
10:00
+
+ + +
+
+ Iya, proyek baru sudah selesai! +
+
10:01
+
+
+ + +
+ + + + + + +
+
+ + + + diff --git a/chat-page/script.js b/chat-page/script.js new file mode 100644 index 0000000..26a17fa --- /dev/null +++ b/chat-page/script.js @@ -0,0 +1,126 @@ +// Fungsi untuk menambahkan pesan baru ke area chat +function addMessage(content, isSent) { + const chatArea = document.querySelector('.chat-area'); + + // Buat elemen pesan + const message = document.createElement('div'); + message.className = `message ${isSent ? 'sent' : 'received'}`; + + // Tambahkan konten pesan + const messageContent = document.createElement('div'); + messageContent.className = 'message-content'; + messageContent.textContent = content; + + // Tambahkan waktu pesan + const now = new Date(); + const time = now.getHours().toString().padStart(2, '0') + ':' + + now.getMinutes().toString().padStart(2, '0'); + + const messageTime = document.createElement('div'); + messageTime.className = 'message-time'; + messageTime.textContent = time; + + // Gabungkan semua elemen + message.appendChild(messageContent); + message.appendChild(messageTime); + + // Tambahkan ke area chat dengan animasi + chatArea.appendChild(message); + + // Scroll ke bawah untuk melihat pesan terbaru + chatArea.scrollTop = chatArea.scrollHeight; +} + +// Fungsi untuk menampilkan indikator mengetik +function showTypingIndicator() { + const chatArea = document.querySelector('.chat-area'); + const typingIndicator = document.createElement('div'); + typingIndicator.className = 'typing-indicator'; + typingIndicator.innerHTML = ` +
+ + + +
+ `; + chatArea.appendChild(typingIndicator); + chatArea.scrollTop = chatArea.scrollHeight; + return typingIndicator; +} + +// Fungsi untuk menyembunyikan indikator mengetik +function hideTypingIndicator(indicator) { + if (indicator) { + indicator.remove(); + } +} + +// Setup Intersection Observer untuk efek fade-out +function setupMessageObserver() { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (!entry.isIntersecting) { + // Ketika elemen keluar dari viewport + entry.target.style.opacity = '0.3'; + entry.target.style.transform = 'translateY(10px)'; + } else { + // Ketika elemen masuk ke viewport + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + } + }); + }, { + threshold: 0.1 // Trigger ketika 10% elemen masih terlihat + }); + + // Terapkan observer ke semua pesan + document.querySelectorAll('.message').forEach(message => { + observer.observe(message); + }); +} + +// Fungsi untuk mengirim pesan +function sendMessage() { + const input = document.querySelector('.message-input'); + const message = input.value.trim(); + + if (message) { + addMessage(message, true); + input.value = ''; + + // Simulasikan balasan otomatis setelah 2 detik + setTimeout(() => { + const typingIndicator = showTypingIndicator(); + + // Setelah 3 detik, sembunyikan indikator dan tampilkan balasan + setTimeout(() => { + hideTypingIndicator(typingIndicator); + addMessage('Pesan Anda sudah saya terima. Terima kasih!', false); + }, 3000); + }, 2000); + } +} + +// Event listener untuk tombol kirim +document.querySelector('.send-btn').addEventListener('click', sendMessage); + +// Event listener untuk tombol Enter +document.querySelector('.message-input').addEventListener('keypress', (e) => { + if (e.key === 'Enter') { + sendMessage(); + } +}); + +// Inisialisasi chat setelah DOM selesai dimuat +document.addEventListener('DOMContentLoaded', () => { + // Tampilkan indikator mengetik saat halaman pertama dimuat + const initialTyping = showTypingIndicator(); + + // Setelah 3 detik, tampilkan pesan sapaan + setTimeout(() => { + hideTypingIndicator(initialTyping); + addMessage('Hai, ada yang bisa saya bantu?', false); + }, 3000); + + setupMessageObserver(); +}); diff --git a/chat-page/style.css b/chat-page/style.css new file mode 100644 index 0000000..8b31a1d --- /dev/null +++ b/chat-page/style.css @@ -0,0 +1,412 @@ +/* Reset CSS */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +body { + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + background: + linear-gradient(135deg, #6a11cb 0%, #2575fc 100%), + repeating-linear-gradient(45deg, rgba(255,255,255,0.05) 0px, rgba(255,255,255,0.05) 10px, transparent 10px, transparent 20px); + padding: 20px; +} + +/* Chat container styling */ +.chat-container { + background: rgba(255, 255, 255, 0.92); + backdrop-filter: blur(12px); + border-radius: 20px; + box-shadow: + 0 20px 50px rgba(0, 0, 0, 0.3), + 0 0 0 2px rgba(255, 255, 255, 0.1) inset, + 0 10px 30px rgba(0, 0, 0, 0.1) inset; + width: 100%; + max-width: 800px; + height: 90vh; + max-height: 800px; + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +/* Glass effect header styling */ +.chat-header { + padding: 15px 20px; + background: rgba(255, 255, 255, 0.25); + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + display: flex; + align-items: center; + border-radius: 20px 20px 0 0; + backdrop-filter: blur(12px); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + position: sticky; + top: 0; + z-index: 10; +} + +/* Enhanced glass effect for both header and input area */ +.chat-header, .input-area { + background: rgba(255, 255, 255, 0.18); + backdrop-filter: blur(15px); + -webkit-backdrop-filter: blur(15px); + border: 1px solid rgba(255, 255, 255, 0.25); +} + +.user-info { + display: flex; + align-items: center; + gap: 15px; +} + +.avatar { + width: 50px; + height: 50px; + border-radius: 50%; + background: linear-gradient(to right, #6a11cb, #2575fc); + color: white; + display: flex; + justify-content: center; + align-items: center; + font-size: 20px; + font-weight: bold; +} + +/* Floating circles background */ +.floating-circles { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + z-index: -1; +} + +.floating-circles li { + position: absolute; + display: block; + list-style: none; + width: 20px; + height: 20px; + background: rgba(106, 17, 203, 0.15); + animation: floating 25s linear infinite; + bottom: -150px; + border-radius: 50%; +} + +.floating-circles li:nth-child(1) { + left: 25%; + width: 80px; + height: 80px; + animation-delay: 0s; +} + +.floating-circles li:nth-child(2) { + left: 10%; + width: 20px; + height: 20px; + animation-delay: 2s; + animation-duration: 12s; +} + +.floating-circles li:nth-child(3) { + left: 70%; + width: 20px; + height: 20px; + animation-delay: 4s; +} + +.floating-circles li:nth-child(4) { + left: 40%; + width: 60px; + height: 60px; + animation-delay: 0s; + animation-duration: 18s; +} + +.floating-circles li:nth-child(5) { + left: 65%; + width: 20px; + height: 20px; + animation-delay: 0s; +} + +.floating-circles li:nth-child(6) { + left: 75%; + width: 110px; + height: 110px; + animation-delay: 3s; +} + +.floating-circles li:nth-child(7) { + left: 35%; + width: 150px; + height: 150px; + animation-delay: 7s; +} + +.floating-circles li:nth-child(8) { + left: 50%; + width: 25px; + height: 25px; + animation-delay: 15s; + animation-duration: 45s; +} + +.floating-circles li:nth-child(9) { + left: 20%; + width: 15px; + height: 15px; + animation-delay: 2s; + animation-duration: 35s; +} + +.floating-circles li:nth-child(10) { + left: 85%; + width: 150px; + height: 150px; + animation-delay: 0s; + animation-duration: 11s; +} + +/* Floating animation */ +@keyframes floating { + 0% { + transform: translateY(0) rotate(0deg); + opacity: 0.7; + border-radius: 50%; + } + 100% { + transform: translateY(-1000px) rotate(720deg); + opacity: 0; + border-radius: 50%; + } +} + +/* Chat area styling */ +.chat-area { + flex-grow: 1; + padding: 20px; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 15px; + position: relative; +} + +.message { + max-width: 70%; + padding: 12px 16px; + border-radius: 18px; + position: relative; + animation: fadeIn 0.3s ease; + transition: opacity 0.5s ease, transform 0.5s ease; +} + +.message.received { + align-self: flex-start; + background: #f0f0f0; + border-bottom-left-radius: 4px; +} + +.message.sent { + align-self: flex-end; + background: linear-gradient(to right, #6a11cb, #2575fc); + color: white; + border-bottom-right-radius: 4px; +} + +.message-content { + word-wrap: break-word; + line-height: 1.4; +} + +.message-time { + font-size: 0.7rem; + text-align: right; + margin-top: 5px; + opacity: 0.7; +} + +.message.sent .message-time { + color: rgba(255, 255, 255, 0.7); +} + +.message.received .message-time { + color: rgba(0, 0, 0, 0.5); +} + +/* Input area styling with animation and glass effect */ +.input-area { + display: flex; + padding: 15px; + border-top: 1px solid rgba(0, 0, 0, 0.05); + align-items: center; + gap: 10px; + transition: transform 0.3s ease, box-shadow 0.3s ease; + position: sticky; + bottom: 0; + z-index: 10; +} + +.input-area:focus-within { + transform: translateY(-5px); + box-shadow: 0 -5px 20px rgba(106, 17, 203, 0.2); +} + +.icon-btn { + background: none; + border: none; + cursor: pointer; + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + transition: background 0.3s ease; +} + +.icon-btn:hover { + background: rgba(106, 17, 203, 0.1); +} + +.icon-btn svg { + width: 24px; + height: 24px; + fill: #6a11cb; +} + +/* Efek untuk pesan saat keluar dari viewport */ +.message:not(:hover) { + transition: opacity 0.5s ease, transform 0.5s ease; +} + +.message-input { + flex-grow: 1; + padding: 12px 15px; + border: 1px solid #ddd; + border-radius: 25px; + background: white; + font-size: 16px; + transition: all 0.3s ease; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05) inset; +} + +.message-input:focus { + border-color: #6a11cb; + box-shadow: 0 0 0 3px rgba(106, 17, 203, 0.2); + outline: none; +} + +.send-btn { + background: linear-gradient(to right, #6a11cb, #2575fc); + border: none; + border-radius: 50%; + width: 45px; + height: 45px; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 5px 15px rgba(37, 117, 252, 0.4); +} + +.send-btn:active { + transform: scale(0.95); +} + +.send-btn:hover { + transform: scale(1.05); + box-shadow: 0 8px 20px rgba(37, 117, 252, 0.6); +} + +.send-btn svg { + width: 24px; + height: 24px; + fill: white; +} + +/* Animations */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Typing animation */ +@keyframes wave { + 0%, 60%, 100% { + transform: scaleY(0.4); + } + 30% { + transform: scaleY(1); + } +} + +/* Typing indicator */ +.typing-indicator { + display: flex; + align-self: flex-start; + background: #f0f0f0; + padding: 10px 15px; + border-radius: 18px; + margin-bottom: 15px; +} + +.typing-dots { + display: flex; + align-items: center; + height: 17px; +} + +.typing-dots span { + display: block; + width: 6px; + height: 6px; + border-radius: 50%; + background: #6a11cb; + margin: 0 2px; +} + +.typing-dots span:nth-child(1) { + animation: wave 1.2s infinite; +} + +.typing-dots span:nth-child(2) { + animation: wave 1.2s infinite 0.2s; +} + +.typing-dots span:nth-child(3) { + animation: wave 1.2s infinite 0.4s; +} + +/* Responsive design */ +@media (max-width: 768px) { + .chat-container { + height: 100vh; + max-height: none; + border-radius: 0; + } + + .message { + max-width: 85%; + } + + body { + padding: 0; + } +}