feat: add exit animations on lead cards and cross-page filter continuity

- Wrap lead cards in AnimatePresence/motion.div so they fade+slide out when removed from the NEW filter
- Update "View All" link to pass active source filter as ?source= URL param
- Initialize AllLeadsPage sourceFilter from URL search params on mount

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-16 18:32:27 +05:30
parent 5efa22a35a
commit 58777222ca
2 changed files with 31 additions and 15 deletions

View File

@@ -1,4 +1,5 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { useSearchParams } from 'react-router';
import { ArrowLeft, Download01, FilterLines, SearchLg, SwitchVertical01 } from '@untitledui/icons'; import { ArrowLeft, Download01, FilterLines, SearchLg, SwitchVertical01 } from '@untitledui/icons';
import { Button } from '@/components/base/buttons/button'; import { Button } from '@/components/base/buttons/button';
import { Input } from '@/components/base/input/input'; import { Input } from '@/components/base/input/input';
@@ -29,11 +30,13 @@ const PAGE_SIZE = 25;
export const AllLeadsPage = () => { export const AllLeadsPage = () => {
const { user } = useAuth(); const { user } = useAuth();
const [searchParams] = useSearchParams();
const initialSource = searchParams.get('source') as LeadSource | null;
const [tab, setTab] = useState<TabKey>('new'); const [tab, setTab] = useState<TabKey>('new');
const [selectedIds, setSelectedIds] = useState<string[]>([]); const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [sortField, setSortField] = useState('createdAt'); const [sortField, setSortField] = useState('createdAt');
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc'); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
const [sourceFilter, setSourceFilter] = useState<LeadSource | null>(null); const [sourceFilter, setSourceFilter] = useState<LeadSource | null>(initialSource);
const [searchQuery, setSearchQuery] = useState(''); const [searchQuery, setSearchQuery] = useState('');
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);

View File

@@ -1,4 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { AnimatePresence, motion } from 'motion/react';
import { Button } from '@/components/base/buttons/button'; import { Button } from '@/components/base/buttons/button';
import { TopBar } from '@/components/layout/top-bar'; import { TopBar } from '@/components/layout/top-bar';
@@ -81,23 +82,35 @@ export const LeadWorkspacePage = () => {
<div> <div>
<div className="mb-3.5 flex items-center justify-between"> <div className="mb-3.5 flex items-center justify-between">
<h2 className="font-display text-md font-bold text-primary">New Leads</h2> <h2 className="font-display text-md font-bold text-primary">New Leads</h2>
<Button href="/leads" color="link-color" size="sm"> <Button
View All {total} Leads href={sourceFilter ? `/leads?source=${sourceFilter}` : '/leads'}
color="link-color"
size="sm"
>
View All {total} Leads
</Button> </Button>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{displayLeads.map((lead) => ( <AnimatePresence>
<LeadCard {displayLeads.map((lead) => (
key={lead.id} <motion.div
lead={lead} key={lead.id}
onAssign={handleAssign} initial={{ opacity: 1, x: 0 }}
onMessage={handleMessage} exit={{ opacity: 0, x: -20, height: 0, marginBottom: 0 }}
onMarkSpam={handleMarkSpam} transition={{ duration: 0.3 }}
onMerge={handleMerge} >
onLogCall={handleLogCall} <LeadCard
onUpdateStatus={handleUpdateStatus} lead={lead}
/> onAssign={handleAssign}
))} onMessage={handleMessage}
onMarkSpam={handleMarkSpam}
onMerge={handleMerge}
onLogCall={handleLogCall}
onUpdateStatus={handleUpdateStatus}
/>
</motion.div>
))}
</AnimatePresence>
{displayLeads.length === 0 && ( {displayLeads.length === 0 && (
<p className="py-8 text-center text-sm text-tertiary"> <p className="py-8 text-center text-sm text-tertiary">
No leads match the current filters. No leads match the current filters.