UI improvments
This commit is contained in:
+55
-79
@@ -2,16 +2,20 @@
|
||||
|
||||
"use client";
|
||||
|
||||
import { BookCopy, CheckCircle, CloudDownload, Download, ExternalLink, Folder, Loader2, Map, MapPinned, MapPlus, WavesLadder, WifiOff, Archive } from 'lucide-react';
|
||||
import { BookCopy, CheckCircle, CloudDownload, Folder, Loader2, Map, MapPinned, MapPlus, WavesLadder, WifiOff } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { DocumentCard, ExternalLinkCard, DocumentItem, LinkItem } from '../components/ui/Cards';
|
||||
import { OfflineBadge } from '../components/ui/OfflineBadge';
|
||||
import { PageHeader } from '../components/ui/PageHeader';
|
||||
|
||||
export default function Documents() {
|
||||
const [isHydrated, setIsHydrated] = useState(false);
|
||||
const [isOffline, setIsOffline] = useState(false);
|
||||
const [syncStatus, setSyncStatus] = useState<'idle' | 'syncing' | 'done'>('idle');
|
||||
const [cachedFiles, setCachedFiles] = useState<Set<string>>(new Set());
|
||||
const [showNotification, setShowNotification] = useState(false);
|
||||
|
||||
const docs = [
|
||||
const docs: DocumentItem[] = [
|
||||
{ title: 'Turistkarta', icon: <Map size={20} />, file: '/files/Turistkarta - Kullaberg.pdf', size: '2.51 MB' },
|
||||
{ title: 'Orienteringskarta', icon: <MapPlus size={20} />, file: '/files/Orienteringskarta - Kullaberg.pdf', size: '3.74 MB' },
|
||||
{ title: 'Badplatser att besöka', icon: <WavesLadder size={20} />, file: '/files/Badplatser på Kullaberg.pdf', size: '1.88 MB' },
|
||||
@@ -20,7 +24,7 @@ export default function Documents() {
|
||||
{ title: 'Destinationskunskap Kullahalvön', icon: <BookCopy size={20} />, file: '/files/Destinationskunskap.pdf', size: '1.97 MB' }
|
||||
];
|
||||
|
||||
const links = [
|
||||
const links: LinkItem[] = [
|
||||
{ title: 'Kullabergs Naturreservat', url: 'https://www.kullabergsnatur.se/' },
|
||||
{ title: 'Vandra på Kullahalvön', url: 'https://www.kullahalvon.com/upptacka--uppleva/friluftsliv--natur/vandra-pa-kullahalvon.html' },
|
||||
{ title: 'Naturkartan', url: 'https://www.naturkartan.se/en/explore' },
|
||||
@@ -71,82 +75,48 @@ export default function Documents() {
|
||||
}
|
||||
await checkCacheStatus();
|
||||
setSyncStatus('done');
|
||||
setShowNotification(true);
|
||||
setTimeout(() => {
|
||||
setShowNotification(false);
|
||||
}, 4000);
|
||||
};
|
||||
|
||||
if (!isHydrated) return <div className="flex justify-center py-20"><Loader2 className="animate-spin text-slate-teal" size={40} /></div>;
|
||||
|
||||
return (
|
||||
<div className="space-y-8 animate-fade-in w-full">
|
||||
<div className="flex flex-col md:flex-row md:justify-between md:items-start gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-black text-slate-teal tracking-tight flex items-center">
|
||||
<Folder className="mr-3 text-seafoam" size={32} />
|
||||
Info & Dokument
|
||||
</h1>
|
||||
<p className="text-moss font-medium mt-2 ml-11">Allt du behöver för att guida besökarna.</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2 items-start md:items-end">
|
||||
{isOffline && (
|
||||
<div className="bg-goldenrod/10 text-goldenrod border border-goldenrod/20 px-3 py-1.5 rounded-full flex items-center text-xs font-black uppercase tracking-widest">
|
||||
<WifiOff size={14} className="mr-2" /> Offline-läge
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isOffline && syncStatus !== 'done' && (
|
||||
<button
|
||||
onClick={handleSyncAll}
|
||||
disabled={syncStatus === 'syncing'}
|
||||
className="flex items-center gap-2 bg-seafoam/10 text-slate-teal border border-seafoam/20 hover:bg-seafoam/20 px-4 py-2 rounded-lg font-bold text-sm transition-colors"
|
||||
>
|
||||
{syncStatus === 'syncing' ? <Loader2 size={18} className="animate-spin" /> : <CloudDownload size={18} />}
|
||||
{syncStatus === 'syncing' ? 'Laddar ner filer...' : 'Gör tillgänglig offline'}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{syncStatus === 'done' && (
|
||||
<div className="flex items-center gap-2 text-moss font-bold text-sm bg-moss/10 border border-moss/20 px-4 py-2 rounded-lg">
|
||||
<CheckCircle size={18} /> Redo för offline
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-8 animate-fade-in w-full relative">
|
||||
<PageHeader
|
||||
title="Info & Dokument"
|
||||
icon={Folder}
|
||||
description="Allt du behöver för att guida besökarna."
|
||||
actions={
|
||||
<>
|
||||
{isOffline && <OfflineBadge />}
|
||||
{!isOffline && syncStatus !== 'done' && (
|
||||
<button
|
||||
onClick={handleSyncAll}
|
||||
disabled={syncStatus === 'syncing'}
|
||||
className="flex items-center gap-2 bg-seafoam/10 text-slate-teal border border-seafoam/20 hover:bg-seafoam/20 px-4 py-2 rounded-lg font-bold text-sm transition-colors"
|
||||
>
|
||||
{syncStatus === 'syncing' ? <Loader2 size={18} className="animate-spin" /> : <CloudDownload size={18} />}
|
||||
{syncStatus === 'syncing' ? 'Laddar ner filer...' : 'Gör tillgängliga offline'}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<section>
|
||||
<h2 className="text-sm font-black text-ebony/50 uppercase tracking-widest mb-4">Nedladdningar</h2>
|
||||
<h2 className="text-sm font-black text-ebony/50 uppercase tracking-widest mb-4">Filer</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{docs.map((doc, idx) => {
|
||||
const isCached = cachedFiles.has(doc.file);
|
||||
return (
|
||||
<a key={idx} href={doc.file} target="_blank" rel="noopener noreferrer"
|
||||
className={`group flex items-center justify-between p-4 rounded-2xl border transition-colors ${isOffline && !isCached ? 'opacity-50 grayscale cursor-not-allowed pointer-events-none border-transparent bg-eggshell/50' : 'bg-white/50 hover:bg-white border-slate-teal/10'}`}
|
||||
>
|
||||
<div className="flex items-center text-slate-teal">
|
||||
<div className={`p-2.5 rounded-xl mr-3 text-eggshell shrink-0 transition-colors ${isCached ? 'bg-moss' : 'bg-slate-teal'}`}>
|
||||
{doc.icon}
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="font-bold text-sm leading-tight text-ebony">{doc.title}</span>
|
||||
<div className="flex flex-wrap items-center gap-2 mt-1">
|
||||
<span className="text-[10px] font-bold text-moss bg-moss/10 px-2 py-0.5 rounded uppercase tracking-wider">
|
||||
{doc.size}
|
||||
</span>
|
||||
{isCached && (
|
||||
<span className="flex items-center gap-1 text-[10px] font-bold text-moss bg-moss/10 px-2 py-0.5 rounded uppercase tracking-wider">
|
||||
<Archive size={10} /> Nedladdad
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isCached ? (
|
||||
<CheckCircle size={18} className="text-moss shrink-0 ml-2" />
|
||||
) : (
|
||||
<Download size={18} className="text-slate-teal/50 group-hover:text-slate-teal transition-colors shrink-0 ml-2" />
|
||||
)}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
{docs.map((doc, idx) => (
|
||||
<DocumentCard
|
||||
key={idx}
|
||||
doc={doc}
|
||||
isOffline={isOffline}
|
||||
isCached={cachedFiles.has(doc.file)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -156,17 +126,23 @@ export default function Documents() {
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{links.map((link, idx) => (
|
||||
<a key={idx} href={isOffline ? '#' : link.url} target={isOffline ? '_self' : '_blank'} rel="noopener noreferrer"
|
||||
className={`group flex items-center justify-between p-4 rounded-2xl border transition-colors ${isOffline ? 'opacity-40 grayscale cursor-not-allowed pointer-events-none bg-eggshell/50 border-transparent' : 'bg-white/50 hover:bg-white border-slate-teal/10'}`}
|
||||
>
|
||||
<span className="font-bold text-slate-teal text-sm leading-tight pr-4">{link.title}</span>
|
||||
<div className={`rounded-full p-2 transition-colors shrink-0 ${isOffline ? 'bg-transparent' : 'bg-white group-hover:bg-seafoam'}`}>
|
||||
<ExternalLink size={16} className={isOffline ? 'text-ebony/40' : 'text-slate-teal group-hover:text-eggshell'} />
|
||||
</div>
|
||||
</a>
|
||||
<ExternalLinkCard
|
||||
key={idx}
|
||||
link={link}
|
||||
isOffline={isOffline}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{showNotification && (
|
||||
<div className="fixed bottom-6 right-6 z-50 animate-fade-in flex items-center gap-3 bg-white text-moss font-bold text-sm border border-moss/20 shadow-xl px-4 py-3 rounded-xl">
|
||||
<div className="bg-moss/10 p-1 rounded-full">
|
||||
<CheckCircle size={20} className="text-moss" />
|
||||
</div>
|
||||
Alla filer är redo för offline
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user