fix bug and version check
Some checks failed
Deploy MyApp on Same Server / build-and-deploy (push) Failing after 1s

This commit is contained in:
2025-12-18 19:39:05 +03:30
parent 2481381798
commit 31b58b7151
12 changed files with 300 additions and 35 deletions

View File

@@ -482,23 +482,43 @@ function DailyReportContent() {
{/* Tabs */}
<div className="bg-white rounded-2xl shadow-md border border-gray-200 overflow-hidden">
<div className="flex border-b border-gray-200 overflow-x-auto">
{TABS.map(tab => (
<button
key={tab.value}
onClick={() => setActiveTab(tab.value)}
className={`flex-1 min-w-[120px] px-6 py-4 text-sm font-medium transition-all duration-200 whitespace-nowrap ${
activeTab === tab.value
? 'text-indigo-600 border-b-2 border-indigo-600 bg-indigo-50/50'
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-50'
}`}
>
{tab.label}
</button>
))}
{/* Segmented Control for Mobile */}
<div className="p-3 md:p-6 md:pb-0">
<div className="bg-gray-100 rounded-xl p-1 flex md:hidden">
{TABS.map(tab => (
<button
key={tab.value}
onClick={() => setActiveTab(tab.value)}
className={`flex-1 px-2 py-2.5 text-xs font-medium rounded-lg transition-all duration-200 ${
activeTab === tab.value
? 'bg-white text-indigo-600 shadow-sm'
: 'text-gray-600 hover:text-gray-900'
}`}
>
{tab.label}
</button>
))}
</div>
{/* Desktop Tabs */}
<div className="hidden md:flex border-b border-gray-200 -mx-6 -mt-6 mb-6">
{TABS.map(tab => (
<button
key={tab.value}
onClick={() => setActiveTab(tab.value)}
className={`flex-1 px-6 py-4 text-sm font-medium transition-all duration-200 whitespace-nowrap ${
activeTab === tab.value
? 'text-indigo-600 border-b-2 border-indigo-600 bg-indigo-50/50'
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-50'
}`}
>
{tab.label}
</button>
))}
</div>
</div>
<div className="p-6">
<div className="p-4 md:p-6 md:pt-0">
{/* Summary Tab */}
{activeTab === 'summary' && (
<SummaryTab

View File

@@ -1,6 +1,7 @@
import type { Metadata } from 'next'
import './globals.css'
import ServiceWorkerRegistration from '@/components/ServiceWorkerRegistration'
import UpdateNotification from '@/components/UpdateNotification'
export const metadata: Metadata = {
title: 'GreenHome',
@@ -35,6 +36,7 @@ export default function RootLayout({ children }: Readonly<{ children: React.Reac
</head>
<body className='persian-number'>
<ServiceWorkerRegistration />
<UpdateNotification />
{children}
</body>
</html>

View File

@@ -8,26 +8,51 @@ export default function ServiceWorkerRegistration() {
typeof window !== 'undefined' &&
'serviceWorker' in navigator
) {
let registration: ServiceWorkerRegistration | null = null;
let checkInterval: NodeJS.Timeout | null = null;
const handleMessage = (event: MessageEvent) => {
if (event.data && event.data.type === 'SW_UPDATED') {
// Service worker has updated, reload the page
window.location.reload();
}
};
const handleVisibilityChange = () => {
if (!document.hidden && registration) {
registration.update();
}
};
const handleFocus = () => {
if (registration) {
registration.update();
}
};
const registerServiceWorker = async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
});
console.log('Service Worker registered successfully:', registration.scope);
// Handle updates
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
if (newWorker) {
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New service worker available
console.log('New service worker available');
}
});
// Listen for messages from service worker
navigator.serviceWorker.addEventListener('message', handleMessage);
// Check for updates periodically
checkInterval = setInterval(() => {
if (registration) {
registration.update();
}
});
}, 60000); // Check every minute
// Check for updates when page becomes visible
document.addEventListener('visibilitychange', handleVisibilityChange);
// Check for updates on page focus
window.addEventListener('focus', handleFocus);
} catch (error) {
console.error('Service Worker registration failed:', error);
}
@@ -39,6 +64,16 @@ export default function ServiceWorkerRegistration() {
} else {
window.addEventListener('load', registerServiceWorker);
}
// Cleanup
return () => {
if (checkInterval) {
clearInterval(checkInterval);
}
document.removeEventListener('visibilitychange', handleVisibilityChange);
window.removeEventListener('focus', handleFocus);
navigator.serviceWorker.removeEventListener('message', handleMessage);
};
}
}, []);

View File

@@ -0,0 +1,140 @@
'use client';
import { useState, useEffect } from 'react';
import { RefreshCw, X } from 'lucide-react';
export default function UpdateNotification() {
const [showUpdate, setShowUpdate] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
useEffect(() => {
if (typeof window === 'undefined' || !('serviceWorker' in navigator)) {
return;
}
let registration: ServiceWorkerRegistration | null = null;
const checkForUpdates = async () => {
try {
registration = await navigator.serviceWorker.ready;
// Listen for service worker updates
registration.addEventListener('updatefound', () => {
const newWorker = registration?.installing;
if (newWorker) {
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New service worker available
setShowUpdate(true);
}
});
}
});
// Check for updates every 60 seconds
const checkInterval = setInterval(async () => {
try {
await registration?.update();
} catch (error) {
console.error('Error checking for updates:', error);
}
}, 60000);
// Also check on page visibility change
const handleVisibilityChange = () => {
if (!document.hidden && registration) {
registration.update();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
// Check on page focus
const handleFocus = () => {
if (registration) {
registration.update();
}
};
window.addEventListener('focus', handleFocus);
return () => {
clearInterval(checkInterval);
document.removeEventListener('visibilitychange', handleVisibilityChange);
window.removeEventListener('focus', handleFocus);
};
} catch (error) {
console.error('Service Worker registration error:', error);
}
};
checkForUpdates();
// Check immediately on load
navigator.serviceWorker.getRegistration().then(reg => {
if (reg) {
reg.update();
}
});
}, []);
const handleUpdate = async () => {
setIsUpdating(true);
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
// Send message to service worker to skip waiting
navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' });
// Reload the page after a short delay
setTimeout(() => {
window.location.reload();
}, 500);
}
};
const handleDismiss = () => {
setShowUpdate(false);
};
if (!showUpdate) return null;
return (
<div className="fixed bottom-4 left-4 right-4 md:left-auto md:right-4 md:w-96 z-50 animate-in slide-in-from-bottom-5">
<div className="bg-white rounded-xl shadow-lg border-2 border-indigo-500 p-4 flex items-center gap-3">
<div className="flex-shrink-0 w-10 h-10 bg-indigo-100 rounded-full flex items-center justify-center">
<RefreshCw className="w-5 h-5 text-indigo-600" />
</div>
<div className="flex-1">
<p className="font-semibold text-gray-900 text-sm">
نسخه جدید آماده است
</p>
<p className="text-xs text-gray-600 mt-0.5">
برای دریافت آخرین تغییرات، اپ را به‌روزرسانی کنید
</p>
</div>
<div className="flex items-center gap-2">
<button
onClick={handleUpdate}
disabled={isUpdating}
className="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{isUpdating ? (
<>
<RefreshCw className="w-4 h-4 animate-spin" />
در حال به‌روزرسانی...
</>
) : (
'به‌روزرسانی'
)}
</button>
<button
onClick={handleDismiss}
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
aria-label="بستن"
>
<X className="w-4 h-4 text-gray-500" />
</button>
</div>
</div>
</div>
);
}

View File

@@ -25,7 +25,7 @@ export function SummaryCard({ param, currentValue, minValue, maxValue, data }: S
case 'soil':
return <HumidityGauge value={currentValue} type="soil" />
case 'lux':
return <LuxGauge value={600} max={2000} />
return <LuxGauge value={currentValue} max={2000} />
case 'gas':
return <GasGauge value={currentValue} max={100} />
default:

View File

@@ -36,8 +36,8 @@ export type GreenhouseAlert = {
export const TABS: { value: TabType; label: string }[] = [
{ value: 'summary', label: 'خلاصه' },
{ value: 'charts', label: 'گزارش نموداری' },
{ value: 'weather', label: 'وضعیت آب و هوا' },
{ value: 'charts', label: 'نمودار' },
{ value: 'weather', label: 'آب و هوا' },
{ value: 'analysis', label: 'تحلیل' },
]

12
src/lib/version.ts Normal file
View File

@@ -0,0 +1,12 @@
export const APP_VERSION = process.env.NEXT_PUBLIC_APP_VERSION || Date.now().toString();
export async function getAppVersion(): Promise<string> {
try {
const response = await fetch('/version.json', { cache: 'no-store' });
const data = await response.json();
return data.version;
} catch {
return APP_VERSION;
}
}