WellNuo/web-pages/unsupported-browser.html
Sergei 48019e0b08 Add unsupported browser page for WellNuo Web
Creates a dedicated page to handle browsers that don't support Web Bluetooth API.
The page detects the user's browser and provides appropriate guidance.

Features:
- Automatic browser and platform detection
- User-friendly messages for Safari, Firefox, iOS
- Recommended browsers with download links
- Mobile app alternative suggestion
- Technical details section (expandable)
- Responsive design matching existing pages

Browser detection:
- Chrome 70+ (supported)
- Edge 79+ (supported)
- Opera 57+ (supported)
- Safari (not supported - Apple limitation)
- Firefox (not supported - privacy concerns)
- iOS (not supported - system restrictions)

Files:
- web-pages/unsupported-browser.html - Standalone page with inline detection
- __tests__/web-pages/unsupported-browser.test.ts - Comprehensive test coverage

All tests pass (17/17).
2026-01-31 17:12:35 -08:00

460 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Browser Not Supported - WellNuo</title>
<link rel="icon" type="image/png" href="/favicon.png">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0,0,0,0.1);
max-width: 600px;
width: 100%;
overflow: hidden;
}
.header {
background: #f8f9fa;
padding: 32px;
text-align: center;
border-bottom: 1px solid #eee;
}
.logo-img { width: 160px; height: auto; }
.content { padding: 32px; }
.warning-icon {
width: 80px;
height: 80px;
background: #fff3e0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 24px;
}
.warning-icon svg { width: 40px; height: 40px; color: #f57c00; }
.title {
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 12px;
text-align: center;
}
.subtitle {
color: #666;
font-size: 15px;
line-height: 1.6;
text-align: center;
margin-bottom: 24px;
}
.info-box {
background: #f0f7ff;
border-radius: 12px;
padding: 16px;
margin-bottom: 24px;
}
.info-title {
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.info-text {
font-size: 14px;
color: #555;
line-height: 1.5;
}
.browser-info {
font-family: 'Courier New', monospace;
font-size: 13px;
color: #666;
margin-top: 8px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 16px;
}
.browser-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 16px;
margin-bottom: 24px;
}
.browser-card {
background: #f8f9fa;
border: 2px solid #e0e0e0;
border-radius: 12px;
padding: 16px;
text-align: center;
text-decoration: none;
transition: all 0.2s;
}
.browser-card:hover {
border-color: #4A90D9;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(74, 144, 217, 0.2);
}
.browser-logo {
font-size: 32px;
margin-bottom: 8px;
}
.browser-name {
font-size: 15px;
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.browser-version {
font-size: 12px;
color: #666;
}
.divider {
border: 0;
border-top: 1px solid #e0e0e0;
margin: 24px 0;
}
.mobile-section {
background: #f5f7fa;
border-radius: 12px;
padding: 20px;
}
.mobile-title {
font-size: 15px;
font-weight: 600;
color: #333;
margin-bottom: 12px;
text-align: center;
}
.mobile-text {
font-size: 14px;
color: #666;
line-height: 1.5;
text-align: center;
margin-bottom: 16px;
}
.mobile-link {
display: block;
background: linear-gradient(135deg, #4A90D9 0%, #357ABD 100%);
color: white;
padding: 12px 24px;
border-radius: 10px;
text-decoration: none;
font-weight: 600;
text-align: center;
transition: all 0.2s;
}
.mobile-link:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(74, 144, 217, 0.4);
}
.technical-details {
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid #e0e0e0;
}
.details-toggle {
background: none;
border: none;
color: #4A90D9;
font-size: 14px;
cursor: pointer;
text-decoration: underline;
padding: 0;
}
.details-content {
display: none;
margin-top: 12px;
font-size: 13px;
color: #666;
line-height: 1.6;
}
.details-content.show {
display: block;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="/logo.png" alt="WellNuo" class="logo-img">
</div>
<div class="content">
<div class="warning-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z">
</path>
</svg>
</div>
<h1 class="title">Browser Not Supported</h1>
<p class="subtitle" id="unsupportedMessage">
WellNuo Web requires a browser that supports Web Bluetooth API for connecting to health sensors.
</p>
<div class="info-box">
<div class="info-title">Your Browser</div>
<div class="info-text" id="browserDetails">
Detecting browser...
</div>
</div>
<div class="section-title">Recommended Browsers</div>
<div class="browser-grid" id="browsersGrid">
<!-- Browsers will be inserted here -->
</div>
<hr class="divider">
<div class="mobile-section">
<div class="mobile-title">Prefer Mobile?</div>
<p class="mobile-text">
Download the WellNuo mobile app for the best experience on your smartphone or tablet.
</p>
<a href="https://wellnuo.com" class="mobile-link" target="_blank">
Visit WellNuo.com
</a>
</div>
<div class="technical-details">
<button class="details-toggle" onclick="toggleDetails()">
Why is my browser not supported?
</button>
<div class="details-content" id="technicalDetails">
<p><strong>Technical Details:</strong></p>
<p id="technicalReason">Loading...</p>
<br>
<p><strong>Web Bluetooth Requirements:</strong></p>
<ul style="margin-left: 20px; margin-top: 8px;">
<li>HTTPS connection (required for security)</li>
<li>Bluetooth hardware enabled on your device</li>
<li>Operating System: Windows 10+, macOS, or Linux</li>
<li>Browser: Chrome 70+, Edge 79+, or Opera 57+</li>
</ul>
</div>
</div>
</div>
</div>
<script>
// Browser detection logic (copied from web/lib/browserCheck.ts)
function detectBrowser() {
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
return { name: 'Unknown', version: '0' };
}
const userAgent = navigator.userAgent;
let name = 'Unknown';
let version = '0';
// Chrome
if (/Chrome\/(\d+)/.test(userAgent) && !/Edg/.test(userAgent) && !/OPR/.test(userAgent)) {
name = 'Chrome';
const match = userAgent.match(/Chrome\/(\d+)/);
version = match ? match[1] : '0';
}
// Edge
else if (/Edg\/(\d+)/.test(userAgent)) {
name = 'Edge';
const match = userAgent.match(/Edg\/(\d+)/);
version = match ? match[1] : '0';
}
// Opera
else if (/OPR\/(\d+)/.test(userAgent)) {
name = 'Opera';
const match = userAgent.match(/OPR\/(\d+)/);
version = match ? match[1] : '0';
}
// Safari
else if (/Safari\/(\d+)/.test(userAgent) && !/Chrome/.test(userAgent)) {
name = 'Safari';
const match = userAgent.match(/Version\/(\d+)/);
version = match ? match[1] : '0';
}
// Firefox
else if (/Firefox\/(\d+)/.test(userAgent)) {
name = 'Firefox';
const match = userAgent.match(/Firefox\/(\d+)/);
version = match ? match[1] : '0';
}
return { name, version };
}
function detectPlatform() {
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
return 'Unknown';
}
const userAgent = navigator.userAgent;
const platform = navigator.platform;
if (/Win/.test(platform)) return 'Windows';
if (/Mac/.test(platform)) return 'macOS';
if (/iPhone|iPad|iPod/.test(userAgent)) return 'iOS';
if (/Android/.test(userAgent)) return 'Android';
if (/Linux/.test(platform)) return 'Linux';
return 'Unknown';
}
function hasWebBluetoothAPI() {
return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
}
function getUnsupportedMessage(browserInfo) {
const { name, platform, hasWebBluetooth } = browserInfo;
if (platform === 'iOS') {
return 'Web Bluetooth is not supported on iOS due to system limitations. Please use a desktop browser or download our mobile app.';
}
if (name === 'Safari') {
return 'Safari does not support Web Bluetooth API. Please use Chrome, Edge, or Opera instead.';
}
if (name === 'Firefox') {
return 'Firefox does not support Web Bluetooth API due to privacy concerns. Please use Chrome, Edge, or Opera instead.';
}
if (!hasWebBluetooth) {
return 'Your browser does not support Web Bluetooth API. Please update to a newer version or try Chrome, Edge, or Opera.';
}
return 'Your browser version may be outdated. Please update to the latest version or try Chrome, Edge, or Opera.';
}
function getTechnicalReason(browserInfo) {
const { name, platform, hasWebBluetooth, version } = browserInfo;
if (platform === 'iOS') {
return 'Apple has not implemented Web Bluetooth API on iOS for security reasons. All browsers on iOS (including Chrome) use Safari\'s WebKit engine, which lacks Bluetooth support.';
}
if (name === 'Safari') {
return 'Apple has not implemented Web Bluetooth API in Safari. This is a design decision by Apple, not a technical limitation.';
}
if (name === 'Firefox') {
return 'Mozilla has chosen not to implement Web Bluetooth API due to privacy and security concerns. There are no current plans to add support.';
}
if (!hasWebBluetooth) {
return `Your browser (${name} ${version}) does not have the Web Bluetooth API available. This could be due to an outdated version or platform restrictions.`;
}
const versionNum = parseInt(version, 10);
if (name === 'Chrome' && versionNum < 70) {
return `Your Chrome version (${version}) is too old. Web Bluetooth API requires Chrome 70 or newer.`;
}
if (name === 'Edge' && versionNum < 79) {
return `Your Edge version (${version}) is too old. Web Bluetooth API requires Edge 79 or newer.`;
}
if (name === 'Opera' && versionNum < 57) {
return `Your Opera version (${version}) is too old. Web Bluetooth API requires Opera 57 or newer.`;
}
return `Your browser (${name} ${version} on ${platform}) may not fully support the Web Bluetooth features required by WellNuo.`;
}
const browserLogos = {
'Google Chrome': '🌐',
'Microsoft Edge': '🔷',
'Opera': '⭕'
};
const recommendedBrowsers = [
{
name: 'Google Chrome',
minVersion: '70',
downloadUrl: 'https://www.google.com/chrome/',
},
{
name: 'Microsoft Edge',
minVersion: '79',
downloadUrl: 'https://www.microsoft.com/edge',
},
{
name: 'Opera',
minVersion: '57',
downloadUrl: 'https://www.opera.com/',
},
];
// Initialize page
function initPage() {
const browser = detectBrowser();
const platform = detectPlatform();
const hasWebBluetooth = hasWebBluetoothAPI();
const browserInfo = {
name: browser.name,
version: browser.version,
platform,
hasWebBluetooth,
};
// Update message
const message = getUnsupportedMessage(browserInfo);
document.getElementById('unsupportedMessage').textContent = message;
// Update browser details
const detailsText = `${browser.name} ${browser.version} on ${platform}`;
const webBluetoothStatus = hasWebBluetooth ? 'Available ✓' : 'Not Available ✗';
document.getElementById('browserDetails').innerHTML = `
<strong>${detailsText}</strong>
<div class="browser-info">Web Bluetooth: ${webBluetoothStatus}</div>
`;
// Update technical reason
const technicalReason = getTechnicalReason(browserInfo);
document.getElementById('technicalReason').textContent = technicalReason;
// Render browser cards
const grid = document.getElementById('browsersGrid');
grid.innerHTML = recommendedBrowsers.map(browser => `
<a href="${browser.downloadUrl}" class="browser-card" target="_blank" rel="noopener noreferrer">
<div class="browser-logo">${browserLogos[browser.name]}</div>
<div class="browser-name">${browser.name}</div>
<div class="browser-version">Version ${browser.minVersion}+</div>
</a>
`).join('');
}
function toggleDetails() {
const details = document.getElementById('technicalDetails');
details.classList.toggle('show');
const button = document.querySelector('.details-toggle');
button.textContent = details.classList.contains('show')
? 'Hide technical details'
: 'Why is my browser not supported?';
}
// Run on load
initPage();
</script>
</body>
</html>