export type WishlistSort = 'title' | 'author' type ListedBook = Pick /** "Last, First" -> "First Last" */ export function wishlistAuthorName(author: string): string { const [last, first] = author.split(/,\s*/, 2) return first ? `${first} ${last}` : author } function searchableText(book: ListedBook): string { const authors = book.authors.flatMap(author => [author, wishlistAuthorName(author)]) return [book.title, ...authors].join(' ').toLowerCase() } const collator = new Intl.Collator('de', { sensitivity: 'base', numeric: true }) function compare(sort: WishlistSort) { const byTitle = (a: ListedBook, b: ListedBook) => collator.compare(a.title, b.title) const byAuthor = (a: ListedBook, b: ListedBook) => collator.compare(a.authors[0] ?? '', b.authors[0] ?? '') return sort === 'title' ? (a: ListedBook, b: ListedBook) => byTitle(a, b) || byAuthor(a, b) : (a: ListedBook, b: ListedBook) => byAuthor(a, b) || byTitle(a, b) } function searchAndSort(books: Book[], search: string, sort: WishlistSort): Book[] { const terms = search.trim().toLowerCase().split(/\s+/).filter(Boolean) return books .filter((book) => { const text = searchableText(book) return terms.every(term => text.includes(term)) }) .toSorted(compare(sort)) } export function useWishlist(search: Ref, sort: Ref) { const { data, error, status } = useFetch('/api/wishlist/books', { key: 'wishlist-books', default: () => [] }) const { data: reading } = useFetch('/api/wishlist/reading', { key: 'wishlist-reading', default: () => [] }) const { data: read } = useFetch('/api/wishlist/read', { key: 'wishlist-read', default: () => [] }) return { books: computed(() => searchAndSort(data.value, search.value, sort.value)), total: computed(() => data.value.length), readingBooks: computed(() => searchAndSort(reading.value, search.value, sort.value)), readingTotal: computed(() => reading.value.length), readBooks: computed(() => searchAndSort(read.value, search.value, sort.value)), readTotal: computed(() => read.value.length), error, status } }