fix(client): add selection mode toggle for bulk delete UX
- Add 'Select' toggle button in filter area - Checkboxes only visible when selection mode is active - Hide individual trash buttons during selection mode - Exit selection mode when: deselecting all, deleting, or changing filters - Resolves visual conflict between checkbox and card title - Removes redundancy between checkbox and trash button The checkbox now appears on-demand via toggle, providing cleaner default UI.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
// Meetings list page
|
// Meetings list page
|
||||||
|
|
||||||
import { Calendar, Loader2 } from 'lucide-react';
|
import { Calendar, Loader2, CheckSquare } from 'lucide-react';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { getAPI } from '@/api/interface';
|
import { getAPI } from '@/api/interface';
|
||||||
@@ -52,6 +52,7 @@ export default function MeetingsPage() {
|
|||||||
const [loadingMore, setLoadingMore] = useState(false);
|
const [loadingMore, setLoadingMore] = useState(false);
|
||||||
|
|
||||||
// Bulk selection state
|
// Bulk selection state
|
||||||
|
const [isSelectionMode, setIsSelectionMode] = useState(false);
|
||||||
const [selectedMeetingIds, setSelectedMeetingIds] = useState<Set<string>>(new Set());
|
const [selectedMeetingIds, setSelectedMeetingIds] = useState<Set<string>>(new Set());
|
||||||
const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);
|
const [showBulkDeleteDialog, setShowBulkDeleteDialog] = useState(false);
|
||||||
const { mutate: deleteMeetings, isLoading: isDeleting } = useDeleteMeetings();
|
const { mutate: deleteMeetings, isLoading: isDeleting } = useDeleteMeetings();
|
||||||
@@ -117,6 +118,7 @@ export default function MeetingsPage() {
|
|||||||
// Clear selections when filters change
|
// Clear selections when filters change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedMeetingIds(new Set());
|
setSelectedMeetingIds(new Set());
|
||||||
|
setIsSelectionMode(false);
|
||||||
}, [searchQuery, stateFilter, projectScope, resolvedProjectId]);
|
}, [searchQuery, stateFilter, projectScope, resolvedProjectId]);
|
||||||
|
|
||||||
const filteredMeetings = useMemo(
|
const filteredMeetings = useMemo(
|
||||||
@@ -149,6 +151,14 @@ export default function MeetingsPage() {
|
|||||||
|
|
||||||
const handleDeselectAll = useCallback(() => {
|
const handleDeselectAll = useCallback(() => {
|
||||||
setSelectedMeetingIds(new Set());
|
setSelectedMeetingIds(new Set());
|
||||||
|
setIsSelectionMode(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const toggleSelectionMode = useCallback(() => {
|
||||||
|
setIsSelectionMode((prev) => {
|
||||||
|
if (prev) setSelectedMeetingIds(new Set());
|
||||||
|
return !prev;
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleBulkDelete = useCallback(() => {
|
const handleBulkDelete = useCallback(() => {
|
||||||
@@ -160,6 +170,7 @@ export default function MeetingsPage() {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setSelectedMeetingIds(new Set());
|
setSelectedMeetingIds(new Set());
|
||||||
setShowBulkDeleteDialog(false);
|
setShowBulkDeleteDialog(false);
|
||||||
|
setIsSelectionMode(false);
|
||||||
fetchMeetings(0, false);
|
fetchMeetings(0, false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -232,6 +243,14 @@ export default function MeetingsPage() {
|
|||||||
{state}
|
{state}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
<Button
|
||||||
|
variant={isSelectionMode ? 'default' : 'outline'}
|
||||||
|
size="sm"
|
||||||
|
onClick={toggleSelectionMode}
|
||||||
|
>
|
||||||
|
<CheckSquare className="h-4 w-4 mr-1" />
|
||||||
|
Select
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -261,8 +280,8 @@ export default function MeetingsPage() {
|
|||||||
meeting={meeting}
|
meeting={meeting}
|
||||||
index={i}
|
index={i}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
showDeleteButton
|
showDeleteButton={!isSelectionMode}
|
||||||
isSelectable={meeting.state !== 'recording'}
|
isSelectable={isSelectionMode && meeting.state !== 'recording'}
|
||||||
isSelected={selectedMeetingIds.has(meeting.id)}
|
isSelected={selectedMeetingIds.has(meeting.id)}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user