fix: verbleibende Lint-Befunde ausraeumen (Lint jetzt ohne Beanstandung)
Vier offene Befunde standen einem sauberen Produktionslauf im Weg:
- recently-viewed-section und site-chrome riefen setState direkt im
Effektrumpf auf, um einen Leerzustand herzustellen ("keine angesehenen
Produkte" bzw. "Eingabe kuerzer als zwei Zeichen"). Beides ergibt sich
unmittelbar aus vorhandenen Werten und wird jetzt beim Rendern
abgeleitet — das spart je einen zusaetzlichen Renderdurchlauf. Bei der
Suche verschwindet die Vorschlagsliste dadurch sofort beim Leeren des
Feldes statt erst nach dem naechsten Effektlauf.
- STAT_KEYS im Admin-Dashboard wurde ausschliesslich als Typ verwendet,
landete als `as const`-Array aber trotzdem im Bundle. Ersetzt durch
einen Typ-Alias.
- Ein DHL-Test importierte DhlApiError, ohne es zu benutzen, und pruefte
nur den Meldungstext — er haette damit jeden beliebigen anderen Fehler
mit passendem Text durchgehen lassen. Er prueft jetzt zusaetzlich den
Fehlertyp, wie seine beiden Geschwistertests.
Stand danach: eslint ohne Beanstandung (vorher 2 Fehler, 2 Warnungen),
tsc ohne Fehler, 201 Tests gruen, Produktions-Build erfolgreich.
This commit is contained in:
1 parent
107cbadf79
commit
02d493dede
4 files changed
+57
-35
No files matched your search
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useRecentlyViewedStore } from "@/store/recently-viewed-store";
|
||||
import { getRecentlyViewedProducts } from "@/app/shop/actions";
|
||||
import { ProductCard } from "@/components/product-card";
|
||||
@@ -15,22 +15,29 @@ import type { ProductWithRelations } from "@/lib/data";
|
||||
export function RecentlyViewedSection({ excludeProductId }: { excludeProductId?: string }) {
|
||||
const { t } = useI18n();
|
||||
const viewedIds = useRecentlyViewedStore((s) => s.viewedIds);
|
||||
const [products, setProducts] = useState<ProductWithRelations[]>([]);
|
||||
const ids = useMemo(
|
||||
() => viewedIds.filter((id) => id !== excludeProductId),
|
||||
[viewedIds, excludeProductId]
|
||||
);
|
||||
const [fetched, setFetched] = useState<ProductWithRelations[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const ids = viewedIds.filter((id) => id !== excludeProductId);
|
||||
if (ids.length === 0) {
|
||||
setProducts([]);
|
||||
return;
|
||||
}
|
||||
// Der Leerfall wird bewusst NICHT mehr per setState im Effekt behandelt:
|
||||
// "keine angesehenen Produkte" ergibt sich unmittelbar aus ids und laesst
|
||||
// sich beim Rendern ableiten. Ein setState direkt im Effektrumpf loest
|
||||
// dagegen einen zusaetzlichen Renderdurchlauf aus, nur um einen Zustand
|
||||
// herzustellen, der ohnehin schon feststand.
|
||||
if (ids.length === 0) return;
|
||||
let cancelled = false;
|
||||
getRecentlyViewedProducts(ids).then((result) => {
|
||||
if (!cancelled) setProducts(result);
|
||||
if (!cancelled) setFetched(result);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [viewedIds, excludeProductId]);
|
||||
}, [ids]);
|
||||
|
||||
const products = ids.length === 0 ? [] : fetched;
|
||||
|
||||
if (products.length === 0) return null;
|
||||
|
||||
|
||||
@@ -101,12 +101,17 @@ function SearchButton() {
|
||||
const [suggestions, setSuggestions] = useState<SearchSuggestion[]>([]);
|
||||
const toggleButtonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
// "Eingabe zu kurz" ergibt sich unmittelbar aus query und wird beim
|
||||
// Rendern abgeleitet, statt im Effekt per setState hergestellt zu werden —
|
||||
// ein setState direkt im Effektrumpf loest einen zusaetzlichen
|
||||
// Renderdurchlauf aus, nur um einen ohnehin feststehenden Zustand zu
|
||||
// erzeugen. Nebeneffekt: beim Leeren des Feldes verschwindet die Liste
|
||||
// sofort, statt erst nach dem naechsten Effektlauf.
|
||||
const visibleSuggestions = query.trim().length < 2 ? [] : suggestions;
|
||||
|
||||
useEffect(() => {
|
||||
const trimmed = query.trim();
|
||||
if (trimmed.length < 2) {
|
||||
setSuggestions([]);
|
||||
return;
|
||||
}
|
||||
if (trimmed.length < 2) return;
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => {
|
||||
fetch(`/api/shop/search-suggestions?q=${encodeURIComponent(trimmed)}`, {
|
||||
@@ -153,9 +158,9 @@ function SearchButton() {
|
||||
placeholder={t.nav.searchPlaceholder}
|
||||
className="w-full border-b border-forest bg-transparent px-2 py-1 text-sm focus:outline-none"
|
||||
/>
|
||||
{suggestions.length > 0 ? (
|
||||
{visibleSuggestions.length > 0 ? (
|
||||
<ul className="absolute top-full left-0 mt-2 w-72 bg-background border border-ink/10 shadow-[0_16px_32px_-16px_rgba(36,53,43,0.25)] z-50">
|
||||
{suggestions.map((s) => (
|
||||
{visibleSuggestions.map((s) => (
|
||||
<li key={s.slug}>
|
||||
<Link
|
||||
href={`/honig/${s.slug}`}
|
||||
|
||||
Reference in new issue
Block a user