feat: add TypeScript definitions for next-auth session with accessToken and user details
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import InventoryForm from './InventoryForm';
|
||||
import Link from 'next/link';
|
||||
import { fetchJson } from '../../lib/api';
|
||||
import type { InventoryItem, Product } from '../../features/inventory/types';
|
||||
import InventoryList from './InventoryList';
|
||||
import Navigation from '../Navigation';
|
||||
|
||||
function formatDate(value: string | null) {
|
||||
if (!value) return null;
|
||||
return new Date(value).toLocaleDateString('sv-SE');
|
||||
}
|
||||
|
||||
function getBestBeforeStatus(bestBeforeDate: string | null) {
|
||||
if (!bestBeforeDate) {
|
||||
return {
|
||||
label: 'Ingen bäst före angiven',
|
||||
color: '#666',
|
||||
background: '#f5f5f5',
|
||||
border: '#ddd',
|
||||
};
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
const bestBefore = new Date(bestBeforeDate);
|
||||
|
||||
today.setHours(0, 0, 0, 0);
|
||||
bestBefore.setHours(0, 0, 0, 0);
|
||||
|
||||
const diffMs = bestBefore.getTime() - today.getTime();
|
||||
const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays < 0) {
|
||||
return {
|
||||
label: 'Utgången',
|
||||
color: '#8b0000',
|
||||
background: '#ffeaea',
|
||||
border: '#f1b5b5',
|
||||
};
|
||||
}
|
||||
|
||||
if (diffDays <= 3) {
|
||||
return {
|
||||
label: 'Snart utgången',
|
||||
color: '#8a4b00',
|
||||
background: '#fff4e5',
|
||||
border: '#f0cf9b',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: 'OK',
|
||||
color: '#1f5f2c',
|
||||
background: '#ecf8ee',
|
||||
border: '#b9e0bf',
|
||||
};
|
||||
}
|
||||
|
||||
type InventoryPageProps = {
|
||||
searchParams?: Promise<{
|
||||
location?: string;
|
||||
sort?: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
function buildInventoryUrl(location?: string, sort?: string) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (location) {
|
||||
params.set('location', location);
|
||||
}
|
||||
|
||||
if (sort) {
|
||||
params.set('sort', sort);
|
||||
}
|
||||
|
||||
const query = params.toString();
|
||||
return query ? `/inventory?${query}` : '/inventory';
|
||||
}
|
||||
|
||||
export default async function InventoryPage({ searchParams }: InventoryPageProps) {
|
||||
const resolvedSearchParams = searchParams ? await searchParams : {};
|
||||
const location = resolvedSearchParams.location || '';
|
||||
const sort = resolvedSearchParams.sort || '';
|
||||
|
||||
const inventoryPath = (() => {
|
||||
const params = new URLSearchParams();
|
||||
if (location) params.set('location', location);
|
||||
if (sort) params.set('sort', sort);
|
||||
const query = params.toString();
|
||||
return query ? `/api/inventory?${query}` : '/api/inventory';
|
||||
})();
|
||||
|
||||
const [inventory, products] = await Promise.all([
|
||||
fetchJson<InventoryItem[]>(inventoryPath),
|
||||
fetchJson<Product[]>('/api/products'),
|
||||
]);
|
||||
|
||||
const locationOptions = ['', 'Kyl', 'Frys', 'Skafferi'];
|
||||
const sortOptions = [
|
||||
{ value: '', label: 'Senast tillagda' },
|
||||
{ value: 'nameAsc', label: 'Namn A\u2013\u00d6' },
|
||||
{ value: 'bestBeforeAsc', label: 'B\u00e4st f\u00f6re Stigande' },
|
||||
{ value: 'bestBeforeDesc', label: 'B\u00e4st f\u00f6re Fallande' },
|
||||
];
|
||||
|
||||
return (
|
||||
<main style={{ padding: '1rem', maxWidth: '1000px', margin: '0 auto' }}>
|
||||
<Navigation />
|
||||
<h1 style={{ marginBottom: '1.5rem' }}>Varor hemma</h1>
|
||||
|
||||
<InventoryForm products={products} />
|
||||
|
||||
<section style={{ marginBottom: '1.5rem' }}>
|
||||
<h2>Filter och sortering</h2>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gap: '1rem',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
|
||||
alignItems: 'start',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, marginBottom: '0.5rem' }}>Plats</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||
{locationOptions.map((option) => {
|
||||
const isActive = location === option;
|
||||
const label = option === '' ? 'Alla' : option;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={option || 'alla'}
|
||||
href={buildInventoryUrl(option || undefined, sort || undefined)}
|
||||
scroll={false}
|
||||
style={{
|
||||
padding: '0.45rem 0.75rem',
|
||||
borderRadius: '999px',
|
||||
border: '1px solid #ddd',
|
||||
textDecoration: 'none',
|
||||
color: '#111',
|
||||
background: isActive ? '#efefef' : '#fff',
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, marginBottom: '0.5rem' }}>Sortering</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||
{sortOptions.map((option) => {
|
||||
const isActive = sort === option.value;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={option.value || 'default'}
|
||||
href={buildInventoryUrl(location || undefined, option.value || undefined)}
|
||||
scroll={false}
|
||||
style={{
|
||||
padding: '0.45rem 0.75rem',
|
||||
borderRadius: '999px',
|
||||
border: '1px solid #ddd',
|
||||
textDecoration: 'none',
|
||||
color: '#111',
|
||||
background: isActive ? '#efefef' : '#fff',
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<InventoryList inventory={inventory} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user