passmaster/app/offline/page.tsx

347 lines
12 KiB
TypeScript

"use client"
import { useState, useEffect } from 'react'
import { motion } from 'framer-motion'
import {
Wifi,
WifiOff,
Download,
Shield,
CheckCircle,
AlertCircle,
Smartphone,
Monitor
} from 'lucide-react'
export default function OfflinePage() {
const [isOnline, setIsOnline] = useState(true)
const [isInstallable, setIsInstallable] = useState(false)
const [deferredPrompt, setDeferredPrompt] = useState<any>(null)
useEffect(() => {
setIsOnline(navigator.onLine)
const handleOnline = () => setIsOnline(true)
const handleOffline = () => setIsOnline(false)
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)
// PWA install prompt
const handleBeforeInstallPrompt = (e: any) => {
e.preventDefault()
setDeferredPrompt(e)
setIsInstallable(true)
}
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
return () => {
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
}
}, [])
const handleInstallClick = async () => {
if (deferredPrompt) {
deferredPrompt.prompt()
const { outcome } = await deferredPrompt.userChoice
if (outcome === 'accepted') {
setDeferredPrompt(null)
setIsInstallable(false)
}
}
}
const offlineFeatures = [
{
icon: Shield,
title: "Complete Offline Functionality",
description: "All password generation features work without internet connection. Service Worker ensures local availability."
},
{
icon: WifiOff,
title: "No Data Transmission",
description: "100% client-side processing. Your passwords never leave your device, even in online mode."
},
{
icon: Download,
title: "PWA Installation",
description: "Install PassMaster as a native app. Works on desktop, tablet and smartphone."
}
]
const installSteps = [
{
step: 1,
title: "Browser Installation",
description: "Click 'Install App' in the address bar or use the button below."
},
{
step: 2,
title: "Offline Test",
description: "Disable your internet connection and test password generation."
},
{
step: 3,
title: "App Icon",
description: "PassMaster appears as an app icon on your home screen or in the app list."
}
]
return (
<div className="min-h-screen py-12">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<div className="flex justify-center mb-6">
<div className="p-4 bg-primary-100 dark:bg-primary-900/20 rounded-full">
{isOnline ? (
<Wifi className="h-12 w-12 text-primary-600" />
) : (
<WifiOff className="h-12 w-12 text-primary-600" />
)}
</div>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white mb-6">
Offline Password Generator (PWA)
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300 mb-6">
Install and use PassMaster completely offline. Service Worker and local storage for maximum independence.
</p>
{/* Connection Status */}
<div className={`inline-flex items-center px-4 py-2 rounded-full text-sm font-medium ${
isOnline
? 'bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-300'
: 'bg-orange-100 text-orange-800 dark:bg-orange-900/20 dark:text-orange-300'
}`}>
{isOnline ? (
<>
<CheckCircle className="h-4 w-4 mr-2" />
Online - Ready for Installation
</>
) : (
<>
<AlertCircle className="h-4 w-4 mr-2" />
Offline Mode Active
</>
)}
</div>
</motion.div>
{/* Install Button */}
{isInstallable && (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
className="text-center mb-12"
>
<button
onClick={handleInstallClick}
className="btn-primary text-lg px-8 py-4 inline-flex items-center space-x-2"
>
<Download className="h-5 w-5" />
<span>Install PassMaster as App</span>
</button>
</motion.div>
)}
{/* Features */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="mb-16"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-8 text-center">
Service Worker & Installation | FAQ | Security
</h2>
<div className="grid md:grid-cols-3 gap-8">
{offlineFeatures.map((feature, index) => (
<motion.div
key={feature.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + index * 0.1 }}
className="card text-center"
>
<div className="flex justify-center mb-4">
<div className="p-3 bg-primary-100 dark:bg-primary-900/20 rounded-full">
<feature.icon className="h-8 w-8 text-primary-600" />
</div>
</div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">
{feature.title}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{feature.description}
</p>
</motion.div>
))}
</div>
</motion.div>
{/* Installation Steps */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className="mb-16"
>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-8 text-center">
PWA Installation in 3 Steps
</h2>
<div className="space-y-6">
{installSteps.map((step, index) => (
<motion.div
key={step.step}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 + index * 0.1 }}
className="flex items-start space-x-4 card"
>
<div className="flex-shrink-0 w-8 h-8 bg-primary-600 text-white rounded-full flex items-center justify-center font-semibold">
{step.step}
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
{step.title}
</h3>
<p className="text-gray-600 dark:text-gray-300">
{step.description}
</p>
</div>
</motion.div>
))}
</div>
</motion.div>
{/* Platform Support */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.6 }}
className="mb-16"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-8 text-center">
Platform Support
</h2>
<div className="grid md:grid-cols-2 gap-8">
<div className="card text-center">
<Monitor className="h-12 w-12 text-primary-600 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Desktop Browser
</h3>
<p className="text-gray-600 dark:text-gray-300">
Chrome, Firefox, Safari, Edge - All modern browsers support PWA installation
</p>
</div>
<div className="card text-center">
<Smartphone className="h-12 w-12 text-primary-600 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Mobile Devices
</h3>
<p className="text-gray-600 dark:text-gray-300">
iOS Safari, Android Chrome - Installation via "Add to Home Screen"
</p>
</div>
</div>
</motion.div>
{/* FAQ Section */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.7 }}
className="card"
>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">
Offline FAQ
</h2>
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
How do I install PassMaster as a PWA?
</h3>
<p className="text-gray-600 dark:text-gray-300">
On supported browsers, an installation icon automatically appears in the address bar.
Alternatively, use the "Install App" button on this page.
</p>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
What offline features are available?
</h3>
<p className="text-gray-600 dark:text-gray-300">
All main functions: password generation, parameter customization, entropy calculation,
and copying to clipboard work completely offline.
</p>
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
What is stored locally?
</h3>
<p className="text-gray-600 dark:text-gray-300">
Only app files (HTML, CSS, JavaScript) are stored in the browser cache.
No passwords or personal data are ever stored.
</p>
</div>
</div>
</motion.div>
{/* JSON-LD for FAQ */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How do I install PassMaster as a PWA?",
"acceptedAnswer": {
"@type": "Answer",
"text": "On supported browsers, an installation icon automatically appears in the address bar. Alternatively, use the 'Install App' button."
}
},
{
"@type": "Question",
"name": "What offline features are available?",
"acceptedAnswer": {
"@type": "Answer",
"text": "All main functions: password generation, parameter customization, entropy calculation, and copying work completely offline."
}
},
{
"@type": "Question",
"name": "What is stored locally?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Only app files are stored in the browser cache. No passwords or personal data are ever stored."
}
}
]
})
}}
/>
</div>
</div>
)
}