diff --git a/backend/app/api/artists.py b/backend/app/api/artists.py index 939fe4f..7b41a6a 100644 --- a/backend/app/api/artists.py +++ b/backend/app/api/artists.py @@ -65,6 +65,16 @@ async def autocomplete(): ]) +@artists_bp.route("/names", methods=["GET"]) +async def names(): + """Every artist, id + name + slug, alphabetical. For filter pickers that + list artists before anything is typed; `autocomplete` deliberately returns + nothing for an empty query.""" + async with get_session() as session: + rows = await ArtistService(session).all_names() + return jsonify([{"id": i, "name": n, "slug": s} for i, n, s in rows]) + + @artists_bp.route("/directory", methods=["GET"]) async def directory(): """FC-3f: cursor-paginated artists directory. diff --git a/backend/app/services/artist_service.py b/backend/app/services/artist_service.py index a4150c8..b7de9dc 100644 --- a/backend/app/services/artist_service.py +++ b/backend/app/services/artist_service.py @@ -300,6 +300,18 @@ class ArtistService: await self.session.commit() return artist + async def all_names(self) -> list[tuple[int, str, str]]: + """Every artist as (id, name, slug), alphabetical. + + For pickers that should show a full list before anything is typed (the + Latest feed's artist filter). Three columns and no joins, so it stays + cheap on a library of thousands of artists. + """ + rows = (await self.session.execute( + select(Artist.id, Artist.name, Artist.slug).order_by(func.lower(Artist.name)) + )).all() + return [(r.id, r.name, r.slug) for r in rows] + async def autocomplete(self, prefix: str, limit: int = 20) -> list[Artist]: cleaned = (prefix or "").strip() if not cleaned: diff --git a/frontend/src/components/posts/PostsFilterBar.vue b/frontend/src/components/posts/PostsFilterBar.vue index d7639b1..92c4884 100644 --- a/frontend/src/components/posts/PostsFilterBar.vue +++ b/frontend/src/components/posts/PostsFilterBar.vue @@ -1,21 +1,22 @@