diff --git a/frontend/app/Navigation.tsx b/frontend/app/Navigation.tsx index 04a999d7..89fc171d 100644 --- a/frontend/app/Navigation.tsx +++ b/frontend/app/Navigation.tsx @@ -90,7 +90,7 @@ export default function Navigation() { ⚙️ Admin - ⚡ Snabbimport recept + 📥 Importera 📅 Matplan - - 🧾 Importera kvitto - ); } diff --git a/frontend/app/import/ImportTabsClient.tsx b/frontend/app/import/ImportTabsClient.tsx new file mode 100644 index 00000000..85579d66 --- /dev/null +++ b/frontend/app/import/ImportTabsClient.tsx @@ -0,0 +1,202 @@ +'use client'; + +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useRef, useState, useEffect } from 'react'; +import Navigation from '../Navigation'; +import ReceiptImportClient from '../kvitto/ReceiptImportClient'; +import { parseErrorResponse } from '../../lib/error-handler'; + +type Tab = 'kvitto' | 'recept'; + +type Product = { id: number; name: string; canonicalName: string | null }; + +export default function ImportTabsClient({ activeTab }: { activeTab: Tab }) { + return ( +
+ +

Importera

+ + {/* Flikar */} +
+ + 🧾 Kvitto + + + 📋 Recept + +
+ + {/* Innehåll */} + {activeTab === 'kvitto' && ( +
+

+ Fotografera eller ladda upp ett kvitto — varorna läggs till i ditt inventarie. +

+ +
+ )} + + {activeTab === 'recept' && } +
+ ); +} + +function ReceptImport() { + const router = useRouter(); + const [selectedMethod, setSelectedMethod] = useState<'file' | 'url'>('file'); + const [selectedFile, setSelectedFile] = useState(null); + const [url, setUrl] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + const handleFileSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!selectedFile) { setError('Välj en PDF eller bildfil först.'); return; } + setError(null); + setIsLoading(true); + try { + const formData = new FormData(); + formData.append('file', selectedFile); + const res = await fetch('/api/quick-import-proxy', { method: 'POST', body: formData }); + if (!res.ok) throw new Error(await parseErrorResponse(res)); + const data = await res.json(); + sessionStorage.setItem('prefilled_markdown', data.markdown ?? ''); + router.push('/recipes/write'); + } catch (err) { + setError(err instanceof Error ? err.message : 'Importen misslyckades.'); + } finally { + setIsLoading(false); + } + }; + + const handleUrlSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!url.trim()) { setError('Vänligen ange en URL.'); return; } + setError(null); + setIsLoading(true); + try { + const res = await fetch('/api/quick-import-proxy', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ input: url.trim() }), + }); + if (!res.ok) throw new Error(await parseErrorResponse(res)); + const data = await res.json(); + sessionStorage.setItem('prefilled_markdown', data.markdown ?? ''); + router.push('/recipes/write'); + } catch (err) { + setError(err instanceof Error ? err.message : 'Importen misslyckades.'); + } finally { + setIsLoading(false); + } + }; + + return ( +
+

+ Ladda upp en PDF eller bild för OCR, eller ange en receptlänk. +

+ + {error && ( +
+ {error} +
+ )} + + {/* Välj metod */} +
+ {(['file', 'url'] as const).map((m) => ( + + ))} +
+ + {selectedMethod === 'file' && ( +
+ setSelectedFile(e.target.files?.[0] ?? null)} + style={{ padding: '0.75rem', background: 'white', border: '1px solid #cbd5e1', borderRadius: '6px' }} + /> + +
+ )} + + {selectedMethod === 'url' && ( +
+ setUrl(e.target.value)} + placeholder="https://exempel.se/recept/..." + style={{ padding: '0.75rem', border: '1px solid #d1d5db', borderRadius: '6px', fontSize: '0.9rem' }} + /> + +
+ )} + +
+ Efter import öppnas receptet automatiskt i redigeringsläget. +
+ +
+ + Skriv in recept istället + +
+
+ ); +} diff --git a/frontend/app/import/page.tsx b/frontend/app/import/page.tsx new file mode 100644 index 00000000..92ef4dfa --- /dev/null +++ b/frontend/app/import/page.tsx @@ -0,0 +1,18 @@ +import { Metadata } from 'next'; +import ImportTabsClient from './ImportTabsClient'; + +type Props = { + searchParams: Promise<{ tab?: string }>; +}; + +export async function generateMetadata({ searchParams }: Props): Promise { + const { tab } = await searchParams; + if (tab === 'recept') return { title: 'Importera recept' }; + return { title: 'Importera kvitto' }; +} + +export default async function ImportPage({ searchParams }: Props) { + const { tab } = await searchParams; + const activeTab = tab === 'recept' ? 'recept' : 'kvitto'; + return ; +} diff --git a/frontend/app/kvitto/page.tsx b/frontend/app/kvitto/page.tsx index 804cdda8..1ebd8154 100644 --- a/frontend/app/kvitto/page.tsx +++ b/frontend/app/kvitto/page.tsx @@ -1,16 +1,5 @@ -import Navigation from '../Navigation'; -import ReceiptImportClient from './ReceiptImportClient'; +import { redirect } from 'next/navigation'; export default function KvittoPage() { - return ( -
- -

Importera kvitto

-

- Fotografera eller ladda upp ett kvitto — varorna läggs till i ditt - inventarie. -

- -
- ); + redirect('/import?tab=kvitto'); } diff --git a/frontend/app/recipes/import/page.tsx b/frontend/app/recipes/import/page.tsx index e15a58ae..150d80f8 100644 --- a/frontend/app/recipes/import/page.tsx +++ b/frontend/app/recipes/import/page.tsx @@ -1,5 +1,5 @@ -import ImportFilePage from './ImportFilePage'; +import { redirect } from 'next/navigation'; export default function Page() { - return ; + redirect('/import?tab=recept'); }