Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>HIVE PROP</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| /* Root Variables */ | |
| :root { | |
| --primary-color: #31511E; | |
| --secondary-color: #F6FCDF; | |
| --accent-color: #859F3D; | |
| --text-primary: rgb(26, 26, 25); | |
| --text-secondary: rgb(49, 81, 30); | |
| --border-radius: 20px; | |
| --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| --transition: all 0.3s ease; | |
| --chat-bg: #F6FCDF; | |
| --bubble-user: #ffffff; | |
| --bubble-bot: #31511E; | |
| } | |
| /* Property Search Styles */ | |
| body { | |
| font-family: 'Poppins', sans-serif; | |
| background-color: #F6FCDF; | |
| margin: 0; | |
| padding: 20px; | |
| color: var(--text-primary); | |
| } | |
| h1 { | |
| text-align: center; | |
| color: var(--primary-color); | |
| margin-bottom: 40px; | |
| font-size: 3rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| letter-spacing: 2px; | |
| position: relative; | |
| animation: fadeIn 1s ease-in-out; | |
| } | |
| h1::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -10px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 100px; | |
| height: 4px; | |
| background: var(--primary-color); | |
| border-radius: 2px; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* Property Search Components */ | |
| .search-container { | |
| display: flex; | |
| justify-content: center; | |
| gap: 20px; | |
| margin-bottom: 40px; | |
| } | |
| #queryForm { | |
| display: flex; | |
| gap: 20px; | |
| align-items: center; | |
| } | |
| #userQuery { | |
| width: 400px; | |
| padding: 15px 25px; | |
| border: 2px solid #E1E8ED; | |
| border-radius: var(--border-radius); | |
| font-size: 1.1rem; | |
| transition: var(--transition); | |
| background: white; | |
| box-shadow: var(--box-shadow); | |
| } | |
| /* Property Card Styles */ | |
| .property { | |
| display: flex; | |
| flex-direction: row; | |
| background: white; | |
| border-radius: 30px; | |
| box-shadow: var(--box-shadow); | |
| margin-bottom: 30px; | |
| overflow: hidden; | |
| width: 90%; | |
| margin-left: auto; | |
| margin-right: auto; | |
| transition: var(--transition); | |
| gap: 20px; | |
| padding: 20px; | |
| animation: slideUp 0.5s ease-out; | |
| position: relative; | |
| } | |
| @keyframes slideUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .image-container { | |
| flex: 2; | |
| position: relative; | |
| overflow: hidden; | |
| border-radius: 20px; | |
| box-shadow: var(--box-shadow); | |
| } | |
| .carousel { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .carousel-images { | |
| display: flex; | |
| transition: transform 0.5s ease-in-out; | |
| height: 100%; | |
| } | |
| .carousel-image { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| flex-shrink: 0; | |
| border-radius: 20px; | |
| } | |
| .property-details { | |
| flex: 3; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* Accordion Styles */ | |
| .accordion-section { | |
| border-bottom: 1px solid #E1E8ED; | |
| } | |
| .accordion-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 15px 0; | |
| cursor: pointer; | |
| border-bottom: 1px solid #E1E8ED; | |
| transition: var(--transition); | |
| } | |
| .accordion-header:hover { | |
| background-color: var(--secondary-color); | |
| border-radius: 10px; | |
| } | |
| .accordion-content { | |
| display: none; | |
| padding: 15px 0; | |
| color: var(--text-secondary); | |
| line-height: 1.6; | |
| } | |
| /* Add active class for accordion */ | |
| .accordion-header.active + .accordion-content { | |
| display: block; /* Show content when active */ | |
| } | |
| /* Loading Spinner */ | |
| .loading-spinner { | |
| border: 4px solid rgba(74, 144, 226, 0.1); | |
| border-left-color: var(--primary-color); | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| animation: spin 1s linear infinite; | |
| margin: 40px auto; | |
| } | |
| .loading-message { | |
| width: 40px; | |
| height: 40px; | |
| margin: auto auto; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .error-message { | |
| color: #DC3545; | |
| text-align: center; | |
| font-weight: 500; | |
| margin-top: 20px; | |
| } | |
| @media (max-width: 1024px) { | |
| .property { | |
| flex-direction: column; | |
| width: 95%; | |
| } | |
| .image-container { | |
| width: 100%; | |
| height: 300px; | |
| } | |
| #userQuery { | |
| width: 300px; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .search-container { | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| #queryForm { | |
| flex-direction: column; | |
| width: 100%; | |
| max-width: 400px; | |
| } | |
| #userQuery { | |
| width: 100%; | |
| } | |
| button { | |
| width: 100%; | |
| } | |
| } | |
| #userQuery:focus { | |
| outline: none; | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2); | |
| } | |
| #userQuery::placeholder { | |
| color: #A4A4A4; | |
| font-weight: 300; | |
| } | |
| .property:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1); | |
| } | |
| .carousel-nav { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .carousel-dot { | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.5); | |
| cursor: pointer; | |
| transition: var(--transition); | |
| } | |
| .carousel-dot.active { | |
| background: white; | |
| } | |
| .property-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .property-header h2 { | |
| font-size: 1.8rem; | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| margin: 0 0 10px 0; | |
| } | |
| .property-type { | |
| display: inline-block; | |
| padding: 8px 16px; | |
| background-color: var(--accent-color); | |
| color: white; | |
| border-radius: 20px; | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| } | |
| .property-info { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); | |
| gap: 20px; | |
| padding: 20px 0; | |
| border-top: 1px solid #E1E8ED; | |
| border-bottom: 1px solid #E1E8ED; | |
| max-height: 300px; /* Set a max height for scrolling */ | |
| overflow-y: auto; /* Enable vertical scrolling */ | |
| scrollbar-width: thin; /* For Firefox */ | |
| scrollbar-color: var(--primary-color) var(--secondary-color); /* For Firefox */ | |
| } | |
| .property-info::-webkit-scrollbar { | |
| width: 8px; /* Width of the scrollbar */ | |
| } | |
| .property-info::-webkit-scrollbar-track { | |
| background: var(--secondary-color); /* Color of the scrollbar track */ | |
| border-radius: 10px; | |
| } | |
| .property-info::-webkit-scrollbar-thumb { | |
| background: var(--primary-color); /* Color of the scrollbar thumb */ | |
| border-radius: 10px; | |
| } | |
| .property-info::-webkit-scrollbar-thumb:hover { | |
| background: var(--accent-color); /* Color of the scrollbar thumb on hover */ | |
| } | |
| .description { | |
| margin: 10px 0; | |
| line-height: 1.6; | |
| color: var(--text-secondary); | |
| } | |
| .key-features { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin: 10px 0; | |
| align-items: center; | |
| } | |
| .feature-pill { | |
| background-color: var(--secondary-color); | |
| padding: 2px 10px; | |
| border-radius: 10px; | |
| font-size: 0.9rem; | |
| color: var(--text-primary); | |
| font-weight: 500; | |
| transition: var(--transition); | |
| border: 2px solid var(--primary-color); /* Added border */ | |
| } | |
| .feature-pill:hover { | |
| background-color: var(--primary-color); | |
| color: white; | |
| transform: translateY(-2px); | |
| } | |
| .amenities-card { | |
| background-color: var(--secondary-color); | |
| border-radius: var(--border-radius); | |
| padding: 20px; | |
| margin: 10px 0; | |
| } | |
| .amenities-pills { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin-top: 10px; | |
| } | |
| .amenity-pill { | |
| background-color: var(--secondary-color); | |
| padding: 2px 10px; | |
| border-radius: 10px; | |
| font-size: 0.9rem; | |
| color: var(--text-primary); | |
| font-weight: 500; | |
| transition: var(--transition); | |
| border: 2px solid var(--primary-color); /* Added border */ | |
| } | |
| .amenity-pill:hover { | |
| background-color: var(--primary-color); | |
| color: white; | |
| transform: translateY(-2px); | |
| } | |
| .construction-status { | |
| display: flex; | |
| justify-content: space-between; | |
| padding-top: 20px; | |
| color: var(--text-secondary); | |
| } | |
| .accordion { | |
| background-color: var(--secondary-color); | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--box-shadow); | |
| margin-bottom: 20px; | |
| overflow: hidden; | |
| } | |
| .accordion-header:hover { | |
| } | |
| .accordion-header .arrow { | |
| transition: transform 0.3s ease; | |
| } | |
| .accordion-header.active .arrow { | |
| transform: rotate(90deg); | |
| } | |
| .accordion-header strong { | |
| font-size: 1.1rem; | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .accordion-arrow { | |
| transition: transform 0.3s ease; | |
| font-size: 0.9rem; | |
| color: var(--primary-color); | |
| } | |
| .accordion-arrow.active { | |
| transform: rotate(180deg); | |
| } | |
| .accordion-section:last-child { | |
| border-bottom: none; | |
| } | |
| .accordion-header.active + .accordion-content { | |
| display: block; /* Show content when active */ | |
| } | |
| .favorite-button { | |
| position: absolute; | |
| top: 10px; | |
| right: 10px; | |
| background: none; | |
| border: none; | |
| color: white; | |
| cursor: pointer; | |
| padding: 10px; | |
| border-radius: 50px; | |
| display: flex; | |
| background-color: var(--primary-color); | |
| } | |
| .favorite-button.active { | |
| color: var(--primary-color); | |
| background-color: var(--accent-color); | |
| border: 2px solid var(--primary-color); | |
| } | |
| /* Chat Assistant Styles */ | |
| .chatbot-container { | |
| position: fixed; | |
| bottom: 2rem; | |
| left: 0; | |
| right: 0; | |
| display: flex; | |
| justify-content: space-between; | |
| padding: 0 2rem; | |
| z-index: 1000; | |
| } | |
| .chatbot-icon { | |
| width: 3.5rem; | |
| height: 3.5rem; | |
| background: var(--primary-color); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .chatbot-icon:hover { | |
| transform: scale(1.1); | |
| box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2); | |
| } | |
| .chatbot-icon i { | |
| color: white; | |
| font-size: 1.5rem; | |
| } | |
| .chat-container { | |
| position: fixed; | |
| bottom: 6rem; | |
| width: 450px; | |
| height: 35rem; | |
| background: var(--chat-bg); | |
| border-radius: 1rem; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); | |
| display: none; | |
| flex-direction: column; | |
| overflow: hidden; | |
| z-index: 999; | |
| animation: slideUp 0.3s ease-out; | |
| border: 1px solid var(--primary-color); /* Added border */ | |
| } | |
| .chat-header { | |
| background: var(--primary-color); | |
| color: white; | |
| padding: 1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.5rem; | |
| position: relative; | |
| border-top-left-radius: 1rem; | |
| border-top-right-radius: 1rem; | |
| } | |
| .chat-header img { | |
| width: 2rem; | |
| height: 2rem; | |
| border-radius: 50%; | |
| } | |
| .chat-header .close-button { | |
| position: absolute; | |
| right: 1rem; | |
| background: none; | |
| border: none; | |
| font-size: 1.5rem; | |
| color: white; | |
| cursor: pointer; | |
| } | |
| /* Custom Scrollbar for Chatbot */ | |
| .chat-body::-webkit-scrollbar { | |
| width: 12px; /* Width of the scrollbar */ | |
| } | |
| .chat-body::-webkit-scrollbar-track { | |
| background: var(--secondary-color); /* Color of the scrollbar track */ | |
| border-radius: 20px; /* Rounded corners for the track */ | |
| border: 2px solid var(--primary-color); /* Border around the track */ | |
| } | |
| .chat-body::-webkit-scrollbar-thumb { | |
| background: var(--primary-color); /* Color of the scrollbar thumb */ | |
| border-radius: 50%; /* Rounded corners for the thumb */ | |
| border: 2px solid var(--secondary-color); /* Border around the thumb */ | |
| } | |
| .chat-body::-webkit-scrollbar-thumb:hover { | |
| background: var(--accent-color); /* Color of the scrollbar thumb on hover */ | |
| } | |
| /* For Firefox */ | |
| .chat-body { | |
| scrollbar-width: thin; /* Width of the scrollbar */ | |
| scrollbar-color: var(--primary-color) var(--secondary-color); /* Color of the scrollbar thumb and track */ | |
| flex: 1; | |
| padding: 1rem; | |
| overflow-y: auto; | |
| background: var(--background-color); /* Color of the scrollbar thumb and track */ | |
| } | |
| .message { | |
| display: flex; | |
| flex-direction: column; | |
| margin-bottom: 1rem; | |
| } | |
| .message-content { | |
| max-width: 80%; | |
| padding: 0.75rem 1rem; | |
| border-radius: 1rem; | |
| position: relative; | |
| animation: messageAppear 0.3s ease-out; | |
| } | |
| @keyframes messageAppear { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .user-message { | |
| align-items: flex-end; | |
| } | |
| .bot-message { | |
| align-items: flex-start; | |
| } | |
| .user-message .message-content { | |
| background: var(--bubble-user); | |
| color: var(--text-color); | |
| border-bottom-right-radius: 0.25rem; | |
| } | |
| .bot-message .message-content { | |
| background: var(--bubble-bot); | |
| color: white; | |
| border-bottom-left-radius: 0.25rem; | |
| } | |
| .typing-indicator { | |
| display: none; | |
| padding: 0.75rem 1rem; | |
| background: var(--bubble-bot); | |
| color: white; | |
| border-radius: 1rem; | |
| border-bottom-left-radius: 0.25rem; | |
| margin-bottom: 1rem; | |
| width: fit-content; | |
| } | |
| .typing-dot { | |
| display: inline-block; | |
| width: 0.5rem; | |
| height: 0.5rem; | |
| margin: 0 0.1rem; | |
| background: white; | |
| border-radius: 50%; | |
| animation: typing 1.4s infinite ease-in-out; | |
| } | |
| .typing-dot:nth-child(1) { animation-delay: 200ms; } | |
| .typing-dot:nth-child(2) { animation-delay: 300ms; } | |
| .typing-dot:nth-child(3) { animation-delay: 400ms; } | |
| @keyframes typing { | |
| 0%, 60%, 100% { transform: translateY(0); } | |
| 30% { transform: translateY(-6px); } | |
| } | |
| .chat-footer { | |
| padding: 1rem; | |
| background: var(--chat-bg); | |
| border-top: 1px solid rgba(0, 0, 0, 0.1); | |
| } | |
| .input-group { | |
| display: flex; | |
| gap: 0.5rem; | |
| align-items: center; | |
| } | |
| .chat-input { | |
| flex: 1; | |
| padding: 0.75rem 1rem; | |
| border: 1px solid rgba(0, 0, 0, 0.1); | |
| border-radius: 1.5rem; | |
| outline: none; | |
| font-size: 0.95rem; | |
| transition: border-color 0.3s ease; | |
| } | |
| .chat-input:focus { | |
| border-color: var(--primary-color); | |
| } | |
| .send-chatbutton { | |
| background: var(--bubble-bot); | |
| color: white; | |
| border: 2px solid var(--primary-color); /* Added border */ | |
| width: 2.5rem; | |
| height: 2.5rem; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: background-color 0.3s ease; | |
| border-radius: 20px; | |
| } | |
| .send-chatbutton:hover { | |
| background: var(--accent-color); | |
| } | |
| .timestamp { | |
| font-size: 0.75rem; | |
| color: #64748b; | |
| margin-top: 0.25rem; | |
| } | |
| .property-card { | |
| background: #f8fafc; | |
| border-radius: 0.5rem; | |
| padding: 1rem; | |
| margin: 0.5rem 0; | |
| border-left: 4px solid var(--primary-color); | |
| box-shadow: var(--box-shadow); | |
| } | |
| .property-name { | |
| font-weight: bold; | |
| color: var(--primary-color); | |
| margin-bottom: 0.5rem; | |
| } | |
| button { | |
| padding: 15px 30px; | |
| background-color: var(--primary-color); | |
| color: white; | |
| border: none; | |
| border-radius: var(--border-radius); | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| font-family: 'Poppins', sans-serif; | |
| box-shadow: var(--box-shadow); | |
| } | |
| button:hover { | |
| background-color: #859F3D; | |
| transform: translateY(-2px); | |
| box-shadow: var(--box-shadow); | |
| } | |
| .loading-spinner { | |
| border: 4px solid rgba(74, 144, 226, 0.1); | |
| border-left-color: var(--primary-color); | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| animation: spin 1s linear infinite; | |
| margin: 40px auto; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .error-message { | |
| color: #DC3545; | |
| text-align: center; | |
| font-weight: 500; | |
| margin-top: 20px; | |
| } | |
| @media (max-width: 1024px) { | |
| .property { | |
| flex-direction: column; | |
| width: 95%; | |
| } | |
| .image-container { | |
| width: 100%; | |
| height: 300px; | |
| } | |
| #userQuery { | |
| width: 300px; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .search-container { | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| #queryForm { | |
| flex-direction: column; | |
| width: 100%; | |
| max-width: 400px; | |
| } | |
| #userQuery { | |
| width: 100%; | |
| } | |
| button { | |
| width: 100%; | |
| } | |
| } | |
| #userQuery:focus { | |
| outline: none; | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2); | |
| } | |
| #userQuery::placeholder { | |
| color: #A4A4A4; | |
| font-weight: 300; | |
| } | |
| .property:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1); | |
| } | |
| .carousel-nav { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| display: flex; | |
| gap: 10px; | |
| } | |
| .carousel-dot { | |
| width: 10px; | |
| height: 10px; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.5); | |
| cursor: pointer; | |
| transition: var(--transition); | |
| } | |
| .carousel-dot.active { | |
| background: white; | |
| } | |
| .property-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .property-header h2 { | |
| font-size: 1.8rem; | |
| font-weight: 700; | |
| color: var(--text-primary); | |
| margin: 0 0 10px 0; | |
| } | |
| .property-type { | |
| display: inline-block; | |
| padding: 8px 16px; | |
| background-color: var(--accent-color); | |
| color: white; | |
| border-radius: 20px; | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| } | |
| .property-info { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); | |
| gap: 20px; | |
| padding: 20px 0; | |
| border-top: 1px solid #E1E8ED; | |
| border-bottom: 1px solid #E1E8ED; | |
| max-height: 300px; /* Set a max height for scrolling */ | |
| overflow-y: auto; /* Enable vertical scrolling */ | |
| scrollbar-width: thin; /* For Firefox */ | |
| scrollbar-color: var(--primary-color) var(--secondary-color); /* For Firefox */ | |
| } | |
| .property-info::-webkit-scrollbar { | |
| width: 8px; /* Width of the scrollbar */ | |
| } | |
| .property-info::-webkit-scrollbar-track { | |
| background: var(--secondary-color); /* Color of the scrollbar track */ | |
| border-radius: 10px; | |
| } | |
| .property-info::-webkit-scrollbar-thumb { | |
| background: var(--primary-color); /* Color of the scrollbar thumb */ | |
| border-radius: 10px; | |
| } | |
| .property-info::-webkit-scrollbar-thumb:hover { | |
| background: var(--accent-color); /* Color of the scrollbar thumb on hover */ | |
| } | |
| .description { | |
| margin: 10px 0; | |
| line-height: 1.6; | |
| color: var(--text-secondary); | |
| } | |
| .key-features { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin: 10px 0; | |
| align-items: center; | |
| } | |
| .feature-pill { | |
| background-color: var(--secondary-color); | |
| padding: 2px 10px; | |
| border-radius: 10px; | |
| font-size: 0.9rem; | |
| color: var(--text-primary); | |
| font-weight: 500; | |
| transition: var(--transition); | |
| border: 2px solid var(--primary-color); /* Added border */ | |
| } | |
| .feature-pill:hover { | |
| background-color: var(--primary-color); | |
| color: white; | |
| transform: translateY(-2px); | |
| } | |
| .amenities-card { | |
| background-color: var(--secondary-color); | |
| border-radius: var(--border-radius); | |
| padding: 20px; | |
| margin: 10px 0; | |
| } | |
| .amenities-pills { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin-top: 10px; | |
| } | |
| .amenity-pill { | |
| background-color: var(--secondary-color); | |
| padding: 2px 10px; | |
| border-radius: 10px; | |
| font-size: 0.9rem; | |
| color: var(--text-primary); | |
| font-weight: 500; | |
| transition: var(--transition); | |
| border: 2px solid var(--primary-color); /* Added border */ | |
| } | |
| .amenity-pill:hover { | |
| background-color: var(--primary-color); | |
| color: white; | |
| transform: translateY(-2px); | |
| } | |
| .construction-status { | |
| display: flex; | |
| justify-content: space-between; | |
| padding-top: 20px; | |
| color: var(--text-secondary); | |
| } | |
| .accordion { | |
| background-color: var(--secondary-color); | |
| border-radius: var(--border-radius); | |
| box-shadow: var(--box-shadow); | |
| margin-bottom: 20px; | |
| overflow: hidden; | |
| } | |
| .accordion-header:hover { | |
| } | |
| .accordion-header .arrow { | |
| transition: transform 0.3s ease; | |
| } | |
| .accordion-header.active .arrow { | |
| transform: rotate(90deg); | |
| } | |
| .accordion-header strong { | |
| font-size: 1.1rem; | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .accordion-arrow { | |
| transition: transform 0.3s ease; | |
| font-size: 0.9rem; | |
| color: var(--primary-color); | |
| } | |
| .accordion-arrow.active { | |
| transform: rotate(180deg); | |
| } | |
| .accordion-section:last-child { | |
| border-bottom: none; | |
| } | |
| .accordion-header.active + .accordion-content { | |
| display: block; /* Show content when active */ | |
| } | |
| .quick-keywords::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .quick-keyword { | |
| flex: 0 0 auto; /* Prevent buttons from shrinking */ | |
| padding: 10px 15px; | |
| border-radius: 50px; | |
| border: none; | |
| color: white; | |
| cursor: pointer; | |
| white-space: nowrap; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>HIVE PROP</h1> | |
| <!-- Property Search Section --> | |
| <div class="search-container"> | |
| <form id="queryForm"> | |
| <input type="text" id="userQuery" placeholder="Search for your dream property..." required> | |
| <button type="button" id="microphoneButton"> | |
| <i class="fas fa-microphone"></i> | |
| </button> | |
| <button type="submit">Search Properties</button> | |
| </form> | |
| </div> | |
| <div id="results"></div> | |
| <div id="errorMessage" class="error-message"></div> | |
| <div id="loadingMessage" style="display: none;"> | |
| <div class="loading-spinner"></div> | |
| <!--<div class="loading-message">Loading...</div>--> | |
| </div> | |
| <div id="listeningMessage" style="display: none;"> | |
| <div class="loading-spinner"></div> | |
| <div class="loading-message">Listening...</div> | |
| </div> | |
| <!-- Chat Assistant and Recommendations Container --> | |
| <div class="chatbot-container"> | |
| <!-- Chat Assistant Icon --> | |
| <div class="chatbot-icon" id="chatbot-icon" style="background-color: #31511E;"> | |
| <i class="fas fa-comment"></i> | |
| </div> | |
| <!-- Recommendations Icon --> | |
| <div class="chatbot-icon" id="recommend-icon" style="background-color: #859F3D;"> | |
| <i class="fas fa-search"></i> | |
| </div> | |
| </div> | |
| <!-- Chat Assistant Container --> | |
| <div class="chat-container" id="chat-container" style="left: 2rem;"> | |
| <div class="chat-header"> | |
| <img src="/api/placeholder/32/32" alt="Bot Avatar"> | |
| <span>Hive Prop Chat Bot</span> | |
| <button class="close-button"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="chat-body" id="chat-body"> | |
| <div class="message bot-message"> | |
| <div class="message-content"> | |
| Hello! I'm your real estate recommendation assistant. How can I help you today? Please reply with 'hi' to access your location. | |
| </div> | |
| <div class="timestamp">Now</div> | |
| </div> | |
| <div class="typing-indicator" id="typing-indicator"> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| </div> | |
| </div> | |
| <div class="chat-footer"> | |
| <div class="input-group"> | |
| <input type="text" class="chat-input" id="user-input" placeholder="Type your message..." required> | |
| <button class="send-chatbutton" id="send-button"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| <button type="button" style="padding: 10px 15px;border-radius: 100px;" id="chatMicrophoneButton"> | |
| <i class="fas fa-microphone"></i> | |
| </button> | |
| </div> | |
| <div class="quick-keywords"> | |
| <button class="quick-keyword" data-message="Show me nearby properties">Nearby Properties</button> | |
| <button class="quick-keyword" data-message="Find luxury homes">Luxury Homes</button> | |
| <button class="quick-keyword" data-message="Affordable apartments">Affordable Apartments</button> | |
| <button class="quick-keyword" data-message="Properties with pools">Properties with Pools</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Recommendations Container --> | |
| <div class="chat-container" id="recommend-container" style="right: 2rem;"> | |
| <div class="chat-header"> | |
| <img src="/content/sample_data/hive_prop.jpg" alt="Bot Avatar"> | |
| <span>Hive Prop Chatbot</span> | |
| <button class="close-button"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| <div class="chat-body" id="recommend-body"> | |
| <div class="message bot-message"> | |
| <div class="message-content"> | |
| Hello! I'm your real estate recommendation assistant. How can I help you today? Please reply with 'hi' to access your location. | |
| </div> | |
| <div class="timestamp">Now</div> | |
| </div> | |
| <div class="typing-indicator" id="recommend-typing-indicator"> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| <div class="typing-dot"></div> | |
| </div> | |
| </div> | |
| <div class="chat-footer"> | |
| <div class="input-group"> | |
| <input type="text" class="chat-input" id="recommend-input" placeholder="Type your message..." required> | |
| <button class="send-chatbutton" id="recommend-send-button"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| <button type="button" style="padding: 10px 15px;border-radius: 100px;" id="recommendMicrophoneButton"> | |
| <i class="fas fa-microphone"></i> | |
| </button> | |
| </div> | |
| <div class="quick-keywords" style=" | |
| border-radius:50px; | |
| display: flex; | |
| flex-direction: row; | |
| overflow-x: auto; | |
| white-space: nowrap; | |
| gap: 10px; | |
| padding: 10px; | |
| scrollbar-width: none; /* Hide scrollbar for Firefox */ | |
| -ms-overflow-style: none; /* Hide scrollbar for IE/Edge */ | |
| position: relative; | |
| background: white; | |
| box-shadow: inset 20px 0 20px -10px rgba(0, 0, 0, 0.5), inset -20px 0 20px -10px rgba(0, 0, 0, 0.5); | |
| "> | |
| <button class="quick-keyword" data-message="Show me nearby properties">Nearby Properties</button> | |
| <button class="quick-keyword" data-message="Find luxury homes">Luxury Homes</button> | |
| <button class="quick-keyword" data-message="Affordable apartments">Affordable Apartments</button> | |
| <button class="quick-keyword" data-message="Properties with pools">Properties with Pools</button> | |
| <button class="quick-keyword" data-message="Pet-friendly rentals">Pet-friendly Rentals</button> | |
| <button class="quick-keyword" data-message="Beachfront properties">Beachfront Properties</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Property Search Scripts | |
| $(document).ready(function() { | |
| $('#queryForm').on('submit', function(event) { | |
| event.preventDefault(); | |
| const query = $('#userQuery').val(); | |
| $('#results').empty(); | |
| $('#errorMessage').empty(); | |
| $('#loadingMessage').show(); | |
| $.ajax({ | |
| url: '/search', | |
| type: 'POST', | |
| contentType: 'application/json', | |
| data: JSON.stringify({ query: query }), | |
| success: function(data) { | |
| $('#loadingMessage').hide(); | |
| if (data.error) { | |
| $('#errorMessage').text('Error: ' + data.error); | |
| } else { | |
| data.forEach(function(property) { | |
| const propertyElement = $(` | |
| <div class="property"> | |
| <div class="image-container"> | |
| <div class="carousel"> | |
| <div class="carousel"> | |
| <div class="carousel-images"> | |
| <img src="${property['Property Image']}" alt="${property['Property Name']}" class="carousel-image"> | |
| </div> | |
| </div> | |
| </div> | |
| <button class="favorite-button"> | |
| <i class="fas fa-heart"></i> | |
| </button> | |
| </div> | |
| <div class="property-details"> | |
| <div class="property-header"> | |
| <h2>${property["Property Name"]}</h2> | |
| <span class="property-type">${property["PropertyType"]}</span> | |
| </div> | |
| <div> | |
| <span> | |
| <strong>Location</strong> | |
| ${property["Address"]}, ${property["City"]}, ${property["State"]}, ${property["Country"]} | |
| </span> | |
| <span> | |
| <strong>Zip Code</strong> | |
| ${property["ZipCode"]} | |
| </span> | |
| </div> | |
| <div class="property-info"> | |
| <div class="scrollable-info" style=" | |
| display: flex; | |
| justify-content: space-between"> <span> | |
| <strong>Market Value</strong><br> | |
| ${property["MarketValue"]} | |
| </span> | |
| <span> | |
| <strong>Total Square Feet</strong><br> | |
| ${property["TotalSquareFeet"]} sq ft | |
| </span> | |
| <span> | |
| <strong>Bedrooms</strong><br> | |
| ${property["Beds"]} | |
| </span> | |
| <span> | |
| <strong>Bathrooms</strong><br> | |
| ${property["Baths"]} | |
| </span> | |
| <span> | |
| <strong>Contact</strong><br> | |
| ${property["Contact"]} | |
| </span> | |
| </div> | |
| </div> | |
| <div class="accordion-section"> | |
| <div class="accordion-header"> | |
| <strong>Description</strong> | |
| <i class="fas fa-chevron-down accordion-arrow"></i> | |
| </div> | |
| <div class="accordion-content"> | |
| <p>${property["Description"]}</p> | |
| </div> | |
| </div> | |
| <div class="accordion-section"> | |
| <div class="accordion-header"> | |
| <strong>Key Features</strong> | |
| <i class="fas fa-chevron-down accordion-arrow"></i> | |
| </div> | |
| <div class="accordion-content"> | |
| <div class="key-features"> | |
| ${property["KeyFeatures"].split(', ').map(feature => ` | |
| <span class="feature-pill">${feature}</span> | |
| `).join('')} | |
| </div> | |
| </div> | |
| </div> | |
| <div class="accordion-section"> | |
| <div class="accordion-header"> | |
| <strong>Nearby Amenities</strong> | |
| <i class="fas fa-chevron-down accordion-arrow"></i> | |
| </div> | |
| <div class="accordion-content"> | |
| <div class="amenities-pills"> | |
| ${property["NearbyAmenities"].split(', ').map(amenity => ` | |
| <span class="amenity-pill">${amenity}</span> | |
| `).join('')} | |
| </div> | |
| </div> | |
| </div> | |
| <div class="construction-status"> | |
| <p><strong>Property Status:</strong> ${property["PropertyStatus"]}</p> | |
| <p><strong>Year Built:</strong> ${property["YearBuilt"]}</p> | |
| </div> | |
| <div class="accordion-section"> | |
| <div class="accordion-header"> | |
| <strong>Agent Details</strong> | |
| <i class="fas fa-chevron-down accordion-arrow"></i> | |
| </div> | |
| <div class="accordion-content"> | |
| <div | |
| style="display: flex; justify-content: space-between"> <span> | |
| <strong>Agent Name</strong><br> | |
| ${property["AgentName"]} | |
| </span> | |
| <span> | |
| <strong>Agent Phone Number</strong><br> | |
| ${property["AgentPhoneNumber"]} | |
| </span> | |
| <span> | |
| <strong>Agent Email</strong><br> | |
| ${property["AgentEmail"]} | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `); | |
| $('#results').append(propertyElement); | |
| propertyElement.find('.accordion-header').each(function() { | |
| $(this).on('click', function() { | |
| const $header = $(this); | |
| const $content = $header.next('.accordion-content'); | |
| const $arrow = $header.find('.accordion-arrow'); | |
| $content.slideToggle(300); | |
| $arrow.toggleClass('active'); | |
| const $otherHeaders = $header.closest('.property-details') | |
| .find('.accordion-header').not($header); | |
| $otherHeaders.each(function() { | |
| $(this).next('.accordion-content').slideUp(300); | |
| $(this).find('.accordion-arrow').removeClass('active'); | |
| }); | |
| }); | |
| }); | |
| propertyElement.find('.favorite-button').on('click', function() { | |
| $(this).toggleClass('active'); | |
| }); | |
| }); | |
| } | |
| }, | |
| error: function() { | |
| $('#loadingMessage').hide(); | |
| $('#errorMessage').text('Error: Unable to fetch data. Please try again later.'); | |
| } | |
| }); | |
| }); | |
| $('#queryForm').on('submit', function() { | |
| $('html, body').animate({ | |
| scrollTop: $('#results').offset().top - 20 | |
| }, 1000); | |
| }); | |
| $('#userQuery').on('focus', function() { | |
| $(this).parent().addClass('focused'); | |
| }).on('blur', function() { | |
| $(this).parent().removeClass('focused'); | |
| }); | |
| $(window).on('resize', function() { | |
| if ($(window).width() <= 768) { | |
| $('.property').addClass('mobile-view'); | |
| } else { | |
| $('.property').removeClass('mobile-view'); | |
| } | |
| }); | |
| $('[data-tooltip]').each(function() { | |
| $(this).tooltip({ | |
| placement: 'top', | |
| title: $(this).data('tooltip') | |
| }); | |
| }); | |
| if ('IntersectionObserver' in window) { | |
| const imageObserver = new IntersectionObserver((entries, observer) => { | |
| entries.forEach(entry => { | |
| if (entry.isIntersecting) { | |
| const img = entry.target; | |
| img.src = img.dataset.src; | |
| img.removeAttribute('data-src'); | |
| observer.unobserve(img); | |
| } | |
| }); | |
| }); | |
| document.querySelectorAll('img[data-src]').forEach(img => { | |
| imageObserver.observe(img); | |
| }); | |
| } | |
| const microphoneButton = document.getElementById("microphoneButton"); | |
| const recognizer = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); | |
| recognizer.lang = 'en-US'; | |
| recognizer.continuous = true; | |
| recognizer.interimResults = true; | |
| let silenceTimer = null; | |
| const SILENCE_DURATION = 3000; | |
| microphoneButton.addEventListener("click", () => { | |
| microphoneButton.classList.add('listening'); | |
| $('#listeningMessage').show(); | |
| recognizer.start(); | |
| console.log("Listening..."); | |
| }); | |
| recognizer.onresult = function(event) { | |
| clearTimeout(silenceTimer); | |
| let finalTranscript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; i++) { | |
| if (event.results[i].isFinal) { | |
| finalTranscript += event.results[i][0].transcript; | |
| } | |
| } | |
| if (finalTranscript) { | |
| $('#userQuery').val(finalTranscript); | |
| console.log("Transcript: ", finalTranscript); | |
| } | |
| silenceTimer = setTimeout(() => { | |
| recognizer.stop(); | |
| console.log("Stopped listening due to silence"); | |
| }, SILENCE_DURATION); | |
| }; | |
| recognizer.onend = function() { | |
| console.log("Speech recognition service disconnected"); | |
| $('#listeningMessage').hide(); | |
| microphoneButton.classList.remove('listening'); | |
| clearTimeout(silenceTimer); | |
| if ($('#userQuery').val().trim()) { | |
| $('#queryForm').submit(); | |
| } | |
| }; | |
| recognizer.onerror = function(event) { | |
| console.error("Speech recognition error", event.error); | |
| $('#listeningMessage').hide(); | |
| microphoneButton.classList.remove('listening'); | |
| clearTimeout(silenceTimer); | |
| }; | |
| document.getElementById("chatbot-icon").addEventListener("click", function() { | |
| const chatContainer = document.getElementById("chat-container"); | |
| chatContainer.style.display = chatContainer.style.display === "none" || chatContainer.style.display === "" ? "flex" : "none"; | |
| }); | |
| const chatBody = document.getElementById("chat-body"); | |
| const userInput = document.getElementById("user-input"); | |
| const sendButton = document.getElementById("send-button"); | |
| const typingIndicator = document.getElementById("typing-indicator"); | |
| function addMessage(content, isUser = false) { | |
| const messageDiv = document.createElement("div"); | |
| messageDiv.className = `message ${isUser ? "user-message" : "bot-message"}`; | |
| const messageContent = document.createElement("div"); | |
| messageContent.className = "message-content"; | |
| messageContent.innerHTML = formatMessageContent(content); | |
| const timestamp = document.createElement("div"); | |
| timestamp.className = "timestamp"; | |
| timestamp.textContent = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); | |
| messageDiv.appendChild(messageContent); | |
| messageDiv.appendChild(timestamp); | |
| chatBody.insertBefore(messageDiv, typingIndicator); | |
| chatBody.scrollTop = chatBody.scrollHeight; | |
| } | |
| function handleUserInput() { | |
| const message = userInput.value.trim().toLowerCase(); | |
| if (!message) return; | |
| addMessage(message, true); | |
| userInput.value = ""; | |
| typingIndicator.style.display = "block"; | |
| if (message === 'hi') { | |
| if (navigator.geolocation) { | |
| navigator.geolocation.getCurrentPosition(function(position) { | |
| const latitude = position.coords.latitude; | |
| const longitude = position.coords.longitude; | |
| console.log("User Location:", latitude, longitude); | |
| fetch("/set-location", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ latitude: latitude, longitude: longitude, session_id: 'chat-session' }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| console.log("Location set:", data); | |
| const city = data.city; | |
| const state = data.state; | |
| const country = data.country; | |
| addMessage(`Location set successfully in ${city}, ${state}, ${country}. Would you like to see nearby properties? Please reply with 'yes' or 'no'.`); | |
| typingIndicator.style.display = "none"; | |
| }) | |
| .catch(error => { | |
| console.error("Error setting location:", error); | |
| addMessage("Unable to access your location. Please try again."); | |
| typingIndicator.style.display = "none"; | |
| }); | |
| }, function(error) { | |
| console.error("Error getting user location:", error); | |
| addMessage("Unable to access your location. Please try again."); | |
| typingIndicator.style.display = "none"; | |
| }); | |
| } else { | |
| console.error("Geolocation is not supported by this browser."); | |
| addMessage("Geolocation is not supported by your browser."); | |
| typingIndicator.style.display = "none"; | |
| } | |
| } else if (message === 'yes') { | |
| fetch("/recommend", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ query: message, session_id: 'chat-session' }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.properties) { | |
| let propertiesMessage = "Here are the 5 nearest properties to your location:\n"; | |
| data.properties.forEach(property => { | |
| propertiesMessage += `* **${property.PropertyName}** at ${property.Address}, ${property.City}\n`; | |
| propertiesMessage += ` Type: ${property.PropertyType}\n`; // Added property type | |
| propertiesMessage += ` (Distance: ${property.Distance} miles)\n\n`; // Added a break before the distance | |
| }); | |
| addMessage(propertiesMessage); | |
| } else { | |
| addMessage(data.response); | |
| } | |
| typingIndicator.style.display = "none"; | |
| }) | |
| .catch(error => { | |
| console.error("Error:", error); | |
| addMessage("I apologize, but I encountered an error. Could you please try again?"); | |
| typingIndicator.style.display = "none"; | |
| }); | |
| } else { | |
| fetch("/generate", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ query: message, session_id: 'chat-session' }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| addMessage(data.response); | |
| typingIndicator.style.display = "none"; | |
| }) | |
| .catch(error => { | |
| console.error("Error:", error); | |
| addMessage("I apologize, but I encountered an error. Could you please try again?"); | |
| typingIndicator.style.display = "none"; | |
| }); | |
| } | |
| } | |
| sendButton.addEventListener("click", handleUserInput); | |
| userInput.addEventListener("keypress", function(event) { | |
| if (event.key === "Enter") { | |
| handleUserInput(); | |
| } | |
| }); | |
| const chatMicrophoneButton = document.getElementById("chatMicrophoneButton"); | |
| const chatRecognizer = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); | |
| chatRecognizer.lang = 'en-US'; | |
| chatRecognizer.continuous = true; | |
| chatRecognizer.interimResults = true; | |
| let chatSilenceTimer = null; | |
| chatMicrophoneButton.addEventListener("click", () => { | |
| chatMicrophoneButton.classList.add('listening'); | |
| $('#listeningMessage').show(); | |
| chatRecognizer.start(); | |
| console.log("Chat listening..."); | |
| }); | |
| chatRecognizer.onresult = function(event) { | |
| clearTimeout(chatSilenceTimer); | |
| let finalTranscript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; i++) { | |
| if (event.results[i].isFinal) { | |
| finalTranscript += event.results[i][0].transcript; | |
| } | |
| } | |
| if (finalTranscript) { | |
| userInput.value = finalTranscript; | |
| console.log("Chat Transcript: ", finalTranscript); | |
| } | |
| chatSilenceTimer = setTimeout(() => { | |
| chatRecognizer.stop(); | |
| console.log("Stopped chat listening due to silence"); | |
| }, SILENCE_DURATION); | |
| }; | |
| chatRecognizer.onend = function() { | |
| console.log("Chat speech recognition service disconnected"); | |
| $('#listeningMessage').hide(); | |
| chatMicrophoneButton.classList.remove('listening'); | |
| clearTimeout(chatSilenceTimer); | |
| if (userInput.value.trim()) { | |
| handleUserInput(); | |
| } | |
| }; | |
| chatRecognizer.onerror = function(event) { | |
| console.error("Chat speech recognition error", event.error); | |
| $('#listeningMessage').hide(); | |
| chatMicrophoneButton.classList.remove('listening'); | |
| clearTimeout(chatSilenceTimer); | |
| }; | |
| document.getElementById("recommend-icon").addEventListener("click", function() { | |
| const recommendContainer = document.getElementById("recommend-container"); | |
| recommendContainer.style.display = recommendContainer.style.display === "none" || recommendContainer.style.display === "" ? "flex" : "none"; | |
| }); | |
| const recommendBody = document.getElementById("recommend-body"); | |
| const recommendInput = document.getElementById("recommend-input"); | |
| const recommendSendButton = document.getElementById("recommend-send-button"); | |
| const recommendTypingIndicator = document.getElementById("recommend-typing-indicator"); | |
| function addRecommendMessage(content, isUser = false) { | |
| const messageDiv = document.createElement("div"); | |
| messageDiv.className = `message ${isUser ? "user-message" : "bot-message"}`; | |
| const messageContent = document.createElement("div"); | |
| messageContent.className = "message-content"; | |
| messageContent.innerHTML = formatMessageContent(content); | |
| const timestamp = document.createElement("div"); | |
| timestamp.className = "timestamp"; | |
| timestamp.textContent = new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); | |
| messageDiv.appendChild(messageContent); | |
| messageDiv.appendChild(timestamp); | |
| recommendBody.insertBefore(messageDiv, recommendTypingIndicator); | |
| recommendBody.scrollTop = recommendBody.scrollHeight; | |
| } | |
| function handleRecommendInput() { | |
| const message = recommendInput.value.trim().toLowerCase(); | |
| if (!message) return; | |
| addRecommendMessage(message, true); | |
| recommendInput.value = ""; | |
| recommendTypingIndicator.style.display = "block"; | |
| if (message === 'hi') { | |
| if (navigator.geolocation) { | |
| navigator.geolocation.getCurrentPosition(function(position) { | |
| const latitude = position.coords.latitude; | |
| const longitude = position.coords.longitude; | |
| console.log("User Location:", latitude, longitude); | |
| fetch("/set-location", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ latitude: latitude, longitude: longitude, session_id: 'recommend-session' }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| console.log("Location set:", data); | |
| const city = data.city; | |
| const state = data.state; | |
| const country = data.country; | |
| addRecommendMessage(`Location set successfully in ${city}, ${state}, ${country}. Would you like to see nearby properties? Please reply with 'yes' or 'no'.`); | |
| recommendTypingIndicator.style.display = "none"; | |
| }) | |
| .catch(error => { | |
| console.error("Error setting location:", error); | |
| addRecommendMessage("Unable to access your location. Please try again."); | |
| recommendTypingIndicator.style.display = "none"; | |
| }); | |
| }, function(error) { | |
| console.error("Error getting user location:", error); | |
| addRecommendMessage("Unable to access your location. Please try again."); | |
| recommendTypingIndicator.style.display = "none"; | |
| }); | |
| } else { | |
| console.error("Geolocation is not supported by this browser."); | |
| addRecommendMessage("Geolocation is not supported by your browser."); | |
| recommendTypingIndicator.style.display = "none"; | |
| } | |
| } else if (message === 'yes') { | |
| fetch("/recommend", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ query: message, session_id: 'recommend-session' }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.properties) { | |
| let propertiesMessage = "Here are the 5 nearest properties to your location:\n"; | |
| data.properties.forEach(property => { | |
| propertiesMessage += `**${property.PropertyName}** at ${property.Address}, ${property.City}\n`; | |
| propertiesMessage += ` Type: ${property.PropertyType}\n`; // Added property type | |
| propertiesMessage += ` (Distance: ${property.Distance} miles)\n\n`; // Added a break before the distance | |
| }); | |
| addRecommendMessage(propertiesMessage); | |
| } else { | |
| addRecommendMessage(data.response); | |
| } | |
| recommendTypingIndicator.style.display = "none"; | |
| }) | |
| .catch(error => { | |
| console.error("Error:", error); | |
| addRecommendMessage("I apologize, but I encountered an error. Could you please try again?"); | |
| recommendTypingIndicator.style.display = "none"; | |
| }); | |
| } else { | |
| fetch("/recommend", { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ query: message, session_id: 'recommend-session' }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| addRecommendMessage(data.response); | |
| recommendTypingIndicator.style.display = "none"; | |
| }) | |
| .catch(error => { | |
| console.error("Error:", error); | |
| addRecommendMessage("I apologize, but I encountered an error. Could you please try again?"); | |
| recommendTypingIndicator.style.display = "none"; | |
| }); | |
| } | |
| } | |
| recommendSendButton.addEventListener("click", handleRecommendInput); | |
| recommendInput.addEventListener("keypress", function(event) { | |
| if (event.key === "Enter") { | |
| handleRecommendInput(); | |
| } | |
| }); | |
| const recommendMicrophoneButton = document.getElementById("recommendMicrophoneButton"); | |
| const recommendRecognizer = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); | |
| recommendRecognizer.lang = 'en-US'; | |
| recommendRecognizer.continuous = true; | |
| recommendRecognizer.interimResults = true; | |
| let recommendSilenceTimer = null; | |
| recommendMicrophoneButton.addEventListener("click", () => { | |
| recommendMicrophoneButton.classList.add('listening'); | |
| $('#listeningMessage').show(); | |
| recommendRecognizer.start(); | |
| console.log("Recommend listening..."); | |
| }); | |
| recommendRecognizer.onresult = function(event) { | |
| clearTimeout(recommendSilenceTimer); | |
| let finalTranscript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; i++) { | |
| if (event.results[i].isFinal) { | |
| finalTranscript += event.results[i][0].transcript; | |
| } | |
| } | |
| if (finalTranscript) { | |
| recommendInput.value = finalTranscript; | |
| console.log("Recommend Transcript: ", finalTranscript); | |
| } | |
| recommendSilenceTimer = setTimeout(() => { | |
| recommendRecognizer.stop(); | |
| console.log("Stopped recommend listening due to silence"); | |
| }, SILENCE_DURATION); | |
| }; | |
| recommendRecognizer.onend = function() { | |
| console.log("Recommend speech recognition service disconnected"); | |
| $('#listeningMessage').hide(); | |
| recommendMicrophoneButton.classList.remove('listening'); | |
| clearTimeout(recommendSilenceTimer); | |
| if (recommendInput.value.trim()) { | |
| handleRecommendInput(); | |
| } | |
| }; | |
| recommendRecognizer.onerror = function(event) { | |
| console.error("Recommend speech recognition error", event.error); | |
| $('#listeningMessage').hide(); | |
| recommendMicrophoneButton.classList.remove('listening'); | |
| clearTimeout(recommendSilenceTimer); | |
| }; | |
| function formatMessageContent(content) { | |
| content = content.replace(/\n/g, '<br>'); | |
| content = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); | |
| return content; | |
| } | |
| document.querySelectorAll('.quick-keyword').forEach(button => { | |
| button.addEventListener('click', function() { | |
| const message = this.getAttribute('data-message'); | |
| if (this.closest('#chat-container')) { | |
| userInput.value = message; | |
| handleUserInput(); | |
| } else if (this.closest('#recommend-container')) { | |
| recommendInput.value = message; | |
| handleRecommendInput(); | |
| } | |
| }); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |