@crmy/web 0.5.5 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/assets/index-CskfWp8E.js +560 -0
  2. package/dist/assets/index-D763l57m.css +1 -0
  3. package/{index.html → dist/index.html} +2 -1
  4. package/package.json +4 -1
  5. package/postcss.config.js +0 -6
  6. package/src/App.tsx +0 -158
  7. package/src/api/client.ts +0 -82
  8. package/src/api/hooks.ts +0 -689
  9. package/src/components/CustomFields.tsx +0 -240
  10. package/src/components/NavLink.tsx +0 -28
  11. package/src/components/crm/AIFab.tsx +0 -37
  12. package/src/components/crm/AccountDrawer.tsx +0 -372
  13. package/src/components/crm/ActivityTimeline.tsx +0 -115
  14. package/src/components/crm/AssignmentDrawer.tsx +0 -396
  15. package/src/components/crm/BriefingPanel.tsx +0 -217
  16. package/src/components/crm/CommandPalette.tsx +0 -254
  17. package/src/components/crm/ContactAvatar.tsx +0 -49
  18. package/src/components/crm/ContactDrawer.tsx +0 -438
  19. package/src/components/crm/ContextPanel.tsx +0 -200
  20. package/src/components/crm/CrmWidgets.tsx +0 -417
  21. package/src/components/crm/DrawerShell.tsx +0 -77
  22. package/src/components/crm/ListToolbar.tsx +0 -252
  23. package/src/components/crm/OpportunityDrawer.tsx +0 -372
  24. package/src/components/crm/PaginationBar.tsx +0 -111
  25. package/src/components/crm/QuickAddDrawer.tsx +0 -652
  26. package/src/components/crm/ShortcutsOverlay.tsx +0 -65
  27. package/src/components/crm/UseCaseDrawer.tsx +0 -454
  28. package/src/components/layout/MobileNav.tsx +0 -49
  29. package/src/components/layout/Sidebar.tsx +0 -157
  30. package/src/components/layout/TopBar.tsx +0 -54
  31. package/src/components/settings/ActorsSettings.tsx +0 -1190
  32. package/src/components/ui/accordion.tsx +0 -52
  33. package/src/components/ui/alert-dialog.tsx +0 -104
  34. package/src/components/ui/alert.tsx +0 -43
  35. package/src/components/ui/aspect-ratio.tsx +0 -5
  36. package/src/components/ui/avatar.tsx +0 -38
  37. package/src/components/ui/badge.tsx +0 -29
  38. package/src/components/ui/breadcrumb.tsx +0 -90
  39. package/src/components/ui/button.tsx +0 -47
  40. package/src/components/ui/calendar.tsx +0 -54
  41. package/src/components/ui/card.tsx +0 -43
  42. package/src/components/ui/carousel.tsx +0 -224
  43. package/src/components/ui/chart.tsx +0 -303
  44. package/src/components/ui/checkbox.tsx +0 -26
  45. package/src/components/ui/collapsible.tsx +0 -9
  46. package/src/components/ui/command.tsx +0 -132
  47. package/src/components/ui/context-menu.tsx +0 -178
  48. package/src/components/ui/date-picker.tsx +0 -313
  49. package/src/components/ui/dialog.tsx +0 -95
  50. package/src/components/ui/drawer.tsx +0 -87
  51. package/src/components/ui/dropdown-menu.tsx +0 -179
  52. package/src/components/ui/form.tsx +0 -129
  53. package/src/components/ui/hover-card.tsx +0 -27
  54. package/src/components/ui/input-otp.tsx +0 -61
  55. package/src/components/ui/input.tsx +0 -22
  56. package/src/components/ui/label.tsx +0 -17
  57. package/src/components/ui/menubar.tsx +0 -207
  58. package/src/components/ui/navigation-menu.tsx +0 -120
  59. package/src/components/ui/pagination.tsx +0 -81
  60. package/src/components/ui/popover.tsx +0 -29
  61. package/src/components/ui/progress.tsx +0 -23
  62. package/src/components/ui/radio-group.tsx +0 -36
  63. package/src/components/ui/resizable.tsx +0 -37
  64. package/src/components/ui/scroll-area.tsx +0 -38
  65. package/src/components/ui/select.tsx +0 -143
  66. package/src/components/ui/separator.tsx +0 -20
  67. package/src/components/ui/sheet.tsx +0 -107
  68. package/src/components/ui/sidebar.tsx +0 -637
  69. package/src/components/ui/skeleton.tsx +0 -7
  70. package/src/components/ui/slider.tsx +0 -23
  71. package/src/components/ui/sonner.tsx +0 -24
  72. package/src/components/ui/switch.tsx +0 -27
  73. package/src/components/ui/table.tsx +0 -72
  74. package/src/components/ui/tabs.tsx +0 -53
  75. package/src/components/ui/textarea.tsx +0 -21
  76. package/src/components/ui/toast.tsx +0 -111
  77. package/src/components/ui/toaster.tsx +0 -24
  78. package/src/components/ui/toggle-group.tsx +0 -49
  79. package/src/components/ui/toggle.tsx +0 -37
  80. package/src/components/ui/tooltip.tsx +0 -28
  81. package/src/components/ui/use-toast.ts +0 -1
  82. package/src/components/ui/utils.ts +0 -9
  83. package/src/contexts/AgentSettingsContext.tsx +0 -24
  84. package/src/hooks/use-mobile.tsx +0 -19
  85. package/src/hooks/use-toast.ts +0 -186
  86. package/src/hooks/useKeyboardShortcuts.ts +0 -95
  87. package/src/hooks/useTheme.ts +0 -24
  88. package/src/index.css +0 -245
  89. package/src/lib/entityColors.ts +0 -18
  90. package/src/lib/stageConfig.ts +0 -32
  91. package/src/lib/utils.ts +0 -6
  92. package/src/main.tsx +0 -25
  93. package/src/pages/Accounts.tsx +0 -205
  94. package/src/pages/Activities.tsx +0 -251
  95. package/src/pages/Agent.tsx +0 -237
  96. package/src/pages/AgentSettings.tsx +0 -544
  97. package/src/pages/Assignments.tsx +0 -750
  98. package/src/pages/Contacts.tsx +0 -200
  99. package/src/pages/Dashboard.tsx +0 -143
  100. package/src/pages/Inbox.tsx +0 -615
  101. package/src/pages/NotFound.tsx +0 -24
  102. package/src/pages/Opportunities.tsx +0 -386
  103. package/src/pages/SearchResults.tsx +0 -49
  104. package/src/pages/Settings.tsx +0 -1884
  105. package/src/pages/UseCases.tsx +0 -396
  106. package/src/pages/auth/Login.tsx +0 -261
  107. package/src/pages/hitl/HITL.tsx +0 -101
  108. package/src/store/appStore.ts +0 -103
  109. package/src/vite-env.d.ts +0 -14
  110. package/tailwind.config.js +0 -121
  111. package/tsconfig.json +0 -24
  112. package/vite.config.ts +0 -27
  113. /package/{public → dist}/android-chrome-192x192.png +0 -0
  114. /package/{public → dist}/android-chrome-512x512.png +0 -0
  115. /package/{public → dist}/apple-touch-icon.png +0 -0
  116. /package/{src/assets/crmy-logo.png → dist/assets/crmy-logo-DWN0xBPW.png} +0 -0
  117. /package/{public → dist}/favicon-16x16.png +0 -0
  118. /package/{public → dist}/favicon-32x32.png +0 -0
  119. /package/{public → dist}/favicon.ico +0 -0
  120. /package/{public → dist}/favicon.svg +0 -0
  121. /package/{public → dist}/site.webmanifest +0 -0
@@ -1,200 +0,0 @@
1
- // Copyright 2026 CRMy Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import { useState, useMemo, useEffect } from 'react';
5
- import { useNavigate } from 'react-router-dom';
6
- import { ContactAvatar } from '@/components/crm/ContactAvatar';
7
- import { TopBar } from '@/components/layout/TopBar';
8
- import { useContacts } from '@/api/hooks';
9
- import { useAppStore } from '@/store/appStore';
10
- import { useAgentSettings } from '@/contexts/AgentSettingsContext';
11
- import { StageBadge } from '@/components/crm/CrmWidgets';
12
- import { ListToolbar, type FilterConfig, type SortOption } from '@/components/crm/ListToolbar';
13
- import { motion } from 'framer-motion';
14
- import { LayoutGrid, List, Sparkles, ChevronUp, ChevronDown } from 'lucide-react';
15
- import { PaginationBar } from '@/components/crm/PaginationBar';
16
- import { useIsMobile } from '@/hooks/use-mobile';
17
- import { stageConfig } from '@/lib/stageConfig';
18
-
19
- type ViewMode = 'table' | 'cards';
20
-
21
- const filterConfigs: FilterConfig[] = [
22
- { key: 'lifecycle_stage', label: 'Stage', options: Object.entries(stageConfig).map(([k, v]) => ({ value: k, label: v.label })) },
23
- ];
24
-
25
- const sortOptions: SortOption[] = [
26
- { key: 'name', label: 'Name' },
27
- { key: 'company', label: 'Company' },
28
- { key: 'created_at', label: 'Created' },
29
- { key: 'lifecycle_stage', label: 'Stage' },
30
- ];
31
-
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
- type Contact = any;
34
-
35
- function displayName(c: Contact): string {
36
- const parts = [c.first_name, c.last_name].filter(Boolean);
37
- if (parts.length > 0) return parts.join(' ');
38
- return c.email || c.company_name || 'Unknown';
39
- }
40
-
41
- export default function Contacts() {
42
- const navigate = useNavigate();
43
- const isMobile = useIsMobile();
44
- const [view, setView] = useState<ViewMode>('table');
45
- const effectiveView = isMobile ? 'cards' : view;
46
- const { openDrawer, openQuickAdd, openAIWithContext } = useAppStore();
47
- const { enabled: agentEnabled } = useAgentSettings();
48
- const [search, setSearch] = useState('');
49
- const [activeFilters, setActiveFilters] = useState<Record<string, string[]>>({});
50
- const [sort, setSort] = useState<{ key: string; dir: 'asc' | 'desc' } | null>(null);
51
- const [page, setPage] = useState(1);
52
- const [pageSize, setPageSize] = useState(25);
53
-
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- const { data, isLoading } = useContacts({ q: search || undefined, limit: 200 }) as any;
56
- const allContacts: Contact[] = data?.data ?? [];
57
-
58
- const handleFilterChange = (key: string, values: string[]) => {
59
- setActiveFilters(prev => { const next = { ...prev }; if (values.length === 0) delete next[key]; else next[key] = values; return next; });
60
- };
61
- const handleSortChange = (key: string) => {
62
- setSort(prev => prev?.key === key ? { key, dir: prev.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'asc' });
63
- };
64
-
65
- const filtered = useMemo(() => {
66
- let result = [...allContacts];
67
- if (activeFilters.lifecycle_stage?.length) result = result.filter(c => activeFilters.lifecycle_stage.includes(c.lifecycle_stage as string));
68
- if (sort) {
69
- result.sort((a, b) => {
70
- const aVal = (a[sort.key] ?? '') as string | number;
71
- const bVal = (b[sort.key] ?? '') as string | number;
72
- if (typeof aVal === 'number' && typeof bVal === 'number') return sort.dir === 'asc' ? aVal - bVal : bVal - aVal;
73
- return sort.dir === 'asc' ? String(aVal).localeCompare(String(bVal)) : String(bVal).localeCompare(String(aVal));
74
- });
75
- }
76
- return result;
77
- }, [allContacts, activeFilters, sort]);
78
-
79
- useEffect(() => { setPage(1); }, [search, activeFilters, sort]);
80
- const paginated = filtered.slice((page - 1) * pageSize, page * pageSize);
81
-
82
- const SortHeader = ({ label, sortKey }: { label: string; sortKey: string }) => (
83
- <th onClick={() => handleSortChange(sortKey)}
84
- className="text-left px-4 py-3 text-xs font-display font-semibold text-muted-foreground cursor-pointer hover:text-foreground transition-colors select-none">
85
- <span className="inline-flex items-center gap-1">
86
- {label}
87
- {sort?.key === sortKey ? (sort.dir === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />) : null}
88
- </span>
89
- </th>
90
- );
91
-
92
- return (
93
- <div className="flex flex-col h-full">
94
- <TopBar title="Contacts">
95
- <div className="hidden md:flex items-center gap-1 bg-muted rounded-xl p-0.5">
96
- <button onClick={() => setView('table')} className={`p-1.5 rounded-lg text-sm transition-all ${view === 'table' ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground'}`}>
97
- <List className="w-4 h-4" />
98
- </button>
99
- <button onClick={() => setView('cards')} className={`p-1.5 rounded-lg text-sm transition-all ${view === 'cards' ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground'}`}>
100
- <LayoutGrid className="w-4 h-4" />
101
- </button>
102
- </div>
103
- </TopBar>
104
-
105
- <ListToolbar
106
- searchValue={search} onSearchChange={setSearch} searchPlaceholder="Search contacts..."
107
- filters={filterConfigs} activeFilters={activeFilters} onFilterChange={handleFilterChange}
108
- onClearFilters={() => setActiveFilters({})} sortOptions={sortOptions} currentSort={sort}
109
- onSortChange={handleSortChange} onAdd={() => openQuickAdd('contact')} addLabel="New Contact" entityType="contacts"
110
- />
111
-
112
- <div className="flex-1 overflow-y-auto px-4 md:px-6 pb-24 md:pb-6">
113
- {isLoading ? (
114
- <div className="space-y-2 pt-2">
115
- {[...Array(5)].map((_, i) => <div key={i} className="h-14 bg-muted/50 rounded-xl animate-pulse" />)}
116
- </div>
117
- ) : filtered.length === 0 ? (
118
- <div className="flex flex-col items-center justify-center py-16 text-muted-foreground">
119
- <p className="text-sm">No contacts found.</p>
120
- <button onClick={() => { setSearch(''); setActiveFilters({}); }} className="mt-2 text-xs text-primary font-semibold hover:underline">
121
- Clear all filters
122
- </button>
123
- </div>
124
- ) : effectiveView === 'table' ? (
125
- <div className="bg-card border border-border rounded-2xl overflow-hidden shadow-sm">
126
- <div className="overflow-x-auto">
127
- <table className="w-full text-sm">
128
- <thead>
129
- <tr className="border-b border-border bg-surface-sunken/50">
130
- <SortHeader label="Name" sortKey="name" />
131
- <SortHeader label="Company" sortKey="company" />
132
- <th className="text-left px-4 py-3 text-xs font-display font-semibold text-muted-foreground">Phone</th>
133
- <SortHeader label="Stage" sortKey="lifecycle_stage" />
134
- {agentEnabled && <th className="px-2 py-3 w-8"></th>}
135
- </tr>
136
- </thead>
137
- <tbody>
138
- {paginated.map((c, i) => (
139
- <tr key={c.id as string} onClick={() => openDrawer('contact', c.id as string)}
140
- className={`border-b border-border last:border-0 hover:bg-primary/5 cursor-pointer group transition-colors ${i % 2 === 1 ? 'bg-surface-sunken/30' : ''}`}>
141
- <td className="px-4 py-3">
142
- <div className="flex items-center gap-3">
143
- <ContactAvatar name={displayName(c)} className="w-8 h-8 text-xs" />
144
- <span className="font-semibold text-foreground">{displayName(c)}</span>
145
- </div>
146
- </td>
147
- <td className="px-4 py-3 text-muted-foreground">{(c.company_name as string) || '—'}</td>
148
- <td className="px-4 py-3 text-muted-foreground text-xs">{(c.phone as string) || '—'}</td>
149
- <td className="px-4 py-3">{c.lifecycle_stage ? <StageBadge stage={c.lifecycle_stage as string} /> : '—'}</td>
150
- {agentEnabled && (
151
- <td className="px-2 py-3">
152
- <button onClick={(e) => { e.stopPropagation(); openAIWithContext({ type: 'contact', id: c.id as string, name: displayName(c), detail: c.company_name as string }); navigate('/agent'); }}
153
- className="p-1.5 rounded-lg opacity-0 group-hover:opacity-100 hover:bg-accent/10 transition-all">
154
- <Sparkles className="w-3.5 h-3.5 text-accent" />
155
- </button>
156
- </td>
157
- )}
158
- </tr>
159
- ))}
160
- </tbody>
161
- </table>
162
- </div>
163
- <div className="px-4">
164
- <PaginationBar page={page} pageSize={pageSize} total={filtered.length} onPageChange={setPage} onPageSizeChange={setPageSize} />
165
- </div>
166
- </div>
167
- ) : (
168
- <>
169
- <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">
170
- {paginated.map((c, i) => (
171
- <motion.div key={c.id as string} initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: i * 0.02 }}
172
- onClick={() => openDrawer('contact', c.id as string)}
173
- className="bg-card border border-border rounded-2xl p-4 cursor-pointer hover:shadow-lg hover:border-primary/20 transition-all press-scale group relative">
174
- {agentEnabled && (
175
- <button onClick={(e) => { e.stopPropagation(); openAIWithContext({ type: 'contact', id: c.id as string, name: displayName(c), detail: c.company_name as string }); navigate('/agent'); }}
176
- className="absolute top-3 right-3 p-1.5 rounded-lg hover:bg-accent/10 transition-all md:opacity-0 md:group-hover:opacity-100">
177
- <Sparkles className="w-3.5 h-3.5 text-accent" />
178
- </button>
179
- )}
180
- <div className="flex items-center gap-3 mb-3">
181
- <ContactAvatar name={displayName(c)} className="w-11 h-11 rounded-2xl text-sm" />
182
- <div>
183
- <p className="font-display font-bold text-foreground">{displayName(c)}</p>
184
- <p className="text-xs text-muted-foreground">{(c.company_name as string) || 'Individual'}</p>
185
- </div>
186
- </div>
187
- <div className="flex items-center gap-2 mb-2">
188
- {c.lifecycle_stage && <StageBadge stage={c.lifecycle_stage as string} />}
189
- </div>
190
- {c.email && <p className="text-xs text-muted-foreground">{c.email as string}</p>}
191
- </motion.div>
192
- ))}
193
- </div>
194
- <PaginationBar page={page} pageSize={pageSize} total={filtered.length} onPageChange={setPage} onPageSizeChange={setPageSize} />
195
- </>
196
- )}
197
- </div>
198
- </div>
199
- );
200
- }
@@ -1,143 +0,0 @@
1
- // Copyright 2026 CRMy Contributors
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import { useState } from 'react';
5
- import { TopBar } from '@/components/layout/TopBar';
6
- import { PipelineSnapshot, ActivityFeed, AccountHealth } from '@/components/crm/CrmWidgets';
7
- import { useAppStore } from '@/store/appStore';
8
- import { useOpportunities } from '@/api/hooks';
9
- import { motion } from 'framer-motion';
10
- import { ArrowRight, TrendingUp, UserPlus, FolderKanban, Activity, Briefcase } from 'lucide-react';
11
-
12
- function greeting() {
13
- const h = new Date().getHours();
14
- if (h < 12) return 'Good morning';
15
- if (h < 17) return 'Good afternoon';
16
- return 'Good evening';
17
- }
18
-
19
- export default function Dashboard() {
20
- const { openDrawer, openQuickAdd } = useAppStore();
21
- const [activityWindow, setActivityWindow] = useState<'today' | 'week'>('today');
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
- const { data: oppsData } = useOpportunities({ limit: 10 }) as any;
24
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
- const opps: any[] = oppsData?.data ?? [];
26
-
27
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
- const hotDeals: any[] = opps
29
- .filter((d: any) => d.stage === 'negotiation' || (d.stage === 'proposal' && d.probability > 50))
30
- .slice(0, 3);
31
-
32
- return (
33
- <div className="flex flex-col h-full">
34
- <TopBar title="Dashboard" />
35
- <div className="flex-1 overflow-y-auto p-4 md:p-6 pb-24 md:pb-6">
36
- {/* Greeting */}
37
- <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} className="mb-6">
38
- <h1 className="text-2xl md:text-3xl font-display font-extrabold">
39
- <span className="gradient-text">{greeting()}</span>
40
- </h1>
41
- <p className="text-sm text-muted-foreground mt-1">Here's what needs your attention today.</p>
42
- </motion.div>
43
-
44
- {/* Quick Actions */}
45
- <motion.div
46
- initial={{ opacity: 0, y: 12 }}
47
- animate={{ opacity: 1, y: 0 }}
48
- transition={{ delay: 0.05 }}
49
- className="grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-3 mb-6"
50
- >
51
- {[
52
- { icon: UserPlus, label: 'New Contact', gradient: 'from-primary/15 to-primary/5', color: 'text-primary', action: () => openQuickAdd('contact') },
53
- { icon: TrendingUp, label: 'New Opportunity', gradient: 'from-accent/15 to-accent/5', color: 'text-accent', action: () => openQuickAdd('opportunity') },
54
- { icon: FolderKanban, label: 'New Use Case', gradient: 'from-success/15 to-success/5', color: 'text-success', action: () => openQuickAdd('use-case') },
55
- { icon: Activity, label: 'Log Activity', gradient: 'from-warning/15 to-warning/5', color: 'text-warning', action: () => openQuickAdd('activity') },
56
- ].map((action) => (
57
- <button
58
- key={action.label}
59
- onClick={action.action}
60
- className={`flex items-center gap-3 p-3 md:p-4 rounded-2xl bg-gradient-to-br ${action.gradient} border border-border/50 hover:shadow-md transition-all press-scale`}
61
- >
62
- <action.icon className={`w-5 h-5 ${action.color}`} />
63
- <span className="text-sm font-display font-semibold text-foreground">{action.label}</span>
64
- </button>
65
- ))}
66
- </motion.div>
67
-
68
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 md:gap-6">
69
- {/* Left column */}
70
- <div className="lg:col-span-2 space-y-4 md:space-y-5">
71
- {hotDeals.length > 0 && (
72
- <motion.div
73
- initial={{ opacity: 0, y: 12 }}
74
- animate={{ opacity: 1, y: 0 }}
75
- transition={{ delay: 0.1 }}
76
- className="bg-card border border-border rounded-2xl p-5 shadow-sm"
77
- >
78
- <h2 className="font-display font-bold text-foreground mb-4">Today's focus</h2>
79
- <div className="space-y-2">
80
- {hotDeals.map((deal: Record<string, unknown>) => {
81
- const contactName = (deal.contact_name ?? deal.contactName ?? '') as string;
82
- const amount = (deal.amount as number) ?? 0;
83
- return (
84
- <div
85
- key={deal.id as string}
86
- onClick={() => openDrawer('opportunity', deal.id as string)}
87
- className="flex items-center gap-3 p-3 rounded-xl bg-surface hover:bg-surface-sunken cursor-pointer transition-all press-scale"
88
- >
89
- <div className="w-8 h-8 rounded-xl bg-accent/10 flex items-center justify-center flex-shrink-0">
90
- <Briefcase className="w-4 h-4 text-accent" />
91
- </div>
92
- <div className="flex-1 min-w-0">
93
- <p className="text-sm font-semibold text-foreground truncate">{deal.name as string}</p>
94
- <p className="text-xs text-muted-foreground">
95
- ${amount >= 1000 ? `${(amount / 1000).toFixed(0)}K` : amount} · {contactName}
96
- </p>
97
- </div>
98
- <ArrowRight className="w-4 h-4 text-muted-foreground" />
99
- </div>
100
- );
101
- })}
102
- </div>
103
- </motion.div>
104
- )}
105
-
106
- <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.15 }}>
107
- <div className="bg-card border border-border rounded-2xl p-5 shadow-sm">
108
- <div className="flex items-center justify-between mb-3">
109
- <h3 className="font-display font-bold text-foreground">Recent activity</h3>
110
- <div className="flex items-center gap-0.5 bg-muted rounded-lg p-0.5">
111
- <button
112
- onClick={() => setActivityWindow('today')}
113
- className={`px-2.5 py-1 rounded-md text-[11px] font-semibold transition-all ${activityWindow === 'today' ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'}`}
114
- >
115
- Today
116
- </button>
117
- <button
118
- onClick={() => setActivityWindow('week')}
119
- className={`px-2.5 py-1 rounded-md text-[11px] font-semibold transition-all ${activityWindow === 'week' ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'}`}
120
- >
121
- This Week
122
- </button>
123
- </div>
124
- </div>
125
- <ActivityFeed limit={8} filterWindow={activityWindow} />
126
- </div>
127
- </motion.div>
128
- </div>
129
-
130
- {/* Right column */}
131
- <div className="space-y-4 md:space-y-5">
132
- <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.08 }}>
133
- <PipelineSnapshot />
134
- </motion.div>
135
- <motion.div initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.11 }}>
136
- <AccountHealth />
137
- </motion.div>
138
- </div>
139
- </div>
140
- </div>
141
- </div>
142
- );
143
- }