fix(qc-queue): correct sidebar badge and Refresh button loading state
Sidebar My QC Queue badge was showing org-wide pending_qc job count instead of the current user personal assigned tasks. Now uses useMyQCQueueCount which sums the linguist and reviewer queue totals from the same me/language-qc-queue API the queue page uses. Refresh button now shows a spinner and Refreshing label while the refetch is in progress so users can see the action took effect. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8dee0b6ff5
commit
31d631f70d
3 changed files with 37 additions and 10 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { Link, useLocation, useParams } from 'react-router-dom';
|
||||
import { useAuthStore } from '../../lib/auth';
|
||||
import { useMyMemberships } from '../../hooks/useClients';
|
||||
import { useJobs, useBriefs } from '../../hooks/useJob';
|
||||
import { useJobs, useBriefs, useMyQCQueueCount } from '../../hooks/useJob';
|
||||
|
||||
interface SidebarItem {
|
||||
label: string;
|
||||
|
|
@ -25,10 +25,7 @@ export function Sidebar({ onMobileClose }: SidebarProps) {
|
|||
const isPMOrAdmin = ['project_manager', 'admin'].includes(user?.role || '');
|
||||
const isAdminOrProduction = ['production', 'admin'].includes(user?.role || '');
|
||||
|
||||
const { data: qcData } = useJobs(
|
||||
{ status: 'pending_qc', size: 1 },
|
||||
{ enabled: isQCRole }
|
||||
);
|
||||
const myQCCount = useMyQCQueueCount(isQCRole);
|
||||
const { data: finalData } = useJobs(
|
||||
{ status: 'pending_final_review', size: 1 },
|
||||
{ enabled: isPMOrAdmin }
|
||||
|
|
@ -40,7 +37,7 @@ export function Sidebar({ onMobileClose }: SidebarProps) {
|
|||
|
||||
const { data: allBriefs = [] } = useBriefs();
|
||||
|
||||
const qcBadge = isQCRole ? (qcData?.total || 0) : 0;
|
||||
const qcBadge = isQCRole ? myQCCount : 0;
|
||||
const finalBadge = isPMOrAdmin ? (finalData?.total || 0) : 0;
|
||||
const failuresBadge = isAdminOrProduction ? (failuresData?.total || 0) : 0;
|
||||
const briefsBadge = allBriefs.filter(b => b.status === 'submitted').length;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useQuery, useMutation, useQueryClient, useQueries } from '@tanstack/react-query';
|
||||
import { apiClient } from '../lib/api';
|
||||
import type {
|
||||
Job,
|
||||
|
|
@ -441,4 +441,27 @@ export function useUploadFinalVtt() {
|
|||
queryClient.invalidateQueries({ queryKey: ['jobs'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the total count of QC tasks personally assigned to the current user (linguist + reviewer combined). */
|
||||
export function useMyQCQueueCount(enabled = true) {
|
||||
const results = useQueries({
|
||||
queries: [
|
||||
{
|
||||
queryKey: ['linguist-queue', 'linguist', 'all'],
|
||||
queryFn: () => apiClient.getMyLanguageQCQueue('linguist'),
|
||||
enabled,
|
||||
staleTime: 30_000,
|
||||
refetchInterval: 30_000,
|
||||
},
|
||||
{
|
||||
queryKey: ['linguist-queue', 'reviewer', 'all'],
|
||||
queryFn: () => apiClient.getMyLanguageQCQueue('reviewer'),
|
||||
enabled,
|
||||
staleTime: 30_000,
|
||||
refetchInterval: 30_000,
|
||||
},
|
||||
],
|
||||
});
|
||||
return (results[0].data?.total ?? 0) + (results[1].data?.total ?? 0);
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ export function LinguistQueue({ defaultRole }: LinguistQueueProps = {}) {
|
|||
const [activeTab, setActiveTab] = useState<LanguageQCStatus | 'all'>('all');
|
||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc');
|
||||
|
||||
const { data, isLoading, refetch } = useQuery({
|
||||
const { data, isLoading, isFetching, refetch } = useQuery({
|
||||
queryKey: ['linguist-queue', activeRole, activeTab],
|
||||
queryFn: () => apiClient.getMyLanguageQCQueue(
|
||||
activeRole,
|
||||
|
|
@ -125,9 +125,16 @@ export function LinguistQueue({ defaultRole }: LinguistQueueProps = {}) {
|
|||
</div>
|
||||
<button
|
||||
onClick={() => refetch()}
|
||||
className="text-sm text-gray-500 hover:text-gray-700 border border-gray-300 rounded px-3 py-1"
|
||||
disabled={isFetching}
|
||||
className="text-sm text-gray-500 hover:text-gray-700 border border-gray-300 rounded px-3 py-1 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1.5"
|
||||
>
|
||||
Refresh
|
||||
{isFetching && (
|
||||
<svg className="animate-spin h-3 w-3" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
</svg>
|
||||
)}
|
||||
{isFetching ? 'Refreshing…' : 'Refresh'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue