Backend: - token_version in JWT (bump_token_version, get_token_version on User model); jwt_required checks tv claim → 401 on mismatch; login routes embed version - Quota pre-flight in all 3 LLM public methods (QuotaExceededError bubbles up) - AI runner catches QuotaExceededError → sets status paused_quota + emits WS event - Admin routes: POST /users (create), POST /users/<id>/reset-password, POST /pricing, GET /focus-groups with aggregated cost; PUT /users/<id> now bumps token_version on disable or role change - backfill_usage.py: idempotent estimated-event generator for historical data, tiktoken for GPT models, char/3.8 for Gemini, --dry-run flag Frontend: - 402 interceptor dispatches quota_exceeded CustomEvent - adminApi: createUser, resetPassword, createPricing, listFocusGroups - UsersTab: New User dialog + Reset Password in edit dialog - PricingTab: New Price dialog (model, provider, input/output/cached prices) - FocusGroupsTab: focus groups table sorted by total cost - Admin.tsx: 4th tab (Focus Groups) - FocusGroupSession: admin-only cost badge + dismissable quota exceeded banner Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
10 lines
342 B
TypeScript
10 lines
342 B
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { adminApi } from '@/lib/api';
|
|
|
|
export function useAdminFocusGroups(params?: { skip?: number; limit?: number }) {
|
|
return useQuery({
|
|
queryKey: ['admin', 'focus-groups', params],
|
|
queryFn: () => adminApi.listFocusGroups(params).then(r => r.data),
|
|
staleTime: 60_000,
|
|
});
|
|
}
|