feat(Navigation): update navigation links and rename 'Matplan' to 'Matsedel'

feat(matsedel): add MealPlanClient and page for meal planning
feat(profil): add AI and suggestion tabs for admin users
This commit is contained in:
Nils-Johan Gynther
2026-04-19 20:06:33 +02:00
parent 7e296acb60
commit 31b7da82cd
8 changed files with 86 additions and 11 deletions
+2
View File
@@ -9,6 +9,8 @@ const ADMIN_TABS: Tab[] = [
{ id: 'profil', label: 'Min profil' },
{ id: 'anvandare', label: '👥 Användare' },
{ id: 'databas', label: '🗄️ Databas' },
{ id: 'forslag', label: '⏳ Förslag' },
{ id: 'ai', label: '🤖 AI' },
];
type Props = {
+10 -1
View File
@@ -22,16 +22,25 @@ export default async function ProfilPage({ searchParams }: Props) {
} else if (tab === 'anvandare' && isAdmin) {
const { default: AnvandareTab } = await import('./tabs/AnvandareTab');
TabContent = AnvandareTab;
} else if (tab === 'forslag' && isAdmin) {
const { default: ForslagTab } = await import('./tabs/ForslagTab');
TabContent = ForslagTab;
} else if (tab === 'ai' && isAdmin) {
const { default: AiTab } = await import('./tabs/AiTab');
TabContent = AiTab;
} else {
TabContent = MinProfilTab;
}
const adminTabs = ['databas', 'anvandare', 'forslag', 'ai'];
const activeTab = isAdmin && adminTabs.includes(tab) ? tab : 'profil';
return (
<>
<Navigation />
<main style={{ padding: '1rem', maxWidth: '1200px', margin: '0 auto' }}>
<h1 style={{ marginBottom: '1.5rem' }}>Min profil</h1>
<ProfileTabs activeTab={tab === 'databas' || tab === 'anvandare' ? tab : 'profil'} isAdmin={isAdmin} />
<ProfileTabs activeTab={activeTab} isAdmin={isAdmin} />
<TabContent />
</main>
</>
+28
View File
@@ -0,0 +1,28 @@
import AiAdminClient from '../../admin/ai/AiAdminClient';
import type { AiModelInfo } from '../../admin/ai/AiAdminClient';
const API_BASE = process.env.NEXT_PUBLIC_API_URL_INTERNAL || 'http://recipe-api:8080';
export default async function AiTab() {
const key = process.env.MISTRAL_API_KEY ?? '';
const hasKey = key.length > 0;
const keyHint = key.length >= 4 ? key.slice(-4) : '????';
let aiFunctions: AiModelInfo[] = [];
try {
const res = await fetch(`${API_BASE}/api/ai/models`, { cache: 'no-store' });
if (res.ok) aiFunctions = await res.json();
} catch {
// backend ej nåbart
}
return (
<div>
<h2 style={{ fontSize: '1.1rem', marginBottom: '0.25rem' }}>🤖 AI-konfiguration</h2>
<p style={{ color: '#666', marginBottom: '2rem', fontSize: '0.9rem' }}>
Översikt över implementerade AI-funktioner och API-nyckelstatus.
</p>
<AiAdminClient keyHint={keyHint} hasKey={hasKey} aiFunctions={aiFunctions} />
</div>
);
}
+25
View File
@@ -0,0 +1,25 @@
import { getAuthHeaders } from '../../../../lib/auth-headers';
import PendingProductsClient from '../../admin/products/pending/PendingProductsClient';
const API_BASE = process.env.NEXT_PUBLIC_API_URL_INTERNAL ?? 'http://recipe-api:8080';
export default async function ForslagTab() {
const headers = await getAuthHeaders();
let products: any[] = [];
try {
const res = await fetch(`${API_BASE}/api/products/pending`, { headers, cache: 'no-store' });
if (res.ok) products = await res.json();
} catch {
// backend ej nåbart
}
return (
<div>
<h2 style={{ fontSize: '1.1rem', marginBottom: '0.5rem' }}>Väntande produktförslag</h2>
<p style={{ color: '#64748b', marginBottom: '1.5rem', fontSize: '0.9rem' }}>
Produkter som användare har föreslagit. Godkänn för att göra dem tillgängliga i katalogen, eller avvisa för att ta bort dem.
</p>
<PendingProductsClient products={products} />
</div>
);
}