style: ui readability refactor v1.2.2 (removed uppercase, tracking, increased font sizes)
This commit is contained in:
138
frontend/app/logs/page.tsx
Normal file
138
frontend/app/logs/page.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { db, Item } from '@/lib/db';
|
||||
import { inventoryApi } from '@/lib/api';
|
||||
import PageShell from '@/components/PageShell';
|
||||
import { History, X, Search, Filter } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { fetchAndCacheItems } from '@/lib/sync';
|
||||
|
||||
export default function LogsPage() {
|
||||
const [auditLogs, setAuditLogs] = useState<any[]>([]);
|
||||
const [inventory, setInventory] = useState<Item[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// Load inventory for name lookups
|
||||
const cached = await db.items.toArray();
|
||||
setInventory(cached);
|
||||
|
||||
// Fetch fresh logs
|
||||
const logs = await inventoryApi.getAuditLogs(50);
|
||||
setAuditLogs(logs);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const filteredLogs = auditLogs.filter(log => {
|
||||
const itemName = inventory.find(i => i.id === log.target_item_id)?.name || '';
|
||||
return (
|
||||
itemName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
log.action.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
(log.username || '').toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<main className="p-4 md:p-8 max-w-4xl mx-auto space-y-8">
|
||||
<header className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-3 bg-primary/10 rounded-2xl text-primary border border-primary/20">
|
||||
<History size={24} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-black tracking-tight text-white uppercase">Audit History</h1>
|
||||
<p className="text-xs text-slate-500 font-bold tracking-widest uppercase">Centralized Transaction Logs</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative group">
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-500 group-focus-within:text-primary transition-colors" size={18} />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search logs by item, action or user..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full bg-slate-900/50 border border-slate-800 focus:border-primary/50 rounded-2xl py-4 pl-12 pr-4 text-sm text-white placeholder:text-slate-600 outline-none transition-all shadow-inner"
|
||||
/>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section className="space-y-4">
|
||||
{loading ? (
|
||||
<div className="flex flex-col items-center justify-center p-20 text-slate-500 animate-pulse">
|
||||
<History size={48} className="opacity-20 mb-4" />
|
||||
<p className="text-xs font-black uppercase tracking-widest">Retrieving logs from cloud...</p>
|
||||
</div>
|
||||
) : filteredLogs.length === 0 ? (
|
||||
<div className="bg-slate-900/30 border border-slate-800 border-dashed rounded-[2.5rem] p-16 flex flex-col items-center justify-center text-center gap-4">
|
||||
<div className="w-16 h-16 bg-slate-800 rounded-full flex items-center justify-center text-slate-600">
|
||||
<Search size={32} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-lg font-bold text-slate-300">No transactions found</p>
|
||||
<p className="text-sm text-slate-500">Try a different search term or check back later</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-3">
|
||||
{filteredLogs.map((log) => (
|
||||
<div key={log.id} className="bg-slate-900/40 border border-slate-800/50 p-5 rounded-3xl flex items-center justify-between gap-4 hover:bg-slate-900/60 transition-colors group">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className={cn(
|
||||
"text-[9px] font-black uppercase tracking-[0.2em] px-2 py-0.5 rounded-md",
|
||||
log.action.includes('CHECK_IN') ? "bg-green-500/10 text-green-500" :
|
||||
(log.action.includes('TRASH') ? "bg-rose-500/10 text-rose-500" : "bg-amber-500/10 text-amber-500")
|
||||
)}>
|
||||
{log.action}
|
||||
</div>
|
||||
<span className="text-[10px] font-bold text-slate-600 uppercase tracking-widest">by</span>
|
||||
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">{log.username || 'System'}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-base font-bold text-slate-100 group-hover:text-primary transition-colors">
|
||||
{inventory.find(i => i.id === log.target_item_id)?.name || `Item #${log.target_item_id}`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mt-3">
|
||||
<div className="text-[10px] text-slate-600 font-mono flex items-center gap-1.5">
|
||||
<div className="w-1 h-1 rounded-full bg-slate-700" />
|
||||
{new Date(log.timestamp).toLocaleString()}
|
||||
</div>
|
||||
{log.details && (
|
||||
<span className="text-[9px] bg-slate-800/50 text-slate-500 px-3 py-1 rounded-full border border-slate-700/50 font-mono">
|
||||
{log.details}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className={cn(
|
||||
"text-2xl font-black tabular-nums group-hover:scale-110 transition-transform",
|
||||
log.quantity_change > 0 ? "text-green-400" : "text-rose-400"
|
||||
)}>
|
||||
{log.quantity_change > 0 ? '+' : ''}{log.quantity_change}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user