b18ac25ccc
- FavoriteIcon (favorites-widget.tsx): dreistufiger Ersatzweg proxy ->
direct -> none; Buchstaben-Platzhalter liegt immer darunter. Direktbild
nur bei http/https-URL (getDirectFaviconSrc), referrerPolicy
no-referrer, kein Drittanbieter-Favicon-Dienst. key={iconUrl|url}
setzt die Stufe bei Aenderung zurueck; kein style.display-Hack mehr
- handleMove + Sortierpfeile im Bearbeitungsmodus (nur bei nicht-inline-
Bearbeitung): optimistische Neuberechnung, PUT /favorites/order ueber
reorderFavorites; erster/letzter Eintrag deaktiviert; Fehler ->
Neuladen mit Fehlermeldung
- favorites-api.ts: reorderFavorites(widgetId, ids)
- de.json/en.json: widgets.favorites.moveUpButton/moveDownButton
- 5 neue Widget-Tests (Ersatzbild-Kette, Nicht-http-URL, Pfeilzustand,
Klick, Fehlerpfad); 11 bestehende unveraendert gruen; volle Web-Suite
64 Dateien/429 Tests und type-check gruen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
/**
|
|
* Favorites API client functions.
|
|
* Mirrors the NestJS FavoritesController routes (08-03).
|
|
* All calls use credentials: 'include' for cookie-based auth.
|
|
* PUT /favorites/order (reorderFavorites) added 260917-jdd.
|
|
*/
|
|
|
|
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
|
|
|
export interface FavoriteLink {
|
|
id: string;
|
|
widgetId: string;
|
|
title: string;
|
|
url: string;
|
|
iconUrl: string | null;
|
|
position: number;
|
|
}
|
|
|
|
/**
|
|
* Fetch all favorite links for a specific widget instance.
|
|
* The widgetId parameter scopes the query to the correct widget (Pitfall 3).
|
|
*/
|
|
export async function fetchFavorites(widgetId: string): Promise<FavoriteLink[]> {
|
|
const res = await fetch(
|
|
`${API_URL}/favorites?widgetId=${encodeURIComponent(widgetId)}`,
|
|
{ credentials: 'include' },
|
|
);
|
|
if (!res.ok) throw new Error('Failed to fetch favorites');
|
|
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Create a new favorite link.
|
|
* Server-side icon discovery runs automatically if iconUrl is not provided.
|
|
*/
|
|
export async function createFavorite(payload: {
|
|
widgetId: string;
|
|
title: string;
|
|
url: string;
|
|
iconUrl?: string;
|
|
}): Promise<FavoriteLink> {
|
|
const res = await fetch(`${API_URL}/favorites`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) throw new Error('Failed to create favorite');
|
|
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Update an existing favorite link.
|
|
* Pass iconUrl: null to clear a stored icon.
|
|
*/
|
|
export async function updateFavorite(
|
|
id: string,
|
|
payload: Partial<{ title: string; url: string; iconUrl: string | null; position: number }>,
|
|
): Promise<FavoriteLink> {
|
|
const res = await fetch(`${API_URL}/favorites/${encodeURIComponent(id)}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) throw new Error('Failed to update favorite');
|
|
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Persist the display order for a widget's favorites (260917-jdd).
|
|
* `ids` is the full id list in the desired order; the server responds with
|
|
* the favorites of this widget in the new order.
|
|
*/
|
|
export async function reorderFavorites(
|
|
widgetId: string,
|
|
ids: string[],
|
|
): Promise<FavoriteLink[]> {
|
|
const res = await fetch(`${API_URL}/favorites/order`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify({ widgetId, ids }),
|
|
});
|
|
if (!res.ok) throw new Error('Failed to reorder favorites');
|
|
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Delete a favorite link by id.
|
|
*/
|
|
export async function deleteFavorite(id: string): Promise<void> {
|
|
const res = await fetch(`${API_URL}/favorites/${encodeURIComponent(id)}`, {
|
|
method: 'DELETE',
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to delete favorite');
|
|
}
|