This commit is contained in:
gpt-engineer-app[bot]
2025-11-12 14:44:56 +00:00
parent 44a713af62
commit e89483b604
2 changed files with 135 additions and 40 deletions

View File

@@ -4,6 +4,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { RefreshButton } from '@/components/ui/refresh-button'; import { RefreshButton } from '@/components/ui/refresh-button';
import { QueueSortControls } from './QueueSortControls'; import { QueueSortControls } from './QueueSortControls';
import { useFilterPanelState } from '@/hooks/useFilterPanelState'; import { useFilterPanelState } from '@/hooks/useFilterPanelState';
@@ -80,25 +81,40 @@ export const QueueFilters = ({
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* Global toggle for detailed views */} {/* Global toggle for detailed views */}
<Button <TooltipProvider>
variant="ghost" <Tooltip>
size="sm" <TooltipTrigger asChild>
onClick={toggleDetails} <Button
className="h-8 gap-2 text-xs text-muted-foreground hover:text-foreground transition-colors" variant="ghost"
title={detailsCollapsed ? "Expand all detailed views" : "Collapse all detailed views"} size="sm"
> onClick={toggleDetails}
{detailsCollapsed ? ( className="h-8 gap-2 text-xs text-muted-foreground hover:text-foreground transition-colors"
<> >
<Maximize2 className="h-3.5 w-3.5" /> {detailsCollapsed ? (
{!isMobile && <span>Expand All</span>} <>
</> <Maximize2 className="h-3.5 w-3.5" />
) : ( {!isMobile && <span>Expand All</span>}
<> </>
<Minimize2 className="h-3.5 w-3.5" /> ) : (
{!isMobile && <span>Collapse All</span>} <>
</> <Minimize2 className="h-3.5 w-3.5" />
)} {!isMobile && <span>Collapse All</span>}
</Button> </>
)}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">
<p className="text-xs">
{detailsCollapsed
? "Show detailed field-by-field view for all items in the queue"
: "Hide detailed field-by-field view for all items in the queue"}
</p>
<p className="text-xs text-muted-foreground mt-1">
This preference is saved to your account
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
{isMobile && ( {isMobile && (
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0"> <Button variant="ghost" size="sm" className="h-8 w-8 p-0">

View File

@@ -1,48 +1,127 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { logger } from '@/lib/logger'; import { logger } from '@/lib/logger';
import { useAuth } from '@/hooks/useAuth';
import { supabase } from '@/lib/supabaseClient';
import { handleNonCriticalError } from '@/lib/errorHandler';
import type { Json } from '@/integrations/supabase/types';
const STORAGE_KEY = 'detailed-view-collapsed'; const STORAGE_KEY = 'detailed-view-collapsed';
interface ModerationPreferences {
detailed_view_collapsed: boolean;
}
interface UseDetailedViewStateReturn { interface UseDetailedViewStateReturn {
isCollapsed: boolean; isCollapsed: boolean;
toggle: () => void; toggle: () => void;
setCollapsed: (value: boolean) => void; setCollapsed: (value: boolean) => void;
loading: boolean;
} }
/** /**
* Hook to manage detailed view collapsed/expanded state * Hook to manage detailed view collapsed/expanded state
* Syncs with localStorage for persistence across sessions * Persists to database for authenticated users, localStorage for guests
* Defaults to collapsed to reduce visual clutter * Defaults to collapsed to reduce visual clutter
*/ */
export function useDetailedViewState(): UseDetailedViewStateReturn { export function useDetailedViewState(): UseDetailedViewStateReturn {
const [isCollapsed, setIsCollapsed] = useState<boolean>(() => { const { user } = useAuth();
// Initialize from localStorage on mount const [isCollapsed, setIsCollapsed] = useState<boolean>(true);
try { const [loading, setLoading] = useState(true);
const stored = localStorage.getItem(STORAGE_KEY);
// Default to collapsed (true) to reduce visual clutter
return stored ? JSON.parse(stored) : true;
} catch (error) {
logger.warn('Error reading detailed view state from localStorage', { error });
return true;
}
});
// Sync to localStorage when state changes // Load preferences on mount
useEffect(() => { useEffect(() => {
loadPreferences();
}, [user]);
const loadPreferences = async () => {
try { try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(isCollapsed)); if (user) {
// Load from database for authenticated users
const { data, error } = await supabase
.from('user_preferences')
.select('moderation_preferences')
.eq('user_id', user.id)
.maybeSingle();
if (error && error.code !== 'PGRST116') {
handleNonCriticalError(error, {
action: 'Load moderation preferences',
userId: user.id,
});
}
if (data?.moderation_preferences) {
const prefs = data.moderation_preferences as ModerationPreferences;
setIsCollapsed(prefs.detailed_view_collapsed ?? true);
}
} else {
// Load from localStorage for guests
try {
const stored = localStorage.getItem(STORAGE_KEY);
setIsCollapsed(stored ? JSON.parse(stored) : true);
} catch (error) {
logger.warn('Error reading detailed view state from localStorage', { error });
}
}
} catch (error) { } catch (error) {
logger.warn('Error saving detailed view state to localStorage', { error }); logger.warn('Error loading detailed view preferences', { error });
} finally {
setLoading(false);
} }
}, [isCollapsed]); };
const toggle = () => setIsCollapsed(prev => !prev); const savePreferences = async (collapsed: boolean) => {
try {
if (user) {
// Save to database for authenticated users
const moderationPrefs: ModerationPreferences = {
detailed_view_collapsed: collapsed,
};
const setCollapsed = (value: boolean) => setIsCollapsed(value); const { error } = await supabase
.from('user_preferences')
.upsert({
user_id: user.id,
moderation_preferences: moderationPrefs as unknown as Json,
updated_at: new Date().toISOString(),
}, {
onConflict: 'user_id',
});
if (error) {
handleNonCriticalError(error, {
action: 'Save moderation preferences',
userId: user.id,
});
}
} else {
// Save to localStorage for guests
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(collapsed));
} catch (error) {
logger.warn('Error saving detailed view state to localStorage', { error });
}
}
} catch (error) {
logger.warn('Error saving detailed view preferences', { error });
}
};
const toggle = () => {
const newValue = !isCollapsed;
setIsCollapsed(newValue);
savePreferences(newValue);
};
const setCollapsed = (value: boolean) => {
setIsCollapsed(value);
savePreferences(value);
};
return { return {
isCollapsed, isCollapsed,
toggle, toggle,
setCollapsed, setCollapsed,
loading,
}; };
} }