feat: implement automated Whiskybase ID discovery

This commit is contained in:
2025-12-18 12:40:57 +01:00
parent 35c2443473
commit fef1c4a275
3 changed files with 211 additions and 3 deletions

View File

@@ -1,8 +1,9 @@
'use client';
import React, { useState } from 'react';
import { Edit2, Save, X, Info, Tag, FlaskConical, CircleDollarSign } from 'lucide-react';
import { Edit2, Save, X, Info, Tag, FlaskConical, CircleDollarSign, Search, Loader2, ExternalLink } from 'lucide-react';
import { updateBottle } from '@/services/update-bottle';
import { discoverWhiskybaseId } from '@/services/discover-whiskybase';
interface EditBottleFormProps {
bottle: {
@@ -21,7 +22,9 @@ interface EditBottleFormProps {
export default function EditBottleForm({ bottle, onComplete }: EditBottleFormProps) {
const [isEditing, setIsEditing] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [isSearching, setIsSearching] = useState(false);
const [error, setError] = useState<string | null>(null);
const [discoveryResult, setDiscoveryResult] = useState<{ id: string; url: string; title: string } | null>(null);
const [formData, setFormData] = useState({
name: bottle.name,
@@ -33,6 +36,33 @@ export default function EditBottleForm({ bottle, onComplete }: EditBottleFormPro
purchase_price: bottle.purchase_price || '',
});
const handleDiscover = async () => {
setIsSearching(true);
setError(null);
setDiscoveryResult(null);
const result = await discoverWhiskybaseId({
name: formData.name,
distillery: formData.distillery,
abv: formData.abv,
age: formData.age
});
if (result.success && result.id) {
setDiscoveryResult({ id: result.id!, url: result.url!, title: result.title! });
} else {
setError(result.error || 'Keinen Treffer gefunden.');
}
setIsSearching(false);
};
const applyDiscovery = () => {
if (discoveryResult) {
setFormData({ ...formData, whiskybase_id: discoveryResult.id });
setDiscoveryResult(null);
}
};
const handleSave = async () => {
setIsSaving(true);
setError(null);
@@ -142,13 +172,47 @@ export default function EditBottleForm({ bottle, onComplete }: EditBottleFormPro
</div>
</div>
<div className="space-y-1">
<label className="text-[10px] font-black uppercase text-zinc-400 ml-1">Whiskybase ID</label>
<label className="text-[10px] font-black uppercase text-zinc-400 ml-1 flex justify-between items-center">
<span>Whiskybase ID</span>
<button
type="button"
onClick={handleDiscover}
disabled={isSearching}
className="text-amber-600 hover:text-amber-700 flex items-center gap-1 normal-case font-bold"
>
{isSearching ? <Loader2 size={10} className="animate-spin" /> : <Search size={10} />}
Automatisch suchen
</button>
</label>
<input
type="text"
value={formData.whiskybase_id}
onChange={(e) => setFormData({ ...formData, whiskybase_id: e.target.value })}
className="w-full px-4 py-2 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-xl outline-none focus:ring-2 focus:ring-amber-500"
/>
{discoveryResult && (
<div className="mt-2 p-3 bg-zinc-50 dark:bg-zinc-800/50 border border-amber-500/20 rounded-xl animate-in fade-in slide-in-from-top-2">
<p className="text-[10px] text-zinc-500 mb-2">Treffer gefunden:</p>
<p className="text-[11px] font-bold text-zinc-800 dark:text-zinc-200 mb-2 truncate">{discoveryResult.title}</p>
<div className="flex gap-2">
<button
type="button"
onClick={applyDiscovery}
className="px-3 py-1.5 bg-amber-600 text-white text-[10px] font-black uppercase rounded-lg hover:bg-amber-700 transition-colors"
>
ID Übernehmen
</button>
<a
href={discoveryResult.url}
target="_blank"
rel="noopener noreferrer"
className="px-3 py-1.5 bg-zinc-200 dark:bg-zinc-700 text-zinc-600 dark:text-zinc-300 text-[10px] font-black uppercase rounded-lg hover:bg-zinc-300 dark:hover:bg-zinc-600 transition-colors flex items-center gap-1"
>
<ExternalLink size={10} /> Prüfen
</a>
</div>
</div>
)}
</div>
<div className="space-y-1">
<label className="text-[10px] font-black uppercase text-amber-600 ml-1 flex items-center gap-1">