Files
recipe-app/frontend/app/recipes/create/page.tsx
T
Nils-Johan Gynther 4f183df711 feat: Implement quick import feature for recipes
- Added QuickImportController and QuickImportService to handle recipe imports from URLs and file paths.
- Created QuickImportModule to encapsulate the quick import functionality.
- Developed frontend ImportFilePage for users to upload files or enter URLs for recipe import.
- Integrated API proxy to communicate with the backend for quick import requests.
- Implemented WriteRecipePage for users to manually input recipes with Markdown support.
- Added page routing for the new import and write recipe functionalities.
2026-04-12 07:41:18 +02:00

224 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import Link from 'next/link';
import { useState } from 'react';
import Navigation from '../../Navigation';
import { parseErrorResponse } from '../../../lib/error-handler';
import { useRouter } from 'next/navigation';
export default function CreateRecipePage() {
const router = useRouter();
const [quickImportUrl, setQuickImportUrl] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleQuickImport = async (e: React.FormEvent) => {
e.preventDefault();
setError(null);
setIsLoading(true);
try {
const input = quickImportUrl.trim();
if (!input) {
setError('Vänligen ange en URL eller filsökväg');
setIsLoading(false);
return;
}
// Försök importera från URL eller fil
const res = await fetch('/api/quick-import-proxy', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input }),
});
if (!res.ok) {
const errorMessage = await parseErrorResponse(res);
setError(errorMessage || 'Importen misslyckades. Kontrollera att länken eller filsökvägen är korrekt.');
setIsLoading(false);
return;
}
const data = await res.json();
if (data.markdown) {
// Omdirigera till /recipes/write med förifylld Markdown
// Vi använder sessionStorage för att passa data mellan sidor
sessionStorage.setItem('prefilled_markdown', data.markdown);
router.push('/recipes/write');
}
} catch (err) {
const message = err instanceof Error ? err.message : 'Något oväntad gick fel';
setError(`Fel: ${message}`);
} finally {
setIsLoading(false);
}
};
return (
<main style={{ padding: '1rem', maxWidth: '800px', margin: '0 auto' }}>
<Navigation />
<h1 style={{ marginBottom: '1.5rem' }}>Lägg till nytt recept</h1>
{/* SNABBIMPORT-SEKTION */}
<div
style={{
background: '#fef3c7',
border: '2px solid #f59e0b',
borderRadius: '8px',
padding: '1.5rem',
marginBottom: '2rem',
}}
>
<h2 style={{ margin: '0 0 0.5rem 0', fontSize: '1.1rem', color: '#92400e' }}>
Snabbimport
</h2>
<p style={{ margin: '0 0 1rem 0', color: '#92400e', fontSize: '0.9rem' }}>
Klistra in en ICA-receptlänk eller filsökväg för att importera direkt:
</p>
<form onSubmit={handleQuickImport} style={{ display: 'grid', gap: '0.75rem' }}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: '0.5rem' }}>
<input
type="text"
value={quickImportUrl}
onChange={(e) => setQuickImportUrl(e.target.value)}
placeholder="https://www.ica.se/recept/... eller C:\recepter\file.pdf"
style={{
padding: '0.75rem',
border: '1px solid #d97706',
borderRadius: '4px',
fontSize: '0.95rem',
boxSizing: 'border-box',
}}
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading || !quickImportUrl.trim()}
style={{
padding: '0.75rem 1.5rem',
background: '#f59e0b',
color: '#fff',
border: 'none',
borderRadius: '4px',
cursor: isLoading || !quickImportUrl.trim() ? 'not-allowed' : 'pointer',
opacity: isLoading || !quickImportUrl.trim() ? 0.6 : 1,
fontSize: '0.95rem',
fontWeight: 600,
whiteSpace: 'nowrap',
}}
>
{isLoading ? 'Laddar...' : '→'}
</button>
</div>
{error && (
<p
style={{
margin: '0.5rem 0 0 0',
color: '#991b1b',
background: '#fee2e2',
padding: '0.75rem',
borderRadius: '4px',
fontSize: '0.85rem',
}}
>
{error}
</p>
)}
<p style={{ margin: '0.75rem 0 0 0', color: '#92400e', fontSize: '0.8rem' }}>
Stöds: ICA-recept, PDF-filer
</p>
</form>
</div>
{/* ELLER-SEPARATOR */}
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '1rem',
margin: '2rem 0',
color: '#999',
}}
>
<div style={{ flex: 1, height: '1px', background: '#ddd' }} />
<span style={{ fontSize: '0.9rem', fontWeight: 500 }}>eller</span>
<div style={{ flex: 1, height: '1px', background: '#ddd' }} />
</div>
{/* KLASSISKA ALTERNATIV */}
<p style={{ marginBottom: '2rem', color: '#666' }}>
Välj ett sätt att lägga till ett recept:
</p>
<div
style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '1.5rem',
}}
>
{/* Skriv in recept */}
<Link
href="/recipes/write"
style={{
padding: '2rem',
border: '2px solid #0070f3',
borderRadius: '8px',
textDecoration: 'none',
background: 'linear-gradient(135deg, #e3f2fd 0%, #ffffff 100%)',
transition: 'all 0.2s',
cursor: 'pointer',
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(-4px)';
(e.currentTarget as HTMLElement).style.boxShadow = '0 8px 16px rgba(0,112,243,0.2)';
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(0)';
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
}}
>
<h2 style={{ margin: '0 0 0.5rem 0', color: '#0070f3', fontSize: '1.3rem' }}>
Skriv in recept
</h2>
<p style={{ margin: 0, color: '#666', fontSize: '0.9rem' }}>
Skriv in receptet med ingredienser och instruktioner
</p>
</Link>
{/* Importera från fil/länk */}
<Link
href="/recipes/import"
style={{
padding: '2rem',
border: '2px solid #10b981',
borderRadius: '8px',
textDecoration: 'none',
background: 'linear-gradient(135deg, #ecfdf5 0%, #ffffff 100%)',
transition: 'all 0.2s',
cursor: 'pointer',
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(-4px)';
(e.currentTarget as HTMLElement).style.boxShadow = '0 8px 16px rgba(16,185,129,0.2)';
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(0)';
(e.currentTarget as HTMLElement).style.boxShadow = 'none';
}}
>
<h2 style={{ margin: '0 0 0.5rem 0', color: '#10b981', fontSize: '1.3rem' }}>
📥 Importera från fil
</h2>
<p style={{ margin: 0, color: '#666', fontSize: '0.9rem' }}>
Ladda upp PDF, länk eller annan filtyp
</p>
</Link>
</div>
</main>
);
}