- Mobile client changes.

This commit is contained in:
Josako
2025-08-03 17:56:52 +02:00
parent 248fae500a
commit cea38e02d2
7 changed files with 119 additions and 577 deletions

View File

@@ -497,6 +497,17 @@ export default {
font-weight: bold;
}
/* Verberg character counter op mobile */
@media (max-width: 768px) {
.character-counter {
display: none;
}
.message-input {
padding-right: 15px; /* Verminder van 60px naar 15px omdat counter weg is */
}
}
/* Input actions */
.input-actions {
display: flex;

View File

@@ -1,12 +1,11 @@
<!-- MobileHeader.vue -->
<template>
<div class="mobile-header">
<SideBarLogo
<SideBarLogo
:logo-url="tenantMake.logo_url"
:make-name="tenantMake.name"
class="mobile-logo"
/>
<LanguageSelector
:initial-language="initialLanguage"
:current-language="currentLanguage"
@@ -56,21 +55,21 @@ const currentLanguage = ref(props.initialLanguage);
const handleLanguageChange = (newLanguage) => {
currentLanguage.value = newLanguage;
// Emit to parent
emit('language-changed', newLanguage);
// Global event for backward compatibility
const globalEvent = new CustomEvent('language-changed', {
detail: { language: newLanguage }
});
document.dispatchEvent(globalEvent);
// Update chatConfig
if (window.chatConfig) {
window.chatConfig.language = newLanguage;
}
// Save preference
localStorage.setItem('preferredLanguage', newLanguage);
};
@@ -88,56 +87,72 @@ const handleLanguageChange = (newLanguage) => {
min-height: 60px;
}
/* Mobile logo styling - compact version */
.mobile-header :deep(.mobile-logo) {
/* Mobile logo container - meer specifieke styling */
.mobile-logo {
flex-shrink: 0;
display: flex !important;
align-items: center;
justify-content: flex-start;
height: 50px; /* Vaste hoogte voor consistentie */
min-width: 120px; /* Minimale breedte */
}
.mobile-header :deep(.mobile-logo .sidebar-logo) {
padding: 0;
margin-right: 15px;
/* Diepere styling voor het logo component */
.mobile-logo :deep(.sidebar-logo) {
padding: 0 !important;
margin: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
height: 100% !important;
}
.mobile-header :deep(.mobile-logo .logo-image) {
max-height: 40px;
max-width: 120px;
.mobile-logo :deep(.logo-image) {
max-height: 40px !important;
max-width: 120px !important;
height: auto !important;
width: auto !important;
object-fit: contain !important;
display: block !important;
}
.mobile-header :deep(.mobile-logo .logo-placeholder) {
width: 40px;
height: 40px;
font-size: 1rem;
.mobile-logo :deep(.logo-placeholder) {
width: 40px !important;
height: 40px !important;
font-size: 1rem !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
/* Mobile language selector styling - compact horizontal version */
.mobile-header :deep(.mobile-language-selector) {
flex-shrink: 0;
/* Mobile language selector styling */
.mobile-language-selector {
flex-shrink: 1;
min-width: 140px;
}
.mobile-header :deep(.mobile-language-selector .language-selector) {
.mobile-language-selector :deep(.language-selector) {
margin: 0;
}
.mobile-header :deep(.mobile-language-selector label) {
.mobile-language-selector :deep(label) {
display: none; /* Hide label in mobile header */
}
.mobile-header :deep(.mobile-language-selector .language-select) {
.mobile-language-selector :deep(.language-select) {
padding: 6px 10px;
font-size: 0.85rem;
min-width: 120px;
margin: 0;
}
/* Show mobile header on mobile */
/* Media queries voor responsiviteit */
@media (max-width: 768px) {
.mobile-header {
display: flex;
}
}
/* Hide mobile header on desktop */
@media (min-width: 769px) {
.mobile-header {
display: none;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -30,6 +30,7 @@ console.log('Components loaded:', Object.keys(Components));
import LanguageSelector from '../../../eveai_chat_client/static/assets/vue-components/LanguageSelector.vue';
import ChatApp from '../../../eveai_chat_client/static/assets/vue-components/ChatApp.vue';
import SideBar from '../../../eveai_chat_client/static/assets/vue-components/SideBar.vue';
import MobileHeader from '../../../eveai_chat_client/static/assets/vue-components/MobileHeader.vue';
// Globale Vue error tracking
window.addEventListener('error', function(event) {
@@ -47,6 +48,9 @@ document.addEventListener('DOMContentLoaded', function() {
// Initialiseer sidebar (vervangt fillSidebarExplanation en initializeLanguageSelector)
initializeSidebar();
// Initialiseer mobile header
initializeMobileHeader();
// Initialiseer chat app (simpel)
initializeChatApp();
});
@@ -114,6 +118,68 @@ function initializeSidebar() {
}
}
/**
* Initialiseert de mobile header component
*/
function initializeMobileHeader() {
const container = document.getElementById('mobile-header-container');
if (!container) {
console.error('#mobile-header-container niet gevonden');
return;
}
try {
// Maak props voor de component
const props = {
tenantMake: {
name: window.chatConfig.tenantMake?.name || '',
logo_url: window.chatConfig.tenantMake?.logo_url || '',
subtitle: window.chatConfig.tenantMake?.subtitle || ''
},
initialLanguage: window.chatConfig.language || 'nl',
supportedLanguageDetails: window.chatConfig.supportedLanguageDetails || {},
allowedLanguages: window.chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de'],
apiPrefix: window.chatConfig.apiPrefix || ''
};
// Mount de component
const app = createApp(MobileHeader, props);
// Create and provide LanguageProvider for mobile header components
const initialLanguage = window.chatConfig?.language || 'nl';
const apiPrefix = window.chatConfig?.apiPrefix || '';
const languageProvider = createLanguageProvider(initialLanguage, apiPrefix);
app.provide(LANGUAGE_PROVIDER_KEY, languageProvider);
// Error handler
app.config.errorHandler = (err, vm, info) => {
console.error('🚨 [Vue Error in MobileHeader]', err);
console.error('Component:', vm);
console.error('Error Info:', info);
};
const mountedApp = app.mount(container);
// Listen to language change events and update the mobile header's language provider
const languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
console.log('MobileHeader: Received language change event:', event.detail.language);
languageProvider.setLanguage(event.detail.language);
}
};
document.addEventListener('language-changed', languageChangeHandler);
// Store the handler for cleanup if needed
mountedApp._languageChangeHandler = languageChangeHandler;
console.log('✅ MobileHeader component successfully mounted with LanguageProvider');
return mountedApp;
} catch (error) {
console.error('🚨 [CRITICAL ERROR] Bij initialiseren mobile header:', error);
}
}
/**
* Initialiseert de chat app (Vue component)
*/

View File

@@ -1,206 +0,0 @@
#!/usr/bin/env python3
"""
Test script to verify mobile header visibility fix for eveai_chat_client
"""
import os
import sys
import re
def test_mobile_header_css_fix():
"""Test that MobileHeader component has correct CSS media queries"""
print("Testing MobileHeader CSS fix...")
mobile_header_path = 'eveai_chat_client/static/assets/vue-components/MobileHeader.vue'
if not os.path.exists(mobile_header_path):
print(f"❌ MobileHeader.vue not found at {mobile_header_path}")
return False
with open(mobile_header_path, 'r') as f:
content = f.read()
# Check for mobile media query (show on mobile)
mobile_show_pattern = r'@media\s*\(\s*max-width:\s*768px\s*\)\s*\{[^}]*\.mobile-header\s*\{[^}]*display:\s*flex'
mobile_show_match = re.search(mobile_show_pattern, content, re.DOTALL)
if not mobile_show_match:
print("❌ Missing mobile media query to show mobile header")
print(" Expected: @media (max-width: 768px) with .mobile-header { display: flex }")
return False
else:
print("✓ Found mobile media query to show header on mobile devices")
# Check for desktop media query (hide on desktop)
desktop_hide_pattern = r'@media\s*\(\s*min-width:\s*769px\s*\)\s*\{[^}]*\.mobile-header\s*\{[^}]*display:\s*none'
desktop_hide_match = re.search(desktop_hide_pattern, content, re.DOTALL)
if not desktop_hide_match:
print("❌ Missing desktop media query to hide mobile header")
print(" Expected: @media (min-width: 769px) with .mobile-header { display: none }")
return False
else:
print("✓ Found desktop media query to hide header on desktop devices")
# Check that both media queries exist in correct order
mobile_pos = content.find('@media (max-width: 768px)')
desktop_pos = content.find('@media (min-width: 769px)')
if mobile_pos == -1 or desktop_pos == -1:
print("❌ Media queries not found in expected format")
return False
if mobile_pos > desktop_pos:
print("⚠️ Warning: Mobile media query comes after desktop query (may cause specificity issues)")
print("✅ MobileHeader CSS fix is correctly implemented")
return True
def test_global_css_rules():
"""Test that global CSS rules for mobile-header-container are correct"""
print("\nTesting global CSS rules...")
css_path = 'eveai_chat_client/static/assets/css/chat.css'
if not os.path.exists(css_path):
print(f"❌ chat.css not found at {css_path}")
return False
with open(css_path, 'r') as f:
content = f.read()
# Check default hidden state
if '#mobile-header-container' not in content or 'display: none' not in content:
print("❌ Missing default hidden state for mobile-header-container")
return False
else:
print("✓ Found default hidden state for mobile-header-container")
# Check mobile media query shows container
# Look for the media query and then check if mobile-header-container is set to display: block within it
media_query_start = content.find('@media (max-width: 768px)')
if media_query_start == -1:
print("❌ Missing @media (max-width: 768px) query")
return False
# Find the closing brace of this media query
brace_count = 0
media_query_end = media_query_start
for i, char in enumerate(content[media_query_start:]):
if char == '{':
brace_count += 1
elif char == '}':
brace_count -= 1
if brace_count == 0:
media_query_end = media_query_start + i
break
media_query_content = content[media_query_start:media_query_end + 1]
if '#mobile-header-container' in media_query_content and 'display: block' in media_query_content:
print("✓ Found mobile media query to show mobile-header-container")
else:
print("❌ Mobile media query doesn't properly show mobile-header-container")
return False
print("✅ Global CSS rules are correct")
return True
def test_built_assets():
"""Test that built assets exist and are recent"""
print("\nTesting built assets...")
assets_to_check = [
'eveai_chat_client/static/dist/chat-client.js',
'eveai_chat_client/static/dist/chat-client.css'
]
for asset_path in assets_to_check:
if not os.path.exists(asset_path):
print(f"❌ Built asset not found: {asset_path}")
return False
else:
# Check file size to ensure it's not empty
size = os.path.getsize(asset_path)
if size == 0:
print(f"❌ Built asset is empty: {asset_path}")
return False
else:
print(f"✓ Built asset exists and has content: {asset_path} ({size} bytes)")
print("✅ Built assets are present and valid")
return True
def test_component_structure():
"""Test that MobileHeader component structure is intact"""
print("\nTesting MobileHeader component structure...")
mobile_header_path = 'eveai_chat_client/static/assets/vue-components/MobileHeader.vue'
with open(mobile_header_path, 'r') as f:
content = f.read()
required_elements = [
'class="mobile-header"',
'SideBarLogo',
'LanguageSelector',
'justify-content: space-between',
'align-items: center'
]
missing_elements = []
for element in required_elements:
if element not in content:
missing_elements.append(element)
else:
print(f"✓ Found: {element}")
if missing_elements:
print(f"❌ Missing elements in MobileHeader:")
for element in missing_elements:
print(f" - {element}")
return False
print("✅ MobileHeader component structure is intact")
return True
def main():
"""Run all tests"""
print("🔧 Testing Mobile Header Visibility Fix")
print("=" * 50)
tests = [
test_mobile_header_css_fix,
test_global_css_rules,
test_built_assets,
test_component_structure
]
passed = 0
failed = 0
for test in tests:
try:
if test():
passed += 1
else:
failed += 1
except Exception as e:
print(f"❌ Test failed with error: {e}")
failed += 1
print()
print("=" * 50)
print(f"📊 Test Results: {passed} passed, {failed} failed")
if failed == 0:
print("🎉 Mobile header visibility fix is complete!")
print("📱 The MobileHeader should now be visible when switching to mobile mode.")
return True
else:
print("⚠️ Some tests failed. Please review the implementation.")
return False
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)

View File

@@ -1,257 +0,0 @@
#!/usr/bin/env python3
"""
Test script to verify mobile responsive functionality for eveai_chat_client
"""
import os
import sys
def test_files_exist():
"""Test that all required files exist"""
print("Testing file existence...")
files_to_check = [
# Vue components
'eveai_chat_client/static/assets/vue-components/MobileHeader.vue',
'eveai_chat_client/static/assets/vue-components/SideBar.vue',
'eveai_chat_client/static/assets/vue-components/index.js',
# Templates
'eveai_chat_client/templates/base.html',
'eveai_chat_client/templates/scripts.html',
# CSS
'eveai_chat_client/static/assets/css/chat.css',
# Source files
'frontend_src/js/chat-client.js',
# Built files
'eveai_chat_client/static/dist/chat-client.js',
'eveai_chat_client/static/dist/chat-client.css',
]
missing_files = []
for file_path in files_to_check:
if not os.path.exists(file_path):
missing_files.append(file_path)
else:
print(f"{file_path}")
if missing_files:
print(f"\n❌ Missing files:")
for file_path in missing_files:
print(f" - {file_path}")
return False
print("✅ All required files exist")
return True
def test_mobile_header_component():
"""Test MobileHeader component structure"""
print("\nTesting MobileHeader component...")
mobile_header_path = 'eveai_chat_client/static/assets/vue-components/MobileHeader.vue'
with open(mobile_header_path, 'r') as f:
content = f.read()
# Check for required elements
required_elements = [
'class="mobile-header"',
'SideBarLogo',
'LanguageSelector',
'@media (min-width: 769px)',
'display: none'
]
missing_elements = []
for element in required_elements:
if element not in content:
missing_elements.append(element)
else:
print(f"✓ Found: {element}")
if missing_elements:
print(f"❌ Missing elements in MobileHeader:")
for element in missing_elements:
print(f" - {element}")
return False
print("✅ MobileHeader component structure is correct")
return True
def test_css_responsive_rules():
"""Test CSS responsive rules"""
print("\nTesting CSS responsive rules...")
css_path = 'eveai_chat_client/static/assets/css/chat.css'
with open(css_path, 'r') as f:
content = f.read()
# Check for required CSS rules
required_rules = [
'#mobile-header-container',
'#sidebar-container',
'@media (max-width: 768px)',
'flex-direction: column',
'display: none',
'display: block'
]
missing_rules = []
for rule in required_rules:
if rule not in content:
missing_rules.append(rule)
else:
print(f"✓ Found: {rule}")
if missing_rules:
print(f"❌ Missing CSS rules:")
for rule in missing_rules:
print(f" - {rule}")
return False
print("✅ CSS responsive rules are correct")
return True
def test_base_html_structure():
"""Test base.html structure"""
print("\nTesting base.html structure...")
base_html_path = 'eveai_chat_client/templates/base.html'
with open(base_html_path, 'r') as f:
content = f.read()
# Check for required elements
required_elements = [
'id="mobile-header-container"',
'id="sidebar-container"',
'class="content-area"'
]
missing_elements = []
for element in required_elements:
if element not in content:
missing_elements.append(element)
else:
print(f"✓ Found: {element}")
if missing_elements:
print(f"❌ Missing elements in base.html:")
for element in missing_elements:
print(f" - {element}")
return False
print("✅ base.html structure is correct")
return True
def test_component_exports():
"""Test component exports"""
print("\nTesting component exports...")
index_js_path = 'eveai_chat_client/static/assets/vue-components/index.js'
with open(index_js_path, 'r') as f:
content = f.read()
# Check for required exports
required_exports = [
'export { default as SideBar }',
'export { default as MobileHeader }'
]
missing_exports = []
for export in required_exports:
if export not in content:
missing_exports.append(export)
else:
print(f"✓ Found: {export}")
if missing_exports:
print(f"❌ Missing exports:")
for export in missing_exports:
print(f" - {export}")
return False
print("✅ Component exports are correct")
return True
def test_chat_client_js():
"""Test chat-client.js mounting logic"""
print("\nTesting chat-client.js mounting logic...")
chat_client_path = 'frontend_src/js/chat-client.js'
with open(chat_client_path, 'r') as f:
content = f.read()
# Check for required mounting logic
required_elements = [
'getElementById(\'sidebar-container\')',
'getElementById(\'mobile-header-container\')',
'Components.SideBar',
'Components.MobileHeader',
'mount(\'#sidebar-container\')',
'mount(\'#mobile-header-container\')'
]
missing_elements = []
for element in required_elements:
if element not in content:
missing_elements.append(element)
else:
print(f"✓ Found: {element}")
if missing_elements:
print(f"❌ Missing elements in chat-client.js:")
for element in missing_elements:
print(f" - {element}")
return False
print("✅ chat-client.js mounting logic is correct")
return True
def main():
"""Run all tests"""
print("🧪 Testing Mobile Responsive Implementation")
print("=" * 50)
tests = [
test_files_exist,
test_mobile_header_component,
test_css_responsive_rules,
test_base_html_structure,
test_component_exports,
test_chat_client_js
]
passed = 0
failed = 0
for test in tests:
try:
if test():
passed += 1
else:
failed += 1
except Exception as e:
print(f"❌ Test failed with error: {e}")
failed += 1
print()
print("=" * 50)
print(f"📊 Test Results: {passed} passed, {failed} failed")
if failed == 0:
print("🎉 All tests passed! Mobile responsive implementation is complete.")
return True
else:
print("⚠️ Some tests failed. Please review the implementation.")
return False
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)