From e0c09fe316c1796946ef9171e2019fb7bd44f126 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 03:26:28 +0000 Subject: [PATCH] =?UTF-8?q?refactor(api):=20genres=20=E2=86=92=20src/api?= =?UTF-8?q?=20modules=20(Effort=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical move of the genres feature from src/hooks/queries/genres to src/api/genres, adding queryOptions factories for the query hooks. No behavior change. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01K513rsQz6Lg1HbbfYiafrE --- src/api/genres/types.ts | 8 +++++++ .../genres/useCreateGenreMutation.ts | 4 ++-- .../queries => api}/genres/useGenres.ts | 24 +++++++++---------- src/components/GenreBadge.test.tsx | 4 ++-- src/components/GenreBadge.tsx | 2 +- .../ArtistsTab/filters/FilterSortControls.tsx | 2 +- src/pages/admin/AddGenreDialog.tsx | 2 +- .../ArtistsManagement/AddArtistDialog.tsx | 2 +- .../BulkEditor/GenresCell.tsx | 2 +- 9 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 src/api/genres/types.ts rename src/{hooks/queries => api}/genres/useCreateGenreMutation.ts (91%) rename src/{hooks/queries => api}/genres/useGenres.ts (57%) diff --git a/src/api/genres/types.ts b/src/api/genres/types.ts new file mode 100644 index 00000000..eab4226f --- /dev/null +++ b/src/api/genres/types.ts @@ -0,0 +1,8 @@ +export type Genre = { + id: string; + name: string; +}; + +export const genresKeys = { + all: () => ["genres"] as const, +}; diff --git a/src/hooks/queries/genres/useCreateGenreMutation.ts b/src/api/genres/useCreateGenreMutation.ts similarity index 91% rename from src/hooks/queries/genres/useCreateGenreMutation.ts rename to src/api/genres/useCreateGenreMutation.ts index 8d23870f..b255f382 100644 --- a/src/hooks/queries/genres/useCreateGenreMutation.ts +++ b/src/api/genres/useCreateGenreMutation.ts @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { supabase } from "@/integrations/supabase/client"; import { useToast } from "@/components/ui/use-toast"; +import { genresKeys } from "./types"; interface CreateGenreParams { name: string; @@ -29,8 +30,7 @@ export function useCreateGenreMutation() { return data; }, onSuccess: () => { - // Invalidate genres query if it exists - queryClient.invalidateQueries({ queryKey: ["genres"] }); + queryClient.invalidateQueries({ queryKey: genresKeys.all() }); toast({ title: "Success", diff --git a/src/hooks/queries/genres/useGenres.ts b/src/api/genres/useGenres.ts similarity index 57% rename from src/hooks/queries/genres/useGenres.ts rename to src/api/genres/useGenres.ts index 51cb2d78..9b39480c 100644 --- a/src/hooks/queries/genres/useGenres.ts +++ b/src/api/genres/useGenres.ts @@ -1,13 +1,8 @@ -import { useQuery } from "@tanstack/react-query"; +import { queryOptions, useQuery } from "@tanstack/react-query"; import { supabase } from "@/integrations/supabase/client"; +import { Genre, genresKeys } from "./types"; -// Query key factory -export const genresKeys = { - all: ["genres"] as const, -}; - -// Business logic function -async function fetchGenres(): Promise> { +export async function fetchGenres(): Promise { const { data, error } = await supabase .from("music_genres") .select("id, name") @@ -20,15 +15,18 @@ async function fetchGenres(): Promise> { return data || []; } -// Hook -export function useGenresQuery() { - return useQuery({ - queryKey: genresKeys.all, +export function genresQuery() { + return queryOptions({ + queryKey: genresKeys.all(), queryFn: fetchGenres, - staleTime: 10 * 60 * 1000, // 10 minutes - genres don't change often + staleTime: 10 * 60 * 1000, }); } +export function useGenresQuery() { + return useQuery(genresQuery()); +} + export function useGenres() { const { data: genres = [], isLoading, error } = useGenresQuery(); diff --git a/src/components/GenreBadge.test.tsx b/src/components/GenreBadge.test.tsx index 80770109..843501ad 100644 --- a/src/components/GenreBadge.test.tsx +++ b/src/components/GenreBadge.test.tsx @@ -1,9 +1,9 @@ import { describe, expect, it, vi, beforeEach } from "vitest"; import { render, screen } from "@testing-library/react"; import { GenreBadge } from "./GenreBadge"; -import * as useGenresModule from "@/hooks/queries/genres/useGenres"; +import * as useGenresModule from "@/api/genres/useGenres"; -vi.mock("@/hooks/queries/genres/useGenres"); +vi.mock("@/api/genres/useGenres"); describe("GenreBadge", () => { beforeEach(() => { diff --git a/src/components/GenreBadge.tsx b/src/components/GenreBadge.tsx index 09bbdccd..8a1a63e5 100644 --- a/src/components/GenreBadge.tsx +++ b/src/components/GenreBadge.tsx @@ -1,5 +1,5 @@ import { Badge } from "@/components/ui/badge"; -import { useGenres } from "@/hooks/queries/genres/useGenres"; +import { useGenres } from "@/api/genres/useGenres"; interface GenreBadgeProps { genreId: string; diff --git a/src/pages/EditionView/tabs/ArtistsTab/filters/FilterSortControls.tsx b/src/pages/EditionView/tabs/ArtistsTab/filters/FilterSortControls.tsx index 6b0cb788..2cfb8373 100644 --- a/src/pages/EditionView/tabs/ArtistsTab/filters/FilterSortControls.tsx +++ b/src/pages/EditionView/tabs/ArtistsTab/filters/FilterSortControls.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from "react"; import type { SortOption, FilterSortState } from "@/hooks/useUrlState"; -import { useGenres } from "@/hooks/queries/genres/useGenres"; +import { useGenres } from "@/api/genres/useGenres"; import { SortControls } from "./SortControls"; import { MobileFilters } from "./MobileFilters"; import { DesktopFilters } from "./DesktopFilters"; diff --git a/src/pages/admin/AddGenreDialog.tsx b/src/pages/admin/AddGenreDialog.tsx index dff5651e..2699263e 100644 --- a/src/pages/admin/AddGenreDialog.tsx +++ b/src/pages/admin/AddGenreDialog.tsx @@ -1,5 +1,5 @@ import { useForm } from "react-hook-form"; -import { useCreateGenreMutation } from "@/hooks/queries/genres/useCreateGenreMutation"; +import { useCreateGenreMutation } from "@/api/genres/useCreateGenreMutation"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; diff --git a/src/pages/admin/ArtistsManagement/AddArtistDialog.tsx b/src/pages/admin/ArtistsManagement/AddArtistDialog.tsx index a18d9093..8b6c4fa5 100644 --- a/src/pages/admin/ArtistsManagement/AddArtistDialog.tsx +++ b/src/pages/admin/ArtistsManagement/AddArtistDialog.tsx @@ -23,7 +23,7 @@ import { import { Music } from "lucide-react"; import { useAuth } from "@/contexts/AuthContext"; import { useUserPermissionsQuery } from "@/hooks/queries/auth/useUserPermissions"; -import { useGenresQuery } from "@/hooks/queries/genres/useGenres"; +import { useGenresQuery } from "@/api/genres/useGenres"; import { useCreateArtistMutation } from "@/hooks/queries/artists/useCreateArtist"; import { useUpdateArtistMutation } from "@/hooks/queries/artists/useUpdateArtist"; import { GenreMultiSelect } from "./GenreMultiSelect"; diff --git a/src/pages/admin/ArtistsManagement/BulkEditor/GenresCell.tsx b/src/pages/admin/ArtistsManagement/BulkEditor/GenresCell.tsx index 88f817eb..07acf75f 100644 --- a/src/pages/admin/ArtistsManagement/BulkEditor/GenresCell.tsx +++ b/src/pages/admin/ArtistsManagement/BulkEditor/GenresCell.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { GenreMultiSelect } from "../GenreMultiSelect"; -import { useGenresQuery } from "@/hooks/queries/genres/useGenres"; +import { useGenresQuery } from "@/api/genres/useGenres"; import { Check, X } from "lucide-react"; interface GenresCellProps {