Merge version_2 into main #2

Merged
bender merged 4 commits from version_2 into main 2026-03-07 09:40:19 +00:00
4 changed files with 166 additions and 2 deletions

10
public/manifest.json Normal file
View File

@@ -0,0 +1,10 @@
{
"name": "Rosslyn Inn & Suites", "short_name": "Rosslyn Inn", "description": "Experience timeless comfort and hospitality at Rosslyn Inn and Suites", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#000000", "orientation": "portrait-primary", "icons": [
{
"src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any"
},
{
"src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any"
}
]
}

96
public/sw.js Normal file
View File

@@ -0,0 +1,96 @@
const CACHE_NAME = 'rosslyn-inn-v1';
const urlsToCache = [
'/',
'/index.html',
'/manifest.json'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
.catch((error) => {
console.error('Cache opening failed:', error);
})
);
});
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
return;
}
if (request.method !== 'GET') {
return;
}
event.respondWith(
caches.match(request)
.then((response) => {
if (response) {
return response;
}
return fetch(request)
.then((response) => {
if (!response || response.status !== 200 || response.type === 'error') {
return response;
}
const responseToCache = response.clone();
const isImage = request.headers.get('accept')?.includes('image');
const isFont = request.headers.get('accept')?.includes('font') || request.url.includes('.woff') || request.url.includes('.woff2');
const isScript = request.headers.get('accept')?.includes('javascript');
const isStylesheet = request.headers.get('accept')?.includes('text/css');
if (isImage || isFont || isScript || isStylesheet) {
caches.open(CACHE_NAME)
.then((cache) => {
cache.put(request, responseToCache);
})
.catch((error) => {
console.error('Cache update failed:', error);
});
}
return response;
})
.catch(() => {
if (request.destination === 'image') {
return new Response(
'<svg role="img" aria-label="Placeholder" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><rect fill="#ddd" width="100" height="100"/></svg>',
{ headers: { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'no-store' } }
);
}
return new Response('Network request failed and no cache available', {
status: 503,
statusText: 'Service Unavailable',
headers: new Headers({
'Content-Type': 'text/plain'
})
});
});
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});

View File

@@ -47,6 +47,64 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script async src="https://cdn.jsdelivr.net/npm/workbox-window@7/build/workbox-window.umd.js"></script>
<script>
{
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('Service Worker registered:', registration);
} catch (error) {
console.error('Service Worker registration failed:', error);
}
});
}
}
</script>
<script>
{
if (typeof window !== 'undefined' && 'performance' in window) {
window.addEventListener('load', () => {
const perfData = window.performance.timing;
const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
const resourcesPerf = window.performance.getEntriesByType('resource');
const metrics = {
pageLoadTime: pageLoadTime,
domInteractive: perfData.domInteractive - perfData.navigationStart,
domComplete: perfData.domComplete - perfData.navigationStart,
resourceCount: resourcesPerf.length,
timestamp: new Date().toISOString()
};
if ('sendBeacon' in navigator) {
navigator.sendBeacon('/api/metrics', JSON.stringify(metrics));
}
console.log('Performance Metrics:', metrics);
});
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Web Vital:', {
name: entry.name,
value: entry.value,
rating: entry.rating
});
}
});
observer.observe({
entryTypes: ['largest-contentful-paint', 'first-input', 'layout-shift']
});
}
}
}
</script>
</head>
<ServiceWrapper>
<body
className={`${halant.variable} ${inter.variable} ${sourceSans3.variable} antialiased`}
@@ -1424,4 +1482,4 @@ export default function RootLayout({
</ServiceWrapper>
</html>
);
}
}

View File

@@ -206,4 +206,4 @@ export default function LandingPage() {
</div>
</ThemeProvider>
);
}
}