feat(favorites-system): User kann Customer/Project favoritisieren (localStorage) [tsc:fail]

This commit is contained in:
Dennis (via Claude+Gemma) 2026-05-23 09:19:24 +02:00
parent eb65bea128
commit 29646c8015
3 changed files with 110 additions and 2 deletions

View File

@ -1,5 +1,8 @@
{
"completed_features": [],
"current_feature": "recently-viewed-widget",
"started_at": "2026-05-23T09:18:37.298269"
"current_feature": "favorites-system",
"started_at": "2026-05-23T09:18:37.298269",
"attempted_features": [
"recently-viewed-widget"
]
}

View File

@ -3300,3 +3300,21 @@ src/index.ts(27,25): error TS2769: No overload matches this call.
Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>,
- `09:19:02` **INFO** Committed feature recently-viewed-widget
- `09:19:02` **INFO** Pushed: rc=0
## Phase-3 Feature: favorites-system (2026-05-23 09:19:02)
- `09:19:02` **INFO** Description: User kann Customer/Project favoritisieren (localStorage)
- `09:19:02` **INFO** Generating apps/web/src/lib/favorites.ts (useFavorites() hook + util. Manages localStorage 'favorites' Set per e…)
- `09:19:22` **INFO** wrote 2276 chars in 19.9s (attempt 1)
- `09:19:22` **INFO** Running tsc --noEmit on api…
- `09:19:24` **WARN** tsc errors:
src/db/schema.ts(37,14): error TS7022: 'customers' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
src/db/schema.ts(45,59): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
src/db/schema.ts(49,14): error TS7022: 'projects' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
src/db/schema.ts(53,56): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
src/index.ts(27,25): error TS2769: No overload matches this call.
Overload 1 of 3, '(plugin: FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
Argument of type 'Promise<FastifyMultipartPlugin>' is not assignable to parameter of type 'FastifyPluginCallback<{ limits: { fileSize: number; }; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
Type 'Promise<FastifyMultipartPlugin>' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>,

View File

@ -0,0 +1,87 @@
import { useState, useEffect } from 'react';
const STORAGE_KEY = 'emberclone_favorites';
const FAVORITES_EVENT = 'emberclone_favorites_updated';
export const favoritesUtils = {
getFavorites: (): Record<string, Set<string>> => {
const data = localStorage.getItem(STORAGE_KEY);
if (!data) return {};
try {
const parsed = JSON.parse(data);
const result: Record<string, Set<string>> = {};
for (const [type, ids] of Object.entries(parsed)) {
result[type] = new Set(ids as string[]);
}
return result;
} catch {
return {};
}
},
saveFavorites: (favorites: Record<string, Set<string>>) => {
const data: Record<string, string[]> = {};
for (const [type, ids] of Object.entries(favorites)) {
data[type] = Array.from(ids);
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
window.dispatchEvent(new CustomEvent(FAVORITES_EVENT));
},
isFavorite: (type: string, id: string): boolean => {
const favs = favoritesUtils.getFavorites();
return favs[type]?.has(id) ?? false;
},
toggleFavorite: (type: string, id: string) => {
const favs = favoritesUtils.getFavorites();
if (!favs[type]) {
favs[type] = new Set();
}
if (favs[type].has(id)) {
favs[type].delete(id);
} else {
favs[type].add(id);
}
favoritesUtils.saveFavorites(favs);
},
getAllFavorites: (type: string): string[] => {
const favs = favoritesUtils.getFavorites();
return Array.from(favs[type] || []);
}
};
export function useFavorites() {
const [favorites, setFavorites] = useState(() => favoritesUtils.getFavorites());
useEffect(() => {
const handleUpdate = () => {
setFavorites(favoritesUtils.getFavorites());
};
window.addEventListener(FAVORITES_EVENT, handleUpdate);
return () => window.removeEventListener(FAVORITES_EVENT, handleUpdate);
}, []);
const isFavorite = (type: string, id: string) => {
return favorites[type]?.has(id) ?? false;
};
const toggle = (type: string, id: string) => {
favoritesUtils.toggleFavorite(type, id);
};
const getAll = (type: string) => {
return Array.from(favorites[type] || []);
};
return {
isFavorite,
toggle,
getAll,
favorites
};
}