Commit dcab7df1 authored by Cursor AI's avatar Cursor AI Committed by Jakob Moser
Browse files

Refactor: Extract Message, ClearSearchLink, and ArtistGroup reusable components from SongList

parent b9b85427
Loading
Loading
Loading
Loading
+28 −67
Changes for frontend/SongList.mjs: 28 added lines, 67 removed lines.
Original line number Diff line number Diff line
// frontend/SongList.mjs
import ArtistGroup from "./components/pieces/ArtistGroup.mjs"
import Message from "./components/pieces/Message.mjs"
import ClearSearchLink from "./components/pieces/ClearSearchLink.mjs"
import SongItem from "./components/pieces/SongItem.mjs"
import PaginationControls from "./components/pieces/PaginationControls.mjs"
import SongListModel from "./SongListModel.mjs"
@@ -32,99 +35,57 @@ const SongList = {
        } = model.getProcessedSongsForView(currentView, searchTerm)

        const artistsOnPage = Object.keys(groupedSongs)
        let messageElement = null

        // Hilfsfunktion für den "Suche zurücksetzen"-Link
        const createClearSearchLink = totalFavs => {
            return m(
                "a.clear-search-link",
                {
                    onclick: e => {
                        e.preventDefault()
                        if (onSearchTermChange) onSearchTermChange("")
                    },
                    href: "#",
                    role: "button",
                    tabindex: 0,
                    onkeypress: e => {
                        if (
                            (e.key === "Enter" || e.key === " ") &&
                            onSearchTermChange
                        ) {
                            e.preventDefault()
                            onSearchTermChange("")
                        }
                    },
                },
                `alle ${totalFavs} Favoriten anzeigen`,
            )
        }

        // Nachrichten-Rendering basierend auf dem `message`-Objekt oder String
        // Message rendering
        let messageElement = null
        if (message && typeof message === "object") {
            if (message.type === "filtered_favorites_some_hidden") {
                messageElement = m("p.view-message", [
                messageElement = m(Message, [
                    `Deine Suche zeigt ${message.showing} von deinen insgesamt ${message.total} Favoriten. `,
                    `Möchtest du `,
                    createClearSearchLink(message.total),
                    m(ClearSearchLink, {
                        totalFavs: message.total,
                        onClear: () => onSearchTermChange && onSearchTermChange("")
                    }),
                    `?`,
                ])
            } else if (
                message.type === "filtered_favorites_none_found_by_search"
            ) {
                messageElement = m("p.view-message", [
            } else if (message.type === "filtered_favorites_none_found_by_search") {
                messageElement = m(Message, [
                    `Deine Suche ergab keine Treffer unter deinen ${message.total} Favoriten. `,
                    `Möchtest du `,
                    createClearSearchLink(message.total),
                    m(ClearSearchLink, {
                        totalFavs: message.total,
                        onClear: () => onSearchTermChange && onSearchTermChange("")
                    }),
                    `?`,
                ])
            }
            // Hier könnten weitere Objekt-Nachrichtentypen behandelt werden
        } else if (message && typeof message === "string") {
            // String-Nachrichten werden angezeigt, wenn totalFilteredItems 0 ist
            // ODER wenn es eine spezifische Ansicht wie 'popular' ist, die immer eine Nachricht hat.
            if (totalFilteredItems === 0 || currentView === "popular") {
                messageElement = m("p.view-message", message)
                messageElement = m(Message, message)
            }
        }

        // Fallback-Nachricht, wenn keine spezifische Nachricht erstellt wurde,
        // aber absolut keine Items vorhanden sind (über alle Seiten).
        // Dies sollte durch getProcessedSongsForView abgedeckt sein, ist aber ein Sicherheitsnetz.
        if (!messageElement && totalFilteredItems === 0 && !model.isLoading) {
            messageElement = m(
                "p.view-message",
                "Keine Songs entsprechen den aktuellen Kriterien.",
            )
            messageElement = m(Message, "Keine Songs entsprechen den aktuellen Kriterien.")
        }

        const elementsToRender = []
        if (messageElement) {
            // Wenn eine Nachricht zum Anzeigen vorhanden ist
            elementsToRender.push(messageElement)
        }

        if (hasContentOnPage) {
            elementsToRender.push(
                artistsOnPage.map(artist =>
                    m(
                        "div.artist-group",
                        {
                            key: `${currentView}-${searchTerm || "all"}-${artist}-${currentPage}`,
                        },
                        [
                            m("h2.artist-name", artist),
                            groupedSongs[artist].map(s =>
                                m(SongItem, {
                                    key: s.id,
                                    song: s,
                                    onToggleFavorite:
                                        model.toggleFavorite.bind(model),
                                }),
                            ),
                        ],
                    ),
                ),
                    m(ArtistGroup, {
                        artist,
                        songs: groupedSongs[artist],
                        currentView,
                        searchTerm,
                        currentPage,
                        onToggleFavorite: model.toggleFavorite.bind(model),
                    })
                )
            )
        }

@@ -134,7 +95,7 @@ const SongList = {
                    currentPage: currentPage,
                    totalPages: totalPages,
                    onPageChange: model.onPageChange.bind(model),
                }),
                })
            )
        }

+25 −0
Changes for frontend/components/pieces/ArtistGroup.mjs: 25 added lines, 0 removed lines.
Original line number Diff line number Diff line
import SongItem from "./SongItem.mjs"

const ArtistGroup = {
    view: function(vnode) {
        const { artist, songs, currentView, searchTerm, currentPage, onToggleFavorite } = vnode.attrs
        return m(
            "div.artist-group",
            {
                key: `${currentView}-${searchTerm || "all"}-${artist}-${currentPage}`,
            },
            [
                m("h2.artist-name", artist),
                songs.map(s =>
                    m(SongItem, {
                        key: s.id,
                        song: s,
                        onToggleFavorite,
                    })
                ),
            ]
        )
    },
}

export default ArtistGroup 
 No newline at end of file
+26 −0
Changes for frontend/components/pieces/ClearSearchLink.mjs: 26 added lines, 0 removed lines.
Original line number Diff line number Diff line
const ClearSearchLink = {
    view: function(vnode) {
        const { totalFavs, onClear } = vnode.attrs
        return m(
            "a.clear-search-link",
            {
                onclick: e => {
                    e.preventDefault()
                    if (onClear) onClear()
                },
                href: "#",
                role: "button",
                tabindex: 0,
                onkeypress: e => {
                    if ((e.key === "Enter" || e.key === " ") && onClear) {
                        e.preventDefault()
                        onClear()
                    }
                },
            },
            `alle ${totalFavs} Favoriten anzeigen`,
        )
    },
}

export default ClearSearchLink 
 No newline at end of file
+7 −0
Changes for frontend/components/pieces/Message.mjs: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
const Message = {
    view: function(vnode) {
        return m("p.view-message", vnode.children)
    },
}

export default Message 
 No newline at end of file