geo/frontend/lib/hooks/use-suggestions-data.ts

148 lines
4.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 优化建议页面数据获取 Hook
*
* 封装建议列表的 SWR 请求 + 重新生成/更新状态的 mutation替代手动 useState + useEffect 模式。
* - 建议列表:依赖 brandId + 筛选条件
* - 重新生成mutation
* - 更新状态mutation
*/
import { useCallback } from "react";
import { useApi, useApiMutation } from "./use-api";
import { fetchWithAuth } from "@/lib/api/client";
import type { SWRConfiguration } from "swr";
import type { SuggestionListResponse, SuggestionStatus } from "@/types/suggestion";
export interface SuggestionsFilters {
type?: string;
priority?: string;
status?: string;
}
export interface UseSuggestionsDataOptions {
brandId?: string;
filters?: SuggestionsFilters;
/** SWR 配置(用于测试时禁用重试等) */
swrOptions?: SWRConfiguration;
}
export interface UseSuggestionsDataReturn {
/** 建议列表 */
suggestions: SuggestionListResponse["suggestions"] | undefined;
/** 是否正在加载 */
isLoading: boolean;
/** 错误信息 */
error: Error | undefined;
/** 刷新建议列表 */
refresh: () => void;
/** 重新生成建议 */
regenerate: () => Promise<SuggestionListResponse | null>;
/** 是否正在重新生成 */
isRegenerating: boolean;
/** 重新生成错误 */
regenerateError: Error | undefined;
/** 更新建议状态 */
updateStatus: (suggestionId: string, newStatus: string) => Promise<void>;
/** 是否正在更新状态 */
isUpdatingStatus: boolean;
}
function buildSuggestionsUrl(brandId: string, filters?: SuggestionsFilters): string | null {
if (!brandId) return null;
const params = new URLSearchParams();
if (filters?.type && filters.type !== "all") params.set("type", filters.type);
if (filters?.priority && filters.priority !== "all") params.set("priority", filters.priority);
if (filters?.status && filters.status !== "all") params.set("status", filters.status);
const qs = params.toString();
return `/api/v1/brands/${brandId}/suggestions${qs ? `?${qs}` : ""}`;
}
export function useSuggestionsData(
options?: UseSuggestionsDataOptions
): UseSuggestionsDataReturn {
const brandId = options?.brandId ?? "";
const filters = options?.filters;
const swrOptions = options?.swrOptions;
// 建议列表
const suggestionsUrl = buildSuggestionsUrl(brandId, filters);
const {
data: suggestionsResponse,
isLoading,
error,
refresh,
mutate,
} = useApi<SuggestionListResponse>(suggestionsUrl, swrOptions);
const suggestions = suggestionsResponse?.suggestions;
// 重新生成建议mutation
const {
trigger: regenerateTrigger,
isMutating: isRegenerating,
error: regenerateError,
} = useApiMutation<SuggestionListResponse>(
brandId ? `/api/v1/brands/${brandId}/suggestions/regenerate` : "",
"POST"
);
const regenerate = useCallback(async (): Promise<SuggestionListResponse | null> => {
const result = await regenerateTrigger();
if (result) {
// 用新数据更新 SWR 缓存
mutate(result);
}
return result;
}, [regenerateTrigger, mutate]);
// 更新建议状态mutation
const {
isMutating: isUpdatingStatus,
} = useApiMutation(
brandId ? `/api/v1/brands/${brandId}/suggestions` : "",
"PUT"
);
const updateStatus = useCallback(
async (suggestionId: string, newStatus: string) => {
await fetchWithAuth(
`/api/v1/brands/${brandId}/suggestions/${suggestionId}/status`,
{
method: "PUT",
body: JSON.stringify({ status: newStatus }),
}
);
// 乐观更新:直接修改本地缓存
mutate(
(current) => {
if (!current) return current;
return {
...current,
suggestions: current.suggestions.map((s) =>
s.id === suggestionId
? { ...s, status: newStatus as SuggestionStatus }
: s
),
};
},
{ revalidate: false }
);
},
[brandId, mutate]
);
return {
suggestions,
isLoading,
error,
refresh,
regenerate,
isRegenerating,
regenerateError,
updateStatus,
isUpdatingStatus,
};
}