fix: resolve TagSelector build error and improve component type safety

- Fixed undefined setTags in TagSelector.tsx
- Updated TagSelector to use Dexie cache for immediate UI updates
- Refined TastingList mapping to satisfy TypeScript interface
- Verified fix with a successful production build
This commit is contained in:
2025-12-19 14:17:40 +01:00
parent f52cfb80fc
commit cbb28b389c
2 changed files with 14 additions and 10 deletions

View File

@@ -34,7 +34,7 @@ export default function TagSelector({ category, selectedTagIds, onToggleTag, lab
const tagList = tags || []; const tagList = tags || [];
if (!search) return tagList; if (!search) return tagList;
const s = search.toLowerCase(); const s = search.toLowerCase();
return tagList.filter((tag: any) => { return tagList.filter((tag) => {
const rawMatch = tag.name.toLowerCase().includes(s); const rawMatch = tag.name.toLowerCase().includes(s);
const translatedMatch = tag.is_system_default && t(`aroma.${tag.name}`).toLowerCase().includes(s); const translatedMatch = tag.is_system_default && t(`aroma.${tag.name}`).toLowerCase().includes(s);
return rawMatch || translatedMatch; return rawMatch || translatedMatch;
@@ -47,14 +47,14 @@ export default function TagSelector({ category, selectedTagIds, onToggleTag, lab
setIsCreating(true); setIsCreating(true);
const result = await createCustomTag(search, category); const result = await createCustomTag(search, category);
if (result.success && result.tag) { if (result.success && result.tag) {
setTags(prev => [...prev, result.tag!]); await db.cache_tags.put(result.tag!);
onToggleTag(result.tag!.id); onToggleTag(result.tag!.id);
setSearch(''); setSearch('');
} }
setIsCreating(false); setIsCreating(false);
}; };
const selectedTags = tags.filter(t => selectedTagIds.includes(t.id)); const selectedTags = (tags || []).filter(t => selectedTagIds.includes(t.id));
return ( return (
<div className="space-y-3"> <div className="space-y-3">
@@ -137,7 +137,7 @@ export default function TagSelector({ category, selectedTagIds, onToggleTag, lab
<Sparkles size={10} /> {t('camera.wbMatchFound') ? 'KI Vorschläge' : 'AI Suggestions'} <Sparkles size={10} /> {t('camera.wbMatchFound') ? 'KI Vorschläge' : 'AI Suggestions'}
</div> </div>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
{tags {(tags || [])
.filter(t => !selectedTagIds.includes(t.id) && suggestedTagNames.some((s: string) => s.toLowerCase() === t.name.toLowerCase())) .filter(t => !selectedTagIds.includes(t.id) && suggestedTagNames.some((s: string) => s.toLowerCase() === t.name.toLowerCase()))
.map(tag => ( .map(tag => (
<button <button
@@ -162,7 +162,7 @@ export default function TagSelector({ category, selectedTagIds, onToggleTag, lab
</div> </div>
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
{suggestedCustomTagNames {suggestedCustomTagNames
.filter(name => !tags.some(t => t.name.toLowerCase() === name.toLowerCase())) .filter(name => !(tags || []).some(t => t.name.toLowerCase() === name.toLowerCase()))
.map(name => ( .map(name => (
<button <button
key={name} key={name}
@@ -172,7 +172,7 @@ export default function TagSelector({ category, selectedTagIds, onToggleTag, lab
setCreatingSuggestion(name); setCreatingSuggestion(name);
const result = await createCustomTag(name, category); const result = await createCustomTag(name, category);
if (result.success && result.tag) { if (result.success && result.tag) {
setTags(prev => [...prev, result.tag!]); await db.cache_tags.put(result.tag!);
onToggleTag(result.tag!.id); onToggleTag(result.tag!.id);
} }
setCreatingSuggestion(null); setCreatingSuggestion(null);
@@ -190,7 +190,7 @@ export default function TagSelector({ category, selectedTagIds, onToggleTag, lab
{/* Suggestions Chips (limit to 6 random or most common) */} {/* Suggestions Chips (limit to 6 random or most common) */}
{!search && tags.length > 0 && ( {!search && tags.length > 0 && (
<div className="flex flex-wrap gap-1.5 pt-1"> <div className="flex flex-wrap gap-1.5 pt-1">
{tags {(tags || [])
.filter(t => !selectedTagIds.includes(t.id) && (!suggestedTagNames || !suggestedTagNames.some((s: string) => s.toLowerCase() === t.name.toLowerCase()))) .filter(t => !selectedTagIds.includes(t.id) && (!suggestedTagNames || !suggestedTagNames.some((s: string) => s.toLowerCase() === t.name.toLowerCase())))
.slice(0, 8) .slice(0, 8)
.map(tag => ( .map(tag => (

View File

@@ -37,6 +37,7 @@ interface Tasting {
} }
}[]; }[];
user_id: string; user_id: string;
isPending?: boolean;
} }
interface TastingListProps { interface TastingListProps {
@@ -88,7 +89,10 @@ export default function TastingList({ initialTastings, currentUserId, bottleId }
bottle_id: p.bottle_id, bottle_id: p.bottle_id,
created_at: p.tasted_at, created_at: p.tasted_at,
user_id: currentUserId || '', user_id: currentUserId || '',
isPending: true isPending: true,
tasting_buddies: [],
tasting_sessions: undefined,
tasting_tags: []
})) }))
]; ];
@@ -149,7 +153,7 @@ export default function TastingList({ initialTastings, currentUserId, bottleId }
}`}> }`}>
{note.is_sample ? 'Sample' : 'Bottle'} {note.is_sample ? 'Sample' : 'Bottle'}
</span> </span>
{(note as any).isPending && ( {note.isPending && (
<div className="bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400 px-2 py-0.5 rounded-lg text-[10px] font-black uppercase tracking-tighter flex items-center gap-1.5 animate-pulse"> <div className="bg-amber-100 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400 px-2 py-0.5 rounded-lg text-[10px] font-black uppercase tracking-tighter flex items-center gap-1.5 animate-pulse">
<Clock size={10} /> <Clock size={10} />
Wartet auf Sync... Wartet auf Sync...
@@ -174,7 +178,7 @@ export default function TastingList({ initialTastings, currentUserId, bottleId }
<Calendar size={12} /> <Calendar size={12} />
{new Date(note.created_at).toLocaleDateString('de-DE')} {new Date(note.created_at).toLocaleDateString('de-DE')}
</div> </div>
{(!currentUserId || note.user_id === currentUserId) && !(note as any).isPending && ( {(!currentUserId || note.user_id === currentUserId) && !note.isPending && (
<button <button
onClick={() => handleDelete(note.id, note.bottle_id)} onClick={() => handleDelete(note.id, note.bottle_id)}
disabled={!!isDeleting} disabled={!!isDeleting}